【FCon】聚焦金融行业在数智化的全面革新,一线的金融数智化实践干货 了解详情
写点什么

JAVA 服务治理实践之无侵入的应用服务监控

  • 2020-02-14
  • 本文字数:7235 字

    阅读完需:约 24 分钟

JAVA服务治理实践之无侵入的应用服务监控

6 月 17 日,极客时间《企业级 Agents 开发实战营》正式上线,10 周掌握企业级 Agents 从设计、开发到部署全流程。

之前在分享微智能的话题中提到了应用服务监控,本文将会着重介绍 Java 环境下如何实现无侵入的监控,以及无侵入模式对实现各种技术架构统一服务治理的意义,还会破解“监控系统如何监控自己”的悖论。此次分享包含宜信众多关键技术实践和落地办法,内容提纲如下:


  1. 服务治理监控,机房监控,APM 的区别与联系

  2. “无侵入”的应用服务监控

  3. 无侵入引领统一服务治理

  4. 打破悖论:监控系统如何监控自己


在开始之前,先解释一下几个概念。首先,APM(Application Performance Management)即应用性能管理,按照 Gartner 提出的抽象模式,它应该涵盖以下内容:


  1. End User Experience:关注终端用户对性能的真实体验

  2. Runtime Application Architecture:应该反映应用的架构

  3. Business Transactions:能够支持分析应用与用户交互的操作事务

  4. Deep Dive Component Monitoring:深度应用诊断,特别是代码级的性能追踪

  5. Analytics / Reporting:按照业务模型对大量性能数据进行(实时)精确分析


下图是 Gartner 对 APM 的抽象模型。APM 领域目前已经有不少商用系统,比如国外的 Dynatrace, IBM APM,国内的 OneAPM,听云 APM 等等。



接下来说说机房监控。机房监控又被称为 IT 监控,这是咱们最常见的监控系统类别,它一般涵盖以下内容:


  1. 操作系统级监控:例如 CPU、内存、网络流量、磁盘 IO、连接数、系统日志等等。

  2. 基础设施监控:例如交换机、路由器负载、存储设备 IO 等等。

  3. 基础设施环境监控:例如 UPS 电源、动力监控、环境温度、电力负荷监控等等。


以操作系统级监控为主的机房监控,目前有不少的实现,例如 Nagios、Zabbix、Open-Falcon;针对基础设施的监控以及环境的监控通常需要借助额外的硬件采集设备来完成。


最后说说服务治理监控。先简单解释一下什么是服务治理。服务治理是针对面向服务架构的系统进行监控和管理的过程。面向服务的架构包括传统 SOA,分布式服务,微服务等。


服务治理的核心内容涵盖四个层面:


  1. 服务注册与发现:服务接口信息被注册到服务配置中心的过程;服务调用方可以通过服务唯一标识从服务配置中心查找服务接口信息。

  2. 应用/服务监控:针对应用以及服务接口进行监控,包括性能监控,业务指标监控,安全监控等。

  3. 服务 SLA 协调:评估并量化各个层面的服务等级指标,这些层面包括应用,应用实例,服务实例,服务接口等。

  4. 服务运行时控制:是服务之间调用过程的干预,包括路由规则,负载均衡,Failover 切换,服务安全,保护降级,弹性伸缩。


服务治理的四个核心内容实际上也是一个层级关系(Layers Topology),上层的实现需要依赖下层的实现。


服务治理的监控主要涉及服务注册与发现和应用服务监控两个层级。在实践中,我们又将这两个层级扩展了辅助层级:


  1. 服务画像:对服务接口特征信息(技术协议、入参出参、方法、类等)进行描述。

  2. 应用画像:对应用的架构、组成、技术栈、部署信息进行描述。

  3. 应用上下文画像:应用的运行是受上下文环境的影响,包括应用所处的容器(物理机 OS、虚拟机、Docker 等),同一个容器内的兄弟服务进程的状态描述。

  4. 应用上下文监控:针对上下文环境的容器,同一容器内的兄弟服务进程的性能监控。


下图展示服务治理的层级关系和服务治理监控包含的内容:



