前景回顧
【mq】從零開始實現 mq-02-如何實現生產者呼叫消費者?
【mq】從零開始實現 mq-03-引入 broker 中間人
上一節我們引入了中間人 broker,讓訊息的生產者和消費者解耦。
這一節我們對初始化程式碼進行優化,便於後期擴充維護。
生產者啟動優化
啟動實現
整體實現調整如下:
@Override
public synchronized void run() {
this.paramCheck();
// 啟動服務端
log.info("MQ 生產者開始啟動客戶端 GROUP: {}, PORT: {}, brokerAddress: {}",
groupName, port, brokerAddress);
try {
//channel future
this.channelFutureList = ChannelFutureUtils.initChannelFutureList(brokerAddress,
initChannelHandler(), check);
// register to broker
this.registerToBroker();
// 標識為可用
enableFlag = true;
log.info("MQ 生產者啟動完成");
} catch (Exception e) {
log.error("MQ 生產者啟動遇到異常", e);
throw new MqException(ProducerRespCode.RPC_INIT_FAILED);
}
}
看起來是不是比起原來清爽很多呢?
但是複雜性只會轉移,不會消失。
答案就是封裝到 initChannelFutureList 中去了。
initChannelFutureList
因為這裡是生產者、消費者都會用到。
所以我們先放在統一的工具類中,實現本身和以前大同小異。
/**
* 初始化列表
* @param brokerAddress 地址
* @param channelHandler 處理類
* @param check 是否檢測可用性
* @return 結果
* @since 0.0.4
*/
public static List<RpcChannelFuture> initChannelFutureList(final String brokerAddress,
final ChannelHandler channelHandler,
final boolean check) {
List<RpcAddress> addressList = InnerAddressUtils.initAddressList(brokerAddress);
List<RpcChannelFuture> list = new ArrayList<>();
for(RpcAddress rpcAddress : addressList) {
try {
final String address = rpcAddress.getAddress();
final int port = rpcAddress.getPort();
EventLoopGroup workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
ChannelFuture channelFuture = bootstrap.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<Channel>(){
@Override
protected void initChannel(Channel ch) throws Exception {
ch.pipeline()
.addLast(new LoggingHandler(LogLevel.INFO))
.addLast(channelHandler);
}
})
.connect(address, port)
.syncUninterruptibly();
log.info("啟動客戶端完成,監聽 address: {}, port:{}", address, port);
RpcChannelFuture rpcChannelFuture = new RpcChannelFuture();
rpcChannelFuture.setChannelFuture(channelFuture);
rpcChannelFuture.setAddress(address);
rpcChannelFuture.setPort(port);
rpcChannelFuture.setWeight(rpcAddress.getWeight());
list.add(rpcChannelFuture);
} catch (Exception exception) {
log.error("註冊到 broker 服務端異常", exception);
if(check) {
throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED);
}
}
}
if(check
&& CollectionUtil.isEmpty(list)) {
log.error("check=true 且可用列表為空,啟動失敗。");
throw new MqException(MqCommonRespCode.REGISTER_TO_BROKER_FAILED);
}
return list;
}
這裡的 check 為了避免 2 種情況:
(1)某一個 broker 不可用
(2)沒有可用的 broker 資訊。
消費者啟動優化
消費者連線 broker 和生產者是類似的。
這裡只是放一下實現,不做更多的贅述。
@Override
public void run() {
// 啟動服務端
log.info("MQ 消費者開始啟動服務端 groupName: {}, brokerAddress: {}",
groupName, brokerAddress);
//1. 引數校驗
this.paramCheck();
try {
//channel future
this.channelFutureList = ChannelFutureUtils.initChannelFutureList(brokerAddress,
initChannelHandler(),
check);
// register to broker
this.registerToBroker();
// 標識為可用
enableFlag = true;
log.info("MQ 消費者啟動完成");
} catch (Exception e) {
log.error("MQ 消費者啟動異常", e);
throw new MqException(ConsumerRespCode.RPC_INIT_FAILED);
}
}
小結
這一小節的內容特別簡單,對初始化部分做了優化,便於後期維護擴充。
希望本文對你有所幫助,如果喜歡,歡迎點贊收藏轉發一波。
我是老馬,期待與你的下次重逢。
開源地址
The message queue in java.(java 簡易版本 mq 實現) https://github.com/houbb/mq