其他分享
首页 > 其他分享> > 微服务框架解决的那些被开发人员忽视的问题

微服务框架解决的那些被开发人员忽视的问题

作者:互联网

 刘宝 分布式实验室 

图片

容器、Mesher(Istio等)、微服务共同构成了弹性软件架构的基础。围绕弹性软件架构的技术创新层出不穷。通过识别这些技术的边界,能够帮助开发者更好的进行技术选型,更好的组合不同的技术为产品服务。本文结合作者在 ServiceComb[1] 项目中的实践,从系统可靠性、运维、软件工程能力提升等方面,分享一些被开发人员忽视的技术和管理问题。Mesher无法解决的微服务问题

图片


基于元数据的服务治理
Mesher的核心功能是服务发现和服务治理,微服务框架的主要功能也是这两部分,从功能上看,他们只是工作方式不同。下图是Istio架构图[2]: 
图片
可以看出它的服务发现工作在TCP层,在没有额外应用配置的情况下,svcA对于svcB的了解,只有TCP报文里面的相关信息,服务发现完成的功能可以简单的概括为请求hostname为svcB的请求,转发到svcB的实例,Envoy能够做的,除了按照一定的负载均衡策略(比如轮询、权重等)进行路由转发,不能够做其他控制,这种情况严重限制了Mesher的治理能力。
为了提升治理能力,需要更多的元数据信息,这些元数据信息可能来源于微服务之间的协议,比如服务发现可以工作在HTTP层,这样Envoy就可以读取cookie信息,实现灰度发布;还有些元数据来源于部署文件,比如在部署svcB的时候,描述这个服务的版本为v1。在“将用户ID为user1的实例转发到v1版本实例”这样一个简单的灰度场景,就需要用到协议层元数据和部署元数据。
“元数据”的多少定义了Mesher能够处理的治理能力集合。多数开源的微服务框架,都拥有比Mesher更多的元数据信息,因此能够做更多的治理控制。ServiceComb是目前元数据管理最完善的微服务框架,除了微服务描述文件microservice.yaml里面定义了微服务名称、版本号、应用等基本属性外,还包含接口描述信息(即通过Open API描述的接口信息)。ServiceComb能够基于接口描述信息做更多的服务治理。 下面以一个接口兼容转发的场景为例,简单介绍下契约在服务治理方面的作用。
ServiceComb基于接口元数据的路由管理
弹性扩缩容、服务永远在线是微服务的核心价值和特征。弹性扩缩容意味着通常运行环境一个微服务都会有多个版本,永远在线则意味着运行环境不可避免的需要考虑多个微服务版本并存的状况。负载均衡管理器的核心功能是将用户请求转发到正确的实例上面去。下面假设微服务A访问微服务B。B的实例采用B11、B12、B21、B22这样的方式描述,比如B11表示微服务B版本1的实例1、B21表示微服务B版本2的实例1、以此类推。
负载均衡策略考虑的是在目标微服务存在多个实例的情况下,采用什么规则转发请求,常见的有Round Robin,Rondam和基于权重的负载均衡策略。负载均衡策略的配置一般是针对目标服务进行配置的,ServiceComb允许给每个微服务配置独立的负载均衡策略:

servicecomb:  loadbalance:    B:      strategy:        name: RoundRobin


接口兼容是微服务管理里面非常常见的问题,虽然可以通过管理要求,让开发者符合一定的规则,比如:


但是微服务处理请求转发仍然面临问题,比如B2相对于B1,新增了接口op2,也就是B11,B12存在接口op1,B21、B22存在接口op1、op2,当A请求op1的时候,可以将请求转发给B11、B12、B21、B22,当A请求op2的时候,只能将请求转发给B21、B22。负责均衡器需要通过契约识别微服务B的哪个实例具备op1和op2。有了契约,这个转发过程是自动完成的,将开发者从处理接口兼容场景下的转发问题中解放出来。
在修改接口语义的情况下,这种情况会稍微变得更加复杂一点。比如B1的op1接口存在bug,修复后,升级到B2,升级后,期望A对于B的访问,都到B2,而不到B1。这个时候,就需要根据灰度规则手工引流。基于契约,CSE能够允许开发者基于请求参数定义转发规则,比如:

servicecomb.darklaunch.policy.B={"policyType":"RULE","ruleItems":[{"groupName":"test","groupCondition":"version=2","policyCondition":"operation=op1","caseInsensitive":true}]}


