数据库路由中间件MyCat - 源代码篇(5)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
3. 连接模块
如之前所述,MyCat的连接分为前端和后端,下面是连接基本相关类图:
3.1 ClosableConnection:
public interface ClosableConnection { String getCharset(); //关闭连接
void close(String reason); boolean isClosed(); public void idleCheck(); long getStartupTime(); String getHost(); int getPort(); int getLocalPort(); long getNetInBytes(); long getNetOutBytes();
}
根据字面意思,一个可以关闭的连接需要实现关闭方法-_-,并且需要原因判断是否是正常关闭。MySQL的通信都需要指定字符集。MyCat服务器建立ServerSocket时输入的端口为服务器在其上面监听客户的连接,当有客户连接时,在随机选择一个没用的端口与客户端通信;建立客户socket时输入的为服务端的监听端口,在本地选择一个未用端口与服务器通信,至于服务器怎么知道和客户端的哪个端口通信,和客户端怎么知道和服务端的哪个端口通信(因为这两个端口都是随机生成的),tcp是采用"三次握手"建立连接,而udp则是每次发送信息时将端口号放在ip报文的数据段里面。所以,连接里面需要提供获得监听端口和服务端口的方法。此外,还需要检查连接是否为空闲状态(idle)。最后,需要一些统计数据。
3.2 NIOConnection:
public interface NIOConnection extends ClosableConnection { //connected
void register() throws IOException; //处理数据
void handle(byte[] data); // 写出一块缓冲数据
void write(ByteBuffer buffer);
}
所有NIO的通信需要在多路复用选择器上注册channel,这里有个对应的register()方法需要实现。然后,读取和写入数据都需要通过缓冲。缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区,使用缓冲区有这么两个好处:
减少实际的物理读写次数
缓冲区在创建时就被分配内存,这块内存区域一直被重用,可以减少动态分配和回收内存的次数 读取到的数据需要经过处理,这里对应的就是handle(byte[])方法。
3.3 AbstractConnection:
从上面的实体图,我们发现,AbstractConnection其实就是把Java的NetworkChannel进行封装,同时需要依赖其他几个类来完成他所需要的操作,如下: 其中,NIOProcessor是对AbstractConnection实现NIO读写的方法类,NIOHandler是处理AbstractConnection读取的数据的处理方法类,NIOSocketWR是执行以上方法的线程类。
3.3.1 NIOProcessor:
NIOProcessor的构建方法:
public NIOProcessor(String name, BufferPool bufferPool,
NameableExecutor executor) throws IOException { this.name = name; this.bufferPool = bufferPool; this.executor = executor; this.frontends = new ConcurrentHashMap<Long, FrontendConnection>(); this.backends = new ConcurrentHashMap<Long, BackendConnection>(); this.commands = new CommandCount();
}
调用位置: MyCatServer.java
...
bufferPool = new BufferPool(processBuferPool, processBufferChunk,
socketBufferLocalPercent / processorCount);
businessExecutor = ExecutorUtil.create("BusinessExecutor",
threadPoolSize);
...for (int i = 0; i < processors.length; i++) {
processors[i] = new NIOProcessor("Processor" + i, bufferPool,
businessExecutor);
}
...
每个MyCat实例会初始化processors个NIOProcessor,每个NIOProcessor公用同一个bufferPool和businessExecutor。 bufferPool是缓冲池,BufferPool这个类负责缓冲统一管理 businessExecutor如之前所述,是业务线程池。 NIOProcessor被池化,很简单,就是保存到数组中,通过MyCatServer的nextProcessor()方法轮询获取一个NIOProcessor,之后每个AbstractConnection通过setNIOProcessor方法,设置NIOProcessor。
public NIOProcessor nextProcessor() { int i = ++nextProcessor; if (i >= processors.length) {
i = nextProcessor = 0;
} return processors[i];
}
可以看出,每个AbstractConnection依赖于一个NIOProcessor,每个NIOProcessor保存着多个AbstractConnection。AbstractConnection分为FrontendConnection和BackendConnection被分别保存在NIOProcessor的frontends和backends这两个ConcurrentHashMap中。 用ConcurrentHashMap是因为NIOAcceptor和NIOConnector线程以及RW线程池都会访问这两个变量。 NIOProcessor其实主要负责连接资源的管理: MyCat会定时检查前端和后端空闲连接,并清理和回收资源: MyCatServer.java:
// 处理器定时检查任务
private TimerTask processorCheck() { return new TimerTask() { @Override
public void run() {
timerExecutor.execute(new Runnable() { @Override
public void run() { try { for (NIOProcessor p : processors) {
p.checkBackendCons();
}
} catch (Exception e) {
LOGGER.warn("checkBackendCons caught err:" + e);
} }
});
timerExecutor.execute(new Runnable() { @Override
public void run() { try { for (NIOProcessor p : processors) {
p.checkFrontCons();
}
} catch (Exception e) {
LOGGER.warn("checkFrontCons caught err:" + e);
}
}
});
}
};
}
检查前端连接,回收空闲资源:
/**
* 定时执行该方法,回收部分资源。
*/
public void checkFrontCons() {
frontendCheck();
} private void frontendCheck() {
Iterator<Entry<Long, FrontendConnection>> it = frontends.entrySet()
.iterator(); while (it.hasNext()) {
FrontendConnection c = it.next().getValue(); // 删除空连接
if (c == null) {
it.remove(); this.frontendsLength.decrementAndGet(); continue;
} // 清理已关闭连接,否则空闲检查。
if (c.isClosed()) {
c.cleanup();
it.remove(); this.frontendsLength.decrementAndGet();
} else { // very important ,for some data maybe not sent
checkConSendQueue(c);
c.idleCheck();
}
}
}
在关闭前端连接时,会清理连接占用的缓存资源: FrontendConnection.java:
protected void cleanup() { //回收读缓冲
if (readBuffer != null) {
recycle(readBuffer); this.readBuffer = null; this.readBufferOffset = 0;
} //回收写缓冲
if (writeBuffer != null) {
recycle(writeBuffer); this.writeBuffer = null;
} //回收压缩协议栈编码解码队列
if(!decompressUnfinishedDataQueue.isEmpty())
{
decompressUnfinishedDataQueue.clear();
} if(!compressUnfinishedDataQueue.isEmpty())
{
compressUnfinishedDataQueue.clear();
} //回收写队列
ByteBuffer buffer = null; while ((buffer = writeQueue.poll()) != null) {
recycle(buffer);
}
}
后端连接检查,除了要清理已关闭的连接,还有要检查SQL执行时间是否超时:
/**
* 定时执行该方法,回收部分资源。
*/
public void checkBackendCons() {
backendCheck();
} // 后端连接检查
private void backendCheck() { long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;
Iterator<Entry<Long, BackendConnection>> it = backends.entrySet().iterator(); while (it.hasNext()) {
BackendConnection c = it.next().getValue(); // 删除空连接
if (c == null) {
it.remove(); continue;
} // SQL执行超时的连接关闭
if (c.isBorrowed()
&& c.getLastTime() < TimeUtil.currentTimeMillis()
- sqlTimeout) {
LOGGER.warn("found backend connection SQL timeout ,close it "
+ c);
c.close("sql timeout");
} // 清理已关闭连接,否则空闲检查。
if (c.isClosed()) {
it.remove(); } else { // very important ,for some data maybe not sent
if (c instanceof AbstractConnection) {
checkConSendQueue((AbstractConnection) c);
}
c.idleCheck();
}
}
}
同时,在检查连接是否关闭时,需要检查写队列是否为空。写队列不为空,证明还有请求没有响应。需要将写队列的剩余请求异步写出,通过NIOSocketWR。
private void checkConSendQueue(AbstractConnection c) { // very important ,for some data maybe not sent
if (!c.writeQueue.isEmpty()) {
c.getSocketWR().doNextWriteCheck();
}
}
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 类似gitlab代码提交的热力图怎么做?
【推荐】 JAVA虚拟机的类加载机制
数据库路由中间件MyCat - 源代码篇(5)的更多相关文章
- 数据库路由中间件MyCat - 源代码篇(1)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...
- 数据库路由中间件MyCat - 源代码篇(13)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...
- 数据库路由中间件MyCat - 源代码篇(7)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...
- 数据库路由中间件MyCat - 源代码篇(15)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. public static void handle(String stmt, ServerConnectio ...
- 数据库路由中间件MyCat - 源代码篇(17)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...
- 数据库路由中间件MyCat - 源代码篇(14)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...
- 数据库路由中间件MyCat - 源代码篇(4)
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(2)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(16)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...
- 数据库路由中间件MyCat - 源代码篇(10)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...
随机推荐
- 【题解】[Ghd]
[题解]Ghd 一道概率非酋题? 题目很有意思,要我们选出大于\(\frac{n}{2}\)个数字使得他们的最大公约数最大. 那么我们若随便选择一个数字,他在答案的集合里的概率就大于\(0.5\)了. ...
- 利用wxpython显示OpenCV图像
核心代码 import wx, cv2 import numpy as np # Start with a numpy array style image I'll call "source ...
- Docker实践中遇到的坑
1.docker容器中后台运行退出执行curl+p+q,再次进入执行命令docker attach 容器id. 2.容器中exit退出后,还原方法为docker ps -a 查看历史运行容器,dock ...
- perl之创建临时文件夹遇到同名文件该咋办
当你在目录下进行一系列操作时,若要创建许多文件或者修改文件,可能会遇到许多麻烦的事.所以呢,新建一个文件夹,然后在这个文件夹下新建文件或者修改文件.假设,你的代码要在一个目录下新建一个文件夹,名为Tm ...
- GDB打印内存命令
用gdb查看内存 格式 x /nfu 参数说明 x是 examine 的缩写 n表示要显示的内存单元的个数 f表示显示方式, 可取如下值 x 按十六进制格式显示变量 d 按十进制格式显示变量 u 按十 ...
- 解决/usr/bin/ld: cannot find -lmysqlclient错误
类似/usr/bin/ld: cannot find -xxxx的错误有很多, 首先我们可以最简单的判断一下: 这类情况一般是由于缺乏某某库文件, 又或者可能是由于已存在的库问题版本不对造成的 一般都 ...
- linux系统调用mount全过程分析【转】
本文转载自:https://blog.csdn.net/skyflying2012/article/details/9748133 系统调用本身是软中断,使用系统调用,内核也陷入内核态,异常处理,找到 ...
- 图解HTTP接口自动化测试框架使用
Robot Framework是一款python语言编写,通用的功能自动化测试框架.它使用了比较易用的表格数据语法,基于关键字驱动测试,主要用来验收测试和验收测试驱动开发(ATDD). 运行RIDE, ...
- 分享知识-快乐自己:Hibernate对象的三种状态
图解: 1):瞬时状态(Transient) 对象与session没有关联,数据库中没有对应的数据. 一般new出来的对象便是瞬时对象. 对瞬时对象使用save()方法便使之成为持久对象. 由于数据库 ...
- Go丨语言对数据库操作报错 panic: dial tcp 127.0.0.1:3306: connectex: No connection could be made because the target machine actively refused it.
panic: dial tcp 127.0.0.1:3306: connectex: No connection could be made because the target machine ac ...