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 ...
随机推荐
- Disconnected from the target VM, address: '127.0.0.1:56577', transport: 'socket'
Disconnected from the target VM, address: '127.0.0.1:56577', transport: 'socket' Disconnected from t ...
- C# MVC中直接执行Js
.NET MVC里如何在服务器端执行JS: 三种解决方案: 1.直接返回JavaScript. public ActionResult XXXAction1() { return JavaS ...
- IDEA创建类似于Eclipse的source folder
1.新建普通文件夹目录directory 2.当前Module右键Open Mudule Settings(F12) 3.选中新建的文件夹并单击上面的Sources,看到文件夹颜色变化即成功.
- C++ extern关键字
[参考] 关于C++中extern的简单笔记
- min-width
min-width 语法: min-width:<length> | <percentage> 默认值:0 适用于:除非置换内联元素,table-row, table-row- ...
- 004——转载C#禁止改变窗体大小
原文链接:http://www.cnblogs.com/shaozhuyong/p/5545005.html 1.先把MaximizeBox和MinimumBox设置为false,这时你发现最大最小化 ...
- 以下是Direct 3d的安装步骤
安装配置 真的是软肋 o( ̄ε ̄*) 我记录以下 步骤 防止下次忘记了 首先要安装到direct3d 之后在vs上配置 如下: 找到 安装direct3d的文件夹 复制路径(如下 我的路径为 G ...
- 用python实现简易学生管理系统
以前用C++和Java写过学生管理系统,也想用Python试试,果然“人生苦短,我用Python”.用Python写的更加简洁,实现雏形也就不到100行代码. 下面上代码 #!/usr/bin/pyt ...
- 二十、网络ifconfig 、ip 、netstat、ss之二
ip 网络层协议 ip地址 点分十进制分为4段,范围 0-255 ip分类 A 占据1段,最左侧一段第一位固定为0 0 000 0000 - 0 111 1111 0 - 127:其中0为网络,12 ...
- EGL Driver message (Critical) eglInitialize: No available renderers.
使用Python的selenium库进行自动化巡检.并将相对应的数据保存 环境: Windows Embedded Standard Python 2.7.16 selenium 3.141.0 [0 ...