数据库路由中间件MyCat - 源代码篇(3)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
2. 前端连接建立与认证
Title:MySql连接建立以及认证过程client->MySql:1.TCP连接请求
MySql->client:2.接受TCP连接client->MySql:3.TCP连接建立MySql->client:4.握手包HandshakePacketclient->MySql:5.认证包AuthPacketMySql->client:6.如果验证成功,则返回OkPacketclient->MySql:7.默认会发送查询版本信息的包MySql->client:8.返回结果包
2.3 (5~6)认证包AuthPacket,如果验证成功,则返回OkPacket
继续执行FrontendConnection的register()方法:
// 异步读取并处理,这个与RW线程中的asynRead()相同,之后客户端收到握手包返回AuthPacket就是从这里开始看。
this.asynRead();
FrontendConnection.asynRead()方法直接调用this.socketWR.asynRead();如之前所述,一个Connection对应一个socketWR。在这里是一个FrontendConnection对应一个NIOSocketWR。NIOSocketWR是操作类,里面的方法实现异步读写。 NIOSocketWR.asynRead():
public void asynRead() throws IOException {
ByteBuffer theBuffer = con.readBuffer; if (theBuffer == null) {
theBuffer = con.processor.getBufferPool().allocate();
con.readBuffer = theBuffer;
} //从channel中读取数据,并且保存到对应AbstractConnection的readBuffer中,readBuffer处于write mode,返回读取了多少字节
int got = channel.read(theBuffer); //调用处理读取到的数据的方法
con.onReadData(got);
}
读取完数据到缓存readBuffer后,调用处理readBuffer方法: AbstractConnection.onReadData(int got):
public void onReadData(int got) throws IOException { //如果连接已经关闭,则不处理
if (isClosed.get()) { return;
}
ByteBuffer buffer = this.readBuffer;
lastReadTime = TimeUtil.currentTimeMillis(); //读取到的字节小于0,表示流关闭,如果等于0,代表TCP连接关闭了
if (got < 0) { this.close("stream closed");
return;
} else if (got == 0) {
if (!this.channel.isOpen()) {
this.close("socket closed");
return;
}
}
netInBytes += got;
processor.addNetInBytes(got); // 循环处理字节信息
//readBuffer一直处于write mode,position记录最后的写入位置
int offset = readBufferOffset, length = 0, position = buffer.position();
for (; ; ) { //获取包头的包长度信息
length = getPacketLength(buffer, offset);
if (length == -1) {
if (!buffer.hasRemaining()) {
buffer = checkReadBuffer(buffer, offset, position);
} break;
} //如果postion小于包起始位置加上包长度,证明readBuffer不够大,需要扩容
if (position >= offset + length) {
buffer.position(offset);
byte[] data = new byte[length]; //读取一个完整的包
buffer.get(data, 0, length); //处理包,每种AbstractConnection的处理函数不同
handle(data); //记录下读取到哪里了
offset += length; //如果最后写入位置等于最后读取位置,则证明所有的处理完了,可以清空缓存和offset
//否则,记录下最新的offset
//由于readBufferOffset只会单线程(绑定的RW线程)修改,但是会有多个线程访问(定时线程池的清理任务),所以设为volatile,不用CAS
if (position == offset) {
if (readBufferOffset != 0) {
readBufferOffset = 0;
}
buffer.clear();
break;
} else {
readBufferOffset = offset;
buffer.position(position);
continue;
}
} else {
if (!buffer.hasRemaining()) {
buffer = checkReadBuffer(buffer, offset, position);
} break;
}
}
}private ByteBuffer checkReadBuffer(ByteBuffer buffer, int offset,
int position) {
if (offset == 0) {
if (buffer.capacity() >= maxPacketSize) {
throw new IllegalArgumentException(
"Packet size over the limit.");
} int size = buffer.capacity() << 1;
size = (size > maxPacketSize) ? maxPacketSize : size;
ByteBuffer newBuffer = processor.getBufferPool().allocate(size);
buffer.position(offset);
newBuffer.put(buffer);
readBuffer = newBuffer;
recycle(buffer);
return newBuffer;
} else {
buffer.position(offset);
buffer.compact();
readBufferOffset = 0;
return buffer;
}
}
可以看出,处理缓存需要考虑到容量,扩容和位置记录等方面。 这里,readBuffer一直处于写模式。MyCat通过position(),还有get(data, 0, length)来读取数据。readBufferOffset用来记录每次读取的offset,需要设置为volatile。由于readBufferOffset只会单线程(绑定的RW线程)修改,但是会有多个线程访问(定时线程池的清理空闲连接的任务),所以设为volatile,不用CAS。这是一个经典的用volatile代替CAS实现多线程安全访问的场景。 MyCat的缓存管理思路很好,之后我会仔细讲。 读取完整包之后,交给handler处理。每种AbstractConnection的handler不同,FrontendConnection的handler为FrontendAuthenticator
this.handler = new FrontendAuthenticator(this);
我们思考下,FrontendConnection会接收什么请求呢?有两种,认证请求和SQL命令请求。只有认证成功后,才会接受SQL命令请求。FrontendAuthenticator只负责认证请求,在认证成功后,将对应AbstractConnection的handler设为处理SQL请求的FrontendCommandHandler即可. 一切正常的话,这里读取到的应为客户端发出的AuthPacket: AuthPacket:
packet length(3 bytes)
packet number (1)
client flags (4)
max packet (4)
charset(1)
username (null terminated string)
password (length code Binary)
database (null terminated string)
public void handle(byte[] data) { // check quit packet
if (data.length == QuitPacket.QUIT.length && data[4] == MySQLPacket.COM_QUIT) {
source.close("quit packet");
return;
}
AuthPacket auth = new AuthPacket();
auth.read(data); // check user
if (!checkUser(auth.user, source.getHost())) {
failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "' with host '" + source.getHost()+ "'");
return;
} // check password
if (!checkPassword(auth.password, auth.user)) {
failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "', because password is error "); return;
} // check degrade
if ( isDegrade( auth.user ) ) {
failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "', because service be degraded "); return;
} // check schema
switch (checkSchema(auth.database, auth.user)) {
case ErrorCode.ER_BAD_DB_ERROR:
failure(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + auth.database + "'");
break; case ErrorCode.ER_DBACCESS_DENIED_ERROR:
String s = "Access denied for user '" + auth.user + "' to database '" + auth.database + "'";
failure(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);
break;
default: //认证成功,设置好用户数据库和权限等,将handler设置为FrontendCommandHandler
success(auth);
}
}
认证成功后,会发送OkPacket,对应FrontendConnection的handler变成了FrontendCommandHandler,可以接受SQL请求了。 发送OkPacket的过程与HandshakePacket相同,这里不再赘述。
source.write(source.writeToBuffer(AUTH_OK, buffer));
OkPacket结构:
packet length(3 bytes)
packet number (1)
0x00 (1,包体首字节为0)
affect rows (length code Binary)
insert id (length code Binary)
server status (2)
warning status (2)
message (length code Binary)
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 线上日志集中化可视化管理:ELK
【推荐】 Spring Boot 学习系列(02)—使用热部署,提升开发效
数据库路由中间件MyCat - 源代码篇(3)的更多相关文章
- 数据库路由中间件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 ...
随机推荐
- 修正IE6中FIXED不能用的办法,转载
.fixed-top /* 头部固定 */{position:fixed;bottom:auto;top:0px;} .fixed-bottom /* 底部固定 */{position:fixed;b ...
- 移动App该怎样保存用户password
版权声明:本文为横云断岭原创文章,未经博主同意不得转载.微信公众号:横云断岭的专栏 https://blog.csdn.net/hengyunabc/article/details/34623957 ...
- linux批量更改权限
用命令 sudo chmod 777 -Rfv /home/name/* 注释:1.777 为 要修改成 的 文件的 权限:2.-R 是 子目录 下的 文件 也修改:3.-f 强制:4. -v是 显示 ...
- 关于android R.java文件无法创建的问题
R.java文件无法创建的原因网上有很多说法普遍是以下两种: 1. xml文件有错误: 解决方法就是找到哪个xml有错然后把错误修复就OK了. 2.编码问题 这时候只要把xml文件的编码改成utf8就 ...
- HDU 1201 Fibonacci Again
Fibonacci Again Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...
- Hadoop- Hadoop环境搭建
Windows下Hadoop的安装 准备工具:64位的JDK,Hadoop安装包(我使用的是2.6.1) JDK下载地址 官网: http://www.oracle.com/technetwork/j ...
- 分享知识-快乐自己:SpringMVC 结合使用拦截器(判断是否用户是否已登陆)
基础拦截器操作: 拦截器是一种AOP操作实现,那么在AOP之中用户一定不需要去关注拦截器的存在,用户只需要按照自己已经习惯的处理方式进行代码的编写即可. 首先我们先创建一个自定义的拦截器: packa ...
- hdu 1002 A + B Problem II(大数)
题意:就是求a+b (a,b都不超过1000位) 思路:用数组存储 第一道大数的题目,虽然很水,纪念一下! 代码: #include<cstdio> #include<cstring ...
- Lucene默认的打分算法——ES默认
改变Lucene的打分模型 随着Apache Lucene 4.0版本在2012年的发布,这款伟大的全文检索工具包终于允许用户修改默认的基于TF/IDF原理的打分算法.Lucene API变得更加容易 ...
- Python: scikit-image 彩色图像滤波
一般的滤波器都是针对灰度图像的,scikit-image 库提供了针对彩色图像滤波的decorator:adapt_rgb,adapt_rgb 提供两种形式的滤波,一种是对rgb三个通道分别进行处理, ...