发现rabbitmq有一个ConnectionFactory。发现rocketmq好像没这个东西。按道理来说如果每次发送消息都新建一条连接肯定是不可能的。

ps:其实之所以是有上面的疑问是因为数据库连接池那个地方来的,因为数据库连接connection并没有说是线程安全的,所以为了线程安全会为每个事物单独分配一个连接。但是rocketmq用的是netty的长连接channel,Java 上关于SocketChannel 的注释 说明是安全的。

Netty : writeAndFlush的线程安全及并发问题

可见,writeAndFlush如果在Netty线程池内执行,则是直接write;否则,将作为一个task插入到Netty线程池执行。

这个问题归根到底是netty的问题。既然是线程安全的,那么整个系统就可以只用一个连接了。

所以我们从源码角度来分析一下rocketmq是如何做得。

下面我们追踪一条消息发送的过程,下面是发送消息时涉及io的模块。

rocketmq就是在channelTables里面维护的连接。

    private Channel getAndCreateNameserverChannel() throws InterruptedException {
String addr = this.namesrvAddrChoosed.get();
if (addr != null) {
ChannelWrapper cw = this.channelTables.get(addr);
if (cw != null && cw.isOK()) {
return cw.getChannel();
}
} final List<String> addrList = this.namesrvAddrList.get();
if (this.lockNamesrvChannel.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
addr = this.namesrvAddrChoosed.get();
if (addr != null) {
ChannelWrapper cw = this.channelTables.get(addr);
if (cw != null && cw.isOK()) {
return cw.getChannel();
}
} if (addrList != null && !addrList.isEmpty()) {
for (int i = 0; i < addrList.size(); i++) {
int index = this.namesrvIndex.incrementAndGet();
index = Math.abs(index);
index = index % addrList.size();
String newAddr = addrList.get(index); this.namesrvAddrChoosed.set(newAddr);
Channel channelNew = this.createChannel(newAddr);
if (channelNew != null)
return channelNew;
}
}
} catch (Exception e) {
log.error("getAndCreateNameserverChannel: create name server channel exception", e);
} finally {
this.lockNamesrvChannel.unlock();
}
} else {
log.warn("getAndCreateNameserverChannel: try to lock name server, but timeout, {}ms", LockTimeoutMillis);
} return null;
}
    private Channel createChannel(final String addr) throws InterruptedException {
ChannelWrapper cw = this.channelTables.get(addr);
if (cw != null && cw.isOK()) {
return cw.getChannel();
} //lockChannelTables是ReentrantLock锁。tryLock() 尝试获取锁,不管成功失败,都立即返回true、false
if (this.lockChannelTables.tryLock(LockTimeoutMillis, TimeUnit.MILLISECONDS)) {
try {
boolean createNewConnection = false;
cw = this.channelTables.get(addr);
if (cw != null) { if (cw.isOK()) {
return cw.getChannel();
} else if (!cw.getChannelFuture().isDone()) {
createNewConnection = false;
} else {
this.channelTables.remove(addr);
createNewConnection = true;
}
} else {
createNewConnection = true;
} if (createNewConnection) {
ChannelFuture channelFuture = this.bootstrap.connect(RemotingHelper.string2SocketAddress(addr));
log.info("createChannel: begin to connect remote host[{}] asynchronously", addr);
cw = new ChannelWrapper(channelFuture);
            //连接被保存到channelTables中。
this
.channelTables.put(addr, cw);
}
} catch (Exception e) {
log.error("createChannel: create channel exception", e);
} finally {
this.lockChannelTables.unlock();
}
} else {
log.warn("createChannel: try to lock channel table, but timeout, {}ms", LockTimeoutMillis);
} if (cw != null) {
ChannelFuture channelFuture = cw.getChannelFuture();
if (channelFuture.awaitUninterruptibly(this.nettyClientConfig.getConnectTimeoutMillis())) {
if (cw.isOK()) {
log.info("createChannel: connect remote host[{}] success, {}", addr, channelFuture.toString());
return cw.getChannel();
} else {
log.warn("createChannel: connect remote host[" + addr + "] failed, " + channelFuture.toString(), channelFuture.cause());
}
} else {
log.warn("createChannel: connect remote host[{}] timeout {}ms, {}", addr, this.nettyClientConfig.getConnectTimeoutMillis(),
channelFuture.toString());
}
} return null;
}

    /**
* @see {@link #connect()}
*/
private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.cause() != null) {
return regFuture;
} final ChannelPromise promise = channel.newPromise();
if (regFuture.isDone()) {
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
} else {
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
}
});
} return promise;
}
    private static void doConnect0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(new OneTimeTask() {
@Override
public void run() {
if (regFuture.isSuccess()) {
if (localAddress == null) {
              //io.netty.channel.connect(remoteAddress,promise);
channel.connect(remoteAddress, promise);
} else {
channel.connect(remoteAddress, localAddress, promise);
}
promise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}

连接这块就点到netty为止,进一步了解可以参考netty这方面的资料。

下面分析rocketmq对channelTables的维护。

1,关闭系统时清空:

2.关闭channel时

RocketMQ之连接以及连接缓存的更多相关文章

  1. MySQL优化二(连接优化和缓存优化)

    body { font-family: Helvetica, arial, sans-serif; font-size: 14px; line-height: 1.6; padding-top: 10 ...

  2. Java创建连接池连接不同数据库

    在一个应用里面,可能涉及到连接多个不同数据库进行操作,而每次连接写不同的实现会很麻烦.前面已经会了用JDBC连接数据库,那么利用反射和工厂模式,可以实现连接不同的数据库,这样处理起来将会很方便.同时建 ...

  3. python通过连接池连接redis,操作redis队列

    在每次使用redis都进行连接的话会拉低redis的效率,都知道redis是基于内存的数据库,效率贼高,所以每次进行连接比真正使用消耗的资源和时间还多.所以为了节省资源,减少多次连接损耗,连接池的作用 ...

  4. spring boot配置druid连接池连接mysql

    Spring Boot 集成教程 Spring Boot 介绍 Spring Boot 开发环境搭建(Eclipse) Spring Boot Hello World (restful接口)例子 sp ...

  5. 安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制

    安卓Socket连接实现连接实现发送接收数据,openwrt wifi转串口连接单片机实现控制 socket 连接采用流的方式进行发送接收数据,采用thread线程的方式. 什么是线程?  详细代码介 ...

  6. 解决Mysql连接池被关闭 ,hibernate尝试连接不能连接的问题。 (默认mysql连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池。系统发布第二天访问链接关闭问题。

    解决Mysql连接池被关闭  ,hibernate尝试连接不能连接的问题. (默认MySQL连接池可以访问的时间为8小时,如果超过8小时没有连接,mysql会自动关闭连接池. 所以系统发布第二天访问会 ...

  7. 通过MSSQL连接服务器连接至Oracle数据库

    前言 有很多时候,我们需要MSSQL与Oracle进行跨库查询或数据交互.本篇随笔将阐述如何通过MSSQL的连接服务器连接至Oracle数据库,并且读取数据的示例. 具体步骤 首先需要到Oracle的 ...

  8. mysql 内连接 左连接 右连接 外连接

    mysql> desc student;+-------+-------------+------+-----+---------+-------+| Field | Type | Null | ...

  9. SQL中的内连接外连接和交叉连接是什么意思?

    内连接又分为等值连接.自然连接和不等连接三种. 外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN).右外连接(RIGHT OUTER JOIN或RIGHT JOIN)和全外连接( ...

  10. SQL内连接-外连接join,left join,right join,full join

    1.创建测试表test1及test2 SQL)); 表已创建. SQL)); 表已创建. ,'name1'); ,'name2'); ,'name3'); ,'name4'); ,'name5'); ...

