贝壳金控赵文乐:基于 Spring Cloud 的服务治理实践

778 阅读12分钟
原文链接: www.infoq.com

大家好,我是来自贝壳金控的赵文乐,目前主要从事架构方面的工作。今天我想跟大家分享《基于 Spring Cloud 的服务治理实践》。我先简单向大家介绍一下服务治理的概念,然后介绍实际案例中的实践。

服务治理的范围及原因



上图是我简单制作的「服务治理实践过程中遇到的问题和解决方法」,不是非常完全,但也可以代表服务治理的大致范围。

服务治理的范围

在服务治理方面,我们需要解决四个方面的问题:

  1. 服务质量。包括:服务列表、服务性能KPI 、链路监控、依赖监控、故障管理与报警系统等;
  2. 线上治理,包括服务发现(让客户端能够发现这个服务)、服务调度(包括客户端和服务端的负载均衡、服务路由、升降级熔断和监控)和配置管理;
  3. 线下治理,包括开发工具、上线审批、下线通知、服务文档;
  4. 运维生态。如容器云、用户角色权限管理、服务的流程审批。

为什么要进行服务治理?



  • 当服务越来越多时,过去简单记录服务的 end point就越来越复杂。所以注册中心是我们需要做的第一步,在这之后,才是服务发现和客户端定制;
  • 当服务开始越来越复杂,我们就要依赖于管理服务。如果架构师不了解系统里所有服务之间的依赖关系,则需要借助框架自动画出依赖关系并予以管理;
  • 当调用越来越多时,我们需要增加监控、容量规划以及度量;
  • 当依赖变得复杂时,除了了解服务的依赖关系,还要动态的察觉依赖;
  • 一般情况下,下层服务如果反调上层服务,会造成循环依赖,需要SOA做到服务之间互相沟通,使管理更加容易;
  • 如果想要防止文档变得混乱,就需要更集中化的文档管理,让大家能够搜到、看到所有的服务的文档;
  • 如果大家都可以公开调用内部服务,那么就会出现安全问题;
  • 质量问题也会难以保障,需要有非常好的服务监控;
  • 当调用多个服务时,需要做服务聚合。特别是当我们依赖于服务编排时,如果用框架来做会更方便。

服务拆分和治理的原则

服务拆分的原则



当我们在说微服务时,我们是在说:到底微服务的颗粒度要做到多细或多粗。这就需要我们先定义服务的不同分类,可以按照不同的维度来做。如服务业务、服务流程或不同的业务域,这是第一种流程服务,即满足最高层服务的流程。在流程服务下还会出现组合服务,会调用多个其他服务进行封装组合。再下面还会有平台服务 —— 在某业务域下的核心服务。最后是基础服务,它通常没有特别的业务含义,是比较通用的服务。

同时,我们也可以根据服务的属性来分类:

  1. 稳定服务和不稳定/ 易变服务需要隔离;
  2. 核心服务和非核心服务需要隔离;
  3. 非功能服务和功能服务。非功能服务通常更容易复用,会把它放在最底层,由不同的服务来调用;
  4. 高可用服务和容错服务。有些服务能容忍一定错误率,这样的服务不能和高可用服务部署在一起。



所以,我们可以按照以上原则做系统分解:

  • 不同的业务域划分大的业务系统,每个业务调用数据量最大的需要拆分;
  • 风险高、频率高、经常更新的需要拆分的;
  • 经常会被复用的底层服务需要拆分;
  • 服务需要专业技能、专业团队,特别是技术栈不统一时进行拆分。

服务设计原则



服务设计原因包括:

  • 服务无状态,幂等性。在设计微服务时,一般都会从领域模型。基于这些领域来驱动微服务REST API的设计;
  • 服务业务隔离,领域驱动( Domain Driven Design );
  • 服务契约驱动( Design by Contract )。先定义接口,再去做服务的实现;
  • 服务资源隔离(数据库,线程池等)。如果不隔离服务的数据库,就很难知道有没有其他服务在调用我们的数据库,至少数据库的用户是需要隔离,不同用户要有不同的权限;
  • 故障可隔离(熔断机制)。Spring Cloud里有Hystrix框架就可以很好的解决这个问题。

