发现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. pm2笔记

    概述 pm2是一个进程管理工具.使用pm2部署NodeJS服务可以轻松实现负载均衡. 指定用户启动 pm2启动时会指定一个PM2_HOME目录,作为存放日志文件.rpc.sock文件,默认情况下会PM ...

  2. JavaScript的条件运算符与条件语句

    1.条件运算符 比较运算符 ==    判断左右两边数据的值是否相等 ===   判断左右两边数据的之是否相等,同时还判断两边的数据类型是否一样 !=  比较运算符的比较结果都是布尔值,true或者f ...

  3. selenium+python自动化82-只截某个元素的图【转载】

    前言 selenium截取全图小伙伴们都知道,曾经去面试的时候,面试官问:如何截图某个元素的图?不要全部的,只要某个元素...小编一下子傻眼了,苦心人,天不负,终于找到解决办法了. selenium截 ...

  4. Chrome崩溃卡死

    Chrome崩溃卡死主要是Flash崩溃导致,解决办法:1,chrome://plugins,停用Chrome自带的Flash插件:2,停用GPU加速.chrome://flags,停用对所有网页执行 ...

  5. Django remedy a security issue refer dos attack

    Today the Django team is issuing multiple releases -- Django 1.4.8, Django 1.5.4, and Django 1.6 bet ...

  6. flutter 极光推送

    一.配置极光开发者 1.注册极光开发者 https://www.jiguang.cn/push 2.创建应用 二.创建APP 1.添加依赖 pubspec.yaml  添加: flutter_jpus ...

  7. 传统路径导出 VS 直接路径导出(oracle exp direct=y)

    Oracle 传统的Export与Import依旧被保留到11g,而且9i与10g有很多依旧使用Export与Import方式进行备份与恢复的企业.从Oracle 7.3开始,传统的exp导出程序提供 ...

  8. mysql使用default来设置字段的默认值

    mysql创建表时,我们可以使用default来设置表字段的默认值,这样当向表中插入或添加数据时,如果没有为此字段设置任何值,则使用default默认值来填充该字段的值 在使用create table ...

  9. [BZOJ 1145] 图腾totem

    Link:https://www.lydsy.com/JudgeOnline/problem.php?id=1145 Solution: 算是一道神题了吧 设 f(abcd)为:当选出的四个数相对大小 ...

  10. 【线性筛】【筛法求素数】【约数个数定理】URAL - 2070 - Interesting Numbers

    素数必然符合题意. 对于合数,如若它是某个素数x的k次方(k为某个素数y减去1),一定不符合题意.只需找出这些数. 由约数个数定理,其他合数一定符合题意. 就从小到大枚举素数,然后把它的素数-1次方都 ...