来源:Docker
容器、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
界面相容是微服务管理里面非常常见的问题,虽然可以通过管理要求,让开发者符合一定的规则,比如:
不允许删除界面不允许修改界面原型定义只允许新增界面,或者对老界面的bug进行修改但是微服务处理请求转发仍然面临问题,比如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
]=
100
servicecomb
.
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
=
CONSUMER
servicecomb
.
invocation
.
operation
=[
ServiceName
].[
Schema
].[
operation
]
servicecomb
.
invocation
.
status
=
200
servicecomb
.
invocation
.
type
=
latencyDistribution
servicecomb
.
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模组,这个模组对任何微服务发现的结果就是一个域名。
Q&A
Q:ServiceComb适合多大的研发团队使用?或者说多大的业务量可以考虑使用微服务框架?
A:和研发团队规模没有关系,实际上小的研发团队一般都会选择适合自己的框架,而不是自己研发。专案规模大小更多的影响的是软件工程过程,即要管控的东西的多少,团队越大涉及的工具集合也会越多。
Q:微服务是怎么能够帮助研发团队去实现DevOps的?
A:微服务实际上是最适合DevOps的,因为更容易组建小的披萨团队。DevOps需要更多的工具支撑,今天的内容没详细涵盖这一部分。
Q:COMB支援哪些常见的协议?
A:支援最好的是REST(HTTP/HTTP2+JSON),还支援基于TCP+Protocol Buffers的协议。
Q:对于大企业的研发团队和中小企业团队来讲,对采用微服务能够获得的收益期望应该是不同的吧?
A:期望上讲每个团队应该都是有差异的。有些期望改善效率,有些期望降低复杂度,有些解决复用问题,有些想加快团队创新和试错速度等。因为微服务实施必须有配套的工具集合才能够更好的管理大规模微服务,所以小团队在没有这些工具的时候,实际上在开发过程和部署上和以前的开发模式并没有特别大的差别。
Q:Istio已经成为了事实上的Mesh标杆,COMB在功能上与Istio多有重合,你认为COMB前景如何?
A:Istio实际上是支援很多扩充套件的,能够支援替换Mesher部分。COMB目前的定位并不是取代Istio,而是期望能够扩充套件其功能。当然mesher还有很多其他功能,可以用于其他Istio不适用的场景。
end:如果你觉得本文对你有帮助的话,记得点赞转发,你的支援就是我更新动力。