服务治理原则包括:

  • 服务SLA;
  • 服务需自治;
  • 服务可开关,降级,限流,动态调整负载路由;
  • 服务可监控,可统计,提供Metrics和Health Check( Metrics Driven Design );
  • 服务文档和版本管理;
  • 服务权限控制;
  • 服务调用链可监控。

Spring Cloud的服务治理



Spring Cloud 组件

上图中比较核心的组建包括:

  • 服务注册提供很多选型。默认是Eureka,还支持Consul和Zookeeper服务注册;
  • 服务调用,REST API通常用Feign Client做服务调用,集成客户端的负载均衡,所以Feign Client在服务治理中非常重要;
  • 服务路由和服务过滤,在Spring Cloud提供的route API、gateway之类的工具;

其他还包括:

  • 服务监控,在 Spring Cloud环境下用的较多的是Hystrix —— 监控控制台,集成的Turbine可以做跨集群的监控;
  • 配置中心,Spring Cloud默认提供的配置管理是通过地址文件进行管理,也支持诸如Zookeeper之类配置中心;
  • 安全控制集成SpringSecurity,它本身不是属于Spring Cloud的范畴,但会提供SpringSecurity Starter,帮助我们快速的建立权限管理;
  • 用 Spring Cloud Sleuth做分布式的链路监控,集成Zipkin之类的框架。

Spring Cloud 存在的问题和痛点



  1. 配置管理。Spring Cloud的配置管理比较简陋,没有特别好的配置管理中心,也没有共享配置。另外,Spring Cloud 配置不支持灰度;
  2. 网关(API Gateway)。网关需要做很多二次开发,没有动态路由;同时,Zuul做不了服务编排,而在市场上也没有一个很好的服务编排的框架;
  3. 服务跟踪。Sleuth框架不成熟。如果跟一些比较成熟的APM框架相比,它是非常欠缺的;
  4. UI。spring cloud 的UI界面非常分散,像Hystrix、eureka、tubine、zipkin都有自己的界面。但这些缺乏集中的管理,用户体也普遍比较差、感觉比较简单,跟商业级的服务治理平台无法相比。

如何改善

更换配置中心。携程的Apollo是一个更好的选择。它里面的很多功能都是原生Spring Cloud配置中心不支持的。所以建议大家尝试一下比较成熟的配置中心。



因为 API Gateway在Spring Cloud中没有操作界面,所以我们就为之定制了专属界面,让它能够管理不同的路由规则。我们还开发了一系列Filter,可以在API Gateway里做签名检查和解密。同时,我们还集成了自己的账户系统和单点登录,支持不同的登录方式。

除此之外,我们集成了用户中心( Accountservice )。因为当 API Gateway开放给渠道用户或合作伙伴用户时,通常没有交互,所以我们就需要通过参数的自动抓取匹配用户,据此判断这个用户是否已经注册。如果还未注册,我们就会自动注册。同时,当一个潜在用户使用我们系统、调用API时,我们就可以通过这种方式把硬件指纹记录下来,后台会给这些用户打标签,我们就可以针对这些用户做push等营销手段。

最后,还有一些前置Filter用于抽取数据。当API请求时,会异步通过日志抽取报文做数据清洗,通过ETL写到数据仓库里。

API gateway的动态路由



举个例子,比如我们把年龄小于30岁的男性路由到一个不同的endpoint ,我们在这过程中会在请求头、请求参数或请求头中通过Json Parse抽取参数和数据转换。我们可以从body里第一个customer对象的ID得到uid,之后保存到上下文中,输出到output,当我们指定endpoint为另外一个URL时把UID这个参数传过去。

