在Spring Boot中实现相关ID(用于SOA /微服务中的分布式跟踪)

上周参加了在Geecon上Sam Newman的微服务讨论后,我开始思考更多有关用于监视,报告和诊断的面向服务/微服务平台最可能的基本功能:相关ID。 关联ID允许在面向服务的复杂平台中进行分布式跟踪,在该平台中,对单个应用程序的请求通常可以由多个下游服务处理。 如果没有关联下游服务请求的能力,那么很难理解平台中如何处理请求。

我已经在我最近从事的几个SOA项目中看到了关联ID的好处,但是正如Sam在他的演讲中提到的那样,通常很容易想到在构建应用程序的初始版本时不需要这种类型的跟踪。 ,但是当您确实意识到好处(和需求!)时,很难将其改造到应用程序中。 我还没有找到在基于Java / Spring的应用程序中实现相关ID的完美方法,但是在通过电子邮件与Sam聊天后,他提出了一些建议,我现在将其变成一个使用Spring Boot的简单项目,以演示如何做到这一点。被实施。

为什么?

在两次Sam的Geecon谈话中,他都提到,根据他的经验,关联ID对于诊断目的非常有用。 关联ID本质上是一个生成的ID,它与单个(通常是用户驱动的)请求一起进入应用程序,该请求通过堆栈向下传递到相关服务。 在SOA或微服务平台中,这种类型的ID非常有用,因为进入应用程序的请求通常被“散布”或由多个下游服务处理,而关联ID允许所有下游请求(从请求的初始点)到根据ID进行关联或分组。 然后,可以通过关联所有下游服务日志并匹配所需的ID来使用相关ID来执行所谓的“分布式跟踪”,以在整个应用程序堆栈中查看请求的跟踪(如果使用集中式日志记录,这非常容易框架,例如logstash )。

面向服务领域的主要参与者一直在讨论分布式跟踪和关联请求的需求,因此Twitter创建了他们的开源Zipkin框架 (通常将其插入RPC框架Finagle )和Netflix。已将其Karyon网络/微服务框架开源,这两个框架均提供分布式跟踪。 当然在该领域有商业产品,其中一个产品是AppDynamics ,虽然很酷,但价格却很高。

在Spring Boot中创建概念验证

与Zipkin和Karyon一样,它们都具有相对侵入性,因为您必须在(通常是自以为是的)框架之上构建服务。 对于某些用例,这可能很好,但对于其他用例而言却没什么用,特别是在构建微服务时。 我最近一直在尝试使用Spring Boot ,并且该框架通过提供许多预配置的明智默认值而建立在广为人知和喜爱(至少对我而言!)的Spring框架上。 这使您可以快速构建微服务(尤其是通过RESTful接口进行通信的微服务)。 本博客pos的其余部分说明了我如何(希望)实现一种实现关联ID的非侵入性方式。

目标

  1. 允许为进入应用程序的初始请求生成关联ID
  2. 启用相关ID传递给下游服务,使用的方法应尽可能不侵入代码

实作

我在GitHub上创建了两个项目, 一个包含一个实现,其中所有请求都以同步方式处理 (即,在单个线程上处理所有请求处理的传统Spring方法),还有一个用于异步(非阻塞)时的实现。 )正在使用的通信方式(即,结合使用Servlet 3异步支持和Spring的DeferredResult和Java的Futures / Callables)。 本文的大部分内容描述了异步实现,因为这更有趣:

  • Spring Boot异步(DeferredResult + Futures)通信相关ID Github回购

这两个代码库中的主要工作都是由CorrelationHeaderFilter承担的,CorrelationHeaderFilter是一个标准的Java EE筛选器,它检查HttpServletRequest标头中是否存在correlationId。 如果找到一个,则在RequestCorrelation类中设置一个ThreadLocal变量(稍后讨论)。 如果未找到相关标识,则生成一个相关标识并将其添加到RequestCorrelation类中:

