├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── co
│ │ └── solinx
│ │ └── kafka
│ │ └── monitor
│ │ ├── Main.java
│ │ ├── Start.java
│ │ ├── api
│ │ ├── AbstractApi.java
│ │ ├── AlarmsApi.java
│ │ ├── BrokerTopicServletApi.java
│ │ ├── BrokersApi.java
│ │ ├── ConsumerApi.java
│ │ ├── HistoryMetricsApi.java
│ │ ├── InitBean.java
│ │ ├── MessageApi.java
│ │ ├── MetricsApi.java
│ │ ├── PartitionsApi.java
│ │ ├── TopicsApi.java
│ │ └── kafkaInfoApi.java
│ │ ├── common
│ │ ├── DateUtils.java
│ │ ├── JettyUtils.java
│ │ ├── KafkaUtils.java
│ │ ├── Utils.java
│ │ └── WebUI.java
│ │ ├── config
│ │ └── PreConfig.java
│ │ ├── core
│ │ ├── consumer
│ │ │ ├── MonitorConsumer.java
│ │ │ └── MonitorConsumerRecord.java
│ │ ├── listener
│ │ │ ├── BrokerListener.java
│ │ │ └── TopicListener.java
│ │ ├── produce
│ │ │ ├── MonitorProducer.java
│ │ │ └── ProduceRecord.java
│ │ └── service
│ │ │ ├── ConfigService.java
│ │ │ ├── ConsumerService.java
│ │ │ ├── CuratorService.java
│ │ │ ├── CustomConsumerGroupService.java
│ │ │ ├── InitService.java
│ │ │ ├── JolokiaService.java
│ │ │ ├── KafkaBaseInfoService.java
│ │ │ ├── MessageService.java
│ │ │ ├── MetricsReportService.java
│ │ │ ├── PartitionService.java
│ │ │ ├── ProduceService.java
│ │ │ └── TopicService.java
│ │ ├── db
│ │ └── DBUtils.java
│ │ ├── model
│ │ ├── Broker.java
│ │ ├── Consumer.java
│ │ ├── Controller.java
│ │ ├── KafkaConfig.java
│ │ ├── KafkaMonitorData.java
│ │ ├── Message.java
│ │ ├── MonitorConfig.java
│ │ ├── PageData.java
│ │ ├── Partition.java
│ │ ├── PartitionReplica.java
│ │ ├── Topic.java
│ │ └── ZooConfig.java
│ │ ├── persist
│ │ └── MetricsDataPersist.java
│ │ └── utils
│ │ ├── DateUtils.java
│ │ ├── IClientListener.java
│ │ ├── IDGenerator.java
│ │ ├── JsonLoader.java
│ │ └── zookeeper
│ │ ├── WatcherEvent.java
│ │ ├── WatcherEventCallback.java
│ │ └── ZookeeperClient.java
├── resources
│ ├── application.properties
│ ├── blog_log.txt
│ ├── kafkaMonitorConfig.json
│ ├── log4j.properties
│ ├── static
│ │ ├── asset-manifest.json
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── manifest.json
│ │ ├── npm.json
│ │ ├── service-worker.js
│ │ ├── static
│ │ │ ├── css
│ │ │ │ ├── main.a5c0e99c.css
│ │ │ │ └── main.a5c0e99c.css.map
│ │ │ ├── js
│ │ │ │ ├── Wysiwyg.0994dccd.chunk.js
│ │ │ │ ├── Wysiwyg.0994dccd.chunk.js.map
│ │ │ │ ├── Wysiwyg.f30d8599.chunk.js
│ │ │ │ ├── Wysiwyg.f30d8599.chunk.js.map
│ │ │ │ ├── charts.5b28f0c0.js
│ │ │ │ ├── charts.5b28f0c0.js.map
│ │ │ │ ├── main.ae3dc27e.js
│ │ │ │ ├── main.ae3dc27e.js.map
│ │ │ │ ├── vendor.061917d4.js
│ │ │ │ ├── vendor.061917d4.js.map
│ │ │ │ ├── vendor.360868ee.js
│ │ │ │ └── vendor.360868ee.js.map
│ │ │ └── media
│ │ │ │ ├── b1.553c69e9.jpg
│ │ │ │ ├── beauty.defb9858.jpg
│ │ │ │ └── default-skin.b257fa9c.svg
│ │ ├── temp
│ │ │ ├── index.css
│ │ │ ├── index.html
│ │ │ ├── index.js
│ │ │ └── index.jsp
│ │ └── weibo.json
│ └── zkConfig.json
└── webapp
│ ├── chart.html
│ ├── index.css
│ ├── index.html
│ ├── index.js
│ └── index.jsp
└── test
├── java
└── co
│ └── solinx
│ ├── AppTest.java
│ └── kafka
│ ├── BrokerDataTest.java
│ ├── KafkaBaseInfoTest.java
│ ├── KafkaConfigTest.java
│ ├── KafkaConsumer.java
│ ├── KafkaConsumerTest.java
│ ├── PartitionDataTest.java
│ ├── PartitionServiceTest.java
│ ├── Test.java
│ ├── TestKafkaConsumerGroupService.java
│ ├── TopicDataTest.java
│ ├── TopicServiceTest.java
│ ├── alarm
│ └── KafkaAlarmTest.java
│ └── metrics
│ └── ProduceServiceTest.java
└── resources
└── zkConfig.json
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Linx
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Kafka Monitor为Kafka的可视化管理与监控工具,为Kafka的稳定运维提供高效、可靠、稳定的保障,这里主要简单介绍Kafka Monitor的相关功能与页面的介绍;
2 | Kafka Monitor主要功能:Kafka基本信息仪表盘、broker列表、topic列表、当前消费者列表、Topic添加删除、Topic数据查询;
3 | ## 一、仪表盘
4 | 仪表盘分三部分:状态、图表、报警;状态栏显示了Kafka当前集群状态、Topic总数、节点数、Partition总数,并通过图表显示当前Kafka的可用性信息;有可用性图表、延迟统计图表、异常统计图表;报警栏位当前kafka的异常信息;
5 | ## 二、broker页面
6 | broker页面显示当前broker的可用总数、broker列表中显示当前Kafka集群中每个broker的基本信息、还可通过点击broker ID进入broker详情页面
7 | ## 三、Topic页面
8 | 页面显示当前kafka所有topic总数、Partition总数、topic首选副本率等,并提供添加Topic功能;
9 | ## 四、消费者页面
10 | 页面显示当前kafka中所有consumer列表,并显示所属group、topic等信息;
11 | ## 五、数据查询页面
12 | 查询页面可通过输入topic、partition、offset与Num(消息数量)查询指定的topic中某个partition的消息;
13 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 |
5 | co.solinx
6 | kafka-monitor
7 | 1.0-SNAPSHOT
8 | jar
9 |
10 | kafka-monitor
11 | http://maven.apache.org
12 |
13 |
14 | UTF-8
15 | 8.5.8
16 |
17 | 1.6.2
18 | 1.1
19 | 1.2.16
20 | 1.0.6
21 | 2.10.0
22 | 8.0.16
23 | 4.13.1
24 | 0.10.0.1
25 | 0.10.0.1
26 | 3.5
27 | 1.2.31
28 | 3.4.14
29 | 1.16.18
30 | 1.3.5
31 |
32 |
33 | 1.8
34 | 1.8
35 | UTF-8
36 |
37 | UTF-8
38 | UTF-8
39 |
40 |
41 |
42 |
43 |
44 | org.apache.kafka
45 | kafka-clients
46 | ${kafka_client.version}
47 |
48 |
49 |
50 | org.apache.commons
51 | commons-lang3
52 | ${commons-lang3.version}
53 |
54 |
55 |
56 | org.apache.kafka
57 | kafka_2.10
58 | ${kafka.version}
59 |
60 |
61 | com.alibaba
62 | fastjson
63 | ${fastjson.version}
64 |
65 |
66 |
67 |
68 | org.slf4j
69 | slf4j-api
70 | ${slf4j_version}
71 |
72 |
73 | org.slf4j
74 | slf4j-log4j12
75 | ${slf4j_version}
76 |
77 |
78 | commons-logging
79 | commons-logging-api
80 | ${jcl_version}
81 |
82 |
83 | log4j
84 | log4j
85 | ${log4j_version}
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | mysql
95 | mysql-connector-java
96 | ${mysql.version}
97 |
98 |
99 |
100 | org.apache.curator
101 | curator-framework
102 | ${curator.version}
103 |
104 |
105 |
106 | org.apache.zookeeper
107 | zookeeper
108 | ${zookeeper.version}
109 |
110 |
111 |
112 | junit
113 | junit
114 | ${junit.version}
115 | test
116 |
117 |
118 |
119 | org.apache.curator
120 | curator-recipes
121 | ${curator.version}
122 |
123 |
124 |
125 |
126 | org.jolokia
127 | jolokia-jvm
128 | ${jolokia-jvm.version}
129 |
130 |
131 |
132 |
133 | org.projectlombok
134 | lombok
135 | ${lombok.version}
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | org.springframework.boot
146 | spring-boot-starter-web
147 |
148 |
149 | ch.qos.logback
150 | logback-classic
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 | org.springframework.boot
159 | spring-boot-dependencies
160 | 2.0.0.RELEASE
161 | pom
162 | import
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 | src/main/resources
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 | org.apache.maven.plugins
183 | maven-compiler-plugin
184 |
185 | 1.8
186 | 1.8
187 |
188 |
189 |
190 |
191 | org.apache.maven.plugins
192 | maven-jar-plugin
193 |
194 |
195 |
196 | true
197 | false
198 | lib/
199 | co.solinx.kafka.monitor.Main
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 | org.apache.maven.plugins
209 | maven-dependency-plugin
210 |
211 |
212 | copy-dependencies
213 | install
214 |
215 | copy-dependencies
216 |
217 |
218 | ${project.build.directory}/lib
219 | true
220 | true
221 | true
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/Main.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor;
2 |
3 | import co.solinx.kafka.monitor.core.service.ConfigService;
4 | import co.solinx.kafka.monitor.core.service.InitService;
5 |
6 | import java.util.Properties;
7 |
8 | /**
9 | * @author linxin
10 | * @version v1.0
11 | * Copyright (c) 2015 by solinx
12 | * @date 2016/12/12.
13 | */
14 | public class Main {
15 |
16 | public static void main(String[] args) throws Exception {
17 |
18 |
19 | // WebUI webUI = new WebUI(ConfigService.monitorConfig.getHost(), ConfigService.monitorConfig.getPort(), "WebUi", "/");
20 | //
21 | // webUI.bind();
22 | initMonitorService();
23 | }
24 |
25 | public static void initMonitorService() {
26 | InitService initService = new InitService();
27 | initService.init();
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/Start.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
5 | import org.springframework.context.annotation.ComponentScan;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | /**
9 | * @author linx
10 | * @create 2018-04 01-0:33
11 | **/
12 | @Configuration
13 | @ComponentScan
14 | @EnableAutoConfiguration
15 | public class Start {
16 |
17 | public static void main(String[] args) {
18 | SpringApplication.run(Start.class);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/AbstractApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.model.PageData;
4 |
5 | /**
6 | * @author linxin
7 | * @version v1.0
8 | * Copyright (c) 2015 by solinx
9 | * @date 2017/12/29.
10 | */
11 | public abstract class AbstractApi {
12 | protected PageData pageData;
13 |
14 |
15 | public String formatData(String callback) {
16 | String resultStr = pageData.toString();
17 | if (callback != null) {
18 | resultStr = String.format("%s(%s)", callback, resultStr);
19 | }
20 | return resultStr;
21 | }
22 |
23 | public String formatData(String callback,PageData pageData) {
24 | String resultStr = pageData.toString();
25 | if (callback != null) {
26 | resultStr = String.format("%s(%s)", callback, resultStr);
27 | }
28 | return resultStr;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/AlarmsApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
4 | import co.solinx.kafka.monitor.model.PageData;
5 | import co.solinx.kafka.monitor.model.Topic;
6 | import com.alibaba.fastjson.JSONArray;
7 | import com.alibaba.fastjson.JSONObject;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * 报警信息
15 | *
16 | * @author linxin
17 | * @version v1.0
18 | * Copyright (c) 2015 by solinx
19 | * @date 2017/12/27.
20 | */
21 | @RestController
22 | @RequestMapping("/data/alarmServlet")
23 | public class AlarmsApi extends AbstractApi {
24 |
25 | private static KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
26 |
27 | @RequestMapping
28 | public String alarms(String callback) {
29 | pageData = new PageData();
30 | try {
31 | List topicList = service.getTopics();
32 |
33 | JSONArray array = new JSONArray();
34 | for (Topic
35 | topic : topicList) {
36 | int preferred = (int) topic.getPreferred();
37 | if (preferred != 100) {
38 | JSONObject temp = new JSONObject();
39 | temp.put("preferred", preferred + "%");
40 | temp.put("underReplicated", topic.getUnderReplicated());
41 | temp.put("topic", topic.getName());
42 | array.add(temp);
43 | }
44 | }
45 | pageData.setData(array);
46 | } catch (Exception e) {
47 | pageData.setStatus(500);
48 | pageData.setError(e.getMessage());
49 | }
50 | return formatData(callback, pageData);
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/BrokerTopicServletApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
4 | import co.solinx.kafka.monitor.model.PageData;
5 | import co.solinx.kafka.monitor.model.Partition;
6 | import co.solinx.kafka.monitor.model.Topic;
7 | import com.alibaba.fastjson.JSONArray;
8 | import com.alibaba.fastjson.JSONObject;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.web.bind.annotation.PathVariable;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import java.util.Arrays;
16 | import java.util.Collection;
17 | import java.util.List;
18 | import java.util.stream.Collectors;
19 |
20 | /**
21 | * @author linxin
22 | * @version v1.0
23 | * Copyright (c) 2015 by solinx
24 | * @date 2017/12/27.
25 | */
26 | @RestController
27 | @RequestMapping("/data/brokerTopicServlet")
28 | public class BrokerTopicServletApi extends AbstractApi {
29 |
30 | private static KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
31 | private Logger logger = LoggerFactory.getLogger(BrokerTopicServletApi.class);
32 |
33 | @RequestMapping("/{brokerID}")
34 | public String topic(@PathVariable int brokerID, String callback) {
35 | pageData = new PageData();
36 | List topicList = service.getTopics();
37 | JSONArray array = new JSONArray();
38 | for (Topic topic :
39 | topicList) {
40 | Collection topicPar = topic.getLeaderPartitions(brokerID);
41 | int partitionCount = topicPar.size();
42 | JSONObject topicObj = new JSONObject();
43 | topicObj.put("name", topic.getName());
44 | topicObj.put("partitionCount", topic.getPartitionMap().size());
45 | topicObj.put("brokerPartitionCount", partitionCount);
46 |
47 | topicObj.put("PartitionIds", Arrays.toString(topicPar.stream().map(p -> p.getId() + " ").collect(Collectors.toList()).toArray()));
48 | array.add(topicObj);
49 | }
50 | pageData.setData(array);
51 | return formatData(callback, pageData);
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/BrokersApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
4 | import co.solinx.kafka.monitor.model.Broker;
5 | import co.solinx.kafka.monitor.model.PageData;
6 | import co.solinx.kafka.monitor.model.Topic;
7 | import com.alibaba.fastjson.JSONObject;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 | import org.springframework.web.bind.annotation.PathVariable;
11 | import org.springframework.web.bind.annotation.RequestMapping;
12 | import org.springframework.web.bind.annotation.RequestMethod;
13 | import org.springframework.web.bind.annotation.RestController;
14 |
15 | import java.util.List;
16 |
17 | /**
18 | * broker信息
19 | *
20 | * @author linxin
21 | * @version v1.0
22 | * Copyright (c) 2015 by solinx
23 | * @date 2017/12/26.
24 | */
25 |
26 | @RestController
27 | @RequestMapping("/data/brokerServlet")
28 | public class BrokersApi extends AbstractApi {
29 |
30 | private Logger logger = LoggerFactory.getLogger(BrokersApi.class);
31 | private KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
32 |
33 | @RequestMapping
34 | public String brokers(String callback) {
35 | pageData = new PageData();
36 | List brokerList = service.getBrokers();
37 |
38 | pageData.setData(brokerList);
39 | return formatData(callback, pageData);
40 | }
41 |
42 | @RequestMapping(value = "/{id}",method= RequestMethod.GET)
43 | public String getBrokerById(@PathVariable int id,String callback) {
44 | pageData = new PageData();
45 | Broker broker = service.getBrokerById(id);
46 | List topicList = service.getTopics();
47 | int partitionCount = 0;
48 | for (Topic topic :
49 | topicList) {
50 | partitionCount += topic.getLeaderPartitions(id).size();
51 | }
52 |
53 | pageData.setData(broker);
54 |
55 | JSONObject extend = new JSONObject();
56 | extend.put("partitionCount", partitionCount);
57 | extend.put("topicCount", topicList.size());
58 | pageData.setExtend(extend);
59 | return formatData(callback, pageData);
60 | }
61 |
62 | @RequestMapping("/summary")
63 | public String getSummary(String callback) {
64 | pageData = new PageData();
65 | List brokerList = service.getBrokers();
66 | JSONObject result = new JSONObject();
67 | result.put("brokerTotal", brokerList.size());
68 | result.put("brokerAbleTotal", brokerList.size());
69 |
70 | pageData.setData(result);
71 | return formatData(callback, pageData);
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/ConsumerApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.CustomConsumerGroupService;
4 | import co.solinx.kafka.monitor.model.Consumer;
5 | import co.solinx.kafka.monitor.model.PageData;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RequestMethod;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | import java.util.List;
12 |
13 | /**
14 | * @author linxin
15 | * @version v1.0
16 | * Copyright (c) 2015 by solinx
17 | * @date 2017/12/27.
18 | */
19 | @RestController
20 | @RequestMapping("/data/consumerServlet")
21 | public class ConsumerApi extends AbstractApi {
22 |
23 | @RequestMapping
24 | public String consumers(String callback) {
25 | pageData = new PageData();
26 | CustomConsumerGroupService consumerGroupService = new CustomConsumerGroupService();
27 | List consumerList = consumers(consumerGroupService);
28 | pageData.setData(consumerList);
29 | return formatData(callback, pageData);
30 | }
31 |
32 | @RequestMapping(value = "/{topicName}", method = RequestMethod.GET)
33 | public String consumersByTopicName(@PathVariable String topicName, String callback) {
34 | pageData = new PageData();
35 | CustomConsumerGroupService consumerGroupService = new CustomConsumerGroupService();
36 | List consumerList = consumers(consumerGroupService, topicName);
37 |
38 | pageData.setData(consumerList);
39 | return formatData(callback, pageData);
40 | }
41 |
42 | public List consumers(CustomConsumerGroupService consumerGroupService) {
43 | return consumerGroupService.getConsumerList();
44 | }
45 |
46 | public List consumers(CustomConsumerGroupService consumerGroupService, String topicName) {
47 | return consumerGroupService.getConsumerList(topicName);
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/HistoryMetricsApi.java:
--------------------------------------------------------------------------------
1 | //package co.solinx.kafka.monitor.api;
2 | //
3 | //import co.solinx.kafka.monitor.common.DateUtils;
4 | //import co.solinx.kafka.monitor.db.DBUtils;
5 | //import co.solinx.kafka.monitor.model.KafkaMonitorData;
6 | //import com.alibaba.fastjson.JSONObject;
7 | //import org.slf4j.Logger;
8 | //import org.slf4j.LoggerFactory;
9 | //import org.springframework.web.bind.annotation.PathVariable;
10 | //import org.springframework.web.bind.annotation.RequestMapping;
11 | //import org.springframework.web.bind.annotation.RestController;
12 | //
13 | //import javax.ws.rs.GET;
14 | //import javax.ws.rs.Path;
15 | //import javax.ws.rs.PathParam;
16 | //import javax.ws.rs.QueryParam;
17 | //import java.util.ArrayList;
18 | //import java.util.List;
19 | //
20 | ///**
21 | // * @author linxin
22 | // * @version v1.0
23 | // * Copyright (c) 2015 by solinx
24 | // * @date 2017/12/27.
25 | // */
26 | //@RestController
27 | //@RequestMapping("/historyMetricsServlet")
28 | //public class HistoryMetricsApi extends AbstractApi {
29 | //
30 | // private DBUtils dbUtils = new DBUtils<>(KafkaMonitorData.class);
31 | // private Logger logger = LoggerFactory.getLogger(HistoryMetricsApi.class);
32 | //
33 | // @RequestMapping("/{type}")
34 | // public String historyData(@PathVariable String type, @QueryParam("startTime") String startTime,
35 | // @QueryParam("endTime") String endTime, @QueryParam("callback") String callback) {
36 | // String where = " where currentTime >= '" + startTime + "' and currentTime <='" + endTime + "'";
37 | //
38 | // JSONObject resultObject;
39 | //
40 | // try {
41 | //
42 | // List dataList = dbUtils.query("select * from kafkaMonitorData " + where);
43 | //
44 | // resultObject = getMetricsByType(dataList, type);
45 | // pageData.setData(resultObject);
46 | // return formatData(callback);
47 | // } catch (Exception e) {
48 | // e.printStackTrace();
49 | // }
50 | // return null;
51 | // }
52 | //
53 | // public JSONObject getMetricsByType(List dataList, String type) {
54 | //
55 | // List consumerAvgList = new ArrayList<>();
56 | // List producerAvgList = new ArrayList<>();
57 | // List consumerErrorList = new ArrayList<>();
58 | // List producerErrorList = new ArrayList<>();
59 | // List delayList = new ArrayList<>();
60 | // List delayedRateList = new ArrayList<>();
61 | // List delayMsAvgList = new ArrayList<>();
62 | // List delayMsMaxList = new ArrayList<>();
63 | // List duplicatedList = new ArrayList<>();
64 | // List duplicatedRateList = new ArrayList<>();
65 | // List lostRateList = new ArrayList<>();
66 | // List lostTotalList = new ArrayList<>();
67 | // List producerTotalList = new ArrayList<>();
68 | // List consumerTotalList = new ArrayList<>();
69 | //
70 | //
71 | // List timeList = new ArrayList<>();
72 | // for (KafkaMonitorData model :
73 | // dataList) {
74 | // consumerAvgList.add(model.getConsumeAvailabilityAvg());
75 | // producerAvgList.add(model.getProduceAvailabilityAvg());
76 | // consumerErrorList.add(model.getConsumerError());
77 | // consumerTotalList.add(model.getConsumerTotal());
78 | // delayList.add(model.getDelay());
79 | // delayedRateList.add(model.getDelayedRate());
80 | // delayMsAvgList.add(model.getDelayMsAvg());
81 | // delayMsMaxList.add(model.getDelayMsMax());
82 | // duplicatedList.add(model.getDuplicated());
83 | // duplicatedRateList.add(model.getDuplicatedRate());
84 | // lostRateList.add(model.getLostRate());
85 | // lostTotalList.add(model.getLostTotal());
86 | // producerErrorList.add(model.getProducerError());
87 | // producerTotalList.add(model.getProducerTotal());
88 | //
89 | // timeList.add(DateUtils.getTimeStr(model.getCurrentTime(), DateUtils.HYPHEN_DISPLAY_DATE));
90 | // }
91 | // JSONObject resultObj = new JSONObject();
92 | //
93 | // switch (type) {
94 | // case "total":
95 | // resultObj.put("producerTotal", producerTotalList);
96 | // resultObj.put("consumerTotal", consumerTotalList);
97 | // break;
98 | // case "avg":
99 | // resultObj.put("producerAvg", producerAvgList);
100 | // resultObj.put("consumerAvg", consumerAvgList);
101 | // break;
102 | // case "delayed":
103 | // resultObj.put("delayed", delayList);
104 | // resultObj.put("duplicated", duplicatedList);
105 | // break;
106 | // case "error":
107 | // resultObj.put("lostTotal", lostTotalList);
108 | // resultObj.put("consumerError", consumerErrorList);
109 | // resultObj.put("producerError", producerErrorList);
110 | // break;
111 | // case "rate":
112 | // resultObj.put("duplicatedRate", duplicatedRateList);
113 | // resultObj.put("lostRate", lostRateList);
114 | // resultObj.put("delayedRate", delayedRateList);
115 | // break;
116 | // case "delay":
117 | // resultObj.put("delayMsAvg", delayMsAvgList);
118 | // resultObj.put("delayMsMax", delayMsMaxList);
119 | // break;
120 | // default:
121 | // break;
122 | // }
123 | //
124 | // resultObj.put("time", timeList);
125 | // return resultObj;
126 | // }
127 | //}
128 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/InitBean.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.InitService;
4 | import org.springframework.beans.factory.InitializingBean;
5 |
6 | /**
7 | * @auther linx
8 | * @create 2018-04-01 11:47
9 | **/
10 | public class InitBean implements InitializingBean {
11 |
12 | @Override
13 | public void afterPropertiesSet() {
14 | InitService initService = new InitService();
15 | initService.init();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/MessageApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.MessageService;
4 | import co.solinx.kafka.monitor.model.Message;
5 | import co.solinx.kafka.monitor.model.PageData;
6 | import com.alibaba.fastjson.JSONArray;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 | import java.util.List;
11 |
12 | @RestController
13 | @RequestMapping("/data/messageServlet")
14 | public class MessageApi extends AbstractApi {
15 |
16 | @RequestMapping
17 | public String message( String topic,
18 | int partition,
19 | int offset,
20 | int messageSum,
21 | String callback) {
22 | pageData = new PageData();
23 | MessageService service = new MessageService();
24 | List messageList = service.getMesage(topic, partition, offset, messageSum);
25 | JSONArray array = new JSONArray();
26 | for (Message message :
27 | messageList) {
28 | array.add(message);
29 | }
30 | pageData.setData(array);
31 | return formatData(callback, pageData);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/MetricsApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.MetricsReportService;
4 | import co.solinx.kafka.monitor.model.PageData;
5 | import com.alibaba.fastjson.JSONObject;
6 | import org.springframework.web.bind.annotation.PathVariable;
7 | import org.springframework.web.bind.annotation.RequestMapping;
8 | import org.springframework.web.bind.annotation.RestController;
9 |
10 |
11 | @RestController
12 | @RequestMapping("/data/metricsServlet")
13 | public class MetricsApi extends AbstractApi {
14 |
15 | @RequestMapping("/{type}")
16 | public String metrics(@PathVariable("type") String type,
17 | String callback) {
18 | pageData = new PageData();
19 | MetricsReportService metricsServlet = MetricsReportService.getMetricsService();
20 | JSONObject resultList = new JSONObject();
21 |
22 | switch (type) {
23 | case "total":
24 | resultList = metricsServlet.getTotalMetrics();
25 | break;
26 | case "avg":
27 | resultList = metricsServlet.getAvgMetrics();
28 | break;
29 | case "delayed":
30 | resultList = metricsServlet.getDelayedMetrics();
31 | break;
32 | case "error":
33 | resultList = metricsServlet.getErrorMetrics();
34 | break;
35 | case "rate":
36 | resultList = metricsServlet.getRateMetrics();
37 | break;
38 | case "delay":
39 | resultList = metricsServlet.getDelayMetrics();
40 | break;
41 | default:
42 | break;
43 | }
44 |
45 | pageData.setData(resultList);
46 | return formatData(callback, pageData);
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/PartitionsApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
4 | import co.solinx.kafka.monitor.model.PageData;
5 | import co.solinx.kafka.monitor.model.Partition;
6 | import co.solinx.kafka.monitor.model.Topic;
7 | import com.alibaba.fastjson.JSONArray;
8 | import com.alibaba.fastjson.JSONObject;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import java.util.Arrays;
13 |
14 | /**
15 | * @author linxin
16 | * @version v1.0
17 | * Copyright (c) 2015 by solinx
18 | * @date 2017/12/27.
19 | */
20 | @RestController
21 | @RequestMapping("/data/partitionServlet")
22 | public class PartitionsApi extends AbstractApi {
23 |
24 | private KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
25 |
26 |
27 | @RequestMapping
28 | public String partition(String topicName, String callback) {
29 | pageData = new PageData();
30 | Topic topic = service.getTopic(topicName);
31 | JSONArray array = new JSONArray();
32 | for (Partition tp :
33 | topic.getPartitionMap().values()) {
34 | JSONObject part = new JSONObject();
35 | part.put("id", tp.getId());
36 | part.put("firstOffset", tp.getFirstOffset());
37 | part.put("lastOffset", tp.getSize());
38 | part.put("size", tp.getSize() - tp.getFirstOffset());
39 | //是否leader
40 | part.put("leader", tp.getLeaderId());
41 | //副本集
42 | part.put("replicas", Arrays.toString(tp.getReplicasArray()));
43 | //同步副本
44 | part.put("inSyncReplicas", Arrays.toString(tp.getIsr()));
45 | //首先副本是否为leader
46 | part.put("leaderPreferred", String.valueOf(tp.isLeaderPreferred()));
47 | //是否复制中
48 | part.put("underReplicated", String.valueOf(tp.isUnderReplicated()));
49 | array.add(part);
50 | }
51 | pageData.setData(array);
52 | return formatData(callback, pageData);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/TopicsApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
4 | import co.solinx.kafka.monitor.core.service.TopicService;
5 | import co.solinx.kafka.monitor.model.PageData;
6 | import co.solinx.kafka.monitor.model.Topic;
7 | import com.alibaba.fastjson.JSONArray;
8 | import com.alibaba.fastjson.JSONObject;
9 | import org.slf4j.Logger;
10 | import org.slf4j.LoggerFactory;
11 | import org.springframework.web.bind.annotation.PathVariable;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RequestMethod;
14 | import org.springframework.web.bind.annotation.RestController;
15 |
16 | import java.util.Arrays;
17 | import java.util.List;
18 |
19 | /**
20 | * @author linxin
21 | * @version v1.0
22 | * Copyright (c) 2015 by solinx
23 | * @date 2017/12/27.
24 | */
25 | @RestController
26 | @RequestMapping("/data/topicServlet")
27 | public class TopicsApi extends AbstractApi {
28 |
29 | private KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
30 | private TopicService topicService = new TopicService();
31 | private final static Logger logger = LoggerFactory.getLogger(TopicsApi.class);
32 |
33 | @RequestMapping
34 | public String topics(String callback) {
35 | List topicList = service.getTopics();
36 | pageData = new PageData();
37 | JSONArray array = new JSONArray();
38 | for (Topic
39 | topic : topicList) {
40 | JSONObject temp = new JSONObject();
41 | double partitionSize = topic.getPartitionMap().size();
42 |
43 | temp.put("name", topic.getName());
44 | temp.put("partitionTotal", partitionSize);
45 |
46 | //partition首选副本率(首选副本为leader),最优为100%
47 | temp.put("preferred", topic.getPreferred());
48 | //正在复制的Partition数,正常应为0
49 | temp.put("underReplicated", topic.getUnderReplicated());
50 | JSONObject configObj = (JSONObject) topic.getConfig().get("config");
51 |
52 | temp.put("customConfig", configObj.size() > 0 ? true : false);
53 |
54 | array.add(temp);
55 | }
56 |
57 | pageData.setData(array);
58 | return formatData(callback, pageData);
59 | }
60 |
61 | @RequestMapping("/summary")
62 | public String summary(String callback) {
63 | pageData = new PageData();
64 | List topicList = service.getTopics();
65 | JSONObject result = new JSONObject();
66 | result.put("topicTotal", topicList.size());
67 | result.put("partitionTotal", topicList.stream().mapToInt((t) -> t.getPartitionMap().size()).sum());
68 |
69 | pageData.setData(result);
70 | return formatData(callback, pageData);
71 | }
72 |
73 | @RequestMapping(value = "/{topicName}", method = RequestMethod.GET)
74 | public String topic(@PathVariable String topicName, String callback) {
75 | pageData = new PageData();
76 |
77 | Topic topic = service.getTopic(topicName);
78 | JSONObject jsonObject = new JSONObject();
79 |
80 | jsonObject.put("name", topic.getName());
81 | jsonObject.put("PartitionTotal", topic.getPartitionMap().size());
82 | jsonObject.put("totalSize", topic.getSize());
83 | jsonObject.put("availableSize", topic.getAvailableSize());
84 | jsonObject.put("PreferredReplicas", topic.getPreferredReplicaPercent() * 100 + "%");
85 | jsonObject.put("UnderReplicatedPartitions", Arrays.toString(topic.getUnderReplicatedPartitions().stream().mapToInt(p -> p.getId()).toArray()));
86 |
87 | pageData.setData(jsonObject);
88 | return formatData(callback, pageData);
89 | }
90 |
91 | @RequestMapping(value = "/create", method = RequestMethod.GET)
92 | public String create(String topic,
93 | int replicaFactor,
94 | int partitions, String callback) {
95 | pageData = new PageData();
96 | try {
97 | topicService.createTopic(topic, Integer.valueOf(partitions)
98 | , replicaFactor);
99 |
100 | } catch (Exception e) {
101 | pageData.setStatus(500);
102 | pageData.setError(e.getMessage());
103 | logger.error("添加topic异常", e);
104 | }
105 | return formatData(callback, pageData);
106 | }
107 |
108 | @RequestMapping(value = "/delete", method = RequestMethod.GET)
109 | public String delete(String topic, String callback) {
110 | pageData = new PageData();
111 | topicService.deleteTopic(topic);
112 | return formatData(callback, pageData);
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/api/kafkaInfoApi.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.api;
2 |
3 | import co.solinx.kafka.monitor.core.service.ConfigService;
4 | import co.solinx.kafka.monitor.core.service.KafkaBaseInfoService;
5 | import co.solinx.kafka.monitor.model.Broker;
6 | import co.solinx.kafka.monitor.model.PageData;
7 | import co.solinx.kafka.monitor.model.Topic;
8 | import com.alibaba.fastjson.JSONObject;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RestController;
11 |
12 | import java.util.List;
13 |
14 | /**
15 | * @author linxin
16 | * @version v1.0
17 | * Copyright (c) 2015 by solinx
18 | * @date 2017/12/27.
19 | */
20 | @RestController
21 | @RequestMapping("/data/kafkaInfoServlet")
22 | public class kafkaInfoApi extends AbstractApi {
23 |
24 | KafkaBaseInfoService service = KafkaBaseInfoService.getInstance();
25 |
26 | @RequestMapping
27 | public String kafkaInfo(String callback) {
28 | pageData = new PageData();
29 | List brokersMap = service.getBrokers();
30 | List topicList = service.getTopics();
31 | JSONObject zkConfigObj = ConfigService.rootObj;
32 |
33 |
34 | JSONObject extend = new JSONObject();
35 |
36 | extend.put("servers", KafkaBaseInfoService.getInstance().randomBroker().getHost());
37 | extend.put("partitionNum", topicList.stream().mapToInt((t) -> t.getPartitionMap().size()).sum());
38 | extend.put("clusterState", clusterState(topicList));
39 | extend.put("topicNum", topicList.size());
40 | extend.put("brokerNum", brokersMap.size());
41 | extend.put("zkConfig", zkConfigObj);
42 | pageData.setExtend(extend);
43 | return formatData(callback, pageData);
44 | }
45 |
46 | public boolean clusterState(List topicList) {
47 | boolean result = true;
48 | for (Topic
49 | topic : topicList) {
50 | int preferred = (int) topic.getPreferred();
51 | if (preferred != 100) {
52 | result = false;
53 | }
54 | }
55 | return result;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/common/JettyUtils.java:
--------------------------------------------------------------------------------
1 | //package co.solinx.kafka.monitor.common;
2 | //
3 | //import org.eclipse.jetty.servlet.DefaultServlet;
4 | //import org.eclipse.jetty.servlet.ServletContextHandler;
5 | //import org.eclipse.jetty.servlet.ServletHolder;
6 | //import org.eclipse.jetty.websocket.WebSocketServlet;
7 | //
8 | //import javax.servlet.Servlet;
9 | //import javax.servlet.ServletException;
10 | //import javax.servlet.annotation.WebServlet;
11 | //import javax.servlet.http.HttpServlet;
12 | //import javax.servlet.http.HttpServletRequest;
13 | //import javax.servlet.http.HttpServletResponse;
14 | //import java.io.IOException;
15 | //import java.net.URL;
16 | //
17 | ///**
18 | // * Created by xin on 2017-01-07.
19 | // *
20 | // * @author linx
21 | // */
22 | //public class JettyUtils {
23 | //
24 | //
25 | // public static ServletContextHandler createServletHandler(Servlet servlet) {
26 | // return createServletHandler(servlet, servlet.getClass().getAnnotation(WebServlet.class).urlPatterns()[0]);
27 | // }
28 | //
29 | // /**
30 | // * ServletHandler
31 | // *
32 | // * @param path
33 | // * @return
34 | // */
35 | // public static ServletContextHandler createServletHandler(Servlet servlet, String path) {
36 | //
37 | // ServletContextHandler handler = new ServletContextHandler();
38 | //// ServletHolder holder = new ServletHolder(servlet);
39 | //// handler.addServlet(holder, path);
40 | //// handler.setContextPath("/data");
41 | //
42 | // return handler;
43 | // }
44 | //
45 | //
46 | // /**
47 | // * WebSocket
48 | // *
49 | // * @param servlet
50 | // * @param path
51 | // * @return
52 | // */
53 | // public static ServletContextHandler createWebSockethandler(WebSocketServlet servlet, String path) {
54 | // ServletContextHandler handler = new ServletContextHandler();
55 | //// ServletHolder holder = new ServletHolder(servlet);
56 | //// handler.addServlet(holder, "/");
57 | //// handler.setContextPath(path);
58 | // return handler;
59 | // }
60 | //
61 | // /**
62 | // * 静态资源
63 | // *
64 | // * @param resourceBase
65 | // * @param path
66 | // * @return
67 | // */
68 | // public static ServletContextHandler createStaticHandler(String resourceBase, String path) {
69 | // ServletContextHandler handler = new ServletContextHandler();
70 | //// handler.setInitParameter("org.eclipse.jetty.servlet.Default.gzip", "false");
71 | //// DefaultServlet staticServlet = new DefaultServlet();
72 | //// ServletHolder holder = new ServletHolder(staticServlet);
73 | //// URL url = JettyUtils.class.getClassLoader().getResource(resourceBase);
74 | //// holder.setInitParameter("resourceBase", url.toString());
75 | //// handler.setContextPath("/");
76 | //// handler.addServlet(holder, path);
77 | // return handler;
78 | // }
79 | //
80 | //
81 | // /**
82 | // * Servlet
83 | // *
84 | // * @param context
85 | // * @return
86 | // */
87 | // public static HttpServlet createServlet(String context) {
88 | //
89 | //
90 | // HttpServlet servlet = new HttpServlet() {
91 | // @Override
92 | // protected void doGet(HttpServletRequest req, HttpServletResponse resp)
93 | // throws ServletException, IOException {
94 | //
95 | //
96 | // resp.setContentType("text/html;charset=utf-8");
97 | // resp.setStatus(HttpServletResponse.SC_OK);
98 | // resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
99 | // resp.getWriter().println(context);
100 | // }
101 | //
102 | // @Override
103 | // protected void doTrace(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
104 | // resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
105 | // }
106 | // };
107 | // return servlet;
108 | // }
109 | //}
110 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/common/KafkaUtils.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.common;
2 |
3 | import co.solinx.kafka.monitor.core.service.ConfigService;
4 | import co.solinx.kafka.monitor.model.Broker;
5 | import co.solinx.kafka.monitor.model.Partition;
6 | import co.solinx.kafka.monitor.model.Topic;
7 | import co.solinx.kafka.monitor.model.ZooConfig;
8 | import kafka.api.PartitionOffsetRequestInfo;
9 | import kafka.common.TopicAndPartition;
10 | import kafka.javaapi.OffsetRequest;
11 | import kafka.javaapi.OffsetResponse;
12 | import kafka.javaapi.TopicMetadataRequest;
13 | import kafka.javaapi.TopicMetadataResponse;
14 | import kafka.network.BlockingChannel;
15 | import kafka.utils.ZKConfig;
16 | import kafka.utils.ZkUtils;
17 | import org.slf4j.Logger;
18 | import org.slf4j.LoggerFactory;
19 |
20 | import java.util.Arrays;
21 | import java.util.List;
22 | import java.util.Properties;
23 | import java.util.stream.Collectors;
24 |
25 | /**
26 | * kafka工具类
27 | *
28 | * @author linx
29 | * @create 2018-01-28 21:56
30 | **/
31 | public class KafkaUtils {
32 |
33 | private static Logger logger = LoggerFactory.getLogger(KafkaUtils.class);
34 |
35 | public static ZkUtils getZkUtils() {
36 | ZooConfig zooConfig = ConfigService.zooConfig;
37 | String ip = zooConfig.getHost();
38 | int sessionTimeout = Integer.parseInt(zooConfig.getSessionTimeoutMs());
39 | int connTimeout = Integer.valueOf(zooConfig.getConnectionTimeoutMs());
40 | return ZkUtils.apply(ip, sessionTimeout, connTimeout, false);
41 | }
42 |
43 | /**
44 | * 取Broker Channel通道
45 | *
46 | * @param broker
47 | * @return
48 | */
49 | public static BlockingChannel getChannel(Broker broker) {
50 | BlockingChannel channel = new BlockingChannel(broker.getHost(),
51 | broker.getPort(), BlockingChannel.UseDefaultBufferSize(),
52 | BlockingChannel.UseDefaultBufferSize(), 10000);
53 |
54 | channel.connect();
55 | return channel;
56 | }
57 |
58 | public TopicMetadataResponse topicMetadataRequest(BlockingChannel channel, String[] topics) {
59 | TopicMetadataRequest request = new TopicMetadataRequest((short) 0, 0, "kafkaMonitor", Arrays.asList(topics));
60 | channel.send(request);
61 | final kafka.api.TopicMetadataResponse underlyingResponse =
62 | kafka.api.TopicMetadataResponse.readFrom(channel.receive().payload());
63 | TopicMetadataResponse response = new TopicMetadataResponse(underlyingResponse);
64 | return response;
65 | }
66 |
67 | /**
68 | * 请求取topic偏移
69 | *
70 | * @param broker
71 | * @param topic
72 | * @param brokerPartitions
73 | * @return
74 | */
75 | public static OffsetResponse sendOffsetRequest(Broker broker, Topic topic,
76 | List brokerPartitions, long time) {
77 |
78 | PartitionOffsetRequestInfo requestInfo = new PartitionOffsetRequestInfo(time, 1);
79 |
80 | final OffsetRequest offsetRequest = new OffsetRequest(
81 | brokerPartitions.stream()
82 | .collect(Collectors.toMap(
83 | partition -> new TopicAndPartition(topic.getName(), partition.getId()),
84 | partition -> requestInfo)), (short) 0, "kafkaMonitor");
85 |
86 | logger.debug("Sending offset request: {}", offsetRequest);
87 | if (broker != null) {
88 | BlockingChannel channel = getChannel(broker);
89 | channel.send(offsetRequest.underlying());
90 | final kafka.api.OffsetResponse underlyingResponse = kafka.api.OffsetResponse.readFrom(channel.receive().payload());
91 | channel.disconnect();
92 | return new OffsetResponse(underlyingResponse);
93 | } else {
94 | return null;
95 | }
96 |
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/common/Utils.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.common;
2 |
3 | import kafka.utils.ZkUtils;
4 | import org.apache.kafka.common.security.JaasUtils;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import scala.collection.JavaConversions;
8 |
9 | import java.util.Arrays;
10 |
11 | /**
12 | * kafkaUtils
13 | * @author linxin
14 | * @version v1.0
15 | * Copyright (c) 2015 by solinx
16 | * @date 2016/12/23.
17 | */
18 | public class Utils {
19 |
20 | private static final Logger logger= LoggerFactory.getLogger(Utils.class);
21 |
22 | public static final int ZK_CONNECTION_TIMEOUT_MS = 30_000;
23 | public static final int ZK_SESSION_TIMEOUT_MS = 30_000;
24 |
25 |
26 | public static int getPartitionNumByTopic(String zk,String topic){
27 | ZkUtils zkUtils=ZkUtils.apply(zk,ZK_SESSION_TIMEOUT_MS,ZK_CONNECTION_TIMEOUT_MS, JaasUtils.isZkSecurityEnabled());
28 |
29 | try {
30 | return zkUtils.getPartitionsForTopics(JavaConversions.asScalaBuffer(Arrays.asList(topic))).apply(topic).size();
31 | }finally {
32 | zkUtils.close();
33 | }
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/common/WebUI.java:
--------------------------------------------------------------------------------
1 | //package co.solinx.kafka.monitor.common;
2 | //
3 | //import org.eclipse.jetty.server.Server;
4 | //import org.eclipse.jetty.server.handler.ContextHandlerCollection;
5 | //import org.eclipse.jetty.servlet.DefaultServlet;
6 | //import org.eclipse.jetty.servlet.ServletContextHandler;
7 | //import org.eclipse.jetty.servlet.ServletHolder;
8 | //import org.eclipse.jetty.websocket.WebSocketHandler;
9 | //import org.eclipse.jetty.websocket.WebSocketServlet;
10 | //import org.slf4j.Logger;
11 | //import org.slf4j.LoggerFactory;
12 | //
13 | //import javax.servlet.Servlet;
14 | //import java.net.InetSocketAddress;
15 | //import java.net.URL;
16 | //
17 | ///**
18 | // * Created by xin on 2017-01-07.
19 | // */
20 | //public class WebUI {
21 | //
22 | // private int port;
23 | // private String name;
24 | // private String basePath;
25 | // private Server server;
26 | // private String host;
27 | // private ContextHandlerCollection collection = new ContextHandlerCollection();
28 | // private static Logger logger = LoggerFactory.getLogger(WebUI.class);
29 | //
30 | // public WebUI(String host, int port, String name, String basePath) {
31 | // this.port = port;
32 | // this.name = name;
33 | // this.basePath = basePath;
34 | // this.host = host;
35 | // }
36 | //
37 | //
38 | // public void attachPage(Servlet name, String path) {
39 | //// attachHandler(JettyUtils.createServletHandler(name, path));
40 | // }
41 | //
42 | // /**
43 | // * 添加页面
44 | // */
45 | // public void attachPage(Servlet servlet) {
46 | //
47 | //
48 | //// attachHandler(JettyUtils.createServletHandler(servlet));
49 | //
50 | // }
51 | //
52 | // /**
53 | // * 添加WebSocket服务
54 | // *
55 | // * @param webSocketServlet WebSocket服务
56 | // * @param path
57 | // */
58 | // public void attachWebSocket(WebSocketServlet webSocketServlet, String path) {
59 | //// attachHandler(JettyUtils.createWebSockethandler(webSocketServlet, path));
60 | // }
61 | //
62 | //
63 | // public void attachHandler(ServletContextHandler handler) {
64 | //// collection.addHandler(handler);
65 | //
66 | // }
67 | //
68 | // public void attachHandler(WebSocketHandler handler) {
69 | // collection.addHandler(handler);
70 | // }
71 | //
72 | // public ServletContextHandler staticRsource() {
73 | // ServletContextHandler handler = new ServletContextHandler();
74 | // handler.setInitParameter("org.eclipse.jetty.servlet.Default.gzip", "false");
75 | // DefaultServlet staticServlet = new DefaultServlet();
76 | // ServletHolder holder = new ServletHolder(staticServlet);
77 | // URL url = JettyUtils.class.getClassLoader().getResource("static");
78 | // holder.setInitParameter("resourceBase", url.toString());
79 | // handler.setContextPath("/");
80 | // handler.addServlet(holder, "/");
81 | // return handler;
82 | // }
83 | //
84 | // public ServletContextHandler jerseyHandler() {
85 | // ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
86 | // context.setContextPath("/");
87 | // ServletHolder servlet = context.addServlet(
88 | // org.glassfish.jersey.servlet.ServletContainer.class, "/*");
89 | // servlet.setInitOrder(1);
90 | // servlet.setInitParameter("jersey.config.server.provider.packages", "co.solinx.kafka.monitor.api");
91 | // context.setContextPath("/data");
92 | // context.addServlet(servlet, "/");
93 | // return context;
94 | // }
95 | //
96 | //
97 | // public void bind() throws Exception {
98 | //
99 | // server = new Server(new InetSocketAddress(host, port));
100 | //
101 | // try {
102 | //
103 | // ContextHandlerCollection handlerCollection = new ContextHandlerCollection();
104 | // handlerCollection.addHandler(staticRsource());
105 | // handlerCollection.addHandler(jerseyHandler());
106 | //// server.setHandler(handlerCollection);
107 | //// server.start();
108 | // logger.debug("name : {} basePath : {}", name, basePath);
109 | // } catch (Exception e) {
110 | // throw e;
111 | // }
112 | // }
113 | //
114 | //
115 | // public static void main(String[] args) {
116 | // WebUI webUI = new WebUI("0.0.0.0", 5050, "WebUi", "/");
117 | //
118 | //
119 | // try {
120 | // //webUI.attachHandler(JettyUtils.createStaticHandler(Constant.STATIC_RESOURCE_DIR,"/static"));
121 | // webUI.attachHandler(JettyUtils.createStaticHandler("static", "/"));
122 | //// webUI.attachPage(new BrokerServlet());
123 | // webUI.bind();
124 | // } catch (Exception e) {
125 | // e.printStackTrace();
126 | // }
127 | // }
128 | //}
129 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/config/PreConfig.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.config;
2 |
3 | import co.solinx.kafka.monitor.api.InitBean;
4 | import org.springframework.context.annotation.Bean;
5 | import org.springframework.context.annotation.ComponentScan;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | /**
9 | * @auther linx
10 | * @create 2018-04-01 11:57
11 | **/
12 | @Configuration
13 | @ComponentScan("co.solinx.kafka.monitor.api")
14 | public class PreConfig {
15 |
16 | @Bean
17 | InitBean initBean() {
18 | return new InitBean();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/consumer/MonitorConsumer.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.consumer;
2 |
3 | import org.apache.kafka.clients.consumer.ConsumerRecord;
4 | import org.apache.kafka.clients.consumer.KafkaConsumer;
5 |
6 | import java.util.Arrays;
7 | import java.util.Iterator;
8 | import java.util.Properties;
9 |
10 | /**
11 | * @author linxin
12 | * @version v1.0
13 | * Copyright (c) 2015 by solinx
14 | * @date 2016/12/23.
15 | */
16 | public class MonitorConsumer {
17 | private KafkaConsumer consumer;
18 | private Iterator> recordIterator;
19 |
20 |
21 | public MonitorConsumer(String topic, Properties properties) {
22 | consumer = new KafkaConsumer<>(properties);
23 | consumer.subscribe(Arrays.asList(topic));
24 | }
25 |
26 | public MonitorConsumerRecord receive() {
27 | if (recordIterator == null || !recordIterator.hasNext())
28 | recordIterator = consumer.poll(Long.MAX_VALUE).iterator();
29 |
30 | ConsumerRecord record = recordIterator.next();
31 | return new MonitorConsumerRecord(record.topic(), record.partition(), record.offset(), record.key(), record.value());
32 | }
33 |
34 | public void close() {
35 | consumer.close();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/consumer/MonitorConsumerRecord.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.consumer;
2 |
3 | /**
4 | * @author linxin
5 | * @version v1.0
6 | * Copyright (c) 2015 by solinx
7 | * @date 2016/12/23.
8 | */
9 | public class MonitorConsumerRecord {
10 |
11 | private final String topic;
12 | private final int partition;
13 | private final long offset;
14 | private final String key;
15 | private final String value;
16 |
17 |
18 | public MonitorConsumerRecord(String topic, int partition, long offset, String key, String value) {
19 | this.topic = topic;
20 | this.partition = partition;
21 | this.offset = offset;
22 | this.key = key;
23 | this.value = value;
24 | }
25 |
26 |
27 | public String getTopic() {
28 | return topic;
29 | }
30 |
31 | public int getPartition() {
32 | return partition;
33 | }
34 |
35 | public long getOffset() {
36 | return offset;
37 | }
38 |
39 | public String getKey() {
40 | return key;
41 | }
42 |
43 | public String getValue() {
44 | return value;
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/listener/BrokerListener.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.listener;
2 |
3 | import co.solinx.kafka.monitor.model.Broker;
4 | import com.alibaba.fastjson.JSON;
5 | import kafka.utils.ZkUtils;
6 | import org.apache.commons.lang3.StringUtils;
7 | import org.apache.curator.framework.CuratorFramework;
8 | import org.apache.curator.framework.recipes.cache.ChildData;
9 | import org.apache.curator.framework.recipes.cache.PathChildrenCache;
10 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheEvent;
11 | import org.apache.curator.framework.recipes.cache.PathChildrenCacheListener;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.util.Map;
16 | import java.util.TreeMap;
17 |
18 | /**
19 | * @author linxin
20 | * @version v1.0
21 | * Copyright (c) 2015 by linx
22 | * @date 2018/1/26.
23 | */
24 | public class BrokerListener implements PathChildrenCacheListener {
25 |
26 | private Logger logger = LoggerFactory.getLogger(BrokerListener.class);
27 |
28 | /**
29 | * broker信息缓存
30 | */
31 | public Map brokerCache = new TreeMap();
32 | /**
33 | * broker节点缓存
34 | */
35 | private PathChildrenCache brokerPathCache;
36 |
37 | public BrokerListener(Map brokerCache, PathChildrenCache brokerPathCache) {
38 | this.brokerCache = brokerCache;
39 | this.brokerPathCache = brokerPathCache;
40 | }
41 |
42 | /**
43 | * 添加broker
44 | *
45 | * @param broker
46 | */
47 | public void addBroker(Broker broker) {
48 | brokerCache.put(broker.getId(), broker);
49 | }
50 |
51 | /**
52 | * 移除broker
53 | *
54 | * @param brokerID
55 | */
56 | public void removeBroker(int brokerID) {
57 | brokerCache.remove(brokerID);
58 | }
59 |
60 | /**
61 | * 解析brokerID
62 | *
63 | * @param childData
64 | * @return
65 | */
66 | public int parseBrokerID(ChildData childData) {
67 | String brokerID = StringUtils.substringAfter(childData.getPath(), ZkUtils.BrokerIdsPath() + "/");
68 | return Integer.parseInt(brokerID);
69 | }
70 |
71 | /**
72 | * 解析Broker对象
73 | *
74 | * @param childData
75 | * @return
76 | */
77 | public Broker parseBroker(ChildData childData) {
78 | Broker broker = JSON.parseObject(childData.getData(), Broker.class);
79 | broker.setId(parseBrokerID(childData));
80 | return broker;
81 | }
82 |
83 | @Override
84 | public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
85 |
86 | logger.debug("BrokerListener {} {}", event.getType(), event.getData());
87 |
88 | switch (event.getType()) {
89 | case CHILD_REMOVED:
90 | removeBroker(parseBrokerID(event.getData()));
91 | break;
92 | case CHILD_ADDED:
93 | case CHILD_UPDATED:
94 | addBroker(parseBroker(event.getData()));
95 | break;
96 | case INITIALIZED:
97 | brokerPathCache.getCurrentData().stream()
98 | .map(BrokerListener.this::parseBroker)
99 | .forEach(broker -> addBroker(broker));
100 | break;
101 | default:
102 | break;
103 |
104 | }
105 |
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/listener/TopicListener.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.listener;
2 |
3 | import co.solinx.kafka.monitor.model.Partition;
4 | import co.solinx.kafka.monitor.model.Topic;
5 | import com.alibaba.fastjson.JSONArray;
6 | import com.alibaba.fastjson.JSONObject;
7 | import kafka.utils.ZkUtils;
8 | import org.apache.curator.framework.CuratorFramework;
9 | import org.apache.curator.framework.recipes.cache.ChildData;
10 | import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
11 | import org.apache.curator.framework.recipes.cache.TreeCacheListener;
12 |
13 | import java.util.List;
14 |
15 | import static org.apache.curator.framework.recipes.cache.TreeCacheEvent.Type.NODE_ADDED;
16 |
17 |
18 | public class TopicListener implements TreeCacheListener{
19 |
20 |
21 | private List topicList;
22 |
23 | public TopicListener(List topicList) {
24 | this.topicList = topicList;
25 | }
26 |
27 | @Override
28 | public void childEvent(CuratorFramework curator, TreeCacheEvent event) throws Exception {
29 | ChildData data = event.getData();
30 | if (data != null) {
31 | if (event.getType() == NODE_ADDED) {
32 |
33 | }
34 | String path = data.getPath();
35 | //判断是否为topics节点
36 | if (path.contains(String.format("%s/",ZkUtils.BrokerTopicsPath())) && (!path.contains("partitions"))) {
37 | Topic topic = JSONObject.parseObject(data.getData(), Topic.class);
38 | String name = path.substring(path.lastIndexOf("/") + 1, path.length());
39 | topic.setName(name);
40 |
41 | int[] tPartiyions = topic.getPartitions().keySet().stream().mapToInt((t) -> Integer.valueOf(t)).sorted().toArray();
42 | for (Object key : tPartiyions
43 | ) {
44 | String partitionPath = String.format("%s/partitions/%s/state", path, key);
45 | String state = new String(curator.getData().forPath(partitionPath));
46 | Partition partition = JSONObject.parseObject(state, Partition.class);
47 | JSONArray replicas = topic.getPartitions().getJSONArray(String.valueOf(key));
48 | int[] replicasArray = new int[replicas.size()];
49 | for (int i = 0; i <
50 | replicas.size(); i++) {
51 | replicasArray[i] = replicas.getInteger(i);
52 | }
53 | partition.setReplicasArray(replicasArray);
54 |
55 | topic.getPartitionMap().put((Integer) key, partition);
56 | }
57 | topicList.add(topic);
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/produce/MonitorProducer.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.produce;
2 |
3 | import org.apache.kafka.clients.producer.KafkaProducer;
4 | import org.apache.kafka.clients.producer.ProducerRecord;
5 | import org.apache.kafka.clients.producer.RecordMetadata;
6 |
7 | import java.util.Properties;
8 | import java.util.concurrent.Future;
9 |
10 | /**
11 | * MonitorProducer
12 | * @author linxin
13 | * @version v1.0
14 | * Copyright (c) 2015 by solinx
15 | * @date 2016/12/22.
16 | */
17 | public class MonitorProducer {
18 | private KafkaProducer producer;
19 |
20 |
21 | public MonitorProducer(Properties properties) {
22 | producer = new KafkaProducer<>(properties);
23 | }
24 |
25 | public RecordMetadata send(ProduceRecord record) throws Exception {
26 | ProducerRecord producerRecord = new ProducerRecord(record.getTopic(), record.getPartition(), record.getKey(), record.getValue());
27 |
28 | Future metadataFuture = producer.send(producerRecord);
29 | return metadataFuture.get();
30 | }
31 |
32 |
33 | public void close() {
34 | producer.close();
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/produce/ProduceRecord.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.produce;
2 |
3 | /**
4 | * @author linxin
5 | * @version v1.0
6 | * Copyright (c) 2015 by solinx
7 | * @date 2016/12/22.
8 | */
9 | public class ProduceRecord {
10 |
11 | private final String topic;
12 | private final int partition;
13 | private final String key;
14 | private final String value;
15 |
16 | public ProduceRecord(String topic, int partition, String key, String value) {
17 | this.topic = topic;
18 | this.partition = partition;
19 | this.key = key;
20 | this.value = value;
21 | }
22 |
23 |
24 | public String getTopic() {
25 | return topic;
26 | }
27 |
28 | public int getPartition() {
29 | return partition;
30 | }
31 |
32 | public String getKey() {
33 | return key;
34 | }
35 |
36 | public String getValue() {
37 | return value;
38 | }
39 |
40 |
41 | @Override
42 | public String toString() {
43 | return "ProduceRecord{" +
44 | "topic='" + topic + '\'' +
45 | ", partition=" + partition +
46 | ", key='" + key + '\'' +
47 | ", value='" + value + '\'' +
48 | '}';
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/service/ConfigService.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.service;
2 |
3 | import co.solinx.kafka.monitor.model.KafkaConfig;
4 | import co.solinx.kafka.monitor.model.MonitorConfig;
5 | import co.solinx.kafka.monitor.model.ZooConfig;
6 | import co.solinx.kafka.monitor.utils.JsonLoader;
7 | import com.alibaba.fastjson.JSONObject;
8 |
9 | import java.util.Properties;
10 |
11 | /**
12 | * @author linxin
13 | * @version v1.0
14 | * Copyright (c) 2015 by solinx
15 | * @date 2016/12/20.
16 | */
17 | public class ConfigService {
18 |
19 |
20 | public static JSONObject rootObj;
21 | public static String bootstrapServers = "bootstrap.servers";
22 | public static String keyDeserializer = "key.deserializer";
23 | public static String valueSerializer = "value.serializer";
24 | public static String keySerializer = "key.serializer";
25 | public static String valueDeserializer = "value.deserializer";
26 | public static String enableAutoCommit = "enable.auto.commit";
27 | public static String autoCommitIntervalMs = "auto.commit.interval.ms";
28 | public static String sessionTimeoutMs = "session.timeout.ms";
29 | public static String groupId = "group.id";
30 |
31 | private static KafkaConfig kafkaconfig;
32 | public static ZooConfig zooConfig;
33 | public static MonitorConfig monitorConfig;
34 |
35 | static {
36 | rootObj = JsonLoader.loadJSONFile(CuratorService.class.getClassLoader().getResourceAsStream("kafkaMonitorConfig.json"));
37 | kafkaconfig = JSONObject.parseObject(rootObj.getJSONObject("kafka").toJSONString(), KafkaConfig.class);
38 | zooConfig = JSONObject.parseObject(rootObj.getJSONObject("zookeeper").toJSONString(), ZooConfig.class);
39 | monitorConfig = JSONObject.parseObject(rootObj.toJSONString(), MonitorConfig.class);
40 | }
41 |
42 |
43 | /**
44 | * kafka配置
45 | *
46 | * @return
47 | */
48 | public static Properties getKafkaProducerConf() {
49 | Properties props = new Properties();
50 | props.setProperty(bootstrapServers, KafkaBaseInfoService.getInstance().reandomBrokerHost());
51 | props.put(keyDeserializer, kafkaconfig.getKeyDeserializer());
52 | props.put(valueDeserializer, kafkaconfig.getValueDeserializer());
53 | props.put(keySerializer, kafkaconfig.getKeySerializer());
54 | props.put(valueSerializer, kafkaconfig.getValueSerializer());
55 | return props;
56 | }
57 |
58 | /**
59 | * consumer配置
60 | *
61 | * @return
62 | */
63 | public static Properties getKafkaConsumerConf() {
64 | Properties props = new Properties();
65 | props.setProperty(bootstrapServers, KafkaBaseInfoService.getInstance().reandomBrokerHost());
66 | props.put(enableAutoCommit, kafkaconfig.getEnableAutoCommit());
67 | props.put(autoCommitIntervalMs, kafkaconfig.getAutoCommitIntervalMs());
68 | props.put(sessionTimeoutMs, kafkaconfig.getSessionTimeoutMs());
69 | props.put(keyDeserializer, kafkaconfig.getKeyDeserializer());
70 | props.put(valueDeserializer, kafkaconfig.getValueDeserializer());
71 | props.put(keySerializer, kafkaconfig.getKeySerializer());
72 | props.put(valueSerializer, kafkaconfig.getValueSerializer());
73 | props.put(groupId, kafkaconfig.getGroupId());
74 | return props;
75 | }
76 |
77 | /**
78 | * zookeeper配置
79 | *
80 | * @return
81 | */
82 | public static Properties getZkProper() {
83 | Properties props = new Properties();
84 | props.put(ZooConfig.HOST, zooConfig.getHost());
85 | props.put(ZooConfig.SESSION_TIMEOUT_MS, zooConfig.getSessionTimeoutMs());
86 | props.put(ZooConfig.CONNECTION_TIMEOUT_MS, zooConfig.getConnectionTimeoutMs());
87 | props.put(ZooConfig.RETRY_ONE_TIME, zooConfig.getRetryOneTime());
88 | return props;
89 | }
90 |
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/service/ConsumerService.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.service;
2 |
3 | import com.alibaba.fastjson.JSON;
4 | import com.alibaba.fastjson.JSONObject;
5 | import co.solinx.kafka.monitor.core.consumer.MonitorConsumer;
6 | import co.solinx.kafka.monitor.core.consumer.MonitorConsumerRecord;
7 | import org.apache.kafka.common.MetricName;
8 | import org.apache.kafka.common.metrics.*;
9 | import org.apache.kafka.common.metrics.stats.*;
10 | import org.apache.kafka.common.utils.SystemTime;
11 | import org.slf4j.Logger;
12 | import org.slf4j.LoggerFactory;
13 |
14 | import java.math.BigDecimal;
15 | import java.text.DecimalFormat;
16 | import java.util.*;
17 | import java.util.concurrent.TimeUnit;
18 |
19 | /**
20 | * 监控程序consumer写入服务
21 | *
22 | * @author linxin
23 | * @version v1.0
24 | * Copyright (c) 2015 by solinx
25 | * @date 2016/12/23.
26 | */
27 | public class ConsumerService {
28 |
29 | private static final Logger logger = LoggerFactory.getLogger(ConsumerService.class);
30 | private static final String METRIC_GROUP_NAME = "consumer-service";
31 |
32 | private final String name = "monitor";
33 | private final Thread thread;
34 | private final ConsumerMetrics sensor;
35 | private MonitorConsumer consumer;
36 | private String MONITOR_TOPIC;
37 | private int delayedTime = 20_000;
38 |
39 |
40 | public ConsumerService() {
41 |
42 | thread = new Thread(() -> {
43 | consumer();
44 | }, name + "consumer-service");
45 |
46 | Properties props = ConfigService.getKafkaConsumerConf();
47 | MONITOR_TOPIC = ConfigService.monitorConfig.getMonitorTopic();
48 |
49 | consumer = new MonitorConsumer(MONITOR_TOPIC, props);
50 |
51 | MetricConfig metricConfig = new MetricConfig().samples(60).timeWindow(1000, TimeUnit.MILLISECONDS);
52 | List reporterList = new ArrayList<>();
53 | reporterList.add(new JmxReporter("kmf.services"));
54 | Metrics metrics = new Metrics(metricConfig, reporterList, new SystemTime());
55 | Map tags = new HashMap<>();
56 | tags.put("name", "monitor");
57 | sensor = new ConsumerMetrics(metrics, tags);
58 | }
59 |
60 | public void start() {
61 | thread.start();
62 | }
63 |
64 | //逻辑 producer生产消息时存入序号、时间
65 | //使用消息生产的时间与consumer消费的时间差作为延迟时间,延迟时间超过某个阀值时该消息延迟的
66 | //使用index判断消息的重复与失败,index为producer生产的序列,
67 | //consumer某个partition首次接收到消息时消息index=0将该partition的nextIndex设置为1,
68 | //consumer某个partition第二次收到消息时当前nextIndex为1,消息的index为1,正常情况下nextIndex==index,nextIndex=index+1
69 | //往后每次收到的消息消息正常时index与nextIndex都是相等的
70 | // indexnextIndex时,消息有丢失,丢失了index-nextIndex条消息,nextIndex=index+1
72 | public void consumer() {
73 |
74 | Map nextIndexs = new HashMap<>();
75 | while (true) {
76 |
77 | MonitorConsumerRecord record;
78 | try {
79 | record = consumer.receive();
80 | JSONObject messageObj = JSON.parseObject(record.getValue());
81 | long msgTime = messageObj.getLong("time");
82 | int msgPartition = record.getPartition();
83 | long index = messageObj.getLong("index");
84 | String topic = messageObj.getString("topic");
85 | long curTime = System.currentTimeMillis();
86 | //延迟时间
87 | long delayTime = curTime - msgTime;
88 | sensor.recordsDelay.record(delayTime);
89 | if (delayTime > delayedTime) {
90 | sensor.recordsDelayed.record();
91 | }
92 | sensor.recordsConsume.record();
93 |
94 | if (!nextIndexs.containsKey(msgPartition)) {
95 | nextIndexs.put(msgPartition, 1l);
96 | continue;
97 | }
98 |
99 | long nextIndex = nextIndexs.get(msgPartition);
100 | if (index == nextIndex) {
101 | nextIndexs.put(msgPartition, index + 1);
102 | } else if (index < nextIndex) {
103 | sensor.recordsDuplicated.record();
104 | } else if (index > nextIndex) {
105 | sensor.recordsLost.record(index - nextIndex);
106 | nextIndexs.put(msgPartition, index + 1);
107 | }
108 | } catch (Exception e) {
109 | sensor.consumerError.record();
110 | logger.warn("{}", e);
111 |
112 | continue;
113 | }
114 | }
115 |
116 | }
117 |
118 | private class ConsumerMetrics {
119 | public final Metrics metrics;
120 | private final Sensor bytesConsume;
121 | private final Sensor consumerError;
122 | private final Sensor recordsConsume;
123 | private final Sensor recordsDuplicated;
124 | private final Sensor recordsLost;
125 | private final Sensor recordsDelay;
126 | private final Sensor recordsDelayed;
127 |
128 |
129 | public ConsumerMetrics(Metrics metrics, final Map tags) {
130 |
131 | this.metrics = metrics;
132 |
133 | consumerError = metrics.sensor("consume-error");
134 | consumerError.add(new MetricName("consume-error-rate", METRIC_GROUP_NAME, "The average number of errors per second", tags), new Rate());
135 | consumerError.add(new MetricName("consume-error-total", METRIC_GROUP_NAME, "The total number of errors", tags), new Total());
136 |
137 | recordsConsume = metrics.sensor("records-consumed");
138 | recordsConsume.add(new MetricName("records-consumed-rate", METRIC_GROUP_NAME, "The average number of records per second that are consumed", tags), new Rate());
139 | recordsConsume.add(new MetricName("records-consumed-total", METRIC_GROUP_NAME, "The total number of records that are consumed", tags), new Total());
140 |
141 | bytesConsume = metrics.sensor("bytes-consume");
142 | recordsDuplicated = metrics.sensor("records-duplicated");
143 | recordsDuplicated.add(new MetricName("records-duplicated-rate", METRIC_GROUP_NAME, "The average number of records per second that are duplicated", tags), new Rate());
144 | recordsDuplicated.add(new MetricName("records-duplicated-total", METRIC_GROUP_NAME, "The total number of records that are duplicated", tags), new Total());
145 |
146 | recordsDelay = metrics.sensor("records-delay");
147 | recordsDelay.add(new MetricName("records-delay-ms-avg", METRIC_GROUP_NAME, "The average latency of records from producer to consumer", tags), new SampledStat(0) {
148 | @Override
149 | protected void update(Sample sample, MetricConfig config, double value, long timeMs) {
150 | sample.value += value;
151 | }
152 |
153 | @Override
154 | public double combine(List samples, MetricConfig config, long now) {
155 | double total = 0.0;
156 | double count = 0;
157 | for (int i = 0; i < samples.size(); i++) {
158 | Sample s = samples.get(i);
159 | total += s.value;
160 | count += s.eventCount;
161 | }
162 | BigDecimal bTotal = new BigDecimal(Double.toString(total));
163 | BigDecimal bCount = new BigDecimal(Double.toString(count));
164 |
165 | return count == 0 ? 0 : bTotal.divide(bCount, 3, BigDecimal.ROUND_HALF_UP).doubleValue();
166 | }
167 | });
168 | recordsDelay.add(new MetricName("records-delay-ms-max", METRIC_GROUP_NAME, "The maximum latency of records from producer to consumer", tags), new Max());
169 |
170 | recordsLost = metrics.sensor("records-lost");
171 | recordsLost.add(new MetricName("records-lost-rate", METRIC_GROUP_NAME, "The average number of records per second that are lost", tags), new Rate());
172 | recordsLost.add(new MetricName("records-lost-total", METRIC_GROUP_NAME, "The total number of records that are lost", tags), new Total());
173 |
174 |
175 | recordsDelayed = metrics.sensor("records-delayed");
176 | recordsDelayed.add(new MetricName("records-delayed-rate", METRIC_GROUP_NAME, "The average number of records per second that are either lost or arrive after maximum allowed latency under SLA", tags), new Rate());
177 | recordsDelayed.add(new MetricName("records-delayed-total", METRIC_GROUP_NAME, "The total number of records that are either lost or arrive after maximum allowed latency under SLA", tags), new Total());
178 |
179 |
180 | metrics.addMetric(new MetricName("consume-availability-avg", METRIC_GROUP_NAME, "The average consume availability", tags),
181 | (config, now) -> {
182 | double recordsConsumedRate = sensor.metrics.metrics().get(new MetricName("records-consumed-rate", METRIC_GROUP_NAME, tags)).value();
183 | double recordsLostRate = sensor.metrics.metrics().get(new MetricName("records-lost-rate", METRIC_GROUP_NAME, tags)).value();
184 | double recordsDelayedRate = sensor.metrics.metrics().get(new MetricName("records-delayed-rate", METRIC_GROUP_NAME, tags)).value();
185 |
186 | if (new Double(recordsLostRate).isNaN()) {
187 | recordsLostRate = 0;
188 | }
189 | if (new Double(recordsDelayedRate).isNaN()) {
190 | recordsDelayedRate = 0;
191 | }
192 |
193 | double consumeAvailability = recordsConsumedRate + recordsLostRate > 0 ?
194 | (recordsConsumedRate - recordsDelayedRate) / (recordsConsumedRate + recordsLostRate) : 0;
195 | BigDecimal bg = new BigDecimal(consumeAvailability);
196 | return bg.setScale(3, BigDecimal.ROUND_HALF_UP).doubleValue();
197 | });
198 |
199 | }
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/service/CuratorService.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.service;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import org.apache.curator.framework.CuratorFramework;
5 | import org.apache.curator.framework.CuratorFrameworkFactory;
6 | import org.apache.curator.retry.RetryOneTime;
7 |
8 | /**
9 | * @author linxin
10 | * @version v1.0
11 | * Copyright (c) 2015 by solinx
12 | * @date 2016/12/14.
13 | */
14 | public class CuratorService {
15 |
16 | static CuratorFramework curator = null;
17 | static JSONObject configObject = ConfigService.rootObj;
18 |
19 | private CuratorService() {
20 |
21 | }
22 |
23 | public static CuratorFramework getInstance() {
24 | if (curator == null) {
25 | JSONObject zkConfig = configObject.getJSONObject("zookeeper");
26 | CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder();
27 | builder.connectString(zkConfig.getString("host"));
28 | builder.sessionTimeoutMs(zkConfig.getInteger("SessionTimeoutMs"));
29 | builder.connectionTimeoutMs(zkConfig.getInteger("ConnectionTimeoutMs"));
30 | builder.retryPolicy(new RetryOneTime(zkConfig.getInteger("RetryOneTime")));
31 |
32 | curator = builder.build();
33 | curator.start();
34 | }
35 | return curator;
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/co/solinx/kafka/monitor/core/service/CustomConsumerGroupService.java:
--------------------------------------------------------------------------------
1 | package co.solinx.kafka.monitor.core.service;
2 |
3 | import kafka.admin.AdminClient;
4 | import kafka.admin.ConsumerGroupCommand;
5 | import kafka.common.TopicAndPartition;
6 | import kafka.coordinator.GroupOverview;
7 | import org.apache.kafka.clients.consumer.Consumer;
8 | import org.apache.kafka.clients.consumer.OffsetAndMetadata;
9 | import org.apache.kafka.common.TopicPartition;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 | import scala.Function1;
13 | import scala.Option;
14 | import scala.collection.JavaConversions;
15 | import scala.collection.Seq;
16 |
17 | import java.util.*;
18 | import java.util.stream.Collectors;
19 | import java.util.stream.Stream;
20 |
21 | /**
22 | * @author linxin
23 | * @version v1.0
24 | * Copyright (c) 2015 by solinx
25 | * @date 2016/12/20.
26 | */
27 | public class CustomConsumerGroupService {
28 |
29 | private Logger logger = LoggerFactory.getLogger(CustomConsumerGroupService.class);
30 | private AdminClient adminClient;
31 | private org.apache.kafka.clients.consumer.KafkaConsumer consumer;
32 | Properties props = new Properties();
33 |
34 | public CustomConsumerGroupService() {
35 | props = ConfigService.getKafkaConsumerConf();
36 | adminClient = AdminClient.create(props);
37 |
38 | logger.debug("{}", adminClient.bootstrapBrokers());
39 | }
40 |
41 |
42 | /**
43 | * 取得所有group
44 | *
45 | * @return
46 | */
47 | public List groupList() {
48 | return JavaConversions.asJavaList(adminClient.listAllConsumerGroupsFlattened());
49 | }
50 |
51 |
52 | public void close() {
53 | adminClient.close();
54 | }
55 |
56 | public ConsumerGroupCommand.ConsumerGroupCommandOptions opts() {
57 | return null;
58 | }
59 |
60 | /**
61 | * 取得group下面所有consumer
62 | *
63 | * @param group
64 | * @return
65 | */
66 | public List getConsumerList(String group) {
67 | List consumerList = new ArrayList();
68 | List consumerSummaryList = JavaConversions.asJavaList(adminClient.describeConsumerGroup(group));
69 |
70 | Consumer consumer = getConsumer();
71 | consumerSummaryList.stream().forEach(consumerSummary -> {
72 |
73 | //取得topic与partition
74 | List topicAndPartitionStream = JavaConversions.asJavaList(consumerSummary.assignment())
75 | .parallelStream().map(tp -> new TopicAndPartition(tp.topic(), tp.partition()))
76 | .collect(Collectors.toList());
77 |
78 | /**
79 | * partition与偏移信息
80 | */
81 | Stream