前言
我们在工作中一定遇见过这样一种情况。我们的产品依赖很多个服务,这些服务可能分配在不同的机器上。我们在测试的时候动不动就会碰到我们认为的bug,例如我们在下单的时候失败了,可能原因很简单,依赖的某一个服务挂了,重起一下就好了。好一点的测试人员就会挨个去看所有依赖服务的log,看看问题出在哪。而一般的测试人员呢,就会把所有开发挨个找一遍或者干脆提个bug。而然所有这些情况效率都不高,尤其是第二种情况会占用开发人员的时间。举一个我见到过的一个特别的例子,我们现在的产品以分析大数据并建立模型为主要业务,科学家团队写的机器学习算法很复杂,建立一个模型可能也要10几20轮的模型训练过程。测试环境的hadoop集群也很小。所以导致跑一个几十G的数据都要数小时的时间。所以我的同事都习惯提交完任务就跑去做别的了,过几个小时再回来看看什么情况。结果好几次都是因为底层的某个服务在跑之前或者跑得过程中就因为各种原因挂掉了。于是几个小时浪费掉了。所以我们需要有一种机制能帮我们及时发现这种服务级别的问题,并准确定位问题的位置以增加我们的工作效率。这就是我们的监控。
我们都知道生产环境的监控是质量保证很重要的一环,不仅仅是能及时的发现错误。针对特定目标的监控可以帮助我们快速定位问题。针对产品环境的监控是个比较大的话题。其实我也搞不好。在我的另一个帖子里关于监控的讨论说过我以前的公司是怎么做监控的。但那些工具一般都比较复杂,很少有测试人员能hold的住。事实上我以往的几个公司,监控大部分都是由运维和开发做的。我们今天就讨论一下简易的,能够用在测试环境的服务级别的监控怎么做吧。
原理
原理其实很简单。我们知道所有的服务都是监听着一个端口号的,所以我们只要尝试使用与这个端口号进行TCP连接并发一个包过去就行了。如果没有抱错,那么说明监听这个端口号的服务是正确的。所以我们一般管这个机制叫发包器。用java代码表示就是特简单的一个Socket就解决了。
实现
首先我们需要一个可以供用户方便注册的地方,用户在这里写监控的信息,例如ip,端口号,责任人的email等等。如下面的例子。
<monitor>
<environment name="test">
<service ip="172.27.12.121" name="API tomcat" port="8080" email="sungaofei@4paradigm.com"/>
</environment>
<environment name="product">
<service ip="192.1.1.1" name="1111" port="8080" email="sungaofei@4paradigm.com"/>
</environment>
</monitor>
上面我们用xml来注册被监控的信息。其实如果后面扩展成监控中心的话,注册机制是要存在数据库中的。我们第一版的简易监控就使用xml就足够了。然后我们定义一个接口,所有监控程序也就是发包器要实现的接口。为什么单独拉一个接口出来,为了以后的扩展. 建立socket只是一种监控方式,以后可能还监控http借口,RPC接口. 他们发包的形式都不一样,但是我们需要统一管理这些发包器. 也就是我们的外部模块不用管监控的是什么,只要知道调用发包器类去发包就行了..
public interface SendRequest extends Callable<Answer>{
}
可以看到这个接口继承了Callable接口,这是java中实现并发编程的一个接口(还有一个接口是runnable),我们的监控程序必须要在多线程下执行,这是为了执行效率,我们的每个服务可能都有一个比较长的超时时间。要是单线程执行的话,监控的服务一旦多起来,可就慢的跟蜗牛一样了。我们看到Callable这个线程接口还有一个泛型Answer ,它规定了这个线程执行后的返回值。也就是规定了我们的监控结果。我们看看可以把它定义成什么样子吧。
@Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class Answer { private Boolean ifSucessed = false; private String mailSubject; private String[] mailToPerson; private String mailContent; public Boolean getIfSucessed() { return ifSucessed; } public void setIfSucessed(Boolean ifSucessed) { this.ifSucessed = ifSucessed; } public String getMailSubject() { return mailSubject; } public void setMailSubject(String mailSubject) { this.mailSubject = mailSubject; } public String[] getMailToPerson() { return mailToPerson; } public void setMailToPerson(String[] mailToPerson) { this.mailToPerson = mailToPerson; } public String getMailContent() { return mailContent; } public void setMailContent(String mailContent) { this.mailContent = mailContent; } } |