这三者由于关注点不同,所以在各自领域会有差异,但也有交集的地方。根据需要也可扩展各自的外延。


下图展示了 APM,服务治理监控,机房监控的区别与联系:



“无侵入”的应用服务监控


这个部分主要介绍如何实现 JAVA 环境下的“无侵入”应用服务监控。如前文提到,要实现应用服务监控,就要先实现服务注册。


经典的服务注册方法有两种:


  • 显式配置:通过人工将服务的接口信息(服务名,服务 URI 等)通过配置的方式存储到服务注册中心。经典的 WebService UDDI 就是这种模式。这种方式的问题在于服务接口信息都是由人工收集的,出现滞后或者谬误可能性高,较高的运维代价,无法适应快速迭代的节奏。

  • 代码实现:通过代码调用服务注册中心客户端,将服务的接口信息发送到服务注册中心。使用 ZooKeeper 客户端实现服务注册就是这种模式。这种方式的进步之处在于服务接口的 URI 可能是通过代码收集出来的,例如获得 IP,context 路径,接口相对地址从而拼接成服务接口的 URI。


我们在早期服务化实现中,采用的是这种模式,但它的问题是需要代码埋点,也就是“侵入”,这引发了如下问题:


  1. 与服务注册中心客户端的紧耦合:如果使用 ZooKeeper,需要依赖它的 jar 包。

  2. 服务注册代码与服务接口代码上下文紧耦合:必须在特定位置去使用服务注册的代码,而且可能还会包含特定服务的信息,这些信息可能是人工编排进去的。

  3. 由于不同系统是由不同团队开发的,需要行政制度,“TopDown”规定服务注册的编程,一旦有“不按套路出牌”的情况就会出现各种运维问题。


无侵入的服务注册思路也用到了”微智能”的思想。


  • 全自动的收集应用实例,服务实例,服务接口的信息。这些信息包括应用唯一标识(AppID),服务名(Service ID),服务实例的 URI,服务接口的 URI,服务接口的元数据(类,方法,入参出参,注解,部署描述符),即自动发现。

  • 收集过程对应用透明,不可有任何直接依赖(API 依赖,jar 包依赖),对系统的研发团队同样透明,即无侵入。

  • 收集过程能够自动适应应用,服务,服务接口的变化,即自我维护。


接下来分析一下 JEE 应用的特点:


  • 以应用服务器(Tomcat,Jetty,IBM WAS,JBoss)为容器,JEE 应用的启动或停止都被其应用容器感知。

  • 遵守 JEE 规范,服务以 servlet,JAXWS,JAXRS 等落地;或遵守“事实”规范,比如 Spring,服务以 Spring MVC 落地。

  • servlet 是 HTTP 的唯一入口(当然还有 RMI,RPC 等,其实类似,这里不做详细展开)。


于是解决方案如下:


从应用服务器的层面来对应用进行画像,即应用画像,服务画像。这个过程发生在每次应用启动的时候,因为这样就能自然捕获应用的变化。就好比电影《星际穿越》里,从四维空间能够更容易,更直接解决三维空间的问题,且更具通用性。


这里用到两种技术:


  • 中间件劫持

  • 应用画像技术


中间件劫持就是将我们自己的代码行为植入到中间件的各种行为中。实现画像和监控主要依靠四种关键行为:应用启动,停止,接收请求,响应回复。


  • 应用启动:用于应用画像,服务画像

  • 应用停止:失效服务摘除

  • 接收请求,响应回复:用于应用,服务监控(稍后会介绍)


对 JEE 应用服务器的劫持核心是掌控 classloader tree,获得优先加载权,从而可以改变这些行为。尽管各家实现不同,但其 classloader tree 结构基本类似。


下面以 Tomcat 为例进行说明,下图是 Tomcat 的 classloader tree:



值得注意的是,每个 JEE 应用都会被分配一个 WebAppClassloader 来加载其所有 class。那么如果我们能够感知应用启动的行为,通过 WebAppClassloader 就可以收集到前文提到的各种画像信息。


那么我们需要植入一个自己的 classloader 来获取优先加载权。通过加载改写后的 class,来改变行为。我们把这个 classloader 成为 UAVClassLoader(无人机类加载器)。



