5.分布式服务架构:原理、设计与实战 --- 基于调用链的服务治理系统的设计与实现

	基于全局的唯一流水ID将一个请求在分布式系统中的流转路径聚合,然后使用调用过程中传递和保存的SpanID将聚合的请求路径
通过树形结构进行展示。

5.1 APM系统简介 
	5.1.1 优秀的开源APM系统 
		1.Ponpoint
			特性:
			1.安装的采集端代理组件对原有的服务代码无入侵
			2.对性能影响比较小,只增加约3%的资源利用率
			3.根据请求的流量自动生成微服务调用的拓扑结构
			4.通过可视化结构显示网络微服务调用的关系,下钻(指点击进入子页面)可显示该服务的详细信息页面
			5.实时监控活动线程,并通过图像的形式展示
			6.可视化的显示请求发生的位置,帮助快速定位问题
			7.可以收集和显示cpu,内存,垃圾回收,请求吞吐量和JVM运行情况等
			8.使用异步线程推送和udp协议减少对程序处理性能的影响

		2.Zipkin
			能够收集服务调用的时序数据,解决微服务架构中定位超时等性能问题。它通过在应用程序中挂在字节码增强库来将实时数据汇报给Zipkin。
		Zipkin UI 可以通过图形的方式显示调用链中有多少请求经过系统的某一节点,并构造和显示系统的拓扑结构。如果定位微服务系统交互的超时问题,
		则可以根据服务节点,调用链长度,时间戳等信息过滤和查找想找到的调用链,一旦找到一个调用链,则能清晰的看到调用链是否有问题以及问题在哪里。

		3.CAT
			是美团点评开源的一款实时的应用和性能监控系统,它的系统原型和理念来源于eBay的CAL系统,并且增强了CAL系统的核心模型,添加了丰富的报表功能。

	5.1.2 国内商业APM产品的介绍 
		1.听云
		2.博睿
		3.OneAPM
		4.云智慧

5.2 调用链跟踪的原理 
	5.2.1 分布式系统的远程调用过程 
		服务于一个用户的请求内部服务调用的结构是一个树形结构,树节点是整个架构的基本单元,每个节点是一个独立的服务节点。在谷歌的Dapper论文中,每个节点
	都对应一个span,节点之间的连线表示span和它的父span之间的关系,具体表现为一次调用请求和响应的调用关系,后面我们把描述一次请求调用和响应组成的数据
	叫做调用信息。
		我们通过什么手段标识响应和请求是一对呢?
		谷歌的Dapper论文通过增加应用层的标记来对服务化中的请求和响应建立联系,例如:它通过http协议头携带标记信息,标记信息包括标识调用链的唯一流水ID,
	这里叫做TraceID,以及标识调用层次和顺序的SpanID和ParentSpanID。

		一次远程调用的过程可以分为4个阶段,每个阶段对应一种远程调用的信息类型:
		1.调用端发送请求的调用信息
		2.被调用端接收请求的调用信息
		3.被调用端发送响应的调用信息
		4.调用端接收响应的调用信息

		上面每种类型的远程调用信息包含:调用端或者被调用端的IP,系统ID;本次请求的TraceID,SpanID和ParentSpanID;时间戳,调用的方法名称及远程调用信息的类型等。
	其中,远程调用信息的类型除了上面4个阶段对应的4种类型,在第3个阶段和第4个阶段又进一步分为响应成功和异常响应,我们也为主子线程间的调用增加了一种调用信息类型。
		我们为远程调用信息的类型定义的枚举类型如下:
		1.RPCPhase.P1:调用端发送请求的调用信息类型
		2.RPCPhase.P2:被调用端接收请求的调用信息类型
		3.RPCPhase.P3:被调用端发送响应成功的调用信息类型
		2.RPCPhase.P4:调用端接收响应成功的调用信息类型
		2.RPCPhase.E3:被调用端发送响应失败的调用信息类型
		2.RPCPhase.E4:调用端接收响应失败的调用信息类型
		2.RPCPhase.SIB:主子线程间传递调用信息类型

	5.2.2 TraceID 
		我们在前端接收用户的请求后,会为用户的请求分配一个TraceID,然后在内部服务调用时,会通过应用层的协议将TraceID传递到下层服务,直到整个调用链路中的每个节点都
	拥有了TraceID,在系统出问题后,我们可以使用这个唯一的TraceID迅速找到系统间发生的所有交互请求和响应,并定位问题发生的节点。
		Vesta 是一款原创的多场景的互联网发号器,此发号器可以作为全局唯一的流水号,也就是TraceID。

	5.2.3 SpanID 
		TraceID 解决了系统间调用关系的串联问题,对调用关系串联后,我们能够找到服务于一个用户请求的调用和响应消息的集合,这些集合的请求和响应都是为同一次用户请求而服务的,
	但是我们无法标识和恢复这些请求和响应调用时的顺序和层级关系。
		因此,我们需要附加的信息在系统之间的请求和响应消息中传递,它就是SpanID,这里包含SpanID和ParentSpanID。
		SpanID和ParentSpanID 组合在一起就可以表示一个树形的调用关系,一个SpanID和ParentSpanID记录了一次调用的节点信息。SpanID表示当前为一个调用节点,ParentSpanID
	表示这个调用节点的父节点,通过这2个数据,我们就可以恢复树形的调用链。
		当出现故障时,我们需要为开发,运维人员显示树形的调用链,只需要下面4个步骤即可:
		1.通过TraceID把一整条调用链的所有信息收集到一个集合中,包括请求和响应
		2.通过SpanID和ParentSpanID恢复树形的调用链,ParentSpanID为-1的节点为调用树的根节点,也就是调用链的源头请求
		3.识别调用链中出错或者超市的节点,做出标记
		4.把恢复的调用树和出错的节点信息通过某种图形显示到UI界面上

		SpanID 是一个64位的整型值,有多种策略生成SpanID:
		1.使用随机数产生SpanID,理论上随机数是可以重复的,但是由于64位长整数型的取值范围,重复的可能性微乎其微,并且本地生成随机数的效率会高于其他方法。
		2.使用分布式的全局唯一的流水号生成方式,可以参考互联网发号器Vesta
		3.每个SpanID包含所有父亲及前辈节点的SpanID,使用圆点符号作为分隔符,不再需要 ParentSpanID 字段。如 SpanID=1.1.2。这种方案实现起来简单,但在某些场景下有
		一个致命的缺点,当一个请求的调用链有太多节点和层次时,SpanID 会携带太多冗余的信息,导致服务间调用的性能下降

	5.2.4 业务链 
		在生产实践中,由于业务流程复杂,一个业务流程的完成由用户的多次请求组成,这些请求之间是有关联的,我们在串联调用链之后,会根据业务的属性,将不同的调用链聚合在一起
	形成业务链,便于排查问题。
		例如用户在电商平台下单后会进行支付,由于在支付后对货物不满意,会申请退款。要完成这样的业务流程至少需要3次用户请求,这3次用户请求是通过业务系统的ID进行串联的,
	用户下单后会产生订单号,支付时会传入订单号,退款时也会传入原始订单号。
		我们需要在多次请求之间建立联系,可以通过业务系统的订单号来串联业务链,调用链是一个简单的树形结构,而业务链是一个森林结构。
		我们在恢复了下单,支付和退款调用链之后,通过业务的ID订单号将3个调用链关联在一起,这样开发,运维可以以更高的角度来查看业务系统的运行状态,迅速定位用户的请求卡在
	哪个链的哪个请求节点上,帮助业务人员和运营人员更好的了解产品的运行情况或者获得一些有价值的业务系统。

