基于APM实现RPC服务和消息队列的指定消费

 

本文内容是基于公司现有框架整理的一篇专利文章.该框架包含完整的一套DevOps流程,包括工单系统(容器申请、服务部署等)\配置中心\路由配置中心\服务治理平台\消息治理平台\葛朗台(基于Docker+K8S)等.前端

该专利的目的为:在业务场景比较复杂,业务流程比较长而且涉及Dubbo\消息队列等服务调用时,指定服务路由和消息路由等,从而实现服务和消息的指定消费功能,可方便测试或者开发人员的工做.java

转载请说明出处.git

 1.背景

  随着公司业务的发展和增加,一个完整的业务流程涉及到不少服务,各个服务可能同时又由于版本迭代或者其余需求部署了多套环境,致使同一服务存在多个版本,而各个服务之间可能经过RPC服务或者消息队列存在关联,使整个业务流程的服务调用呈现为树状结构,每个分支是一种可能的调用链路。在测试过程当中,测试人员想要按照特定的调用链路进行功能测试变得错综复杂,难以实现。github

  在进行某个服务的功能测试时,不少状况下须要调用指定生产者的RPC服务或者消息被指定消费者消费。对于RPC服务调用来讲,消费者服务调用提供者每每会随机调用一个提供者服务;对于消息队列来讲,消息会被随机的消费者消费,从而达不到调用指定环境的某个服务或者消息被指定服务的消费者消费的目的,为测试带来很大不便,致使工做效率低。apache

当前测试过程存在的问题总结以下:

  1. 服务调用链路没法指定。服务与服务之间调用链路复杂,没法指定具体的服务调用链路,服务与服务之间随机调用,达不到测试目的。
  2. RPC消费者没法指定消费特定的提供者。每一个服务存在多个版本,致使RPC服务提供者会存在多个,当RPC消费者调用提供者时,会随机调用一个提供者的服务,没法实现服务的指定消费功能。
  3. MQ队列消息没法被指定的消费者消费。MQ生产者生产的消息,会被随机的消费者消费,特定消息被指定的消费者消费没法实现。
  4. 完整的服务调用链路没法可视化。对于用户,服务的调用过程彻底透明,没法直观的查看服务调用过程,即服务调用的具体节点信息等。

  针对以上问题,提出了基于应用性能管理(Application Performance Management,简称APM)java agent技术在对业务代码无侵入彻底透明的状况下,结合路由码(Route Code),实现RPC服务(下以Dubbo为例)KafkaRabbitMQ等消息队列的指定消费功能,也称为路由功能,实现了服务调用按照指定的调用链路执行,极大的提升了测试效率和测试的方便性。架构

2.平台框架图

 

架构说明:

  1. 在一般状况下,业务的测试流程是经过前端H5页面或者经过接口工具等触发测试流程。其中整个业务流程可能的涉及的调用链路包括Http、SpringMVC、Dubbo、RabbitMQ、Kafka等。
  2. Squid代理,负责将不论是IP访问仍是域名访问,统一构建出Http Header,置入用户IP或用户标识,即路由码。
  3. 基于现有的服务调用链路,提出路由码(Route Code)概念,结合APM和java agent技术,在对业务代码无侵入,彻底透明的状况下,实现路由码、服务调用链路的层层服务传递和基于路由码的指定消费功能,包括Dubbo指定消费、RabbitMQ队列指定消费和Kafka指定消费。