还有一种是报文的转换,即Payload Transformation。这个技术其实在很久以前就已经存在了,在ESB、SOAP时代,我们通常会利用XML来做报文的转换。所以现在通常用来做报文转换的工具是Json、Json Paser、Velocity Template、FreeMarker等。还有一些协议的转换,我们内部有很多API都是基于dubbo或者是其他的一些RPC协议。所以当收到外部REST API请求时,我们会做一个协议、格式的转换。



在上图中,入参是比较复杂的Json,我们通过Input Mapping模板上逻辑输出变量,嵌入到另外的Json对象中。如果我们在内部有一套比较标准的API,可以通过这种方式适配到外部不同的API。这样便集成了规则引擎,可以做一些比较基本的服务编排。

一体化的服务监控和跟踪



在Spring Cloud里提供了很多不同的服务监控工具,利用这些工具可以做服务的业务监控和埋点,来收集各种Metrics。当我们发送消息时,我们会在适当的地方做埋点,收集数据,最后再把这些集成起来,做报表展示和告警。所以整个这套服务监控和跟踪都是一体化的。



我们在做中间件埋点时,可以有许多的选择,比如JDK proxy、http client、Servlet filters、Spring MVC handler都可以添加埋点,但我们更多会在Feign Client提供一些拦截器,当服务调用时,会有一些不同的event。

在DB里,我们用的比较多的是Druid datasource filter,它提供了很多扩展,我们可以在这里边做SQL查询的埋点,记录每条SQL的响应时间和调用频次。同时,Mybatis也可以做埋点,定制一些插件。

服务监控的整体架构



过去我们使用日志做服务监控的数据收集,大家都知道也有不少的服务监控都是基于上报的API。但我们通过日志的方式收集数据对应用的性能比较友好,不会因为我们埋点影响到业务。同时,耦合度也比较低,只是分析度量数据。通过不同的Instruments写到日志里。最后通过Logstash到Kafka进入ElasticSearch,基于这些查询可以快速生成简单的报表。



以上所说的内容,如果都只是停留在框架级别,用户和程序员根本看不到服务治理的概念。所以我们做了一套服务治理平台,可以看到所有服务治理内容。同时,我们还把配置中心嵌到了服务治理平台中,将服务网关管理、Rabbit MQ消息队列管理、通过消息队列业务ID查询消息轨迹以及一些项目管理相关的离线服务治理等功能集成在一起。

Q & A

问:下层服务和上层服务指的是什么?

答:所谓的下层服务,就是底下平台级的服务。比如你有一个发短信的服务,如果这个服务跟你的账户体系耦合在一起,它就是反向调用,如果在短信服务里需要到会员中心获取手机号,这就是不合理的设计,就是下层服务调上层服务的例子。

问:服务调用是每个服务各自写一个FeignClient,还是由服务方提供统一的jar包?

答:我们现在做法是:在定义服务接口时,这个服务接口就是FeignClient,然后把服务接口和它领域的对象封装成统一的jar包,作为服务方提供。之后,客户端用它来调用就可以了。在调用过程中,框架里的拦截器会做埋点、注入及监控的工作。

问:老的服务如何调用FeignClient?

答:用延伸注解来实现。FeignClient在Spring Cloud用的是比较新的OpenFeign注解,支持一些特殊功能。比如插入自己的http client和做很多拦截器,老的FeignClient不是很友好,而且它跟Spring mvc的注解也不一致,但是作为一个很老的服务,如果要调用FeignClient的话,我们通常会把所有FeignClient用到的class打成一个大的jar包,为这些老的服务实现调用。

问:如果有机会是不是直接选择自研好一点?

答:作为开发人员或架构师,每个人都想自研,确实也有很多团队自己做自研框架。但自研的问题是从入门到融会贯通的时间。虽然Spring Cloud现在十分简陋,但上手就可以用。如果在整个团队里都用Spring Cloud,可以很快地做一些简单的服务治理,然后再慢慢的优化这个过程。还有一个原因,Spring Cloud在行业里的接受度比较高,大家的学习曲线比较短,通常自研的框架很多工程师可能不太接受或不太信任。