微服务架构有一个核心特征:采用REST进行通信。微服务之间通过网络进行通信,不通过共享文件、数据库、内存等方式进行通信。亚马逊最早引入微服务的时候,强制所有开发团队必须通过网络的方式开放服务能力,不允许任何其他方式进行能力开放。REST作为一种轻量级的网络通信方式,在微服务架构下被广泛采用。
为了满足微服务团队之间的交流,OPENAPI Initiative制定了Open API标准,该标准综合考虑了不同语言、REST等接口习惯,定义了完整的跨平台接口标准。CSE是最早采用和实现该标准的微服务开发框架,并将“以契约为中心”的概念,应用于整个微服务开发实践。下图展现了软件开发周期的各个环节“契约”所扮演的角色。
1.管理、设计环节
微服务的能力全部通过REST接口开放,从管理和设计角度,提供了一个很好的管控点。在实际的项目团队里面,需求分析和设计人员首先对微服务的接口进行设计,并创建独立的git库存放接口定义。通过做好git库的权限管理,并将Code Review流程纳入接口项目的管控,管理者能够保证软件规格是按照设计要求进行的。
CSE开发的微服务,启动的时候会分析微服务所有的对外接口,并将契约信息注册到服务中心,服务之间的调用需要严格满足契约的要求,并提供了管理界面,可以方便的查询契约。
通过静态的方法管控规格一般是不够的,管理者还可以使用契约生成测试代码,书写一定的验收测试用例,或者将契约提交给第三方进行验收测试。
为了更好的支持接口管控和微服务实现分离,需要对代码组织方式提出一定的要求。[基于CSE的微服务工程实践-Native API先行](https://bbs.huaweicloud.com/blogs/7c03a4793cd011e9bd5a7ca23e93a891) 提供了一个可以供开发者参考的项目。
2.开发、测试环节
基于契约,可以开发代码生成工具,生成开发项目或者测试项目。开发者只需要按照接口要求,实现业务逻辑和测试逻辑。
[单体应用微服务改造实践](https://bbs.huaweicloud.com/blogs/17ad483f325f11e9bd5a7ca23e93a891) 里面提到了自动化测试在保障软件质量方面起到的关键作用,也提到了如何通过开发测试微服务进行更好的集成测试。通过工具生成开发、测试项目,使得开发人员能够在开发业务逻辑的时候,同步开发测试微服务。
3.部署环节
契约能够帮助用户更好的管理能力开放。“API经济” 打开了公司能力通过API的方式对外界开放的窗口,很多公司通过提供接口给其他公司使用,并通过计费系统,按调用次数或者时长进行收费。能力开放的核心组件是APIG,APIG能够帮助用户完成开放接口的配置、计费以及常见的管控,比如流量控制等。有了契约,完成这些配置,不需要人工输入,只需要将契约导入,勾选对应接口的规则就完成了能力开放。
4.治理
治理是本文讨论的重点。谈到微服务,就会谈到微服务治理。不同的地方对于微服务治理有不一样的定义。本文认为,保障微服务可靠运行的机制集合,共同构成了服务治理。为了更好的理解这个概念,下面分几种最常见的场景进行分解,帮助理解什么样的服务治理才是最好的,同时也可以帮助用户理解在采用其他技术,比如Istio在微服务治理能力方面的差异。
4.1负载均衡管理
弹性扩缩容、服务永远在线是微服务的核心价值和特征。弹性扩缩容意味着通常运行环境一个微服务都会有多个版本,永远在线则意味着运行环境不可避免的需要考虑多个微服务版本并存的状况。负载均衡管理器的核心功能是将用户请求转发到正确的实例上面去。下面假设微服务A访问微服务B。B的实例采用B11, B12, B21, B22这样的方式描述,比如B11表示微服务B版本1的实例1,B21表示微服务B版本2的实例1,以此类推。
负载均衡策略考虑的是在目标微服务存在多个实例的情况下,采用什么规则转发请求,常见的有Round Robin,Rondam和基于权重的负载均衡策略。负载均衡策略的配置一般是针对目标服务进行配置的,CSE允许给每个微服务配置独立的负载均衡策略:
```
cse:
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。CSE有了契约,这个转发过程是框架自动完成的,将开发者从处理接口兼容场景下的转发问题中解放出来。
在修改接口语义的情况下,这种情况会稍微变得更加复杂一点。比如B1的op1接口存在bug,修复后,升级到B2,升级后,期望A对于B的访问,都到B2,而不到B1。这个时候,就需要根据灰度规则手工引流。基于契约,CSE能够允许开发者基于请求参数定义转发规则,比如:
```
cse.darklaunch.policy.B={"policyType":"RULE","ruleItems":[{"groupName":"test","groupCondition":"version=2","policyCondition":"operation=op1","caseInsensitive":true}]}
```
上面的配置,将operation参数值为op1的请求,转发到微服务B的版本2实例上去。其中operation通常是接口参数名称,用于将接口参数值不同的请求转发到不同的实例,比如将userid=Chengdu的请求,转发到B2。CSE对接口参数进行了扩展,在InvocationContext里面的参数名称也可以用于灰度规则,这样就实现了在bug修改过程中的对于某个接口的灰度引流。
A将请求转发给B的时候,可能存在故障。有些故障是可恢复的,比如短暂的网络波动,导致请求失败,重试一下就可能成功;有些故障是不能恢复的,或者恢复时间较长,比如B11由于接收到大量请求,出现大量超时,如果B11继续接收新请求,那么会加重超时情况。对于可以恢复的故障,需要能够进行重试;对于不可恢复故障,则要考虑对于故障实例的隔离。CSE在负载均衡方面,对故障进行了分类,以针对具体的异常情况,进行重试和隔离。
负载均衡策略也会影响到性能。在容灾部署模式下,微服务实例被部署到不同的AZ上面,A在访问B的过程中,需要考虑当前A的实例所在的AZ,并将请求转发到同AZ的B实例上面去。AZ亲和能够提升性能,在本AZ无实例可用的情况下,还需要将请求转发到其他AZ,以实现可靠。
4.2流量控制和隔离仓
流量控制通常分为客户端流控和服务端流控。流量控制的难题是无法在产品开发的时候,就预测微服务的处理能力。有些用户场景,需要根据业务需要,人为降低部分接口的调用频率,以保证重点业务的资源能够得到及时满足。CSE基于契约,提供了非常简单易用的限流配置能力,可以控制具体到接口的流量。
```
cse.flowcontrol.Provider.qps.limit.[ServiceName].[Schema].[operation]=100
cse.flowcontrol.Consumer.qps.limit.[ServiceName].[Schema].[operation]=100
```
流量控制机制有很多不足。在微服务架构下,进行全局限流需要在考虑性能和准确性方面进行折中,设计很复杂的算法,所以通常没有提供全局限流能力。服务处理能力和运行环境有关系,需要很复杂的并发测试工具,来识别服务的处理能力,针对接口级别的限流策略面临着在实际环境中无法实施的尴尬境地。进行限流控制的本质原因,是解决计算资源分配的需要,所以从实际微服务实践中,进行必要的资源隔离,是比限流更有效的手段,通过资源隔离限制资源占用,不需要对处理能力进行预估,让业务系统尽最大能力处理更多的请求而不至于导致全局崩溃。通过线程池进行资源隔离,是非常有效的手段。基于契约,CSE能够非常方便的给具体接口分配独立资源。
```
cse.executors.Provider].[Schema].[operation]: custom-executor
```
线程池隔离的最早实践来源于Hystrix,CSE也通过biz-keeper模块集成了相关功能。但是Hystrix具体在使用的时候,存在性能差、调用栈过长并隐藏了底层错误导致无法定位问题等,Hystrix本身在实际业务系统中无法发挥本来设计上要解决的问题。CSE提供的线程池能力,在几乎不降低业务性能的情况下,提供了很好的资源隔离机制。
线程池排队队列的大小,是采用线程池隔离需要考虑的一个重要问题。设置合理的队列大小,及时丢弃请求(新来的请求,在队里里面排队超时的请求)对于系统可靠性和故障快速恢复显得非常重要。否则突发的大规模请求,可能导致系统瘫痪,长时间无法自动恢复。
5.运维
在运维阶段,契约仍然发挥非常重要的作用。基于契约,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数据,可以开发强大的的运维工具,展现系统接口运行状况,快速查询接口运维数据。
CSE的metrics数据深入底层运行环境,能够读取和统计接口在队列排队、业务处理等非常细粒度环节的时延,从而为分析“毛刺”问题提供了数据支持。(“毛刺”是同事在分析性能问题的时候,对于部分请求某些时刻时延偏高,但是又不容易模拟重现现象的生动描述)