数据库路由中间件MyCat - 源代码篇(7)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
3. 连接模块
3.4 FrontendConnection前端连接
构造方法:
public FrontendConnection(NetworkChannel channel) throws IOException { super(channel);
InetSocketAddress localAddr = (InetSocketAddress) channel.getLocalAddress();
InetSocketAddress remoteAddr = null; if (channel instanceof SocketChannel) {
remoteAddr = (InetSocketAddress) ((SocketChannel) channel).getRemoteAddress();
} else if (channel instanceof AsynchronousSocketChannel) {
remoteAddr = (InetSocketAddress) ((AsynchronousSocketChannel) channel).getRemoteAddress();
} this.host = remoteAddr.getHostString(); this.port = localAddr.getPort(); this.localPort = remoteAddr.getPort(); this.handler = new FrontendAuthenticator(this);
}
FrontendConnection是对前端连接channel的封装,接受NetworkChannel作为参数构造。前端连接建立,需要先验证其权限,所以,handler首先设置为FrontendAuthenticator 等到验证成功,handler会被设置成FrontendCommandHandler。 下面来看和FrontendConnection相关的Handler: FrontendCommandHandler会先解析请求类型,之后调用不同的方法处理不同类型的请求。例如,FrontendQueryHandler会解析query类型的sql请求语句:
@Override
public void handle(byte[] data)
{ if(source.getLoadDataInfileHandler()!=null&&source.getLoadDataInfileHandler().isStartLoadData())
{
MySQLMessage mm = new MySQLMessage(data); int packetLength = mm.readUB3(); if(packetLength+4==data.length)
{
source.loadDataInfileData(data);
} return;
} switch (data[4])
{ case MySQLPacket.COM_INIT_DB:
commands.doInitDB();
source.initDB(data); break; case MySQLPacket.COM_QUERY:
commands.doQuery();
source.query(data); break; case MySQLPacket.COM_PING:
commands.doPing();
source.ping(); break; case MySQLPacket.COM_QUIT:
commands.doQuit();
source.close("quit cmd"); break; case MySQLPacket.COM_PROCESS_KILL:
commands.doKill();
source.kill(data); break; case MySQLPacket.COM_STMT_PREPARE:
commands.doStmtPrepare();
source.stmtPrepare(data); break; case MySQLPacket.COM_STMT_EXECUTE:
commands.doStmtExecute();
source.stmtExecute(data); break; case MySQLPacket.COM_STMT_CLOSE:
commands.doStmtClose();
source.stmtClose(data); break; case MySQLPacket.COM_HEARTBEAT:
commands.doHeartbeat();
source.heartbeat(data); break; default:
commands.doOther();
source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unknown command"); }
}
FrontendCommandHandler会调用FrontendConnection合适的方法解析处理不同的请求,例如它的initDB(byte[] data)方法:
public void initDB(byte[] data) {
MySQLMessage mm = new MySQLMessage(data);
mm.position(5);
String db = mm.readString(); // 检查schema的有效性
if (db == null || !privileges.schemaExists(db)) {
writeErrMessage(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + db + "'"); return;
} if (!privileges.userExists(user, host)) {
writeErrMessage(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + user + "'"); return;
}
Set<String> schemas = privileges.getUserSchemas(user); if (schemas == null || schemas.size() == 0 || schemas.contains(db)) { this.schema = db;
write(writeToBuffer(OkPacket.OK, allocate()));
} else {
String s = "Access denied for user '" + user + "' to database '" + db + "'";
writeErrMessage(ErrorCode.ER_DBACCESS_DENIED_ERROR, s);
}
}
方法调用: 通过查看可以发现,在command packet被解析出是initDB类型的请求时(其实就是用户发送的查询语句为“use XXX”),会调用此方法进行处理,同时,这些方法都是被RW线程执行的。 此方法从FrontedPrivilege中验证用户是否有权限访问这个逻辑库,如果有就把当前连接的逻辑库设为用户请求的逻辑库。 其他方法与handler也是相似的关系,可以看出,FrontendConnection组合了多种封装的handler来处理不同的请求的不同阶段。至于各种handler,会在之后sql解析,sql路由,协议实现等模块详细介绍。
3.4.1 ServerConnection服务端连接
前端连接包括ServerConnection(服务端连接)和ManagerConnection(管理端连接)。前端链接不会直接创建,而是通过工厂创建: 工厂方法:
@Override
protected FrontendConnection getConnection(NetworkChannel channel) throws IOException {
SystemConfig sys = MycatServer.getInstance().getConfig().getSystem();
ServerConnection c = new ServerConnection(channel);
MycatServer.getInstance().getConfig().setSocketParams(c, true);
c.setPrivileges(MycatPrivileges.instance());
c.setQueryHandler(new ServerQueryHandler(c));
c.setLoadDataInfileHandler(new ServerLoadDataInfileHandler(c)); // c.setPrepareHandler(new ServerPrepareHandler(c));
c.setTxIsolation(sys.getTxIsolation());
c.setSession2(new NonBlockingSession(c)); return c;
}
可以看出,每个新的ServerConnection都会绑定一个新的ServerQueryHandler负责处理sql指令,一个ServerLoadDataInfileHandler负责处理文件载入命令,一个session负责处理事务 下面是相关的类图 这里的所有独立的handler里面都是static方法,可供其他类直接调用。每个ServerConnection都会有一个NonBlockingSession来处理。 这里说下连接、会话、逻辑库、MyCat实例的关系(与MySQL里面的连接、会话、数据库、MySQL实例的关系不太一样);首先每个MyCat实例都管理多个数据库。连接是针对MyCat实例建立的,并且,MyCat的连接(AbstractConnection)是不可复用的,在close方法会关闭连接并清理使用的资源。但是缓存资源(buffer)是可以复用的。比如,在一个前端连接长时间空闲时或者出现异常时,会被清理掉。每个连接会拥有一个session来处理事务,保存会话信息。 这里,每个连接拥有一个会话。每个连接中的方法,被RW线程执行,相当于与RW线程绑定。RW线程是可以复用的,这里相当于MySQL中的连接是可以复用的(连接池)。 Session.java:
public interface Session { /**
* 取得源端连接
*/
FrontendConnection getSource(); /**
* 取得当前目标端数量
*/
int getTargetCount(); /**
* 开启一个会话执行
*/
void execute(RouteResultset rrs, int type); /**
* 提交一个会话执行
*/
void commit(); /**
* 回滚一个会话执行
*/
void rollback(); /**
* 取消一个正在执行中的会话
*
* @param sponsor
* 如果发起者为null,则表示由自己发起。
*/
void cancel(FrontendConnection sponsor); /**
* 终止会话,必须在关闭源端连接后执行该方法。
*/
void terminate();
}
下面我们着重研究它的实现类NonBlockingSession: 首先,取得源端连接方法FrontendConnection getSource();,其实就是NonBlockingSession在创建时就已绑定一个连接,谁会调用这个方法取得源端链接呢? 可以发现,主要有各种查询的handler还有SQLengine会去调用。因为处理无论返回什么结果,都需要返回给源端。 int getTargetCount();取得当前目标端数量。根据目标端的数量不同会用不同的handler处理转发SQL和合并结果。
@Override
public void execute(RouteResultset rrs, int type) { // 清理之前处理用的资源
clearHandlesResources(); if (LOGGER.isDebugEnabled()) {
StringBuilder s = new StringBuilder();
LOGGER.debug(s.append(source).append(rrs).toString() + " rrs ");
} // 检查路由结果是否为空
RouteResultsetNode[] nodes = rrs.getNodes(); if (nodes == null || nodes.length == 0 || nodes[0].getName() == null
|| nodes[0].getName().equals("")) { //如果为空,则表名有误,提示客户端
source.writeErrMessage(ErrorCode.ER_NO_DB_ERROR, "No dataNode found ,please check tables defined in schema:"
+ source.getSchema()); return;
} //如果路由结果个数为1,则为单点查询或事务
if (nodes.length == 1) { //使用SingleNodeHandler处理单点查询或事务
singleNodeHandler = new SingleNodeHandler(rrs, this); try {
singleNodeHandler.execute();
} catch (Exception e) {
LOGGER.warn(new StringBuilder().append(source).append(rrs), e);
source.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());
}
} else { //如果路由结果>1,则为多点查询或事务
boolean autocommit = source.isAutocommit();
SystemConfig sysConfig = MycatServer.getInstance().getConfig()
.getSystem(); //mutiNodeLimitType没有用。。。
int mutiNodeLimitType = sysConfig.getMutiNodeLimitType(); //使用multiNodeHandler处理多点查询或事务
multiNodeHandler = new MultiNodeQueryHandler(type, rrs, autocommit, this); try {
multiNodeHandler.execute();
} catch (Exception e) {
LOGGER.warn(new StringBuilder().append(source).append(rrs), e);
source.writeErrMessage(ErrorCode.ERR_HANDLE_DATA, e.toString());
}
}
}
每次一个Session执行SQL时,会先清理handler使用的资源。SingleNodeHandler与multiNodeHandler之后会讲。这里的handler我们之后会在每个模块去讲,Session之后也还会提到,敬请期待
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 Android TV 开发(5)
【推荐】 分布式存储系统可靠性系列三:设计模式
数据库路由中间件MyCat - 源代码篇(7)的更多相关文章
- 数据库路由中间件MyCat - 源代码篇(1)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...
- 数据库路由中间件MyCat - 源代码篇(13)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...
- 数据库路由中间件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 ...
随机推荐
- for 没有作用域的说话
for i in range(10): passprint(i) 打印的结果就是9 打印的最后一次结果
- vuejs如何在服务器部署
title: vuejs如何在服务器部署 date: 2017-10-31 20:41:03 tags: [vue] --- 上传到网站服务器 Vue 是一个 javascript 的前端框架,它是运 ...
- 规模预算 之 FP法(作成中)
五大要素 「外部入力」「外部出力」「内部論理ファイル」 「外部インタフェースファイル」「外部照会」 优点 1) 開発初期段階での概算が可能 2) エンドユーザが認識可能な計測法である(ユーザ目線での機 ...
- 修改git以往历史中所有commit的name和email
当换了新的电脑设备或者在 homestead 中使用 git 的时候:如果忘了 git config 设置用户名和邮箱:这样当 git commit 的时候就会使用设备名作为 git 用户名:或者我们 ...
- vscode的环境变量code
vscode的安装路径 本质:vscode的安装路径/Applications/Visual Studio Code.app/Contents/Resources/app/bin 下面有code可执行 ...
- 关于http以及aphace配置https
我是通过腾讯云配置的ssl. 网站:www.xian029.cn 免费申请,然后通过phpstudy 来配置的. 密码学: 研究密码编码与解码的学科,可以分为编码学和破译学. HTTPS ...
- Java框架spring Boot学习笔记(一):开始第一个项目
新建一个项目,选择Spring initializr 修改group和项目名 添加依赖包Web,MongoDB 设置保存位置和工程名 新建一个test的文件 输入代码: package com.xxx ...
- DJango 基础 (5)
模板加载静态文件 在settings.py文件中添加STATICFILES_DIRS,设置静态文件目录路径,同templates. # settings.py文件中STATIC_URL = '/st ...
- 数据库-1055报错-把only_full_group_by去掉
[Err] 1055 - Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated c ...
- [剑指Offer]5-替换空格
链接 https://www.nowcoder.com/practice/9023a0c988684a53960365b889ceaf5e?tpId=13&tqId=11210&tPa ...