前言:
2019马上过去,即将迎来新的一年,作为在软件行业有“搬砖”一年的程序猿,每天面临着各种总结,各种汇报,各种规划。在这个行业摸爬滚打三年多,也不算是一个新人了,还是没钱,没车,没房,也就没老婆,有时想想自己也挺失败的,唉,没办法,继续努力吧,努力就有希望。总想写点东西,奈何文笔不好,自己积淀也没那么深,每次打开笔记本,又关掉了。万事开头难,总得记录点什么,也让自己在记录的过程中捋一捋思绪,废话不多说,马上开始(写的不好,请各位大佬手下留情,多提意见和建议哈)。
一、什么是微服务架构:
这个概念相信作为程序猿没少听到过,很多也在实际工作中应用过,我也不太赘述了,简而言之。【服务】: 提供互联网产品完整访问能力;【微服务】:一大群服务共同提供互联网产品完整访问能力,但每一个服务提供部分访问能力,这就称为微服务。人多力量大,人多好办事嘛。但就像企业里一样,人一多,就容易出现各种问题,微服务本身没有问题,但要用好微服务,任重而道远。(本文全不能容均基于go, python架构而言,为啥不提java??java有完整的微服务响应框架呀?因为楼主很搂逼,只会go, python呀)。
二、微服务架构优点:
在提微服务缺点之前,先简略过一下微服务的优点。相比起集成式大型应用。微服务主要优点在于:
1.微服务技术选型更灵活,单一大型应用在立项之初,可能就选定了语言。要想某部分功能用其他语言就比较恼火(也可以写,不是不可以)。而微服务架构就灵活多了,我想go就go,想python就python,想java就java,想c++就c++,实在是不服,还能搬出c来写呢。只要大家都提供统一的调用方式即可,可以HTTP, 可以RPC,甚至要TCP都没有什么问题。
2.系统可以有效的解耦(理论上的,没处理好也不能做到解耦),可以单独对部分系统进行升级,快速部署,而不用重启整个【服务】,减少升级对服务的影响。
3.服务更小,可以使人员更精炼,专业的事由专业的人来做,效率更高。同时,服务更小了,更容易做到高内聚,低耦合,利于横向扩展。
4.【服务】可访问性增加,由于单台服务器性能总是有限的,服务全部功能都集中于一台服务器上,不同的功能模块吃资源能力完全不同(比如处理图片的模块资源消耗高于数据处理,文本处理等)。将资源消耗完全不同的部署在一台机器上,可能造成高消耗资源服务由于对资源消耗过多,影响低消耗的服务接受访问能力(所以要提倡节能减排啊)。
三、微服务架构缺点:
搞了半天,终于进入了主题了,前面只简单的罗列了几点微服务架构的优点,各位大佬肯定知道的比我更多。接下来浅谈一下微服务架构的一些缺点。
1.服务部署问题:
集成式大型应用,部署很快。可以用一台或者多台服务器部署相应的代码即可。而当系统由多个微服务组成时,部署这么多服务也就成为了一个难点(有成熟的容器化部署和容器管理工具,如docker,kubernetes等貌似也不难呀,但要玩转docker,k8s等也是需要一番功力的),增加了运维的成本。
2.服务发现问题:
各个微服务部署好了之后,就得考虑各个服务之间怎么去发现对方,获取访问能力。在微服务规模较少时,一般的做法是配置固定的ip或者域名,后端反向代理到多个微服务的ip地址。这在微服务规模较少时很有用,立竿见影。但是当某个服务实例较多时效果就打折扣了,比如我一个服务启动了20个实例,突然某一个实例由于某些原因下线,这是方向代理又将客户端(此处客户端不仅仅指用户端,还指所有访问该服务的其他服务,都是客户端)请求转发到该实例上时,就会无法提供服务。所以需要引入服务发现机制,服务正常启动时告诉客户端该实例可以访问,服务下线时告诉客户端该服务已下线,你别再调用我了。服务动态注册,服务发现的实现方式有很多。可以用redis,zookeeper,自己写一个中间组建或者直接使用腾讯,阿里提供的成熟产品。
3.服务优雅退出问题:
微服务架构下,各个微服务可以更好地发布新的版本。当新的实例启动后,如何优雅的将新的实例替换成老的实例呢??直接kill -9??然后再启动新的实例??太暴力了,这是客户端可能直接就报500了。优雅的做法是将要替换的实例流量逐渐减少,直到没有请求进来,待处理完所有请求后,再销毁该实例,同时用新实例去代替,再让流量进来,逐步完成所有老实例的替换。说的挺容易,但要解决好这个问题还是比较难的。
4.服务日志处理:
日志是程序中很重要的功能,将日志打到shell中,将日志保存为文件,或者记录到其他地方(DB)都是很常见的做法。集成式服务还好,就那么几台服务器,我登录上去看就行。但微服务架构下可不行了,几十上百个实例,我要看个日志根本就不知道去哪个服务上去看,出了错都不知道在哪看才是最可怕的。这个时候就需要引入一些新的日志系统进来,阿里云,腾讯云提供了统一的日志收集功能蛮不错的,也可以自己搭建日志系统,常用的是ELK进行日志收集。
5.服务日志格式需要更好的规范:
就比如,服务A调用服务B,说你返回的数据有问题。这是你要查服务B,你可能会一脸懵逼,你的请求参数是什么,没有请求参数我咋个找,我返回给你的是什么等,总不能说这是个玄学问题吧(以前有同事还真这么干过,你给他说你返回的参数有问题啊,只是偶尔,他找不到问题,直接给你来一句这是个玄学问题,当时气得直吐血啊)。由于微服务架构服务之间的调用链路坑你会非常长,比如A调用B,B调用C,C调用D,D调用E,所以全局统一的流水号就特别重要了。在参数进入服务时,打印当前访问的参数,访问接口类型,调用者ip,该实例信息(服务名称,服务所在机房名称,实例名称)等就显得特别的重要。在服务出现异常时,需要打印错误发生的代码模块,代码位置,错误栈信息等。在服务处理完成后需要记录此次相应的的状态,返回值,请求耗时时间等(部分敏感信息可能需要做脱敏处理,还可以对日志信息进行加密,防止信息泄露等)。以后有机会,可以出一篇关于日志格式的相应文章。
6.微服务调用的疑难杂症等:
举个例子,有一天A告诉你,兄弟,你服务响应咋这么慢啊。我一个请求过去了你5秒都没返回呀。你一看,什么鬼,马上去查日志,一切正常啊。最长耗时都不超过100ms,你对他说不可能吧。我这边一切正常。他说着就给你丢来了日志,顺便把请求参数给了你,你试着去调用了一下,没问题啊,很快,才80ms。然后你就很懵逼了。他又把请求流水号发给你,你一查蒙了,兄弟你的请求根本没有进来啊,我这边没有日志。你可能会怀疑是不是网络问题啊??而且这个不是经常的,是偶发的,你可能就更坚定了。慢慢的这种情况不多,但也不少偶尔会出现。你会去问运维,是不是近期集群网络有波动啊。然后运维告诉你,兄弟,网络一切正常啊,并拿出了统计图来打你脸等等。这种问题可能大家偶尔会遇到过,这种问题可能的问题在于(只是说可能),请求通过统一的网关过来,在分发到各个实例中的。不管是轮询给各个实例平均转发请求,还是按照某种权重转发,都有可能将请求转发到某个很忙的实例中。该实例由于处理某种耗资源的请求,服务不能接受其他请求了。也有可能转发不合理,将大量请求转发到了特定几个实例上,造成了热点问题(楼主之前遇到过,由于TCP保持连接数是有限的,客户端在建立连接时,TCP的半连接队列已经满了,这是服务端直接放弃了该请求进入半连接队列,无法建立连接。导致微服务实例没有收到该请求等。但微服务实例要实时上报自身的资源占用等数据供客户端来动态选择调用哪个微服务实例,也是一个不小的问题)。
当然微服务架构还有可能遇到其他疑难杂症问题等,欢迎各位大佬补充。
7.数据孤岛问题:
微服务架构核心在于灵活性,不同的服务可能选用不同的语言,不同的DB来做,哪个性能好,哪个搞得快就用哪个。MySQL,MongoDB,ES,HBase,图数据库等等。在满足功能基础上没有问题,也能快速响应业务需求。但微服务的灵活性也造成了数据的隔离性,所以数据都是在你服务中处理完成。比如A服务负责用户信息相关的,B负责订单相关的,C负责库存相关的,D负责优惠券相关的等。当某一天领导要你做个功能,展示某个用户这一年的使用情况,比如买了多少东西,获得了多少积分,得到了多少勋章,花了多少钱,省了多少钱,对哪些商品更感兴趣等综合报表时,就可能蒙了,这么多的服务,数据分散在各个系统中,我TM怎么拿这些数据呢??你可能会想,不是在各个微服务中嘛,让他们提供接口我来调用呀。你去和各个系统的兄弟沟通,让他们提供相应接口。脾气不好的直接说:“什么屌需求啊,又不是我的事”。脾气好的会说:“没问题,我们给你开发,只是需求优先级较低,我们还忙着其他需求呢”。这个时候你可能会去领导那反馈,排期等。这样也不是不可以,但当需求变化了咋办,我要在返回数据里多加一些字段等,你又得屁颠屁颠去沟通,累死个人了。直接访问别人的库也不行,我的服务数据库,为啥要暴露给你啊,万一我增删了字段或者逻辑改变了,我还得通知你同步,万一不同步这不是我的锅么??这就是微服务架构数据孤岛问题了,各个服务数据隐藏了。上帝视角看不到全部的数据,做用户全生命周期的事就相对的困难,而且你一次性请求那么多微服务的数据,超时了咋办。这个时候,你可能需要引入数仓系统。来将各个微服务的数据统一汇集到数仓,统一查找。但数仓系统(以hadoop举例)又是典型的批处理系统,适合做单次数据导入,不适合数据的实时同步。你拿到的可能是昨天同步的数据,并不是实时数据。为了获得实时数据,又要引入流式计算(spark等),将各微服务的数据实时写到数仓或者ES等。
8.服务向下兼容:
微服务架构的特点是灵活,迅速。一般情况下下游服务需求规划都会领先上游版本,这个时候就需要做到服务的向下兼容。在发布新的版本同时,必须保证兼容老的版本,不影响其使用。但谁来保证老版本可用性呢??靠程序员,测试同学一起保证??功能点一多就有可能覆盖不完全。结果测试时没有问题,一上线了就可能出现报错。这个时候是不是需要引入单元测试和集成测试(不仅是微服务架构,所有的架构都应该需要单元测试和集成测试)??
9.服务间耦合:
微服务的设计思想是高内聚,低耦合。但很多时候在业务系统中经常出现链式调用,A调用B,B调用C,C调用D,D在调用E,而且很多时候在链式调用中中间服务起到的仅仅是参数转换或者透传服务。比如A调用B,B中没有相关数据,要调用C,就把A的参数转换一下甚至直接透传给了C,由C返回给了B,B再返回给A,比如D返回给C要30ms,C返回给B40ms,B返回给A要50ms,加一起就是130ms了。更有甚者会由于服务间粒度划分不清晰,出现交叉调用。A调用B,B调用C,C再调用B,然后C返回给B,B再返回给A。本来的低耦合是要求各个微服务提供独立的访问功能,但B和C相互依赖了。某部分功能C挂了B用不了,B挂了C也用不了,这背离了微服务架构初衷啊。当出现很复杂的链式调用,或者交叉调用,透传参数等。是不是将各个微服务下沉一点,该引入一个数据聚合层??客户端请求到达数据聚合层,由数据聚合层去调用各个微服务,组装数据,拆解循环调用等情况??再由数据聚合层返回给客户端??这是不是有点小中台的意思啦??
10.微服务架构下服务异常恢复:
如A调用B,这是B有bug,导致返回500,这个时候该咋个办??B肯定会去修复bug,但bug修复后,这次请求已经丢了啊,不可能让客户端再重试吧。这个时候就需要引入异常恢复机制,对于某些可以异步化的接口流程。加入异常恢复机制,如将发生异常的请求参数,状态进行保存(可放到DB里,也可以放到队列里),进行自动或手动的回复。在处理完毕后应当通知调用方(可以是回调方式,也可以是异步队列通知方式等),对异常请求进行恢复(异常恢复设计要考虑好,比如某些处理流程是带状态的,而且状态可能已经入库了,这个时候再用同样的参数去请求,相应的数据状态怎么处理,这块是关键呀)。
11.微服务日志统一问题:
前面提到了ELK来自建日志平台,当我们以各个服务名称作为ES index创建日志后,以各个服务纬度去查看日志是方便了不少。但是还是缺乏整个链路的打通。以电商系统举例,需要经历商品选择,下单,支付,库存,优惠券,积分,成就,物流,评价,退换货等流程。能不能一眼就知道该用户的商品处于哪个流程中呢??这就可能就需要以一个全新的角度来串联起整个用户购物流程。将各个服务的日志统一起来,形成用户购物生命周期,加入某一个环境出了问题,也能更方便客服介入为客户处理相关的问题,提升服务质量。
本文只是简单的罗列除了本人觉得在使用微服务架构时应当注意的一些点,微服务架构在使用过程中肯定还有这样那样的问题,由于本人才疏学浅,很多写的不好的或者有错误的地方欢迎各位大佬进行指正哈。对于文中提到的单个缺点问题,有机会我也会分享一些解决方法,欢迎大家提供宝贵的意见和建议,小弟肯定知错就改。学海无涯,继续努力搬砖吧。
网友评论