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<String, String> mdcContainer =MDC.getCopyOfContextMap();
logger.info(JSON.toJSONString(mdcContainer));
for (Map.Entry<String, String> 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<String, Object> mdc= message.getMessageProperties().getHeaders();
Map<String, String> mdcString= new HashMap<String,String>();
logger.info("接收到的MDC"+ JSON.toJSONString(mdc));
for (Map.Entry<String, Object> 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;
}
java 分布式实践的更多相关文章
- 大型网站系统与Java中间件实践
大型网站系统与Java中间件实践(贯通分布式高并发高数据高访问量网站架构与实现之权威著作,九大一线互联网公司CTO联合推荐) 曾宪杰 著 ISBN 978-7-121-22761-5 2014年4 ...
- 《大型网站系统与JAVA中间件实践》【PDF】下载
<大型网站系统与JAVA中间件实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062557 内容简介 到底是本什么书,拥有这样 ...
- 学Java分布式和高架构,必懂的两大知识点!
今天小编为你们分享阿里巴巴2018年招聘应届毕业生,Java工程师的面试考题,主要分为三种 Java中获取 mysql连接的方式: 第一部分:分布式 三步变成:分布式 1.将你的整个软件视为一个系 ...
- Java分布式 一些概念理解
转至 java那些事 2017-02-09 有些朋友工作一年了觉得该深入一下子了,所以想深入学习一下以提升自己的专业技能,想问一下如何入门Java分布式应用,学习过程大致是怎么样的,涉及到那些知识, ...
- 深入理解java:5. Java分布式架构
什么是分布式架构 分布式系统(distributed system)是建立在网络之上的软件系统. 内聚性是指每一个数据库分布节点高度自治,有本地的数据库管理系统. 透明性是指每一个数据库分布节点对用户 ...
- Java分布式开发
分布式概念的引入是基于性能的提升,应用的可靠性而提出的.所谓Java分布式,即是在使用Java语言进行企业级应用开发的过程中,采用分布式技术解决业务逻辑的高并发.高可用性的一些架构设计方案. 1. R ...
- 5个强大的Java分布式缓存框架推荐
在开发中大型Java软件项目时,很多Java架构师都会遇到数据库读写瓶颈,如果你在系统架构时并没有将缓存策略考虑进去,或者并没有选择更优的 缓存策略,那么到时候重构起来将会是一个噩梦.本文主要是分享了 ...
- Java注解实践
Java注解实践 标签 : Java基础 注解对代码的语意没有直接影响, 他们只负责提供信息给相关的程序使用. 注解永远不会改变被注解代码的含义, 但可以通过工具对被注解的代码进行特殊处理. JDK ...
- Java分布式处理技术(RMI,JDNI)
http://hedaoyuan.blog.51cto.com/4639772/813702 1.1 RMI的基本概念 1.1.1 什么是RMI RMI(Remote Method Invocatio ...
随机推荐
- UEditor设置内容setContent()失效的解决方法
ueditor常见用法: https://blog.csdn.net/qq_31879707/article/details/54894735#UE.Editor:setContent() UEdit ...
- 推荐一款在IntelliJ IDEA中使用微信/QQ的插件
SmartIM SmartIM4IntelliJ 是一个 IntelliJ IDEA 上的 SmartIM(原 SmartQQ)插件,可以在 IDEA 中使用 QQ 或微信聊天. 功能 收发文本消息 ...
- BZOJ 2229 / Luogu P3329 [ZJOI2011]最小割 (分治最小割板题)
题面 求所有点对的最小割中<=c的数量 分析 分治最小割板题 首先,注意这样一个事实:如果(X,Y)是某个s1-t1最小割,(Z,W)是某个s2-t2最小割,那么X∩Z.X∩W.Y∩Z.Y∩W这 ...
- python自动华 (十五)
Python自动化 [第十五篇]:CSS.JavaScript 和 Dom介绍 本节内容 CSS javascript dom CSS position标签 fixed: 固定在页面的某个位置 rel ...
- xunit输出
//输出,只能注入 public class MyUnitTest { private IServiceCollection service; private readonly ITestOutput ...
- 事务日志已满 请参阅sys.databases中的log_reuse_wait_desc列解决办法
http://www.myexception.cn/sql-server/153219.html http://blog.csdn.net/kedingboy12345/article/details ...
- Codevs 1743 反转卡片(splay)
1743 反转卡片 时间限制: 2 s 空间限制: 256000 KB 题目等级 : 大师 Master 题目描述 Description [dzy493941464|yywyzdzr原创] 小A将N ...
- (RERERERERERERERERERERE) BZOJ 2746: [HEOI2012]旅行问题
二次联通门 : BZOJ 2746: [HEOI2012]旅行问题 神TM STL的vector push_back进一个数后取出时就变成了一个很小的负数.. 调不出来了, 不调了 #include ...
- Til the Cows Come Home ( POJ 2387) (简单最短路 Dijkstra)
problem Bessie is out in the field and wants to get back to the barn to get as much sleep as possibl ...
- Print工具类
这篇文章已经废弃. 实际开发中,打印信息只会用日志框架(Log4j2). 受到Thinking in Java中静态引入(import static)的启发, Deolin也打算写一个方便自己的工具类 ...