上面的配置,将Operation参数值为op1的请求,转发到微服务B的版本2实例上去。其中Operation通常是接口参数名称,用于将接口参数值不同的请求转发到不同的实例,比如将userid=Chengdu的请求,转发到B2。ServiceComb对接口参数进行了扩展,在InvocationContext里面的参数名称也可以用于灰度规则,这样就实现了在bug修改过程中的对于某个接口的灰度引流。
ServiceComb的流量控制和隔离仓
流量控制通常分为客户端流控和服务端流控。流量控制的难题是无法在产品开发的时候,就预测微服务的处理能力。有些用户场景,需要根据业务需要,人为降低部分接口的调用频率,以保证重点业务的资源能够得到及时满足。ServiceComb基于契约,提供了非常简单易用的限流配置能力,可以控制具体到接口的流量。

servicecomb.flowcontrol.Provider.qps.limit.[ServiceName].[Schema].[operation]=100servicecomb.flowcontrol.Consumer.qps.limit.[ServiceName].[Schema].[operation]=100


流量控制机制有很多不足。在微服务架构下,进行全局限流需要在性能和准确性方面进行折中,所以通常没有提供全局限流能力。服务处理能力和运行环境有关系,需要很复杂的并发测试工具,来识别服务的处理能力,针对接口级别的限流策略面临着在实际环境中无法实施的尴尬境地。进行限流控制的本质原因,是解决计算资源分配的需要,所以从实际微服务实践中,进行必要的资源隔离,是比限流更有效的手段,通过资源隔离限制资源占用,不需要对处理能力进行预估,让业务系统尽可能处理更多的请求而不至于导致全局崩溃。通过线程池进行资源隔离,是非常有效的手段。基于契约,ServiceComb能够非常方便的给具体接口分配独立资源。

servicecomb.executors.Provider].[Schema].[operation]: custom-executor


微服务的可靠性和弹性
Mesher技术在服务发现方面提供了一些治理能力,但是Mesher技术并没有直接让微服务本身变得更加可靠,也没有解决微服务自身的弹性问题。
以最常见的服务超时问题举例。在实际的应用场景中,微服务的各个实例都是对等的,当一个实例出现超时的时候,其他实例一般也会出现超时,这些超时通常是由于用户请求分布不均匀,突发流量超出系统处理能力导致的。Mesher的治理能力可以在一个实例出现大量超时的时候,隔离这个实例,但是这种隔离,反而加重了其他实例的负担,导致系统恢复时间延长。
结合实际情况,解决这类问题通常有几个思路。一方面是对微服务占用资源较多的接口,分配独立的处理资源,不占用其他能够快速处理的接口的资源,避免排队;另外一方面,需要设置合理的队列大小,对长期排队的请求进行丢弃。
线程池隔离的最早实践来源于Hystrix,ServiceComb也通过biz-keeper模块集成了相关功能。但是Hystrix具体在使用的时候,存在性能差、调用栈过长并隐藏了底层错误导致无法定位问题等,Hystrix组件在实际业务系统中无法发挥本来设计上要解决的问题。ServiceComb提供的线程池能力,在几乎不降低业务性能的情况下,提供了很好的资源隔离机制。
线程池排队队列的大小,是采用线程池隔离需要考虑的一个重要问题。设置合理的队列大小,及时丢弃请求(新来的请求,在队里里面排队超时的请求)对于系统可靠性和故障快速恢复显得非常重要。否则突发的大规模请求,可能导致系统瘫痪,长时间无法自动恢复。
Mesher也不能够使得微服务自身具备弹性。拥有水平扩容能力,是微服务的一个重要设计指标,一个无状态的微服务具备更好的弹性,而有状态服务的弹性相对而言就差很多,通常可以采用读写分离等技术,保证写的一致性,而保证读操作具备弹性。弹性设计是微服务很重要的一个课题,这里不深入讨论,感兴趣的读者,可以阅读《持续演进的Cloud Native云原生架构下微服务最佳实践》作者王启军的“可扩展设计”系列文章。
此外,微服务开发框架还要能够处理分布式事务等问题,能够提供有效手段保证数据一致性。
微服务开发框架在软件工程能力方面的作用

图片


