数据库路由中间件MyCat - 源代码篇(8)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
3. 连接模块
3.5 后端连接
对于后端连接,我们只关心MySQL的。
从后端连接工厂开始MySQLConnectionFactory.java:
public MySQLConnection make(MySQLDataSource pool, ResponseHandler handler,
String schema) throws IOException { //DBHost配置
DBHostConfig dsc = pool.getConfig(); //根据是否为NIO返回SocketChannel或者AIO的AsynchronousSocketChannel
NetworkChannel channel = openSocketChannel(MycatServer.getInstance()
.isAIO()); //新建MySQLConnection
MySQLConnection c = new MySQLConnection(channel, pool.isReadNode()); //根据配置初始化MySQLConnection
MycatServer.getInstance().getConfig().setSocketParams(c, false);
c.setHost(dsc.getIp());
c.setPort(dsc.getPort());
c.setUser(dsc.getUser());
c.setPassword(dsc.getPassword());
c.setSchema(schema); //目前实际连接还未建立,handler为MySQL连接认证MySQLConnectionAuthenticator
c.setHandler(new MySQLConnectionAuthenticator(c, handler));
c.setPool(pool);
c.setIdleTimeout(pool.getConfig().getIdleTimeout()); //AIO和NIO连接方式建立实际的MySQL连接
if (channel instanceof AsynchronousSocketChannel) {
((AsynchronousSocketChannel) channel).connect( new InetSocketAddress(dsc.getIp(), dsc.getPort()), c,
(CompletionHandler) MycatServer.getInstance()
.getConnector());
} else { //通过NIOConnector建立连接
((NIOConnector) MycatServer.getInstance().getConnector())
.postConnect(c); } return c;
}
通过NIOConnector建立实际连接的过程与前端连接的建立相似,也是先放在队列中,之后由NIOConnector去建立连接。
public void postConnect(AbstractConnection c) {
connectQueue.offer(c);
selector.wakeup();
}public void run() { final Selector tSelector = this.selector; for (;;) {
++connectCount; try { //查看有无连接就绪
tSelector.select(1000L);
connect(tSelector);
Set<SelectionKey> keys = tSelector.selectedKeys(); try { for (SelectionKey key : keys) {
Object att = key.attachment(); if (att != null && key.isValid() && key.isConnectable()) {
finishConnect(key, att);
} else {
key.cancel();
}
}
} finally {
keys.clear();
}
} catch (Exception e) {
LOGGER.warn(name, e);
}
}
}private void connect(Selector selector) {
AbstractConnection c = null; while ((c = connectQueue.poll()) != null) { try {
SocketChannel channel = (SocketChannel) c.getChannel(); //注册OP_CONNECT监听与后端连接是否真正建立
channel.register(selector, SelectionKey.OP_CONNECT, c); //主动连接
channel.connect(new InetSocketAddress(c.host, c.port));
} catch (Exception e) {
c.close(e.toString());
}
}
}private void finishConnect(SelectionKey key, Object att) {
BackendAIOConnection c = (BackendAIOConnection) att; try { if (finishConnect(c, (SocketChannel) c.channel)) { //做原生NIO连接是否完成的判断和操作
clearSelectionKey(key);
c.setId(ID_GENERATOR.getId()); //绑定特定的NIOProcessor以作idle清理
NIOProcessor processor = MycatServer.getInstance()
.nextProcessor();
c.setProcessor(processor); //与特定NIOReactor绑定监听读写
NIOReactor reactor = reactorPool.getNextReactor();
reactor.postRegister(c);
}
} catch (Exception e) { //如有异常,将key清空
clearSelectionKey(key);
c.close(e.toString());
c.onConnectFailed(e);
}
}private boolean finishConnect(AbstractConnection c, SocketChannel channel)
throws IOException { if (channel.isConnectionPending()) {
channel.finishConnect();
c.setLocalPort(channel.socket().getLocalPort()); return true;
} else { return false;
}
}private void clearSelectionKey(SelectionKey key) { if (key.isValid()) {
key.attach(null);
key.cancel();
}
}
绑定到具体的NIOReactor之后,监听读事件。和之前讲的前端连接建立过程类似。这次是后端MySQL主动发握手包。这时,读事件就绪,NIOReactor中的RW线程会调用对应AbstractConnection(这里是MySQLConnection)的handler的处理方法处理。这里MySQLConnection中的handler参考工厂方法发现是MySQLConnectionAuthenticator。查看handle方法:
/**
* MySQL 4.1版本之前是MySQL323加密,MySQL 4.1和之后的版本都是MySQLSHA1加密,在MySQL5.5的版本之后可以客户端插件式加密(这个MyCat实现)
* @see @http://dev.mysql.com/doc/internals/en/determining-authentication-method.html
*/@Overridepublic void handle(byte[] data) { try { switch (data[4]) { //如果是OkPacket,检查是否认证成功
case OkPacket.FIELD_COUNT:
HandshakePacket packet = source.getHandshake(); if (packet == null) { //如果为null,证明链接第一次建立,处理
processHandShakePacket(data); // 发送认证数据包
source.authenticate(); break;
} // 如果packet不为null,处理认证结果
//首先将连接设为已验证并将handler改为MySQLConnectionHandler
source.setHandler(new MySQLConnectionHandler(source));
source.setAuthenticated(true); //判断是否用了压缩协议
boolean clientCompress = Capabilities.CLIENT_COMPRESS==(Capabilities.CLIENT_COMPRESS & packet.serverCapabilities); boolean usingCompress= MycatServer.getInstance().getConfig().getSystem().getUseCompression()==1 ; if(clientCompress&&usingCompress)
{
source.setSupportCompress(true);
} //设置ResponseHandler
if (listener != null) {
listener.connectionAcquired(source);
} break; //如果为ErrorPacket,则认证失败
case ErrorPacket.FIELD_COUNT:
ErrorPacket err = new ErrorPacket();
err.read(data);
String errMsg = new String(err.message);
LOGGER.warn("can't connect to mysql server ,errmsg:"+errMsg+" "+source); //source.close(errMsg);
throw new ConnectionException(err.errno, errMsg); //如果是EOFPacket,则为MySQL 4.1版本,是MySQL323加密
case EOFPacket.FIELD_COUNT:
auth323(data[3]); break; default:
packet = source.getHandshake(); if (packet == null) {
processHandShakePacket(data); // 发送认证数据包
source.authenticate(); break;
} else { throw new RuntimeException("Unknown Packet!");
} } } catch (RuntimeException e) { if (listener != null) {
listener.connectionError(e, source); return;
} throw e;
}
}
在连接建立并认证后,MySQLConnectionHandler来处理这个连接的请求和相应。
MySQL服务端响应客户端查询请求的流程如下:可以分为三个阶段:
(第一阶段)客户端发送查询请求包COM_QUERY (command query packet),如果有结果集返回,且结果集不为空,则返回FieldCount(列数量)包;如果结果集为空,则返回OKPacket;如果命令有错,则返回ERRPacket;如果是Load file data命令,则返回LOCAL_INFILE_Request。
(第二阶段)如果有结果集返回,则先返回列集合,所有列返回完了之后,会返回EOFPacket;如果过程中出现错误,则返回错误包。
(第三阶段)之后返回行记录,返回全部行记录之后,返回EOFPacket。如果有错误,回错误包。
MyCat实现源代码如下:
protected void handleData(byte[] data) { switch (resultStatus) { //第一阶段
case RESULT_STATUS_INIT:
switch (data[4]) { //返回OKPacket
case OkPacket.FIELD_COUNT:
handleOkPacket(data); break; //返回错误包
case ErrorPacket.FIELD_COUNT:
handleErrorPacket(data); break; //返回Load Data进一步操作
case RequestFilePacket.FIELD_COUNT:
handleRequestPacket(data); break; //返回结果集列数量 default:
//记录列数量并进入第二阶段
resultStatus = RESULT_STATUS_HEADER;
header = data;
fields = new ArrayList<byte[]>((int) ByteUtil.readLength(data, 4));
} break; //第二阶段
case RESULT_STATUS_HEADER:
switch (data[4]) { //返回错误包
case ErrorPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleErrorPacket(data); break; //返回EOF,证明列集合返回完毕,进入第三阶段
case EOFPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_FIELD_EOF;
handleFieldEofPacket(data); break; //返回的是列集合,记录 default:
fields.add(data);
} break; //第三阶段
case RESULT_STATUS_FIELD_EOF:
switch (data[4]) { //返回错误包
case ErrorPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleErrorPacket(data); break; //返回EOF,证明结果集返回完毕,回到第一阶段等待下一个请求的响应
case EOFPacket.FIELD_COUNT:
resultStatus = RESULT_STATUS_INIT;
handleRowEofPacket(data); break; //返回结果集包 default:
handleRowPacket(data);
} break; default:
throw new RuntimeException("unknown status!");
}
}
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 IOS渠道追踪方式
数据库路由中间件MyCat - 源代码篇(8)的更多相关文章
- 数据库路由中间件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 ...
随机推荐
- d3 - bar chart
用 D3.js 做一个简单的柱形图. 做柱形图有很多种方法,比如用 HTML 的 div 标签,或用 svg . 推荐用 SVG 来做各种图形.SVG 意为可缩放矢量图形(Scalable Vecto ...
- Ceph集群rbd-mirror A、B区域备份实施方案
Ceph集群rbd-mirror A.B区域备份实施方案 备注:首先准备两个集群, 并确认其状态,集群的准备过程在这就不做陈述 1.查看集群状态 A区域 [root@ceph2111 ceph]# c ...
- maven-appfuse配备步骤
版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/qiaqia609/article/details/36231851 maven-appfuse配置步 ...
- Java for LeetCode 089 Gray Code
The gray code is a binary numeral system where two successive values differ in only one bit. Given a ...
- 剑指Offer:删除链表的节点【18】
剑指Offer:删除链表的节点[18] 题目描述 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3-& ...
- Hadoop- Hadoop详解
首先所有知识以官网为准,所有的内容在官网上都有展示,所有的变动与改进,新增内容都以官网为准.hadoop.apache.org Hadoop是一个开源的可拓展的分布式并行处理计算平台,利用服务器集群根 ...
- Linux三种网络-vmware三种网络模式
Host-Only 桥接 NAT VMware虚拟机三种联网方法及原理 一.Brigde——桥接:默认使用VMnet0 1.原理: Bridge 桥"就是一个主机,这个机器拥有两块网卡,分别 ...
- 微信video和audio无法自动播放解决方案
//音频,写法一<audio src="music/bg.mp3" autoplay loop controls>你的浏览器还不支持哦</audio> // ...
- 分享知识-快乐自己:SpringBoot 使用注解API的方式定义启动端口号
在Spring Boot2.0以上配置嵌入式Servlet容器时EmbeddedServletContainerCustomizer类不存在,经网络查询发现被WebServerFactoryCusto ...
- IDEAL葵花宝典:java代码开发规范插件 checkstyle、visualVM、PMD 插件
前言: visualVM: 运行java程序的时候启动visualvm,方便查看jvm的情况 比如堆内存大小的分配:某个对象占用了多大的内存,jvm调优必备工具. checkstyle: CheckS ...