From e193834e82b2641bc9462d478836498ecbdd7c22 Mon Sep 17 00:00:00 2001 From: yongfeigao Date: Fri, 7 Dec 2018 18:13:32 +0800 Subject: [PATCH] =?UTF-8?q?1.3=E5=8F=91=E5=B8=83=201=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=B2=BE=E5=87=86offset=E6=90=9C=E7=B4=A2=20?= =?UTF-8?q?2=20=E9=87=8D=E8=AF=95&=E6=AD=BB=E6=B6=88=E6=81=AF=E6=94=AF?= =?UTF-8?q?=E6=8C=81offset=E6=90=9C=E7=B4=A2=203=20=E5=AE=A2=E6=88=B7?= =?UTF-8?q?=E7=AB=AF=E5=BC=82=E5=B8=B8=E5=8F=91=E9=80=81=E5=88=B0=E8=B4=9F?= =?UTF-8?q?=E8=B4=A3=E4=BA=BA=204=20=E6=94=AF=E6=8C=81=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=87=8D=E5=8F=91=E5=AE=A1=E6=A0=B8=205=20=E6=B6=88=E8=B4=B9?= =?UTF-8?q?=E8=80=85=E6=B6=88=E8=B4=B9=E6=B6=88=E6=81=AF=E9=87=8D=E8=AF=95?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E4=BB=8E=E9=BB=98=E8=AE=A415=E5=88=86?= =?UTF-8?q?=E9=92=9F=E6=89=A9=E5=A4=A7=E8=87=B32=E5=B0=8F=E6=97=B6=206=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=83=A8=E5=88=86UI=207=20=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=9F=A5=E8=AF=A2=E6=89=A9=E5=B1=95=E4=B8=BA?= =?UTF-8?q?=E4=BB=BB=E6=84=8F=E6=97=B6=E9=97=B4=E6=AE=B5=208=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E6=AD=BB=E6=B6=88=E6=81=AF=E9=98=9F=E5=88=97=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=209=20=E6=B6=88=E6=81=AF=E5=B1=95=E7=A4=BA=E6=94=AF?= =?UTF-8?q?=E6=8C=81xml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 + mq-client-common-open/pom.xml | 2 +- .../com/sohu/index/tv/mq/common/Result.java | 4 +- .../java/com/sohu/tv/mq/util/Version.java | 2 +- .../com/sohu/tv/mq/util/SerializerTest.java | 239 ---------------- mq-client-open/pom.xml | 2 +- .../sohu/tv/mq/rocketmq/RocketMQConsumer.java | 6 +- .../mq/rocketmq/RocketMQConsumerJsonTest.java | 41 +++ .../mq/rocketmq/RocketMQProducerJsonTest.java | 41 +++ .../tv/mq/rocketmq/RocketMQProducerTest.java | 7 +- .../com/sohu/tv/mq/util/SerializerTest.java | 194 ------------- mq-client-open/src/test/resources/logback.xml | 16 -- mq-cloud-common/pom.xml | 2 +- mq-cloud/pom.xml | 2 +- mq-cloud/sql/1.2-1.3.sql | 12 + mq-cloud/sql/init.sql | 13 + .../com/sohu/tv/mq/cloud/bo/AlarmConfig.java | 4 +- .../java/com/sohu/tv/mq/cloud/bo/Audit.java | 1 + .../tv/mq/cloud/bo/AuditResendMessage.java | 113 ++++++++ .../java/com/sohu/tv/mq/cloud/bo/Broker.java | 2 +- .../sohu/tv/mq/cloud/bo/DecodedMessage.java | 17 ++ .../com/sohu/tv/mq/cloud/bo/MQOffset.java | 2 +- .../tv/mq/cloud/bo/MessageQueryCondition.java | 26 +- .../com/sohu/tv/mq/cloud/bo/NameServer.java | 4 +- .../sohu/tv/mq/cloud/bo/ServerInfoExt.java | 2 +- .../tv/mq/cloud/conf/CommonConfiguration.java | 2 +- .../sohu/tv/mq/cloud/dao/AlarmConfigDao.java | 2 +- .../mq/cloud/dao/AuditResendMessageDao.java | 58 ++++ .../com/sohu/tv/mq/cloud/dao/ConsumerDao.java | 4 +- .../sohu/tv/mq/cloud/dao/NameServerDao.java | 4 +- .../sohu/tv/mq/cloud/dao/ServerStatusDao.java | 4 +- .../sohu/tv/mq/cloud/dao/UserConsumerDao.java | 4 +- .../com/sohu/tv/mq/cloud/dao/UserDao.java | 4 +- .../sohu/tv/mq/cloud/mq/MQAdminTemplate.java | 2 - .../com/sohu/tv/mq/cloud/mq/SohuMQAdmin.java | 39 ++- .../service/AlarmConfigBridingService.java | 2 +- .../mq/cloud/service/AlarmConfigService.java | 2 +- .../service/AuditResendMessageService.java | 95 +++++++ .../tv/mq/cloud/service/AuditService.java | 30 ++ .../service/ConsumerRetryTrafficService.java | 4 +- .../tv/mq/cloud/service/ConsumerService.java | 5 +- .../sohu/tv/mq/cloud/service/MQDeployer.java | 4 +- .../tv/mq/cloud/service/MessageService.java | 107 ++++++- .../mq/cloud/service/ServerDataService.java | 4 +- .../tv/mq/cloud/service/TopicService.java | 75 ++++- .../mq/cloud/service/UserConsumerService.java | 4 +- .../impl/DefaultAlertMessageSender.java | 4 +- .../tv/mq/cloud/task/AlarmConfigTask.java | 4 +- .../tv/mq/cloud/task/ProducerStatsTask.java | 100 ++++--- .../tv/mq/cloud/task/ServerStatusTask.java | 4 +- .../task/monitor/SohuMonitorListener.java | 4 +- .../com/sohu/tv/mq/cloud/util/DateUtil.java | 3 +- .../com/sohu/tv/mq/cloud/util/SplitUtil.java | 4 +- .../com/sohu/tv/mq/cloud/util/Status.java | 6 +- .../web/controller/ConsumerController.java | 28 +- .../cloud/web/controller/LoginController.java | 4 +- .../web/controller/RegisterController.java | 2 +- .../controller/TopicMessageController.java | 268 +++++++++++++---- .../cloud/web/controller/UserController.java | 4 +- .../admin/AdminBrokerController.java | 4 +- .../admin/AdminClusterController.java | 6 +- .../admin/AdminMessageController.java | 211 ++++++++++++++ .../admin/AdminServerController.java | 4 +- .../controller/admin/AdminUserController.java | 4 +- .../admin/AlarmConfigController.java | 4 +- .../web/controller/admin/AuditController.java | 96 ++++++- .../controller/admin/MonitorController.java | 4 +- .../controller/param/AlarmConfigParam.java | 4 +- .../web/controller/param/BrokerParam.java | 4 +- .../mq/cloud/web/vo/AuditResendMessageVO.java | 27 ++ .../sohu/tv/mq/cloud/web/vo/BrokerStatVO.java | 4 +- .../mq/cloud/web/vo/ConsumerProgressVO.java | 102 ++++++- .../tv/mq/cloud/web/vo/ResendMessageVO.java | 69 +++++ mq-cloud/src/main/resources/application.yml | 2 +- .../src/main/resources/static/css/common.css | 25 +- .../static/img/intro/consumeRetry.png | Bin 0 -> 23788 bytes .../src/main/resources/static/js/common.js | 47 +++ .../resources/multiselect/multiselect.js | 89 ++++++ .../templates/admin/audit/addTopic.html | 4 +- .../resources/templates/admin/audit/list.html | 4 +- .../templates/admin/audit/resendMessage.html | 164 +++++++++++ .../templates/admin/cluster/list.html | 4 +- .../templates/admin/monitor/consumer.html | 4 +- .../templates/admin/nameserver/list.html | 4 +- .../templates/admin/server/list.html | 5 +- .../resources/templates/admin/user/list.html | 4 +- .../templates/consumer/progress.html | 269 +++++++++++++++--- .../main/resources/templates/inc/include.html | 3 + .../main/resources/templates/inc/left.html | 4 +- .../main/resources/templates/login/index.html | 4 +- .../resources/templates/msg/idSearch.html | 11 +- .../main/resources/templates/msg/index.html | 177 ++++++++++-- .../resources/templates/msg/keySearch.html | 15 +- .../resources/templates/msg/offsetSearch.html | 31 ++ .../main/resources/templates/msg/search.html | 13 +- .../templates/msg/topicOffsetSearch.html | 28 ++ .../resources/templates/register/index.html | 4 +- .../main/resources/templates/user/topic.html | 6 +- .../main/resources/templates/user/update.html | 4 +- .../tv/mq/cloud/mq/MQAdminTemplateTest.java | 20 +- .../sohu/tv/mq/cloud/mq/SohuMQAdminTest.java | 59 ++++ .../AlarmConfigBridingServiceTest.java | 4 +- .../mq/cloud/service/MessageServiceTest.java | 4 +- .../service/SohuMonitorListenerTest.java | 4 +- .../mq/cloud/task/ClusterMonitorTaskTest.java | 2 +- .../mq/cloud/task/ProducerStatsTaskTest.java | 4 +- pom.xml | 2 +- 107 files changed, 2418 insertions(+), 797 deletions(-) delete mode 100644 mq-client-common-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java create mode 100644 mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQConsumerJsonTest.java create mode 100644 mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerJsonTest.java delete mode 100644 mq-client-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java create mode 100644 mq-cloud/sql/1.2-1.3.sql create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AuditResendMessage.java create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AuditResendMessageDao.java create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditResendMessageService.java create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminMessageController.java create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/AuditResendMessageVO.java create mode 100644 mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ResendMessageVO.java create mode 100644 mq-cloud/src/main/resources/static/img/intro/consumeRetry.png create mode 100644 mq-cloud/src/main/resources/static/resources/multiselect/multiselect.js create mode 100644 mq-cloud/src/main/resources/templates/admin/audit/resendMessage.html create mode 100644 mq-cloud/src/main/resources/templates/msg/offsetSearch.html create mode 100644 mq-cloud/src/main/resources/templates/msg/topicOffsetSearch.html create mode 100644 mq-cloud/src/test/java/com/sohu/tv/mq/cloud/mq/SohuMQAdminTest.java diff --git a/README.md b/README.md index c239ecb2..a863a825 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,10 @@ ![消费详情](mq-cloud/src/main/resources/static/img/intro/consumeDetail2.png) +* 某个消费者具体的消费详情-可以查询重试消息和死消息 + + ![消费详情](mq-cloud/src/main/resources/static/img/intro/consumeRetry.png) + * 消息 ![消息](mq-cloud/src/main/resources/static/img/intro/msgSearch.png) diff --git a/mq-client-common-open/pom.xml b/mq-client-common-open/pom.xml index 6d7ceecb..e9d5e1ba 100644 --- a/mq-client-common-open/pom.xml +++ b/mq-client-common-open/pom.xml @@ -6,7 +6,7 @@ com.sohu.tv mq - 1.2.RELEASE + 1.3.RELEASE mq-client-common-open diff --git a/mq-client-common-open/src/main/java/com/sohu/index/tv/mq/common/Result.java b/mq-client-common-open/src/main/java/com/sohu/index/tv/mq/common/Result.java index 58642cda..f6480511 100644 --- a/mq-client-common-open/src/main/java/com/sohu/index/tv/mq/common/Result.java +++ b/mq-client-common-open/src/main/java/com/sohu/index/tv/mq/common/Result.java @@ -60,10 +60,10 @@ public Exception getException() { public void setException(Exception exception) { this.exception = exception; - } + } @Override public String toString() { return "Result [isSuccess=" + isSuccess + ", result=" + result + ", exception=" + exception + "]"; - } + } } diff --git a/mq-client-common-open/src/main/java/com/sohu/tv/mq/util/Version.java b/mq-client-common-open/src/main/java/com/sohu/tv/mq/util/Version.java index 0d7bb435..4133908c 100644 --- a/mq-client-common-open/src/main/java/com/sohu/tv/mq/util/Version.java +++ b/mq-client-common-open/src/main/java/com/sohu/tv/mq/util/Version.java @@ -7,6 +7,6 @@ public class Version { public static String get() { - return "1.2.RELEASE"; + return "1.3.RELEASE"; } } diff --git a/mq-client-common-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java b/mq-client-common-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java deleted file mode 100644 index 3a267ac1..00000000 --- a/mq-client-common-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.sohu.tv.mq.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import org.junit.Assert; -import org.junit.Test; - -import com.sohu.tv.mq.serializable.DefaultMessageSerializer; - -@SuppressWarnings({"unchecked", "rawtypes"}) -public class SerializerTest { - - @Test - public void testSerialize() throws Exception { - ObjectA objectA = generateTestObject(); - byte[] byteArray = new DefaultMessageSerializer().serialize(objectA); - store(byteArray); - Assert.assertTrue(byteArray.length > 1); - } - - @Test - public void testDeserialize() throws Exception { - byte[] byteArray = read(); - ObjectA objectA2 = (ObjectA) new DefaultMessageSerializer().deserialize(byteArray); - Assert.assertNotNull(objectA2); - clean(); - } - - public void clean() { - File f = new File("tmp"); - f.deleteOnExit(); - } - - private byte[] read() throws Exception { - FileInputStream fis = new FileInputStream("tmp"); - byte[] byteArray = new byte[fis.available()]; - fis.read(byteArray); - fis.close(); - return byteArray; - } - - private void store(byte[] byteArray) throws Exception { - FileOutputStream fos = new FileOutputStream("tmp"); - fos.write(byteArray); - fos.close(); - } - - public static ObjectA generateTestObject() { - List list = new ArrayList(); - ObjectB objectB = null; - for(int i = 1; i <= 3; ++i) { - objectB = new ObjectB(); - objectB.setI(i); - objectB.setI(Integer.valueOf(String.valueOf(i))); - objectB.setL(i); - objectB.setL(Long.valueOf(String.valueOf(i))); - objectB.setS((short)i); - objectB.setS(Short.valueOf(String.valueOf(i))); - objectB.setC('a'); - objectB.setC(Character.valueOf('a')); - objectB.setB((byte)i); - objectB.setB(Byte.valueOf(String.valueOf(i))); - objectB.setDate(new Date()); - objectB.setStr("abc"); - String[] sarray = {"a", "b", "c"}; - objectB.setSarray(sarray); - list.add(objectB); - } - - ObjectA objectA = new ObjectA(); - objectA.setI(8); - objectA.setI(Integer.valueOf(String.valueOf("8"))); - objectA.setL(8); - objectA.setL(Long.valueOf(String.valueOf("8"))); - objectA.setS((short)8); - objectA.setS(Short.valueOf(String.valueOf("8"))); - objectA.setC('a'); - objectA.setC(Character.valueOf('a')); - objectA.setB((byte)8); - objectA.setB(Byte.valueOf(String.valueOf("8"))); - objectA.setDate(new Date()); - objectA.setStr("abc"); - String[] sarray = {"a", "b", "c"}; - objectA.setSarray(sarray); - objectA.setName("name"); - objectA.setObjectB(objectB); - objectA.setBlist(list); - - return objectA; - } - - public static class ObjectP{ - private String name; - public void setName(String name) { - this.name = name; - } - @Override - public String toString() { - return "ObjectP [name=" + name + "]"; - } - } - - public static class ObjectA extends ObjectP{ - private int i; - private Integer I; - private long l; - private Long L; - private short s; - private Short S; - private char c; - private Character C; - private byte b; - private Byte B; - private String str; - private String[] sarray; - private Date date; - private ObjectB objectB; - private List blist; - public void setI(int i) { - this.i = i; - } - public void setI(Integer i) { - I = i; - } - public void setL(long l) { - this.l = l; - } - public void setL(Long l) { - L = l; - } - public void setS(short s) { - this.s = s; - } - public void setS(Short s) { - S = s; - } - public void setC(char c) { - this.c = c; - } - public void setC(Character c) { - C = c; - } - public void setB(byte b) { - this.b = b; - } - public void setB(Byte b) { - B = b; - } - public void setStr(String str) { - this.str = str; - } - public void setSarray(String[] sarray) { - this.sarray = sarray; - } - public void setDate(Date date) { - this.date = date; - } - public void setObjectB(ObjectB objectB) { - this.objectB = objectB; - } - public void setBlist(List blist) { - this.blist = blist; - } - @Override - public String toString() { - return "ObjectA [i=" + i + ", I=" + I + ", l=" + l + ", L=" + L + ", s=" + s + ", S=" + S + ", c=" + c - + ", C=" + C + ", b=" + b + ", B=" + B + ", str=" + str + ", sarray=" + Arrays.toString(sarray) - + ", date=" + date + ", objectB=" + objectB + ", blist=" + blist + ", toString()=" - + super.toString() + "]"; - } - } - - public static class ObjectB{ - private int i; - private Integer I; - private long l; - private Long L; - private short s; - private Short S; - private char c; - private Character C; - private byte b; - private Byte B; - private String str; - private String[] sarray; - private Date date; - public void setI(int i) { - this.i = i; - } - public void setI(Integer i) { - I = i; - } - public void setL(long l) { - this.l = l; - } - public void setL(Long l) { - L = l; - } - public void setS(short s) { - this.s = s; - } - public void setS(Short s) { - S = s; - } - public void setC(char c) { - this.c = c; - } - public void setC(Character c) { - C = c; - } - public void setB(byte b) { - this.b = b; - } - public void setB(Byte b) { - B = b; - } - public void setStr(String str) { - this.str = str; - } - public void setSarray(String[] sarray) { - this.sarray = sarray; - } - public void setDate(Date date) { - this.date = date; - } - @Override - public String toString() { - return "ObjectB [i=" + i + ", I=" + I + ", l=" + l + ", L=" + L + ", s=" + s + ", S=" + S + ", c=" + c - + ", C=" + C + ", b=" + b + ", B=" + B + ", str=" + str + ", sarray=" + Arrays.toString(sarray) - + ", date=" + date + "]"; - } - } -} diff --git a/mq-client-open/pom.xml b/mq-client-open/pom.xml index 6f5c766b..567739c5 100644 --- a/mq-client-open/pom.xml +++ b/mq-client-open/pom.xml @@ -6,7 +6,7 @@ com.sohu.tv mq - 1.2.RELEASE + 1.3.RELEASE mq-client-open diff --git a/mq-client-open/src/main/java/com/sohu/tv/mq/rocketmq/RocketMQConsumer.java b/mq-client-open/src/main/java/com/sohu/tv/mq/rocketmq/RocketMQConsumer.java index 3797e015..021f07a2 100644 --- a/mq-client-open/src/main/java/com/sohu/tv/mq/rocketmq/RocketMQConsumer.java +++ b/mq-client-open/src/main/java/com/sohu/tv/mq/rocketmq/RocketMQConsumer.java @@ -65,6 +65,7 @@ public class RocketMQConsumer extends AbstractConfig { public RocketMQConsumer(String consumerGroup, String topic) { super(consumerGroup, topic); consumer = new DefaultMQPushConsumer(consumerGroup); + consumer.setConsumeTimeout(2 * 60); } public void start() { @@ -86,8 +87,8 @@ public ConsumeConcurrentlyStatus consumeMessage(List msgs, } List> msgList = null; for (MessageExt me : msgs) { + byte[] bytes = me.getBody(); try { - byte[] bytes = me.getBody(); if (bytes == null || bytes.length == 0) { logger.warn("MessageExt={},MessageBody is null", me); continue; @@ -115,7 +116,8 @@ public ConsumeConcurrentlyStatus consumeMessage(List msgs, msgList.add((Map) getMessageSerializer().deserialize(bytes)); } } catch (Exception e) { - logger.error(e.getMessage(), e); + logger.error("topic:{} consumer:{} msg:{} msgId:{} bornTimestamp:{}", getTopic(), + getConsumer(), new String(bytes), me.getMsgId(), me.getBornTimestamp(), e); if (reconsume) { return ConsumeConcurrentlyStatus.RECONSUME_LATER; } diff --git a/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQConsumerJsonTest.java b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQConsumerJsonTest.java new file mode 100644 index 00000000..05790487 --- /dev/null +++ b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQConsumerJsonTest.java @@ -0,0 +1,41 @@ +package com.sohu.tv.mq.rocketmq; + +import java.util.concurrent.atomic.AtomicLong; + +import org.apache.rocketmq.common.message.MessageExt; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.sohu.index.tv.mq.common.ConsumerCallback; + +public class RocketMQConsumerJsonTest { + + private AtomicLong counter = new AtomicLong(); + + private RocketMQConsumer consumer; + + @Before + public void init() { + consumer = TestUtil.buildConsumer("mqcloud-test-topic-consumer", "mqcloud-test-topic"); + } + + @Test + public void test() throws InterruptedException { + consumer.setConsumerCallback(new ConsumerCallback() { + public void call(String t, MessageExt k) throws Exception { + System.out.println(t); + } + }); + consumer.start(); + while (true) { + System.out.println(counter.get()); + Thread.sleep(1000); + } + } + + @After + public void clean() { + consumer.shutdown(); + } +} diff --git a/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerJsonTest.java b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerJsonTest.java new file mode 100644 index 00000000..ebd997eb --- /dev/null +++ b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerJsonTest.java @@ -0,0 +1,41 @@ +package com.sohu.tv.mq.rocketmq; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.rocketmq.client.producer.SendResult; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import com.alibaba.fastjson.JSON; +import com.sohu.index.tv.mq.common.Result; + +public class RocketMQProducerJsonTest { + + private RocketMQProducer producer; + + @Before + public void init() { + producer = TestUtil.buildProducer("mqcloud-test-topic-producer", "mqcloud-test-topic"); + producer.start(); + } + + @Test + public void produce() { + Map map = new HashMap(); + map.put("a", "b"); + map.put("c", "d"); + map.put("o", "c"); + String str = JSON.toJSONString(map); + Result sendResult = producer.publish(str, "abc"); + System.out.println(sendResult); + Assert.assertTrue(sendResult.isSuccess()); + } + + @After + public void clean() { + producer.shutdown(); + } +} diff --git a/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerTest.java b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerTest.java index b393c73b..b0363fd7 100644 --- a/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerTest.java +++ b/mq-client-open/src/test/java/com/sohu/tv/mq/rocketmq/RocketMQProducerTest.java @@ -10,7 +10,6 @@ import org.junit.Test; import com.sohu.index.tv.mq.common.Result; -import com.sohu.tv.mq.util.SerializerTest; public class RocketMQProducerTest { @@ -27,7 +26,7 @@ public void produce() { Map map = new HashMap(); map.put("a", "b"); map.put("c", "d"); - map.put("o", SerializerTest.generateTestObject()); + map.put("o", "c"); Result sendResult = producer.publish(map); Assert.assertTrue(sendResult.isSuccess()); } @@ -36,7 +35,7 @@ public void produce() { public void produce100() throws InterruptedException { Map map = new HashMap(); map.put("c", "d"); - map.put("o", SerializerTest.generateTestObject()); + map.put("o", "c"); for (int i = 0; i < 1000; i++) { map.put("a", i); Result sendResult = producer.publish(map); @@ -50,7 +49,7 @@ public void produceByte() { Map map = new HashMap(); map.put("a", "b"); map.put("c", "d"); - map.put("o", SerializerTest.generateTestObject()); + map.put("o", "c"); Result sendResult = producer.publish(map.toString().getBytes()); Assert.assertTrue(sendResult.isSuccess()); } diff --git a/mq-client-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java b/mq-client-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java deleted file mode 100644 index 9b87e3c1..00000000 --- a/mq-client-open/src/test/java/com/sohu/tv/mq/util/SerializerTest.java +++ /dev/null @@ -1,194 +0,0 @@ -package com.sohu.tv.mq.util; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -public class SerializerTest { - public static ObjectA generateTestObject() { - List list = new ArrayList(); - ObjectB objectB = null; - for(int i = 1; i <= 3; ++i) { - objectB = new ObjectB(); - objectB.setI(i); - objectB.setI(Integer.valueOf(String.valueOf(i))); - objectB.setL(i); - objectB.setL(Long.valueOf(String.valueOf(i))); - objectB.setS((short)i); - objectB.setS(Short.valueOf(String.valueOf(i))); - objectB.setC('a'); - objectB.setC(Character.valueOf('a')); - objectB.setB((byte)i); - objectB.setB(Byte.valueOf(String.valueOf(i))); - objectB.setDate(new Date()); - objectB.setStr("abc"); - String[] sarray = {"a", "b", "c"}; - objectB.setSarray(sarray); - list.add(objectB); - } - - ObjectA objectA = new ObjectA(); - objectA.setI(8); - objectA.setI(Integer.valueOf(String.valueOf("8"))); - objectA.setL(8); - objectA.setL(Long.valueOf(String.valueOf("8"))); - objectA.setS((short)8); - objectA.setS(Short.valueOf(String.valueOf("8"))); - objectA.setC('a'); - objectA.setC(Character.valueOf('a')); - objectA.setB((byte)8); - objectA.setB(Byte.valueOf(String.valueOf("8"))); - objectA.setDate(new Date()); - objectA.setStr("abc"); - String[] sarray = {"a", "b", "c"}; - objectA.setSarray(sarray); - objectA.setName("name"); - objectA.setObjectB(objectB); - objectA.setBlist(list); - - return objectA; - } - - public static class ObjectP{ - private String name; - public void setName(String name) { - this.name = name; - } - @Override - public String toString() { - return "ObjectP [name=" + name + "]"; - } - } - - public static class ObjectA extends ObjectP{ - private int i; - private Integer I; - private long l; - private Long L; - private short s; - private Short S; - private char c; - private Character C; - private byte b; - private Byte B; - private String str; - private String[] sarray; - private Date date; - private ObjectB objectB; - private List blist; - public void setI(int i) { - this.i = i; - } - public void setI(Integer i) { - I = i; - } - public void setL(long l) { - this.l = l; - } - public void setL(Long l) { - L = l; - } - public void setS(short s) { - this.s = s; - } - public void setS(Short s) { - S = s; - } - public void setC(char c) { - this.c = c; - } - public void setC(Character c) { - C = c; - } - public void setB(byte b) { - this.b = b; - } - public void setB(Byte b) { - B = b; - } - public void setStr(String str) { - this.str = str; - } - public void setSarray(String[] sarray) { - this.sarray = sarray; - } - public void setDate(Date date) { - this.date = date; - } - public void setObjectB(ObjectB objectB) { - this.objectB = objectB; - } - public void setBlist(List blist) { - this.blist = blist; - } - @Override - public String toString() { - return "ObjectA [i=" + i + ", I=" + I + ", l=" + l + ", L=" + L + ", s=" + s + ", S=" + S + ", c=" + c - + ", C=" + C + ", b=" + b + ", B=" + B + ", str=" + str + ", sarray=" + Arrays.toString(sarray) - + ", date=" + date + ", objectB=" + objectB + ", blist=" + blist + ", toString()=" - + super.toString() + "]"; - } - } - - public static class ObjectB{ - private int i; - private Integer I; - private long l; - private Long L; - private short s; - private Short S; - private char c; - private Character C; - private byte b; - private Byte B; - private String str; - private String[] sarray; - private Date date; - public void setI(int i) { - this.i = i; - } - public void setI(Integer i) { - I = i; - } - public void setL(long l) { - this.l = l; - } - public void setL(Long l) { - L = l; - } - public void setS(short s) { - this.s = s; - } - public void setS(Short s) { - S = s; - } - public void setC(char c) { - this.c = c; - } - public void setC(Character c) { - C = c; - } - public void setB(byte b) { - this.b = b; - } - public void setB(Byte b) { - B = b; - } - public void setStr(String str) { - this.str = str; - } - public void setSarray(String[] sarray) { - this.sarray = sarray; - } - public void setDate(Date date) { - this.date = date; - } - @Override - public String toString() { - return "ObjectB [i=" + i + ", I=" + I + ", l=" + l + ", L=" + L + ", s=" + s + ", S=" + S + ", c=" + c - + ", C=" + C + ", b=" + b + ", B=" + B + ", str=" + str + ", sarray=" + Arrays.toString(sarray) - + ", date=" + date + "]"; - } - } -} diff --git a/mq-client-open/src/test/resources/logback.xml b/mq-client-open/src/test/resources/logback.xml index c6649b3e..3e43b492 100644 --- a/mq-client-open/src/test/resources/logback.xml +++ b/mq-client-open/src/test/resources/logback.xml @@ -11,22 +11,6 @@ - - - - - - - - - - - - - - - - diff --git a/mq-cloud-common/pom.xml b/mq-cloud-common/pom.xml index fa4047fa..ec919507 100644 --- a/mq-cloud-common/pom.xml +++ b/mq-cloud-common/pom.xml @@ -6,7 +6,7 @@ com.sohu.tv mq - 1.2.RELEASE + 1.3.RELEASE mq-cloud-common diff --git a/mq-cloud/pom.xml b/mq-cloud/pom.xml index 21142fb8..8d5269c1 100644 --- a/mq-cloud/pom.xml +++ b/mq-cloud/pom.xml @@ -5,7 +5,7 @@ com.sohu.tv mq - 1.2.RELEASE + 1.3.RELEASE mq-cloud diff --git a/mq-cloud/sql/1.2-1.3.sql b/mq-cloud/sql/1.2-1.3.sql new file mode 100644 index 00000000..5f5e8387 --- /dev/null +++ b/mq-cloud/sql/1.2-1.3.sql @@ -0,0 +1,12 @@ +-- ---------------------------- +-- Table structure for `audit_resend_message` +-- ---------------------------- +DROP TABLE IF EXISTS `audit_resend_message`; +CREATE TABLE `audit_resend_message` ( + `aid` int(11) NOT NULL COMMENT '审核id', + `tid` int(11) NOT NULL COMMENT 'topic id', + `msgId` char(32) NOT NULL COMMENT 'broker offset msg id', + `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '申请类型:0:未处理,1:发送成功,2:发送失败', + `times` int(11) NOT NULL DEFAULT '0' COMMENT '发送次数', + `send_time` datetime COMMENT '发送时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='消息重发审核表'; \ No newline at end of file diff --git a/mq-cloud/sql/init.sql b/mq-cloud/sql/init.sql index 9aedd64f..9a50b8fd 100644 --- a/mq-cloud/sql/init.sql +++ b/mq-cloud/sql/init.sql @@ -139,6 +139,19 @@ CREATE TABLE `audit_user_consumer_delete` ( `topic` varchar(64) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='审核用户与消费者组关系删除相关表'; +-- ---------------------------- +-- Table structure for `audit_resend_message` +-- ---------------------------- +DROP TABLE IF EXISTS `audit_resend_message`; +CREATE TABLE `audit_resend_message` ( + `aid` int(11) NOT NULL COMMENT '审核id', + `tid` int(11) NOT NULL COMMENT 'topic id', + `msgId` char(32) NOT NULL COMMENT 'broker offset msg id', + `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '申请类型:0:未处理,1:发送成功,2:发送失败', + `times` int(11) NOT NULL DEFAULT '0' COMMENT '发送次数', + `send_time` datetime COMMENT '发送时间' +) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='消息重发审核表'; + -- ---------------------------- -- Table structure for `broker_traffic` -- ---------------------------- diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AlarmConfig.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AlarmConfig.java index 58e84a6a..c8bc436b 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AlarmConfig.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AlarmConfig.java @@ -9,7 +9,7 @@ */ public class AlarmConfig { // 报警 - public static int ALERT = 0; + public static int ALERT = 0; // consumer名称,为空行为默认配置 private String consumer; @@ -138,5 +138,5 @@ public String spliceWarnFrequency() { */ private String showValue(Long arg) { return arg == null ? "" : String.valueOf(arg); - } + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Audit.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Audit.java index 8eba4dd7..c6645717 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Audit.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Audit.java @@ -159,6 +159,7 @@ public enum TypeEnum { BECOME_ADMIN(9, "成为管理员"), DELETE_USERPRODUCER(10, "删除生产者"), DELETE_USERCONSUMER(11, "删除消费用户"), + RESEND_MESSAGE(12, "重发消息"), ; private Integer type; diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AuditResendMessage.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AuditResendMessage.java new file mode 100644 index 00000000..5bd558d9 --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/AuditResendMessage.java @@ -0,0 +1,113 @@ +package com.sohu.tv.mq.cloud.bo; + +import java.util.Date; + +import com.sohu.tv.mq.cloud.util.DateUtil; + +/** + * 消息重发 + * + * @author yongfeigao + * @date 2018年12月6日 + */ +public class AuditResendMessage { + private long aid; + private long tid; + // broker offset msg id + private String msgId; + // 申请类型:0:未处理,1:发送成功,2:发送失败 + private int status; + // 发送次数 + private int times; + // 发送时间 + private Date sendTime; + + public long getAid() { + return aid; + } + + public void setAid(long aid) { + this.aid = aid; + } + + public long getTid() { + return tid; + } + + public void setTid(long tid) { + this.tid = tid; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public int getStatus() { + return status; + } + + public String getStatusDesc() { + return StatusEnum.getEnumByStatus(status).getDesc(); + } + + public void setStatus(int status) { + this.status = status; + } + + public int getTimes() { + return times; + } + + public void setTimes(int times) { + this.times = times; + } + + public Date getSendTime() { + return sendTime; + } + + public String getSendTimeFormat() { + return DateUtil.getFormat(DateUtil.YMD_BLANK_HMS_COLON).format(sendTime); + } + + public void setSendTime(Date sendTime) { + this.sendTime = sendTime; + } + + // 审批状态 + public enum StatusEnum { + + INIT(0, "未处理"), + SUCCESS(1, "成功"), + FAILED(2, "失败"); + + private int status; + private String desc; + + StatusEnum(int status, String desc) { + this.status = status; + this.desc = desc; + } + + public int getStatus() { + return status; + } + + public String getDesc() { + return desc; + } + + public static StatusEnum getEnumByStatus(int status) { + for (StatusEnum statusEnum : StatusEnum.values()) { + if (statusEnum.status == status) { + return statusEnum; + } + } + return StatusEnum.INIT; + } + } +} diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Broker.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Broker.java index b91d385d..9a48cf6c 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Broker.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/Broker.java @@ -101,6 +101,6 @@ public String getCheckTimeFormat() { @Override public String toString() { return "Broker [cid=" + cid + ", addr=" + addr + ", brokerID=" + brokerID + ", brokerName=" + brokerName - + " createTime=" + createTime + ", checkStatus=" + checkStatus + ", checkTime=" + checkTime + "]"; + + " createTime=" + createTime + ", checkStatus=" + checkStatus + ", checkTime=" + checkTime + "]"; } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/DecodedMessage.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/DecodedMessage.java index e2888417..ca5b09b5 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/DecodedMessage.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/DecodedMessage.java @@ -2,7 +2,9 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.ByteBuffer; +import org.apache.rocketmq.common.message.MessageDecoder; import org.apache.rocketmq.common.message.MessageExt; import com.alibaba.fastjson.JSONObject; @@ -48,4 +50,19 @@ private String address(SocketAddress addr) { sb.append(inetSocketAddress.getPort()); return sb.toString(); } + + /** + * 获取offsetMsgId + * @return + */ + public String getOffsetMsgId() { + ByteBuffer byteBufferMsgId = ByteBuffer.allocate(MessageDecoder.MSG_ID_LENGTH); + return MessageDecoder.createMessageId(byteBufferMsgId, getStoreHostBytes(), getCommitLogOffset()); + } + + @Override + public String toString() { + return super.toString(); + } + } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MQOffset.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MQOffset.java index 49e48124..0865f8ac 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MQOffset.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MQOffset.java @@ -37,7 +37,7 @@ public void setOffset(long offset) { this.offset = offset; } public boolean hasMessage() { - return offset <= maxOffset && maxOffset > 0; + return offset < maxOffset && maxOffset > 0; } @Override public String toString() { diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MessageQueryCondition.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MessageQueryCondition.java index da68bd02..70586579 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MessageQueryCondition.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/MessageQueryCondition.java @@ -16,7 +16,6 @@ public class MessageQueryCondition { public static final int MESSAGE_SIZE = 30; public static final int MAX_MESSAGE_SIZE = 5000; - public static final int TIME_SPAN = 60 * 1000; private List mqOffsetList; // topic private String topic; @@ -25,8 +24,6 @@ public class MessageQueryCondition { // 关键字 private String key; // 查询时间 - private long time; - // 查询时间 private long start; // 查询时间 private long end; @@ -40,6 +37,8 @@ public class MessageQueryCondition { private long curSize; // 查询的次数 private long times; + // 最大偏移量 + private long maxOffset; public List getMqOffsetList() { return mqOffsetList; @@ -165,14 +164,6 @@ public String serialize() throws IOException { return CompressUtil.compress(JSON.toJSONString(this)); } - public long getTime() { - return time; - } - - public void setTime(long time) { - this.time = time; - } - public void setStart(long start) { this.start = start; } @@ -212,4 +203,17 @@ public void reset() { setPrevSize(0); setKey(null); } + + public long getMaxOffset() { + return maxOffset; + } + + public void setMaxOffset(long maxOffset) { + this.maxOffset = maxOffset; + } + + public long getMinOffset() { + long minOffset = maxOffset - 100; + return minOffset < 0 ? 0 : minOffset; + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/NameServer.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/NameServer.java index 359671cd..75f3e37d 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/NameServer.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/NameServer.java @@ -12,7 +12,7 @@ public class NameServer { // cluster id private int cid; // ip:port - private String addr; + private String addr; private Date createTime; // 检测状态 @@ -61,6 +61,6 @@ public void setCheckTime(Date checkTime) { } public String getCheckStatusDesc() { - return CheckStatusEnum.getCheckStatusEnumByStatus(getCheckStatus()).getDesc(); + return CheckStatusEnum.getCheckStatusEnumByStatus(getCheckStatus()).getDesc(); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/ServerInfoExt.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/ServerInfoExt.java index b2153e8f..3a178984 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/ServerInfoExt.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/bo/ServerInfoExt.java @@ -98,6 +98,6 @@ public String toString() { + ", getNout()=" + getNout() + ", getDread()=" + getDread() + ", getDwrite()=" + getDwrite() + ", getDiops()=" + getDiops() + ", getDbusy()=" + getDbusy() + ", getDspace()=" + getDspace() + ", getMswapFree()=" + getMswapFree() + ", getNinExt()=" + getNinExt() + ", getNoutExt()=" - + getNoutExt() + ", getdExt()=" + getdExt() + ", machineType=" + machineType + "]"; + + getNoutExt() + ", getdExt()=" + getdExt() + ", machineType=" + machineType + "]"; } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/conf/CommonConfiguration.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/conf/CommonConfiguration.java index 8a51c6b6..55d57ab5 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/conf/CommonConfiguration.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/conf/CommonConfiguration.java @@ -214,7 +214,7 @@ public void destroy() { @Bean public CipherHelper cipherHelper() throws UnsupportedEncodingException { CipherHelper cipherHelper = new CipherHelper(mqCloudConfigHelper.getCiperKey()); - return cipherHelper; + return cipherHelper; } /** diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AlarmConfigDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AlarmConfigDao.java index 1e344a12..a08afa53 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AlarmConfigDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AlarmConfigDao.java @@ -43,6 +43,6 @@ public interface AlarmConfigDao { + "warn_unit_time,warn_unit_count,ignore_warn)" + "values(#{alarmConfig.consumer},#{alarmConfig.accumulateTime},#{alarmConfig.accumulateCount}," + "#{alarmConfig.blockTime},#{alarmConfig.consumerFailCount}," - + "#{alarmConfig.warnUnitTime},#{alarmConfig.warnUnitCount},#{alarmConfig.ignoreWarn})") + + "#{alarmConfig.warnUnitTime},#{alarmConfig.warnUnitCount},#{alarmConfig.ignoreWarn})") public Integer insert(@Param("alarmConfig") AlarmConfig alarmConfig); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AuditResendMessageDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AuditResendMessageDao.java new file mode 100644 index 00000000..9ec59e77 --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/AuditResendMessageDao.java @@ -0,0 +1,58 @@ +package com.sohu.tv.mq.cloud.dao; + +import java.util.List; + +import org.apache.ibatis.annotations.Insert; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; + +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; +/** + * 消息重发 + * + * @author yongfeigao + * @date 2018年12月7日 + */ +public interface AuditResendMessageDao { + /** + * 批量保存 + * + * @param id + * @return + */ + @Insert("") + public Integer insert(@Param("msgList")List auditResendMessageList); + + /** + * 查询 + * + * @param id + * @return + */ + @Select("select * from audit_resend_message where aid=#{aid}") + public List select(@Param("aid") long aid); + + /** + * 查询 + * + * @param id + * @return + */ + @Select("select * from audit_resend_message where aid=#{aid} and msgId = #{msgId}") + public AuditResendMessage selectOne(@Param("aid") long aid, @Param("msgId") String msgId); + + /** + * 更新 + * + * @param id + * @return + */ + @Update("update audit_resend_message set status=#{status}, times = times + 1, send_time = now() " + + "where aid = #{aid} and msgId = #{msgId}") + public Integer update(@Param("aid") long aid, @Param("msgId") String msgId, @Param("status") int status); +} diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ConsumerDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ConsumerDao.java index 9976da95..58849cf5 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ConsumerDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ConsumerDao.java @@ -39,14 +39,14 @@ public interface ConsumerDao { /** * 查询记录 * @param consumer - */ + */ @Select("select * from consumer") public List selectAll(); /** * 查询记录 * @param consumer - */ + */ @Select("select * from consumer where id = #{id}") public Consumer selectById(@Param("id") long id); diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/NameServerDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/NameServerDao.java index 12c8082d..233dd377 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/NameServerDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/NameServerDao.java @@ -5,7 +5,7 @@ import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; import com.sohu.tv.mq.cloud.bo.NameServer; @@ -41,7 +41,7 @@ public interface NameServerDao { public Integer update(@Param("cid") int cid, @Param("addr") String addr, @Param("checkStatus") int checkStatus); /** - * 删除 + * 删除 * * @param notice */ diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ServerStatusDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ServerStatusDao.java index 48e74cb9..7734d9ca 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ServerStatusDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/ServerStatusDao.java @@ -50,7 +50,7 @@ public interface ServerStatusDao { * 保存服务器发行版信息 * @param ip * @param dist from /etc/issue - */ + */ @Insert("") public void saveServerInfo(@Param("ip") String ip, @Param("dist") String dist, @Param("type") int type); @@ -129,5 +129,5 @@ public List queryServerStat(@Param("ip") String ip, * @return */ @Update("update server set machine_type=#{type} where ip = #{ip}") - public Integer updateServer(@Param("ip") String ip, @Param("type") int type); + public Integer updateServer(@Param("ip") String ip, @Param("type") int type); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserConsumerDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserConsumerDao.java index 30cb93ca..c128808a 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserConsumerDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserConsumerDao.java @@ -88,6 +88,6 @@ public interface UserConsumerDao { * @param cid * @return */ - @Select("select u.* from user_consumer uc, user u where uc.uid = u.id and uc.tid = #{tid} and uc.consumer_id = #{cid}") - public List selectUserByConsumer(@Param("tid") long tid, @Param("cid") long cid); + @Select("select u.* from user_consumer uc, user u where uc.uid = u.id and uc.tid = #{tid} and uc.consumer_id = #{cid}") + public List selectUserByConsumer(@Param("tid") long tid, @Param("cid") long cid); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserDao.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserDao.java index e1acfd85..6706f664 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserDao.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/dao/UserDao.java @@ -101,7 +101,7 @@ public interface UserDao { @Select("") - public List selectByIdList(@Param("idList") Collection idList); + public List selectByIdList(@Param("idList") Collection idList); /** * 密码重置 @@ -110,5 +110,5 @@ public interface UserDao { * @param password */ @Update("update user set password=#{password} where id = #{uid}") - public Integer resetPassword(@Param("uid") long uid, @Param("password") String password); + public Integer resetPassword(@Param("uid") long uid, @Param("password") String password); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/MQAdminTemplate.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/MQAdminTemplate.java index 2fca5a69..43523673 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/MQAdminTemplate.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/MQAdminTemplate.java @@ -40,8 +40,6 @@ public T execute(MQAdminCallback callback) { T t = callback.callback(mqAdmin); return t; } catch (Exception e) { - // 异常记录日志 - logger.error("cluster:{} error", callback.mqCluster(), e); try { // 触发异常情况回调 return callback.exception(e); diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/SohuMQAdmin.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/SohuMQAdmin.java index 7eac2ef4..6f4732fa 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/SohuMQAdmin.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/mq/SohuMQAdmin.java @@ -2,8 +2,11 @@ import java.lang.reflect.Field; +import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.impl.factory.MQClientInstance; +import org.apache.rocketmq.client.producer.SendResult; +import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.protocol.body.TopicList; import org.apache.rocketmq.remoting.RPCHook; import org.apache.rocketmq.remoting.exception.RemotingException; @@ -44,6 +47,7 @@ public SohuMQAdmin(String adminExtGroup) { /** * 获取系统内置topic + * * @param timeoutMillis * @return * @throws NoSuchFieldException @@ -57,6 +61,39 @@ public SohuMQAdmin(String adminExtGroup) { public TopicList getSystemTopicList(long timeoutMillis) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, RemotingException, MQClientException, InterruptedException { + return getMQClientInstance().getMQClientAPIImpl().getSystemTopicList(timeoutMillis); + } + + /** + * 发送消息 + * + * @param msg + * @return + * @throws NoSuchFieldException + * @throws SecurityException + * @throws IllegalArgumentException + * @throws IllegalAccessException + * @throws MQClientException + * @throws RemotingException + * @throws MQBrokerException + * @throws InterruptedException + */ + public SendResult sendMessage(Message msg) throws NoSuchFieldException, SecurityException, IllegalArgumentException, + IllegalAccessException, MQClientException, RemotingException, MQBrokerException, InterruptedException { + return getMQClientInstance().getDefaultMQProducer().send(msg); + } + + /** + * 获取 @MQClientInstance + * + * @return + * @throws NoSuchFieldException + * @throws SecurityException + * @throws IllegalArgumentException + * @throws IllegalAccessException + */ + public MQClientInstance getMQClientInstance() + throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { // 反射获取defaultMQAdminExtImpl实例 Field defaultMQAdminExtImplField = DefaultMQAdminExt.class.getDeclaredField("defaultMQAdminExtImpl"); defaultMQAdminExtImplField.setAccessible(true); @@ -65,6 +102,6 @@ public TopicList getSystemTopicList(long timeoutMillis) Field field = DefaultMQAdminExtImpl.class.getDeclaredField("mqClientInstance"); field.setAccessible(true); MQClientInstance mqClientInstance = (MQClientInstance) field.get(defaultMQAdminExtImpl); - return mqClientInstance.getMQClientAPIImpl().getSystemTopicList(timeoutMillis); + return mqClientInstance; } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigBridingService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigBridingService.java index d70a6a7f..089cdb85 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigBridingService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigBridingService.java @@ -153,7 +153,7 @@ private Long getDefaultConfig(ConsumerWarnEnum type) { */ public void setConfigTable(List alarmConfigList) { for (AlarmConfig alarmConfig : alarmConfigList) { - CONFIG_TABLE.put(alarmConfig.getConsumer(), alarmConfig); + CONFIG_TABLE.put(alarmConfig.getConsumer(), alarmConfig); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigService.java index d3b8878f..5960a355 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AlarmConfigService.java @@ -87,6 +87,6 @@ public Result save(AlarmConfig alarmConfig) { logger.error("save err, alarmConfig:{}", alarmConfig, e); return Result.getDBErrorResult(e); } - return Result.getResult(count); + return Result.getResult(count); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditResendMessageService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditResendMessageService.java new file mode 100644 index 00000000..5d7979fa --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditResendMessageService.java @@ -0,0 +1,95 @@ +package com.sohu.tv.mq.cloud.service; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; +import com.sohu.tv.mq.cloud.dao.AuditResendMessageDao; +import com.sohu.tv.mq.cloud.util.Result; +/** + * 消息重发服务 + * + * @author yongfeigao + * @date 2018年12月6日 + */ +@Service +public class AuditResendMessageService { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private AuditResendMessageDao auditResendMessageDao; + + /** + * 查询 + * @param aid + * @return + */ + public Result> query(long aid) { + List list = null; + try { + list = auditResendMessageDao.select(aid); + } catch (Exception e) { + logger.error("query err, aid:{}", aid, e); + return Result.getDBErrorResult(e); + } + return Result.getResult(list); + } + + /** + * 查询一条 + * @param aid + * @param msgId + * @return + */ + public Result queryOne(long aid, String msgId) { + AuditResendMessage auditResendMessage = null; + try { + auditResendMessage = auditResendMessageDao.selectOne(aid, msgId); + } catch (Exception e) { + logger.error("queryOne err, aid:{}, msgId:{}", aid, msgId, e); + return Result.getDBErrorResult(e); + } + return Result.getResult(auditResendMessage); + } + + /** + * 更新 + * @param aid + * @param msgId + * @param status + * @return + */ + public Result update(long aid, String msgId, int status) { + Integer updatedRows = null; + try { + updatedRows = auditResendMessageDao.update(aid, msgId, status); + } catch (Exception e) { + logger.error("update err, aid:{} msgId:{} status:{}", aid, msgId, status, e); + return Result.getDBErrorResult(e); + } + return Result.getResult(updatedRows); + } + + /** + * 保存 + * @param auditResendMessageList + * @return + */ + @Transactional + public Result save(List auditResendMessageList) { + Integer updatedRows = null; + try { + updatedRows = auditResendMessageDao.insert(auditResendMessageList); + } catch (Exception e) { + logger.error("insert err, size:{}", auditResendMessageList.size(), e); + throw e; + } + return Result.getResult(updatedRows); + } +} diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditService.java index 56625b98..97593364 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/AuditService.java @@ -16,6 +16,7 @@ import com.sohu.tv.mq.cloud.bo.AuditAssociateConsumer; import com.sohu.tv.mq.cloud.bo.AuditAssociateProducer; import com.sohu.tv.mq.cloud.bo.AuditConsumer; +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; import com.sohu.tv.mq.cloud.bo.AuditResetOffset; import com.sohu.tv.mq.cloud.bo.AuditTopic; import com.sohu.tv.mq.cloud.bo.AuditTopicUpdate; @@ -73,6 +74,9 @@ public class AuditService { @Autowired private AuditUserConsumerDeleteDao auditUserConsumerDeleteDao; + + @Autowired + private AuditResendMessageService auditResendMessageService; /** * 查询列表 @@ -405,4 +409,30 @@ public Result saveAuditAndUserConsumerDelete(Audit audit, long ucid, String c } return Result.getResult(audit); } + + /** + * 保存审核以及重发消息信息 + * @param audit + * @param auditResendMessageList + * @return + */ + @Transactional + public Result saveAuditAndAuditResendMessage(Audit audit, List auditResendMessageList){ + Long count = null; + try { + count = auditDao.insert(audit); + //如果保存成功,保存auditResendMessageList + if(count != null && count > 0) { + for(AuditResendMessage msg : auditResendMessageList) { + msg.setAid(audit.getId()); + } + auditResendMessageService.save(auditResendMessageList); + } + } catch (Exception e) { + logger.error("insert err, audit:{}", audit, e); + TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); + return Result.getDBErrorResult(e); + } + return Result.getResult(audit); + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerRetryTrafficService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerRetryTrafficService.java index ff3f2717..18bf2497 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerRetryTrafficService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerRetryTrafficService.java @@ -67,7 +67,7 @@ public void invoke(MQAdminExt mqAdmin) throws Exception { String statKey = topicConsumer.getTopic() + "@" + topicConsumer.getConsumer(); TopicHourTraffic topicTraffic = new TopicHourTraffic(); fetchTraffic(mqAdmin, topicConsumer.getTopic(), statKey, topicTraffic); - if(topicTraffic.getCount() > 0) { + if(topicTraffic.getCount() > 0) { Result> userListResult = userConsumerService.queryUserByConsumer( topicConsumer.getTid(), topicConsumer.getCid()); String email = null; @@ -81,7 +81,7 @@ public void invoke(MQAdminExt mqAdmin) throws Exception { email = sb.toString(); } long consumerFailCount = alarmConfigBridingService.getConsumerFailCount(topicConsumer.getConsumer()); - if (consumerFailCount >= 0 && consumerFailCount < topicTraffic.getCount()) { + if (consumerFailCount >= 0 && consumerFailCount < topicTraffic.getCount()) { // 验证报警频率 if (alarmConfigBridingService.needWarn("consumerFail", topicConsumer.getTopic(), topicConsumer.getConsumer())) { alertService.sendWanMail(email, "消费失败", diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerService.java index fc0acad0..bb373a98 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ConsumerService.java @@ -150,7 +150,7 @@ public Result> queryByTid(long tid) { return queryByTidList(tidList); } - /** + /** * 查询全量consumer * * @param Result> @@ -166,7 +166,7 @@ public Result> queryAll() { return Result.getResult(consumerList); } - /** + /** * 按照tid列表查询consumer * * @param Result> @@ -403,6 +403,7 @@ public Result callback(MQAdminExt mqAdmin) throws Exception return Result.getResult(consumerConnection); } public Result exception(Exception e) throws Exception { + logger.warn("cluster:{} consumerGroup:{} error:{}", mqCluster, consumerGroup, e.getMessage()); return Result.getDBErrorResult(e); } public Cluster mqCluster() { diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MQDeployer.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MQDeployer.java index 11af4a1e..6e289c1f 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MQDeployer.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MQDeployer.java @@ -349,6 +349,7 @@ private Result fetchTopicConfig(BrokerParam brokerParam){ if (brokerParam.isSlave()) { return Result.getResult(Status.NO_RESULT); } + Cluster cluster = clusterService.getMQClusterById(brokerParam.getMqClusterId()); // 获取topic配置 return mqAdminTemplate.execute(new MQAdminCallback>() { public Result callback(MQAdminExt mqAdmin) throws Exception { @@ -379,11 +380,12 @@ public Result callback(MQAdminExt mqAdmin) throws Exception { } public Result exception(Exception e) throws Exception { + logger.error("cluster:{} error", cluster, e); return Result.getDBErrorResult(e); } public Cluster mqCluster() { - return clusterService.getMQClusterById(brokerParam.getMqClusterId()); + return cluster; } }); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MessageService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MessageService.java index c253b6dd..155fe9b8 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MessageService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/MessageService.java @@ -9,7 +9,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.Set; - + import org.apache.rocketmq.client.QueryResult; import org.apache.rocketmq.client.consumer.DefaultMQPullConsumer; import org.apache.rocketmq.client.consumer.MQPullConsumer; @@ -17,9 +17,12 @@ import org.apache.rocketmq.client.consumer.PullStatus; import org.apache.rocketmq.client.exception.MQBrokerException; import org.apache.rocketmq.client.exception.MQClientException; +import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.common.MixAll; +import org.apache.rocketmq.common.TopicConfig; import org.apache.rocketmq.common.admin.ConsumeStats; import org.apache.rocketmq.common.admin.OffsetWrapper; +import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; import org.apache.rocketmq.common.message.MessageQueue; import org.apache.rocketmq.common.protocol.ResponseCode; @@ -28,6 +31,8 @@ import org.apache.rocketmq.common.protocol.body.ConsumerConnection; import org.apache.rocketmq.common.protocol.heartbeat.SubscriptionData; import org.apache.rocketmq.common.protocol.route.BrokerData; +import org.apache.rocketmq.common.protocol.route.QueueData; +import org.apache.rocketmq.common.protocol.route.TopicRouteData; import org.apache.rocketmq.remoting.common.RemotingHelper; import org.apache.rocketmq.tools.admin.MQAdminExt; import org.apache.rocketmq.tools.admin.api.MessageTrack; @@ -37,6 +42,7 @@ import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.util.HtmlUtils; import com.alibaba.fastjson.JSON; import com.sohu.tv.mq.cloud.bo.Cluster; @@ -49,6 +55,7 @@ import com.sohu.tv.mq.cloud.mq.DefaultCallback; import com.sohu.tv.mq.cloud.mq.MQAdminCallback; import com.sohu.tv.mq.cloud.mq.MQAdminTemplate; +import com.sohu.tv.mq.cloud.mq.SohuMQAdmin; import com.sohu.tv.mq.cloud.util.MQCloudConfigHelper; import com.sohu.tv.mq.cloud.util.MessageTypeLoader; import com.sohu.tv.mq.cloud.util.Result; @@ -88,6 +95,9 @@ public class MessageService { private MessageSerializer messageSerializer = new DefaultMessageSerializer(); + @Autowired + private TopicService topicService; + /** * 根据key查询消息 * @param cluster @@ -130,7 +140,13 @@ public int compare(DecodedMessage o1, DecodedMessage o2) { return Result.getResult(messageList); } public Result> exception(Exception e) throws Exception { - logger.error("queryMessage cluster:{} topic:{} key:{}", cluster, topic, key, e); + logger.error("queryMessage cluster:{} topic:{} key:{}, err:{}", cluster, topic, key, e.getMessage()); + // 此异常代表查无数据,不进行异常提示 + if(e instanceof MQClientException) { + if(((MQClientException) e).getResponseCode() == ResponseCode.NO_MESSAGE) { + return Result.getOKResult(); + } + } return Result.getWebErrorResult(e); } public Cluster mqCluster() { @@ -179,7 +195,7 @@ public Cluster mqCluster() { * @return * @throws MQClientException */ - public Result queryMessage(MessageQueryCondition messageQueryCondition) { + public Result queryMessage(MessageQueryCondition messageQueryCondition, boolean offsetSearch) { List messageList = null; MQPullConsumer consumer = null; try { @@ -188,7 +204,7 @@ public Result queryMessage(MessageQueryCondition messageQueryCondit consumer = getConsumer(cluster); // 初始化参数 if (messageQueryCondition.getMqOffsetList() == null) { - List mqOffsetList = getMQOffsetList(consumer, messageQueryCondition); + List mqOffsetList = getMQOffsetList(cluster, consumer, messageQueryCondition, true, offsetSearch); if (mqOffsetList == null) { return Result.getResult(Status.NO_RESULT); } @@ -205,7 +221,7 @@ public Result queryMessage(MessageQueryCondition messageQueryCondit if (!mqOffset.hasMessage()) { continue; } - fetchMessage(consumer, messageQueryCondition, mqOffset, messageList); + fetchMessage(consumer, messageQueryCondition, mqOffset, messageList, offsetSearch); if (!messageQueryCondition.needSearch()) { break; } @@ -242,7 +258,7 @@ public Result queryMessage(MessageQueryCondition messageQueryCondit * @throws Exception */ private void fetchMessage(MQPullConsumer consumer, MessageQueryCondition messageQueryCondition, MQOffset mqOffset, - List messageList) throws Exception { + List messageList, boolean offsetSearch) throws Exception { while (mqOffset.hasMessage() && messageQueryCondition.needSearch()) { try { // 拉取消息 @@ -256,7 +272,7 @@ private void fetchMessage(MQPullConsumer consumer, MessageQueryCondition message + pullResult.getMsgFoundList().size()); for (MessageExt msg : pullResult.getMsgFoundList()) { // 过滤不在时间范围的消息 - if (!messageQueryCondition.valid(msg.getStoreTimestamp())) { + if (!offsetSearch && !messageQueryCondition.valid(msg.getStoreTimestamp())) { continue; } byte[] bytes = msg.getBody(); @@ -315,7 +331,7 @@ private DecodedMessage toDecodedMessage(MessageExt msg) { if (decodedBody instanceof byte[]) { m.setDecodedBody(new String((byte[]) decodedBody)); } else if (decodedBody instanceof String) { - m.setDecodedBody((String) decodedBody); + m.setDecodedBody(HtmlUtils.htmlEscape((String)decodedBody)); } else if (decodedBody instanceof Map && mqCloudConfigHelper.getMapWithByteList() != null && !mqCloudConfigHelper.getMapWithByteList().contains(msg.getTopic())) { @@ -335,7 +351,8 @@ private DecodedMessage toDecodedMessage(MessageExt msg) { * @return * @throws MQClientException */ - private List getMQOffsetList(MQPullConsumer consumer, MessageQueryCondition messageQueryCondition) { + private List getMQOffsetList(Cluster cluster, MQPullConsumer consumer, + MessageQueryCondition messageQueryCondition, boolean retryWithErr, boolean offsetSearch) { List offsetList = null; try { Set mqs = consumer.fetchSubscribeMessageQueues(messageQueryCondition.getTopic()); @@ -344,8 +361,21 @@ private List getMQOffsetList(MQPullConsumer consumer, MessageQueryCond long minOffset = 0; long maxOffset = 0; try { - minOffset = consumer.searchOffset(mq, messageQueryCondition.getStart()); - maxOffset = consumer.searchOffset(mq, messageQueryCondition.getEnd()); + if(!offsetSearch) { + minOffset = consumer.searchOffset(mq, messageQueryCondition.getStart()); + maxOffset = consumer.searchOffset(mq, messageQueryCondition.getEnd()); + } else { + maxOffset = messageQueryCondition.getEnd(); + minOffset = messageQueryCondition.getStart(); + long tmpMaxOffset = consumer.maxOffset(mq); + if(maxOffset > tmpMaxOffset) { + maxOffset = tmpMaxOffset; + } + long tmpMinOffset = consumer.minOffset(mq); + if(minOffset < tmpMinOffset) { + minOffset = tmpMinOffset; + } + } } catch (Exception e) { logger.warn("mq:{} start:{} end:{} offset err:{}", mq, messageQueryCondition.getStart(), messageQueryCondition.getEnd(), @@ -360,12 +390,34 @@ private List getMQOffsetList(MQPullConsumer consumer, MessageQueryCond mqOffset.setOffset(minOffset); offsetList.add(mqOffset); } - } catch (MQClientException e) { + } catch (Exception e) { + if(retryWithErr + && e instanceof MQClientException + && messageQueryCondition.getTopic().startsWith(MixAll.DLQ_GROUP_TOPIC_PREFIX) + && e.getMessage().contains("Can not find Message Queue for this topic")) { + TopicRouteData topicRouteData = topicService.route(cluster, messageQueryCondition.getTopic()); + if(topicRouteData != null) { + List queueDatas = topicRouteData.getQueueDatas(); + QueueData queueData = queueDatas.get(0); + TopicConfig topicConfig = new TopicConfig(); + topicConfig.setTopicName(messageQueryCondition.getTopic()); + topicConfig.setWriteQueueNums(queueData.getWriteQueueNums()); + topicConfig.setReadQueueNums(queueData.getReadQueueNums()); + topicConfig.setTopicSysFlag(queueData.getTopicSynFlag()); + Result result = topicService.createAndUpdateTopicOnCluster(cluster, topicConfig); + if(result.isNotOK()) { + logger.warn("change topic:{} perm failed, {}", topicConfig.getTopicName(), result); + } else { + // 尝试一次 + return getMQOffsetList(cluster, consumer, messageQueryCondition, false, offsetSearch); + } + } + } logger.error("getMQOffsetList", e); } return offsetList; } - + /** * 获取消费者 * @@ -442,6 +494,7 @@ public Result callback(MQAdminExt mqAdmin) throws Exception { } public Result exception(Exception e) throws Exception { + logger.error("cluster:{} topic:{} error", mqCluster, mp.getTopic(), e); return Result.getWebErrorResult(e); } @@ -616,4 +669,32 @@ private boolean consumed(Map consumerOffsetMap, MessageParam } return false; } + + /** + * 重发消息 + * @param cluster + * @param topic + * @param msgId + * @return Result + */ + public Result resend(Cluster cluster, String topic, String msgId) { + return mqAdminTemplate.execute(new MQAdminCallback>() { + public Result callback(MQAdminExt mqAdmin) throws Exception { + MessageExt messageExt = mqAdmin.viewMessage(topic, msgId); + SohuMQAdmin sohuMQAdmin = (SohuMQAdmin) mqAdmin; + Message message = new Message(topic, messageExt.getTags(), messageExt.getKeys(), messageExt.getBody()); + SendResult sr = sohuMQAdmin.sendMessage(message); + return Result.getResult(sr); + } + + public Result exception(Exception e) throws Exception { + logger.error("cluster:{} topic:{} msgId:{}, send msg err", cluster, topic, msgId, e); + return Result.getDBErrorResult(e); + } + + public Cluster mqCluster() { + return cluster; + } + }); + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ServerDataService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ServerDataService.java index e2d13b6b..7311bdda 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ServerDataService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/ServerDataService.java @@ -7,7 +7,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; +import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.interceptor.TransactionAspectSupport; @@ -204,5 +204,5 @@ public Result updateServer(String ip, int type) { return Result.getDBErrorResult(e); } return Result.getResult(rows); - } + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/TopicService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/TopicService.java index 41bda1db..4615f3b9 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/TopicService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/TopicService.java @@ -81,13 +81,24 @@ public class TopicService { * @return TopicRouteData */ public TopicRouteData route(Topic topic) { + return route(clusterService.getMQClusterById(topic.getClusterId()), topic.getName()); + } + + /** + * 获取topic路由数据 + * + * @param mqCluster + * @param topic + * @return TopicRouteData + */ + public TopicRouteData route(Cluster cluster, String topic) { return mqAdminTemplate.execute(new DefaultCallback() { public TopicRouteData callback(MQAdminExt mqAdmin) throws Exception { - return route(mqAdmin, topic.getName()); + return route(mqAdmin, topic); } public Cluster mqCluster() { - return clusterService.getMQClusterById(topic.getClusterId()); + return cluster; } }); } @@ -264,13 +275,39 @@ public Integer save(Topic topic) { * @param topic */ public TopicStatsTable stats(Topic topic) { - return mqAdminTemplate.execute(new DefaultCallback() { + return stats(topic.getClusterId(), topic.getName()); + } + + /** + * 获取topic各个队列状态数据 + * + * @param mqCluster + * @param topic + */ + public TopicStatsTable stats(long clusterId, String topic) { + return stats(clusterService.getMQClusterById(clusterId), topic); + } + + /** + * 获取topic各个队列状态数据 + * + * @param mqCluster + * @param topic + */ + public TopicStatsTable stats(Cluster cluster, String topic) { + return mqAdminTemplate.execute(new MQAdminCallback() { public TopicStatsTable callback(MQAdminExt mqAdmin) throws Exception { - return mqAdmin.examineTopicStats(topic.getName()); + return mqAdmin.examineTopicStats(topic); } public Cluster mqCluster() { - return clusterService.getMQClusterById(topic.getClusterId()); + return cluster; + } + + @Override + public TopicStatsTable exception(Exception e) throws Exception { + logger.warn("cluster:{}, topic:{}, err:{}", cluster, topic, e.getMessage()); + return null; } }); } @@ -335,27 +372,36 @@ public Result createTopic(Cluster mqCluster, Audit audit, AuditTopic auditTop * @param auditTopic */ public Result createAndUpdateTopicOnCluster(Cluster mqCluster, AuditTopic auditTopic) { + TopicConfig topicConfig = new TopicConfig(); + topicConfig.setReadQueueNums(auditTopic.getQueueNum()); + topicConfig.setWriteQueueNums(auditTopic.getQueueNum()); + topicConfig.setTopicName(auditTopic.getName()); + if(auditTopic.getOrdered() == AuditTopic.HAS_ORDER) { + topicConfig.setOrder(true); + } + return createAndUpdateTopicOnCluster(mqCluster, topicConfig); + } + + /** + * 创建topic + * @param mqCluster + * @param auditTopic + */ + public Result createAndUpdateTopicOnCluster(Cluster mqCluster, TopicConfig topicConfig) { return mqAdminTemplate.execute(new MQAdminCallback>() { public Result callback(MQAdminExt mqAdmin) throws Exception { - TopicConfig topicConfig = new TopicConfig(); - topicConfig.setReadQueueNums(auditTopic.getQueueNum()); - topicConfig.setWriteQueueNums(auditTopic.getQueueNum()); - topicConfig.setTopicName(auditTopic.getName()); - if(auditTopic.getOrdered() == AuditTopic.HAS_ORDER) { - topicConfig.setOrder(true); - } long start = System.currentTimeMillis(); Set masterSet = CommandUtil.fetchMasterAddrByClusterName(mqAdmin, mqCluster.getName()); for (String addr : masterSet) { mqAdmin.createAndUpdateTopicConfig(addr, topicConfig); } long end = System.currentTimeMillis(); - logger.info("create topic use:{}ms,topic:{}", (end- start), auditTopic.getName()); + logger.info("create or update topic use:{}ms,topic:{}", (end- start), topicConfig.getTopicName()); return Result.getOKResult(); } @Override public Result exception(Exception e) throws Exception { - logger.error("create topic:{} err:{}", auditTopic.getName(), e.getMessage()); + logger.error("create or update topic:{} err:{}", topicConfig.getTopicName(), e.getMessage()); return Result.getWebErrorResult(e); } public Cluster mqCluster() { @@ -546,6 +592,7 @@ public Cluster mqCluster() { } @Override public Result exception(Exception e) throws Exception { + logger.error("cluster:{} error", cluster, e); return Result.getWebErrorResult(e).setException(e); } }); diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/UserConsumerService.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/UserConsumerService.java index 47247f90..a65fc1d2 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/UserConsumerService.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/UserConsumerService.java @@ -173,7 +173,7 @@ public Result> queryByNameAndTid(long tid, String consumerNam * 查询用户 * @param userConsumer * @return - */ + */ public Result> queryUserByConsumer(long tid, long cid){ List userList = null; try { @@ -182,7 +182,7 @@ public Result> queryUserByConsumer(long tid, long cid){ logger.error("user err, tid:{},cid:{}", tid, cid, e); return Result.getDBErrorResult(e); } - return Result.getResult(userList); + return Result.getResult(userList); } /** diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/impl/DefaultAlertMessageSender.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/impl/DefaultAlertMessageSender.java index d980682e..0cafc8f8 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/impl/DefaultAlertMessageSender.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/service/impl/DefaultAlertMessageSender.java @@ -1,5 +1,5 @@ package com.sohu.tv.mq.cloud.service.impl; - + import java.util.Properties; import javax.annotation.PostConstruct; @@ -153,5 +153,5 @@ public boolean sendPhone(String message, String phone, int timeout) { public void setMqCloudConfigHelper(MQCloudConfigHelper mqCloudConfigHelper) { this.mqCloudConfigHelper = mqCloudConfigHelper; - } + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/AlarmConfigTask.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/AlarmConfigTask.java index 5a1f7e80..f1cd4701 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/AlarmConfigTask.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/AlarmConfigTask.java @@ -30,13 +30,13 @@ public class AlarmConfigTask { @Scheduled(cron = "3 */10 * * * *") public void refreshAlarmConfig() { - long start = System.currentTimeMillis(); + long start = System.currentTimeMillis(); Result> userAlarmConfigResult = alarmConfigService.queryAll(); if (userAlarmConfigResult.isNotOK()) { logger.error("refresh user alarm config err:{}", userAlarmConfigResult); return; } - alarmConfigBridingService.setConfigTable(userAlarmConfigResult.getResult()); + alarmConfigBridingService.setConfigTable(userAlarmConfigResult.getResult()); logger.info("refresh alarm config use: {}ms", (System.currentTimeMillis() - start)); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ProducerStatsTask.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ProducerStatsTask.java index a3510bf8..a8dd0652 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ProducerStatsTask.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ProducerStatsTask.java @@ -3,9 +3,11 @@ import java.util.ArrayList; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.lang3.math.NumberUtils; import org.slf4j.Logger; @@ -15,11 +17,13 @@ import com.sohu.tv.mq.cloud.bo.ProducerStat; import com.sohu.tv.mq.cloud.bo.ProducerTotalStat; +import com.sohu.tv.mq.cloud.bo.User; import com.sohu.tv.mq.cloud.bo.UserProducer; import com.sohu.tv.mq.cloud.service.AlertService; import com.sohu.tv.mq.cloud.service.ProducerStatService; import com.sohu.tv.mq.cloud.service.ProducerTotalStatService; import com.sohu.tv.mq.cloud.service.UserProducerService; +import com.sohu.tv.mq.cloud.service.UserService; import com.sohu.tv.mq.cloud.util.DateUtil; import com.sohu.tv.mq.cloud.util.MQCloudConfigHelper; import com.sohu.tv.mq.cloud.util.Result; @@ -54,6 +58,9 @@ public class ProducerStatsTask { @Autowired private MQCloudConfigHelper mqCloudConfigHelper; + @Autowired + private UserService userService; + /** * 删除统计表数据 */ @@ -97,45 +104,31 @@ private void log(Result result, Date date, String flag, long start) { public void exceptionProducerStats() { taskExecutor.execute(new Runnable() { public void run() { - producerExcetpion(); + Date date = new Date(); + int dt = NumberUtils.toInt(DateUtil.formatYMD(date)); + String time = DateUtil.getFormat(DateUtil.HHMM).format(new Date(date.getTime() - 5 * 60 * 1000)); + producerExcetpion(dt, time); } }); } - private void producerExcetpion() { - Date date = new Date(); - int dt = NumberUtils.toInt(DateUtil.formatYMD(date)); - String time = DateUtil.getFormat(DateUtil.HHMM).format(new Date(date.getTime() - 5 * 60 * 1000)); + protected void producerExcetpion(int dt, String time) { + long start = System.currentTimeMillis(); int size = 0; Result> listResult = producerTotalStatService.queryExceptionList(dt, time); if(listResult.isNotEmpty()) { - StringBuilder sb = new StringBuilder(); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); - sb.append(""); List list = listResult.getResult(); size = list.size(); Map> groupedMap = group(list); for(String k : groupedMap.keySet()) { - long tid = getTid(k); + StringBuilder sb = new StringBuilder(); + sb.append("
"); - sb.append("producer"); - sb.append(""); - sb.append("时间"); - sb.append(""); - sb.append("client"); - sb.append(""); - sb.append("broker"); - sb.append(""); - sb.append("异常"); - sb.append("
"); + sb.append(""); + sb.append(""); + // 获取发送者列表 + List userProducerList = getUserProducer(k); + + long tid = userProducerList == null ? 0 : userProducerList.get(0).getTid(); List totalList = groupedMap.get(k); for(ProducerTotalStat producerTotalStat : totalList) { Result> producerStatResult = producerStatService.query(producerTotalStat.getId()); @@ -184,13 +177,16 @@ private void producerExcetpion() { } sb.append(""); } + + sb.append(""); + sb.append("
producer时间clientbroker异常
"); + + String userMail = getUserMail(userProducerList); + alertService.sendWanMail(userMail, "客户端异常", sb.toString()); } - sb.append(""); - sb.append(""); - alertService.sendMail("MQCloud客户端异常", sb.toString()); } logger.info("exceptionProducerStats dt:{} time:{} size:{} use:{}ms", dt, time, size, - (System.currentTimeMillis() - date.getTime())); + (System.currentTimeMillis() - start)); } private void removeBlankException(List producerStatList) { @@ -208,13 +204,45 @@ private void removeBlankException(List producerStatList) { * @param producer * @return */ - private long getTid(String producer) { + private List getUserProducer(String producer) { Result> userProducerResult = userProducerService.queryUserProducer(producer); if(userProducerResult.isNotEmpty()) { - UserProducer userProducer = userProducerResult.getResult().get(0); - return userProducer.getTid(); + return userProducerResult.getResult(); + } + return null; + } + + /** + * 获取topic id + * @param producer + * @return + */ + private String getUserMail(List userProducerList) { + if(userProducerList == null) { + return null; + } + Set userIDSet = new HashSet(); + for(UserProducer userProducer : userProducerList) { + userIDSet.add(userProducer.getUid()); + } + String receiver = null; + // 获取用户id + if(!userIDSet.isEmpty()) { + // 获取用户信息 + Result> userListResult = userService.query(userIDSet); + StringBuilder sb = new StringBuilder(); + if(userListResult.isNotEmpty()) { + for(User u : userListResult.getResult()) { + sb.append(u.getEmail()); + sb.append(","); + } + } + if(sb.length() > 0) { + sb.deleteCharAt(sb.length() - 1); + receiver = sb.toString(); + } } - return 0; + return receiver; } /** diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ServerStatusTask.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ServerStatusTask.java index 9d363922..ed146a98 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ServerStatusTask.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/ServerStatusTask.java @@ -138,7 +138,7 @@ public void process(String line, int lineNum) throws Exception { private void saveServerStatus(String ip, OSInfo osInfo) { if(osInfo == null) { return; - } - serverDataService.saveServerInfo(ip, osInfo.getIssue(), -1); + } + serverDataService.saveServerInfo(ip, osInfo.getIssue(), -1); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/monitor/SohuMonitorListener.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/monitor/SohuMonitorListener.java index 44a03f67..04963d48 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/monitor/SohuMonitorListener.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/task/monitor/SohuMonitorListener.java @@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; - + import com.sohu.tv.mq.cloud.bo.ConsumerBlock; import com.sohu.tv.mq.cloud.bo.ConsumerStat; import com.sohu.tv.mq.cloud.bo.Topic; @@ -470,7 +470,7 @@ public void blockWarn(Map> map) { alertService.sendWanMail(topicExt.getReceiver(), "客户端阻塞", content.toString()); } } - + @Override public void endRound() { long use = System.currentTimeMillis() - time; diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/DateUtil.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/DateUtil.java index 8c0558dc..e1badf75 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/DateUtil.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/DateUtil.java @@ -25,7 +25,8 @@ public class DateUtil { public static final String YMDHM_DASH = "yyyy-MM-dd-HH-mm"; public static final String YMD_DASH_DOT_H = "yyyy-MM-dd.HH"; public static final String YMDHMS_DOT_SSS = "yyyyMMddHHmmss.SSS"; - public static final String YMD_BLANK_HMS_COLON = "yyyyMMdd HH:mm"; + public static final String YMD_BLANK_HM_COLON = "yyyyMMdd HH:mm"; + public static final String YMD_BLANK_HMS_COLON = "yyyyMMdd HH:mm:ss"; public static final String YMD_DASH_BLANK_HMS_COLON = "yyyy-MM-dd HH:mm:ss"; public static final String YMD_BLANK_HMS_COLON_DOT_SSS = "yyyyMMdd HH:mm:ss.SSS"; diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/SplitUtil.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/SplitUtil.java index 9263a65b..5f1bfeae 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/SplitUtil.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/SplitUtil.java @@ -1,7 +1,7 @@ package com.sohu.tv.mq.cloud.util; /** - * 分隔工具 + * 分隔工具 * * @Description: * @author yongfeigao @@ -46,7 +46,7 @@ public static String split(String str, String splitor, int maxSize) { end = str.length(); } sb.append(str.substring(i * maxSize, end)); - if (i < count - 1) { + if (i < count - 1) { sb.append(splitor); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/Status.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/Status.java index c0b18abb..2d3fc1bf 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/Status.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/util/Status.java @@ -25,8 +25,8 @@ public enum Status { PERMISSION_DENIED_ERROR(303, "permission denied"), NOT_INIT_IP(304, "请先配置好服务器环境"), REPEAT_ERROR(305, "关联关系已存在"), - LONGIN_ERROR(306, "用户名或密码错误"), - OLD_PASSWORD_ERROR(307, "原始密码输入有误"), + LONGIN_ERROR(306, "用户名或密码错误"), + OLD_PASSWORD_ERROR(307, "原始密码输入有误"), // 4xx代表请求问题 NOT_FOUND_ERROR(404, "请求不存在"), @@ -47,6 +47,8 @@ public enum Status { EMAIL_SEND_ERR(512, "数据操作成功,发送审核邮件失败"), DB_UPDATE_ERR_DELETE_USERCONSUMER_OK(513, "userConsumer删除成功,但是审核记录更新失败"), AUDIT_RECORD_REPEAT(514, "已提交该审核记录,不可重复提交,请耐心等待"), + AUDIT_MESSAGE_CANNOT_AUTID_WHEN_NOT_SEND_OK(515, "消息还未全部发送成功,不可审批"), + AUDIT_MESSAGE_NOT_SEND_OK(516, "消息未发送成功"), // 6xx代表web请求异常 WEB_ERROR(600, "请求错误"), WEB_ERROR_PAGE(601, "出错了,请联系管理员"), diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/ConsumerController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/ConsumerController.java index 81a9615d..b539e2c4 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/ConsumerController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/ConsumerController.java @@ -14,6 +14,8 @@ import org.apache.rocketmq.common.MixAll; import org.apache.rocketmq.common.admin.ConsumeStats; import org.apache.rocketmq.common.admin.OffsetWrapper; +import org.apache.rocketmq.common.admin.TopicOffset; +import org.apache.rocketmq.common.admin.TopicStatsTable; import org.apache.rocketmq.common.message.MessageQueue; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -53,7 +55,7 @@ import com.sohu.tv.mq.cloud.web.vo.ConsumerProgressVO; import com.sohu.tv.mq.cloud.web.vo.UserInfo; /** - * topic接口 + * 消费者接口 * @Description: * @author yongfeigao * @date 2018年6月12日 @@ -120,9 +122,10 @@ public String consumeProgress(UserInfo userInfo, @RequestParam("tid") int tid, M Map> consumerMap = getConsumerMap(tid, cidList); List list = new ArrayList(); + Topic topic = topicTopology.getTopic(); if(!clusteringConsumerList.isEmpty()) { // 抓取集群消费模式下消费者状态 - Cluster cluster = clusterService.getMQClusterById(topicTopology.getTopic().getClusterId()); + Cluster cluster = clusterService.getMQClusterById(topic.getClusterId()); Map consumeStatsMap = consumerService.fetchClusteringConsumeProgress(cluster, clusteringConsumerList); @@ -130,7 +133,7 @@ public String consumeProgress(UserInfo userInfo, @RequestParam("tid") int tid, M for(Consumer consumer : clusteringConsumerList) { ConsumerProgressVO consumerProgressVO = new ConsumerProgressVO(); consumerProgressVO.setConsumer(consumer); - consumerProgressVO.setTopic(topicTopology.getTopic().getName()); + consumerProgressVO.setTopic(topic.getName()); consumerProgressVO.setOwnerList(consumerMap.get(consumer.getId())); if(consumeStatsMap == null) { list.add(consumerProgressVO); @@ -165,6 +168,15 @@ public String consumeProgress(UserInfo userInfo, @RequestParam("tid") int tid, M } } } + + // 获取死topic状况 + String dlqTopic = MixAll.getDLQTopic(consumer.getName()); + consumerProgressVO.setDlqTopic(dlqTopic); + TopicStatsTable topicStatsTable = topicService.stats(cluster, dlqTopic); + if(topicStatsTable != null) { + consumerProgressVO.setDlqOffsetMap(new TreeMap(topicStatsTable.getOffsetTable())); + } + consumerProgressVO.setOffsetMap(offsetMap); consumerProgressVO.setRetryOffsetMap(retryOffsetMap); list.add(consumerProgressVO); @@ -176,12 +188,12 @@ public String consumeProgress(UserInfo userInfo, @RequestParam("tid") int tid, M if(!broadcastConsumerList.isEmpty()) { // 抓取广播消费模式下消费者状态 Map> consumeStatsMap = consumerService.fetchBroadcastConsumeProgress( - topicTopology.getTopic(), broadcastConsumerList); + topic, broadcastConsumerList); // 组装广播消费模式vo for(Consumer consumer : broadcastConsumerList) { ConsumerProgressVO consumerProgressVO = new ConsumerProgressVO(); consumerProgressVO.setConsumer(consumer); - consumerProgressVO.setTopic(topicTopology.getTopic().getName()); + consumerProgressVO.setTopic(topic.getName()); consumerProgressVO.setOwnerList(consumerMap.get(consumer.getId())); if(consumeStatsMap == null) { listExt.add(consumerProgressVO); @@ -213,7 +225,7 @@ public String consumeProgress(UserInfo userInfo, @RequestParam("tid") int tid, M } } setResult(map, "resultExt", Result.getResult(listExt)); - setResult(map, "topic", topicTopology.getTopic()); + setResult(map, "topic", topic); FreemarkerUtil.set("long", Long.class, map); return view; } @@ -358,7 +370,7 @@ public Result delete(UserInfo userInfo, @Valid UserConsumerParam userConsumer } - /** + /** * 消费者列表 * @param topicParam * @return @@ -380,7 +392,7 @@ public Result list(UserInfo userInfo, @RequestParam("tid") int tid) throws Ex @ResponseBody @RequestMapping("/list/all") public Result listAll(UserInfo userInfo) throws Exception { - Result> consumerListResult = consumerService.queryAll(); + Result> consumerListResult = consumerService.queryAll(); return Result.getWebResult(consumerListResult); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/LoginController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/LoginController.java index 1e7ec056..bfddfcbf 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/LoginController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/LoginController.java @@ -14,7 +14,7 @@ import com.sohu.tv.mq.cloud.bo.User; import com.sohu.tv.mq.cloud.common.util.CipherHelper; import com.sohu.tv.mq.cloud.common.util.WebUtil; -import com.sohu.tv.mq.cloud.service.UserService; +import com.sohu.tv.mq.cloud.service.UserService; import com.sohu.tv.mq.cloud.util.MQCloudConfigHelper; import com.sohu.tv.mq.cloud.util.Result; import com.sohu.tv.mq.cloud.util.Status; @@ -41,7 +41,7 @@ public class LoginController extends ViewController { @RequestMapping public String index(Map map) { setView(map, "index"); - setResult(map,mqCloudConfigHelper.getIsOpenRegister()); + setResult(map,mqCloudConfigHelper.getIsOpenRegister()); return view(); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/RegisterController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/RegisterController.java index 311ade37..55bae1f9 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/RegisterController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/RegisterController.java @@ -55,7 +55,7 @@ public Result add(@RequestParam("email") String email, // 验证用户是否开启注册功能 if (mqCloudConfigHelper.getIsOpenRegister() != null && mqCloudConfigHelper.getIsOpenRegister() != 1) { return Result.getResult(Status.PERMISSION_DENIED_ERROR); - } + } User user = new User(); email = email.trim(); if(StringUtils.isBlank(email)) { diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/TopicMessageController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/TopicMessageController.java index 8b741093..ac52d565 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/TopicMessageController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/TopicMessageController.java @@ -1,8 +1,7 @@ package com.sohu.tv.mq.cloud.web.controller; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -11,6 +10,8 @@ import javax.validation.Valid; import org.apache.commons.lang3.StringUtils; +import org.apache.rocketmq.common.admin.TopicOffset; +import org.apache.rocketmq.common.admin.TopicStatsTable; import org.apache.rocketmq.common.protocol.body.ClusterInfo; import org.apache.rocketmq.tools.admin.MQAdminExt; import org.springframework.beans.factory.annotation.Autowired; @@ -18,20 +19,27 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSON; +import com.sohu.tv.mq.cloud.bo.Audit; +import com.sohu.tv.mq.cloud.bo.Audit.TypeEnum; +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; import com.sohu.tv.mq.cloud.bo.Cluster; import com.sohu.tv.mq.cloud.bo.DecodedMessage; import com.sohu.tv.mq.cloud.bo.MessageData; import com.sohu.tv.mq.cloud.bo.MessageQueryCondition; import com.sohu.tv.mq.cloud.bo.Topic; +import com.sohu.tv.mq.cloud.bo.UserProducer; import com.sohu.tv.mq.cloud.mq.DefaultCallback; import com.sohu.tv.mq.cloud.mq.MQAdminTemplate; +import com.sohu.tv.mq.cloud.service.AlertService; +import com.sohu.tv.mq.cloud.service.AuditService; import com.sohu.tv.mq.cloud.service.ClusterService; import com.sohu.tv.mq.cloud.service.MessageService; import com.sohu.tv.mq.cloud.service.TopicService; +import com.sohu.tv.mq.cloud.service.UserProducerService; import com.sohu.tv.mq.cloud.util.CompressUtil; -import com.sohu.tv.mq.cloud.util.DateUtil; import com.sohu.tv.mq.cloud.util.FreemarkerUtil; import com.sohu.tv.mq.cloud.util.Result; import com.sohu.tv.mq.cloud.util.SplitUtil; @@ -49,6 +57,8 @@ @RequestMapping("/msg") public class TopicMessageController extends ViewController { + public static final int TIME_SPAN = 5 * 60 * 1000; + @Autowired private MessageService messageService; @@ -61,6 +71,15 @@ public class TopicMessageController extends ViewController { @Autowired private MQAdminTemplate mqAdminTemplate; + @Autowired + private UserProducerService userProducerService; + + @Autowired + private AuditService auditService; + + @Autowired + private AlertService alertService; + /** * 首页 * @return @@ -82,20 +101,39 @@ public String index(UserInfo userInfo, messageQueryCondition.setCid((int)topic.getClusterId()); messageQueryCondition.setTopic(topic.getName()); // 舍掉毫秒 - calculateTime(messageQueryCondition, (System.currentTimeMillis() - MessageQueryCondition.TIME_SPAN) / 1000 * 1000); + long now = System.currentTimeMillis() / 1000 * 1000; + long startTime = now - TIME_SPAN; + messageQueryCondition.setStart(startTime); + messageQueryCondition.setEnd(now); setResult(map, messageQueryCondition); // 获取集群信息 + Cluster cluster = clusterService.getMQClusterById(topic.getClusterId()); ClusterInfo clusterInfo = mqAdminTemplate.execute(new DefaultCallback() { public ClusterInfo callback(MQAdminExt mqAdmin) throws Exception { return mqAdmin.examineBrokerClusterInfo(); } public Cluster mqCluster() { - return clusterService.getMQClusterById(topic.getClusterId()); + return cluster; } }); int brokerSize = clusterInfo.getBrokerAddrTable().size(); setResult(map, "brokerSize", brokerSize); + + // 获取topic最大偏移量 + TopicStatsTable topicStatsTable = topicService.stats(cluster, topic.getName()); + if(topicStatsTable != null) { + long maxOffset = 0; + for(TopicOffset topicOffset : topicStatsTable.getOffsetTable().values()) { + if(maxOffset < topicOffset.getMaxOffset()) { + maxOffset = topicOffset.getMaxOffset(); + } + } + messageQueryCondition.setMaxOffset(maxOffset); + } + + // 设置是否是拥有者 + setOwner(userInfo, map, tid); return view; } @@ -108,7 +146,8 @@ public Cluster mqCluster() { public String search(UserInfo userInfo, HttpServletRequest request, HttpServletResponse response, - @RequestParam("time") String time, + @RequestParam("startTime") Long startTime, + @RequestParam("endTime") Long endTime, @RequestParam("append") boolean append, @RequestParam(name="key", required=false) String key, @RequestParam(name="messageParam") String messageParam, @@ -117,14 +156,16 @@ public String search(UserInfo userInfo, // 解析参数对象 if(StringUtils.isEmpty(key)) { key = null; + } else { + key = key.trim(); } - MessageQueryCondition messageQueryCondition = parseParam(time, key, messageParam, append); + MessageQueryCondition messageQueryCondition = parseParam(startTime, endTime, key, messageParam, append); if(messageQueryCondition == null) { setResult(map, Result.getResult(Status.PARAM_ERROR)); return view; } // 消息查询 - Result result = messageService.queryMessage(messageQueryCondition); + Result result = messageService.queryMessage(messageQueryCondition, false); setResult(map, result); return view; } @@ -144,6 +185,7 @@ public String view(UserInfo userInfo, Map map) throws Exception { String view = viewModule() + "/idSearch"; Cluster cluster = clusterService.getMQClusterById(cid); + msgId = msgId.trim(); // 消息查询 Result result = messageService.queryMessage(cluster, topic, msgId); setResult(map, result); @@ -160,19 +202,18 @@ public String keySearch(UserInfo userInfo, HttpServletRequest request, HttpServletResponse response, @RequestParam("topic") String topic, - @RequestParam("start") String start, - @RequestParam("end") String end, + @RequestParam("keyStartTime") Long beginTime, + @RequestParam("keyEndTime") Long endTime, @RequestParam("cid") int cid, @RequestParam(name="msgKey") String msgKey, Map map) throws Exception { String view = viewModule() + "/keySearch"; Cluster cluster = clusterService.getMQClusterById(cid); // 时间点 - long beginTime = toLong(start); - long endTime = toLong(end); if(beginTime == 0 || endTime == 0 || beginTime > endTime) { setResult(map, Result.getResult(Status.PARAM_ERROR)); } else { + msgKey = msgKey.trim(); // 消息查询 Result> result = messageService.queryMessageByKey(cluster, topic, msgKey, beginTime, endTime); setResult(map, result); @@ -180,17 +221,6 @@ public String keySearch(UserInfo userInfo, return view; } - private long toLong(String dt) { - SimpleDateFormat simpleDateFormat = DateUtil.getFormat(DateUtil.YMD_DASH_BLANK_HMS_COLON); - try { - Date startDate = simpleDateFormat.parse(dt); - return startDate.getTime(); - } catch (Exception e) { - logger.error("parse dt:{}", dt, e); - } - return 0; - } - /** * 组装参数 * @param time @@ -200,8 +230,11 @@ private long toLong(String dt) { * @return * @throws IOException */ - private MessageQueryCondition parseParam(String time, String key, String messageParam, boolean append) - throws IOException { + private MessageQueryCondition parseParam(Long startTime, Long endTime, String key, String messageParam, + boolean append) throws IOException { + if(endTime < startTime) { + return null; + } // 解析对象 String json = CompressUtil.uncompress(messageParam); MessageQueryCondition messageQueryCondition = JSON.parseObject(json, MessageQueryCondition.class); @@ -209,41 +242,15 @@ private MessageQueryCondition parseParam(String time, String key, String message messageQueryCondition.prepareForSearch(); return messageQueryCondition; } - // 解析查询的时间 - long queryTime = 0; - try { - queryTime = DateUtil.getFormat(DateUtil.YMD_DASH_BLANK_HMS_COLON).parse(time).getTime(); - } catch (Exception e) { - logger.error("time parse err:{}", time, e); - } - if(queryTime == 0) { - return null; - } messageQueryCondition.reset(); - calculateTime(messageQueryCondition, queryTime); + messageQueryCondition.setStart(startTime); + messageQueryCondition.setEnd(endTime); if(StringUtils.isNotEmpty(key)) { messageQueryCondition.setKey(key); } return messageQueryCondition; } - /** - * 计算时间 - * @param messageQueryCondition - * @param time - */ - private void calculateTime(MessageQueryCondition messageQueryCondition, long time) { - messageQueryCondition.setTime(time); - messageQueryCondition.setStart(time - MessageQueryCondition.TIME_SPAN); - long end = time + MessageQueryCondition.TIME_SPAN; - // 舍掉毫秒 - long now = System.currentTimeMillis() / 1000 * 1000; - if(end > now) { - end = now; - } - messageQueryCondition.setEnd(end); - } - /** * 消息轨迹 * @return @@ -259,6 +266,159 @@ public String track(UserInfo userInfo, @Valid MessageParam messageParam, Map map) throws Exception { + String view = viewModule() + "/offsetSearch"; + // 解析参数对象 + if(StringUtils.isEmpty(key)) { + key = null; + } else { + key = key.trim(); + } + MessageQueryCondition messageQueryCondition = parseParam(offsetStart, offsetEnd, key, messageParam, append); + if(messageQueryCondition == null) { + setResult(map, Result.getResult(Status.PARAM_ERROR)); + return view; + } + + // 消息查询 + Result result = messageService.queryMessage(messageQueryCondition, true); + setResult(map, result); + return view; + } + + /** + * 根据偏移量搜索某个topic + * @return + * @throws Exception + */ + @RequestMapping("/topic/offset/search") + public String topicOffsetSearch(UserInfo userInfo, + HttpServletRequest request, + HttpServletResponse response, + @RequestParam("toCid") int cid, + @RequestParam("toTopic") String topic, + @RequestParam("toStart") Long offsetStart, + @RequestParam("toEnd") Long offsetEnd, + @RequestParam(name="toKey", required=false) String key, + @RequestParam("toAppend") boolean append, + @RequestParam(name="toMessageParam", required=false) String messageParam, + Map map) throws Exception { + String view = viewModule() + "/topicOffsetSearch"; + MessageQueryCondition messageQueryCondition = null; + if(StringUtils.isEmpty(messageParam)) { + messageQueryCondition = new MessageQueryCondition(); + messageQueryCondition.setCid(cid); + messageQueryCondition.setTopic(topic); + messageQueryCondition.setStart(offsetStart); + messageQueryCondition.setEnd(offsetEnd); + if(StringUtils.isNotEmpty(key)) { + messageQueryCondition.setKey(key); + } + messageQueryCondition.reset(); + } else { + messageQueryCondition = parseParam(offsetStart, offsetEnd, key, messageParam, append); + } + if(messageQueryCondition == null) { + setResult(map, Result.getResult(Status.PARAM_ERROR)); + return view; + } + messageQueryCondition.setTopic(topic); + + // 消息查询 + Result result = messageService.queryMessage(messageQueryCondition, true); + setResult(map, result); + return view; + } + + + /** + * 消息重发审核 + * @param userInfo + * @param tid + * @param msgIds + * @return + * @throws Exception + */ + @ResponseBody + @RequestMapping("/resend") + public Result resend(UserInfo userInfo, + @RequestParam("tid") int tid, + @RequestParam("msgIds") String msgIds) throws Exception { + // 检测 + String[] msgIdArray = msgIds.split(","); + if(msgIdArray.length == 0) { + return Result.getResult(Status.PARAM_ERROR); + } + if(!isOwner(userInfo, tid)) { + return Result.getResult(Status.PERMISSION_DENIED_ERROR); + } + // 构造审核记录 + Audit audit = new Audit(); + audit.setType(TypeEnum.RESEND_MESSAGE.getType()); + audit.setUid(userInfo.getUser().getId()); + // 构造消息记录 + List auditResendMessageList = new ArrayList(); + for(String msgId : msgIdArray) { + AuditResendMessage auditResendMessage = new AuditResendMessage(); + auditResendMessage.setMsgId(msgId); + auditResendMessage.setTid(tid); + auditResendMessageList.add(auditResendMessage); + } + + // 保存记录 + Result result = auditService.saveAuditAndAuditResendMessage(audit, auditResendMessageList); + // 发送提醒邮件 + if(result.isOK()) { + Result topicResult = topicService.queryTopic(tid); + if(topicResult.isOK()) { + String tip = " topic:" + topicResult.getResult().getName() + " 消息量:" + msgIdArray.length; + alertService.sendAuditMail(userInfo.getUser(), TypeEnum.RESEND_MESSAGE, tip); + } + } + return Result.getWebResult(result); + } + + /** + * 设置是否是拥有者 + * @param userInfo + * @param map + * @param tid + */ + private void setOwner(UserInfo userInfo, Map map, int tid) { + setResult(map, "owner", getOwner(userInfo, tid)); + } + + private boolean isOwner(UserInfo userInfo, int tid) { + return 1 == getOwner(userInfo, tid); + } + + private int getOwner(UserInfo userInfo, int tid) { + int owner = 0; + if(userInfo.getUser().isAdmin()) { + owner = 1; + } else { + Result> result = userProducerService.queryUserProducer(userInfo.getUser().getId(), tid); + if(result.isOK()) { + owner = 1; + } + } + return owner; + } + @Override public String viewModule() { return "msg"; diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/UserController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/UserController.java index 5477c6e7..cf9cda13 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/UserController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/UserController.java @@ -9,7 +9,7 @@ import java.util.Set; import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; +import javax.validation.Valid; import org.apache.commons.codec.digest.DigestUtils; import org.apache.rocketmq.common.protocol.body.ClusterInfo; @@ -134,7 +134,7 @@ public Result resetPassword(@RequestParam("uid") int uid, Result result = userService.resetPassword(uid, passwordNew); return result; } - + /** * 获取user列表 * diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminBrokerController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminBrokerController.java index 78bf7c2b..1f7510b3 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminBrokerController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminBrokerController.java @@ -2,7 +2,7 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.List; +import java.util.List; import org.apache.rocketmq.common.protocol.body.ClusterInfo; import org.apache.rocketmq.common.protocol.route.BrokerData; @@ -91,7 +91,7 @@ public Cluster mqCluster() { return getMQCluster(cid); } }); - if (brokerListResult.isEmpty()) { + if (brokerListResult.isEmpty()) { return brokerListResult; } Result result = brokerService.refresh(cid, brokerListResult.getResult()); diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminClusterController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminClusterController.java index bdc044bc..574cf8dc 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminClusterController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminClusterController.java @@ -1,5 +1,5 @@ package com.sohu.tv.mq.cloud.web.controller.admin; - + import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -262,7 +262,7 @@ public Cluster mqCluster() { } public Result> exception(Exception e) throws Exception { - logger.error("cluster:{} err", mqCluster(), e.getMessage()); + logger.error("cluster:{} err", mqCluster(), e); return Result.getWebErrorResult(e); } }); @@ -313,7 +313,7 @@ public Cluster mqCluster() { } public Map> exception(Exception e) throws Exception { - logger.error("cluster:{} err", mqCluster(), e.getMessage()); + logger.error("cluster:{} err", mqCluster(), e); return null; } }); diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminMessageController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminMessageController.java new file mode 100644 index 00000000..71e39866 --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminMessageController.java @@ -0,0 +1,211 @@ +package com.sohu.tv.mq.cloud.web.controller.admin; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.rocketmq.client.producer.SendResult; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.sohu.tv.mq.cloud.bo.Audit; +import com.sohu.tv.mq.cloud.bo.Audit.StatusEnum; +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; +import com.sohu.tv.mq.cloud.bo.Cluster; +import com.sohu.tv.mq.cloud.bo.Topic; +import com.sohu.tv.mq.cloud.service.AuditResendMessageService; +import com.sohu.tv.mq.cloud.service.AuditService; +import com.sohu.tv.mq.cloud.service.ClusterService; +import com.sohu.tv.mq.cloud.service.MessageService; +import com.sohu.tv.mq.cloud.service.TopicService; +import com.sohu.tv.mq.cloud.util.Result; +import com.sohu.tv.mq.cloud.util.Status; +import com.sohu.tv.mq.cloud.web.vo.ResendMessageVO; +import com.sohu.tv.mq.cloud.web.vo.UserInfo; + +/** + * 消息 + * + * @author yongfeigao + * @date 2018年12月6日 + */ +@Controller +@RequestMapping("/admin/message") +public class AdminMessageController { + + private Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Autowired + private AuditService auditService; + + @Autowired + private AuditResendMessageService auditResendMessageService; + + @Autowired + private MessageService messageService; + + @Autowired + private ClusterService clusterService; + + @Autowired + private TopicService topicService; + + /** + * resend message + * + * @param aid + * @param map + * @return + */ + @ResponseBody + @RequestMapping(value = "/resend", method = RequestMethod.POST) + public Result resend(UserInfo userInfo, @RequestParam("aid") long aid) { + // 获取audit + Result auditResult = auditService.queryAudit(aid); + if (auditResult.isNotOK()) { + return auditResult; + } + // 校验状态是否合法 + Audit audit = auditResult.getResult(); + if (StatusEnum.INIT.getStatus() != audit.getStatus()) { + return Result.getResult(Status.PARAM_ERROR); + } + + // 查询审核记录 + Result> listResult = auditResendMessageService.query(aid); + if (listResult.isEmpty()) { + return listResult; + } + + // 获取topic + List auditResendMessageList = listResult.getResult(); + Result topicResult = topicService.queryTopic(auditResendMessageList.get(0).getTid()); + if (topicResult.isNotOK()) { + return topicResult; + } + String topic = topicResult.getResult().getName(); + + // 获取cluster + Cluster cluster = clusterService.getMQClusterById(topicResult.getResult().getClusterId()); + + // 统计状态 + ResendMessageVO resendMessageVO = new ResendMessageVO(); + resendMessageVO.setTotal(auditResendMessageList.size()); + // 发送消息 + for (AuditResendMessage msg : auditResendMessageList) { + if(AuditResendMessage.StatusEnum.SUCCESS.getStatus() == msg.getStatus()) { + continue; + } + Result sendResult = messageService.resend(cluster, topic, msg.getMsgId()); + int status = AuditResendMessage.StatusEnum.SUCCESS.getStatus(); + if (sendResult.isNotOK()) { + logger.warn("resendMessage cluster:{} topic:{} msgId:{} err:{}", cluster, topic, msg.getMsgId(), + sendResult); + status = AuditResendMessage.StatusEnum.FAILED.getStatus(); + resendMessageVO.incrFailed(); + } else { + resendMessageVO.incrSuccess(); + } + Result updateResult = auditResendMessageService.update(aid, msg.getMsgId(), status); + if (updateResult.isNotOK()) { + resendMessageVO.incrStatusUpdatedFailed(); + logger.warn("resendMessage cluster:{} topic:{} msgId:{} update not ok :{}", cluster, topic, + msg.getMsgId(), updateResult); + } + } + + if(resendMessageVO.getSuccess() > 0 || resendMessageVO.getFailed() > 0) { + // 查询审核记录 + listResult = auditResendMessageService.query(aid); + if (listResult.isNotEmpty()) { + resendMessageVO.setMsgList(listResult.getResult()); + } + } + + if(resendMessageVO.sendAllOK()) { + return Result.getResult(resendMessageVO); + } else { + return Result.getResult(Status.AUDIT_MESSAGE_NOT_SEND_OK).setResult(resendMessageVO); + } + } + + /** + * resend message + * + * @param aid + * @param map + * @return + */ + @ResponseBody + @RequestMapping(value = "/resend/one", method = RequestMethod.POST) + public Result resendOne(UserInfo userInfo, @RequestParam("aid") long aid, @RequestParam("msgId") String msgId) { + // 获取audit + Result auditResult = auditService.queryAudit(aid); + if (auditResult.isNotOK()) { + return auditResult; + } + // 校验状态是否合法 + Audit audit = auditResult.getResult(); + if (StatusEnum.INIT.getStatus() != audit.getStatus()) { + return Result.getResult(Status.PARAM_ERROR); + } + + // 查询审核记录 + Result auditResendMessageResult = auditResendMessageService.queryOne(aid, msgId); + if (auditResendMessageResult.isNotOK()) { + return auditResendMessageResult; + } + + // 获取topic + AuditResendMessage auditResendMessage = auditResendMessageResult.getResult(); + Result topicResult = topicService.queryTopic(auditResendMessage.getTid()); + if (topicResult.isNotOK()) { + return topicResult; + } + String topic = topicResult.getResult().getName(); + + // 获取cluster + Cluster cluster = clusterService.getMQClusterById(topicResult.getResult().getClusterId()); + + // 统计状态 + ResendMessageVO resendMessageVO = new ResendMessageVO(); + resendMessageVO.setTotal(1); + if(AuditResendMessage.StatusEnum.SUCCESS.getStatus() != auditResendMessage.getStatus()) { + Result sendResult = messageService.resend(cluster, topic, msgId); + int status = AuditResendMessage.StatusEnum.SUCCESS.getStatus(); + if (sendResult.isNotOK()) { + logger.warn("resendMessage cluster:{} topic:{} msgId:{} err:{}", cluster, topic, msgId, + sendResult); + status = AuditResendMessage.StatusEnum.FAILED.getStatus(); + resendMessageVO.incrFailed(); + } else { + resendMessageVO.incrSuccess(); + } + Result updateResult = auditResendMessageService.update(aid, msgId, status); + if (updateResult.isNotOK()) { + logger.warn("resendMessage cluster:{} topic:{} msgId:{} update not ok :{}", cluster, topic, + msgId, updateResult); + resendMessageVO.incrStatusUpdatedFailed(); + } + + // 查询审核记录 + auditResendMessageResult = auditResendMessageService.queryOne(aid, msgId); + if (auditResendMessageResult.isOK()) { + List list = new ArrayList(1); + list.add(auditResendMessageResult.getResult()); + resendMessageVO.setMsgList(list); + } + } + + if(resendMessageVO.sendAllOK()) { + return Result.getResult(resendMessageVO); + } else { + return Result.getResult(Status.AUDIT_MESSAGE_NOT_SEND_OK).setResult(resendMessageVO); + } + } +} diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminServerController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminServerController.java index e19f7c41..5fb9251f 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminServerController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminServerController.java @@ -2,7 +2,7 @@ import java.math.BigDecimal; import java.text.DecimalFormat; -import java.util.ArrayList; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -189,7 +189,7 @@ private List getMachineTypeVO() { for (MachineType mt : MachineType.values()) { result.add(new MachineTypeVO(mt)); } - return result; + return result; } /** diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminUserController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminUserController.java index 3f183f33..189622a0 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminUserController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AdminUserController.java @@ -1,7 +1,7 @@ package com.sohu.tv.mq.cloud.web.controller.admin; import java.util.List; -import java.util.Map; +import java.util.Map; import javax.validation.Valid; import org.springframework.beans.BeanUtils; @@ -88,7 +88,7 @@ public Result resetPassword(@RequestParam("uid") int uid, @RequestParam("pass return Result.getResult(Status.PARAM_ERROR); } Result result = userService.resetPassword(uid, password); - return result; + return result; } @Override diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AlarmConfigController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AlarmConfigController.java index d0c0f195..83da02fd 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AlarmConfigController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AlarmConfigController.java @@ -1,5 +1,5 @@ package com.sohu.tv.mq.cloud.web.controller.admin; - + import javax.validation.Valid; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -66,7 +66,7 @@ public Result addUserAlarmConfig(@Valid AlarmConfigParam alarmConfigParam) { @ResponseBody public Result deleteAlarmConfigByConsumer(@RequestParam("consumer") String consumer) { Result deleteResult = alarmConfigService.deleteByConsumer(consumer); - return deleteResult; + return deleteResult; } @Override diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AuditController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AuditController.java index cdeeee7e..1a7696b9 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AuditController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/AuditController.java @@ -25,6 +25,7 @@ import com.sohu.tv.mq.cloud.bo.AuditAssociateProducer; import com.sohu.tv.mq.cloud.bo.AuditConsumer; import com.sohu.tv.mq.cloud.bo.AuditConsumerDelete; +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; import com.sohu.tv.mq.cloud.bo.AuditResetOffset; import com.sohu.tv.mq.cloud.bo.AuditTopic; import com.sohu.tv.mq.cloud.bo.AuditTopicDelete; @@ -42,6 +43,7 @@ import com.sohu.tv.mq.cloud.service.AssociateProducerService; import com.sohu.tv.mq.cloud.service.AuditConsumerDeleteService; import com.sohu.tv.mq.cloud.service.AuditConsumerService; +import com.sohu.tv.mq.cloud.service.AuditResendMessageService; import com.sohu.tv.mq.cloud.service.AuditResetOffsetService; import com.sohu.tv.mq.cloud.service.AuditService; import com.sohu.tv.mq.cloud.service.AuditTopicDeleteService; @@ -63,6 +65,7 @@ import com.sohu.tv.mq.cloud.web.vo.AuditAssociateProducerVO; import com.sohu.tv.mq.cloud.web.vo.AuditConsumerDeleteVO; import com.sohu.tv.mq.cloud.web.vo.AuditConsumerVO; +import com.sohu.tv.mq.cloud.web.vo.AuditResendMessageVO; import com.sohu.tv.mq.cloud.web.vo.AuditResetOffsetVO; import com.sohu.tv.mq.cloud.web.vo.AuditTopicDeleteVO; import com.sohu.tv.mq.cloud.web.vo.AuditTopicUpdateVO; @@ -136,6 +139,9 @@ public class AuditController extends AdminViewController { @Autowired private ClusterService clusterService; + + @Autowired + private AuditResendMessageService auditResendMessageService; /** * 审核主列表 @@ -248,6 +254,10 @@ public String detail(@RequestParam("type") int type, view = "deleteUserConsumer"; result = getDeleteUserConsumerResult(aid); break; + case RESEND_MESSAGE: + view = "resendMessage"; + result = getResendMessageResult(aid); + break; } setResult(map, result); return adminViewModule() + "/" + view; @@ -479,8 +489,16 @@ public Result refuse(UserInfo userInfo, case DELETE_USERCONSUMER: msg = getDeleteUserConsumerTipMessage(aid); break; + case RESEND_MESSAGE: + msg = null; + break; + } + StringBuilder sb = new StringBuilder("您"); + if(audit.getCreateTime() != null) { + String createTime = DateUtil.getFormat(DateUtil.YMD_DASH_BLANK_HMS_COLON).format(audit.getCreateTime()); + sb.append(createTime); } - StringBuilder sb = new StringBuilder("您申请的"); + sb.append("申请的"); sb.append(typeEnum.getName()); if (msg != null) { sb.append("["); @@ -1275,7 +1293,12 @@ private boolean agreeAndTip(Audit audit, String auditMail, String tip) { if (updateResult.isOK()) { UserMessage userMessage = new UserMessage(); TypeEnum typeEnum = TypeEnum.getEnumByType(audit.getType()); - StringBuilder sb = new StringBuilder("您申请的"); + StringBuilder sb = new StringBuilder("您"); + if(audit.getCreateTime() != null) { + String createTime = DateUtil.getFormat(DateUtil.YMD_DASH_BLANK_HMS_COLON).format(audit.getCreateTime()); + sb.append(createTime); + } + sb.append("申请的"); sb.append(typeEnum.getName()); if (tip != null) { sb.append("["); @@ -1641,6 +1664,75 @@ private Result getDeleteUserConsumerResult(long aid) { auditUserConsumerDeleteVO.setCommit(false); return Result.getResult(auditUserConsumerDeleteVO); } + + private Result getResendMessageResult(long aid) { + // 查询audit + Result auditResult = auditService.queryAudit(aid); + if (auditResult.isNotOK()) { + return auditResult; + } + // 查询AuditResendMessage + Audit audit = auditResult.getResult(); + Result> auditResendMessageListResult = auditResendMessageService.query(audit.getId()); + if(auditResendMessageListResult.isEmpty()) { + return auditResendMessageListResult; + } + + // 查询topic + List auditResendMessageList = auditResendMessageListResult.getResult(); + Result topicResult = topicService.queryTopic(auditResendMessageList.get(0).getTid()); + if(topicResult.isNotOK()) { + return topicResult; + } + + // 拼装vo + AuditResendMessageVO auditResendMessageVO = new AuditResendMessageVO(); + auditResendMessageVO.setTopic(topicResult.getResult().getName()); + auditResendMessageVO.setMsgList(auditResendMessageList); + return Result.getResult(auditResendMessageVO); + } + + /** + * resendMessage + * + * @param aid + * @param map + * @return + */ + @ResponseBody + @RequestMapping(value = "/resend/message", method = RequestMethod.POST) + public Result resendMessage(UserInfo userInfo, @RequestParam("aid") long aid) { + // 获取audit + Result auditResult = auditService.queryAudit(aid); + if (auditResult.isNotOK()) { + return auditResult; + } + // 校验状态是否合法 + Audit audit = auditResult.getResult(); + if (StatusEnum.INIT.getStatus() != audit.getStatus()) { + return Result.getResult(Status.PARAM_ERROR); + } + + // 查询审核记录 + Result> listResult = auditResendMessageService.query(aid); + if (listResult.isNotOK()) { + return listResult; + } + // 未全部重发成功不可审核 + List auditResendMessageList = listResult.getResult(); + for(AuditResendMessage msg : auditResendMessageList) { + if(AuditResendMessage.StatusEnum.SUCCESS.getStatus() != msg.getStatus()) { + return Result.getResult(Status.AUDIT_MESSAGE_CANNOT_AUTID_WHEN_NOT_SEND_OK); + } + } + // 更新申请状态 + boolean updateOK = agreeAndTip(audit, userInfo.getUser().getEmail(), null); + if (updateOK) { + return Result.getOKResult(); + } + return Result.getResult(Status.DB_UPDATE_ERR_DELETE_RESET_OFFSET_OK); + } + /** * vo赋值 * diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/MonitorController.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/MonitorController.java index cfae8906..00f441ef 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/MonitorController.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/admin/MonitorController.java @@ -1,6 +1,6 @@ package com.sohu.tv.mq.cloud.web.controller.admin; -import java.util.HashSet; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -84,7 +84,7 @@ public String list(Map map) { if (alarmConfiglist.size() > 0) { consumerMonitorVO.setAlarmConfig(alarmConfiglist); } - setResult(map, consumerMonitorVO); + setResult(map, consumerMonitorVO); return view(); } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/AlarmConfigParam.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/AlarmConfigParam.java index e6b8e03f..d2ce5ba4 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/AlarmConfigParam.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/AlarmConfigParam.java @@ -9,7 +9,7 @@ * @author zhehongyuan * @date 2018年9月28日 */ -public class AlarmConfigParam { +public class AlarmConfigParam { // consumer名称,为空行为默认配置 private String consumer; // 堆积时间 @@ -74,5 +74,5 @@ public Integer getIgnoreWarn() { } public void setIgnoreWarn(Integer ignoreWarn) { this.ignoreWarn = ignoreWarn; - } + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/BrokerParam.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/BrokerParam.java index 4f37a470..71910a7b 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/BrokerParam.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/controller/param/BrokerParam.java @@ -67,7 +67,7 @@ public boolean isSlave() { return true; } return false; - } + } public int getBrokerRoleID() { if(SLAVE.equals(getBrokerRole())) { @@ -75,7 +75,7 @@ public int getBrokerRoleID() { } return 0; } - + public String getBrokerName() { return brokerName; } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/AuditResendMessageVO.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/AuditResendMessageVO.java new file mode 100644 index 00000000..0f2a1bd9 --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/AuditResendMessageVO.java @@ -0,0 +1,27 @@ +package com.sohu.tv.mq.cloud.web.vo; + +import java.util.List; + +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; +/** + * 重发消息VO + * + * @author yongfeigao + * @date 2018年12月6日 + */ +public class AuditResendMessageVO { + private String topic; + private List msgList; + public String getTopic() { + return topic; + } + public void setTopic(String topic) { + this.topic = topic; + } + public List getMsgList() { + return msgList; + } + public void setMsgList(List msgList) { + this.msgList = msgList; + } +} diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/BrokerStatVO.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/BrokerStatVO.java index a85766e8..2ca9464b 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/BrokerStatVO.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/BrokerStatVO.java @@ -1,6 +1,6 @@ package com.sohu.tv.mq.cloud.web.vo; -import java.util.Map; +import java.util.Map; import com.sohu.tv.mq.cloud.bo.CheckStatusEnum; @@ -69,6 +69,6 @@ public void setCheckTime(String checkTime) { this.checkTime = checkTime; } public String getCheckStatusDesc() { - return CheckStatusEnum.getCheckStatusEnumByStatus(getCheckStatus()).getDesc(); + return CheckStatusEnum.getCheckStatusEnumByStatus(getCheckStatus()).getDesc(); } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ConsumerProgressVO.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ConsumerProgressVO.java index 2f8bafb8..58291376 100644 --- a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ConsumerProgressVO.java +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ConsumerProgressVO.java @@ -6,84 +6,116 @@ import java.util.Map.Entry; import org.apache.rocketmq.common.admin.OffsetWrapper; +import org.apache.rocketmq.common.admin.TopicOffset; import org.apache.rocketmq.common.message.MessageQueue; import com.sohu.tv.mq.cloud.bo.ConsumeStatsExt; import com.sohu.tv.mq.cloud.bo.Consumer; import com.sohu.tv.mq.cloud.bo.User; + /** * 消费进度 - * @Description: + * + * @Description: * @author yongfeigao * @date 2018年7月11日 */ public class ConsumerProgressVO { private Consumer consumer; private double consumeTps; + // 正常offset private Map offsetMap; + // 重试offset private Map retryOffsetMap; - private String retryTopic; + // 死消息offset + private Map dlqOffsetMap; + // 正常topic private String topic; + // 重试topic + private String retryTopic; + // 死消息topic + private String dlqTopic; + private long lastTimestamp = Long.MAX_VALUE; private List consumeStatsList; private List ownerList; + public Consumer getConsumer() { return consumer; } + public void setConsumer(Consumer consumer) { this.consumer = consumer; } + public double getConsumeTps() { return consumeTps; } + public void setConsumeTps(double consumeTps) { this.consumeTps = consumeTps; } + public Map getOffsetMap() { return offsetMap; } + public void setOffsetMap(Map offsetMap) { this.offsetMap = offsetMap; } + public Map getRetryOffsetMap() { + // retry无数据不返回 + if(getRetryMaxOffset() == 0) { + return null; + } return retryOffsetMap; } + public void setRetryOffsetMap(Map retryOffsetMap) { this.retryOffsetMap = retryOffsetMap; } + public long getMinLastTimestamp() { return lastTimestamp; } + public long getLastTimestamp() { return lastTimestamp; } + public void setLastTimestamp(long lastTimestamp) { this.lastTimestamp = lastTimestamp; } + public String getRetryTopic() { return retryTopic; } + public void setRetryTopic(String retryTopic) { this.retryTopic = retryTopic; } + public String getTopic() { return topic; } + public void setTopic(String topic) { this.topic = topic; } + public long getDiff() { long diffTotal = computeTotalDiff(offsetMap, false); diffTotal += computeTotalDiff(retryOffsetMap, true); - if(diffTotal < 0) { + if (diffTotal < 0) { diffTotal = 0; } return diffTotal; } - + private long computeTotalDiff(Map map, boolean retry) { long diffTotal = 0L; - if(map == null) { + if (map == null) { return diffTotal; } // 最小时间 @@ -93,32 +125,80 @@ private long computeTotalDiff(Map map, boolean retr Entry next = it.next(); long diff = next.getValue().getBrokerOffset() - next.getValue().getConsumerOffset(); diffTotal += diff; - + long lastTimestamp = next.getValue().getLastTimestamp(); - if(lastTimestamp == 0) { + if (lastTimestamp == 0) { continue; } - // 取非重试队列和重试队列有堆积的 最小时间 - if((!retry || (retry && diff > 0)) && lastTimestamp < minReportTime) { + // 取非重试队列和重试队列有堆积的 最小时间 + if ((!retry || (retry && diff > 0)) && lastTimestamp < minReportTime) { minReportTime = lastTimestamp; } } - - if(lastTimestamp > minReportTime) { + + if (lastTimestamp > minReportTime) { lastTimestamp = minReportTime; } return diffTotal; } + + public long getMaxOffset() { + return getMaxOffset(offsetMap); + } + + public long getRetryMaxOffset() { + return getMaxOffset(retryOffsetMap); + } + + private long getMaxOffset(Map offsetMap) { + long maxOffset = 0; + for(OffsetWrapper offsetWrapper : offsetMap.values()) { + if(maxOffset < offsetWrapper.getBrokerOffset()) { + maxOffset = offsetWrapper.getBrokerOffset(); + } + } + return maxOffset; + } + + public long getDlqMaxOffset() { + long maxOffset = 0; + for(TopicOffset topicOffset : dlqOffsetMap.values()) { + if(maxOffset < topicOffset.getMaxOffset()) { + maxOffset = topicOffset.getMaxOffset(); + } + } + return maxOffset; + } + public List getConsumeStatsList() { return consumeStatsList; } + public void setConsumeStatsList(List consumeStatsList) { this.consumeStatsList = consumeStatsList; } + + public Map getDlqOffsetMap() { + return dlqOffsetMap; + } + + public void setDlqOffsetMap(Map dlqOffsetMap) { + this.dlqOffsetMap = dlqOffsetMap; + } + public List getOwnerList() { return ownerList; } + public void setOwnerList(List ownerList) { this.ownerList = ownerList; } + + public String getDlqTopic() { + return dlqTopic; + } + + public void setDlqTopic(String dlqTopic) { + this.dlqTopic = dlqTopic; + } } diff --git a/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ResendMessageVO.java b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ResendMessageVO.java new file mode 100644 index 00000000..15318e41 --- /dev/null +++ b/mq-cloud/src/main/java/com/sohu/tv/mq/cloud/web/vo/ResendMessageVO.java @@ -0,0 +1,69 @@ +package com.sohu.tv.mq.cloud.web.vo; + +import java.util.List; + +import com.sohu.tv.mq.cloud.bo.AuditResendMessage; + +/** + * 重发消息统计 + * + * @author yongfeigao + * @date 2018年12月7日 + */ +public class ResendMessageVO { + // 总量 + private int total; + // 发送成功量 + private int success; + // 发送失败量 + private int failed; + // 状态更新失败量 + private int statusUpdatedFailed; + + private List msgList; + + public int getTotal() { + return total; + } + public void setTotal(int total) { + this.total = total; + } + public int getSuccess() { + return success; + } + public void incrSuccess() { + ++this.success; + } + public void setSuccess(int success) { + this.success = success; + } + public int getFailed() { + return failed; + } + public void setFailed(int failed) { + this.failed = failed; + } + public void incrFailed() { + ++this.failed; + } + public int getStatusUpdatedFailed() { + return statusUpdatedFailed; + } + public void setStatusUpdatedFailed(int statusUpdatedFailed) { + this.statusUpdatedFailed = statusUpdatedFailed; + } + + public void incrStatusUpdatedFailed() { + ++this.statusUpdatedFailed; + } + + public boolean sendAllOK() { + return total == success && statusUpdatedFailed == 0; + } + public List getMsgList() { + return msgList; + } + public void setMsgList(List msgList) { + this.msgList = msgList; + } +} diff --git a/mq-cloud/src/main/resources/application.yml b/mq-cloud/src/main/resources/application.yml index 078e80d3..0c888dad 100644 --- a/mq-cloud/src/main/resources/application.yml +++ b/mq-cloud/src/main/resources/application.yml @@ -56,4 +56,4 @@ mqcloud: clientArtifactId: mq-client-open producerClass: com.sohu.tv.mq.rocketmq.RocketMQProducer consumerClass: com.sohu.tv.mq.rocketmq.RocketMQConsumer - ticketKey: ticket #cas登录返回后的key,用户名密码可以忽略 + ticketKey: ticket #cas登录返回后的key,用户名密码可以忽略 \ No newline at end of file diff --git a/mq-cloud/src/main/resources/static/css/common.css b/mq-cloud/src/main/resources/static/css/common.css index 0bad29c7..11959d65 100644 --- a/mq-cloud/src/main/resources/static/css/common.css +++ b/mq-cloud/src/main/resources/static/css/common.css @@ -120,7 +120,7 @@ span.bell-dot{ } .show { - display: block; + display: block; } @@ -132,4 +132,27 @@ legend{padding:.5em;border:0;width:auto;margin-bottom:10px} .tableUpdate > tbody > tr > td, .tableUpdate > tfoot > tr > td { padding : 4px; +} + + +.tooltip{ + z-index: 1151 !important; +} + +.selected{ + background-color: #d9edf7; +} + +.table-striped > tbody > tr.selected:nth-child(odd) > td, +.table-striped > tbody > tr.selected:nth-child(odd) > th { + background-color: #d9edf7; +} + +.table-hover > tbody > tr.selected:hover > td, +.table-hover > tbody > tr.selected:hover > th { + background-color: #d9edf7; +} + +table.tr-pointer > tbody > tr:not(.no_more_data){ + cursor:pointer; } \ No newline at end of file diff --git a/mq-cloud/src/main/resources/static/img/intro/consumeRetry.png b/mq-cloud/src/main/resources/static/img/intro/consumeRetry.png new file mode 100644 index 0000000000000000000000000000000000000000..c8d950be103b4fa7dbb99e3f571bc62ef88efab9 GIT binary patch literal 23788 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 literal 0 HcmV?d00001 diff --git a/mq-cloud/src/main/resources/static/js/common.js b/mq-cloud/src/main/resources/static/js/common.js index 7a545a6e..17dc3e29 100644 --- a/mq-cloud/src/main/resources/static/js/common.js +++ b/mq-cloud/src/main/resources/static/js/common.js @@ -132,4 +132,51 @@ function grafanaClick(chart){ } return false; } +} + +/** + * 格式化时间戳 + * @param longDate + * @returns + */ +function formatDateYMDHM(longDate) { + if (!longDate) { + return ""; + } + var d = new Date(parseInt(longDate)); + var m = parseInt(d.getMonth(), 10) + 1; + var da = parseInt(d.getDate(), 10); + var hour = parseInt(d.getHours(), 10); + var min = parseInt(d.getMinutes(), 10); + var sec = parseInt(d.getSeconds(), 10); + if (m < 10) { + m = "0" + m; + } + if (da < 10) { + da = "0" + da; + } + if (hour < 10) { + hour = "0" + hour; + } + if (min < 10) { + min = "0" + min; + } + if (sec < 10) { + sec = "0" + sec; + } + return d.getFullYear() + "-" + m + "-" + da + " " + hour + ":" + min + ":" + sec; +} + + +/** + * 解析格式化字符串到时间戳 + * @param formatDate yyyy-MM-dd HH:mm:ss + * @returns + */ +function parseDate(formatDate) { + if (!formatDate) { + return ""; + } + var date = Date.parse(formatDate.replace(/-/g,'/')); + return date; } \ No newline at end of file diff --git a/mq-cloud/src/main/resources/static/resources/multiselect/multiselect.js b/mq-cloud/src/main/resources/static/resources/multiselect/multiselect.js new file mode 100644 index 00000000..a0367887 --- /dev/null +++ b/mq-cloud/src/main/resources/static/resources/multiselect/multiselect.js @@ -0,0 +1,89 @@ +/* + * Jquery Multiselect插件 中文叫列表多选插件 + * 使用例子: + * $('table').multiSelect({ + * actcls: 'active', + * selector: 'tbody tr', + * callback: false + * }); + */ +(function ($) { + $.fn.multiSelect = function (options) { + $.fn.multiSelect.init($(this), options); + }; + + $.extend($.fn.multiSelect, { + defaults: { + actcls: 'selected', //选中样式 + selector: 'tr', //选择的行元素 + except: ['tbody'], //选中后不去除多选效果的元素队列 + statics: ['.static'], //被排除行元素条件 + callback: false //选中回调 + }, + first: null, //按shift时,用于记住第一个点击的item + last: null, //最后点击的item + init: function (scope, options) { + this.scope = scope; + this.options = $.extend({}, this.defaults, options); + this.initEvent(); + }, + checkStatics: function (dom) { + for (var i in this.options.statics) { + if (dom.is(this.options.statics[i])) { + return true; + } + } + }, + initEvent: function () { + var self = this, + scope = self.scope, + options = self.options, + callback = options.callback, + actcls = options.actcls; + + scope.on('click.mSelect', options.selector, function (e) { + if (!e.shiftKey && self.checkStatics($(this))) { + return; + } + + if ($(this).hasClass(actcls)) { + $(this).removeClass(actcls); + } else { + $(this).addClass(actcls); + } + + if (e.shiftKey && self.last) { + if (!self.first) { + self.first = self.last; + } + var start = self.first.index(); + var end = $(this).index(); + if (start > end) { + var temp = start; + start = end; + end = temp; + } + $(options.selector, scope).removeClass(actcls).slice(start, end + 1).each(function () { + if (!self.checkStatics($(this))) { + $(this).addClass(actcls); + } + }); + window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty(); + } else if (!e.ctrlKey && !e.metaKey) { + $(this).siblings().removeClass(actcls); + } + self.last = $(this); + $.isFunction(callback) && callback($(options.selector + '.' + actcls, scope)); + }); + + /** + * 清楚shift按住的状态 + */ + $(document).on('keyup.mSelect', function (e) { + if (e.keyCode == 16) { + self.first = null; + } + }); + } + }); +})(jQuery); \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/audit/addTopic.html b/mq-cloud/src/main/resources/templates/admin/audit/addTopic.html index 40199a06..7a74028a 100644 --- a/mq-cloud/src/main/resources/templates/admin/audit/addTopic.html +++ b/mq-cloud/src/main/resources/templates/admin/audit/addTopic.html @@ -36,7 +36,7 @@ checked>全局有序 -
+
@@ -50,7 +50,7 @@
- 条/秒 + 条/秒
diff --git a/mq-cloud/src/main/resources/templates/admin/audit/list.html b/mq-cloud/src/main/resources/templates/admin/audit/list.html index b6e07c80..5089f1b3 100644 --- a/mq-cloud/src/main/resources/templates/admin/audit/list.html +++ b/mq-cloud/src/main/resources/templates/admin/audit/list.html @@ -94,8 +94,8 @@ - - + + diff --git a/mq-cloud/src/main/resources/templates/admin/audit/resendMessage.html b/mq-cloud/src/main/resources/templates/admin/audit/resendMessage.html new file mode 100644 index 00000000..0811684b --- /dev/null +++ b/mq-cloud/src/main/resources/templates/admin/audit/resendMessage.html @@ -0,0 +1,164 @@ +<#if response.notOK> + +<#else> + + + + + \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/cluster/list.html b/mq-cloud/src/main/resources/templates/admin/cluster/list.html index 629731b3..bcede51c 100644 --- a/mq-cloud/src/main/resources/templates/admin/cluster/list.html +++ b/mq-cloud/src/main/resources/templates/admin/cluster/list.html @@ -24,7 +24,7 @@
<#if response.result.hasNameServer>
- +
@@ -873,7 +873,7 @@ }else{ toastr.error("抓取失败!"+data.message); } - }, 'json'); + }, 'json'); } \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/monitor/consumer.html b/mq-cloud/src/main/resources/templates/admin/monitor/consumer.html index 4901604a..978c73d0 100644 --- a/mq-cloud/src/main/resources/templates/admin/monitor/consumer.html +++ b/mq-cloud/src/main/resources/templates/admin/monitor/consumer.html @@ -1,4 +1,4 @@ -
+
全局阈值:
@@ -429,4 +429,4 @@ $("input[name='ignoreWarn'][value=0]").prop("checked",true); $("#updateAlarmThresholdModal").modal('show'); } - + \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/nameserver/list.html b/mq-cloud/src/main/resources/templates/admin/nameserver/list.html index afa8d9b2..7eecb1cf 100644 --- a/mq-cloud/src/main/resources/templates/admin/nameserver/list.html +++ b/mq-cloud/src/main/resources/templates/admin/nameserver/list.html @@ -42,7 +42,7 @@ 序号 addr - 创建时间 + 创建时间 监控状态 监控时间 操作 @@ -679,5 +679,5 @@ enable("associateNameSrvBtn"); } }, 'json'); -} +} \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/server/list.html b/mq-cloud/src/main/resources/templates/admin/server/list.html index 14c39abb..02a4afa2 100644 --- a/mq-cloud/src/main/resources/templates/admin/server/list.html +++ b/mq-cloud/src/main/resources/templates/admin/server/list.html @@ -2,7 +2,7 @@
- +
@@ -605,5 +605,6 @@ } } return true; -} +} + \ No newline at end of file diff --git a/mq-cloud/src/main/resources/templates/admin/user/list.html b/mq-cloud/src/main/resources/templates/admin/user/list.html index 97393d82..97010bf7 100644 --- a/mq-cloud/src/main/resources/templates/admin/user/list.html +++ b/mq-cloud/src/main/resources/templates/admin/user/list.html @@ -1,7 +1,7 @@
- +
@@ -228,5 +228,5 @@ enable("updateUserPasswordBtn"); } }, 'json'); -} +} diff --git a/mq-cloud/src/main/resources/templates/consumer/progress.html b/mq-cloud/src/main/resources/templates/consumer/progress.html index 5eeeafe1..9a2428a1 100644 --- a/mq-cloud/src/main/resources/templates/consumer/progress.html +++ b/mq-cloud/src/main/resources/templates/consumer/progress.html @@ -81,14 +81,103 @@ <#if consumerProgressVO.offsetMap??>