热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JavaMicroservices:传送唯一标识(requestid)在Hessiancall,restAPI,JMS和Thread之间

传送唯一requestid在项目或者Microservices之间背景介绍Log4jformat中加requestidRestAPI调用JMSmessage传输之间线程池中的线程H


传送唯一request id在项目或者Micro services之间

    • 背景介绍
    • Log4j format 中加requestid
    • Rest API 调用
    • JMS message 传输之间
    • 线程池中的线程
    • Hessian call 之间


背景介绍

随着项目中要把原来的几个项目变成许多 MircoServices. 所谓的 Micro services化把一个大的项目。
带来一些其它的问题。比如怎么样更方便的查看log,因为项目都变成了一个一个小的项目了。
每个项目的log 文件都在它们怎么自己的项目里。尽管我们用了ELK把所有的log 到发到Elastic search 通过 logstash. 但是没有办法把项目之间的log message 串联在一起显示。只能通过时间的比对或者是其它的信息。

由于这个原因。我引入一个request id 在项目直接调用。这样我们就可以直接用request id 搜索相关的log message 了。再也不用因为很难找到被调用项目的log而发愁了。


Log4j format 中加requestid

log4j.appender.file.layout.ConversionPattern=[%d{yyyy-MM-dd HH:mm:ss,SSS}][%X{REQUEST_ID}] %-5p[%t](%C:%L) - %m%n

Rest API 调用

由于我用MDC 还保存我的requestId 的。所有 调用之前要取一下requestId 再MDC里
发送端要加下面的 interceptor

public class RestfulServiceLogInterceptor implements ClientHttpRequestInterceptor{@Overridepublic ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {String requestId= (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId=UUID.randomUUID().toString();}request.getHeaders().add("REQUEST_ID", requestId);return execution.execute(request, body);}
}

