微服务难道就没有代价吗?

in 工作记录 with 0 comment

玩二手电脑组装的人,在想要满足需求的情况下,往往是预算不足,只好淘旧的硬件来自行组装,在性价比,稳定性,扩展性之间平衡和妥协。写代码这件事上,往往也是预算不足,充满了平衡和妥协。

脱离需求谈技术实现,就和装一台2万块的电脑天天用来刷贴吧一样。

以上,摘自《代码与二手电脑装机艺术》还没写(划掉)。

微服务、微服务、微服务,从我刚加入这个行业开始,这个词就就到处都在出现。高可用、易扩容、能容灾、高并发,微服务大概就像手机届里的苹果,看到的第一印象就是高雅,牛逼。

而三年前刚刚入行的我,就像一个青涩小学生,自然是:

—— 摸都没摸过。

兴趣是最好的老师。

第一年里补充计算机里着各种知识概念,写着丑陋的代码,享受了SSM构架下的一堆XML配置文件。

很快就有了机会自己整项目,SpringBoot从零开始,前端、后端、CDN、域名、 Linux、Nginx、Docker,全都自己来,从第一个文件建立到上线。

对我而言是里程碑式的一件事:

拥有了掌握项目一切的自信。

为什么我要使用微服务

如果你有构建SpringBoot的经验,起一个SpringBoot只需要几分钟,你就做好了Controller层,很快连上了数据库,一个Demo就出来了。如果没有构建SpringBoot的经验,你可能需要几天时间,简单了解下SpringBoot 构建自己的SpringBoot项目,总之不会需要太久。

项目需要快速的迭代且服务不能中断,长期以往,你可能会像我一样拆分出多个SpringBoot程序,之间用HTTP工具类调用,代码变的混乱和不可复用,依赖越来越乱,所有服务是单实例的,个个程序都有自己的缓存,升级必须重启,想改变又因为各种限制...

总之结果就是看上去简单又复杂一坨代码。

这个时候又要做新的项目并且还要和旧的项目交互,我觉得我需要微服务了。

写第一行代码前

为了理解和学习微服务,我做了什么?

需要在了解SpringBoot的前提下,熟悉几代SpringCloud各种组件的停更/升级/替换等。了解注册中心,配置中心,消息总线,路由网关,负载均衡调用器,服务接口调用工具,分布式事务,断路器,熔断,限流等概念。

项目构建需要多理解项目构建工具一些,例如Maven中锁版本,包的层级依赖,各个服务间公用代码的包的抽取和打包,项目总体上会更碎一些。

一开始真的是被各种名词唬住,怕怕的,但是其实本质上并不是什么复杂的东西,然后的确要一些时间。

学习参考了:

https://www.bilibili.com/video/BV18E411x7eT

在尚硅谷看了一些微服务基本概念和功能。

https://github.com/macrozheng/mall-swarm

很多人都知道的开源微服务项目,优点是文档很厉害,如果是新手,很容易看懂各个部分,快速上手。 缺点就是比较Demo级别,直接用这个构架作为生产环境不现实,完全不够用。

pig4cloud
https://gitee.com/log4j/pig
https://www.yuque.com/pig4cloud/pig

商用级别的,上手后很值得参考的项目,缺点可能是开源做的不是很好。

https://github.com/youlaitech/youlai-mall

开源微服务项目,值得参考。

人月神话?

两个人之前需要沟通2次,3个人就需要6次,写代码的沟通成本是非常高的。

软件开发的进度难以用人天衡量。

如果项目不是成百上千万并发的需求或者复杂度,后端一人或者两人可能是最高效的,前端同理。

开发时,对功能要问是什么? 为什么? 有了此功能有什么意义? 谁在用? 真的有用到吗? 等等.. 理解透彻才能做好,话语的传递总是有偏差,人和人的沟通总是充满偏差。

以上,我的一些经验。

起步前的情况

起步

构架上,一步到位还是循序渐进?

出于人力和时间和当前自身水平,第一个版本我选择循序渐进,单实例,不分库,明确清晰的 (有点做菜放适量盐的感觉) 服务直接拆分。