5.3 调用链跟踪系统的设计与实现 
	5.3.1 整体架构 
		调用链跟踪系统通常由采集器,处理器和分布式存储系统组成,经过这几个模块处理后的调用数据会在调用链展示系统中对外提供查看和查询的功能。每个模块的功能职责如下:
		1.采集器:负责把业务系统的远程服务调用信息从业务系统中传递给处理器
		2.处理器:负责从业务系统的采集器中接收服务调用信息并聚合调用链,将其存储在分布式数据存储中,以及对调用链进行分析,并输出给监控和报警系统
		3.分布式存储系统:存储海量的调用链数据,并支持灵活的查询和搜索功能

	5.3.2 TraceID和SpanID在服务间的传递 
		调用链系统的输入是各个业务系统之间的远程服务调用产生的请求和响应信息。

		1.Java 进程内传递
			在java应用系统内通常通过ThreadLocal来传递 TraceID和SpanID,这样在需要调用外部系统时,TraceID和SpanID都是可以得到的。
		2.服务间传递
			在服务于服务之间的通信里,我们需要在应用层的网络通信协议中传递TraceID和SpanID。如果我们使用restful,则http头是传递TraceID和SpanID的最佳位置;
		如果我们使用的是rpc远程调用,则通常需要在rpc的序列化协议上增加定制的字段,将TraceID和SpanID从调用方传递给被调用方。
		3.主子线程间传递
			在服务化架构里,为了缩短请求的响应时间,提升用户体验,我们通常把一些非核心链路上的逻辑抽象成异步处理,这通常会在异步线程中执行,如果我们想跟着这部分
		调用链,则需要在创建新的线程或者子线程,将TraceID和SpanID一并传递过去。
		4.消息队列传递
			为了让系统之间最大化的解耦或者对突发流量进行消峰处理,通常我们会使用消息队列,但之后就无法对调用流程进行调用链的跟踪了,我们需要一种方案来弥补这种缺陷。
			调用链跟踪消息队列的实现有以下3种方式:
			1.通过更改消息队列实现的底层协议,将TraceID和SpanID在底层透明的传递,这样就不需要在应用层有感知,但是更改消息队列协议的实现复杂,消息队列产品多样化,
			这种方案不容易。
			2.在应用层的报文上增加附加字段,应用层在发送消息时,手工将TranceID和SpanID通过报文传输,这种方案入侵了业务系统,但实现起来比较简单。
			3.在第2种方案的基础上,可以在消息队列客户端的库上做定制化,在每次发送消息时将TraceID和SpanID增加到消息报文中,在消息队列的处理机的库中先对报文进行解析,
			再将业务报文传递给应用层,这样既不用修改底层协议,也不用在开发者在写业务逻辑代码时,手工赋值这2个字段。
		5.缓存,数据库访问
			作为调用链中非常重要的一部分,我们需要对数据库和缓存的访问进行跟踪。
			1.对缓存和数据库服务进行二次开发,通常改造缓存和数据库服务于其客户端的网络通信协议,将TraceID和SpanID通过网络通信协议进行透明的传输,但是这种方案的技术
			难度比较大,风险比较高。
			2.封装缓存,数据库的客户端,将TracdID和SpanID域访问的数据进行关联,这种方案实现简单,使用方案,对业务代码无入侵。

	5.3.3 采集器的设计与实现 
		现在解决如何把调用信息从业务系统中传递出来。下面介绍采集器,通过它从业务应用系统中把TraceID和SpanID传递到调用链的处理器上。采集器的职责就是解决采集TraceID和
	SpanID数据及推送数据的问题。
		1.采集器的实现方法
			对java应用系统的采集一般有4种方法:应用层主动推送,aop推送,JavaAgent字节码增强和大数据日志推送。

			1.应用层推送
				这种方式,应用层通过编写代码的方式,把相关数据推送到调用链处理器,比较简单,缺点是入侵了业务。
			2.AOP推送
				这种方法在应用的业务层代码中使用AOP拦截目标服务调用,把请求和响应的调用信息收集后,推送到调用链处理器。这种方法虽然入侵了业务代码,但是只需要开发aop
			切面拦截类,并将aop拦截类注入业务代码的Spring配置环境中,这里aop功能独立,可升级,使用起来比较方便,耦合并不严重。
			3.JavaAgent 字节码增强
				JavaAgent 是 JDK5 的新特性,开发者可以构建一个独立于应用程序的代理程序,用来检测和协助运行在jvm上的程序,甚至能够替换和修改某些类的定义,增加定制化
			的监控代码,实现更为灵活的运行时虚拟机监控和Java字节码操作,这样的特性实际上提供了一种虚拟机级别支持的AOP实现方式。好处是完全不会入侵业务系统的代码,便于
			以后对程序进行优化和重构等,最小化对业务代码的影响。
			4.代理推送
				这种方法与第四章对大数据日志系统的建设方式类似,日志文件通过应用程序打印相关调用日志,然后随着日志一起推送到日志中心,再从日志中心提取相关的调用日志,
			组成调用链。

		2.推送的实现方法
			采集器采集的数据需要从业务服务的线程推送到调用链的处理器上,在推送的过程中,我们必须保证不能影响业务的正常运行,一般有如下2种方式。
			1.kafka消息队列
				在采集器和处理器中间增加了kafka消息队列,对于高峰时的日志处理起到了有效的消峰作用。
			2.udp推送
				无连接的特点,udp传输数据的效率上比tcp活着任何上层协议的效率都高。
				不管是kafka还是udp推送,我们都不能从业务线程中直接发送数据,这样一旦收集器出现问题,就会影响业务流程。因此,我们通常使用异步线程池发送调用信息,
			异步线程池与业务线程之间必须采用有界队列,防止收集器出现问题,出现数据积压后导致应用内存耗光而产生OutOfMemoryError问题。

	5.3.4 处理器的设计与实现 
		通过kafka或者upd,调用信息从业务系统传递到调用链处理器,调用链处理器对调用信息进行组合和聚合,并进行一定的附加处理,然后存储在大数据存储系统中,或者通过
	Spark等流式处理后发送给监控和报警系统。

		1.处理器的处理逻辑
			手机端的处理器需要对收集的信息进行处理,通常我们会使用java开发一个处理器,对从udp或者kafka得到的日志进行聚合,然后存入调用链的大数据存储系统中。
			一般在生产实践过程中,我们在处理器中除了要对调用链进行聚合,还需要从调用链中发现调用的问题,例如:抛异常,超时,服务响应时间过长等。这时我们还需要使用
		类似Storm,Spark等流式计算系统会回复的调用链进行分析,然后发送给报警和监控系统。

		2.调用链的大数据存储系统
			一条调用链波爱护一个用户请求在服务化架构中的多次调用信息,每个调用信息分成4个阶段,每个阶段都有不同类型的信息进行存储。我们需要用大数据存储技术。因此,
		HBase 是比较合适的存储系统,另外,基于HBase的TSDB 也适合存储基于时序的数据。

	5.3.5 调用链系统的展示 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值