├── .coveralls.yml ├── qrcode.jpg ├── mq-test ├── src │ └── test │ │ └── java │ │ └── com │ │ └── github │ │ └── houbb │ │ └── mq │ │ └── test │ │ ├── package-info.java │ │ ├── producer │ │ ├── package-info.java │ │ ├── ProducerMain.java │ │ └── ProducerMainBatch.java │ │ ├── broker │ │ └── BrokerMain.java │ │ └── consumer │ │ ├── ConsumerMain.java │ │ └── ConsumerPullMain.java └── pom.xml ├── mq-broker ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── houbb │ │ └── mq │ │ └── broker │ │ ├── package-info.java │ │ ├── support │ │ ├── package-info.java │ │ ├── api │ │ │ ├── package-info.java │ │ │ ├── LocalBrokerProducerService.java │ │ │ └── LocalBrokerConsumerService.java │ │ ├── push │ │ │ ├── IBrokerPushService.java │ │ │ ├── BrokerPushContext.java │ │ │ └── BrokerPushService.java │ │ ├── persist │ │ │ ├── MqBrokerPersistPutContext.java │ │ │ ├── IMqBrokerPersist.java │ │ │ └── LocalMqBrokerPersist.java │ │ └── valid │ │ │ ├── BrokerRegisterValidService.java │ │ │ └── IBrokerRegisterValidService.java │ │ ├── dto │ │ ├── producer │ │ │ └── package-info.java │ │ ├── ServiceEntry.java │ │ ├── BrokerServiceEntryChannel.java │ │ ├── persist │ │ │ ├── MqMessagePersistPull.java │ │ │ ├── MqMessagePersistTake.java │ │ │ ├── MqMessagePersistPut.java │ │ │ └── MqMessagePersistPutBatch.java │ │ ├── ChannelGroupNameDto.java │ │ ├── consumer │ │ │ ├── ConsumerSubscribeReq.java │ │ │ ├── ConsumerUnSubscribeReq.java │ │ │ └── ConsumerSubscribeBo.java │ │ └── BrokerRegisterReq.java │ │ ├── api │ │ ├── IMqBroker.java │ │ ├── IBrokerProducerService.java │ │ └── IBrokerConsumerService.java │ │ ├── constant │ │ ├── BrokerConst.java │ │ └── BrokerRespCode.java │ │ ├── resp │ │ └── MqBrokerRespCode.java │ │ ├── utils │ │ └── InnerChannelUtils.java │ │ └── core │ │ └── MqBroker.java └── pom.xml ├── mq-common ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── houbb │ │ └── mq │ │ └── common │ │ ├── package-info.java │ │ ├── dto │ │ ├── package-info.java │ │ ├── req │ │ │ ├── MqMessageBatchReq.java │ │ │ ├── MqConsumerUpdateStatusBatchReq.java │ │ │ ├── MqCommonReq.java │ │ │ ├── MqHeartBeatReq.java │ │ │ ├── MqConsumerUpdateStatusReq.java │ │ │ ├── component │ │ │ │ └── MqConsumerUpdateStatusDto.java │ │ │ ├── MqConsumerPullReq.java │ │ │ └── MqMessage.java │ │ └── resp │ │ │ ├── MqConsumerResultResp.java │ │ │ ├── MqConsumerPullResp.java │ │ │ └── MqCommonResp.java │ │ ├── support │ │ ├── package-info.java │ │ ├── hook │ │ │ ├── RpcShutdownHook.java │ │ │ ├── ShutdownHooks.java │ │ │ ├── AbstractShutdownHook.java │ │ │ └── DefaultShutdownHook.java │ │ ├── status │ │ │ ├── StatusManager.java │ │ │ └── IStatusManager.java │ │ └── invoke │ │ │ ├── IInvokeService.java │ │ │ └── impl │ │ │ ├── TimeoutCheckThread.java │ │ │ └── InvokeService.java │ │ ├── api │ │ └── Destroyable.java │ │ ├── constant │ │ ├── ConsumerTypeConst.java │ │ ├── MessageStatusConst.java │ │ └── MethodType.java │ │ ├── rpc │ │ ├── RpcChannelFuture.java │ │ ├── RpcAddress.java │ │ └── RpcMessageDto.java │ │ ├── resp │ │ ├── ConsumerStatus.java │ │ ├── MqCommonRespCode.java │ │ └── MqException.java │ │ └── util │ │ ├── ChannelUtil.java │ │ ├── InnerAddressUtils.java │ │ ├── DelimiterUtil.java │ │ ├── RandomUtils.java │ │ └── ChannelFutureUtils.java └── pom.xml ├── mq-consumer ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── houbb │ │ └── mq │ │ └── consumer │ │ ├── package-info.java │ │ ├── api │ │ ├── package-info.java │ │ ├── IMqConsumerListenerContext.java │ │ ├── IMqConsumerListener.java │ │ └── IMqConsumer.java │ │ ├── support │ │ ├── package-info.java │ │ ├── listener │ │ │ ├── MqConsumerListenerContext.java │ │ │ ├── IMqListenerService.java │ │ │ └── MqListenerService.java │ │ └── broker │ │ │ ├── IConsumerBrokerService.java │ │ │ └── ConsumerBrokerConfig.java │ │ ├── constant │ │ ├── ConsumerConst.java │ │ └── ConsumerRespCode.java │ │ ├── dto │ │ └── MqTopicTagDto.java │ │ ├── handler │ │ └── MqConsumerHandler.java │ │ └── core │ │ ├── MqConsumerPull.java │ │ └── MqConsumerPush.java └── pom.xml ├── mq-producer ├── src │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── houbb │ │ └── mq │ │ └── producer │ │ ├── package-info.java │ │ ├── support │ │ ├── package-info.java │ │ └── broker │ │ │ ├── IProducerBrokerService.java │ │ │ └── ProducerBrokerConfig.java │ │ ├── constant │ │ ├── package-info.java │ │ ├── ProducerConst.java │ │ ├── SendStatus.java │ │ └── ProducerRespCode.java │ │ ├── dto │ │ ├── SendResult.java │ │ └── SendBatchResult.java │ │ ├── api │ │ └── IMqProducer.java │ │ ├── handler │ │ └── MqProducerHandler.java │ │ └── core │ │ └── MqProducer.java └── pom.xml ├── cgit.sh ├── cgit.bat ├── .gitignore ├── release_rm.sh ├── .travis.yml ├── release.sh ├── release.bat ├── CHANGELOG.md └── README.md /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci -------------------------------------------------------------------------------- /qrcode.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houbb/mq/HEAD/qrcode.jpg -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.test; 6 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.broker; 6 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.common; 6 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.common.dto; 6 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.consumer; 6 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.producer; 6 | -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/producer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.test.producer; 6 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.broker.support; 6 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.common.support; 6 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/api/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.consumer.api; 6 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.consumer.support; 6 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/support/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.producer.support; 6 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/producer/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.broker.dto.producer; 6 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/api/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.broker.support.api; 6 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/constant/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * @author binbin.hou 3 | * @since 1.0.0 4 | */ 5 | package com.github.houbb.mq.producer.constant; 6 | -------------------------------------------------------------------------------- /cgit.sh: -------------------------------------------------------------------------------- 1 | # 提交 2 | 3 | git add . 4 | git commit -m "[Feature] add for new" 5 | git push 6 | git status 7 | 8 | # 1. 赋值权限: chmod +x ./cgit.sh 9 | # 2. 执行: ./cgit.sh 10 | # Last Update Time: 2018-11-21 21:55:38 11 | # Author: houbb -------------------------------------------------------------------------------- /cgit.bat: -------------------------------------------------------------------------------- 1 | :: 用于提交当前变更(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-11-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 .\cgit.bat 5 | 6 | git add . 7 | git commit -m "[Feature] add for new" 8 | git push 9 | git status 10 | 11 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/api/IMqBroker.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.api; 2 | 3 | /** 4 | * 消息持久化 5 | * 6 | * @author binbin.hou 7 | * @since 0.0.3 8 | */ 9 | public interface IMqBroker { 10 | } 11 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/api/IMqConsumerListenerContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.api; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public interface IMqConsumerListenerContext { 8 | 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/api/Destroyable.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.api; 2 | 3 | /** 4 | * 可销毁的 5 | * @author binbin.hou 6 | * @since 0.0.5 7 | */ 8 | public interface Destroyable { 9 | 10 | /** 11 | * 销毁方法 12 | * @since 0.0.8 13 | */ 14 | void destroyAll(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/hook/RpcShutdownHook.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.hook; 2 | 3 | /** 4 | * rpc 关闭 hook 5 | * (1)可以添加对应的 hook 管理类 6 | * @since 0.0.5 7 | */ 8 | public interface RpcShutdownHook { 9 | 10 | /** 11 | * 钩子函数实现 12 | * @since 0.0.5 13 | */ 14 | void hook(); 15 | 16 | } 17 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/listener/MqConsumerListenerContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.support.listener; 2 | 3 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqConsumerListenerContext implements IMqConsumerListenerContext { 10 | } 11 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/push/IBrokerPushService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.push; 2 | 3 | /** 4 | * 消息推送服务 5 | * 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public interface IBrokerPushService { 10 | 11 | /** 12 | * 异步推送 13 | * @param context 消息 14 | */ 15 | void asyncPush(final BrokerPushContext context); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/constant/ProducerConst.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public final class ProducerConst { 8 | 9 | private ProducerConst(){} 10 | 11 | /** 12 | * 默认分组名称 13 | */ 14 | public static final String DEFAULT_GROUP_NAME = "P_DEFAULT_GROUP_NAME"; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/broker/BrokerMain.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.test.broker; 2 | 3 | import com.github.houbb.mq.broker.core.MqBroker; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class BrokerMain { 10 | 11 | public static void main(String[] args) { 12 | MqBroker broker = new MqBroker(); 13 | broker.start(); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/constant/BrokerConst.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.constant; 2 | 3 | /** 4 | * 中间人常量 5 | * 6 | * @author binbin.hou 7 | * @since 0.0.3 8 | */ 9 | public final class BrokerConst { 10 | 11 | private BrokerConst(){} 12 | 13 | /** 14 | * 默认端口 15 | * @since 0.0.3 16 | */ 17 | public static final int DEFAULT_PORT = 9999; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/persist/MqBrokerPersistPutContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.persist; 2 | 3 | import com.github.houbb.mq.common.rpc.RpcAddress; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqBrokerPersistPutContext { 10 | 11 | /** 12 | * 发送者地址信息 13 | */ 14 | private RpcAddress rpcAddress; 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # maven ignore 2 | target/ 3 | *.jar 4 | *.war 5 | *.zip 6 | *.tar 7 | *.tar.gz 8 | 9 | # eclipse ignore 10 | .settings/ 11 | .project 12 | .classpath 13 | 14 | # idea ignore 15 | .idea/ 16 | *.ipr 17 | *.iml 18 | *.iws 19 | 20 | # temp ignore 21 | *.log 22 | *.cache 23 | *.diff 24 | *.patch 25 | *.tmp 26 | *.java~ 27 | *.properties~ 28 | *.xml~ 29 | 30 | # system ignore 31 | .DS_Store 32 | Thumbs.db 33 | 34 | # excel 35 | *.xls 36 | *.xlsx 37 | 38 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/constant/ConsumerTypeConst.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 0.0.9 6 | */ 7 | public final class ConsumerTypeConst { 8 | 9 | /** 10 | * 推送模式 11 | * @since 0.0.9 12 | */ 13 | public static final String PUSH = "PUSH"; 14 | 15 | /** 16 | * 拉取模式 17 | * @since 0.0.9 18 | */ 19 | public static final String PULL = "PULL"; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/constant/ConsumerConst.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public final class ConsumerConst { 8 | 9 | private ConsumerConst(){} 10 | 11 | /** 12 | * 默认分组名称 13 | */ 14 | public static final String DEFAULT_GROUP_NAME = "C_DEFAULT_GROUP_NAME"; 15 | 16 | /** 17 | * 默认端口号 18 | */ 19 | public static final int DEFAULT_PORT = 9527; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqMessageBatchReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqMessageBatchReq extends MqCommonReq { 10 | 11 | private List mqMessageList; 12 | 13 | public List getMqMessageList() { 14 | return mqMessageList; 15 | } 16 | 17 | public void setMqMessageList(List mqMessageList) { 18 | this.mqMessageList = mqMessageList; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/valid/BrokerRegisterValidService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.valid; 2 | 3 | import com.github.houbb.mq.broker.dto.BrokerRegisterReq; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 0.1.4 8 | */ 9 | public class BrokerRegisterValidService implements IBrokerRegisterValidService { 10 | 11 | @Override 12 | public boolean producerValid(BrokerRegisterReq registerReq) { 13 | return true; 14 | } 15 | 16 | @Override 17 | public boolean consumerValid(BrokerRegisterReq registerReq) { 18 | return true; 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/api/IMqConsumerListener.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.api; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | import com.github.houbb.mq.common.resp.ConsumerStatus; 5 | 6 | /** 7 | * @author binbin.hou 8 | * @since 1.0.0 9 | */ 10 | public interface IMqConsumerListener { 11 | 12 | 13 | /** 14 | * 消费 15 | * @param mqMessage 消息体 16 | * @param context 上下文 17 | * @return 结果 18 | */ 19 | ConsumerStatus consumer(final MqMessage mqMessage, 20 | final IMqConsumerListenerContext context); 21 | 22 | } 23 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/resp/MqConsumerResultResp.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.resp; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 消息消费结果 7 | * @author binbin.hou 8 | * @since 0.0.3 9 | */ 10 | public class MqConsumerResultResp extends MqCommonResp { 11 | 12 | /** 13 | * 消费状态 14 | * @since 0.0.3 15 | */ 16 | private String consumerStatus; 17 | 18 | public String getConsumerStatus() { 19 | return consumerStatus; 20 | } 21 | 22 | public void setConsumerStatus(String consumerStatus) { 23 | this.consumerStatus = consumerStatus; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/rpc/RpcChannelFuture.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.rpc; 2 | 3 | import io.netty.channel.ChannelFuture; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 0.0.3 8 | */ 9 | public class RpcChannelFuture extends RpcAddress { 10 | 11 | /** 12 | * channel future 信息 13 | * @since 0.0.3 14 | */ 15 | private ChannelFuture channelFuture; 16 | 17 | public ChannelFuture getChannelFuture() { 18 | return channelFuture; 19 | } 20 | 21 | public void setChannelFuture(ChannelFuture channelFuture) { 22 | this.channelFuture = channelFuture; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/constant/SendStatus.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public enum SendStatus { 8 | SUCCESS("SUCCESS", "发送成功"), 9 | FAILED("FAILED", "发送失败"), 10 | ; 11 | 12 | private final String code; 13 | private final String desc; 14 | 15 | SendStatus(String code, String desc) { 16 | this.code = code; 17 | this.desc = desc; 18 | } 19 | 20 | public String getCode() { 21 | return code; 22 | } 23 | 24 | public String getDesc() { 25 | return desc; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/api/IMqConsumer.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.api; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public interface IMqConsumer { 8 | 9 | /** 10 | * 订阅 11 | * @param topicName topic 名称 12 | * @param tagRegex 标签正则 13 | */ 14 | void subscribe(String topicName, String tagRegex); 15 | 16 | /** 17 | * 取消订阅 18 | * @param topicName topic 名称 19 | * @param tagRegex 标签正则 20 | */ 21 | void unSubscribe(String topicName, String tagRegex); 22 | 23 | /** 24 | * 注册监听器 25 | * @param listener 监听器 26 | */ 27 | void registerListener(final IMqConsumerListener listener); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqConsumerUpdateStatusBatchReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | import com.github.houbb.mq.common.dto.req.component.MqConsumerUpdateStatusDto; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 批量更新状态入参 9 | * 10 | * @author binbin.hou 11 | * @since 0.1.3 12 | */ 13 | public class MqConsumerUpdateStatusBatchReq extends MqCommonReq { 14 | 15 | private List statusList; 16 | 17 | public List getStatusList() { 18 | return statusList; 19 | } 20 | 21 | public void setStatusList(List statusList) { 22 | this.statusList = statusList; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/valid/IBrokerRegisterValidService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.valid; 2 | 3 | import com.github.houbb.mq.broker.dto.BrokerRegisterReq; 4 | 5 | /** 6 | * 注册验证方法 7 | * 8 | * @author binbin.hou 9 | * @since 0.1.4 10 | */ 11 | public interface IBrokerRegisterValidService { 12 | 13 | /** 14 | * 生产者验证合法性 15 | * @param registerReq 注册信息 16 | * @return 结果 17 | * @since 0.1.4 18 | */ 19 | boolean producerValid(BrokerRegisterReq registerReq); 20 | 21 | /** 22 | * 消费者验证合法性 23 | * @param registerReq 注册信息 24 | * @return 结果 25 | * @since 0.1.4 26 | */ 27 | boolean consumerValid(BrokerRegisterReq registerReq); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /mq-broker/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mq 7 | com.github.houbb 8 | 0.1.4 9 | 10 | 4.0.0 11 | 12 | mq-broker 13 | 14 | 15 | 16 | ${project.groupId} 17 | mq-common 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/ServiceEntry.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto; 2 | 3 | import com.github.houbb.mq.common.rpc.RpcAddress; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class ServiceEntry extends RpcAddress { 10 | 11 | /** 12 | * 分组名称 13 | */ 14 | private String groupName; 15 | 16 | public String getGroupName() { 17 | return groupName; 18 | } 19 | 20 | public void setGroupName(String groupName) { 21 | this.groupName = groupName; 22 | } 23 | 24 | @Override 25 | public String toString() { 26 | return "ServiceEntry{" + 27 | "groupName='" + groupName + '\'' + 28 | "} " + super.toString(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/hook/ShutdownHooks.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.hook; 2 | 3 | /** 4 | *

project: rpc-ShutdownHooks

5 | *

create on 2019/10/30 21:36

