如要监控Storm集群和运行在其上的Topology,该如何做呢?
Storm已经为你考虑到了,Storm支持Thrift的C/S架构,在部署Nimbus组件的机器上启动一个Thrift Server进程来提供服务,我们可以通过编写一个Thrift Client来请求Thrift Server,来获取你想得到的集群和Topology的相关数据,来接入监控平台,如Zabbix等,我目前使用的就是Zabbix。
整体的流程已经清楚了,下面就来实践吧。
1 安装Thrift
由于我们要使用Thrift来编译Storm的源代码来获得Thrift Client相关的Java源代码,所以需要先安装Thrift,这里选取的版本为0.9.2。
到官网下载好安装包:http://thrift.apache.org/
编译安装:configure && make && make install
验证:thrift --version
如果打印出Thrift version 0.9.2,代表安装成功。
2 编译Thrift Client代码
首先下载Storm源代码,这里使用最新的0.9.3版本:http://mirrors.hust.edu.cn/apache/storm/apache-storm-0.9.3/apache-storm-0.9.3-src.tar.gz
解压后进行编译:thrift -gen java apache-storm-0.9.3/storm-core/src/storm.thrift
在当前目录下出现gen-java文件夹,此文件夹下就是Thrift Client的Java源代码了。
3 使用Thrift Client API
然后创建一个Maven项目来进行执行监控数据的获取。
项目生成一个Jar文件,输入一些命令和自定义参数,然后输出结果。
以命令行的形式进行调用,这样可以方便的接入监控系统,当然使用形式可以根据自身情况施行。
创建好后,把gen-java生成的代码拷贝进来。
在pom.xml里引入Thrift对应版本的库:
[html] view plain copy
1. <dependency>
2. <groupId>org.apache.thrift</groupId>
3. <artifactId>libthrift</artifactId>
4. <version>0.9.2</version>
5. </dependency>
首先写一些Thrift相关的辅助类。
ClientInfo.java
[java] view plain copy
1. package com.damacheng009.storm.monitor.thrift;
2.
3. import org.apache.thrift.protocol.TBinaryProtocol;
4. import org.apache.thrift.transport.TFramedTransport;
5. import org.apache.thrift.transport.TSocket;
6.
7. import backtype.storm.generated.Nimbus;
8.
9. /**
10. * 代表一个Thrift Client的信息
11. * @author jb-xingchencheng
12. *
13. */
14. public class ClientInfo {
15. private TSocket tsocket;
16. private TFramedTransport tTransport;
17. private TBinaryProtocol tBinaryProtocol;
18. private Nimbus.Client client;
19.
20. public TSocket getTsocket() {
21. return tsocket;
22. }
23.
24. public void setTsocket(TSocket tsocket) {
25. this.tsocket = tsocket;
26. }
27.
28. public TFramedTransport gettTransport() {
29. return tTransport;
30. }
31.
32. public void settTransport(TFramedTransport tTransport) {
33. this.tTransport = tTransport;
34. }
35.
36. public TBinaryProtocol gettBinaryProtocol() {
37. return tBinaryProtocol;
38. }
39.
40. public void settBinaryProtocol(TBinaryProtocol tBinaryProtocol) {
41. this.tBinaryProtocol = tBinaryProtocol;
42. }
43.
44. public Nimbus.Client getClient() {
45. return client;
46. }
47.
48. public void setClient(Nimbus.Client client) {
49. this.client = client;
50. }
51. }
ClientManager.java
[java] view plain copy
1. package com.damacheng009.storm.monitor.thrift;
2.
3. import org.apache.thrift.protocol.TBinaryProtocol;
4. import org.apache.thrift.transport.TFramedTransport;
5. import org.apache.thrift.transport.TSocket;
6. import org.apache.thrift.transport.TTransportException;
7.
8. import backtype.storm.generated.Nimbus;
9.
10. /**
11. * Thrift Client管理类
12. * @author jb-xingchencheng
13. *
14. */
15. public class ClientManager {
16. public static ClientInfo getClient(String nimbusHost, int nimbusPort) throws TTransportException {
17. new ClientInfo();
18. new TSocket(nimbusHost, nimbusPort);
19. new TFramedTransport(tsocket);
20. new TBinaryProtocol(tTransport);
21. new Nimbus.Client(tBinaryProtocol);
22. tTransport.open();
23. client.setTsocket(tsocket);
24. client.settTransport(tTransport);
25. client.settBinaryProtocol(tBinaryProtocol);
26. client.setClient(c);
27.
28. return client;
29. }
30.
31. public static void closeClient(ClientInfo client) {
32. if (null == client) {
33. return;
34. }
35.
36. if (null != client.gettTransport()) {
37. client.gettTransport().close();
38. }
39.
40. if (null != client.getTsocket()) {
41. client.getTsocket().close();
42. }
43. }
44. }
然后就可以写自己的逻辑去获取集群和拓扑的数据了,Storm提供的UI界面上展示的数据基本都可以获取到,这里只举出一个简单的例子,我们想获得某个拓扑发生异常的次数,和发生的异常的堆栈。剩下的项目你可以随意的定制。
下面是入口类:
Main.java
[java] view plain copy
1. package com.damacheng009.storm.monitor;
2.
3. import com.damacheng009.storm.monitor.logic.Logic;
4.
5. /**
6. * 入口类
7. * @author jb-xingchencheng
8. *
9. */
10. public class Main {
11. // NIMBUS的信息
12. public static String NIMBUS_HOST = "192.168.180.36";
13. public static int NIMBUS_PORT = 6627;
14.
15. /**
16. * 命令格式 CMD(命令) [ARG0] [ARG1] ...(更多参数)
17. * @param args
18. */
19. public static void main(String[] args) {
20. if (args.length < 3) {
21. return;
22. }
23.
24. 0];
25. 1]);
26.
27. 2];
28. "-1";
29. if (cmd.equals("get_topo_exp_size")) {
30. 3];
31. result = Logic.getTopoExpSize(topoName);
32. else if (cmd.equals("get_topo_exp_stack_trace")) {
33. 3];
34. result = Logic.getTopoExpStackTrace(topoName);
35. }
36.
37. System.out.println(result);
38. }
39. }
测试的时候把具体的HOST和PORT改一下即可。
然后是具体的逻辑类。
Logic.java
[java] view plain copy
1. package com.damacheng009.storm.monitor.logic;
2.
3. import java.util.Date;
4. import java.util.List;
5. import java.util.Set;
6.
7. import com.damacheng009.storm.monitor.Main;
8. import com.damacheng009.storm.monitor.thrift.ClientInfo;
9. import com.damacheng009.storm.monitor.thrift.ClientManager;
10.
11. import backtype.storm.generated.ClusterSummary;
12. import backtype.storm.generated.ErrorInfo;
13. import backtype.storm.generated.TopologyInfo;
14. import backtype.storm.generated.TopologySummary;
15.
16. public class Logic {
17. /**
18. * 取得某个拓扑的异常个数
19. * @param topoName
20. * @return
21. */
22. public static String getTopoExpSize(String topoName) {
23. null;
24. int errorTotal = 0;
25.
26. try {
27. client = ClientManager.getClient(Main.NIMBUS_HOST, Main.NIMBUS_PORT);
28.
29. ClusterSummary clusterSummary = client.getClient().getClusterInfo();
30. List<TopologySummary> topoSummaryList = clusterSummary.getTopologies();
31. for (TopologySummary ts : topoSummaryList) {
32. if (ts.getName().equals(topoName)) {
33. TopologyInfo topologyInfo = client.getClient().getTopologyInfo(ts.getId());
34. Set<String> errorKeySet = topologyInfo.getErrors().keySet();
35. for (String errorKey : errorKeySet) {
36. List<ErrorInfo> listErrorInfo = topologyInfo.getErrors().get(errorKey);
37. errorTotal += listErrorInfo.size();
38. }
39. break;
40. }
41. }
42.
43. return String.valueOf(errorTotal);
44. catch (Exception e) {
45. return "-1";
46. finally {
47. ClientManager.closeClient(client);
48. }
49. }
50.
51. /**
52. * 返回某个拓扑的异常堆栈
53. * @param topoName
54. * @return
55. */
56. public static String getTopoExpStackTrace(String topoName) {
57. null;
58. new StringBuilder();
59.
60. try {
61. client = ClientManager.getClient(Main.NIMBUS_HOST, Main.NIMBUS_PORT);
62.
63. ClusterSummary clusterSummary = client.getClient().getClusterInfo();
64. List<TopologySummary> topoSummaryList = clusterSummary.getTopologies();
65. for (TopologySummary ts : topoSummaryList) {
66. if (ts.getName().equals(topoName)) {
67. TopologyInfo topologyInfo = client.getClient().getTopologyInfo(ts.getId());
68. // 得到错误信息
69. Set<String> errorKeySet = topologyInfo.getErrors().keySet();
70. for (String errorKey : errorKeySet) {
71. List<ErrorInfo> listErrorInfo = topologyInfo.getErrors().get(errorKey);
72. for (ErrorInfo ei : listErrorInfo) {
73. // 发生异常的时间
74. long expTime = (long) ei.getError_time_secs() * 1000;
75. // 现在的时间
76. long now = System.currentTimeMillis();
77.
78. // 由于获取的是全量的错误堆栈,我们可以设置一个范围来获取指定范围的错误,看情况而定
79. // 如果超过5min,那么就不用记录了,因为5min检查一次
80. if (now - expTime > 1000 * 60 * 5) {
81. continue;
82. }
83.
84. new Date(expTime) + "\n");
85. "\n");
86. }
87. }
88.
89. break;
90. }
91. }
92.
93. return error.toString().isEmpty() ? "none" : error.toString();
94. catch (Exception e) {
95. return "-1";
96. finally {
97. ClientManager.closeClient(client);
98. }
99. }
100. }
最后打成一个Jar包,就可以跑起来接入监控系统了,如在Zabbix中,可以把各个监控项设置为自定义的item,在Zabbix Client中配置命令行来运行Jar取得数据。
接下来的测试过程先略过。
对于Storm监控的实践,目前就是这样了。