UAVClassLoader 算法基本原理


  • UAVClassLoader 创建时,将能够读取到的 Class 文件对应的 Class 名存储到 ClassMap 中

  • 将 TomcatLoader 设置为 UAVClassLoader 的 Parent

  • 将 UAVClassLoader 设置为 TomcatLoader 的一个属性

  • 重写 TomcatLoader 的 loadClass 方法

  • 1、如果 UseUAVClassLoaderFlag 为 true,则使用 UAVClassLoader.loadClass

  • 2、加载成功则返回 Class

  • 3、失败则使用 TomcatLoader 自己的 loadClass

  • UAVClassLoader 的 LoadClass 方法

  • 1、如果 ClassMap 中含有要加载的 Class,则使用自己的 findClass 加载 Class

  • 2、否则,将 UseUAVClassLoaderFlag 设置为 false

  • 3、使用 TomcatLoader.loadClass(注:这时 TomcatLoader 会直接用自己的 loadClass)

  • 将 UsePlusLoader Flag 设置为 true


具备中间件劫持能力之后,就可以进行应用画像和服务画像了。


JEE 应用服务器的应用启动实际是 Web 容器的创建过程。在 Tomcat 中的 StandardContext 就是 Web 容器的根类,在其加载的时候,UAVClassLoader 会感知,通过改写或 bytecode weave 手段在其 start 方法的最后植入代码,完成两个步骤:


  • 收集将 Web 容器的上文信息:包括 WebAppClassLoader 实例、Context Path、应用名、ServletContext、BasePath(应用实际路径)、WorkDir(应用工作目录)等。

  • 植入应用、服务画像的代码。


应用画像包含了应用相关信息的收集,下面列举几个关键画像信息:


  1. 应用标识(AppID):先取部署的应用名(由应用服务器配置决定),如果应用名为空则取 Context Path(通常可能就是 war 包的名字),应用标识对于应用实例自动归类有妙用,就是实现应用实例自动归类。

  2. 应用名称:使用 WebAppClassLoader 可以获得 web.xml 的路径,通过解析 web.xml 提取 display-name(这个也是 servlet 规范),如果为空,则使用应用标识作为名称。

  3. 应用的 URI:应用 URI=http(s)://

  4. 应用的类库信息:通过 WebAppClassLoader 可以获取所有类库信息,通过类库信息可以掌握应用的技术栈,可以扩展做很多有趣的事情。


服务画像是按照技术规范(参见 JEE 应用的特点 2),常见的技术规范:


  1. Servlet 规范

  2. JAXWS 规范

  3. JAXRS 规范

  4. Spring 规范

  5. RMI 规范

  6. RPC 规范(Netty,Thrift,Hessian 等)


针对每种技术规范从 3 个方面进行收集:


  1. Class 和 Method:通过 Java 的反射方式提取信息,如服务类名,方法名,入参出参。

  2. Annotation:通过注解扫描工具提取具有相关注解的类,然后通过注解 API 提取注解信息。

  3. 部署描述符:通过 WebAppClassLoader 获取 web.xml, spring-config.xml, log4j.xml 等部署描述符文件路径,然后使用 DOM 解析提取关注的 tag 信息。


下面以 Servlet 为例对服务画像过程进行说明:


  1. 使用 FastClasspathScanner(轻量的开源类扫描工具)将带有 javax.servlet.annotation.WebServlet( Servlet 3.0 的注解类)的 Class 扫描,这个过程需要 WebAppClassLoader 支持。并提取注解的信息(比如 urlPatterns,loadOnStartup)。

  2. 通过 WebAppClassLoader 获取应用的实际路径(BasePath),而 web.xml 就在/WEB-INF 下,加载 web.xml 提取所有的元素值(就是 servlet class),同时也提取,等元素值。

  3. 对通过注解获得的 Servlet 与通过 web.xml 获得的 Servlet 进行合并。

  4. 3.1 只有注解有或只有 web.xml 有的,直接保留。

  5. 3.2 web.xml 与注解重叠的 servlet,保留 web.xml 的信息(Servlet 规范,部署描述符替换注解)

  6. 对 Servlet 画像数据进行整理


