概述
RocketMQ4.x可观测主要是基于消息轨迹来实现,在发送消息和消费消息时,将上下文中的参数添加进轨迹消息,将轨迹数据数据当成消息保存到本身集群中。而从5.x版本使用Telemetry遥测协议将可观测开关以及接入点下发到客户端,将客户端上报的所有可观测消息在Proxy侧做收敛,再统一使用标准的OpenTelemetry和Opencensu两个协议上报到SLS,SLS再通过TLog的方式与Prometheus、Grafana进行对接,来支持标准化的可观测协议,达到更丰富的可观测能力。
即对于使用5.x gRPC SDK客户端的用户无法通过原本的轨迹消息进行可观测,而是需要借助于新的能力,我们想提供即使使用新的SDK客户端也能观察到原本的消息轨迹的支持。
TCP SDK消息轨迹的实现
对于一条消息而言,生产和消费开启消息轨迹的情况下,单个MessageID会产生三条轨迹消息,分别为发送后、消费前和消费后。需要将本身消息的MessageID作为轨迹消息的Key,这样才能够在控制台根据MessageID查到所有的轨迹数据。
如上图所示:
生产和消费消息时分别使用Hook的方式将消息轨迹的数据添加到上下文TraceContext中,然后使用TraceDispatcher异步拼装轨迹数据消息,然后发送到服务端Broker中。
每个生产组或者消费组都会有一个TraceDispatcher实例,对于生产消息的轨迹消息,只会在消息生产完成以后发送到Broker。如下图所示,为轨迹的整体数据结构:
消息生产和消费时,并不是一个TraceContext就会生成一条轨迹消息。对于Producer而言,同一个生产组,不同消息可能会生成一条轨迹消息来存储;对于Consumer而言,同一个Topic,同一个消费组的不同消息可能会生成一条轨迹消息来存储。一个MessageID对应的生产消息轨迹只会有一条,而由于一条消息可能存在重复消费的情况,所以一个MessageID对应的消费消息轨迹可能会有多条。那么通过MessageID查询到的所有的轨迹数据,对于重复多次消费的,需要区分同一组消费前后即SubBefore和SubAfter的轨迹数据。RocketMQ根据TraceContext中的RequestId来区分是否同一组消费数据,即消费Hook中的消费前后是有关联的,其TraceContext中的RequestId是相同的。控制台做轨迹数据展现时,会先根据MessageID查出所有的轨迹数据,然后根据TraceType区分生产和消费类型,最后根据RequestId区分同一组消费的轨迹数据。
5.x gRPC SDK消息轨迹的实现
5.x SDK采用gRPC协议多语言客户端通过Proxy访问RocketMQ集群,生产消息模型基本和原来一致,消费消息采用POP消费模型,客户端Receive消息消费完成后,主动通过ReceiptHandle确认ACK消息。
实现gRPC SDK消息轨迹的方案为可以使用Hook分别在SendMessageActivity、ReceiveMessageActivity和AckMessageActivity时添加轨迹数据,实现Proxy实例共享同一个TraceDispatcher进行轨迹数据消息的发送。另外对于消费消息时,由于POP消息和ACK动作是分开的,变得无状态化,所以消费上下文无法再关联起来,原本的通过统一RequestID进行关联不再可行,我们采用在POP消息生成的消息句柄ReceiptHandle中添加MessageID,然后在ACK消息时,通过ReceiptHandle中的MessageID进行关联,这样就可以将消费前后的轨迹数据重新关联起来。