具体技术方案描述以下:

  1. 首先创建路由配置中心(Route Configure Center),配置路由码(路由码能够是IP或者随机生产的字符串)对应的消费关系等元数据,即携带该路由码的Dubbo消费者或者消息将调用哪些服务提供者或者被哪些消费者消费。经过改造Skywalking(APM),结合TransmittableThreadLocal(在使用线程池等会池化复用线程的组件状况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题),将路由码在服务调用链路过程当中向下传递,开发符合业务流程需求的Snowwalking。
  2. 经过Snowwalking,开发SpringMVC拦截器。对SpringMVC的DispatcherServlet类进行拦截加强,获取请求的Http Header做为路由码并组装链路信息,放入TransmittableThreadLocal中,便于后继服务调用路由码的向下传递。
  3. 经过Snowalking,开发Http的拦截器。对Http请求进行拦截加强,若是TransmittableThreadLocal中存在路由码和链路信息,则将TransmittableThreadLocal内容取出放入Http请求Header中,不然获取当前请求节点IP做为路由码放入TransmittableThreadLocal中,并设置入Http请求的Header中。
  4. 经过Snowwalking,开发RabbitMQ拦截器。对RabbitMQ的生产者和消费者分别作拦截加强。对于生产者,对生产类进行拦截加强,将TransmittableThreadLocal内容取出放入消息的Properties中,将路由码和链路信息向下传递。对于消费者,对消费者的消费方法进行拦截加强,当消费者随机拉取一条消息后,将消息Properties中的路由码取出,根据在路由配置中心配置和消息治理平台(Message Governance Platform,简称MGP,用于配置服务之间消息发布、消费关系等元数据)判断当前消费者是否有消费权限,若是有消费权限,则当前消费者可继续消费该消息,不然,该消费者没法消费该消息,将该消息从新入队到消息队列,再从新从消息队列拉取其余消息进行消费,其中还包括处理特殊状况下的消息丢弃和消息重发等。
  5. 经过Snowwalking,开发Dubbo拦截器。对Dubbo消费者的调用入口方法进行拦截,根据当前TransmittableThreadLocal中的路由码、路由配置中心路由元数据以及服务治理平台(Service Governance Platform,简称SGP,用于配置各个服务发布、消费关系等元数据)生产消费关系元数据,获取当前Dubbo消费者可消费的生产者,过滤Invoker列表,将符合配置的生产者返回给消费者,从而实现Dubbo服务根据路由码实现指定消费功能。
  6. 经过Snowwalking,开发Kafka拦截器。对Kafka的生产者和消费者分别进行拦截加强。对于生产者,对消息发送类进行拦截加强,若是Kafka的版本低于0.11,则从TransmittableThreadLocal中解析出路由码,经过特定格式存入消息的Payload中;若是Kafka的版本高于0.11版本,则将TransmittableThreadLocal中的路由码以及链路信息存入消息的Header中,发送至Kafka。同理,对于消费者,想要实现Kafka的指定消费,前提是经过参数加强或者其余方式,将消费者的group.id隔离成不一样的值,使须要执行指定消费的消费者属于不一样的group,这样每一个消费者均可以消费某个Topic的全部partition。而后对Kafka的消费方法进行拦截加强,若是Kafka的版本低于0.11,则消费服务解析消息Payload,获取路由码,而后经过路由配置中心配置的消费关系,判断当前消费者是否有权限消费该条消息,若是有权限,则消费,不然,跳过该消息,继续执行获取下一条消息进行消费判断;若是Kafka的版本高于0.11版本,则将路由码和链路信息从消息Header中获取出来,解析出路由码,再经过统一路由配置中心配置的消费关系,判断当前消费者是否有权限消费该条消息,逻辑同上。
  7. 若是存在服务其余调用方式,一样能够对其进行加强拦截,实现路由码和调用链路的向下传递等,而保证服务调用链路不间断。
  8. 经过以上对各类调用链路作拦截加强,便可将路由码结合APM、TransmittableThreadLocal和java agent技术在各类业务场景下进行传递,根据路由配置等元数据实现携带指定路由码服务或者消息的指定消费功能。

3.关键技术点

  1.路由码(Route Code)传递

  路由码是指定消费的基础。服务发起者在调用提供者时,会携带路由码,服务提供者提供服务的同时,会经过Snowwalking获取消费者的路由码,并放入TransmittableThreadLocal中,使路由码在链路中进行传递。下图为一种可能的服务调用链路路由码传递时序图:框架

  

  2.Snowwalking

  Snowwalking基于Skywalking改造,结合TransmittableThreadLocal,使路由码在跨线程的状况下,依然能够进行传递。经过Snowwalking对服务链路的各类调用方式进行拦截加强,达到路由码、调用链路信息向下传递和指定消费等功能,对业务代码零侵入且彻底透明。异步

4.效果

  实现该技术方案以后,测试人员或者开发人员只须要进行简单的配置便可实现RPC服务、RabbitMQ和Kafka等消息队列的指定消费功能,知足了测试需求的同时极大的提升了相关工做人员的工做效率,该技术方案简单高效,针对开发人员和测试人员彻底透明且对业务代码无侵入,不会影响生产环境服务和消息的随机消费。工具

  下图给出了一种服务指定消费关系。假设路由配置中心配置路由码code1对应的路由链路为B1,C2,D3,则前端H5页面触发测试流程时,经过Squid代理将路由Code(code1)设置到Http请求的Header当中,当SpringMVC接收到Http请求时,就会被SpringMVC plugin拦截,取出Http请求Header中的路由Code(code1)放入TransmittableThreadLocal中.当SpringMVC须要调用Dubbo服务B时,在服务调用以前就会被Dubbo plugin拦截,根据路由Code(code1)和路由Code配置的服务调用关系,过滤服务B的Invoker List,从而过滤出B1为须要指定调用的服务,进而定向调用至服务B1,服务B调用服务C和服务C调用服务D逻辑相同,从而完成RPC服务的指定消费,其余状况同理,再也不赘述。性能

相关文章
相关标签/搜索