4.1 ServiceID:由 Servlet Class 定义,保证唯一性


4.2 Service URI:ServiceURI=


JAXWS,JAXRS,SpringMVC 等的画像过程基本一致,主要区别在第 1 步时,需要通过注解提取“服务接口的相对路径”信息。例如:


JAXWS 需要服务名


ServiceURI=


JAXRS 或 SpringMVC 的每个服务接口路径都是到方法级的


ServiceURI=



有同学可能会问:“讲了半天,虽然我们已经拿到了应用,服务的画像数据,哪服务注册是怎么发生的呢?”这个问题会在第三部分揭秘。


接下来,看看如何实现“无侵入”的应用服务监控。应用服务监控实际上是对应用实例,服务实例,服务接口的性能指标进行捕获的过程,常用的性能指标:响应时间,请求计数,错误计数,响应代码计数等等。这些值的捕获是发生在请求进入和出去的地方。


下图是 JEE 应用服务器的 Http CallFlow 展示了 HTTP 的请求响应过程:




应用服务监控的落地方法:


  • 运用中间件劫持技术改写 CoyoteAdaptor.service()方法, 它负责整个 Tomcat 的请求处理,在方法开头拦截请求,方法结尾拦截响应。这里可以监控应用服务器,应用,所有的 URL 的性能指标。

  • 运用中间件劫持技术改写 StandardWrapper.service()方法,它负责 Servlet 的请求处理,同上如法炮制。这里可以监控所有 Http 服务的性能指标(参见 JEE 应用特点 3)。



总结起来,通过中间件劫持和应用画像技术,可以轻松的实现对应用/服务的画像以及监控。由于 Tomcat 的服务器架构比较古老,所以我们采用了改写或 bytecode weave 的方式,但是也仅仅只是改写了 Tomcat 的 3 个方法。如果是 Jetty,可以通过实现它的 InterceptChain 来实现,完全没有代码改写,只是增加了植入代码。而 Jboss 可以通过扩展它的 listener 来实现,也没有代码改写。


那么捕获到这些数据之后如何监控呢?这也会在第三部分解答。


无侵入模式引领统一服务治理


无侵入模式除了解决前面提到的诸多技术问题以外,还与我们自身的实际需要相关。


  1. 面临“微服务”的转型关口,但十年沉淀下来上百的系统也需要时间来逐步重构,这个过程可能很长。

  2. 各个系统虽然基本都是 JAVA 的,但是使用的 JEE 技术还是各有不同的,比如 JAXWS,JAXRS,纯 Servlet,SpringMVC,Thrift,SpringBoot 等等。

  3. 系统架构差异大,包含单体架构,分布式服务架构,半 SOA 化架构,微服务架构。

  4. 系统的迭代很快,几乎每天都有更新,新型技术栈引入的可能不断增加。


从服务治理的角度,非统一的技术栈意味着像 Dubbo 之类统一技术栈的玩法不可行。所以“反转”这个思路,把从以服务调用技术栈为中心的治理方式转向以服务自动画像与注册为基础,逐步接管调用链路的治理方式。


而对 JEE 应用完全无侵入的模式恰好适应了这一需求。


我们的服务治理系统的代号叫无人机(UAV),寓意是无人机能够 7*24 的不间断巡航,随时随地收集地理,建筑,物品,人的变化,随时随地监控他们的行为,甚至精确的打击或操控。


我们把 UAV 定位为统一服务治理的模式,一种与“服务调用”技术栈基本无关的治理方法。


下图是 UAV 的架构图:



接下来解答第二部分遗留的两个问题:


  • 收集了应用和服务画像之后,如何实现服务注册

  • 捕获到监控数据之后如何监控呢


先来看看 UAV 的捕获流图:



数据捕获步骤:


  1. 无论是画像数据,还是性能数据(实时数据)暂存在内存中。JEE 应用服务器中可以暂存到 MBean 中

  2. 监控代理程序每隔 5 秒(可调节),采集 Mbean 中的画像和性能数据

  3. 监控代理程序将收集的数据发送给消息系统

  4. 健康管理程序负责各种数据落地

  5. 同时健康管理程序会提取服务实例以及接口的信息注册到缓存中,这个缓存作为服务注册的存储中心。由于每个一定周期(5 秒)都有画像数据推送,也意味着维持了服务心跳


