MQ

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

2017-07-17  本文已影响151人  王帅199207

转自:http://chuansong.me/n/603660351655http://www.cnblogs.com/davidwang456/articles/6544573.html

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

  1. 服务治理监控,机房监控,APM的区别与联系
  2. “无侵入”的应用服务监控
  3. 无侵入引领统一服务治理
  4. 打破悖论:监控系统如何监控自己

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

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


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

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

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

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

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

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

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


这三者由于关注点不同,所以在各自领域会有差异,但也有交集的地方。根据需要也可扩展各自的外延。
下图展示了APM,服务治理监控,机房监控的区别与联系:

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

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

我们在早期服务化实现中,采用的是这种模式,但它的问题是需要代码埋点,也就是“侵入”,这引发了如下问题:
与服务注册中心客户端的紧耦合:如果使用ZooKeeper,需要依赖它的jar包。

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

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

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

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

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

这里用到两种技术:

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

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

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

值得注意的是,每个JEE应用都会被分配一个WebAppClassloader来加载其所有class。那么如果我们能够感知应用启动的行为,通过WebAppClassloader就可以收集到前文提到的各种画像信息。
那么我们需要植入一个自己的classloader来获取优先加载权。通过加载改写后的class,来改变行为。我们把这个classloader称为UAVClassLoader(无人机类加载器)。


UAVClassLoader算法基本原理

具备中间件劫持能力之后,就可以进行应用画像和服务画像了。
JEE应用服务器的应用启动实际是Web容器的创建过程。在Tomcat中的StandardContext就是Web容器的根类,在其加载的时候,UAVClassLoader会感知,通过改写或bytecode weave手段在其start方法的最后植入代码,完成两个步骤:

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

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

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

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

针对每种技术规范从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进行合并。
    3.1 只有注解有或只有web.xml有的,直接保留。
    3.2 web.xml与注解重叠的servlet,保留web.xml的信息(Servlet规范,部署描述符替换注解)

  4. 对Servlet画像数据进行整理
    4.1 ServiceID:由Servlet Class定义,保证唯一性
    4.2 Service URI:ServiceURI=<应用URI>+
    JAXWS,JAXRS,SpringMVC等的画像过程基本一致,主要区别在第1步时,需要通过注解提取“服务接口的相对路径”信息。例如:

JAXWS需要服务名
ServiceURI=<应用URI>++

JAXRS或SpringMVC的每个服务接口路径都是到方法级的
ServiceURI=<应用URI>+++

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

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

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



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

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

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

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


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

无侵入模式除了解决前面提到的诸多技术问题以外,还与我们自身的实际需要相关。
面临“微服务”的转型关口,但十年沉淀下来上百的系统也需要时间来逐步重构,这个过程可能很长。

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

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

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

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

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

我们的服务治理系统的代号叫无人机(UAV),寓意是无人机能够7*24的不间断巡航,随时随地收集地理,建筑,物品,人的变化,随时随地监控他们的行为,甚至精确的打击或操控。
我们把UAV定位为统一服务治理的模式,一种与“服务调用”技术栈基本无关的治理方法。

下图是UAV的架构图:



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

先来看看UAV的捕获流图:


数据捕获步骤:

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




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

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


下图是画像数据的Sample:


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

监控系统通常都面临一个问题:如何监控自己。监控系统不能监控自己的原因也很明显,因为不能处理来自自身的异常。
其实破解之法也很直接,就是“冗余”。不过这里“冗余”不是简单的冗余资源,而是在处理机制上实现“冗余”。

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

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

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

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

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

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

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

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

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



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



进程监控:Tomcat为例

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



应用画像,服务画像

应用监控
JEE应用服务器监控:

Tomcat为例



服务监控



应用日志监控

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

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

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

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

上一篇 下一篇

猜你喜欢

热点阅读