第一个版本,要考虑多实例吗?

考虑但没有直接实现多实例。

同一个服务起多个实例,之前难免需要共享缓存和多线程共享的缓存,虽然我第一版没有打算起多实例,缓存层和共享资源也要抽取好,方便后期改造进Redis之类的地方去。

第一个版本,需要考虑分库吗?

考虑但不直接分库。

例如A库有 C、D、E 表,B库有 F、G 表,如果把这些表都放在一个库,也是能跑的,但是要在逻辑上做好区分,不要耦合,方便后期拆库。

多实例,带来了什么问题

如果要全部实现以上3点,需要付出巨大的代价。

定时任务会跑多次怎么办?

A B实例中同一刻的定时任务,往往只需要跑一遍,有一些定时任务框架,大体上也是抽取出来订单任务单独跑个单例最方便,现在需求不是很多,后期可能要抽取下定时任务。

WebSokcet和Socket怎么多实例呢?

这方面的资料挺少的,核心就是依赖客户端优秀的重连机制实现。
特意去V站发帖咨询:

https://www.v2ex.com/t/849702#reply29

订单号生成,这也要新建个服务吗?

A B实例需要生成不可重复的订单号,有长度限制并且还要包含时间信息和其他信息只有3 - 5位 保证不可重复性,之前的单机策略是1毫秒下最多滚999个订单号,加锁保证唯一,多实例下要去同一个地方取订单号保证唯一性,单独新建个服务代价太大,还没想好怎么做。

微信公众号的访问权限获取

微信公众号接口的策略大概是两个小时获取一次Token,凭借此Token访问微信相关接口,还有其他一些微信接口的封装做为了一个模块,多实例存在一个获取和存储Token的问题,重复获取Token如何解决,还没想好,暂时还是单例。

多实例共享缓存

服务A的两个实例 B C 如果共享资源只能依赖第三个进程,如果第三个进程是单例的那也就很不微服务了,比如一般用的Redis如果要起集群就是3个实例。

代码层也要改造,所有的共享缓存之类的全部丢进Redis中。

庞大的内存

一个SpringBoot需要约500M内存。如果需要多实例,难免就要Redis集群(起码3个Redis),注册中心集群(Nacos3个),MySQL起码两个双主,普通的服务起码2个实例,内存和服务器数量的需求相对单例求是可怕的。

分布式事务

一个事务A服务保存,B服务也要保存,成了跨进程的事务,这可怎么办?
笨办法手动提交也算能解决,也会很多分布式锁的解决方案,我也没去了解。
现在的手段就是尽可能避免需要分布式事务,还没有遇到非要处理的情况,有需求了才会去处理这种情况。

部署和运维

微服务,彻底的前后端分离,两份前端,八个后端进程,占了5个G的内存,如果考虑多实例,约要准备总计15G内存,分别散布起码3台服务器上,考虑到后期还要拆分和增加服务。

不使用云服务器,自行维护一切的尝试

拉了20M的专线,购买了UPS电源,自行在硬件上安装系统,成功跑起来了第一版,为了维护服务,相比云服务器,我需要自行应付断电,服务器要有ECC内存,硬盘要用起码企业级,服务器有双电源保障,硬盘RAID10保证数据安全和可用性,异地容灾后期也成了必须品...

第一次不用云服务器,才知道云服务器给我做了多少工作!

自建硬件可以拥有近乎无限的性能和硬件扩展,但是也带来了大量的运维工作还有异地容灾相当麻烦。

集群部署 —— Docker

集群部署,DockerFile 和 DockerCompose就满足不了了,需要Docker集群,自然就用DockerStack,如果实例太多太多,还需要K8S这种管理集群了。

彻底贯彻微服务,相比单例服务更高的运维成本,前期开发成本高,但是带来的优点也是显而易见的,如果需求足够大,贯彻微服务总体降低开发成本,运维,容灾,升级都更容易。

云服务器需要付出大量的钱,硬件较贵(硬盘、CPU、内存),但是提供了很多运维能力。如果自行维护机房服务器,没有了云服务器提供的运维能力,要付出相当高的建立和维护成本。