作为开发者,更加关注如何实现功能;作为管理者,关注的问题是多方面的。短期的问题包括软件的设计规格被满足,规格能够被验证;长期的问题包括开发过程可重复,软件系统能够被继承、被重用;还包括如何组织能力差异大的开发团队进行紧密协作,高效分工。满足这些管理要求,可以进行文化建设、制度建设。但是最好的制度,没有工具支撑,都会影响开发者效率,最终流于形式得不到正确的实施。
辅助管理的工具有很多,包括项目和缺陷管理工具,测试工具,CI/CD工具等。ServiceComb在微服务框架上也提供了独特的软件工程实践能力支撑。概括起来就是“以契约为中心”,下图展现了这个工程能力: 
图片
管理、设计环节
微服务的能力全部通过REST接口开放,从管理和设计角度,提供了一个很好的管控点。在实际的项目团队里面,需求分析和设计人员首先对微服务的接口进行设计,并创建独立的Git库存放接口定义。通过做好Git库的权限管理,并将Code Review流程纳入接口项目的管控,管理者能够保证软件规格是按照设计要求进行的。
ServiceComb开发的微服务,启动的时候会分析微服务所有的对外接口,并将契约信息注册到服务中心,服务之间的调用需要严格满足契约的要求,并提供了管理界面,可以方便的查询契约。
通过静态的方法管控规格一般是不够的,管理者还可以使用契约生成测试代码,书写一定的验收测试用例,或者将契约提交给第三方进行验收测试。
为了更好的支持微服务接口和微服务实现分离,需要按照一定的目录规范组织代码。《基于CSE的微服务工程实践-Native API先行[3]》提供了一个可以供开发者参考的项目。
开发、测试环节
基于契约,可以开发代码生成工具,生成开发项目或者测试项目。开发者只需要按照接口要求,实现业务逻辑和测试逻辑。
《单体应用微服务改造实践[4]》里面提到了自动化测试在保障软件质量方面起到的关键作用,也提到了如何通过开发测试微服务进行更好的集成测试。通过工具生成开发、测试项目,使得开发人员能够在开发业务逻辑的时候,同步开发测试微服务。
部署环节
契约能够帮助用户更好的管理能力开放。“API经济” 打开了公司能力通过API的方式对外界开放的窗口,很多公司通过提供接口给其他公司使用,并通过计费系统,按调用次数或者时长进行收费。能力开放的核心组件是APIG,APIG能够帮助用户完成开放接口的配置、计费以及常见的管控,比如流量控制等。有了契约,完成这些配置,不需要人工输入,只需要将契约导入,勾选对应接口的规则就完成了能力开放。
治理
谈到微服务,就会谈到微服务治理。不同的地方对于微服务治理有不一样的定义。本文认为,保障微服务可靠运行的机制集合,共同构成了服务治理。在比较Mesher和微服务开发框架的章节,已经分析了契约在治理方面的一些能力,这里不再重复。
运维
在运维阶段,契约仍然发挥非常重要的作用。基于契约,metrics可以生成接口级别的时延分布数据。

servicecomb.invocation.role=CONSUMERservicecomb.invocation.operation=[ServiceName].[Schema].[operation]servicecomb.invocation.status=200servicecomb.invocation.type=latencyDistributionservicecomb.invocation.scope=[1,3)


基于契约和metrics数据,可以开发强大的的运维工具,展现系统接口运行状况,快速查询接口运维数据。
ServiceComb的metrics数据深入底层运行环境,能够读取和统计接口在队列排队、业务处理等非常细粒度环节的时延,从而为分析“毛刺”问题提供了数据支持。(“毛刺”是同事在分析性能问题的时候,对于部分请求某些时刻时延偏高,但是又不容易模拟重现现象的生动描述。)
容器、Mesher和微服务的有效配合

图片


上面重点讨论了Mesher和微服务,容器解决的问题相对来说比较容易理解。容器管理应用的弹性伸缩,在搭建测试环境、快速部署等方面都带来很大的便利。有很多基于虚拟机的历史遗留系统,自身开发了强大的部署管理系统,依然具备生命力,然而容器技术使得这个过程变得更加简单和标准了。
Mesher无法解决应用自身的可靠性和弹性,然而Mesher在治理能力方面还可以进一步增强。通过扩展Mesher的元数据管理能力,Mesher也能够实现大多数微服务开发框架提供的治理能力。ServiceComb项目最近在讨论将基于Go语言开发的mesher项目[5]纳入微服务解决方案的一部分,新增加的mesher项目规划了和Istio的互协作,同时规划了把接口契约纳入元数据管理范围,以扩充mesher的治理能力。微服务开发框架也没有能够完全解决应用自身的可靠性和弹性,很多问题还是需要开发者结合实际情况处理。总体看,Mesher在治理能力方面能够接近微服务框架提供的能力,而微服务框架可以在解决应用可靠性和弹性方面提供更多的支持。
从当前情况看技术选型,如果项目规模不复杂,或者开发语言统一,可以选择微服务开发框架,而不选择mesher技术,微服务开发框架能够帮助开发者更加灵活的处理业务问题,能力更加成熟,扩展性也更好。如果项目规模复杂,涉及多种开发语言,可以选择将服务发现和治理采用mesher技术承载,具体的开发语言仍然选择一个合适的微服务开发框架,不启用对应的服务发现模块。大多数微服务框架都支持去掉服务发现功能,比如Spring Cloud去掉服务发现后,就直接使用spring boot;ServiceComb开发框架去掉服务发现后,就是一个自定义的loadbalance模块,这个模块对任何微服务发现的结果就是一个域名。


标签:实例,服务,框架,开发人员,ServiceComb,接口,忽视,Mesher,契约
来源: https://blog.51cto.com/u_15127630/2768591