public class RestfulServiceAsyncLogInterceptor implements AsyncClientHttpRequestInterceptor{&#64;Overridepublic ListenableFuture<ClientHttpResponse> intercept(HttpRequest request, byte[] body, AsyncClientHttpRequestExecution execution) throws IOException{String requestId&#61; (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId&#61;UUID.randomUUID().toString();}request.getHeaders().add("REQUEST_ID", requestId);return execution.execute(request, body);}}

被调用端要加下面的Filter。


&#64;Component
public class LogFilter implements Filter {&#64;Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {try {if (servletRequest !&#61; null && servletRequest instanceof HttpServletRequest) {HttpServletRequest req &#61; (HttpServletRequest) servletRequest;String requestId &#61; req.getHeader("REQUEST_ID");MDC.put("REQUEST_ID", requestId );}filterChain.doFilter(servletRequest, servletResponse);} finally {LogUtils.clearHeaderPropertiesInMDC();}}}

JMS message 传输之间

发送JMS 端&#xff0c; 发送的时候要用下面的类包装message.


public abstract class BaseMessageCreator implements MessageCreator{&#64;Overridepublic Message createMessage(Session session) throws JMSException {Message message &#61; this.createInternalMessage(session);this.postProcess(message);return message;}public void postProcess(Message message){String requestId&#61; (String) MDC.get("REQUEST_ID");if(StringUtils.isEmpty(requestId)) {requestId&#61;UUID.randomUUID().toString();}message.setStringProperty("REQUEST_ID", requestId);}protected abstract Message createInternalMessage(Session session) throws JMSException;}

JMS 接受端 &#xff0c; 要用下面类取message header 的requestid.
我用的是AOP. 当然如果你觉得麻烦。也可以直接再你的JMS listner上&#xff0c; 直接取。


&#64;Aspect
public class JmsTransactionAspectLogger {&#64;Pointcut("execution(public * javax.jms.MessageListener.onMessage(..))")private void isMessageListener() {}&#64;Pointcut("execution(public * org.springframework.jms.listener.SessionAwareMessageListener.onMessage(..))")private void isSessionAwareMessageListener() {}&#64;Pointcut("isMessageListener() || isSessionAwareMessageListener()")private void jmsTransactionMethod() {}public void beforeJmsListener(JoinPoint joinPoint) {try {Object[] args &#61; joinPoint.getArgs();for (int i &#61; 0; i < args.length; i&#43;&#43;) {Object arg &#61; args[i];if (arg instanceof Message) {Message message &#61; (Message) arg;String requestId &#61; message.getStringProperty("REQUEST_ID");MDC.put("REQUEST_ID", requestId );}}} catch (Exception ex) {throw e;}}&#64;Around(value &#61; "jmsTransactionMethod()")public Object handleJmsTransaction(final ProceedingJoinPoint joinPoint) throws Throwable {this.beforeJmsListener(joinPoint);try {retValue &#61; joinPoint.proceed(joinPoint.getArgs());} catch (Throwable ex) {throw ex;} finally {this.afterJmsListener(joinPoint, startTime);}return retValue;}public void afterJmsListener(JoinPoint joinPoint, long startTime) {LogUtils.clearHeaderPropertiesInMDC();}

线程池中的线程

线程要用继承下面的抽象类。

如果不是用的线程中的线程&#xff0c;那么没问题
因为 MDC中的值可以从主线程中传过去的。

public class MdcContextHolder {private String requestId;public MdcContextHolder(){this.requestId &#61; (String)MDC.get("REQUEST_ID");}public void setHeaderPropertiesInMDC(){MDC.put("REQUEST_ID", requestId);}
}public abstract class LogCallable<V> implements Callable<V> {MdcContextHolder mdcContextHolder &#61; new MdcContextHolder();;&#64;Overridepublic V call() throws Exception {this.preCall();V v &#61; innerCall();this.afterCall();return v;}private void preCall(){if(mdcContextHolder!&#61;null){mdcContextHolder.setHeaderPropertiesInMDC();}}private void afterCall(){LogUtils.clearHeaderPropertiesInMDC();}public abstract V innerCall() throws Exception;
}public abstract class LogRunnable implements Runnable{MdcContextHolder mdcContextHolder &#61; new MdcContextHolder();;&#64;Overridepublic void run() {this.preRun();this.innerRun();this.afterRun();}private void preRun(){if(mdcContextHolder!&#61;null){mdcContextHolder.setHeaderPropertiesInMDC();}}private void afterRun(){LogUtils.clearHeaderPropertiesInMDC();}public abstract void innerRun();public class MdcTaskDecorator implements TaskDecorator {&#64;Overridepublic Runnable decorate(final Runnable runnable) {LogRunnable mdcRunnable &#61; new LogRunnable() {&#64;Overridepublic void innerRun() {runnable.run();}};return mdcRunnable;}
}

Hessian call 之间

发送端

public class CustomHessianProxyFactory extends HessianProxyFactory {&#64;Overridepublic Object create(Class<?> api, URL url, ClassLoader loader) {if (api &#61;&#61; null)throw new NullPointerException("api must not be null for HessianProxyFactory.create()");InvocationHandler handler &#61; new CustomHessianProxy(url, this, api);return Proxy.newProxyInstance(loader, new Class[] { api, HessianRemoteObject.class }, handler);}private static class CustomHessianProxy extends HessianProxy {protected CustomHessianProxy(URL url, HessianProxyFactory factory) {super(url, factory);}public CustomHessianProxy(URL url, HessianProxyFactory factory, Class<?> type) {super(url, factory, type);}&#64;Overrideprotected void addRequestHeaders(HessianConnection conn) {super.addRequestHeaders(conn);this.addParameterInHeader(conn);}private void addParameterInHeader(HessianConnection conn){String requestId&#61; (String) MDC.get("REQUEST_ID");conn.addHeader("REQUEST_ID", requestId);}}}

被调用端 用上面提到的logfilter 就可以了


推荐阅读
  • 本文介绍了使用postman进行接口测试的方法,以测试用户管理模块为例。首先需要下载并安装postman,然后创建基本的请求并填写用户名密码进行登录测试。接下来可以进行用户查询和新增的测试。在新增时,可以进行异常测试,包括用户名超长和输入特殊字符的情况。通过测试发现后台没有对参数长度和特殊字符进行检查和过滤。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Spring常用注解(绝对经典),全靠这份Java知识点PDF大全
    本文介绍了Spring常用注解和注入bean的注解,包括@Bean、@Autowired、@Inject等,同时提供了一个Java知识点PDF大全的资源链接。其中详细介绍了ColorFactoryBean的使用,以及@Autowired和@Inject的区别和用法。此外,还提到了@Required属性的配置和使用。 ... [详细]
  • SpringMVC接收请求参数的方式总结
    本文总结了在SpringMVC开发中处理控制器参数的各种方式,包括处理使用@RequestParam注解的参数、MultipartFile类型参数和Simple类型参数的RequestParamMethodArgumentResolver,处理@RequestBody注解的参数的RequestResponseBodyMethodProcessor,以及PathVariableMapMethodArgumentResol等子类。 ... [详细]
  • JavaWeb中读取文件资源的路径问题及解决方法
    在JavaWeb开发中,读取文件资源的路径是一个常见的问题。本文介绍了使用绝对路径和相对路径两种方法来解决这个问题,并给出了相应的代码示例。同时,还讨论了使用绝对路径的优缺点,以及如何正确使用相对路径来读取文件。通过本文的学习,读者可以掌握在JavaWeb中正确找到和读取文件资源的方法。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
  • ShiftLeft:将静态防护与运行时防护结合的持续性安全防护解决方案
    ShiftLeft公司是一家致力于将应用的静态防护和运行时防护与应用开发自动化工作流相结合以提升软件开发生命周期中的安全性的公司。传统的安全防护方式存在误报率高、人工成本高、耗时长等问题,而ShiftLeft提供的持续性安全防护解决方案能够解决这些问题。通过将下一代静态代码分析与应用开发自动化工作流中涉及的安全工具相结合,ShiftLeft帮助企业实现DevSecOps的安全部分,提供高效、准确的安全能力。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
  • Rancher 部署 logstash
    RancherOnlogstash一、下载logstash镜像二、创建挂载目录与映射配置文件2.1创建配置文件2.2配置启动命令2.3映射配置文件概述:logstas ... [详细]
  • IamtryingtolearnabouttheELKstack(ElasticSearch,LogstashandKibana).Inanefforttogets ... [详细]
author-avatar
兔子狗530_772
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有