随机推荐

  1. Linux下安装php环境并且配置Nginx支持php-fpm模块[www]

    Linux下安装php环境并且配置Nginx支持php-fpm模块 http://www.cnblogs.com/freeweb/p/5425554.html 5分钟搭建 nginx +php --- ...

  2. html怎样让表格里面的内容居中

    html怎样让表格里面的内容居中 text-align:center; 在表格td中,有两个属性控制居中显示 align——表示左右居中——left,center,right valign——控制上下 ...

  3. centos7安装tengine强制使用HTTPS访问

    操作系统:centos7.2 x64tengine:Tengine/2.2.0主机IP: 10.0.0.12 一.安装tengine 1.1 下载源码安装包 1.1.1 源码包pcre-8.40   ...

  4. 《锋利的JQuery》读书要点笔记2——DOM操作

    第三章 jQuery中的DOM操作 3.1 DOM(Document Object Model)操作的分类 1. DOM Core    例如:document.getElementsByTagNam ...

  5. MAC使用homeBrew安装Redis

    homeBrew的操作命令如下: brew search ** //查找某个软件包 brew list //列出已经安装的软件的包 brew install ** //安装某个软件包,默认安装的是稳定 ...

  6. 天猫首页迷思之-jquery实现整个div的懒加载(2)-插件面向对象化-闭包和原型的实例

    前文有简单的实现了一个制作懒加载的方法,但其实以方法的形式做插件扩展性不强.那么本文就来用面向对象的方法将其制作成一个真正的插件: 我想要的最终的调用效果是: $(".loading&quo ...

  7. HDU 1308 What Day Is It?(模拟,日期)

    解题报告:输入一个年月日,让你求出那一天是星期几,但是做这题之前必须先了解一点历史.首先在1582年之前,判断是否是闰年的标准是只要能被四整除就是闰年, 然后在1752年9月2号的后的11天被抹去了, ...

  8. Carthage 不知不觉用到了

    Carthage 是什么? 这个问题我们简单点一句话总结: 你知道什么事CocoaPods吧,我相信只要是做iOS的都知道,Carthage 的属性和作用和我们常用的CocoaPods都是一样的,这样 ...

  9. sum (bestcoder)

    sum Accepts: 640 Submissions: 1744 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 131072/13107 ...

  10. 2014 非常好用的开源 Android 测试工具

    http://www.php100.com/html/it/mobile/2014/1015/7495.html 当前有很大的趋势是转向移动应用平台,Android 是最广泛使用的移动操作系统,201 ...