spark分布式java代码_java 分布式实践

java 分布式实践

spring boot cloud实践

开源的全链路跟踪很多,比如 Spring Cloud Sleuth + Zipkin,国内有美团的 CAT 等等。

其目的就是当一个请求经过多个服务时,可以通过一个固定值获取整条请求链路的行为日志,基于此可以再进行耗时分析等,衍生出一些性能诊断的功能。

不过对于我们而言,首要目的就是 Trouble Shooting,出了问题需要快速定位异常出现在什么服务,整个请求的链路是怎样的。

为了让解决方案轻量,我们在日志中打印 RequestId 以及 TraceId 来标记链路。

RequestId 在 Gateway 生成表示唯一一次请求,TraceId 相当于二级路径,一开始与 RequestId 一样,但进入线程池或者消息队列后,TraceId 会增加标记来标识唯一条路径。

举个例子,当一次请求向 MQ 发送一个消息,那么这个消息可能会被多个消费者消费,此时每个消费线程都会自己生成一个 TraceId 来标记消费链路。加入 TraceId 的目的就是为了避免只用 RequestId 过滤出太多日志。

实现上,通过 ThreadLocal 存放 APIRequestContext 串联单服务内的所有调用。

当跨服务调用时,将 APIRequestContext 信息转化为 HTTP Header,被调用方获取到 HTTP Header 后再次构建 APIRequestContext 放入 ThreadLocal,重复循环保证 RequestId 和 TraceId 不丢失即可。

如果进入 MQ,那么 APIRequestContext 信息转化为 Message Header 即可(基于 RabbitMQ 实现)。

当日志汇总到日志系统后,如果出现问题,只需要捕获发生异常的 RequestId 或是 TraceId 即可进行问题定位。

1. 功能介绍

2. 使用方法

启动udf-rabbitmq-producer:

启动udf-rabbitmq-comsumer:

查看日志: produce的日志

|2019-04-04 11:53:12.260| INFO|XNIO-1 task-10|udf.udf.rabbitmq.producer.controller.SendMessage:34||127.0.0.1|172.27.9.60|clientSysName|ito-rabbitmq-provider|1904041153122562832859336|BIZ|init rabbitmq2org.springframework.amqp.rabbit.core.RabbitTemplate@1b1f5012|

|2019-04-04 11:53:12.262| INFO|XNIO-1 task-10|udf.udf.rabbitmq.springboot.starter.postprocess.MDCMesagePostProcess:41||127.0.0.1|172.27.9.60|clientSysName|ito-rabbitmq-provider|1904041153122562832859336|BIZ|{"traceId":"1904041153122562832859336","clientSysName":"clientSysName","logType":"BIZ","request":"HttpServletRequestImpl [ POST /v1/sendMsg ]","clientNodeName":"127.0.0.1","serverNodeName":"172.27.9.60","transId":"transId","requestUrl":"http://127.0.0.1:10616/v1/sendMsg","serverSysName":"ito-rabbitmq-provider","startTime":"1554349992256"}|

|2019-04-04 11:53:12.264| INFO|XNIO-1 task-10|udf.udf.log.springboot.starter.filter.NormalAspect:52||127.0.0.1|172.27.9.60|clientSysName|ito-rabbitmq-provider|1904041153122562832859336|RESP|interfaceName=http://127.0.0.1:10616/v1/sendMsg^executeTime=8^desc=request:HttpServletRequestImpl [ POST /v1/sendMsg ],response:{"age":0,"name":"string"}|

comsumer的日志

|2019-04-04 11:53:12.295| INFO|dataDeepDealListenerContainer-6|udf.udf.rabbitmq.springboot.starter.postprocess.MDCReceivePostProcessors:52||127.0.0.1|172.27.9.60|ito-rabbitmq-provider|ito-rabbitmq-comsumer|1904041152498513697567629|BIZ|接收到的MDC{"traceId":"1904041153122562832859336","clientSysName":"clientSysName","logType":"BIZ","request":"HttpServletRequestImpl [ POST /v1/sendMsg ]","clientNodeName":"127.0.0.1","serverNodeName":"172.27.9.60","transId":"transId","requestUrl":"http://127.0.0.1:10616/v1/sendMsg","serverSysName":"ito-rabbitmq-provider","startTime":"1554349992256","__TypeId__":"udf.udf.rabbitmq.producer.model.SampleMessage"}|

|2019-04-04 11:53:12.296| INFO|dataDeepDealListenerContainer-6|udf.udf.rabbitmq.comsumer.mqlistener.SampleMessageListener:26||127.0.0.1|172.27.9.60|ito-rabbitmq-provider|ito-rabbitmq-comsumer|1904041153122562832859336|BIZ|成功处理MQ消息, 消息体:{"name":"string","age":0}|

实现要点: 对rabbitmqtemplate进行增强,添加前置消息处理器:

@Bean

RabbitTemplate reforeRabbitTemplate(ConnectionFactory connectionFactory,Jackson2JsonMessageConverter jackson2JsonMessageConverter)

{

RabbitTemplate rabbitTemplate =new RabbitTemplate(connectionFactory);

rabbitTemplate.setBeforePublishPostProcessors(mdcMesagePostProcess);

rabbitTemplate.setAfterReceivePostProcessors(mdcReceivePostProcessors);

rabbitTemplate.setMessageConverter(jackson2JsonMessageConverter);

logger.info("init rabbitmq"+rabbitTemplate);

return rabbitTemplate;

}

public class MDCMesagePostProcess implements MessagePostProcessor {

private Logger logger= LoggerFactory.getLogger(MDCMesagePostProcess.class);

@Override

public Message postProcessMessage(Message message) throws AmqpException {

Map mdcContainer =MDC.getCopyOfContextMap();

logger.info(JSON.toJSONString(mdcContainer));

for (Map.Entry m : mdcContainer.entrySet()) {

message.getMessageProperties().setHeader(m.getKey(),m.getValue());

}

return message;

}

}

@Service

public class MDCReceivePostProcessors implements MessagePostProcessor {

@Value("${spring.application.name}")

private String sysName;

private Logger logger= LoggerFactory.getLogger(MDCReceivePostProcessors.class);

@Override

public Message postProcessMessage(Message message) throws AmqpException {

Map mdc= message.getMessageProperties().getHeaders();

Map mdcString= new HashMap();

logger.info("接收到的MDC"+ JSON.toJSONString(mdc));

for (Map.Entry m : mdc.entrySet()) {

mdcString.put(m.getKey(),m.getValue().toString());

}

mdcString.put(LogKeyConstants.CLIENT_SYS_NAME, String.valueOf(mdc.get(LogKeyConstants.SERVER_SYS_NAME)));

mdcString.put(LogKeyConstants.SERVER_SYS_NAME, sysName);

MDC.setContextMap(mdcString);

return message;

}

}

消费者还是需要额外指定:

@Bean

public SimpleMessageListenerContainer dataDeepDealListenerContainer(ConnectionFactory connectionFactory, Queue queue) {

SimpleMessageListenerContainer container =new SimpleMessageListenerContainer();

container.setConnectionFactory(connectionFactory);

container.setQueues(queue);

container.setMessageListener(sampleMessageListener);

container.setAcknowledgeMode(AcknowledgeMode.MANUAL);

container.setAfterReceivePostProcessors(mdcReceivePostProcessors);

return container;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值