下面的图展示了实现细节:





通过 UAV 的捕获流图,可以发现在数据传输格式上采用了统一的 Schema,实现对画像数据,性能数据,日志数据的统一。这样不仅仅统一了三种数据的传输,也对实现后期数据的各种转换和处理提供了统一的处理模板,具有更好的扩展性。


下图是性能监控数据的 Sample:



下图是画像数据的 Sample:



打破悖论:监控系统如何监控自己


监控系统通常都面临一个问题:如何监控自己。监控系统不能监控自己的原因也很明显,因为不能处理来自自身的异常。


其实破解之法也很直接,就是“冗余”。不过这里“冗余”不是简单的冗余资源,而是在处理机制上实现“冗余”。


破解之法:双通道+双心跳


UAV 采用双通道+双心跳的方式:


  1. 双通道就是一条 Http 传输通道,用来传输容器/节点画像数据和监控数据;一条 MQ 传输通道,用来传输实时数据,画像数据,日志数据。

  2. 双心跳是指不管来自 Http 通道还是 MQ 通道的数据实际上即可以看成业务数据,也可以看成心跳数据(远端的节点还活着并且在工作)。

  3. 来自每个通道数据都会通过健康管理程序“签到”。因此 UAV 的任何节点(监控代理程序,健康管理程序)出现宕机,都能够被发现;并且它们的进程状态,应用状态也被自己监控。



这样的做法并不是为了冗余而冗余,而是有以下考虑:


  1. 从分布式系统的考虑,UAV 实现了心跳服务,并且允许多活,也允许多级心跳上行,那么 Http 通信方式更加适合这样的场景;同时,Http 通信意味着每次携带的数据 payload 不能太大,所以更适合容器,节点的画像和监控数据,这些数据以及指标比较固定。

  2. 应用,服务的个数是未知的,且无论是画像还是指标(性能,业务等)也可能很多,意味着数据 payload 可能较大,而 MQ 适合 payload 较大的场景;MQ 可以一定程度保证数据有序,且队列可以暂时持久化数据,防止了由于接收端宕机导致的数据丢失;同时“多活”的消费者,可以动态扩展,也能在某些消费者宕机后,快速接替继续消费。

  3. 两种通信方式意味着更高的可靠性,即便当某些服务不可用时,监控系统的另一部分依然可以继续工作。例如如果 UAV 的实时数据服务都挂了,那么应用的性能数据就看不了,但是应用的进程(比如 Tomcat)的性能数据还是能看的。


下面是一部分 UAV 实际监控的效果图。


应用容器监控:包含 UAV 节点,所有服务进程,JAVA 进程的监控



容器画像,UAV 节点画像,进程画像



进程监控:Tomcat 为例



应用监控:应用,应用实例



应用画像,服务画像



应用监控



JEE 应用服务器监控:


Tomcat 为例



服务监控



应用日志监控



最后总结一下:


  • APM,服务治理监控,机房监控由于关注点不同,所以监控内容有差异,但也有交集。服务治理监控可以根据实际需要扩展外延到 APM 或机房监控的范畴。

  • “无侵入”应用服务监控的关键是实现中间件劫持和应用画像技术。

  • 无侵入模式的玩法颠覆了传统以服务调用技术栈为中心的服务治理模式,可以容纳更多技术栈,具有更好的可扩展性,适合混合架构下的服务治理需要。

  • 监控系统如何监控自己的破解之法就是实现双通道+双心跳,从容器、进程的角度,或从应用,服务的角度来两个维度来落地心跳和性能监控。


嘉宾介绍


张真,目前就职宜信技术研发中心,高级架构师。主要负责基础系统架构演进与优化,服务治理,监控平台,微服务建设,DevOps 平台,自动化测试框架以及电子签约,短信,邮件等应用系统。


一直都从事分布式系统的研发,早年就职于 IBM 中国研发中心,负责 IBM WebSphere 应用服务器包括传统 WAS,下一代轻量级 JEE 应用服务器 Liberty 等的设计与开发,及应用服务器对云计算,移动平台的支持,并为银行、电商客户提供技术咨询和服务。