public class CorrelationHeaderFilter implements Filter {//...@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)throws IOException, ServletException {final HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;String currentCorrId = httpServletRequest.getHeader(RequestCorrelation.CORRELATION_ID_HEADER);if (!currentRequestIsAsyncDispatcher(httpServletRequest)) {if (currentCorrId == null) {currentCorrId = UUID.randomUUID().toString();LOGGER.info("No correlationId found in Header. Generated : " + currentCorrId);} else {LOGGER.info("Found correlationId in Header : " + currentCorrId);}RequestCorrelation.setId(currentCorrId);}filterChain.doFilter(httpServletRequest, servletResponse);}//...private boolean currentRequestIsAsyncDispatcher(HttpServletRequest httpServletRequest) {return httpServletRequest.getDispatcherType().equals(DispatcherType.ASYNC);}

此代码中唯一可能不会立即显而易见的是条件检查currentRequestIsAsyncDispatcher(httpServletRequest) ,但这是为了防止当Async Dispatcher线程运行以返回结果时正在执行的相关ID代码(请注意,因为我最初并不希望Async Dispatcher再次触发执行过滤器!)。

这是RequestCorrelation类,其中包含一个简单的ThreadLocal <String>静态变量,用于保存当前执行线程的相关ID(通过上面的CorrelationHeaderFilter设置):

public class RequestCorrelation {public static final String CORRELATION_ID = "correlationId";private static final ThreadLocal<String> id = new ThreadLocal<String>();public static String getId() { return id.get(); }public static void setId(String correlationId) { id.set(correlationId); }
}

一旦将关联ID存储在RequestCorrelation类中,就可以通过调用RequestCorrelation中的静态getId()方法,将其检索并添加到下游服务请求(或数据存储访问等)中。 将这种行为封装在应用程序服务之外可能是一个好主意,并且您可以在我创建的RestClient类中看到如何执行此操作的示例,该类构成Spring的RestTemplate并处理标头中相关性ID的设置从调用类透明地显示。

@Component
public class CorrelatingRestClient implements RestClient {private RestTemplate restTemplate = new RestTemplate();@Overridepublic String getForString(String uri) {String correlationId = RequestCorrelation.getId();HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.set(RequestCorrelation.CORRELATION_ID, correlationId);LOGGER.info("start REST request to {} with correlationId {}", uri, correlationId);//TODO: error-handling and fault-tolerance in productionResponseEntity<String> response = restTemplate.exchange(uri, HttpMethod.GET,new HttpEntity<String>(httpHeaders), String.class);LOGGER.info("completed REST request to {} with correlationId {}", uri, correlationId);return response.getBody();}
}//... calling Class
public String exampleMethod() {RestClient restClient = new CorrelatingRestClient();return restClient.getForString(URI_LOCATION); //correlation id handling completely abstracted to RestClient impl
}

使这项工作适用于异步请求…

当您同步处理所有请求时,上面包含的代码可以很好地工作,但是在SOA /微服务平台中以非阻塞异步方式处理请求通常是个好主意。 在Spring中,可以通过将DeferredResult类与Servlet 3异步支持结合使用来实现。 在异步方法中使用ThreadLocal变量的问题在于,最初处理请求(并创建DeferredResult / Future)的线程将不是执行实际处理的线程。

因此,需要一点胶水代码以确保相关性id跨线程传播。 这可以通过使用所需功能扩展Callable来实现:(不要担心示例调用类代码看起来不直观-在Spring中,DeferredResults和Futures之间的这种适应是必然的,在整个过程中,包括样板ListenableFutureAdapter的完整代码为在我的GitHub存储库中):

public class CorrelationCallable<V> implements Callable<V> {private String correlationId;private Callable<V> callable;public CorrelationCallable(Callable<V> targetCallable) {correlationId = RequestCorrelation.getId();callable = targetCallable;}@Overridepublic V call() throws Exception {RequestCorrelation.setId(correlationId);return callable.call();}
}//... Calling Class@RequestMapping("externalNews")
public DeferredResult<String> externalNews() {return new ListenableFutureAdapter<>(service.submit(new CorrelationCallable<>(externalNewsService::getNews)));
}

这样就可以了-无论处理的同步/异步性质如何,相关ID的传播!

您可以克隆包含我的异步示例的Github报告,并通过在命令行上运行mvn spring-boot:run来执行应用程序。 如果您在浏览器中(或通过curl)访问http:// localhost:8080 / externalNews ,您将在Spring Boot控制台中看到类似于以下内容的内容,该内容清楚地表明了在初始请求中生成的关联ID,然后被传播到模拟外部调用(请查看ExternalNewsServiceRest类以了解如何实现):

[nio-8080-exec-1] u.c.t.e.c.w.f.CorrelationHeaderFilter    : No correlationId found in Header. Generated : d205991b-c613-4acd-97b8-97112b2b2ad0
[pool-1-thread-1] u.c.t.e.c.w.c.CorrelatingRestClient      : start REST request to http://localhost:8080/news with correlationId d205991b-c613-4acd-97b8-97112b2b2ad0
[nio-8080-exec-2] u.c.t.e.c.w.f.CorrelationHeaderFilter    : Found correlationId in Header : d205991b-c613-4acd-97b8-97112b2b2ad0
[pool-1-thread-1] u.c.t.e.c.w.c.CorrelatingRestClient      : completed REST request to http://localhost:8080/news with correlationId d205991b-c613-4acd-97b8-97112b2b2ad0

结论

我对这个简单的原型感到非常满意,它确实满足了我上面列出的两个目标。 未来的工作将包括为此代码编写一些测试(不要为TDDing而感到羞耻!),并将此功能扩展到更实际的示例。

我要非常感谢Sam,不仅是因为在Geecon的精彩演讲中分享了他的知识,还感谢我抽出时间来回复我的电子邮件。 如果您对微服务和相关工作感兴趣,我强烈建议您参阅Sam's Microservice书籍,该书可在O'Reilly的Early Access中获得 。 我很喜欢阅读当前可用的章节,并且最近实施了许多SOA项目,我可以从中获得很多不错的建议。 我将以极大的兴趣关注本书的发展!

资源资源

我多次使用Tomasz Nurkiewicz的优秀博客来学习如何最好地在Spring中连接所有DeferredResult / Future代码:

http://www.nurkiewicz.com/2013/03/deferredresult-asynchronous-processing.html

翻译自: https://www.javacodegeeks.com/2014/05/implementing-correlation-ids-in-spring-boot-for-distributed-tracing-in-soamicroservices.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363571.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机专业论文关于天气预报的,关于天气预报论文范文写作 天气预报相关论文写作资料...

天气预报,本文是一篇关于天气预报论文范文&#xff0c;可作为相关选题参考,和写作参考文献。天气预报论文参考文献&#xff1a;每天吃过晚饭,严惠语都会准时打开电视,收看黑龙江电视台和云南电视台的天气预报节目.自从女儿考上云南大学,为了能攒够女儿下学期的费用,老公背起行李…

OkHttp自定义重试次数

本文主要应用了OkHttp的Interceptor来实现自定义重试次数 虽然OkHttp自带retryOnConnectionFailure(true)方法可以实现重试&#xff0c;但是不支持自定义重试次数&#xff0c;所以有时并不能满足我们的需求。 #1.自定义重试拦截器&#xff1a; /*** 重试拦截器*/ public clas…

Ruby的库路径

irb下打入p $即显示出来 如: irb(main):003:0* p $: ["c:/ruby/lib/ruby/site_ruby/1.8", "c:/ruby/lib/ruby/site_ruby/1.8/i386-msvcrt", "c:/ruby/lib/ruby/site_ruby", "c:/ruby/lib/ruby/1.8", "c:/ruby/lib/ruby/1.8/i38 …

【快报】基于K2 BPM的新一代协同办公门户实践交流会

2014年2月28日&#xff0c;“基于BPM的新一代协同办公门户”用户实践交流活动在深圳金茂JW万豪酒店3楼Meet Room IV举办。本次会议由K2携手微软共同举办&#xff0c;邀请到的参会企业都是K2 的BPM老客户或对BPM新一代协同办公门户非常感兴趣的新朋友&#xff0c;他们有&#xf…

80个让你笑爆肚皮的程序员段子,不好笑算我输!

阅读本文大概需要 12 分钟。 1、折磨 对程序员的一般折磨&#xff1a;离奇的需求&#xff0c;对程序员的超级折磨&#xff1a;改三次需求~~~ 2、开发时间 项目经理说&#xff1a;给你三个人&#xff0c;多久可以完成&#xff1f;程序员&#xff1a;3个月&#xff01;那五个人呢…

Spring Integration Java DSL示例–使用Jms名称空间工厂进一步简化

在较早的博客文章中&#xff0c;我谈到了虚拟卢布戈德堡流程&#xff0c;该流程通过一系列复杂的步骤将字符串变成大写&#xff0c;本文的前提是引入Spring Integration Java DSL&#xff0c;作为通过xml配置文件定义集成流程的替代方法。 感谢Artem Bilan &#xff0c;在写完…

冯诺依曼计算机结构教案,冯诺依曼结构的计算机-同济大学精品课程.PPT

冯诺依曼结构的计算机-同济大学精品课程* 第一章 概 述 同济大学软件学院 课程的内容&#xff1a; 1&#xff0e;介绍单计算机系统的硬件组成 (内部结构、工作机制) 2&#xff0e;计算机自动工作的原理 3&#xff0e;在对工作原理了解的基础上&#xff0c;讲述有关计算机设 计的…

非安装版的PostgreSQL8.3.3的首次使用经历

第一次用Postgre&#xff0c;已经是好多年以前了&#xff0c;隐约记得是在linux下边&#xff0c;build半天&#xff0c;然后手动配置库文件之类。 今天在整理资料的时候发现&#xff0c;Postgre出现了解压缩直接使用的版本&#xff0c;上边注明了for expert only. 呵呵&#xf…

腾讯TBS X5 WebView的简单使用

工作中经常涉及H5网页的加载工作&#xff0c;最多使用的就是安卓系统控件WebView&#xff0c;但是当网页内容比较多的时候&#xff0c;需要等待很久才能加载完&#xff0c;加载完后用户才能看到网页中的内容&#xff0c;这样用户需要等很久&#xff0c;体验很差。 那能不能边加…

针对新手的Java EE7和Maven项目–第5部分–使用Arquillian / Wildfly 8进行单元测试

从前面的部分恢复 第1 部分 &#xff0c; 第2 部分 &#xff0c; 第3 部分 &#xff0c; 第4部分 &#xff0c; 这是第一篇“额外”的帖子&#xff0c;基于我正在此系列博客文章上“构建”的Maven JavaEE7演示项目。 我们已经定义了一个坚实的结构和一些模块。 在这样的应用程…

C/S,B/S的区别与联系

C/S 是Client/Server 的缩写。服务器通常采用高性能的PC、工作站或小型机&#xff0c;并采用 大型数据库系统&#xff0c;如Oracle、Sybase、Informix 或SQL Server。客户端需要安装专用的客户 端软件。 B/&#xff33;是Brower/Server 的缩写&#xff0c;客户机上只要安装一个…

五年级下册电子计算机与多媒体,语文人教版五年级下册《电子计算机与多媒体》.doc...

《电子计算机与多媒体》教学设计沈彦坤 石家庄市鹿泉区石井乡封庄小学?一、概述??语文&#xff0c;五年级五年级下册&#xff0c;第一课时熟悉课文内容,学会生字,理解新词意思&#xff0c;学习课文1-3段。了解计算机的产生与发展&#xff0c;了解什么是多媒体,计算机与多媒体…

Airflow安装与使用

# Airflow 1.10安装本次安装Airflow版本为1.10&#xff0c;其需要依赖Python和DB&#xff0c;本次选择的DB为Mysql。本次安装组件及版本如下&#xff1a;Airflow 1.10.0Python 3.6.5Mysql 5.7# 整体流程1. 建表2. 安装3. 配置4. 运行5. 配置任务启动scheduleairflow schedul…

Wordpress安装

Wordpress是一种用PHP语言和MySQL的数据库的开源的免费Blog引擎&#xff0c;用户可以在支持PHP和MySQL数据库的服务器上建立自己的Blog,它具有非常强大的功能和成千上万的插件和模板主题。安装步骤如下(本文以Wordpress2.6中文版为例): (1) 在MySQL中创建Wordpress库&#xff0…

python 内置标准库socketserver模块的思考

socketserver模块简化了编写网络服务器的任务, 在很大程度上封装了一些操作, 你可以看成是事件驱动型的设计, 这很不错。它定义了两个最基本的类--服务器类 BaseServer, 请求处理类 BaseRequestHandler. BaseServer 基本服务器类封装了基本的一些socket操作, socket原语中对so…

关于素数的一些定理

1.何谓素数&#xff1f; 指在一个大于1的整数中&#xff0c;如果一个数只能整除1与本身&#xff0c;则该数为素数&#xff08;质数&#xff09;&#xff0c;否则为合数。 1既不是素数也不是合数 2.素数个数无限多 3.所有大于2的素数都可以唯一的表示为两个平方数之差 pa^2-b^2(…

教师节的感触

刚喝了点酒&#xff0c;因为今天是教师节&#xff0c;我觉得博客是一个很好的平台&#xff0c;可以把自己的一些感想就是的发表出来。把音乐放出来一 边写着博客是一种很自我的感觉&#xff0c;可以在微酣的时候把自己的点滴感受好好的梳理一下倒是一种不错的方式。这是自己第一…

Spring 4.1和Java 8:java.util.Optional

由于Spring 4.1的Java 8的的java.util.Optional &#xff0c;容器对象可能会或可能不包含非空值&#xff0c;支持与RequestParam &#xff0c; RequestHeader和MatrixVariable 。 使用Java 8的java.util.Optional请确保参数永远不会为null 。 请求参数 在此示例中&#xff0c;…

计算机网络层实验路由表苏州科技,苏州科技大学计算机网络实验报告课案.docx...

苏州科技大学计算机网络实验报告课案苏州科技学院 电子信息实验中心实验报告课  程计算机网络原理学    名徐金玮班  级计算机1312专  业计算机科学与技术指导教师陶滔学年 / 学期2015~2016学年第一学期实验一 物理层实验实验项目性质&#xff1a;设计性  计划学…

Fibonacci again and again

Fibonacci again and again http://acm.hdu.edu.cn/showproblem.php?pid1848 Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 12494 Accepted Submission(s): 5439 Problem Description任何一个大学生对菲波那…