6 | * 7 | * @author Administrator 8 | * @since 0.0.5 9 | */ 10 | public final class ShutdownHooks { 11 | 12 | private ShutdownHooks(){} 13 | 14 | /** 15 | * 添加 rpc shutdown hook 16 | * @param rpcShutdownHook 钩子函数实现 17 | * @since 0.0.5 18 | */ 19 | public static void rpcShutdownHook(final RpcShutdownHook rpcShutdownHook) { 20 | Runtime.getRuntime().addShutdownHook(new Thread() { 21 | @Override 22 | public void run() { 23 | rpcShutdownHook.hook(); 24 | } 25 | }); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/constant/ProducerRespCode.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.constant; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public enum ProducerRespCode implements RespCode { 10 | 11 | RPC_INIT_FAILED("P00001", "生产者启动失败"), 12 | MSG_SEND_FAILED("P00002", "生产者消息发送失败"); 13 | 14 | private final String code; 15 | private final String msg; 16 | 17 | ProducerRespCode(String code, String msg) { 18 | this.code = code; 19 | this.msg = msg; 20 | } 21 | 22 | @Override 23 | public String getCode() { 24 | return code; 25 | } 26 | 27 | @Override 28 | public String getMsg() { 29 | return msg; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/constant/BrokerRespCode.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.constant; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public enum BrokerRespCode implements RespCode { 10 | 11 | RPC_INIT_FAILED("B00001", "中间人启动失败"), 12 | MSG_PUSH_FAILED("B00002", "中间人消息推送失败"), 13 | ; 14 | 15 | private final String code; 16 | private final String msg; 17 | 18 | BrokerRespCode(String code, String msg) { 19 | this.code = code; 20 | this.msg = msg; 21 | } 22 | 23 | @Override 24 | public String getCode() { 25 | return code; 26 | } 27 | 28 | @Override 29 | public String getMsg() { 30 | return msg; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqCommonReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqCommonReq implements Serializable { 10 | 11 | /** 12 | * 请求标识 13 | */ 14 | private String traceId; 15 | 16 | /** 17 | * 方法类型 18 | */ 19 | private String methodType; 20 | 21 | public String getTraceId() { 22 | return traceId; 23 | } 24 | 25 | public void setTraceId(String traceId) { 26 | this.traceId = traceId; 27 | } 28 | 29 | public String getMethodType() { 30 | return methodType; 31 | } 32 | 33 | public void setMethodType(String methodType) { 34 | this.methodType = methodType; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/BrokerServiceEntryChannel.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 0.0.3 8 | */ 9 | public class BrokerServiceEntryChannel extends ServiceEntry { 10 | 11 | private Channel channel; 12 | 13 | /** 14 | * 最后访问时间 15 | * @since 0.0.6 16 | */ 17 | private long lastAccessTime; 18 | 19 | public Channel getChannel() { 20 | return channel; 21 | } 22 | 23 | public void setChannel(Channel channel) { 24 | this.channel = channel; 25 | } 26 | 27 | public long getLastAccessTime() { 28 | return lastAccessTime; 29 | } 30 | 31 | public void setLastAccessTime(long lastAccessTime) { 32 | this.lastAccessTime = lastAccessTime; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/resp/ConsumerStatus.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.resp; 2 | 3 | import com.github.houbb.mq.common.constant.MessageStatusConst; 4 | 5 | /** 6 | * 消费状态 7 | * 8 | * @author binbin.hou 9 | * @since 1.0.0 10 | */ 11 | public enum ConsumerStatus { 12 | SUCCESS(MessageStatusConst.CONSUMER_SUCCESS, "消费成功"), 13 | FAILED(MessageStatusConst.CONSUMER_FAILED, "消费失败"), 14 | CONSUMER_LATER(MessageStatusConst.CONSUMER_LATER, "稍后消费"), 15 | ; 16 | 17 | private final String code; 18 | private final String desc; 19 | 20 | ConsumerStatus(String code, String desc) { 21 | this.code = code; 22 | this.desc = desc; 23 | } 24 | 25 | public String getCode() { 26 | return code; 27 | } 28 | 29 | public String getDesc() { 30 | return desc; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/resp/MqConsumerPullResp.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.resp; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | 5 | import java.io.Serializable; 6 | import java.util.List; 7 | 8 | /** 9 | * 消费者拉取 10 | * @author binbin.hou 11 | * @since 1.0.0 12 | */ 13 | public class MqConsumerPullResp extends MqCommonResp { 14 | 15 | /** 16 | * 消息列表 17 | */ 18 | private List list; 19 | 20 | public List getList() { 21 | return list; 22 | } 23 | 24 | public void setList(List list) { 25 | this.list = list; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return "MqConsumerPullResp{" + 31 | "list=" + list + 32 | "} " + super.toString(); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/status/StatusManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.status; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 0.0.3 6 | */ 7 | public class StatusManager implements IStatusManager { 8 | 9 | private boolean status; 10 | 11 | private boolean initFailed; 12 | 13 | @Override 14 | public boolean status() { 15 | return this.status; 16 | } 17 | 18 | @Override 19 | public IStatusManager status(boolean status) { 20 | this.status = status; 21 | 22 | return this; 23 | } 24 | 25 | @Override 26 | public boolean initFailed() { 27 | return initFailed; 28 | } 29 | 30 | @Override 31 | public StatusManager initFailed(boolean initFailed) { 32 | this.initFailed = initFailed; 33 | return this; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/util/ChannelUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.util; 2 | 3 | import io.netty.channel.Channel; 4 | import io.netty.channel.ChannelHandlerContext; 5 | 6 | /** 7 | * channel 工具类 8 | * @author binbin.hou 9 | * @since 1.0.0 10 | */ 11 | public class ChannelUtil { 12 | 13 | private ChannelUtil(){} 14 | 15 | /** 16 | * 获取 channel 标识 17 | * @param channel 管道 18 | * @return 结果 19 | * @since 1.0.0 20 | */ 21 | public static String getChannelId(Channel channel) { 22 | return channel.id().asLongText(); 23 | } 24 | 25 | /** 26 | * 获取 channel 标识 27 | * @param ctx 管道 28 | * @return 结果 29 | * @since 1.0.0 30 | */ 31 | public static String getChannelId(ChannelHandlerContext ctx) { 32 | return getChannelId(ctx.channel()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/hook/AbstractShutdownHook.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.hook; 2 | 3 | import com.github.houbb.log.integration.core.Log; 4 | import com.github.houbb.log.integration.core.LogFactory; 5 | 6 | /** 7 | * rpc 关闭 hook 8 | * (1)可以添加对应的 hook 管理类 9 | * 10 | * @since 0.0.5 11 | */ 12 | public abstract class AbstractShutdownHook implements RpcShutdownHook { 13 | 14 | /** 15 | * AbstractShutdownHook logger 16 | */ 17 | private static final Log LOG = LogFactory.getLog(AbstractShutdownHook.class); 18 | 19 | @Override 20 | public void hook() { 21 | LOG.info("[Shutdown Hook] start"); 22 | this.doHook(); 23 | LOG.info("[Shutdown Hook] end"); 24 | } 25 | 26 | /** 27 | * 执行 hook 操作 28 | * @since 0.0.5 29 | */ 30 | protected abstract void doHook(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/resp/MqCommonResp.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.resp; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqCommonResp implements Serializable { 10 | 11 | /** 12 | * 响应编码 13 | * @since 1.0.0 14 | */ 15 | private String respCode; 16 | 17 | /** 18 | * 响应消息 19 | * @since 1.0.0 20 | */ 21 | private String respMessage; 22 | 23 | public String getRespCode() { 24 | return respCode; 25 | } 26 | 27 | public void setRespCode(String respCode) { 28 | this.respCode = respCode; 29 | } 30 | 31 | public String getRespMessage() { 32 | return respMessage; 33 | } 34 | 35 | public void setRespMessage(String respMessage) { 36 | this.respMessage = respMessage; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mq-consumer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mq 7 | com.github.houbb 8 | 0.1.4 9 | 10 | 4.0.0 11 | 12 | mq-consumer 13 | 14 | 15 | 16 | com.github.houbb 17 | mq-common 18 | 19 | 20 | 21 | ${project.groupId} 22 | mq-broker 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /mq-producer/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mq 7 | com.github.houbb 8 | 0.1.4 9 | 10 | 4.0.0 11 | 12 | mq-producer 13 | 14 | 15 | 16 | com.github.houbb 17 | mq-common 18 | 19 | 20 | 21 | ${project.groupId} 22 | mq-broker 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/listener/IMqListenerService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.support.listener; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | import com.github.houbb.mq.common.resp.ConsumerStatus; 5 | import com.github.houbb.mq.consumer.api.IMqConsumerListener; 6 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 7 | 8 | /** 9 | * @author binbin.hou 10 | * @since 0.0.3 11 | */ 12 | public interface IMqListenerService { 13 | 14 | /** 15 | * 注册 16 | * @param listener 监听器 17 | * @since 0.0.3 18 | */ 19 | void register(final IMqConsumerListener listener); 20 | 21 | /** 22 | * 消费消息 23 | * @param mqMessage 消息 24 | * @param context 上下文 25 | * @return 结果 26 | * @since 0.0.3 27 | */ 28 | ConsumerStatus consumer(final MqMessage mqMessage, 29 | final IMqConsumerListenerContext context); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/status/IStatusManager.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.status; 2 | 3 | /** 4 | * 状态管理 5 | * 6 | *

project: rpc-StatusManager

7 | *

create on 2019/10/30 20:48

8 | * 9 | * @author Administrator 10 | * @since 0.1.3 11 | */ 12 | public interface IStatusManager { 13 | 14 | /** 15 | * 获取状态编码 16 | * @return 状态编码 17 | * @since 0.1.3 18 | */ 19 | boolean status(); 20 | 21 | /** 22 | * 设置状态编码 23 | * @param status 编码 24 | * @return this 25 | * @since 0.1.3 26 | */ 27 | IStatusManager status(final boolean status); 28 | 29 | /** 30 | * 初始化失败 31 | * @return 初始化失败 32 | * @since 0.1.4 33 | */ 34 | boolean initFailed(); 35 | 36 | /** 37 | * 设置初始化失败 38 | * @param failed 编码 39 | * @return this 40 | * @since 0.1.4 41 | */ 42 | IStatusManager initFailed(final boolean failed); 43 | 44 | } 45 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/resp/MqBrokerRespCode.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.resp; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 0.1.4 8 | */ 9 | public enum MqBrokerRespCode implements RespCode { 10 | 11 | B_NOT_SUPPORT_METHOD("B00001", "暂时不支持的方法类型"), 12 | 13 | P_REGISTER_VALID_FAILED("BP0001", "生产者注册验证失败"), 14 | P_REGISTER_CHANNEL_NOT_VALID("BP0002", "生产者 channel 不合法"), 15 | 16 | C_REGISTER_VALID_FAILED("BC0001", "消费者注册验证失败"), 17 | C_REGISTER_CHANNEL_NOT_VALID("BC0002", "消费者 channel 不合法"), 18 | ; 19 | 20 | private final String code; 21 | private final String msg; 22 | 23 | MqBrokerRespCode(String code, String msg) { 24 | this.code = code; 25 | this.msg = msg; 26 | } 27 | 28 | public String getCode() { 29 | return code; 30 | } 31 | 32 | public String getMsg() { 33 | return msg; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/constant/ConsumerRespCode.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.constant; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public enum ConsumerRespCode implements RespCode { 10 | 11 | RPC_INIT_FAILED("C00001", "消费者启动失败"), 12 | SUBSCRIBE_FAILED("C00002", "消费者注册失败"), 13 | UN_SUBSCRIBE_FAILED("C00003", "消费者注销失败"), 14 | CONSUMER_STATUS_ACK_FAILED("C00004", "消费者状态回执失败"), 15 | CONSUMER_STATUS_ACK_BATCH_FAILED("C00005", "消费者状态批量回执失败"), 16 | ; 17 | 18 | private final String code; 19 | private final String msg; 20 | 21 | ConsumerRespCode(String code, String msg) { 22 | this.code = code; 23 | this.msg = msg; 24 | } 25 | 26 | @Override 27 | public String getCode() { 28 | return code; 29 | } 30 | 31 | @Override 32 | public String getMsg() { 33 | return msg; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/persist/MqMessagePersistPull.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.persist; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqConsumerPullReq; 4 | import com.github.houbb.mq.common.dto.req.MqMessage; 5 | import com.github.houbb.mq.common.rpc.RpcAddress; 6 | 7 | /** 8 | * @author binbin.hou 9 | * @since 0.0.3 10 | */ 11 | public class MqMessagePersistPull { 12 | 13 | /** 14 | * 消息体 15 | */ 16 | private MqConsumerPullReq pullReq; 17 | 18 | /** 19 | * 地址信息 20 | */ 21 | private RpcAddress rpcAddress; 22 | 23 | public MqConsumerPullReq getPullReq() { 24 | return pullReq; 25 | } 26 | 27 | public void setPullReq(MqConsumerPullReq pullReq) { 28 | this.pullReq = pullReq; 29 | } 30 | 31 | public RpcAddress getRpcAddress() { 32 | return rpcAddress; 33 | } 34 | 35 | public void setRpcAddress(RpcAddress rpcAddress) { 36 | this.rpcAddress = rpcAddress; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqHeartBeatReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public class MqHeartBeatReq extends MqCommonReq { 8 | 9 | /** 10 | * address 信息 11 | * @since 0.0.6 12 | */ 13 | private String address; 14 | 15 | /** 16 | * 端口号 17 | * @since 0.0.6 18 | */ 19 | private int port; 20 | 21 | /** 22 | * 请求时间 23 | * @since 0.0.6 24 | */ 25 | private long time; 26 | 27 | public String getAddress() { 28 | return address; 29 | } 30 | 31 | public void setAddress(String address) { 32 | this.address = address; 33 | } 34 | 35 | public int getPort() { 36 | return port; 37 | } 38 | 39 | public void setPort(int port) { 40 | this.port = port; 41 | } 42 | 43 | public long getTime() { 44 | return time; 45 | } 46 | 47 | public void setTime(long time) { 48 | this.time = time; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/persist/MqMessagePersistTake.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.persist; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 0.0.3 8 | */ 9 | public class MqMessagePersistTake { 10 | 11 | /** 12 | * 分组名称 13 | */ 14 | private String groupName; 15 | 16 | /** 17 | * 标题名称 18 | */ 19 | private String topic; 20 | 21 | /** 22 | * 标签 23 | */ 24 | private List tags; 25 | 26 | public String getGroupName() { 27 | return groupName; 28 | } 29 | 30 | public void setGroupName(String groupName) { 31 | this.groupName = groupName; 32 | } 33 | 34 | public String getTopic() { 35 | return topic; 36 | } 37 | 38 | public void setTopic(String topic) { 39 | this.topic = topic; 40 | } 41 | 42 | public List getTags() { 43 | return tags; 44 | } 45 | 46 | public void setTags(List tags) { 47 | this.tags = tags; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/dto/SendResult.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.dto; 2 | 3 | import com.github.houbb.mq.producer.constant.SendStatus; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class SendResult { 10 | 11 | /** 12 | * 消息唯一标识 13 | */ 14 | private String messageId; 15 | 16 | /** 17 | * 发送状态 18 | */ 19 | private SendStatus status; 20 | 21 | public static SendResult of(String messageId, SendStatus status) { 22 | SendResult result = new SendResult(); 23 | result.setMessageId(messageId); 24 | result.setStatus(status); 25 | 26 | return result; 27 | } 28 | 29 | public String getMessageId() { 30 | return messageId; 31 | } 32 | 33 | public void setMessageId(String messageId) { 34 | this.messageId = messageId; 35 | } 36 | 37 | public SendStatus getStatus() { 38 | return status; 39 | } 40 | 41 | public void setStatus(SendStatus status) { 42 | this.status = status; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/resp/MqCommonRespCode.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.resp; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public enum MqCommonRespCode implements RespCode { 10 | 11 | SUCCESS("0000", "成功"), 12 | FAIL("9999", "失败"), 13 | TIMEOUT("8888", "超时"), 14 | 15 | RPC_GET_RESP_FAILED("10001", "RPC 获取响应失败"), 16 | REGISTER_TO_BROKER_FAILED("10002", "注册到 Broker 失败"), 17 | 18 | P_REGISTER_TO_BROKER_FAILED("P00001", "生产者注册到 Broker 失败"), 19 | P_INIT_FAILED("P00002", "生产者初始化失败"), 20 | 21 | C_REGISTER_TO_BROKER_FAILED("C00001", "消费者注册到 Broker 失败"), 22 | C_INIT_FAILED("C00002", "消费者初始化失败"), 23 | ; 24 | 25 | private final String code; 26 | private final String msg; 27 | 28 | MqCommonRespCode(String code, String msg) { 29 | this.code = code; 30 | this.msg = msg; 31 | } 32 | 33 | public String getCode() { 34 | return code; 35 | } 36 | 37 | public String getMsg() { 38 | return msg; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/invoke/IInvokeService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.invoke; 2 | 3 | 4 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 5 | 6 | /** 7 | * 调用服务接口 8 | * @author binbin.hou 9 | * @since 1.0.0 10 | */ 11 | public interface IInvokeService { 12 | 13 | /** 14 | * 添加请求信息 15 | * @param seqId 序列号 16 | * @param timeoutMills 超时时间 17 | * @return this 18 | * @since 1.0.0 19 | */ 20 | IInvokeService addRequest(final String seqId, 21 | final long timeoutMills); 22 | 23 | /** 24 | * 放入结果 25 | * @param seqId 唯一标识 26 | * @param rpcResponse 响应结果 27 | * @return this 28 | * @since 1.0.0 29 | */ 30 | IInvokeService addResponse(final String seqId, final RpcMessageDto rpcResponse); 31 | 32 | /** 33 | * 获取标志信息对应的结果 34 | * @param seqId 序列号 35 | * @return 结果 36 | * @since 1.0.0 37 | */ 38 | RpcMessageDto getResponse(final String seqId); 39 | 40 | /** 41 | * 是否依然包含请求待处理 42 | * @return 是否 43 | * @since 0.0.5 44 | */ 45 | boolean remainsRequest(); 46 | 47 | } 48 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/api/IMqProducer.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.api; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | import com.github.houbb.mq.producer.dto.SendBatchResult; 5 | import com.github.houbb.mq.producer.dto.SendResult; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * @author binbin.hou 11 | * @since 1.0.0 12 | */ 13 | public interface IMqProducer { 14 | 15 | /** 16 | * 同步发送消息 17 | * @param mqMessage 消息类型 18 | * @return 结果 19 | */ 20 | SendResult send(final MqMessage mqMessage); 21 | 22 | /** 23 | * 单向发送消息 24 | * @param mqMessage 消息类型 25 | * @return 结果 26 | */ 27 | SendResult sendOneWay(final MqMessage mqMessage); 28 | 29 | /** 30 | * 同步发送消息-批量 31 | * @param mqMessageList 消息类型 32 | * @return 结果 33 | * @since 0.1.3 34 | */ 35 | SendBatchResult sendBatch(final List mqMessageList); 36 | 37 | /** 38 | * 单向发送消息-批量 39 | * @param mqMessageList 消息类型 40 | * @return 结果 41 | * @since 0.1.3 42 | */ 43 | SendBatchResult sendOneWayBatch(final List mqMessageList); 44 | 45 | } 46 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/ChannelGroupNameDto.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto; 2 | 3 | import io.netty.channel.Channel; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class ChannelGroupNameDto { 10 | 11 | /** 12 | * 分组名称 13 | */ 14 | private String consumerGroupName; 15 | 16 | /** 17 | * 通道 18 | */ 19 | private Channel channel; 20 | 21 | public static ChannelGroupNameDto of(String consumerGroupName, 22 | Channel channel) { 23 | ChannelGroupNameDto dto = new ChannelGroupNameDto(); 24 | dto.setConsumerGroupName(consumerGroupName); 25 | dto.setChannel(channel); 26 | return dto; 27 | } 28 | 29 | public String getConsumerGroupName() { 30 | return consumerGroupName; 31 | } 32 | 33 | public void setConsumerGroupName(String consumerGroupName) { 34 | this.consumerGroupName = consumerGroupName; 35 | } 36 | 37 | public Channel getChannel() { 38 | return channel; 39 | } 40 | 41 | public void setChannel(Channel channel) { 42 | this.channel = channel; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/dto/SendBatchResult.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.dto; 2 | 3 | import com.github.houbb.mq.producer.constant.SendStatus; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * 批量发送结果 9 | * 10 | * @author binbin.hou 11 | * @since 0.1.3 12 | */ 13 | public class SendBatchResult { 14 | 15 | /** 16 | * 消息唯一标识 17 | */ 18 | private List messageIds; 19 | 20 | /** 21 | * 发送状态 22 | */ 23 | private SendStatus status; 24 | 25 | public static SendBatchResult of(List messageIds, SendStatus status) { 26 | SendBatchResult result = new SendBatchResult(); 27 | result.setMessageIds(messageIds); 28 | result.setStatus(status); 29 | 30 | return result; 31 | } 32 | 33 | public List getMessageIds() { 34 | return messageIds; 35 | } 36 | 37 | public void setMessageIds(List messageIds) { 38 | this.messageIds = messageIds; 39 | } 40 | 41 | public SendStatus getStatus() { 42 | return status; 43 | } 44 | 45 | public void setStatus(SendStatus status) { 46 | this.status = status; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /mq-test/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mq 7 | com.github.houbb 8 | 0.1.4 9 | 10 | 4.0.0 11 | 12 | mq-test 13 | 14 | 15 | ${project.groupId} 16 | mq-broker 17 | 18 | 19 | ${project.groupId} 20 | mq-producer 21 | 22 | 23 | ${project.groupId} 24 | mq-consumer 25 | 26 | 27 | 28 | junit 29 | junit 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/consumer/ConsumerMain.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.test.consumer; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.mq.common.dto.req.MqMessage; 5 | import com.github.houbb.mq.common.resp.ConsumerStatus; 6 | import com.github.houbb.mq.consumer.api.IMqConsumerListener; 7 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 8 | import com.github.houbb.mq.consumer.core.MqConsumerPush; 9 | 10 | /** 11 | * @author binbin.hou 12 | * @since 1.0.0 13 | */ 14 | public class ConsumerMain { 15 | 16 | //1. 首先启动消费者,然后启动生产者。 17 | public static void main(String[] args) { 18 | final MqConsumerPush mqConsumerPush = new MqConsumerPush(); 19 | mqConsumerPush.start(); 20 | 21 | mqConsumerPush.subscribe("TOPIC", "TAGA"); 22 | mqConsumerPush.registerListener(new IMqConsumerListener() { 23 | @Override 24 | public ConsumerStatus consumer(MqMessage mqMessage, IMqConsumerListenerContext context) { 25 | System.out.println("---------- 自定义 " + JSON.toJSONString(mqMessage)); 26 | return ConsumerStatus.SUCCESS; 27 | } 28 | }); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/constant/MessageStatusConst.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 0.0.3 6 | */ 7 | public final class MessageStatusConst { 8 | 9 | private MessageStatusConst(){} 10 | 11 | /** 12 | * 待消费 13 | * ps: 生产者推送到 broker 的初始化状态 14 | */ 15 | public static final String WAIT_CONSUMER = "W"; 16 | 17 | /** 18 | * 推送给消费端处理中 19 | * ps: broker 准备推送时,首先将状态更新为 P,等待推送结果 20 | * @since 0.1.0 21 | */ 22 | public static final String TO_CONSUMER_PROCESS = "TCP"; 23 | 24 | /** 25 | * 推送给消费端成功 26 | * @since 0.1.0 27 | */ 28 | public static final String TO_CONSUMER_SUCCESS = "TCS"; 29 | 30 | /** 31 | * 推送给消费端失败 32 | * @since 0.1.0 33 | */ 34 | public static final String TO_CONSUMER_FAILED = "TCF"; 35 | 36 | /** 37 | * 消费完成 38 | */ 39 | public static final String CONSUMER_SUCCESS = "CS"; 40 | 41 | /** 42 | * 消费失败 43 | */ 44 | public static final String CONSUMER_FAILED = "CF"; 45 | 46 | /** 47 | * 稍后消费 48 | * @since 0.1.0 49 | */ 50 | public static final String CONSUMER_LATER = "CL"; 51 | 52 | } 53 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/persist/MqMessagePersistPut.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.persist; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | import com.github.houbb.mq.common.rpc.RpcAddress; 5 | 6 | /** 7 | * @author binbin.hou 8 | * @since 0.0.3 9 | */ 10 | public class MqMessagePersistPut { 11 | 12 | /** 13 | * 消息体 14 | */ 15 | private MqMessage mqMessage; 16 | 17 | /** 18 | * 地址信息 19 | */ 20 | private RpcAddress rpcAddress; 21 | 22 | /** 23 | * 消息状态 24 | */ 25 | private String messageStatus; 26 | 27 | public MqMessage getMqMessage() { 28 | return mqMessage; 29 | } 30 | 31 | public void setMqMessage(MqMessage mqMessage) { 32 | this.mqMessage = mqMessage; 33 | } 34 | 35 | public RpcAddress getRpcAddress() { 36 | return rpcAddress; 37 | } 38 | 39 | public void setRpcAddress(RpcAddress rpcAddress) { 40 | this.rpcAddress = rpcAddress; 41 | } 42 | 43 | public String getMessageStatus() { 44 | return messageStatus; 45 | } 46 | 47 | public void setMessageStatus(String messageStatus) { 48 | this.messageStatus = messageStatus; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/consumer/ConsumerPullMain.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.test.consumer; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.mq.common.dto.req.MqMessage; 5 | import com.github.houbb.mq.common.resp.ConsumerStatus; 6 | import com.github.houbb.mq.consumer.api.IMqConsumerListener; 7 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 8 | import com.github.houbb.mq.consumer.core.MqConsumerPull; 9 | 10 | /** 11 | * @author binbin.hou 12 | * @since 1.0.0 13 | */ 14 | public class ConsumerPullMain { 15 | 16 | //1. 首先启动消费者,然后启动生产者。 17 | public static void main(String[] args) { 18 | final MqConsumerPull mqConsumerPull = new MqConsumerPull(); 19 | mqConsumerPull.appKey("test") 20 | .appSecret("mq"); 21 | mqConsumerPull.start(); 22 | 23 | mqConsumerPull.subscribe("TOPIC", "TAGA"); 24 | mqConsumerPull.registerListener(new IMqConsumerListener() { 25 | @Override 26 | public ConsumerStatus consumer(MqMessage mqMessage, IMqConsumerListenerContext context) { 27 | System.out.println("---------- 自定义 " + JSON.toJSONString(mqMessage)); 28 | return ConsumerStatus.SUCCESS; 29 | } 30 | }); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/rpc/RpcAddress.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.rpc; 2 | 3 | import com.github.houbb.load.balance.support.server.IServer; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class RpcAddress implements IServer { 10 | 11 | /** 12 | * address 信息 13 | * @since 0.0.3 14 | */ 15 | private String address; 16 | 17 | /** 18 | * 端口号 19 | * @since 0.0.3 20 | */ 21 | private int port; 22 | 23 | /** 24 | * 权重 25 | * @since 0.0.3 26 | */ 27 | private int weight; 28 | 29 | public String getAddress() { 30 | return address; 31 | } 32 | 33 | public void setAddress(String address) { 34 | this.address = address; 35 | } 36 | 37 | public int getPort() { 38 | return port; 39 | } 40 | 41 | public void setPort(int port) { 42 | this.port = port; 43 | } 44 | 45 | public int getWeight() { 46 | return weight; 47 | } 48 | 49 | public void setWeight(int weight) { 50 | this.weight = weight; 51 | } 52 | 53 | @Override 54 | public String url() { 55 | return this.address+":"+port; 56 | } 57 | 58 | @Override 59 | public int weight() { 60 | return this.weight; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/persist/MqMessagePersistPutBatch.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.persist; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqMessage; 4 | import com.github.houbb.mq.common.rpc.RpcAddress; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * @author binbin.hou 10 | * @since 0.0.3 11 | */ 12 | public class MqMessagePersistPutBatch { 13 | 14 | /** 15 | * 消息体 16 | */ 17 | private List mqMessageList; 18 | 19 | /** 20 | * 地址信息 21 | */ 22 | private RpcAddress rpcAddress; 23 | 24 | /** 25 | * 消息状态 26 | */ 27 | private String messageStatus; 28 | 29 | public List getMqMessageList() { 30 | return mqMessageList; 31 | } 32 | 33 | public void setMqMessageList(List mqMessageList) { 34 | this.mqMessageList = mqMessageList; 35 | } 36 | 37 | public RpcAddress getRpcAddress() { 38 | return rpcAddress; 39 | } 40 | 41 | public void setRpcAddress(RpcAddress rpcAddress) { 42 | this.rpcAddress = rpcAddress; 43 | } 44 | 45 | public String getMessageStatus() { 46 | return messageStatus; 47 | } 48 | 49 | public void setMessageStatus(String messageStatus) { 50 | this.messageStatus = messageStatus; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/util/InnerAddressUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.util; 2 | 3 | import com.github.houbb.heaven.util.common.ArgUtil; 4 | import com.github.houbb.mq.common.rpc.RpcAddress; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | /** 10 | * 内部地址工具类 11 | * 12 | * @author binbin.hou 13 | * @since 0.0.3 14 | */ 15 | public final class InnerAddressUtils { 16 | 17 | private InnerAddressUtils(){} 18 | 19 | /** 20 | * 初始化地址信息 21 | * @param address 地址 22 | * @return 结果列表 23 | * @since 0.0.3 24 | */ 25 | public static List initAddressList(String address) { 26 | ArgUtil.notEmpty(address, "address"); 27 | 28 | String[] strings = address.split(","); 29 | List list = new ArrayList<>(); 30 | for(String s : strings) { 31 | String[] infos = s.split(":"); 32 | 33 | RpcAddress rpcAddress = new RpcAddress(); 34 | rpcAddress.setAddress(infos[0]); 35 | rpcAddress.setPort(Integer.parseInt(infos[1])); 36 | if(infos.length > 2) { 37 | rpcAddress.setWeight(Integer.parseInt(infos[2])); 38 | } else { 39 | rpcAddress.setWeight(1); 40 | } 41 | list.add(rpcAddress); 42 | } 43 | return list; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/producer/ProducerMain.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.test.producer; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.util.DateUtil; 5 | import com.github.houbb.mq.common.dto.req.MqMessage; 6 | import com.github.houbb.mq.producer.core.MqProducer; 7 | import com.github.houbb.mq.producer.dto.SendResult; 8 | 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.Arrays; 11 | 12 | /** 13 | * @author binbin.hou 14 | * @since 1.0.0 15 | */ 16 | public class ProducerMain { 17 | 18 | public static void main(String[] args) { 19 | MqProducer mqProducer = new MqProducer(); 20 | mqProducer.appKey("test") 21 | .appSecret("mq"); 22 | mqProducer.start(); 23 | 24 | for(int i = 0; i < 20; i++) { 25 | MqMessage mqMessage = buildMessage(i); 26 | SendResult sendResult = mqProducer.send(mqMessage); 27 | System.out.println(JSON.toJSON(sendResult)); 28 | } 29 | } 30 | 31 | private static MqMessage buildMessage(int i) { 32 | String message = "HELLO MQ!" + i; 33 | MqMessage mqMessage = new MqMessage(); 34 | mqMessage.setTopic("TOPIC"); 35 | mqMessage.setTags(Arrays.asList("TAGA", "TAGB")); 36 | mqMessage.setPayload(message); 37 | 38 | return mqMessage; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/resp/MqException.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.resp; 2 | 3 | import com.github.houbb.heaven.response.respcode.RespCode; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqException extends RuntimeException implements RespCode{ 10 | 11 | private final RespCode respCode; 12 | 13 | public MqException(RespCode respCode) { 14 | this.respCode = respCode; 15 | } 16 | 17 | public MqException(String message, RespCode respCode) { 18 | super(message); 19 | this.respCode = respCode; 20 | } 21 | 22 | public MqException(String message, Throwable cause, RespCode respCode) { 23 | super(message, cause); 24 | this.respCode = respCode; 25 | } 26 | 27 | public MqException(Throwable cause, RespCode respCode) { 28 | super(cause); 29 | this.respCode = respCode; 30 | } 31 | 32 | public MqException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, RespCode respCode) { 33 | super(message, cause, enableSuppression, writableStackTrace); 34 | this.respCode = respCode; 35 | } 36 | 37 | @Override 38 | public String getCode() { 39 | return this.respCode.getCode(); 40 | } 41 | 42 | @Override 43 | public String getMsg() { 44 | return this.respCode.getMsg(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /release_rm.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="0.0.2" 6 | newVersion="0.0.2" 7 | projectName="sensitive" 8 | 9 | # 删除分支 10 | oldBranchName="release_"${oldVersion} 11 | git branch -d ${oldBranchName} 12 | git push origin --delete ${oldBranchName} 13 | 14 | echo "1. Branch remove success..." 15 | 16 | # 拉取新的分支 17 | newBranchName="release_"${newVersion} 18 | git branch ${newBranchName} 19 | git checkout ${newBranchName} 20 | git push --set-upstream origin ${newBranchName} 21 | 22 | echo "2. NEW BRANCH DONE." 23 | 24 | # 修改新分支的版本号 25 | ## snapshot 版本号 26 | snapshot_new_version=${newVersion}"-SNAPSHOT" 27 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 28 | mvn -N versions:update-child-modules 29 | mvn versions:commit 30 | 31 | git add . 32 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 33 | git push 34 | git status 35 | echo "3. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 36 | 37 | echo "============================= BRANCH RE-CREATE END..." 38 | 39 | echo "============================= BRANCH LIST =============================" 40 | git branch -a 41 | 42 | # 使用方式: 43 | # 注意:本脚本用于删除分支,谨慎使用! 44 | # 1. 赋值权限: chmod +x ./release_rm.sh 45 | # 2. 执行: ./release_rm.sh 46 | # Last Update Time: 2018-06-21 11:10:42 47 | # Author: houbb -------------------------------------------------------------------------------- /mq-test/src/test/java/com/github/houbb/mq/test/producer/ProducerMainBatch.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.test.producer; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.mq.common.dto.req.MqMessage; 5 | import com.github.houbb.mq.producer.core.MqProducer; 6 | import com.github.houbb.mq.producer.dto.SendBatchResult; 7 | import com.github.houbb.mq.producer.dto.SendResult; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 1.0.0 16 | */ 17 | public class ProducerMainBatch { 18 | 19 | public static void main(String[] args) { 20 | MqProducer mqProducer = new MqProducer(); 21 | mqProducer.start(); 22 | 23 | List mqMessageList = new ArrayList<>(); 24 | for(int i = 0; i < 20; i++) { 25 | MqMessage mqMessage = buildMessage(i); 26 | mqMessageList.add(mqMessage); 27 | } 28 | 29 | SendBatchResult sendResult = mqProducer.sendBatch(mqMessageList); 30 | System.out.println(JSON.toJSON(sendResult)); 31 | } 32 | 33 | private static MqMessage buildMessage(int i) { 34 | String message = "HELLO MQ!" + i; 35 | MqMessage mqMessage = new MqMessage(); 36 | mqMessage.setTopic("TOPIC"); 37 | mqMessage.setTags(Arrays.asList("TAGA", "TAGB")); 38 | mqMessage.setPayload(message); 39 | 40 | return mqMessage; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/util/DelimiterUtil.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.util; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 5 | import io.netty.buffer.ByteBuf; 6 | import io.netty.buffer.Unpooled; 7 | 8 | /** 9 | * @author binbin.hou 10 | * @since 1.0.0 11 | */ 12 | public class DelimiterUtil { 13 | 14 | private DelimiterUtil(){} 15 | 16 | /** 17 | * 分隔符 18 | */ 19 | public static final String DELIMITER = "~!@#$%^&*"; 20 | 21 | /** 22 | * 长度 23 | * 24 | * ps: 这个长度是必须的,避免把缓冲区打爆 25 | */ 26 | public static final int LENGTH = 65535; 27 | 28 | /** 29 | * 分隔符 buffer 30 | * @since 1.0.0 31 | */ 32 | public static final ByteBuf DELIMITER_BUF = Unpooled.copiedBuffer(DELIMITER.getBytes()); 33 | 34 | /** 35 | * 获取对应的字节缓存 36 | * @param text 文本 37 | * @return 结果 38 | * @since 1.0.0 39 | */ 40 | public static ByteBuf getByteBuf(String text) { 41 | return Unpooled.copiedBuffer(text.getBytes()); 42 | } 43 | 44 | /** 45 | * 获取消息 46 | * @param rpcMessageDto 消息体 47 | * @return 结果 48 | * @since 1.0.0 49 | */ 50 | public static ByteBuf getMessageDelimiterBuffer(RpcMessageDto rpcMessageDto) { 51 | String json = JSON.toJSONString(rpcMessageDto); 52 | String jsonDelimiter = json + DELIMITER; 53 | 54 | return Unpooled.copiedBuffer(jsonDelimiter.getBytes()); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/consumer/ConsumerSubscribeReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.consumer; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 4 | 5 | /** 6 | * 消费者注册入参 7 | * @author binbin.hou 8 | * @since 0.0.3 9 | */ 10 | public class ConsumerSubscribeReq extends MqCommonReq { 11 | 12 | /** 13 | * 分组名称 14 | * @since 0.0.3 15 | */ 16 | private String groupName; 17 | 18 | /** 19 | * 标题名称 20 | */ 21 | private String topicName; 22 | 23 | /** 24 | * 标签正则 25 | */ 26 | private String tagRegex; 27 | 28 | /** 29 | * 消费者类型 30 | * @since 0.0.9 31 | */ 32 | private String consumerType; 33 | 34 | public String getConsumerType() { 35 | return consumerType; 36 | } 37 | 38 | public void setConsumerType(String consumerType) { 39 | this.consumerType = consumerType; 40 | } 41 | 42 | public String getGroupName() { 43 | return groupName; 44 | } 45 | 46 | public void setGroupName(String groupName) { 47 | this.groupName = groupName; 48 | } 49 | 50 | public String getTopicName() { 51 | return topicName; 52 | } 53 | 54 | public void setTopicName(String topicName) { 55 | this.topicName = topicName; 56 | } 57 | 58 | public String getTagRegex() { 59 | return tagRegex; 60 | } 61 | 62 | public void setTagRegex(String tagRegex) { 63 | this.tagRegex = tagRegex; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/BrokerRegisterReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class BrokerRegisterReq extends MqCommonReq { 10 | 11 | /** 12 | * 服务信息 13 | */ 14 | private ServiceEntry serviceEntry; 15 | 16 | /** 17 | * 账户标识 18 | * @since 0.1.4 19 | */ 20 | private String appKey; 21 | 22 | /** 23 | * 账户密码 24 | * @since 0.1.4 25 | */ 26 | private String appSecret; 27 | 28 | public ServiceEntry getServiceEntry() { 29 | return serviceEntry; 30 | } 31 | 32 | public void setServiceEntry(ServiceEntry serviceEntry) { 33 | this.serviceEntry = serviceEntry; 34 | } 35 | 36 | public String getAppKey() { 37 | return appKey; 38 | } 39 | 40 | public void setAppKey(String appKey) { 41 | this.appKey = appKey; 42 | } 43 | 44 | public String getAppSecret() { 45 | return appSecret; 46 | } 47 | 48 | public void setAppSecret(String appSecret) { 49 | this.appSecret = appSecret; 50 | } 51 | 52 | @Override 53 | public String toString() { 54 | return "BrokerRegisterReq{" + 55 | "serviceEntry=" + serviceEntry + 56 | ", appKey='" + appKey + '\'' + 57 | ", appSecret='" + appSecret + '\'' + 58 | "} " + super.toString(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/consumer/ConsumerUnSubscribeReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.consumer; 2 | 3 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 4 | 5 | /** 6 | * 消费者注销入参 7 | * @author binbin.hou 8 | * @since 1.0.0 9 | */ 10 | public class ConsumerUnSubscribeReq extends MqCommonReq { 11 | 12 | /** 13 | * 分组名称 14 | * @since 0.0.3 15 | */ 16 | private String groupName; 17 | 18 | /** 19 | * 标题名称 20 | */ 21 | private String topicName; 22 | 23 | /** 24 | * 标签正则 25 | */ 26 | private String tagRegex; 27 | 28 | /** 29 | * 消费者类型 30 | * @since 0.0.9 31 | */ 32 | private String consumerType; 33 | 34 | public String getConsumerType() { 35 | return consumerType; 36 | } 37 | 38 | public void setConsumerType(String consumerType) { 39 | this.consumerType = consumerType; 40 | } 41 | 42 | public String getGroupName() { 43 | return groupName; 44 | } 45 | 46 | public void setGroupName(String groupName) { 47 | this.groupName = groupName; 48 | } 49 | 50 | public String getTopicName() { 51 | return topicName; 52 | } 53 | 54 | public void setTopicName(String topicName) { 55 | this.topicName = topicName; 56 | } 57 | 58 | public String getTagRegex() { 59 | return tagRegex; 60 | } 61 | 62 | public void setTagRegex(String tagRegex) { 63 | this.tagRegex = tagRegex; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/util/RandomUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.util; 2 | 3 | import com.github.houbb.heaven.annotation.CommonEager; 4 | import com.github.houbb.heaven.util.lang.StringUtil; 5 | import com.github.houbb.heaven.util.util.CollectionUtil; 6 | import com.github.houbb.load.balance.api.ILoadBalance; 7 | import com.github.houbb.load.balance.api.impl.LoadBalanceContext; 8 | import com.github.houbb.load.balance.support.server.IServer; 9 | 10 | import java.util.List; 11 | import java.util.Objects; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 1.0.0 16 | */ 17 | @CommonEager 18 | public class RandomUtils { 19 | 20 | /** 21 | * 负载均衡 22 | * 23 | * @param list 列表 24 | * @param key 分片键 25 | * @return 结果 26 | * @since 0.0.7 27 | */ 28 | public static T loadBalance(final ILoadBalance loadBalance, 29 | final List list, String key) { 30 | if(CollectionUtil.isEmpty(list)) { 31 | return null; 32 | } 33 | 34 | if(StringUtil.isEmpty(key)) { 35 | LoadBalanceContext loadBalanceContext = LoadBalanceContext.newInstance() 36 | .servers(list); 37 | return loadBalance.select(loadBalanceContext); 38 | } 39 | 40 | // 获取 code 41 | int hashCode = Objects.hash(key); 42 | int index = hashCode % list.size(); 43 | return list.get(index); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/api/IBrokerProducerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * rpc All rights reserved. 4 | */ 5 | 6 | package com.github.houbb.mq.broker.api; 7 | 8 | 9 | import com.github.houbb.mq.broker.dto.ServiceEntry; 10 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 11 | import io.netty.channel.Channel; 12 | 13 | /** 14 | *

生产者注册服务类

15 | * 16 | *
 Created: 2019/10/23 9:08 下午  
17 | *
 Project: rpc  
18 | * 19 | * @author houbinbin 20 | * @since 0.0.3 21 | */ 22 | public interface IBrokerProducerService { 23 | 24 | /** 25 | * 注册当前服务信息 26 | * (1)将该服务通过 {@link ServiceEntry#getGroupName()} 进行分组 27 | * 订阅了这个 serviceId 的所有客户端 28 | * @param serviceEntry 注册当前服务信息 29 | * @param channel channel 30 | * @since 0.0.8 31 | */ 32 | MqCommonResp register(final ServiceEntry serviceEntry, Channel channel); 33 | 34 | /** 35 | * 注销当前服务信息 36 | * @param serviceEntry 注册当前服务信息 37 | * @param channel 通道 38 | * @since 0.0.8 39 | */ 40 | MqCommonResp unRegister(final ServiceEntry serviceEntry, Channel channel); 41 | 42 | /** 43 | * 获取服务地址信息 44 | * @param channelId channel 45 | * @return 结果 46 | * @since 0.0.3 47 | */ 48 | ServiceEntry getServiceEntry(final String channelId); 49 | 50 | /** 51 | * 校验有效性 52 | * @param channelId 通道唯一标识 53 | * @since 0.1.4 54 | */ 55 | void checkValid(final String channelId); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/listener/MqListenerService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.support.listener; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.annotation.NotThreadSafe; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.common.dto.req.MqMessage; 8 | import com.github.houbb.mq.common.resp.ConsumerStatus; 9 | import com.github.houbb.mq.consumer.api.IMqConsumerListener; 10 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 11 | import com.github.houbb.mq.consumer.handler.MqConsumerHandler; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 1.0.0 16 | */ 17 | @NotThreadSafe 18 | public class MqListenerService implements IMqListenerService { 19 | 20 | private static final Log log = LogFactory.getLog(MqListenerService.class); 21 | 22 | private IMqConsumerListener mqConsumerListener; 23 | 24 | @Override 25 | public void register(IMqConsumerListener listener) { 26 | this.mqConsumerListener = listener; 27 | } 28 | 29 | @Override 30 | public ConsumerStatus consumer(MqMessage mqMessage, IMqConsumerListenerContext context) { 31 | if(mqConsumerListener == null) { 32 | log.warn("当前监听类为空,直接忽略处理。message: {}", JSON.toJSON(mqMessage)); 33 | return ConsumerStatus.SUCCESS; 34 | } else { 35 | return mqConsumerListener.consumer(mqMessage, context); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqConsumerUpdateStatusReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public class MqConsumerUpdateStatusReq extends MqCommonReq { 8 | 9 | /** 10 | * 消息唯一标识 11 | */ 12 | private String messageId; 13 | 14 | /** 15 | * 消息状态 16 | */ 17 | private String messageStatus; 18 | 19 | /** 20 | * 消费者分组名称 21 | */ 22 | private String consumerGroupName; 23 | 24 | public String getMessageId() { 25 | return messageId; 26 | } 27 | 28 | public void setMessageId(String messageId) { 29 | this.messageId = messageId; 30 | } 31 | 32 | public String getMessageStatus() { 33 | return messageStatus; 34 | } 35 | 36 | public void setMessageStatus(String messageStatus) { 37 | this.messageStatus = messageStatus; 38 | } 39 | 40 | public String getConsumerGroupName() { 41 | return consumerGroupName; 42 | } 43 | 44 | public void setConsumerGroupName(String consumerGroupName) { 45 | this.consumerGroupName = consumerGroupName; 46 | } 47 | 48 | @Override 49 | public String toString() { 50 | return "MqConsumerUpdateStatusReq{" + 51 | "messageId='" + messageId + '\'' + 52 | ", messageStatus='" + messageStatus + '\'' + 53 | ", consumerGroupName='" + consumerGroupName + '\'' + 54 | "} " + super.toString(); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/utils/InnerChannelUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.utils; 2 | 3 | import com.github.houbb.mq.broker.dto.BrokerServiceEntryChannel; 4 | import com.github.houbb.mq.broker.dto.ServiceEntry; 5 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 6 | import io.netty.channel.Channel; 7 | 8 | /** 9 | * @author binbin.hou 10 | * @since 1.0.0 11 | */ 12 | public class InnerChannelUtils { 13 | 14 | private InnerChannelUtils(){} 15 | 16 | /** 17 | * 构建基本服务地址 18 | * @param rpcChannelFuture 信息 19 | * @return 结果 20 | * @since 0.0.5 21 | */ 22 | public static ServiceEntry buildServiceEntry(RpcChannelFuture rpcChannelFuture) { 23 | ServiceEntry serviceEntry = new ServiceEntry(); 24 | 25 | serviceEntry.setAddress(rpcChannelFuture.getAddress()); 26 | serviceEntry.setPort(rpcChannelFuture.getPort()); 27 | serviceEntry.setWeight(rpcChannelFuture.getWeight()); 28 | return serviceEntry; 29 | } 30 | 31 | public static BrokerServiceEntryChannel buildEntryChannel(ServiceEntry serviceEntry, 32 | Channel channel) { 33 | BrokerServiceEntryChannel result = new BrokerServiceEntryChannel(); 34 | result.setChannel(channel); 35 | result.setGroupName(serviceEntry.getGroupName()); 36 | result.setAddress(serviceEntry.getAddress()); 37 | result.setPort(serviceEntry.getPort()); 38 | result.setWeight(serviceEntry.getWeight()); 39 | return result; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/component/MqConsumerUpdateStatusDto.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req.component; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public class MqConsumerUpdateStatusDto { 8 | 9 | /** 10 | * 消息唯一标识 11 | * @since 0.1.3 12 | */ 13 | private String messageId; 14 | 15 | /** 16 | * 消息状态 17 | * @since 0.1.3 18 | */ 19 | private String messageStatus; 20 | 21 | /** 22 | * 消费者分组名称 23 | * @since 0.1.3 24 | */ 25 | private String consumerGroupName; 26 | 27 | public String getMessageId() { 28 | return messageId; 29 | } 30 | 31 | public void setMessageId(String messageId) { 32 | this.messageId = messageId; 33 | } 34 | 35 | public String getMessageStatus() { 36 | return messageStatus; 37 | } 38 | 39 | public void setMessageStatus(String messageStatus) { 40 | this.messageStatus = messageStatus; 41 | } 42 | 43 | public String getConsumerGroupName() { 44 | return consumerGroupName; 45 | } 46 | 47 | public void setConsumerGroupName(String consumerGroupName) { 48 | this.consumerGroupName = consumerGroupName; 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return "MqConsumerUpdateStatusDto{" + 54 | "messageId='" + messageId + '\'' + 55 | ", messageStatus='" + messageStatus + '\'' + 56 | ", consumerGroupName='" + consumerGroupName + '\'' + 57 | '}'; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true 5 | script: mvn test 6 | after_success: 7 | - mvn clean cobertura:cobertura coveralls:report 8 | env: 9 | global: 10 | - secure: nUyxAiCRV60rKxKa1OeBFlYopCAV9yiUnr8bFBVSurDmzsgP+rOsUDOE3GN0PAHBV9QId+i2bNk7JGM29RRVhKXzYD92d55Aa+b1d2C71/+UUJeaMi30Wa+tEY3v+ddQI4RFqqFl4IJmqN7kcjOUmRjmLHRa2KILgk3EnbpMXqggr71P2cOcedOneuYOXY1JACrqV46ZFTUSqIS8E+OLxGUyGRAtKcETcLHItHHC2MOfpYVc/hNnFev8XdtVo2fOmXe3MeTxsI4x6PcsdA2fzxbHfvuqk5lZvA/+/GOh9Nx0IO1UdbdzMa1MjOw23y1eHjSvqo2emYLtYeDrNOygC2NoqtID5kL0nhf6rs1gKFn1AZyb7xbxSVCfu2n/IOfqpIKhRTDYByjABLCILs0C/J+dQJOixs9PDYEW2MzRpr0KPY6sDSsHbz+o54xxo7xCXBvwIQKDSDIED54Z/EhqW6NDUmUK2oyHac+lu/X8Z/IhLzlMVXiH1TcL/+mSPr4ZYAEpxr8F24aqhdaAEV+h7h4qt+4cxPJKpf6J8zgAfgLJyIK9kGBdRBlPZOR1xtRcwWgQat4jvUfEnAlr5/RjnbZ/9YN51l85aBkb91jTeknm7U15FgULJe+Q58UT2zxdg8IH1s7yhWVZXdTZU1+kxK5HSlojJ1LgU89jAbjUIj0= 11 | - secure: mDkrL1YOztqL56a4u5Wn30c3GQbVJmzrzA37Wzalg+QsWqH2lqxTc+RxhRk47SayQpe3OncASyHtnz5DV2tsq1X+Mk+wJZNKeAsrsBXqekqThkg2w864P4y2h3xZoHVNd67q5Ny4e2dx+F7cECIeUsarVnGqxUslHua5vOoJxhCGG/W8lM1hIEPj5ziccMZDtMvsEnDUL9/yfVIv0aSCsP3OX8z6/SdmXmL+DQoya7toJBCHKNbFcOVGiD/OiiDZygmb5z8Q4wiUL3/2idRSqCRv4aAjkNG5Keo77MT28fmNDyjmFgwhiQcU1n07A1BBkucSZ3L6pbJthz9PpkSQaEmlYC7HO3jx+InmFvGYnrCS2LEOQ5dpyMY9bXA8QdRHU1cJicEPHFgNTs6BYpIlaE/Ei4uzQMDn/jHWYL6jgTEdky+wToiiKTswMM0qtBzAP/9fce+WnGlxrDvCwHXG8PJZZnHnOomRptCI3/Pfu3NZ+oEEzKIsCzNleoZiA9ul8GsdKGkvNNqQ3od9QTa3ai75NWy6w+GEneMID0lFwS1nbqMTgwAsbO24hxMWAu8mOJVRgKsXOax3QDTose7oB7vWaE2oPA4LICEByUNoznZOhcRfbdR0Wvc9CcTLBo1guvBi/d9VcxsI2LTarQQ1UZ+bWt0y6jfec0CImu+jrT0= 12 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqConsumerPullReq.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public class MqConsumerPullReq extends MqCommonReq { 8 | 9 | /** 10 | * 分组名称 11 | */ 12 | private String groupName; 13 | 14 | /** 15 | * 拉取大小 16 | */ 17 | private int size; 18 | 19 | /** 20 | * 标题名称 21 | */ 22 | private String topicName; 23 | 24 | /** 25 | * 标签正则 26 | */ 27 | private String tagRegex; 28 | 29 | public String getTopicName() { 30 | return topicName; 31 | } 32 | 33 | public void setTopicName(String topicName) { 34 | this.topicName = topicName; 35 | } 36 | 37 | public String getTagRegex() { 38 | return tagRegex; 39 | } 40 | 41 | public void setTagRegex(String tagRegex) { 42 | this.tagRegex = tagRegex; 43 | } 44 | 45 | public String getGroupName() { 46 | return groupName; 47 | } 48 | 49 | public void setGroupName(String groupName) { 50 | this.groupName = groupName; 51 | } 52 | 53 | public int getSize() { 54 | return size; 55 | } 56 | 57 | public void setSize(int size) { 58 | this.size = size; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "MqConsumerPullReq{" + 64 | "groupName='" + groupName + '\'' + 65 | ", size=" + size + 66 | ", topicName='" + topicName + '\'' + 67 | ", tagRegex='" + tagRegex + '\'' + 68 | "} " + super.toString(); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/dto/MqTopicTagDto.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.dto; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqTopicTagDto { 10 | 11 | /** 12 | * 消费者分组名称 13 | */ 14 | private String groupName; 15 | 16 | /** 17 | * 标题名称 18 | */ 19 | private String topicName; 20 | 21 | /** 22 | * 标签名称 23 | */ 24 | private String tagRegex; 25 | 26 | public String getTopicName() { 27 | return topicName; 28 | } 29 | 30 | public void setTopicName(String topicName) { 31 | this.topicName = topicName; 32 | } 33 | 34 | public String getTagRegex() { 35 | return tagRegex; 36 | } 37 | 38 | public void setTagRegex(String tagRegex) { 39 | this.tagRegex = tagRegex; 40 | } 41 | 42 | public String getGroupName() { 43 | return groupName; 44 | } 45 | 46 | public void setGroupName(String groupName) { 47 | this.groupName = groupName; 48 | } 49 | 50 | @Override 51 | public boolean equals(Object object) { 52 | if (this == object) return true; 53 | if (object == null || getClass() != object.getClass()) return false; 54 | MqTopicTagDto tagDto = (MqTopicTagDto) object; 55 | return Objects.equals(groupName, tagDto.groupName) && 56 | Objects.equals(topicName, tagDto.topicName) && 57 | Objects.equals(tagRegex, tagDto.tagRegex); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(groupName, topicName, tagRegex); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/invoke/impl/TimeoutCheckThread.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.invoke.impl; 2 | 3 | import com.github.houbb.heaven.util.common.ArgUtil; 4 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 5 | 6 | import java.util.Map; 7 | import java.util.concurrent.ConcurrentHashMap; 8 | 9 | /** 10 | * 超时检测线程 11 | * @author binbin.hou 12 | * @since 0.0.2 13 | */ 14 | public class TimeoutCheckThread implements Runnable { 15 | 16 | /** 17 | * 请求信息 18 | * @since 0.0.2 19 | */ 20 | private final ConcurrentHashMap requestMap; 21 | 22 | /** 23 | * 请求信息 24 | * @since 0.0.2 25 | */ 26 | private final ConcurrentHashMap responseMap; 27 | 28 | /** 29 | * 新建 30 | * @param requestMap 请求 Map 31 | * @param responseMap 结果 map 32 | * @since 0.0.2 33 | */ 34 | public TimeoutCheckThread(ConcurrentHashMap requestMap, 35 | ConcurrentHashMap responseMap) { 36 | ArgUtil.notNull(requestMap, "requestMap"); 37 | this.requestMap = requestMap; 38 | this.responseMap = responseMap; 39 | } 40 | 41 | @Override 42 | public void run() { 43 | for(Map.Entry entry : requestMap.entrySet()) { 44 | long expireTime = entry.getValue(); 45 | long currentTime = System.currentTimeMillis(); 46 | 47 | if(currentTime > expireTime) { 48 | final String key = entry.getKey(); 49 | // 结果设置为超时,从请求 map 中移除 50 | responseMap.putIfAbsent(key, RpcMessageDto.timeout()); 51 | requestMap.remove(key); 52 | } 53 | } 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /mq-common/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | mq 7 | com.github.houbb 8 | 0.1.4 9 | 10 | 4.0.0 11 | 12 | mq-common 13 | 14 | 15 | 16 | 17 | com.github.houbb 18 | log-integration 19 | 20 | 21 | com.github.houbb 22 | heaven 23 | 24 | 25 | com.github.houbb 26 | load-balance 27 | 28 | 29 | com.github.houbb 30 | id-core 31 | 32 | 33 | com.github.houbb 34 | sisyphus-core 35 | 36 | 37 | 38 | 39 | io.netty 40 | netty-all 41 | 42 | 43 | com.alibaba 44 | fastjson 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/dto/req/MqMessage.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.dto.req; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author binbin.hou 7 | * @since 1.0.0 8 | */ 9 | public class MqMessage extends MqCommonReq { 10 | 11 | /** 12 | * 分组名称 13 | */ 14 | private String groupName; 15 | 16 | /** 17 | * 标题名称 18 | */ 19 | private String topic; 20 | 21 | /** 22 | * 标签 23 | */ 24 | private List tags; 25 | 26 | /** 27 | * 内容 28 | */ 29 | private String payload; 30 | 31 | /** 32 | * 业务标识 33 | */ 34 | private String bizKey; 35 | 36 | /** 37 | * 负载分片标识 38 | */ 39 | private String shardingKey; 40 | 41 | public String getGroupName() { 42 | return groupName; 43 | } 44 | 45 | public void setGroupName(String groupName) { 46 | this.groupName = groupName; 47 | } 48 | 49 | public String getTopic() { 50 | return topic; 51 | } 52 | 53 | public void setTopic(String topic) { 54 | this.topic = topic; 55 | } 56 | 57 | public List getTags() { 58 | return tags; 59 | } 60 | 61 | public void setTags(List tags) { 62 | this.tags = tags; 63 | } 64 | 65 | public String getPayload() { 66 | return payload; 67 | } 68 | 69 | public void setPayload(String payload) { 70 | this.payload = payload; 71 | } 72 | 73 | public String getBizKey() { 74 | return bizKey; 75 | } 76 | 77 | public void setBizKey(String bizKey) { 78 | this.bizKey = bizKey; 79 | } 80 | 81 | public String getShardingKey() { 82 | return shardingKey; 83 | } 84 | 85 | public void setShardingKey(String shardingKey) { 86 | this.shardingKey = shardingKey; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/persist/IMqBrokerPersist.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.persist; 2 | 3 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPut; 4 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPutBatch; 5 | import com.github.houbb.mq.common.dto.req.MqConsumerPullReq; 6 | import com.github.houbb.mq.common.dto.req.component.MqConsumerUpdateStatusDto; 7 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 8 | import com.github.houbb.mq.common.dto.resp.MqConsumerPullResp; 9 | import io.netty.channel.Channel; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 0.0.3 16 | */ 17 | public interface IMqBrokerPersist { 18 | 19 | /** 20 | * 保存消息 21 | * @param mqMessage 消息 22 | * @return 响应 23 | * @since 0.0.3 24 | */ 25 | MqCommonResp put(final MqMessagePersistPut mqMessage); 26 | 27 | /** 28 | * 保存消息-批量 29 | * @param putList 消息 30 | * @return 响应 31 | * @since 0.1.3 32 | */ 33 | MqCommonResp putBatch(final List putList); 34 | 35 | /** 36 | * 更新状态 37 | * @param messageId 消息唯一标识 38 | * @param consumerGroupName 消费者分组名称 39 | * @param status 状态 40 | * @return 结果 41 | * @since 0.0.3 42 | */ 43 | MqCommonResp updateStatus(final String messageId, 44 | final String consumerGroupName, 45 | final String status); 46 | 47 | /** 48 | * 更新状态-批量 49 | * @param statusDtoList 状态列表 50 | * @return 结果 51 | * @since 0.1.3 52 | */ 53 | MqCommonResp updateStatusBatch(List statusDtoList); 54 | 55 | /** 56 | * 拉取消息 57 | * @param pull 拉取消息 58 | * @param channel 通道 59 | * @return 结果 60 | */ 61 | MqConsumerPullResp pull(final MqConsumerPullReq pull, final Channel channel); 62 | 63 | } 64 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | echo "============================= RELEASE START..." 3 | 4 | ## 版本号信息(需要手动指定) 5 | oldVersion="0.0.1" 6 | newVersion="0.0.2" 7 | projectName="sensitive" 8 | 9 | # release 项目版本 10 | ## snapshot 版本号 11 | snapshot_version=${oldVersion}"-SNAPSHOT" 12 | ## 新的版本号 13 | release_version=${oldVersion} 14 | 15 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${snapshot_version} -DnewVersion=${release_version} 16 | mvn -N versions:update-child-modules 17 | mvn versions:commit 18 | echo "1. RELEASE ${snapshot_version} TO ${release_version} DONE." 19 | 20 | 21 | # 推送到 github 22 | git add . 23 | git commit -m "release branch ${oldVersion}" 24 | git push 25 | git status 26 | 27 | echo "2. PUSH TO GITHUB DONE." 28 | 29 | 30 | # 推送到 maven 中央仓库 31 | mvn clean deploy -P release 32 | 33 | echo "3. PUSH TO MAVEN CENTER DONE." 34 | 35 | # 合并到 master 分支 36 | branchName="release_"${oldVersion} # 分支名称 37 | git checkout master 38 | git pull 39 | git checkout ${branchName} 40 | git rebase master 41 | git checkout master 42 | git merge ${branchName} 43 | git push 44 | 45 | echo "4. MERGE TO MASTER DONE." 46 | 47 | 48 | # 拉取新的分支 49 | newBranchName="release_"${newVersion} 50 | git branch ${newBranchName} 51 | git checkout ${newBranchName} 52 | git push --set-upstream origin ${newBranchName} 53 | 54 | echo "5. NEW BRANCH DONE." 55 | 56 | # 修改新分支的版本号 57 | ## snapshot 版本号 58 | snapshot_new_version=${newVersion}"-SNAPSHOT" 59 | mvn versions:set -DgroupId=com.github.houbb -DartifactId=${projectName} -DoldVersion=${release_version} -DnewVersion=${snapshot_new_version} 60 | mvn -N versions:update-child-modules 61 | mvn versions:commit 62 | 63 | git add . 64 | git commit -m "modify branch ${release_version} TO ${snapshot_new_version}" 65 | git push 66 | git status 67 | echo "6. MODIFY ${release_version} TO ${snapshot_new_version} DONE." 68 | 69 | echo "============================= RELEASE END..." 70 | 71 | 72 | # 使用方式: 73 | # 1. 赋值权限: chmod +x ./release.sh 74 | # 2. 执行: ./release.sh 75 | # Last Update Time: 2018-01-20 12:07:34 76 | # Author: houbb 77 | 78 | 79 | -------------------------------------------------------------------------------- /release.bat: -------------------------------------------------------------------------------- 1 | :: 用于 release 当前项目(windows) 2 | :: author: houbb 3 | :: LastUpdateTime: 2018-1-22 09:08:52 4 | :: 用法:双击运行,或者当前路径 cmd 直接输入 release.bat 5 | 6 | :: 关闭回显 7 | @echo OFF 8 | 9 | ECHO "============================= RELEASE START..." 10 | 11 | :: 版本号信息(需要手动指定) 12 | :::: 旧版本名称 13 | SET version=0.1.4 14 | :::: 新版本名称 15 | SET newVersion=0.1.5 16 | :::: 组织名称 17 | SET groupName=com.github.houbb 18 | :::: 项目名称 19 | SET projectName=mq 20 | 21 | :: release 项目版本 22 | :::: snapshot 版本号 23 | SET snapshot_version=%version%"-SNAPSHOT" 24 | :::: 新的版本号 25 | SET release_version=%version% 26 | 27 | call mvn versions:set -DgroupId=%groupName% -DartifactId=%projectName% -DoldVersion=%snapshot_version% -DnewVersion=%release_version% 28 | call mvn -N versions:update-child-modules 29 | call mvn versions:commit 30 | call echo "1. RELEASE %snapshot_version% TO %release_version% DONE." 31 | 32 | :: 推送到 github 33 | git add . 34 | git commit -m "release branch %version%" 35 | git push 36 | git status 37 | 38 | ECHO "2. PUSH TO GITHUB DONE." 39 | 40 | :: 发布到 mvn 中央仓库 41 | mvn clean deploy -P release 42 | 43 | ECHO "3. PUSH TO MAVEN CENTER DONE." 44 | 45 | :: 合并到 master 分支 46 | :::: 分支名称 47 | ::SET branchName="release_"%version% 48 | ::git checkout master 49 | ::git pull 50 | ::git checkout %branchName% 51 | ::git rebase master 52 | ::git checkout master 53 | ::git merge %branchName% 54 | ::git push 55 | :: 56 | ::ECHO "3. MERGE TO MASTER DONE." 57 | 58 | 59 | :::: 拉取新的分支 60 | ::SET newBranchName="release_"%newVersion% 61 | ::git branch %newBranchName% 62 | ::git checkout %newBranchName% 63 | ::git push --set-upstream origin %newBranchName% 64 | :: 65 | ::ECHO "4. NEW BRANCH DONE." 66 | :: 67 | :::: 修改新分支的版本号 68 | ::SET snapshot_new_version=%newVersion%"-SNAPSHOT" 69 | ::call mvn versions:set -DgroupId=%groupName% -DartifactId=%projectName% -DoldVersion=%release_version% -DnewVersion=%snapshot_new_version% 70 | ::call mvn -N versions:update-child-modules 71 | ::call mvn versions:commit 72 | :: 73 | ::git add . 74 | ::git commit -m "modify branch %release_version% TO %snapshot_new_version%" 75 | ::git push 76 | ::git status 77 | ::ECHO "5. MODIFY %release_version% TO %snapshot_new_version% DONE." 78 | :: 79 | ::ECHO "============================= RELEASE END..." 80 | 81 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/dto/consumer/ConsumerSubscribeBo.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.dto.consumer; 2 | 3 | import com.github.houbb.load.balance.support.server.IServer; 4 | import com.github.houbb.mq.common.rpc.RpcAddress; 5 | 6 | import java.util.Objects; 7 | 8 | /** 9 | * 消费者注册业务对象 10 | * @author binbin.hou 11 | * @since 0.0.3 12 | */ 13 | public class ConsumerSubscribeBo extends RpcAddress implements IServer { 14 | 15 | /** 16 | * 分组名称 17 | * @since 0.0.3 18 | */ 19 | private String groupName; 20 | 21 | /** 22 | * 标题名称 23 | * @since 0.0.3 24 | */ 25 | private String topicName; 26 | 27 | /** 28 | * 标签正则 29 | * @since 0.0.3 30 | */ 31 | private String tagRegex; 32 | 33 | /** 34 | * 通道标识 35 | * @since 0.0.3 36 | */ 37 | private String channelId; 38 | 39 | public String getGroupName() { 40 | return groupName; 41 | } 42 | 43 | public void setGroupName(String groupName) { 44 | this.groupName = groupName; 45 | } 46 | 47 | public String getTopicName() { 48 | return topicName; 49 | } 50 | 51 | public void setTopicName(String topicName) { 52 | this.topicName = topicName; 53 | } 54 | 55 | public String getTagRegex() { 56 | return tagRegex; 57 | } 58 | 59 | public void setTagRegex(String tagRegex) { 60 | this.tagRegex = tagRegex; 61 | } 62 | 63 | public String getChannelId() { 64 | return channelId; 65 | } 66 | 67 | public void setChannelId(String channelId) { 68 | this.channelId = channelId; 69 | } 70 | 71 | @Override 72 | public boolean equals(Object object) { 73 | if (this == object) return true; 74 | if (object == null || getClass() != object.getClass()) return false; 75 | ConsumerSubscribeBo that = (ConsumerSubscribeBo) object; 76 | return Objects.equals(groupName, that.groupName) && 77 | Objects.equals(topicName, that.topicName) && 78 | Objects.equals(tagRegex, that.tagRegex) && 79 | Objects.equals(channelId, that.channelId); 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return Objects.hash(groupName, topicName, tagRegex, channelId); 85 | } 86 | 87 | } 88 | 89 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/support/broker/IProducerBrokerService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.support.broker; 2 | 3 | import com.github.houbb.mq.common.api.Destroyable; 4 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 5 | import com.github.houbb.mq.common.dto.req.MqMessage; 6 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 7 | import com.github.houbb.mq.producer.dto.SendBatchResult; 8 | import com.github.houbb.mq.producer.dto.SendResult; 9 | import io.netty.channel.Channel; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 0.0.5 16 | */ 17 | public interface IProducerBrokerService extends Destroyable { 18 | 19 | /** 20 | * 初始化列表 21 | * @param config 配置 22 | * @since 0.0.5 23 | */ 24 | void initChannelFutureList(final ProducerBrokerConfig config); 25 | 26 | /** 27 | * 注册到服务端 28 | * @since 0.0.5 29 | */ 30 | void registerToBroker(); 31 | 32 | /** 33 | * 调用服务端 34 | * @param channel 调用通道 35 | * @param commonReq 通用请求 36 | * @param respClass 类 37 | * @param 泛型 38 | * @param 结果 39 | * @return 结果 40 | * @since 0.0.5 41 | */ 42 | R callServer(Channel channel, 43 | T commonReq, 44 | Class respClass); 45 | 46 | /** 47 | * 获取请求通道 48 | * @param key 标识 49 | * @return 结果 50 | * @since 0.0.5 51 | */ 52 | Channel getChannel(String key); 53 | 54 | /** 55 | * 同步发送消息 56 | * @param mqMessage 消息类型 57 | * @return 结果 58 | */ 59 | SendResult send(final MqMessage mqMessage); 60 | 61 | /** 62 | * 单向发送消息 63 | * @param mqMessage 消息类型 64 | * @return 结果 65 | */ 66 | SendResult sendOneWay(final MqMessage mqMessage); 67 | 68 | 69 | /** 70 | * 同步发送消息-批量 71 | * 1. 必须具有相同的 shardingKey,如果不同则忽略。 72 | * @param mqMessageList 消息类型 73 | * @return 结果 74 | * @since 0.1.3 75 | */ 76 | SendBatchResult sendBatch(final List mqMessageList); 77 | 78 | /** 79 | * 单向发送消息-批量 80 | * @param mqMessageList 消息类型 81 | * @return 结果 82 | * @since 0.1.3 83 | */ 84 | SendBatchResult sendOneWayBatch(final List mqMessageList); 85 | 86 | } 87 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/constant/MethodType.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.constant; 2 | 3 | /** 4 | * @author binbin.hou 5 | * @since 1.0.0 6 | */ 7 | public class MethodType { 8 | 9 | /** 10 | * 生产者发送消息 11 | */ 12 | public static final String P_SEND_MSG = "P_SEND_MSG"; 13 | 14 | /** 15 | * 生产者发送消息 16 | * @since 0.0.3 17 | */ 18 | public static final String P_SEND_MSG_ONE_WAY = "P_SEND_MSG_ONE_WAY"; 19 | 20 | /** 21 | * 生产者注册 22 | * @since 0.0.3 23 | */ 24 | public static final String P_REGISTER = "P_REGISTER"; 25 | 26 | /** 27 | * 生产者取消注册 28 | * @since 0.0.3 29 | */ 30 | public static final String P_UN_REGISTER = "P_UN_REGISTER"; 31 | 32 | 33 | /** 34 | * 消费者注册 35 | * @since 0.0.3 36 | */ 37 | public static final String C_REGISTER = "C_REGISTER"; 38 | 39 | /** 40 | * 消费者取消注册 41 | * @since 0.0.3 42 | */ 43 | public static final String C_UN_REGISTER = "C_UN_REGISTER"; 44 | 45 | /** 46 | * 消费者订阅 47 | * @since 0.0.3 48 | */ 49 | public static final String C_SUBSCRIBE = "C_SUBSCRIBE"; 50 | 51 | /** 52 | * 消费者取消订阅 53 | * @since 0.0.3 54 | */ 55 | public static final String C_UN_SUBSCRIBE = "C_UN_SUBSCRIBE"; 56 | 57 | /** 58 | * 消费者消息主动拉取 59 | * @since 0.0.3 60 | */ 61 | public static final String C_MESSAGE_PULL = "C_MESSAGE_PULL"; 62 | 63 | /** 64 | * 消费者心跳 65 | * @since 0.0.6 66 | */ 67 | public static final String C_HEARTBEAT = "C_HEARTBEAT"; 68 | 69 | /** 70 | * 消费者消费状态 71 | * @since 0.1.0 72 | */ 73 | public static final String C_CONSUMER_STATUS = "C_CONSUMER_STATUS"; 74 | 75 | /** 76 | * 中间人消息推送 77 | * @since 0.0.3 78 | */ 79 | public static final String B_MESSAGE_PUSH = "B_MESSAGE_PUSH"; 80 | 81 | /** 82 | * 消费者消费状态-批量 83 | * @since 0.1.3 84 | */ 85 | public static final String C_CONSUMER_STATUS_BATCH = "C_CONSUMER_STATUS_BATCH"; 86 | 87 | /** 88 | * 生产者发送消息-批量 89 | * @since 0.1.3 90 | */ 91 | public static final String P_SEND_MSG_BATCH = "P_SEND_MSG_BATCH"; 92 | 93 | /** 94 | * 生产者发送消息-批量 95 | * @since 0.1.3 96 | */ 97 | public static final String P_SEND_MSG_ONE_WAY_BATCH = "P_SEND_MSG_ONE_WAY_BATCH"; 98 | 99 | } 100 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/handler/MqProducerHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.handler; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.lang.StringUtil; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 8 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 9 | import com.github.houbb.mq.common.util.ChannelUtil; 10 | import io.netty.buffer.ByteBuf; 11 | import io.netty.channel.ChannelHandlerContext; 12 | import io.netty.channel.SimpleChannelInboundHandler; 13 | 14 | /** 15 | * @author binbin.hou 16 | * @since 1.0.0 17 | */ 18 | public class MqProducerHandler extends SimpleChannelInboundHandler { 19 | 20 | private static final Log log = LogFactory.getLog(MqProducerHandler.class); 21 | 22 | /** 23 | * 调用管理类 24 | */ 25 | private IInvokeService invokeService; 26 | 27 | public void setInvokeService(IInvokeService invokeService) { 28 | this.invokeService = invokeService; 29 | } 30 | 31 | @Override 32 | protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 33 | ByteBuf byteBuf = (ByteBuf)msg; 34 | byte[] bytes = new byte[byteBuf.readableBytes()]; 35 | byteBuf.readBytes(bytes); 36 | 37 | String text = new String(bytes); 38 | log.debug("[Client] channelId {} 接收到消息 {}", ChannelUtil.getChannelId(ctx), text); 39 | 40 | RpcMessageDto rpcMessageDto = null; 41 | try { 42 | rpcMessageDto = JSON.parseObject(bytes, RpcMessageDto.class); 43 | } catch (Exception exception) { 44 | log.error("RpcMessageDto json 格式转换异常 {}", JSON.parse(bytes)); 45 | return; 46 | } 47 | 48 | if(rpcMessageDto.isRequest()) { 49 | // 请求类 50 | final String methodType = rpcMessageDto.getMethodType(); 51 | final String json = rpcMessageDto.getJson(); 52 | } else { 53 | // 丢弃掉 traceId 为空的信息 54 | if(StringUtil.isBlank(rpcMessageDto.getTraceId())) { 55 | log.debug("[Client] response traceId 为空,直接丢弃", JSON.toJSON(rpcMessageDto)); 56 | return; 57 | } 58 | 59 | invokeService.addResponse(rpcMessageDto.getTraceId(), rpcMessageDto); 60 | log.debug("[Client] response is :{}", JSON.toJSON(rpcMessageDto)); 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 变更日志 2 | 3 | | 类型 | 说明 | 4 | |:----|:----| 5 | | A | 新增 | 6 | | U | 更新 | 7 | | D | 删除 | 8 | | T | 测试 | 9 | | O | 优化 | 10 | | F | 修复BUG | 11 | 12 | # release_0.0.1 13 | 14 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 15 | |:---|:---|:---|:---|:--| 16 | | 1 | A | 服务端代码的启动 | 2022-03-27 23:15:03 | | 17 | | 2 | A | 客户端代码的启动 | 2022-03-27 23:15:03 | | 18 | 19 | # release_0.0.2 20 | 21 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 22 | |:---|:---|:---|:---|:--| 23 | | 1 | A | 实现客户端调用服务端 | 2022-03-27 23:15:03 | | 24 | | 2 | A | 解决粘包问题 | 2022-03-27 23:15:03 | | 25 | 26 | # release_0.0.3 27 | 28 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 29 | |:---|:---|:---|:---|:--| 30 | | 1 | A | 添加 broker 中间人 | 2022-04-05 23:15:03 | | 31 | 32 | # release_0.0.4 33 | 34 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 35 | |:---|:---|:---|:---|:--| 36 | | 1 | A | check 启动 broker 检测可用性配置 | 2022-04-05 23:15:03 | | 37 | | 2 | O | init 代码优化 | 2022-04-05 23:15:03 | | 38 | 39 | # release_0.0.5 40 | 41 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 42 | |:---|:---|:---|:---|:--| 43 | | 1 | A | 生产者添加关闭钩子函数 | 2022-04-05 23:15:03 | | 44 | | 2 | A | 消费者添加关闭钩子函数 | 2022-04-05 23:15:03 | | 45 | 46 | # release_0.0.6 47 | 48 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 49 | |:---|:---|:---|:---|:--| 50 | | 1 | A | 添加消费者心跳 | 2022-04-05 23:15:03 | | 51 | 52 | # release_0.0.7 53 | 54 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 55 | |:---|:---|:---|:---|:--| 56 | | 1 | A | 添加负载均衡 | 2022-04-08 23:15:03 | | 57 | 58 | # release_0.0.8 59 | 60 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 61 | |:---|:---|:---|:---|:--| 62 | | 1 | A | 添加负载均衡 | 2022-04-08 23:15:03 | | 63 | | 2 | O | 配置调整为 fluent | 2022-04-08 23:15:03 | | 64 | 65 | # release_0.0.9 66 | 67 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 68 | |:---|:---|:---|:---|:--| 69 | | 1 | A | 添加主动拉取消费者 | 2022-04-08 23:15:03 | | 70 | 71 | # release_0.1.0 72 | 73 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 74 | |:---|:---|:---|:---|:--| 75 | | 1 | A | 添加主动拉取消费者消息回执 | 2022-04-09 23:15:03 | | 76 | 77 | # release_0.1.1 78 | 79 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 80 | |:---|:---|:---|:---|:--| 81 | | 1 | A | 调整状态更新入参 | 2022-04-10 23:15:03 | | 82 | | 2 | O | 优化工具类依赖 | 2022-04-10 23:15:03 | | 83 | 84 | # release_0.1.2 85 | 86 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 87 | |:---|:---|:---|:---|:--| 88 | | 1 | F | 修正状态ACK groupName | 2022-04-17 23:15:03 | | 89 | 90 | # release_0.1.3 91 | 92 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 93 | |:---|:---|:---|:---|:--| 94 | | 1 | A | 批量消息发送同步/异步 | 2022-04-17 23:15:03 | | 95 | | 2 | A | 批量消息状态 ACK | 2022-04-17 23:15:03 | | 96 | 97 | # release_0.1.4 98 | 99 | | 序号 | 变更类型 | 说明 | 时间 | 备注 | 100 | |:---|:---|:---|:---|:--| 101 | | 1 | A | 添加注册鉴权 | 2022-04-19 23:15:03 | 提升安全性 | 102 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/api/LocalBrokerProducerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * rpc All rights reserved. 4 | */ 5 | 6 | package com.github.houbb.mq.broker.support.api; 7 | 8 | 9 | import com.github.houbb.log.integration.core.Log; 10 | import com.github.houbb.log.integration.core.LogFactory; 11 | import com.github.houbb.mq.broker.api.IBrokerProducerService; 12 | import com.github.houbb.mq.broker.dto.BrokerServiceEntryChannel; 13 | import com.github.houbb.mq.broker.dto.ServiceEntry; 14 | import com.github.houbb.mq.broker.resp.MqBrokerRespCode; 15 | import com.github.houbb.mq.broker.utils.InnerChannelUtils; 16 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 17 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 18 | import com.github.houbb.mq.common.resp.MqException; 19 | import com.github.houbb.mq.common.util.ChannelUtil; 20 | import io.netty.channel.Channel; 21 | 22 | import java.util.Map; 23 | import java.util.concurrent.ConcurrentHashMap; 24 | 25 | /** 26 | *

生产者注册服务类

27 | * 28 | *
 Created: 2019/10/23 9:08 下午  
29 | *
 Project: rpc  
30 | * 31 | * @author houbinbin 32 | * @since 0.0.3 33 | */ 34 | public class LocalBrokerProducerService implements IBrokerProducerService { 35 | 36 | private static final Log log = LogFactory.getLog(LocalBrokerProducerService.class); 37 | 38 | private final Map registerMap = new ConcurrentHashMap<>(); 39 | 40 | @Override 41 | public MqCommonResp register(ServiceEntry serviceEntry, Channel channel) { 42 | final String channelId = ChannelUtil.getChannelId(channel); 43 | BrokerServiceEntryChannel entryChannel = InnerChannelUtils.buildEntryChannel(serviceEntry, channel); 44 | registerMap.put(channelId, entryChannel); 45 | 46 | 47 | MqCommonResp resp = new MqCommonResp(); 48 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 49 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 50 | return resp; 51 | } 52 | 53 | @Override 54 | public MqCommonResp unRegister(ServiceEntry serviceEntry, Channel channel) { 55 | final String channelId = ChannelUtil.getChannelId(channel); 56 | registerMap.remove(channelId); 57 | 58 | MqCommonResp resp = new MqCommonResp(); 59 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 60 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 61 | return resp; 62 | } 63 | 64 | @Override 65 | public ServiceEntry getServiceEntry(String channelId) { 66 | return registerMap.get(channelId); 67 | } 68 | 69 | @Override 70 | public void checkValid(String channelId) { 71 | if(!registerMap.containsKey(channelId)) { 72 | log.error("channelId: {} 未注册", channelId); 73 | throw new MqException(MqBrokerRespCode.P_REGISTER_CHANNEL_NOT_VALID); 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/rpc/RpcMessageDto.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.rpc; 2 | 3 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 4 | 5 | import java.io.Serializable; 6 | 7 | /** 8 | * @author binbin.hou 9 | * @since 1.0.0 10 | */ 11 | public class RpcMessageDto implements Serializable { 12 | 13 | /** 14 | * 请求时间 15 | */ 16 | private long requestTime; 17 | 18 | /** 19 | * 请求标识 20 | */ 21 | private String traceId; 22 | 23 | /** 24 | * 方法类型 25 | */ 26 | private String methodType; 27 | 28 | /** 29 | * 是否为请求消息 30 | */ 31 | private boolean isRequest; 32 | 33 | private String respCode; 34 | 35 | private String respMsg; 36 | 37 | private String json; 38 | 39 | public long getRequestTime() { 40 | return requestTime; 41 | } 42 | 43 | public void setRequestTime(long requestTime) { 44 | this.requestTime = requestTime; 45 | } 46 | 47 | public String getTraceId() { 48 | return traceId; 49 | } 50 | 51 | public void setTraceId(String traceId) { 52 | this.traceId = traceId; 53 | } 54 | 55 | public String getMethodType() { 56 | return methodType; 57 | } 58 | 59 | public void setMethodType(String methodType) { 60 | this.methodType = methodType; 61 | } 62 | 63 | public boolean isRequest() { 64 | return isRequest; 65 | } 66 | 67 | public void setRequest(boolean request) { 68 | isRequest = request; 69 | } 70 | 71 | public String getRespCode() { 72 | return respCode; 73 | } 74 | 75 | public void setRespCode(String respCode) { 76 | this.respCode = respCode; 77 | } 78 | 79 | public String getRespMsg() { 80 | return respMsg; 81 | } 82 | 83 | public void setRespMsg(String respMsg) { 84 | this.respMsg = respMsg; 85 | } 86 | 87 | public String getJson() { 88 | return json; 89 | } 90 | 91 | public void setJson(String json) { 92 | this.json = json; 93 | } 94 | 95 | public static RpcMessageDto timeout() { 96 | RpcMessageDto dto = new RpcMessageDto(); 97 | dto.setRespCode(MqCommonRespCode.TIMEOUT.getCode()); 98 | dto.setRespMsg(MqCommonRespCode.TIMEOUT.getMsg()); 99 | 100 | return dto; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "RpcMessageDto{" + 106 | "requestTime=" + requestTime + 107 | ", traceId='" + traceId + '\'' + 108 | ", methodType='" + methodType + '\'' + 109 | ", isRequest=" + isRequest + 110 | ", respCode='" + respCode + '\'' + 111 | ", respMsg='" + respMsg + '\'' + 112 | ", json='" + json + '\'' + 113 | '}'; 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/broker/IConsumerBrokerService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.support.broker; 2 | 3 | import com.github.houbb.mq.common.api.Destroyable; 4 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 5 | import com.github.houbb.mq.common.dto.req.component.MqConsumerUpdateStatusDto; 6 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 7 | import com.github.houbb.mq.common.dto.resp.MqConsumerPullResp; 8 | import com.github.houbb.mq.common.resp.ConsumerStatus; 9 | import io.netty.channel.Channel; 10 | 11 | import java.util.List; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 0.0.5 16 | */ 17 | public interface IConsumerBrokerService extends Destroyable { 18 | 19 | /** 20 | * 初始化列表 21 | * @param config 配置 22 | * @since 0.0.5 23 | */ 24 | void initChannelFutureList(final ConsumerBrokerConfig config); 25 | 26 | /** 27 | * 注册到服务端 28 | * @since 0.0.5 29 | */ 30 | void registerToBroker(); 31 | 32 | /** 33 | * 调用服务端 34 | * @param channel 调用通道 35 | * @param commonReq 通用请求 36 | * @param respClass 类 37 | * @param 泛型 38 | * @param 结果 39 | * @return 结果 40 | * @since 0.0.5 41 | */ 42 | R callServer(Channel channel, 43 | T commonReq, 44 | Class respClass); 45 | 46 | /** 47 | * 获取请求通道 48 | * @param key 标识 49 | * @return 结果 50 | * @since 0.0.5 51 | */ 52 | Channel getChannel(String key); 53 | 54 | /** 55 | * 订阅 56 | * @param topicName topic 名称 57 | * @param tagRegex 标签正则 58 | * @param consumerType 消费者类型 59 | */ 60 | void subscribe(String topicName, String tagRegex, String consumerType); 61 | 62 | /** 63 | * 取消订阅 64 | * @param topicName topic 名称 65 | * @param tagRegex 标签正则 66 | * @param consumerType 消费者类型 67 | */ 68 | void unSubscribe(String topicName, String tagRegex, String consumerType); 69 | 70 | /** 71 | * 心跳 72 | * @since 0.0.6 73 | */ 74 | void heartbeat(); 75 | 76 | /** 77 | * 拉取消息 78 | * @param topicName 标题名称 79 | * @param tagRegex 标签正则 80 | * @param fetchSize 大小 81 | * @return 结果 82 | */ 83 | MqConsumerPullResp pull(String topicName, 84 | String tagRegex, 85 | int fetchSize); 86 | 87 | /** 88 | * 消费状态回执 89 | * @param messageId 消息唯一标识 90 | * @param consumerStatus 消费状态 91 | * @return 结果 92 | * @since 0.1.0 93 | */ 94 | MqCommonResp consumerStatusAck(String messageId, 95 | ConsumerStatus consumerStatus); 96 | 97 | /** 98 | * 消费状态回执-批量 99 | * @param statusDtoList 状态列表 100 | * @return 结果 101 | * @since 0.1.3 102 | */ 103 | MqCommonResp consumerStatusAckBatch(List statusDtoList); 104 | 105 | } 106 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/api/IBrokerConsumerService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019. houbinbin Inc. 3 | * rpc All rights reserved. 4 | */ 5 | 6 | package com.github.houbb.mq.broker.api; 7 | 8 | 9 | import com.github.houbb.load.balance.api.ILoadBalance; 10 | import com.github.houbb.mq.broker.dto.ChannelGroupNameDto; 11 | import com.github.houbb.mq.broker.dto.ServiceEntry; 12 | import com.github.houbb.mq.broker.dto.consumer.ConsumerSubscribeBo; 13 | import com.github.houbb.mq.broker.dto.consumer.ConsumerSubscribeReq; 14 | import com.github.houbb.mq.broker.dto.consumer.ConsumerUnSubscribeReq; 15 | import com.github.houbb.mq.common.dto.req.MqHeartBeatReq; 16 | import com.github.houbb.mq.common.dto.req.MqMessage; 17 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 18 | import io.netty.channel.Channel; 19 | 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | *

消费者注册服务类

25 | * 26 | *
 Created: 2019/10/23 9:08 下午  
27 | *
 Project: rpc  
28 | * 29 | * @author houbinbin 30 | * @since 0.0.3 31 | */ 32 | public interface IBrokerConsumerService { 33 | 34 | /** 35 | * 设置负载均衡策略 36 | * @param loadBalance 负载均衡 37 | * @since 0.0.7 38 | */ 39 | void loadBalance(ILoadBalance loadBalance); 40 | 41 | /** 42 | * 注册当前服务信息 43 | * (1)将该服务通过 {@link ServiceEntry#getGroupName()} 进行分组 44 | * 订阅了这个 serviceId 的所有客户端 45 | * @param serviceEntry 注册当前服务信息 46 | * @param channel channel 47 | * @since 0.0.3 48 | */ 49 | MqCommonResp register(final ServiceEntry serviceEntry, Channel channel); 50 | 51 | /** 52 | * 注销当前服务信息 53 | * @param serviceEntry 注册当前服务信息 54 | * @param channel channel 55 | * @since 0.0.3 56 | */ 57 | MqCommonResp unRegister(final ServiceEntry serviceEntry, Channel channel); 58 | 59 | /** 60 | * 监听服务信息 61 | * (1)监听之后,如果有任何相关的机器信息发生变化,则进行推送。 62 | * (2)内置的信息,需要传送 ip 信息到注册中心。 63 | * 64 | * @param serviceEntry 客户端明细信息 65 | * @param clientChannel 客户端 channel 信息 66 | * @since 0.0.3 67 | */ 68 | MqCommonResp subscribe(final ConsumerSubscribeReq serviceEntry, 69 | final Channel clientChannel); 70 | 71 | /** 72 | * 取消监听服务信息 73 | * (1)监听之后,如果有任何相关的机器信息发生变化,则进行推送。 74 | * (2)内置的信息,需要传送 ip 信息到注册中心。 75 | * 76 | * @param serviceEntry 客户端明细信息 77 | * @param clientChannel 客户端 channel 信息 78 | * @since 0.0.3 79 | */ 80 | MqCommonResp unSubscribe(final ConsumerUnSubscribeReq serviceEntry, 81 | final Channel clientChannel); 82 | 83 | /** 84 | * 获取所有匹配的消费者-主动推送 85 | * 1. 同一个 groupName 只返回一个,注意负载均衡 86 | * 2. 返回匹配当前消息的消费者通道 87 | * 88 | * @param mqMessage 消息体 89 | * @return 结果 90 | */ 91 | List getPushSubscribeList(MqMessage mqMessage); 92 | 93 | /** 94 | * 心跳 95 | * @param mqHeartBeatReq 入参 96 | * @param channel 渠道 97 | * @since 0.0.6 98 | */ 99 | void heartbeat(final MqHeartBeatReq mqHeartBeatReq, Channel channel); 100 | 101 | /** 102 | * 校验有效性 103 | * @param channelId 通道唯一标识 104 | * @since 0.1.4 105 | */ 106 | void checkValid(final String channelId); 107 | 108 | } 109 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/push/BrokerPushContext.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.push; 2 | 3 | import com.github.houbb.mq.broker.dto.ChannelGroupNameDto; 4 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPut; 5 | import com.github.houbb.mq.broker.support.persist.IMqBrokerPersist; 6 | import com.github.houbb.mq.common.dto.req.MqMessage; 7 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 8 | import io.netty.channel.Channel; 9 | 10 | import java.util.List; 11 | import java.util.Map; 12 | 13 | /** 14 | * @author binbin.hou 15 | * @since 0.0.3 16 | */ 17 | public class BrokerPushContext { 18 | 19 | private IMqBrokerPersist mqBrokerPersist; 20 | 21 | private MqMessagePersistPut mqMessagePersistPut; 22 | 23 | private List channelList; 24 | 25 | private IInvokeService invokeService; 26 | 27 | /** 28 | * 获取响应超时时间 29 | * @since 0.0.3 30 | */ 31 | private long respTimeoutMills; 32 | 33 | /** 34 | * 推送最大尝试次数 35 | * @since 0.0.8 36 | */ 37 | private int pushMaxAttempt; 38 | 39 | /** 40 | * channel 标识和 groupName map 41 | * @since 0.1.1 42 | */ 43 | private Map channelGroupMap; 44 | 45 | public static BrokerPushContext newInstance() { 46 | return new BrokerPushContext(); 47 | } 48 | 49 | public IMqBrokerPersist mqBrokerPersist() { 50 | return mqBrokerPersist; 51 | } 52 | 53 | public BrokerPushContext mqBrokerPersist(IMqBrokerPersist mqBrokerPersist) { 54 | this.mqBrokerPersist = mqBrokerPersist; 55 | return this; 56 | } 57 | 58 | public MqMessagePersistPut mqMessagePersistPut() { 59 | return mqMessagePersistPut; 60 | } 61 | 62 | public BrokerPushContext mqMessagePersistPut(MqMessagePersistPut mqMessagePersistPut) { 63 | this.mqMessagePersistPut = mqMessagePersistPut; 64 | return this; 65 | } 66 | 67 | public List channelList() { 68 | return channelList; 69 | } 70 | 71 | public BrokerPushContext channelList(List channelList) { 72 | this.channelList = channelList; 73 | return this; 74 | } 75 | 76 | public IInvokeService invokeService() { 77 | return invokeService; 78 | } 79 | 80 | public BrokerPushContext invokeService(IInvokeService invokeService) { 81 | this.invokeService = invokeService; 82 | return this; 83 | } 84 | 85 | public long respTimeoutMills() { 86 | return respTimeoutMills; 87 | } 88 | 89 | public BrokerPushContext respTimeoutMills(long respTimeoutMills) { 90 | this.respTimeoutMills = respTimeoutMills; 91 | return this; 92 | } 93 | 94 | public int pushMaxAttempt() { 95 | return pushMaxAttempt; 96 | } 97 | 98 | public BrokerPushContext pushMaxAttempt(int pushMaxAttempt) { 99 | this.pushMaxAttempt = pushMaxAttempt; 100 | return this; 101 | } 102 | 103 | public Map channelGroupMap() { 104 | return channelGroupMap; 105 | } 106 | 107 | public BrokerPushContext channelGroupMap(Map channelGroupMap) { 108 | this.channelGroupMap = channelGroupMap; 109 | return this; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/hook/DefaultShutdownHook.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.hook; 2 | 3 | import com.github.houbb.heaven.util.util.DateUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.mq.common.api.Destroyable; 7 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 8 | import com.github.houbb.mq.common.support.status.IStatusManager; 9 | import com.github.houbb.mq.common.support.status.StatusManager; 10 | 11 | /** 12 | * 默认的 hook 实现 13 | *

project: rpc-ClientShutdownHook

14 | *

create on 2019/10/30 20:26

15 | * 16 | * @author Administrator 17 | * @since 0.0.5 18 | */ 19 | public class DefaultShutdownHook extends AbstractShutdownHook { 20 | 21 | /** 22 | * DefaultShutdownHook logger 23 | */ 24 | private static final Log logger = LogFactory.getLog(DefaultShutdownHook.class); 25 | 26 | /** 27 | * 调用管理类 28 | * @since 0.0.5 29 | */ 30 | private IInvokeService invokeService; 31 | 32 | /** 33 | * 销毁管理类 34 | * @since 0.0.5 35 | */ 36 | private Destroyable destroyable; 37 | 38 | /** 39 | * 状态管理类 40 | * @since 0.0.5 41 | */ 42 | private IStatusManager statusManager; 43 | 44 | /** 45 | * 为剩余的请求等待时间 46 | * @since 0.0.5 47 | */ 48 | private long waitMillsForRemainRequest = 60 * 1000; 49 | 50 | public IInvokeService getInvokeService() { 51 | return invokeService; 52 | } 53 | 54 | public void setInvokeService(IInvokeService invokeService) { 55 | this.invokeService = invokeService; 56 | } 57 | 58 | public Destroyable getDestroyable() { 59 | return destroyable; 60 | } 61 | 62 | public void setDestroyable(Destroyable destroyable) { 63 | this.destroyable = destroyable; 64 | } 65 | 66 | public IStatusManager getStatusManager() { 67 | return statusManager; 68 | } 69 | 70 | public void setStatusManager(IStatusManager statusManager) { 71 | this.statusManager = statusManager; 72 | } 73 | 74 | public long getWaitMillsForRemainRequest() { 75 | return waitMillsForRemainRequest; 76 | } 77 | 78 | public void setWaitMillsForRemainRequest(long waitMillsForRemainRequest) { 79 | this.waitMillsForRemainRequest = waitMillsForRemainRequest; 80 | } 81 | 82 | /** 83 | * (1)设置 status 状态为等待关闭 84 | * (2)查看是否 {@link IInvokeService#remainsRequest()} 是否包含请求 85 | * (3)超时检测-可以不添加,如果难以关闭成功,直接强制关闭即可。 86 | * (4)关闭所有线程池资源信息 87 | * (5)设置状态为成功关闭 88 | */ 89 | @Override 90 | protected void doHook() { 91 | statusManager.status(false); 92 | // 设置状态为等待关闭 93 | logger.info("[Shutdown] set status to wait for shutdown."); 94 | 95 | // 循环等待当前执行的请求执行完成 96 | long startMills = System.currentTimeMillis(); 97 | while (invokeService.remainsRequest()) { 98 | long currentMills = System.currentTimeMillis(); 99 | long costMills = currentMills - startMills; 100 | if(costMills >= waitMillsForRemainRequest) { 101 | logger.warn("[Shutdown] still remains request, but timeout, break."); 102 | break; 103 | } 104 | 105 | logger.debug("[Shutdown] still remains request, wait for a while."); 106 | DateUtil.sleep(100); 107 | } 108 | 109 | // 销毁 110 | destroyable.destroyAll(); 111 | 112 | // 设置状态为关闭成功 113 | statusManager.status(false); 114 | logger.info("[Shutdown] set status to shutdown success."); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/util/ChannelFutureUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.util; 2 | 3 | import com.github.houbb.heaven.util.util.CollectionUtil; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 7 | import com.github.houbb.mq.common.resp.MqException; 8 | import com.github.houbb.mq.common.rpc.RpcAddress; 9 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 10 | import io.netty.bootstrap.Bootstrap; 11 | import io.netty.channel.*; 12 | import io.netty.channel.nio.NioEventLoopGroup; 13 | import io.netty.channel.socket.nio.NioSocketChannel; 14 | import io.netty.handler.logging.LogLevel; 15 | import io.netty.handler.logging.LoggingHandler; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | /** 21 | * @author binbin.hou 22 | * @since 1.0.0 23 | */ 24 | public class ChannelFutureUtils { 25 | 26 | private static final Log log = LogFactory.getLog(ChannelFutureUtils.class); 27 | 28 | /** 29 | * 初始化列表 30 | * @param brokerAddress 地址 31 | * @param channelHandler 处理类 32 | * @param check 是否检测可用性 33 | * @return 结果 34 | * @since 0.0.4 35 | */ 36 | public static List initChannelFutureList(final String brokerAddress, 37 | final ChannelHandler channelHandler, 38 | final boolean check) { 39 | List addressList = InnerAddressUtils.initAddressList(brokerAddress); 40 | 41 | List list = new ArrayList<>(); 42 | for(RpcAddress rpcAddress : addressList) { 43 | try { 44 | final String address = rpcAddress.getAddress(); 45 | final int port = rpcAddress.getPort(); 46 | 47 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 48 | Bootstrap bootstrap = new Bootstrap(); 49 | ChannelFuture channelFuture = bootstrap.group(workerGroup) 50 | .channel(NioSocketChannel.class) 51 | .option(ChannelOption.SO_KEEPALIVE, true) 52 | .handler(new ChannelInitializer(){ 53 | @Override 54 | protected void initChannel(Channel ch) throws Exception { 55 | ch.pipeline() 56 | .addLast(new LoggingHandler(LogLevel.INFO)) 57 | .addLast(channelHandler); 58 | } 59 | }) 60 | .connect(address, port) 61 | .syncUninterruptibly(); 62 | 63 | log.info("启动客户端完成,监听 address: {}, port:{}", address, port); 64 | 65 | RpcChannelFuture rpcChannelFuture = new RpcChannelFuture(); 66 | rpcChannelFuture.setChannelFuture(channelFuture); 67 | rpcChannelFuture.setAddress(address); 68 | rpcChannelFuture.setPort(port); 69 | rpcChannelFuture.setWeight(rpcAddress.getWeight()); 70 | list.add(rpcChannelFuture); 71 | } catch (Exception exception) { 72 | log.error("注册到 broker 服务端异常", exception); 73 | if(check) { 74 | throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED); 75 | } 76 | } 77 | } 78 | 79 | if(check 80 | && CollectionUtil.isEmpty(list)) { 81 | log.error("check=true 且可用列表为空,启动失败。"); 82 | throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED); 83 | } 84 | return list; 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/support/broker/ProducerBrokerConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.support.broker; 2 | 3 | import com.github.houbb.load.balance.api.ILoadBalance; 4 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 5 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 6 | import com.github.houbb.mq.common.support.status.IStatusManager; 7 | 8 | /** 9 | * @author binbin.hou 10 | * @since 0.0.5 11 | */ 12 | public class ProducerBrokerConfig { 13 | 14 | /** 15 | * 分组名称 16 | */ 17 | private String groupName; 18 | 19 | /** 20 | * 中间人地址 21 | */ 22 | private String brokerAddress; 23 | 24 | /** 25 | * 调用管理服务 26 | * @since 0.0.2 27 | */ 28 | private IInvokeService invokeService; 29 | 30 | /** 31 | * 获取响应超时时间 32 | * @since 0.0.2 33 | */ 34 | private long respTimeoutMills; 35 | 36 | /** 37 | * 检测 broker 可用性 38 | * @since 0.0.4 39 | */ 40 | private boolean check; 41 | 42 | /** 43 | * 状态管理 44 | * @since 0.0.5 45 | */ 46 | private IStatusManager statusManager; 47 | 48 | /** 49 | * 负载均衡 50 | * @since 0.0.7 51 | */ 52 | private ILoadBalance loadBalance; 53 | 54 | /** 55 | * 最大尝试次数 56 | * @since 0.0.8 57 | */ 58 | private int maxAttempt; 59 | 60 | /** 61 | * 账户标识 62 | * @since 0.1.4 63 | */ 64 | private String appKey; 65 | 66 | /** 67 | * 账户密码 68 | * @since 0.1.4 69 | */ 70 | private String appSecret; 71 | 72 | public static ProducerBrokerConfig newInstance() { 73 | return new ProducerBrokerConfig(); 74 | } 75 | 76 | public String appKey() { 77 | return appKey; 78 | } 79 | 80 | public ProducerBrokerConfig appKey(String appKey) { 81 | this.appKey = appKey; 82 | return this; 83 | } 84 | 85 | public String appSecret() { 86 | return appSecret; 87 | } 88 | 89 | public ProducerBrokerConfig appSecret(String appSecret) { 90 | this.appSecret = appSecret; 91 | return this; 92 | } 93 | 94 | public int maxAttempt() { 95 | return maxAttempt; 96 | } 97 | 98 | public ProducerBrokerConfig maxAttempt(int maxAttempt) { 99 | this.maxAttempt = maxAttempt; 100 | return this; 101 | } 102 | 103 | public String groupName() { 104 | return groupName; 105 | } 106 | 107 | public ProducerBrokerConfig groupName(String groupName) { 108 | this.groupName = groupName; 109 | return this; 110 | } 111 | 112 | public String brokerAddress() { 113 | return brokerAddress; 114 | } 115 | 116 | public ProducerBrokerConfig brokerAddress(String brokerAddress) { 117 | this.brokerAddress = brokerAddress; 118 | return this; 119 | } 120 | 121 | public IInvokeService invokeService() { 122 | return invokeService; 123 | } 124 | 125 | public ProducerBrokerConfig invokeService(IInvokeService invokeService) { 126 | this.invokeService = invokeService; 127 | return this; 128 | } 129 | 130 | public long respTimeoutMills() { 131 | return respTimeoutMills; 132 | } 133 | 134 | public ProducerBrokerConfig respTimeoutMills(long respTimeoutMills) { 135 | this.respTimeoutMills = respTimeoutMills; 136 | return this; 137 | } 138 | 139 | public boolean check() { 140 | return check; 141 | } 142 | 143 | public ProducerBrokerConfig check(boolean check) { 144 | this.check = check; 145 | return this; 146 | } 147 | 148 | public IStatusManager statusManager() { 149 | return statusManager; 150 | } 151 | 152 | public ProducerBrokerConfig statusManager(IStatusManager statusManager) { 153 | this.statusManager = statusManager; 154 | return this; 155 | } 156 | 157 | public ILoadBalance loadBalance() { 158 | return loadBalance; 159 | } 160 | 161 | public ProducerBrokerConfig loadBalance(ILoadBalance loadBalance) { 162 | this.loadBalance = loadBalance; 163 | return this; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /mq-common/src/main/java/com/github/houbb/mq/common/support/invoke/impl/InvokeService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.common.support.invoke.impl; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.lang.ObjectUtil; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 8 | import com.github.houbb.mq.common.resp.MqException; 9 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 10 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 11 | 12 | import java.util.concurrent.ConcurrentHashMap; 13 | import java.util.concurrent.Executors; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | /** 17 | * 调用服务接口 18 | * @author binbin.hou 19 | * @since 1.0.0 20 | */ 21 | public class InvokeService implements IInvokeService { 22 | 23 | private static final Log logger = LogFactory.getLog(InvokeService.class); 24 | 25 | /** 26 | * 请求序列号 map 27 | * (1)这里后期如果要添加超时检测,可以添加对应的超时时间。 28 | * 可以把这里调整为 map 29 | * 30 | * key: seqId 唯一标识一个请求 31 | * value: 存入该请求最长的有效时间。用于定时删除和超时判断。 32 | * @since 0.0.2 33 | */ 34 | private final ConcurrentHashMap requestMap; 35 | 36 | /** 37 | * 响应结果 38 | * @since 1.0.0 39 | */ 40 | private final ConcurrentHashMap responseMap; 41 | 42 | public InvokeService() { 43 | requestMap = new ConcurrentHashMap<>(); 44 | responseMap = new ConcurrentHashMap<>(); 45 | 46 | final Runnable timeoutThread = new TimeoutCheckThread(requestMap, responseMap); 47 | Executors.newScheduledThreadPool(1) 48 | .scheduleAtFixedRate(timeoutThread,60, 60, TimeUnit.SECONDS); 49 | } 50 | 51 | @Override 52 | public IInvokeService addRequest(String seqId, long timeoutMills) { 53 | logger.debug("[Invoke] start add request for seqId: {}, timeoutMills: {}", seqId, 54 | timeoutMills); 55 | 56 | final long expireTime = System.currentTimeMillis()+timeoutMills; 57 | requestMap.putIfAbsent(seqId, expireTime); 58 | 59 | return this; 60 | } 61 | 62 | @Override 63 | public IInvokeService addResponse(String seqId, RpcMessageDto rpcResponse) { 64 | // 1. 判断是否有效 65 | Long expireTime = this.requestMap.get(seqId); 66 | // 如果为空,可能是这个结果已经超时了,被定时 job 移除之后,响应结果才过来。直接忽略 67 | if(ObjectUtil.isNull(expireTime)) { 68 | return this; 69 | } 70 | 71 | //2. 判断是否超时 72 | if(System.currentTimeMillis() > expireTime) { 73 | logger.debug("[Invoke] seqId:{} 信息已超时,直接返回超时结果。", seqId); 74 | rpcResponse = RpcMessageDto.timeout(); 75 | } 76 | 77 | // 这里放入之前,可以添加判断。 78 | // 如果 seqId 必须处理请求集合中,才允许放入。或者直接忽略丢弃。 79 | // 通知所有等待方 80 | responseMap.putIfAbsent(seqId, rpcResponse); 81 | logger.debug("[Invoke] 获取结果信息,seqId: {}, rpcResponse: {}", seqId, JSON.toJSON(rpcResponse)); 82 | logger.debug("[Invoke] seqId:{} 信息已经放入,通知所有等待方", seqId); 83 | 84 | // 移除对应的 requestMap 85 | requestMap.remove(seqId); 86 | logger.debug("[Invoke] seqId:{} remove from request map", seqId); 87 | 88 | // 同步锁 89 | synchronized (this) { 90 | this.notifyAll(); 91 | logger.debug("[Invoke] {} notifyAll()", seqId); 92 | } 93 | 94 | 95 | return this; 96 | } 97 | 98 | @Override 99 | public RpcMessageDto getResponse(String seqId) { 100 | try { 101 | RpcMessageDto rpcResponse = this.responseMap.get(seqId); 102 | if(ObjectUtil.isNotNull(rpcResponse)) { 103 | logger.debug("[Invoke] seq {} 对应结果已经获取: {}", seqId, rpcResponse); 104 | return rpcResponse; 105 | } 106 | 107 | // 进入等待 108 | while (rpcResponse == null) { 109 | logger.debug("[Invoke] seq {} 对应结果为空,进入等待", seqId); 110 | 111 | // 同步等待锁 112 | synchronized (this) { 113 | this.wait(); 114 | } 115 | 116 | logger.debug("[Invoke] {} wait has notified!", seqId); 117 | 118 | rpcResponse = this.responseMap.get(seqId); 119 | logger.debug("[Invoke] seq {} 对应结果已经获取: {}", seqId, rpcResponse); 120 | } 121 | 122 | return rpcResponse; 123 | } catch (InterruptedException e) { 124 | logger.error("获取响应异常", e); 125 | throw new MqException(MqCommonRespCode.RPC_GET_RESP_FAILED); 126 | } 127 | } 128 | 129 | @Override 130 | public boolean remainsRequest() { 131 | return this.requestMap.size() > 0; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/support/broker/ConsumerBrokerConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.support.broker; 2 | 3 | import com.github.houbb.load.balance.api.ILoadBalance; 4 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 5 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 6 | import com.github.houbb.mq.common.support.status.IStatusManager; 7 | import com.github.houbb.mq.consumer.support.listener.IMqListenerService; 8 | 9 | /** 10 | * @author binbin.hou 11 | * @since 0.0.5 12 | */ 13 | public class ConsumerBrokerConfig { 14 | 15 | /** 16 | * 分组名称 17 | */ 18 | private String groupName; 19 | 20 | /** 21 | * 中间人地址 22 | */ 23 | private String brokerAddress; 24 | 25 | /** 26 | * 调用管理服务 27 | * @since 0.0.2 28 | */ 29 | private IInvokeService invokeService; 30 | 31 | /** 32 | * 获取响应超时时间 33 | * @since 0.0.2 34 | */ 35 | private long respTimeoutMills; 36 | 37 | /** 38 | * 检测 broker 可用性 39 | * @since 0.0.4 40 | */ 41 | private boolean check; 42 | 43 | /** 44 | * 状态管理 45 | * @since 0.0.5 46 | */ 47 | private IStatusManager statusManager; 48 | 49 | /** 50 | * 监听服务类 51 | * @since 0.0.5 52 | */ 53 | private IMqListenerService mqListenerService; 54 | 55 | /** 56 | * 负载均衡 57 | * @since 0.0.7 58 | */ 59 | private ILoadBalance loadBalance; 60 | 61 | /** 62 | * 订阅最大尝试次数 63 | * @since 0.0.8 64 | */ 65 | private int subscribeMaxAttempt = 3; 66 | 67 | /** 68 | * 取消订阅最大尝试次数 69 | * @since 0.0.8 70 | */ 71 | private int unSubscribeMaxAttempt = 3; 72 | 73 | /** 74 | * 消费状态更新最大尝试次数 75 | * @since 0.1.0 76 | */ 77 | private int consumerStatusMaxAttempt = 3; 78 | 79 | /** 80 | * 账户标识 81 | * @since 0.1.4 82 | */ 83 | protected String appKey; 84 | 85 | /** 86 | * 账户密码 87 | * @since 0.1.4 88 | */ 89 | protected String appSecret; 90 | 91 | public String appKey() { 92 | return appKey; 93 | } 94 | 95 | public ConsumerBrokerConfig appKey(String appKey) { 96 | this.appKey = appKey; 97 | return this; 98 | } 99 | 100 | public String appSecret() { 101 | return appSecret; 102 | } 103 | 104 | public ConsumerBrokerConfig appSecret(String appSecret) { 105 | this.appSecret = appSecret; 106 | return this; 107 | } 108 | 109 | public int consumerStatusMaxAttempt() { 110 | return consumerStatusMaxAttempt; 111 | } 112 | 113 | public ConsumerBrokerConfig consumerStatusMaxAttempt(int consumerStatusMaxAttempt) { 114 | this.consumerStatusMaxAttempt = consumerStatusMaxAttempt; 115 | return this; 116 | } 117 | 118 | public static ConsumerBrokerConfig newInstance() { 119 | return new ConsumerBrokerConfig(); 120 | } 121 | 122 | public int subscribeMaxAttempt() { 123 | return subscribeMaxAttempt; 124 | } 125 | 126 | public ConsumerBrokerConfig subscribeMaxAttempt(int subscribeMaxAttempt) { 127 | this.subscribeMaxAttempt = subscribeMaxAttempt; 128 | return this; 129 | } 130 | 131 | public int unSubscribeMaxAttempt() { 132 | return unSubscribeMaxAttempt; 133 | } 134 | 135 | public ConsumerBrokerConfig unSubscribeMaxAttempt(int unSubscribeMaxAttempt) { 136 | this.unSubscribeMaxAttempt = unSubscribeMaxAttempt; 137 | return this; 138 | } 139 | 140 | public String groupName() { 141 | return groupName; 142 | } 143 | 144 | public ConsumerBrokerConfig groupName(String groupName) { 145 | this.groupName = groupName; 146 | return this; 147 | } 148 | 149 | public String brokerAddress() { 150 | return brokerAddress; 151 | } 152 | 153 | public ConsumerBrokerConfig brokerAddress(String brokerAddress) { 154 | this.brokerAddress = brokerAddress; 155 | return this; 156 | } 157 | 158 | public IInvokeService invokeService() { 159 | return invokeService; 160 | } 161 | 162 | public ConsumerBrokerConfig invokeService(IInvokeService invokeService) { 163 | this.invokeService = invokeService; 164 | return this; 165 | } 166 | 167 | public long respTimeoutMills() { 168 | return respTimeoutMills; 169 | } 170 | 171 | public ConsumerBrokerConfig respTimeoutMills(long respTimeoutMills) { 172 | this.respTimeoutMills = respTimeoutMills; 173 | return this; 174 | } 175 | 176 | public boolean check() { 177 | return check; 178 | } 179 | 180 | public ConsumerBrokerConfig check(boolean check) { 181 | this.check = check; 182 | return this; 183 | } 184 | 185 | public IStatusManager statusManager() { 186 | return statusManager; 187 | } 188 | 189 | public ConsumerBrokerConfig statusManager(IStatusManager statusManager) { 190 | this.statusManager = statusManager; 191 | return this; 192 | } 193 | 194 | public IMqListenerService mqListenerService() { 195 | return mqListenerService; 196 | } 197 | 198 | public ConsumerBrokerConfig mqListenerService(IMqListenerService mqListenerService) { 199 | this.mqListenerService = mqListenerService; 200 | return this; 201 | } 202 | 203 | public ILoadBalance loadBalance() { 204 | return loadBalance; 205 | } 206 | 207 | public ConsumerBrokerConfig loadBalance(ILoadBalance loadBalance) { 208 | this.loadBalance = loadBalance; 209 | return this; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/persist/LocalMqBrokerPersist.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.persist; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.util.CollectionUtil; 5 | import com.github.houbb.heaven.util.util.MapUtil; 6 | import com.github.houbb.heaven.util.util.regex.RegexUtil; 7 | import com.github.houbb.log.integration.core.Log; 8 | import com.github.houbb.log.integration.core.LogFactory; 9 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPut; 10 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPutBatch; 11 | import com.github.houbb.mq.common.constant.MessageStatusConst; 12 | import com.github.houbb.mq.common.dto.req.MqConsumerPullReq; 13 | import com.github.houbb.mq.common.dto.req.MqMessage; 14 | import com.github.houbb.mq.common.dto.req.component.MqConsumerUpdateStatusDto; 15 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 16 | import com.github.houbb.mq.common.dto.resp.MqConsumerPullResp; 17 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 18 | import io.netty.channel.Channel; 19 | 20 | import java.util.ArrayList; 21 | import java.util.Arrays; 22 | import java.util.List; 23 | import java.util.Map; 24 | import java.util.concurrent.ConcurrentHashMap; 25 | 26 | /** 27 | * 本地持久化策略 28 | * @author binbin.hou 29 | * @since 1.0.0 30 | */ 31 | public class LocalMqBrokerPersist implements IMqBrokerPersist { 32 | 33 | private static final Log log = LogFactory.getLog(LocalMqBrokerPersist.class); 34 | 35 | /** 36 | * 队列 37 | * ps: 这里只是简化实现,暂时不考虑并发等问题。 38 | */ 39 | private final Map> map = new ConcurrentHashMap<>(); 40 | 41 | //1. 接收 42 | //2. 持久化 43 | //3. 通知消费 44 | @Override 45 | public synchronized MqCommonResp put(MqMessagePersistPut put) { 46 | this.doPut(put); 47 | 48 | MqCommonResp commonResp = new MqCommonResp(); 49 | commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 50 | commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 51 | return commonResp; 52 | } 53 | 54 | private void doPut(MqMessagePersistPut put) { 55 | log.info("put elem: {}", JSON.toJSON(put)); 56 | 57 | MqMessage mqMessage = put.getMqMessage(); 58 | final String topic = mqMessage.getTopic(); 59 | 60 | // 放入元素 61 | MapUtil.putToListMap(map, topic, put); 62 | } 63 | 64 | @Override 65 | public MqCommonResp putBatch(List putList) { 66 | // 构建列表 67 | for(MqMessagePersistPut put : putList) { 68 | this.doPut(put); 69 | } 70 | 71 | MqCommonResp commonResp = new MqCommonResp(); 72 | commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 73 | commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 74 | return commonResp; 75 | } 76 | 77 | @Override 78 | public MqCommonResp updateStatus(String messageId, 79 | String consumerGroupName, 80 | String status) { 81 | // 这里性能比较差,所以不可以用于生产。仅作为测试验证 82 | this.doUpdateStatus(messageId, consumerGroupName, status); 83 | 84 | MqCommonResp commonResp = new MqCommonResp(); 85 | commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 86 | commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 87 | return commonResp; 88 | } 89 | 90 | 91 | private void doUpdateStatus(String messageId, 92 | String consumerGroupName, 93 | String status) { 94 | // 这里性能比较差,所以不可以用于生产。仅作为测试验证 95 | for(List list : map.values()) { 96 | for(MqMessagePersistPut put : list) { 97 | MqMessage mqMessage = put.getMqMessage(); 98 | if(mqMessage.getTraceId().equals(messageId)) { 99 | put.setMessageStatus(status); 100 | 101 | break; 102 | } 103 | } 104 | } 105 | } 106 | 107 | @Override 108 | public MqCommonResp updateStatusBatch(List statusDtoList) { 109 | for(MqConsumerUpdateStatusDto statusDto : statusDtoList) { 110 | this.doUpdateStatus(statusDto.getMessageId(), statusDto.getConsumerGroupName(), 111 | statusDto.getMessageStatus()); 112 | } 113 | 114 | MqCommonResp commonResp = new MqCommonResp(); 115 | commonResp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 116 | commonResp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 117 | return commonResp; 118 | } 119 | 120 | @Override 121 | public MqConsumerPullResp pull(MqConsumerPullReq pullReq, Channel channel) { 122 | //1. 拉取匹配的信息 123 | //2. 状态更新为代理中 124 | //3. 如何更新对应的消费状态呢? 125 | 126 | // 获取状态为 W 的订单 127 | final int fetchSize = pullReq.getSize(); 128 | final String topic = pullReq.getTopicName(); 129 | final String tagRegex = pullReq.getTagRegex(); 130 | 131 | List resultList = new ArrayList<>(fetchSize); 132 | List putList = map.get(topic); 133 | // 性能比较差 134 | if(CollectionUtil.isNotEmpty(putList)) { 135 | for(MqMessagePersistPut put : putList) { 136 | if(!isEnableStatus(put)) { 137 | continue; 138 | } 139 | 140 | final MqMessage mqMessage = put.getMqMessage(); 141 | List tagList = mqMessage.getTags(); 142 | if(RegexUtil.hasMatch(tagList, tagRegex)) { 143 | // 设置为处理中 144 | // TODO: 消息的最终状态什么时候更新呢? 145 | // 可以给 broker 一个 ACK 146 | put.setMessageStatus(MessageStatusConst.TO_CONSUMER_PROCESS); 147 | resultList.add(mqMessage); 148 | } 149 | 150 | if(resultList.size() >= fetchSize) { 151 | break; 152 | } 153 | } 154 | } 155 | 156 | MqConsumerPullResp resp = new MqConsumerPullResp(); 157 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 158 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 159 | resp.setList(resultList); 160 | return resp; 161 | } 162 | 163 | private boolean isEnableStatus(final MqMessagePersistPut persistPut) { 164 | final String status = persistPut.getMessageStatus(); 165 | // 数据库可以设计一个字段,比如待消费时间,进行排序。 166 | // 这里只是简化实现,仅用于测试。 167 | List statusList = Arrays.asList(MessageStatusConst.WAIT_CONSUMER, 168 | MessageStatusConst.CONSUMER_LATER); 169 | return statusList.contains(status); 170 | } 171 | 172 | } 173 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/handler/MqConsumerHandler.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.handler; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.lang.StringUtil; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.common.constant.MethodType; 8 | import com.github.houbb.mq.common.dto.req.MqMessage; 9 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 10 | import com.github.houbb.mq.common.dto.resp.MqConsumerResultResp; 11 | import com.github.houbb.mq.common.resp.ConsumerStatus; 12 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 13 | import com.github.houbb.mq.common.resp.MqException; 14 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 15 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 16 | import com.github.houbb.mq.common.util.ChannelUtil; 17 | import com.github.houbb.mq.common.util.DelimiterUtil; 18 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 19 | import com.github.houbb.mq.consumer.support.listener.IMqListenerService; 20 | import com.github.houbb.mq.consumer.support.listener.MqConsumerListenerContext; 21 | import io.netty.buffer.ByteBuf; 22 | import io.netty.channel.ChannelHandlerContext; 23 | import io.netty.channel.SimpleChannelInboundHandler; 24 | 25 | /** 26 | * @author binbin.hou 27 | * @since 0.0.3 28 | */ 29 | public class MqConsumerHandler extends SimpleChannelInboundHandler { 30 | 31 | private static final Log log = LogFactory.getLog(MqConsumerHandler.class); 32 | 33 | /** 34 | * 调用管理类 35 | * @since 0.0.3 36 | */ 37 | private IInvokeService invokeService; 38 | 39 | /** 40 | * 消息监听服务类 41 | * @since 0.0.3 42 | */ 43 | private IMqListenerService mqListenerService; 44 | 45 | public void setInvokeService(IInvokeService invokeService) { 46 | this.invokeService = invokeService; 47 | } 48 | 49 | public void setMqListenerService(IMqListenerService mqListenerService) { 50 | this.mqListenerService = mqListenerService; 51 | } 52 | 53 | @Override 54 | protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { 55 | ByteBuf byteBuf = (ByteBuf) msg; 56 | byte[] bytes = new byte[byteBuf.readableBytes()]; 57 | byteBuf.readBytes(bytes); 58 | 59 | RpcMessageDto rpcMessageDto = null; 60 | try { 61 | rpcMessageDto = JSON.parseObject(bytes, RpcMessageDto.class); 62 | } catch (Exception exception) { 63 | log.error("RpcMessageDto json 格式转换异常 {}", new String(bytes)); 64 | return; 65 | } 66 | 67 | if (rpcMessageDto.isRequest()) { 68 | MqCommonResp commonResp = this.dispatch(rpcMessageDto, ctx); 69 | 70 | if(commonResp == null) { 71 | log.debug("当前消息为 null,忽略处理。"); 72 | return; 73 | } 74 | 75 | writeResponse(rpcMessageDto, commonResp, ctx); 76 | } else { 77 | final String traceId = rpcMessageDto.getTraceId(); 78 | 79 | // 丢弃掉 traceId 为空的信息 80 | if(StringUtil.isBlank(traceId)) { 81 | log.debug("[Server Response] response traceId 为空,直接丢弃", JSON.toJSON(rpcMessageDto)); 82 | return; 83 | } 84 | 85 | // 添加消息 86 | invokeService.addResponse(traceId, rpcMessageDto); 87 | } 88 | } 89 | 90 | /** 91 | * 消息的分发 92 | * 93 | * @param rpcMessageDto 入参 94 | * @param ctx 上下文 95 | * @return 结果 96 | */ 97 | private MqCommonResp dispatch(RpcMessageDto rpcMessageDto, ChannelHandlerContext ctx) { 98 | final String methodType = rpcMessageDto.getMethodType(); 99 | final String json = rpcMessageDto.getJson(); 100 | 101 | String channelId = ChannelUtil.getChannelId(ctx); 102 | log.debug("channelId: {} 接收到 method: {} 内容:{}", channelId, 103 | methodType, json); 104 | 105 | // 消息发送 106 | if(MethodType.B_MESSAGE_PUSH.equals(methodType)) { 107 | // 日志输出 108 | log.info("收到服务端消息: {}", json); 109 | return this.consumer(json); 110 | } 111 | 112 | throw new UnsupportedOperationException("暂不支持的方法类型"); 113 | } 114 | 115 | /** 116 | * 消息消费 117 | * @param json 原始请求 118 | * @return 结果 119 | * @since 0.0.3 120 | */ 121 | private MqCommonResp consumer(final String json) { 122 | try { 123 | // 如果是 broker,应该进行处理化等操作。 124 | MqMessage mqMessage = JSON.parseObject(json, MqMessage.class); 125 | IMqConsumerListenerContext context = new MqConsumerListenerContext(); 126 | ConsumerStatus consumerStatus = this.mqListenerService.consumer(mqMessage, context); 127 | 128 | MqConsumerResultResp resp = new MqConsumerResultResp(); 129 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 130 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 131 | resp.setConsumerStatus(consumerStatus.getCode()); 132 | return resp; 133 | } catch (MqException mqException) { 134 | log.error("消息消费业务异常", mqException); 135 | MqConsumerResultResp resp = new MqConsumerResultResp(); 136 | resp.setRespCode(mqException.getCode()); 137 | resp.setRespMessage(mqException.getMsg()); 138 | return resp; 139 | } catch (Exception exception) { 140 | log.error("消息消费系统异常", exception); 141 | MqConsumerResultResp resp = new MqConsumerResultResp(); 142 | resp.setRespCode(MqCommonRespCode.FAIL.getCode()); 143 | resp.setRespMessage(MqCommonRespCode.FAIL.getMsg()); 144 | return resp; 145 | } 146 | } 147 | 148 | /** 149 | * 结果写回 150 | * 151 | * @param req 请求 152 | * @param resp 响应 153 | * @param ctx 上下文 154 | */ 155 | private void writeResponse(RpcMessageDto req, 156 | Object resp, 157 | ChannelHandlerContext ctx) { 158 | final String id = ctx.channel().id().asLongText(); 159 | 160 | RpcMessageDto rpcMessageDto = new RpcMessageDto(); 161 | // 响应类消息 162 | rpcMessageDto.setRequest(false); 163 | rpcMessageDto.setTraceId(req.getTraceId()); 164 | rpcMessageDto.setMethodType(req.getMethodType()); 165 | rpcMessageDto.setRequestTime(System.currentTimeMillis()); 166 | String json = JSON.toJSONString(resp); 167 | rpcMessageDto.setJson(json); 168 | 169 | // 回写到 client 端 170 | ByteBuf byteBuf = DelimiterUtil.getMessageDelimiterBuffer(rpcMessageDto); 171 | ctx.writeAndFlush(byteBuf); 172 | log.debug("[Server] channel {} response {}", id, JSON.toJSON(rpcMessageDto)); 173 | } 174 | 175 | 176 | } 177 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mq 2 | 3 | [mq](https://github.com/houbb/mq) 是基于 netty 实现的 java mq 框架,类似于 rocket mq。 4 | 5 | [![Build Status](https://travis-ci.com/houbb/mq.svg?branch=master)](https://travis-ci.com/houbb/mq) 6 | [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.houbb/mq/badge.svg)](http://mvnrepository.com/artifact/com.github.houbb/mq) 7 | [![](https://img.shields.io/badge/license-Apache2-FF0080.svg)](https://github.com/houbb/mq/blob/master/LICENSE.txt) 8 | [![Open Source Love](https://badges.frapsoft.com/os/v2/open-source.svg?v=103)](https://github.com/houbb/nlp-common) 9 | 10 | > [变更日志](https://github.com/houbb/mq/blob/master/CHANGELOG.md) 11 | 12 | 主要用于个人学习,由渐入深,理解 mq 的底层实现原理。 13 | 14 | ## 特性 15 | 16 | - 基于 netty4 的客户端调用服务端 17 | 18 | - timeout 超时处理 19 | 20 | - broker 启动的 check 检测服务可用性 21 | 22 | - load balance 负载均衡 23 | 24 | - 基于 TAG 的消息过滤,broker 端实现 25 | 26 | - 生产者的消息同步发送,ONE WAY 发送 27 | 28 | - 生产消息的批量发送 29 | 30 | - 消息状态的批量确认 31 | 32 | - fail 支持 failOver failFast 等失败处理策略 33 | 34 | - heartbeat 服务端心跳 35 | 36 | - AT LEAST ONCE 最少一次原则 37 | 38 | # 快速入门 39 | 40 | ## 测试 41 | 42 | ### 注册中心 43 | 44 | 依赖 maven 包: 45 | 46 | ```xml 47 | 48 | com.github.houbb 49 | mq-broker 50 | 0.1.3 51 | 52 | ``` 53 | 54 | 代码实现: 55 | 56 | ```java 57 | MqBroker broker = new MqBroker(); 58 | broker.start(); 59 | ``` 60 | 61 | ### 消费者 62 | 63 | 依赖 maven 包: 64 | 65 | ```xml 66 | 67 | com.github.houbb 68 | mq-consumer 69 | 0.1.3 70 | 71 | ``` 72 | 73 | 代码实现: 74 | 75 | 76 | ```java 77 | final MqConsumerPush mqConsumerPush = new MqConsumerPush(); 78 | mqConsumerPush.start(); 79 | 80 | mqConsumerPush.subscribe("TOPIC", "TAGA"); 81 | mqConsumerPush.registerListener(new IMqConsumerListener() { 82 | @Override 83 | public ConsumerStatus consumer(MqMessage mqMessage, IMqConsumerListenerContext context) { 84 | System.out.println("---------- 自定义 " + JSON.toJSONString(mqMessage)); 85 | return ConsumerStatus.SUCCESS; 86 | } 87 | }); 88 | ``` 89 | 90 | ### 生产者 91 | 92 | 依赖 maven 包: 93 | 94 | ```xml 95 | 96 | com.github.houbb 97 | mq-producer 98 | 0.1.3 99 | 100 | ``` 101 | 102 | 代码实现: 103 | 104 | ```java 105 | MqProducer mqProducer = new MqProducer(); 106 | mqProducer.start(); 107 | 108 | String message = "HELLO MQ!"; 109 | MqMessage mqMessage = new MqMessage(); 110 | mqMessage.setTopic("TOPIC"); 111 | mqMessage.setTags(Arrays.asList("TAGA", "TAGB")); 112 | mqMessage.setPayload(message); 113 | 114 | SendResult sendResult = mqProducer.send(mqMessage); 115 | System.out.println(JSON.toJSON(sendResult)); 116 | ``` 117 | 118 | # 前言 119 | 120 | 工作至今,接触 mq 框架已经有很长时间。 121 | 122 | 但是对于其原理一直只是知道个大概,从来没有深入学习过。 123 | 124 | 以前一直想写,但由于各种原因被耽搁。 125 | 126 | ## 技术准备 127 | 128 | [Java 并发实战学习](https://houbb.github.io/2019/01/18/jcip-00-overview) 129 | 130 | [TCP/IP 协议学习笔记](https://houbb.github.io/2019/04/05/protocol-tcp-ip-01-overview-01) 131 | 132 | [Netty 权威指南学习](https://houbb.github.io/2019/05/10/netty-definitive-gudie-00-overview) 133 | 134 | 这些技术的准备阶段,花费了比较长的时间。 135 | 136 | 也建议想写 mq 框架的有相关的知识储备。 137 | 138 | 其他 mq 框架使用的经验此处不再赘述。 139 | 140 | ## 快速迭代 141 | 142 | 原来一直想写 mq,却不行动的原因就是想的太多,做的太少。 143 | 144 | 想一下把全部写完,结果就是啥都没写。 145 | 146 | 所以本次的开发,每个代码分支做的事情实际很少,只做一个功能点。 147 | 148 | 陆陆续续经过近一个月的完善,对 mq 框架有了自己的体会和进一步的认知。 149 | 150 | 代码实现功能,主要参考 [Apache Dubbo](https://dubbo.apache.org/zh/docs/introduction/) 151 | 152 | # 文档 153 | 154 | ## 文档 155 | 156 | 文档将使用 markdown 文本的形式,补充 code 层面没有的东西。 157 | 158 | [【mq】从零开始实现 mq-01-生产者、消费者启动 ](https://mp.weixin.qq.com/s/moF528JiVG9dqCi5oFMbVg) 159 | 160 | [【mq】从零开始实现 mq-02-如何实现生产者调用消费者?](https://mp.weixin.qq.com/s/_OF4hbh9llaxN27Cv_cToQ) 161 | 162 | [【mq】从零开始实现 mq-03-引入 broker 中间人](https://mp.weixin.qq.com/s/BvEWsLp3_35yFVRqBOxS2w) 163 | 164 | [【mq】从零开始实现 mq-04-启动检测与实现优化](https://mp.weixin.qq.com/s/BvEWsLp3_35yFVRqBOxS2w) 165 | 166 | [【mq】从零开始实现 mq-05-实现优雅停机](https://mp.weixin.qq.com/s/BvEWsLp3_35yFVRqBOxS2w) 167 | 168 | [【mq】从零开始实现 mq-06-消费者心跳检测 heartbeat](https://mp.weixin.qq.com/s/lsvm9UoQWK98Jy3kuS2aNg) 169 | 170 | [【mq】从零开始实现 mq-07-负载均衡 load balance](https://mp.weixin.qq.com/s/ZNuecNeVJzIPCp252Hn4GQ) 171 | 172 | [【mq】从零开始实现 mq-08-配置优化 fluent](https://mp.weixin.qq.com/s/_O20KKdGwxMcHc87rcuWug) 173 | 174 | [【mq】从零开始实现 mq-09-消费者拉取消息 pull message](https://mp.weixin.qq.com/s/bAqOJ4fKWTAVet0Oqv8S0g) 175 | 176 | [【mq】从零开始实现 mq-10-消费者拉取消息回执 pull message ack](https://mp.weixin.qq.com/s/OgcQI-Go1ZS9-pdLtYwkcg) 177 | 178 | [【mq】从零开始实现 mq-11-消费者消息回执添加分组信息 pull message ack groupName](https://mp.weixin.qq.com/s/3RnB7KhZB3n8yGI6Z02-bw) 179 | 180 | [【mq】从零开始实现 mq-12-消息的批量发送与回执](https://mp.weixin.qq.com/s/tg0gxwbGWd7cn_RGMiEWew) 181 | 182 | [【mq】从零开始实现 mq-13-注册鉴权 auth](https://mp.weixin.qq.com/s/SzWAqyHpeTrDQyUTknsJGQ) 183 | 184 | 185 | ## 代码注释 186 | 187 | 代码有详细的注释,便于阅读和后期维护。 188 | 189 | ## 测试 190 | 191 | 目前测试代码算不上完善。后续将陆续补全。 192 | 193 | # mq 模块 194 | 195 | | 模块 | 说明 | 196 | |:---|:---| 197 | | mq-common | 公共代码 | 198 | | mq-broker | 注册中心 | 199 | | mq-producer | 服务端 | 200 | | mq-consumer | 客户端 | 201 | | mq-test | 测试模块 | 202 | 203 | # 测试代码 204 | 205 | 这部分测试代码可以关注公众号【老马啸西风】,后台回复【mq】领取。 206 | 207 | ![qrcode](qrcode.jpg) 208 | 209 | # 后期 ROAD-MAP 210 | 211 | - [ ] all 模块 212 | 213 | - [x] check broker 启动检测 214 | 215 | - [x] 关闭时通知 register center 216 | 217 | - [x] 优雅关闭添加超时设置 218 | 219 | - [x] heartbeat 心跳检测机制 220 | 221 | - [x] 完善 load-balance 实现 + shardingkey 粘性消费、请求 222 | 223 | - [x] 失败重试的拓展 224 | 225 | - [x] 消费者 pull 策略实现 226 | 227 | - [x] pull 消息消费的 ACK 处理 228 | 229 | - [x] broker springboot 实现 230 | 231 | - [x] 消息的 ack 处理,要基于 groupName 进行处理 232 | 233 | - [x] 消息的回溯消费 offset 234 | 235 | - [x] 消息的批量发送,批量 ACK 236 | 237 | - [x] 添加注册鉴权,保证安全性 238 | 239 | - [ ] 顺序消息 240 | 241 | - [ ] 事务消息 242 | 243 | - [ ] 定时消息 244 | 245 | - [ ] 流量控制 back-press 反压 246 | 247 | - [ ] 消息可靠性 248 | 249 | - [ ] offline message 离线消息 250 | 251 | - [ ] dead message 死信队列 252 | 253 | - [ ] 断线重连 254 | 255 | 256 | # 中间件等工具开源矩阵 257 | 258 | [heaven: 收集开发中常用的工具类](https://github.com/houbb/heaven) 259 | 260 | [rpc: 基于 netty4 实现的远程调用工具](https://github.com/houbb/rpc) 261 | 262 | [mq: 简易版 mq 实现](https://github.com/houbb/mq) 263 | 264 | [ioc: 模拟简易版 spring ioc](https://github.com/houbb/ioc) 265 | 266 | [mybatis: 简易版 mybatis](https://github.com/houbb/mybatis) 267 | 268 | [cache: 渐进式 redis 缓存](https://github.com/houbb/cache) 269 | 270 | [jdbc-pool: 数据库连接池实现](https://github.com/houbb/jdbc-pool) 271 | 272 | [sandglass: 任务调度时间工具框架](https://github.com/houbb/sandglass) 273 | 274 | [sisyphus: 支持注解的重试框架](https://github.com/houbb/sisyphus) 275 | 276 | [resubmit: 防止重复提交框架,支持注解](https://github.com/houbb/resubmit) 277 | 278 | [auto-log: 日志自动输出](https://github.com/houbb/auto-log) 279 | 280 | [async: 多线程异步并行框架](https://github.com/houbb/async) 281 | 282 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/core/MqConsumerPull.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.core; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.util.CollectionUtil; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.common.constant.ConsumerTypeConst; 8 | import com.github.houbb.mq.common.dto.req.MqMessage; 9 | import com.github.houbb.mq.common.dto.req.component.MqConsumerUpdateStatusDto; 10 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 11 | import com.github.houbb.mq.common.dto.resp.MqConsumerPullResp; 12 | import com.github.houbb.mq.common.resp.ConsumerStatus; 13 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 14 | import com.github.houbb.mq.consumer.api.IMqConsumerListenerContext; 15 | import com.github.houbb.mq.consumer.dto.MqTopicTagDto; 16 | import com.github.houbb.mq.consumer.support.listener.MqConsumerListenerContext; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | import java.util.concurrent.Executors; 21 | import java.util.concurrent.ScheduledExecutorService; 22 | import java.util.concurrent.TimeUnit; 23 | 24 | /** 25 | * 拉取消费策略 26 | * 27 | * @author binbin.hou 28 | * @since 0.0.9 29 | */ 30 | public class MqConsumerPull extends MqConsumerPush { 31 | 32 | private static final Log log = LogFactory.getLog(MqConsumerPull.class); 33 | 34 | /** 35 | * 拉取定时任务 36 | * 37 | * @since 0.0.9 38 | */ 39 | private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 40 | 41 | /** 42 | * 单次拉取大小 43 | * @since 0.0.9 44 | */ 45 | private int size = 10; 46 | 47 | /** 48 | * 初始化延迟毫秒数 49 | * @since 0.0.9 50 | */ 51 | private int pullInitDelaySeconds = 5; 52 | 53 | /** 54 | * 拉取周期 55 | * @since 0.0.9 56 | */ 57 | private int pullPeriodSeconds = 5; 58 | 59 | /** 60 | * 订阅列表 61 | * @since 0.0.9 62 | */ 63 | private final List subscribeList = new ArrayList<>(); 64 | 65 | /** 66 | * 状态回执是否批量 67 | * @since 0.1.3 68 | */ 69 | private boolean ackBatchFlag = true; 70 | 71 | public MqConsumerPull size(int size) { 72 | this.size = size; 73 | return this; 74 | } 75 | 76 | public MqConsumerPull pullInitDelaySeconds(int pullInitDelaySeconds) { 77 | this.pullInitDelaySeconds = pullInitDelaySeconds; 78 | return this; 79 | } 80 | 81 | public MqConsumerPull pullPeriodSeconds(int pullPeriodSeconds) { 82 | this.pullPeriodSeconds = pullPeriodSeconds; 83 | return this; 84 | } 85 | 86 | public MqConsumerPull ackBatchFlag(boolean ackBatchFlag) { 87 | this.ackBatchFlag = ackBatchFlag; 88 | return this; 89 | } 90 | 91 | /** 92 | * 初始化拉取消息 93 | * @since 0.0.6 94 | */ 95 | @Override 96 | public void afterInit() { 97 | //5S 发一次心跳 98 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() { 99 | @Override 100 | public void run() { 101 | if(CollectionUtil.isEmpty(subscribeList)) { 102 | log.warn("订阅列表为空,忽略处理。"); 103 | return; 104 | } 105 | 106 | for(MqTopicTagDto tagDto : subscribeList) { 107 | final String topicName = tagDto.getTopicName(); 108 | final String tagRegex = tagDto.getTagRegex(); 109 | 110 | MqConsumerPullResp resp = consumerBrokerService.pull(topicName, tagRegex, size); 111 | 112 | if(MqCommonRespCode.SUCCESS.getCode().equals(resp.getRespCode())) { 113 | List mqMessageList = resp.getList(); 114 | if(CollectionUtil.isNotEmpty(mqMessageList)) { 115 | List statusDtoList = new ArrayList<>(mqMessageList.size()); 116 | for(MqMessage mqMessage : mqMessageList) { 117 | IMqConsumerListenerContext context = new MqConsumerListenerContext(); 118 | final String messageId = mqMessage.getTraceId(); 119 | ConsumerStatus consumerStatus = mqListenerService.consumer(mqMessage, context); 120 | log.info("消息:{} 消费结果 {}", messageId, consumerStatus); 121 | 122 | // 状态同步更新 123 | if(!ackBatchFlag) { 124 | MqCommonResp ackResp = consumerBrokerService.consumerStatusAck(messageId, consumerStatus); 125 | log.info("消息:{} 状态回执结果 {}", messageId, JSON.toJSON(ackResp)); 126 | } else { 127 | // 批量 128 | MqConsumerUpdateStatusDto statusDto = new MqConsumerUpdateStatusDto(); 129 | statusDto.setMessageId(messageId); 130 | statusDto.setMessageStatus(consumerStatus.getCode()); 131 | statusDto.setConsumerGroupName(groupName); 132 | statusDtoList.add(statusDto); 133 | } 134 | } 135 | 136 | // 批量执行 137 | if(ackBatchFlag) { 138 | MqCommonResp ackResp = consumerBrokerService.consumerStatusAckBatch(statusDtoList); 139 | log.info("消息:{} 状态批量回执结果 {}", statusDtoList, JSON.toJSON(ackResp)); 140 | statusDtoList = null; 141 | } 142 | } 143 | } else { 144 | log.error("拉取消息失败: {}", JSON.toJSON(resp)); 145 | } 146 | } 147 | } 148 | }, pullInitDelaySeconds, pullPeriodSeconds, TimeUnit.SECONDS); 149 | } 150 | 151 | @Override 152 | protected String getConsumerType() { 153 | return ConsumerTypeConst.PULL; 154 | } 155 | 156 | @Override 157 | public synchronized void subscribe(String topicName, String tagRegex) { 158 | MqTopicTagDto tagDto = buildMqTopicTagDto(topicName, tagRegex); 159 | 160 | if(!subscribeList.contains(tagDto)) { 161 | subscribeList.add(tagDto); 162 | } 163 | } 164 | 165 | @Override 166 | public void unSubscribe(String topicName, String tagRegex) { 167 | MqTopicTagDto tagDto = buildMqTopicTagDto(topicName, tagRegex); 168 | 169 | subscribeList.remove(tagDto); 170 | } 171 | 172 | private MqTopicTagDto buildMqTopicTagDto(String topicName, String tagRegex) { 173 | MqTopicTagDto dto = new MqTopicTagDto(); 174 | dto.setTagRegex(tagRegex); 175 | dto.setTopicName(topicName); 176 | // 主动拉取这里会有问题,会导致不同的 groupName 的消息,实际上已经被消费了 177 | // 所有实际上应该有一个消息+group 的映射关系表,单个消息可以被多次重复消费。 178 | // groupName+messageId+status==>在数据库层面实现 179 | dto.setGroupName(groupName); 180 | return dto; 181 | } 182 | 183 | } 184 | -------------------------------------------------------------------------------- /mq-producer/src/main/java/com/github/houbb/mq/producer/core/MqProducer.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.producer.core; 2 | 3 | import com.github.houbb.heaven.util.common.ArgUtil; 4 | import com.github.houbb.load.balance.api.ILoadBalance; 5 | import com.github.houbb.load.balance.api.impl.LoadBalances; 6 | import com.github.houbb.log.integration.core.Log; 7 | import com.github.houbb.log.integration.core.LogFactory; 8 | import com.github.houbb.mq.common.dto.req.MqMessage; 9 | import com.github.houbb.mq.common.resp.MqException; 10 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 11 | import com.github.houbb.mq.common.support.hook.DefaultShutdownHook; 12 | import com.github.houbb.mq.common.support.hook.ShutdownHooks; 13 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 14 | import com.github.houbb.mq.common.support.invoke.impl.InvokeService; 15 | import com.github.houbb.mq.common.support.status.IStatusManager; 16 | import com.github.houbb.mq.common.support.status.StatusManager; 17 | import com.github.houbb.mq.producer.api.IMqProducer; 18 | import com.github.houbb.mq.producer.constant.ProducerConst; 19 | import com.github.houbb.mq.producer.constant.ProducerRespCode; 20 | import com.github.houbb.mq.producer.dto.SendBatchResult; 21 | import com.github.houbb.mq.producer.dto.SendResult; 22 | import com.github.houbb.mq.producer.support.broker.IProducerBrokerService; 23 | import com.github.houbb.mq.producer.support.broker.ProducerBrokerConfig; 24 | import com.github.houbb.mq.producer.support.broker.ProducerBrokerService; 25 | 26 | import java.util.List; 27 | 28 | /** 29 | * 默认 mq 生产者 30 | * @author binbin.hou 31 | * @since 1.0.0 32 | */ 33 | public class MqProducer extends Thread implements IMqProducer { 34 | 35 | private static final Log log = LogFactory.getLog(MqProducer.class); 36 | 37 | /** 38 | * 分组名称 39 | */ 40 | private String groupName = ProducerConst.DEFAULT_GROUP_NAME; 41 | 42 | /** 43 | * 中间人地址 44 | */ 45 | private String brokerAddress = "127.0.0.1:9999"; 46 | 47 | /** 48 | * 获取响应超时时间 49 | * @since 0.0.2 50 | */ 51 | private long respTimeoutMills = 5000; 52 | 53 | /** 54 | * 检测 broker 可用性 55 | * @since 0.0.4 56 | */ 57 | private volatile boolean check = true; 58 | 59 | /** 60 | * 调用管理服务 61 | * @since 0.0.2 62 | */ 63 | private final IInvokeService invokeService = new InvokeService(); 64 | 65 | /** 66 | * 状态管理类 67 | * @since 0.0.5 68 | */ 69 | private final IStatusManager statusManager = new StatusManager(); 70 | 71 | /** 72 | * 生产者-中间服务端服务类 73 | * @since 0.0.5 74 | */ 75 | private final IProducerBrokerService producerBrokerService = new ProducerBrokerService(); 76 | 77 | /** 78 | * 为剩余的请求等待时间 79 | * @since 0.0.5 80 | */ 81 | private long waitMillsForRemainRequest = 60 * 1000; 82 | 83 | /** 84 | * 负载均衡策略 85 | * @since 0.0.7 86 | */ 87 | private ILoadBalance loadBalance = LoadBalances.weightRoundRobbin(); 88 | 89 | /** 90 | * 消息发送最大尝试次数 91 | * @since 0.0.8 92 | */ 93 | private int maxAttempt = 3; 94 | 95 | /** 96 | * 账户标识 97 | * @since 0.1.4 98 | */ 99 | private String appKey; 100 | 101 | /** 102 | * 账户密码 103 | * @since 0.1.4 104 | */ 105 | private String appSecret; 106 | 107 | public MqProducer appKey(String appKey) { 108 | this.appKey = appKey; 109 | return this; 110 | } 111 | 112 | public MqProducer appSecret(String appSecret) { 113 | this.appSecret = appSecret; 114 | return this; 115 | } 116 | 117 | public MqProducer groupName(String groupName) { 118 | this.groupName = groupName; 119 | return this; 120 | } 121 | 122 | public MqProducer brokerAddress(String brokerAddress) { 123 | this.brokerAddress = brokerAddress; 124 | return this; 125 | } 126 | 127 | public MqProducer respTimeoutMills(long respTimeoutMills) { 128 | this.respTimeoutMills = respTimeoutMills; 129 | return this; 130 | } 131 | 132 | public MqProducer check(boolean check) { 133 | this.check = check; 134 | return this; 135 | } 136 | 137 | public MqProducer waitMillsForRemainRequest(long waitMillsForRemainRequest) { 138 | this.waitMillsForRemainRequest = waitMillsForRemainRequest; 139 | return this; 140 | } 141 | 142 | public MqProducer loadBalance(ILoadBalance loadBalance) { 143 | this.loadBalance = loadBalance; 144 | return this; 145 | } 146 | 147 | public MqProducer maxAttempt(int maxAttempt) { 148 | this.maxAttempt = maxAttempt; 149 | return this; 150 | } 151 | 152 | /** 153 | * 参数校验 154 | */ 155 | private void paramCheck() { 156 | ArgUtil.notEmpty(groupName, "groupName"); 157 | ArgUtil.notEmpty(brokerAddress, "brokerAddress"); 158 | } 159 | 160 | @Override 161 | public synchronized void run() { 162 | this.paramCheck(); 163 | 164 | // 启动服务端 165 | log.info("MQ 生产者开始启动客户端 GROUP: {} brokerAddress: {}", 166 | groupName, brokerAddress); 167 | 168 | try { 169 | //0. 配置信息 170 | ProducerBrokerConfig config = ProducerBrokerConfig.newInstance() 171 | .groupName(groupName) 172 | .brokerAddress(brokerAddress) 173 | .check(check) 174 | .respTimeoutMills(respTimeoutMills) 175 | .invokeService(invokeService) 176 | .statusManager(statusManager) 177 | .loadBalance(loadBalance) 178 | .maxAttempt(maxAttempt) 179 | .appKey(appKey) 180 | .appSecret(appSecret); 181 | 182 | //1. 初始化 183 | this.producerBrokerService.initChannelFutureList(config); 184 | 185 | //2. 连接到服务端 186 | this.producerBrokerService.registerToBroker(); 187 | 188 | //3. 标识为可用 189 | statusManager.status(true); 190 | 191 | //4. 添加钩子函数 192 | final DefaultShutdownHook rpcShutdownHook = new DefaultShutdownHook(); 193 | rpcShutdownHook.setStatusManager(statusManager); 194 | rpcShutdownHook.setInvokeService(invokeService); 195 | rpcShutdownHook.setWaitMillsForRemainRequest(waitMillsForRemainRequest); 196 | rpcShutdownHook.setDestroyable(this.producerBrokerService); 197 | ShutdownHooks.rpcShutdownHook(rpcShutdownHook); 198 | 199 | log.info("MQ 生产者启动完成"); 200 | } catch (Exception e) { 201 | log.error("MQ 生产者启动遇到异常", e); 202 | // 设置为初始化失败 203 | statusManager.initFailed(true); 204 | 205 | throw new MqException(ProducerRespCode.RPC_INIT_FAILED); 206 | } 207 | } 208 | 209 | @Override 210 | public SendResult send(MqMessage mqMessage) { 211 | return this.producerBrokerService.send(mqMessage); 212 | } 213 | 214 | @Override 215 | public SendResult sendOneWay(MqMessage mqMessage) { 216 | return this.producerBrokerService.sendOneWay(mqMessage); 217 | } 218 | 219 | @Override 220 | public SendBatchResult sendBatch(List mqMessageList) { 221 | return producerBrokerService.sendBatch(mqMessageList); 222 | } 223 | 224 | @Override 225 | public SendBatchResult sendOneWayBatch(List mqMessageList) { 226 | return producerBrokerService.sendOneWayBatch(mqMessageList); 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/core/MqBroker.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.core; 2 | 3 | import com.github.houbb.load.balance.api.ILoadBalance; 4 | import com.github.houbb.load.balance.api.impl.LoadBalances; 5 | import com.github.houbb.log.integration.core.Log; 6 | import com.github.houbb.log.integration.core.LogFactory; 7 | import com.github.houbb.mq.broker.api.IBrokerConsumerService; 8 | import com.github.houbb.mq.broker.api.IBrokerProducerService; 9 | import com.github.houbb.mq.broker.api.IMqBroker; 10 | import com.github.houbb.mq.broker.constant.BrokerConst; 11 | import com.github.houbb.mq.broker.constant.BrokerRespCode; 12 | import com.github.houbb.mq.broker.dto.consumer.ConsumerSubscribeBo; 13 | import com.github.houbb.mq.broker.handler.MqBrokerHandler; 14 | import com.github.houbb.mq.broker.support.api.LocalBrokerConsumerService; 15 | import com.github.houbb.mq.broker.support.api.LocalBrokerProducerService; 16 | import com.github.houbb.mq.broker.support.persist.IMqBrokerPersist; 17 | import com.github.houbb.mq.broker.support.persist.LocalMqBrokerPersist; 18 | import com.github.houbb.mq.broker.support.push.BrokerPushService; 19 | import com.github.houbb.mq.broker.support.push.IBrokerPushService; 20 | import com.github.houbb.mq.broker.support.valid.BrokerRegisterValidService; 21 | import com.github.houbb.mq.broker.support.valid.IBrokerRegisterValidService; 22 | import com.github.houbb.mq.common.resp.MqException; 23 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 24 | import com.github.houbb.mq.common.support.invoke.impl.InvokeService; 25 | import com.github.houbb.mq.common.util.DelimiterUtil; 26 | import io.netty.bootstrap.ServerBootstrap; 27 | import io.netty.buffer.ByteBuf; 28 | import io.netty.channel.*; 29 | import io.netty.channel.nio.NioEventLoopGroup; 30 | import io.netty.channel.socket.nio.NioServerSocketChannel; 31 | import io.netty.handler.codec.DelimiterBasedFrameDecoder; 32 | 33 | /** 34 | * @author binbin.hou 35 | * @since 1.0.0 36 | */ 37 | public class MqBroker extends Thread implements IMqBroker { 38 | 39 | private static final Log log = LogFactory.getLog(MqBroker.class); 40 | 41 | /** 42 | * 端口号 43 | */ 44 | private int port = BrokerConst.DEFAULT_PORT; 45 | 46 | /** 47 | * 调用管理类 48 | * 49 | * @since 1.0.0 50 | */ 51 | private final IInvokeService invokeService = new InvokeService(); 52 | 53 | /** 54 | * 消费者管理 55 | * 56 | * @since 0.0.3 57 | */ 58 | private IBrokerConsumerService registerConsumerService = new LocalBrokerConsumerService(); 59 | 60 | /** 61 | * 生产者管理 62 | * 63 | * @since 0.0.3 64 | */ 65 | private IBrokerProducerService registerProducerService = new LocalBrokerProducerService(); 66 | 67 | /** 68 | * 持久化类 69 | * 70 | * @since 0.0.3 71 | */ 72 | private IMqBrokerPersist mqBrokerPersist = new LocalMqBrokerPersist(); 73 | 74 | /** 75 | * 推送服务 76 | * 77 | * @since 0.0.3 78 | */ 79 | private IBrokerPushService brokerPushService = new BrokerPushService(); 80 | 81 | /** 82 | * 获取响应超时时间 83 | * @since 0.0.3 84 | */ 85 | private long respTimeoutMills = 5000; 86 | 87 | /** 88 | * 负载均衡 89 | * @since 0.0.7 90 | */ 91 | private ILoadBalance loadBalance = LoadBalances.weightRoundRobbin(); 92 | 93 | /** 94 | * 推送最大尝试次数 95 | * @since 0.0.8 96 | */ 97 | private int pushMaxAttempt = 3; 98 | 99 | /** 100 | * 注册验证服务类 101 | * @since 0.1.4 102 | */ 103 | private IBrokerRegisterValidService brokerRegisterValidService = new BrokerRegisterValidService(); 104 | 105 | public MqBroker port(int port) { 106 | this.port = port; 107 | return this; 108 | } 109 | 110 | public MqBroker registerConsumerService(IBrokerConsumerService registerConsumerService) { 111 | this.registerConsumerService = registerConsumerService; 112 | return this; 113 | } 114 | 115 | public MqBroker registerProducerService(IBrokerProducerService registerProducerService) { 116 | this.registerProducerService = registerProducerService; 117 | return this; 118 | } 119 | 120 | public MqBroker mqBrokerPersist(IMqBrokerPersist mqBrokerPersist) { 121 | this.mqBrokerPersist = mqBrokerPersist; 122 | return this; 123 | } 124 | 125 | public MqBroker brokerPushService(IBrokerPushService brokerPushService) { 126 | this.brokerPushService = brokerPushService; 127 | return this; 128 | } 129 | 130 | public MqBroker respTimeoutMills(long respTimeoutMills) { 131 | this.respTimeoutMills = respTimeoutMills; 132 | return this; 133 | } 134 | 135 | public MqBroker loadBalance(ILoadBalance loadBalance) { 136 | this.loadBalance = loadBalance; 137 | return this; 138 | } 139 | 140 | public MqBroker pushMaxAttempt(int pushMaxAttempt) { 141 | this.pushMaxAttempt = pushMaxAttempt; 142 | return this; 143 | } 144 | 145 | public MqBroker brokerRegisterValidService(IBrokerRegisterValidService brokerRegisterValidService) { 146 | this.brokerRegisterValidService = brokerRegisterValidService; 147 | return this; 148 | } 149 | 150 | private ChannelHandler initChannelHandler() { 151 | registerConsumerService.loadBalance(this.loadBalance); 152 | 153 | MqBrokerHandler handler = new MqBrokerHandler(); 154 | handler.invokeService(invokeService) 155 | .respTimeoutMills(respTimeoutMills) 156 | .registerConsumerService(registerConsumerService) 157 | .registerProducerService(registerProducerService) 158 | .mqBrokerPersist(mqBrokerPersist) 159 | .brokerPushService(brokerPushService) 160 | .respTimeoutMills(respTimeoutMills) 161 | .pushMaxAttempt(pushMaxAttempt) 162 | .brokerRegisterValidService(brokerRegisterValidService); 163 | 164 | return handler; 165 | } 166 | 167 | @Override 168 | public void run() { 169 | // 启动服务端 170 | log.info("MQ 中间人开始启动服务端 port: {}", port); 171 | 172 | EventLoopGroup bossGroup = new NioEventLoopGroup(); 173 | EventLoopGroup workerGroup = new NioEventLoopGroup(); 174 | 175 | try { 176 | final ByteBuf delimiterBuf = DelimiterUtil.getByteBuf(DelimiterUtil.DELIMITER); 177 | ServerBootstrap serverBootstrap = new ServerBootstrap(); 178 | serverBootstrap.group(workerGroup, bossGroup) 179 | .channel(NioServerSocketChannel.class) 180 | .childHandler(new ChannelInitializer() { 181 | @Override 182 | protected void initChannel(Channel ch) throws Exception { 183 | ch.pipeline() 184 | .addLast(new DelimiterBasedFrameDecoder(DelimiterUtil.LENGTH, delimiterBuf)) 185 | .addLast(initChannelHandler()); 186 | } 187 | }) 188 | // 这个参数影响的是还没有被accept 取出的连接 189 | .option(ChannelOption.SO_BACKLOG, 128) 190 | // 这个参数只是过一段时间内客户端没有响应,服务端会发送一个 ack 包,以判断客户端是否还活着。 191 | .childOption(ChannelOption.SO_KEEPALIVE, true); 192 | 193 | // 绑定端口,开始接收进来的链接 194 | ChannelFuture channelFuture = serverBootstrap.bind(port).syncUninterruptibly(); 195 | log.info("MQ 中间人启动完成,监听【" + port + "】端口"); 196 | 197 | channelFuture.channel().closeFuture().syncUninterruptibly(); 198 | log.info("MQ 中间人关闭完成"); 199 | } catch (Exception e) { 200 | log.error("MQ 中间人启动异常", e); 201 | throw new MqException(BrokerRespCode.RPC_INIT_FAILED); 202 | } finally { 203 | workerGroup.shutdownGracefully(); 204 | bossGroup.shutdownGracefully(); 205 | } 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/push/BrokerPushService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.push; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.log.integration.core.Log; 5 | import com.github.houbb.log.integration.core.LogFactory; 6 | import com.github.houbb.mq.broker.constant.BrokerRespCode; 7 | import com.github.houbb.mq.broker.dto.ChannelGroupNameDto; 8 | import com.github.houbb.mq.broker.dto.persist.MqMessagePersistPut; 9 | import com.github.houbb.mq.broker.support.persist.IMqBrokerPersist; 10 | import com.github.houbb.mq.common.constant.MessageStatusConst; 11 | import com.github.houbb.mq.common.constant.MethodType; 12 | import com.github.houbb.mq.common.dto.req.MqCommonReq; 13 | import com.github.houbb.mq.common.dto.req.MqMessage; 14 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 15 | import com.github.houbb.mq.common.dto.resp.MqConsumerResultResp; 16 | import com.github.houbb.mq.common.resp.ConsumerStatus; 17 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 18 | import com.github.houbb.mq.common.resp.MqException; 19 | import com.github.houbb.mq.common.rpc.RpcMessageDto; 20 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 21 | import com.github.houbb.mq.common.util.ChannelUtil; 22 | import com.github.houbb.mq.common.util.DelimiterUtil; 23 | import com.github.houbb.sisyphus.core.core.Retryer; 24 | import io.netty.buffer.ByteBuf; 25 | import io.netty.channel.Channel; 26 | 27 | import java.util.List; 28 | import java.util.concurrent.Callable; 29 | import java.util.concurrent.ExecutorService; 30 | import java.util.concurrent.Executors; 31 | 32 | /** 33 | * @author binbin.hou 34 | * @since 0.0.3 35 | */ 36 | public class BrokerPushService implements IBrokerPushService { 37 | 38 | private static final Log log = LogFactory.getLog(BrokerPushService.class); 39 | 40 | private static final ExecutorService EXECUTOR_SERVICE = Executors.newSingleThreadExecutor(); 41 | 42 | @Override 43 | public void asyncPush(final BrokerPushContext context) { 44 | EXECUTOR_SERVICE.submit(new Runnable() { 45 | @Override 46 | public void run() { 47 | log.info("开始异步处理 {}", JSON.toJSON(context)); 48 | final MqMessagePersistPut persistPut = context.mqMessagePersistPut(); 49 | final MqMessage mqMessage = persistPut.getMqMessage(); 50 | final List channelList = context.channelList(); 51 | final IMqBrokerPersist mqBrokerPersist = context.mqBrokerPersist(); 52 | final IInvokeService invokeService = context.invokeService(); 53 | final long responseTime = context.respTimeoutMills(); 54 | final int pushMaxAttempt = context.pushMaxAttempt(); 55 | 56 | // 更新状态为处理中 57 | final String messageId = mqMessage.getTraceId(); 58 | log.info("开始更新消息为处理中:{}", messageId); 59 | 60 | 61 | for(final ChannelGroupNameDto channelGroupNameDto : channelList) { 62 | final Channel channel = channelGroupNameDto.getChannel(); 63 | final String consumerGroupName =channelGroupNameDto.getConsumerGroupName(); 64 | 65 | try { 66 | mqBrokerPersist.updateStatus(messageId, consumerGroupName, MessageStatusConst.TO_CONSUMER_PROCESS); 67 | 68 | String channelId = ChannelUtil.getChannelId(channel); 69 | 70 | log.info("开始处理 channelId: {}", channelId); 71 | //1. 调用 72 | mqMessage.setMethodType(MethodType.B_MESSAGE_PUSH); 73 | 74 | // 重试推送 75 | MqConsumerResultResp resultResp = Retryer.newInstance() 76 | .maxAttempt(pushMaxAttempt) 77 | .callable(new Callable() { 78 | @Override 79 | public MqConsumerResultResp call() throws Exception { 80 | MqConsumerResultResp resp = callServer(channel, mqMessage, 81 | MqConsumerResultResp.class, invokeService, responseTime); 82 | 83 | // 失败校验 84 | if(resp == null 85 | || !ConsumerStatus.SUCCESS.getCode() 86 | .equals(resp.getConsumerStatus())) { 87 | throw new MqException(BrokerRespCode.MSG_PUSH_FAILED); 88 | } 89 | return resp; 90 | } 91 | }).retryCall(); 92 | 93 | //2. 更新状态 94 | //2.1 处理成功,取 push 消费状态 95 | if(MqCommonRespCode.SUCCESS.getCode().equals(resultResp.getRespCode())) { 96 | mqBrokerPersist.updateStatus(messageId, consumerGroupName, resultResp.getConsumerStatus()); 97 | } else { 98 | // 2.2 处理失败 99 | log.error("消费失败:{}", JSON.toJSON(resultResp)); 100 | mqBrokerPersist.updateStatus(messageId, consumerGroupName, MessageStatusConst.TO_CONSUMER_FAILED); 101 | } 102 | log.info("完成处理 channelId: {}", channelId); 103 | } catch (Exception exception) { 104 | log.error("处理异常"); 105 | mqBrokerPersist.updateStatus(messageId, consumerGroupName, MessageStatusConst.TO_CONSUMER_FAILED); 106 | } 107 | } 108 | 109 | log.info("完成异步处理"); 110 | } 111 | }); 112 | } 113 | 114 | /** 115 | * 调用服务端 116 | * @param channel 调用通道 117 | * @param commonReq 通用请求 118 | * @param respClass 类 119 | * @param invokeService 调用管理类 120 | * @param respTimeoutMills 响应超时时间 121 | * @param 泛型 122 | * @param 结果 123 | * @return 结果 124 | * @since 1.0.0 125 | */ 126 | private R callServer(Channel channel, 127 | T commonReq, 128 | Class respClass, 129 | IInvokeService invokeService, 130 | long respTimeoutMills) { 131 | final String traceId = commonReq.getTraceId(); 132 | final long requestTime = System.currentTimeMillis(); 133 | 134 | RpcMessageDto rpcMessageDto = new RpcMessageDto(); 135 | rpcMessageDto.setTraceId(traceId); 136 | rpcMessageDto.setRequestTime(requestTime); 137 | rpcMessageDto.setJson(JSON.toJSONString(commonReq)); 138 | rpcMessageDto.setMethodType(commonReq.getMethodType()); 139 | rpcMessageDto.setRequest(true); 140 | 141 | // 添加调用服务 142 | invokeService.addRequest(traceId, respTimeoutMills); 143 | 144 | // 遍历 channel 145 | // 关闭当前线程,以获取对应的信息 146 | // 使用序列化的方式 147 | ByteBuf byteBuf = DelimiterUtil.getMessageDelimiterBuffer(rpcMessageDto); 148 | 149 | //负载均衡获取 channel 150 | channel.writeAndFlush(byteBuf); 151 | 152 | String channelId = ChannelUtil.getChannelId(channel); 153 | log.debug("[Client] channelId {} 发送消息 {}", channelId, JSON.toJSON(rpcMessageDto)); 154 | // channel.closeFuture().syncUninterruptibly(); 155 | 156 | if (respClass == null) { 157 | log.debug("[Client] 当前消息为 one-way 消息,忽略响应"); 158 | return null; 159 | } else { 160 | //channelHandler 中获取对应的响应 161 | RpcMessageDto messageDto = invokeService.getResponse(traceId); 162 | if (MqCommonRespCode.TIMEOUT.getCode().equals(messageDto.getRespCode())) { 163 | throw new MqException(MqCommonRespCode.TIMEOUT); 164 | } 165 | 166 | String respJson = messageDto.getJson(); 167 | return JSON.parseObject(respJson, respClass); 168 | } 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /mq-consumer/src/main/java/com/github/houbb/mq/consumer/core/MqConsumerPush.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.consumer.core; 2 | 3 | import com.github.houbb.heaven.util.common.ArgUtil; 4 | import com.github.houbb.load.balance.api.ILoadBalance; 5 | import com.github.houbb.load.balance.api.impl.LoadBalances; 6 | import com.github.houbb.log.integration.core.Log; 7 | import com.github.houbb.log.integration.core.LogFactory; 8 | import com.github.houbb.mq.common.constant.ConsumerTypeConst; 9 | import com.github.houbb.mq.common.resp.MqException; 10 | import com.github.houbb.mq.common.rpc.RpcChannelFuture; 11 | import com.github.houbb.mq.common.support.hook.DefaultShutdownHook; 12 | import com.github.houbb.mq.common.support.hook.ShutdownHooks; 13 | import com.github.houbb.mq.common.support.invoke.IInvokeService; 14 | import com.github.houbb.mq.common.support.invoke.impl.InvokeService; 15 | import com.github.houbb.mq.common.support.status.IStatusManager; 16 | import com.github.houbb.mq.common.support.status.StatusManager; 17 | import com.github.houbb.mq.consumer.api.IMqConsumer; 18 | import com.github.houbb.mq.consumer.api.IMqConsumerListener; 19 | import com.github.houbb.mq.consumer.constant.ConsumerConst; 20 | import com.github.houbb.mq.consumer.constant.ConsumerRespCode; 21 | import com.github.houbb.mq.consumer.support.broker.ConsumerBrokerConfig; 22 | import com.github.houbb.mq.consumer.support.broker.ConsumerBrokerService; 23 | import com.github.houbb.mq.consumer.support.broker.IConsumerBrokerService; 24 | import com.github.houbb.mq.consumer.support.listener.IMqListenerService; 25 | import com.github.houbb.mq.consumer.support.listener.MqListenerService; 26 | 27 | /** 28 | * 推送消费策略 29 | * 30 | * @author binbin.hou 31 | * @since 1.0.0 32 | */ 33 | public class MqConsumerPush extends Thread implements IMqConsumer { 34 | 35 | private static final Log log = LogFactory.getLog(MqConsumerPush.class); 36 | 37 | /** 38 | * 组名称 39 | */ 40 | protected String groupName = ConsumerConst.DEFAULT_GROUP_NAME; 41 | 42 | /** 43 | * 中间人地址 44 | */ 45 | protected String brokerAddress = "127.0.0.1:9999"; 46 | 47 | /** 48 | * 获取响应超时时间 49 | * @since 0.0.2 50 | */ 51 | protected long respTimeoutMills = 5000; 52 | 53 | /** 54 | * 检测 broker 可用性 55 | * @since 0.0.4 56 | */ 57 | protected volatile boolean check = true; 58 | 59 | /** 60 | * 为剩余的请求等待时间 61 | * @since 0.0.5 62 | */ 63 | protected long waitMillsForRemainRequest = 60 * 1000; 64 | 65 | /** 66 | * 调用管理类 67 | * 68 | * @since 1.0.0 69 | */ 70 | protected final IInvokeService invokeService = new InvokeService(); 71 | 72 | /** 73 | * 消息监听服务类 74 | * @since 0.0.5 75 | */ 76 | protected final IMqListenerService mqListenerService = new MqListenerService(); 77 | 78 | /** 79 | * 状态管理类 80 | * @since 0.0.5 81 | */ 82 | protected final IStatusManager statusManager = new StatusManager(); 83 | 84 | /** 85 | * 生产者-中间服务端服务类 86 | * @since 0.0.5 87 | */ 88 | protected final IConsumerBrokerService consumerBrokerService = new ConsumerBrokerService(); 89 | 90 | /** 91 | * 负载均衡策略 92 | * @since 0.0.7 93 | */ 94 | protected ILoadBalance loadBalance = LoadBalances.weightRoundRobbin(); 95 | 96 | /** 97 | * 订阅最大尝试次数 98 | * @since 0.0.8 99 | */ 100 | protected int subscribeMaxAttempt = 3; 101 | 102 | /** 103 | * 取消订阅最大尝试次数 104 | * @since 0.0.8 105 | */ 106 | protected int unSubscribeMaxAttempt = 3; 107 | 108 | /** 109 | * 消费状态更新最大尝试次数 110 | * @since 0.1.0 111 | */ 112 | protected int consumerStatusMaxAttempt = 3; 113 | 114 | /** 115 | * 账户标识 116 | * @since 0.1.4 117 | */ 118 | protected String appKey; 119 | 120 | /** 121 | * 账户密码 122 | * @since 0.1.4 123 | */ 124 | protected String appSecret; 125 | 126 | public String appKey() { 127 | return appKey; 128 | } 129 | 130 | public MqConsumerPush appKey(String appKey) { 131 | this.appKey = appKey; 132 | return this; 133 | } 134 | 135 | public String appSecret() { 136 | return appSecret; 137 | } 138 | 139 | public MqConsumerPush appSecret(String appSecret) { 140 | this.appSecret = appSecret; 141 | return this; 142 | } 143 | 144 | public MqConsumerPush consumerStatusMaxAttempt(int consumerStatusMaxAttempt) { 145 | this.consumerStatusMaxAttempt = consumerStatusMaxAttempt; 146 | return this; 147 | } 148 | 149 | public MqConsumerPush subscribeMaxAttempt(int subscribeMaxAttempt) { 150 | this.subscribeMaxAttempt = subscribeMaxAttempt; 151 | return this; 152 | } 153 | 154 | public MqConsumerPush unSubscribeMaxAttempt(int unSubscribeMaxAttempt) { 155 | this.unSubscribeMaxAttempt = unSubscribeMaxAttempt; 156 | return this; 157 | } 158 | 159 | public MqConsumerPush groupName(String groupName) { 160 | this.groupName = groupName; 161 | return this; 162 | } 163 | 164 | public MqConsumerPush brokerAddress(String brokerAddress) { 165 | this.brokerAddress = brokerAddress; 166 | return this; 167 | } 168 | 169 | public MqConsumerPush respTimeoutMills(long respTimeoutMills) { 170 | this.respTimeoutMills = respTimeoutMills; 171 | return this; 172 | } 173 | 174 | public MqConsumerPush check(boolean check) { 175 | this.check = check; 176 | return this; 177 | } 178 | 179 | public MqConsumerPush waitMillsForRemainRequest(long waitMillsForRemainRequest) { 180 | this.waitMillsForRemainRequest = waitMillsForRemainRequest; 181 | return this; 182 | } 183 | 184 | public MqConsumerPush loadBalance(ILoadBalance loadBalance) { 185 | this.loadBalance = loadBalance; 186 | return this; 187 | } 188 | 189 | /** 190 | * 参数校验 191 | */ 192 | private void paramCheck() { 193 | ArgUtil.notEmpty(brokerAddress, "brokerAddress"); 194 | ArgUtil.notEmpty(groupName, "groupName"); 195 | } 196 | 197 | @Override 198 | public void run() { 199 | // 启动服务端 200 | log.info("MQ 消费者开始启动服务端 groupName: {}, brokerAddress: {}", 201 | groupName, brokerAddress); 202 | 203 | //1. 参数校验 204 | this.paramCheck(); 205 | 206 | try { 207 | //0. 配置信息 208 | ConsumerBrokerConfig config = ConsumerBrokerConfig.newInstance() 209 | .groupName(groupName) 210 | .brokerAddress(brokerAddress) 211 | .check(check) 212 | .respTimeoutMills(respTimeoutMills) 213 | .invokeService(invokeService) 214 | .statusManager(statusManager) 215 | .mqListenerService(mqListenerService) 216 | .loadBalance(loadBalance) 217 | .subscribeMaxAttempt(subscribeMaxAttempt) 218 | .unSubscribeMaxAttempt(unSubscribeMaxAttempt) 219 | .consumerStatusMaxAttempt(consumerStatusMaxAttempt) 220 | .appKey(appKey) 221 | .appSecret(appSecret); 222 | 223 | //1. 初始化 224 | this.consumerBrokerService.initChannelFutureList(config); 225 | 226 | //2. 连接到服务端 227 | this.consumerBrokerService.registerToBroker(); 228 | 229 | //3. 标识为可用 230 | statusManager.status(true); 231 | 232 | //4. 添加钩子函数 233 | final DefaultShutdownHook rpcShutdownHook = new DefaultShutdownHook(); 234 | rpcShutdownHook.setStatusManager(statusManager); 235 | rpcShutdownHook.setInvokeService(invokeService); 236 | rpcShutdownHook.setWaitMillsForRemainRequest(waitMillsForRemainRequest); 237 | rpcShutdownHook.setDestroyable(this.consumerBrokerService); 238 | ShutdownHooks.rpcShutdownHook(rpcShutdownHook); 239 | 240 | //5. 启动完成以后的事件 241 | this.afterInit(); 242 | 243 | log.info("MQ 消费者启动完成"); 244 | } catch (Exception e) { 245 | log.error("MQ 消费者启动异常", e); 246 | 247 | statusManager.initFailed(true); 248 | 249 | throw new MqException(ConsumerRespCode.RPC_INIT_FAILED); 250 | } 251 | } 252 | 253 | /** 254 | * 初始化完成以后 255 | */ 256 | protected void afterInit() { 257 | 258 | } 259 | 260 | @Override 261 | public void subscribe(String topicName, String tagRegex) { 262 | final String consumerType = getConsumerType(); 263 | consumerBrokerService.subscribe(topicName, tagRegex, consumerType); 264 | } 265 | 266 | @Override 267 | public void unSubscribe(String topicName, String tagRegex) { 268 | final String consumerType = getConsumerType(); 269 | consumerBrokerService.unSubscribe(topicName, tagRegex, consumerType); 270 | } 271 | 272 | @Override 273 | public void registerListener(IMqConsumerListener listener) { 274 | this.mqListenerService.register(listener); 275 | } 276 | 277 | /** 278 | * 获取消费策略类型 279 | * @return 类型 280 | * @since 0.0.9 281 | */ 282 | protected String getConsumerType() { 283 | return ConsumerTypeConst.PUSH; 284 | } 285 | 286 | } 287 | -------------------------------------------------------------------------------- /mq-broker/src/main/java/com/github/houbb/mq/broker/support/api/LocalBrokerConsumerService.java: -------------------------------------------------------------------------------- 1 | package com.github.houbb.mq.broker.support.api; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.github.houbb.heaven.util.util.CollectionUtil; 5 | import com.github.houbb.heaven.util.util.MapUtil; 6 | import com.github.houbb.heaven.util.util.regex.RegexUtil; 7 | import com.github.houbb.load.balance.api.ILoadBalance; 8 | import com.github.houbb.log.integration.core.Log; 9 | import com.github.houbb.log.integration.core.LogFactory; 10 | import com.github.houbb.mq.broker.api.IBrokerConsumerService; 11 | import com.github.houbb.mq.broker.dto.BrokerServiceEntryChannel; 12 | import com.github.houbb.mq.broker.dto.ChannelGroupNameDto; 13 | import com.github.houbb.mq.broker.dto.ServiceEntry; 14 | import com.github.houbb.mq.broker.dto.consumer.ConsumerSubscribeBo; 15 | import com.github.houbb.mq.broker.dto.consumer.ConsumerSubscribeReq; 16 | import com.github.houbb.mq.broker.dto.consumer.ConsumerUnSubscribeReq; 17 | import com.github.houbb.mq.broker.resp.MqBrokerRespCode; 18 | import com.github.houbb.mq.broker.utils.InnerChannelUtils; 19 | import com.github.houbb.mq.common.dto.req.MqHeartBeatReq; 20 | import com.github.houbb.mq.common.dto.req.MqMessage; 21 | import com.github.houbb.mq.common.dto.resp.MqCommonResp; 22 | import com.github.houbb.mq.common.resp.MqCommonRespCode; 23 | import com.github.houbb.mq.common.resp.MqException; 24 | import com.github.houbb.mq.common.util.ChannelUtil; 25 | import com.github.houbb.mq.common.util.RandomUtils; 26 | import io.netty.channel.Channel; 27 | 28 | import java.util.*; 29 | import java.util.concurrent.ConcurrentHashMap; 30 | import java.util.concurrent.Executors; 31 | import java.util.concurrent.ScheduledExecutorService; 32 | import java.util.concurrent.TimeUnit; 33 | 34 | /** 35 | * @author binbin.hou 36 | * @since 1.0.0 37 | */ 38 | public class LocalBrokerConsumerService implements IBrokerConsumerService { 39 | 40 | private static final Log log = LogFactory.getLog(LocalBrokerConsumerService.class); 41 | 42 | private final Map registerMap = new ConcurrentHashMap<>(); 43 | 44 | /** 45 | * 订阅集合-推送策略 46 | * key: topicName 47 | * value: 对应的订阅列表 48 | */ 49 | private final Map> pushSubscribeMap = new ConcurrentHashMap<>(); 50 | 51 | /** 52 | * 心跳 map 53 | * @since 0.0.6 54 | */ 55 | private final Map heartbeatMap = new ConcurrentHashMap<>(); 56 | 57 | /** 58 | * 心跳定时任务 59 | * 60 | * @since 0.0.6 61 | */ 62 | private static final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); 63 | 64 | /** 65 | * 负载均衡策略 66 | * @since 0.0.7 67 | */ 68 | private ILoadBalance loadBalance; 69 | 70 | public LocalBrokerConsumerService() { 71 | //120S 扫描一次 72 | final long limitMills = 2 * 60 * 1000; 73 | scheduledExecutorService.scheduleAtFixedRate(new Runnable() { 74 | @Override 75 | public void run() { 76 | for(Map.Entry entry : heartbeatMap.entrySet()) { 77 | String key = entry.getKey(); 78 | long lastAccessTime = entry.getValue().getLastAccessTime(); 79 | long currentTime = System.currentTimeMillis(); 80 | 81 | if(currentTime - lastAccessTime > limitMills) { 82 | removeByChannelId(key); 83 | } 84 | } 85 | } 86 | }, 2 * 60, 2 * 60, TimeUnit.SECONDS); 87 | } 88 | 89 | @Override 90 | public void loadBalance(ILoadBalance loadBalance) { 91 | this.loadBalance = loadBalance; 92 | } 93 | 94 | @Override 95 | public MqCommonResp register(ServiceEntry serviceEntry, Channel channel) { 96 | final String channelId = ChannelUtil.getChannelId(channel); 97 | BrokerServiceEntryChannel entryChannel = InnerChannelUtils.buildEntryChannel(serviceEntry, channel); 98 | registerMap.put(channelId, entryChannel); 99 | 100 | entryChannel.setLastAccessTime(System.currentTimeMillis()); 101 | heartbeatMap.put(channelId, entryChannel); 102 | 103 | MqCommonResp resp = new MqCommonResp(); 104 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 105 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 106 | return resp; 107 | } 108 | 109 | @Override 110 | public MqCommonResp unRegister(ServiceEntry serviceEntry, Channel channel) { 111 | final String channelId = ChannelUtil.getChannelId(channel); 112 | removeByChannelId(channelId); 113 | 114 | MqCommonResp resp = new MqCommonResp(); 115 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 116 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 117 | return resp; 118 | } 119 | 120 | /** 121 | * 根据 channelId 移除信息 122 | * @param channelId 通道唯一标识 123 | * @since 0.0.6 124 | */ 125 | private void removeByChannelId(final String channelId) { 126 | BrokerServiceEntryChannel channelRegister = registerMap.remove(channelId); 127 | log.info("移除注册信息 id: {}, channel: {}", channelId, JSON.toJSON(channelRegister)); 128 | BrokerServiceEntryChannel channelHeartbeat = heartbeatMap.remove(channelId); 129 | log.info("移除心跳信息 id: {}, channel: {}", channelId, JSON.toJSON(channelHeartbeat)); 130 | } 131 | 132 | @Override 133 | public MqCommonResp subscribe(ConsumerSubscribeReq serviceEntry, Channel clientChannel) { 134 | final String channelId = ChannelUtil.getChannelId(clientChannel); 135 | final String topicName = serviceEntry.getTopicName(); 136 | 137 | final String consumerType = serviceEntry.getConsumerType(); 138 | Map> subscribeMap = getSubscribeMapByConsumerType(consumerType); 139 | 140 | ConsumerSubscribeBo subscribeBo = new ConsumerSubscribeBo(); 141 | subscribeBo.setChannelId(channelId); 142 | subscribeBo.setGroupName(serviceEntry.getGroupName()); 143 | subscribeBo.setTopicName(topicName); 144 | subscribeBo.setTagRegex(serviceEntry.getTagRegex()); 145 | 146 | // 放入集合 147 | MapUtil.putToSetMap(subscribeMap, topicName, subscribeBo); 148 | 149 | MqCommonResp resp = new MqCommonResp(); 150 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 151 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 152 | return resp; 153 | } 154 | 155 | private Map> getSubscribeMapByConsumerType(String consumerType) { 156 | return pushSubscribeMap; 157 | } 158 | 159 | @Override 160 | public MqCommonResp unSubscribe(ConsumerUnSubscribeReq serviceEntry, Channel clientChannel) { 161 | final String channelId = ChannelUtil.getChannelId(clientChannel); 162 | final String topicName = serviceEntry.getTopicName(); 163 | final String consumerType = serviceEntry.getConsumerType(); 164 | Map> subscribeMap = getSubscribeMapByConsumerType(consumerType); 165 | 166 | ConsumerSubscribeBo subscribeBo = new ConsumerSubscribeBo(); 167 | subscribeBo.setChannelId(channelId); 168 | subscribeBo.setGroupName(serviceEntry.getGroupName()); 169 | subscribeBo.setTopicName(topicName); 170 | subscribeBo.setTagRegex(serviceEntry.getTagRegex()); 171 | 172 | // 集合 173 | Set set = subscribeMap.get(topicName); 174 | if(CollectionUtil.isNotEmpty(set)) { 175 | set.remove(subscribeBo); 176 | } 177 | 178 | MqCommonResp resp = new MqCommonResp(); 179 | resp.setRespCode(MqCommonRespCode.SUCCESS.getCode()); 180 | resp.setRespMessage(MqCommonRespCode.SUCCESS.getMsg()); 181 | return resp; 182 | } 183 | 184 | @Override 185 | public List getPushSubscribeList(MqMessage mqMessage) { 186 | final String topicName = mqMessage.getTopic(); 187 | Set set = pushSubscribeMap.get(topicName); 188 | if(CollectionUtil.isEmpty(set)) { 189 | return Collections.emptyList(); 190 | } 191 | 192 | //2. 获取匹配的 tag 列表 193 | final List tagNameList = mqMessage.getTags(); 194 | 195 | Map> groupMap = new HashMap<>(); 196 | for(ConsumerSubscribeBo bo : set) { 197 | String tagRegex = bo.getTagRegex(); 198 | 199 | if(RegexUtil.hasMatch(tagNameList, tagRegex)) { 200 | String groupName = bo.getGroupName(); 201 | 202 | MapUtil.putToListMap(groupMap, groupName, bo); 203 | } 204 | } 205 | 206 | //3. 按照 groupName 分组之后,每一组只随机返回一个。最好应该调整为以 shardingkey 选择 207 | final String shardingKey = mqMessage.getShardingKey(); 208 | List channelGroupNameList = new ArrayList<>(); 209 | 210 | for(Map.Entry> entry : groupMap.entrySet()) { 211 | List list = entry.getValue(); 212 | 213 | ConsumerSubscribeBo bo = RandomUtils.loadBalance(loadBalance, list, shardingKey); 214 | final String channelId = bo.getChannelId(); 215 | BrokerServiceEntryChannel entryChannel = registerMap.get(channelId); 216 | if(entryChannel == null) { 217 | log.warn("channelId: {} 对应的通道信息为空", channelId); 218 | continue; 219 | } 220 | 221 | final String groupName = entry.getKey(); 222 | ChannelGroupNameDto channelGroupNameDto = ChannelGroupNameDto.of(groupName, 223 | entryChannel.getChannel()); 224 | channelGroupNameList.add(channelGroupNameDto); 225 | } 226 | 227 | return channelGroupNameList; 228 | } 229 | 230 | @Override 231 | public void heartbeat(MqHeartBeatReq mqHeartBeatReq, Channel channel) { 232 | final String channelId = ChannelUtil.getChannelId(channel); 233 | log.info("[HEARTBEAT] 接收消费者心跳 {}, channelId: {}", 234 | JSON.toJSON(mqHeartBeatReq), channelId); 235 | 236 | ServiceEntry serviceEntry = new ServiceEntry(); 237 | serviceEntry.setAddress(mqHeartBeatReq.getAddress()); 238 | serviceEntry.setPort(mqHeartBeatReq.getPort()); 239 | 240 | BrokerServiceEntryChannel entryChannel = InnerChannelUtils.buildEntryChannel(serviceEntry, channel); 241 | entryChannel.setLastAccessTime(mqHeartBeatReq.getTime()); 242 | 243 | heartbeatMap.put(channelId, entryChannel); 244 | } 245 | 246 | @Override 247 | public void checkValid(String channelId) { 248 | if(!registerMap.containsKey(channelId)) { 249 | log.error("channelId: {} 未注册", channelId); 250 | throw new MqException(MqBrokerRespCode.C_REGISTER_CHANNEL_NOT_VALID); 251 | } 252 | } 253 | 254 | } 255 | --------------------------------------------------------------------------------