个人也比较喜欢参与开源社区贡献,如 Cloud Foundry、Apache CXF、Apache Wink 等。目前主要关注微服务架构实施,微智能设计思想应用,虚拟化技术应用,共识计算研究,十分欢迎和大家广泛的,深入的交流。


本文转载自宜信技术学院网站。


原文链接:http://college.creditease.cn/detail/111


2020-02-14 18:461587

评论

发布
暂无评论
发现更多内容

什么容易被记住——造梦师指南

Justin

心理学 28天写作 游戏设计

工业互联网助力数字中国建设(新论)

浪潮云

工业互联网

西北大学研发猴脸识别技术,金丝猴可刷脸打卡;IJCAI 2020丨基于学习实例隐空间的文本风格转换

京东科技开发者

人脸识别 IT 量子通讯

我的配置中心知识整理

老白鹿

微服务 技术选型 配置中心 配置管理

压力太大的话,就放点气儿吧

道伟

28天写作

如何解决深度推荐系统中的Embedding冷启动问题?

王喆

机器学习 深度学习 推荐系统 计算广告 Embedding

线程池面试必考

叫练

面试 线程池 线程池工作原理

go get下载包失败问题

happlyfox

28天写作 2月春节不断更 Go 语言

读书笔记-MySQL

yunCrush

MySQL

Fast AI人工智能审图平台-建筑图纸设计效率的倍增器

AI AI审图

LiteOS:盘点那些重要的数据结构

华为云开发者联盟

源码 数据结构 LiteOS LOS_DL_LIST Priority Queue

Kafka.05 - 生产者与消费者介绍

insight

kafak 2月春节不断更

“云原生”的应用价值及关键属性解读

浪潮云

透彻解析!如何快速的开发一个完整的直播app,薪资翻倍

欢喜学安卓

android 程序员 面试 移动开发

从磁盘读取成本分析两种 100% 遍历思路:按格子遍历 & 按线遍历

宫水三叶的刷题日记

面试 LeetCode 数据结构与算法

产业实践推动科技创新,京东科技集团3篇论文入选ICASSP 2021

京东科技开发者

人工智能 机器学习 信号 语音识别

诊所数字化:最大的数据资产-患者数字档案内容

boshi

电子病历 数字化医疗 七日更 28天写作

Java lambda表达式人类使用指南

ES_her0

28天写作

关于GaussDB(DWS)的正则表达式知多少?人人都能看得懂的详解来了!

华为云开发者联盟

正则表达式 GaussDB

话题讨论 | 技术人员的职业发展困惑,你也有么?

架构精进之路

职业规划 话题讨论 28天写作 技术人员

上天的源码要不要——GitHub 热点速览 v.21.08

HelloGitHub

Python GitHub 开源 Go 语言

Selenium 常用方法与属性、鼠标悬停与 Select 操作

梦想橡皮擦

Python 28天写作 2月春节不断更

(28DW-S8-Day3) 比特币、 区块链是什么?

mtfelix

比特币 区块链 28天写作

这些面试题你会吗?连续四年百度Android岗必问面试题!深度好文

欢喜学安卓

android 程序员 面试 移动开发

前端面试常考题:JS垃圾回收机制

华为云开发者联盟

JavaScript Vue 大前端 js 垃圾回收

解决dyld: Library not loaded icu4c

一个大红包

brew icu4c

产品经理训练营第四周总结

产品经理训练营

Mybatis【20】-- Mybatis延迟加载怎么处理?

秦怀杂货店

数据库 缓存 mybatis 加载

基于matlab的控制系统与仿真2-传递函数模型

AXYZdong

matlab 2月春节不断更

透彻解析!在字节跳动我是如何当面试官的,讲的明明白白!

欢喜学安卓

android 程序员 面试 移动开发

【进阶】面试官问我Chrome浏览器的渲染原理(6000字长文)

我是哪吒

学习 程序员 chorme 28天写作 2月春节不断更

JAVA服务治理实践之无侵入的应用服务监控_文化 & 方法_张真_InfoQ精选文章