SMProxy 分析 (基于 Swoole 开发的 MySQL 数据库连接池)
来源:SegmentFault
2023-01-14 20:14:28
0浏览
收藏
你在学习数据库相关的知识吗?本文《SMProxy 分析 (基于 Swoole 开发的 MySQL 数据库连接池)》,主要介绍的内容就涉及到MySQL、PHP、swoole,如果你想提升自己的开发能力,就不要错过这篇文章,大家要知道编程理论基础和实战操作都是不可或缺的哦!
前言:
- 在深入了解SMProxy之前,一直认为连接池是对mysql连接对象进行统一管理的处理,但是随之而来的问题是现有的php框架都没有自带mysql连接池,如何以最小的代价替代框架的数据库模块一直是一个难题。
在深入了解SMProxy之后,发现SMProxy的奇妙之处就在于你并不需要对框架的数据库模块进行任何的修改,即可使用SMProxy架构,它是基于mysql客户端与mysql服务端的中间件,通过swoole/server自己模拟与mysql报文交互并内部管理连接池对象来提升效率。
优点:
- 明显客观的性能提升,减少创建连接的资源消耗
- 无需对框架进行任何的修改即可使用
缺点:
- 需要对mysql协议有一定的了解,如果希望改动,则需要对协议有更深入的理解。
- 如果发生错误,增加了排查错误的成本。
知识点的补充:
- 协议中常用的数据, 10进制,16进制,2进制。
- 为啥使用16进制表示字节中的内容, 在二进制中每4个位表示16进制中的一位, 二进制与16进制相互转化比十进制快的多 。
swoole:
运用到的知识点 swoole/server以及swoole/client, 不做更多的介绍
tcp 粘包问题: https://www.cnblogs.com/JsonM...
client -> tcp buffer(等待cpu指令, 如果buffer缓存达到上限,就会直接发送到server, 所有有可能一次性接受多个数据) -> server
mysql 协议分析
https://www.cnblogs.com/davyg...
SMProxy:
执行流程图

接下来将针对mysql与SMProxy的swoole服务交互进行一定的说明:(如果对以下报文有疑问,可以仔细翻看mysql协议https://www.cnblogs.com/davyg...
再进行tcp交互之中,需要服务端向mysql客户端发送握手初始化报文,只要发送符合mysql协议的握手报文,mysql客户端便会进行下一步发送认证请求的操作。
// 位于SMProxy/src/Handler/Frontend/FrontendAuthticator public function getHandshakePacket(int $server_id) { $rand1 = RandomUtil::randomBytes(8); $rand2 = RandomUtil::randomBytes(12); $this->seed = array_merge($rand1, $rand2); $hs = new HandshakePacket(); $hs->packetId = 0; // 以下根据握手报文 // 协议版本号 $hs->protocolVersion = Versions::PROTOCOL_VERSION; // 服务器版本号信息 $hs->serverVersion = Versions::SERVER_VERSION; // 服务器线程 $hs->threadId = $server_id; // 随机数 $hs->seed = $rand1; // 填充值,服务器权能标识, $hs->serverCapabilities = $this->getServerCapabilities(); // 字符编码 $hs->serverCharsetIndex = (CharsetUtil::getIndex(CONFIG['server']['charset'] ?? 'utf8mb4') & 0xff); // 服务器状态 $hs->serverStatus = 2; // 服务器权能标识+填充值 $hs->restOfScrambleBuff = $rand2; return getString($hs->write()); } //位于 SMProxy/src/HandshakePacket public function write() { // default init 256,so it can avoid buff extract $buffer = []; // 写入消息头长度 BufferUtil::writeUB3($buffer, $this->calcPacketSize()); // 写入序号 -- 消息头的 $buffer[] = $this->packetId; // 写入协议版本号 $buffer[] = $this->protocolVersion; // 写入服务器版本信息 BufferUtil::writeWithNull($buffer, getBytes($this->serverVersion)); // 写入服务器线程ID BufferUtil::writeUB4($buffer, $this->threadId); // 挑战随机数 9个字节 包含一个填充值 BufferUtil::writeWithNull($buffer, $this->seed); // 服务器权能标识 BufferUtil::writeUB2($buffer, $this->serverCapabilities); // 1字节 字符编码 $buffer[] = $this->serverCharsetIndex; // 服务器状态 BufferUtil::writeUB2($buffer, $this->serverStatus); if ($this ->serverCapabilities & Capabilities::CLIENT_PLUGIN_AUTH) { // 服务器权能标志 16位 BufferUtil::writeUB2($buffer, $this->serverCapabilities); // 挑战长度+填充值+挑战随机数 $buffer[] = max(13, count($this->seed) + count($this->restOfScrambleBuff) + 1); $buffer = array_merge($buffer, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); } else { // 10字节填充数 $buffer = array_merge($buffer, self::$FILLER_13); } // +12字节挑战随机数 if ($this ->serverCapabilities & Capabilities::CLIENT_SECURE_CONNECTION) { BufferUtil::writeWithNull($buffer, $this->restOfScrambleBuff); } if ($this ->serverCapabilities & Capabilities::CLIENT_PLUGIN_AUTH) { BufferUtil::writeWithNull($buffer, getBytes($this->pluginName)); } return $buffer; }
当mysql客户端发送登录认证报文后,这时进行登录账号密码校验的是swoole/server而不是mysql服务端,所以在配置文件server.json中的root跟password正是mysql客户端请求的账号与密码,而swoole/server与mysql服务端交互锁需要的账号密码位于database的配置中。
// 位于SMProxy/src/SMProxyServer private function auth(BinaryPacket $bin, \swoole_server $server, int $fd) { // 如果数据长度是20, -- 可能自定义的, 4-20是密码, 最后4位不知道干啥 if ($bin->data[0] == 20) { // 密码长度是16 , 判断账号密码 $checkAccount = $this->checkAccount($server, $fd, $this->source[$fd]->user, array_copy($bin->data, 4, 20)); if (!$checkAccount) { // 发送ERROR报文 $this ->accessDenied($server, $fd, 4); } else { if ($server->exist($fd)) { // 发送OK报文 $server->send($fd, getString(OkPacket::$SWITCH_AUTH_OK)); } // 认证标志设置为true $this->source[$fd]->auth = true; } } elseif ($bin->data[4] == 14) { // 序号等于14 if ($server->exist($fd)) { // 无需认证即登录 $server->send($fd, getString(OkPacket::$OK)); } } else { $authPacket = new AuthPacket(); // 读取报文信息 登录认证报文 $authPacket->read($bin); // 判断账号密码 $checkAccount = $this->checkAccount($server, $fd, $authPacket->user ?? '', $authPacket->password ?? []); if (!$checkAccount) { // 密码校验失败 if ($authPacket->pluginName == 'mysql_native_password') { // 发送ERROR报文 $this ->accessDenied($server, $fd, 2); } else { // 记录用户数据 $this->source[$fd]->user = $authPacket ->user; $this->source[$fd]->database = $authPacket->database; // 填充数 $this->source[$fd]->seed = RandomUtil::randomBytes(20); // 发送EOF报文 $authSwitchRequest = array_merge( [254], getBytes('mysql_native_password'), [0], $this->source[$fd]->seed, [0] ); if ($server->exist($fd)) { $server->send($fd, getString(array_merge(getMysqlPackSize(count($authSwitchRequest)), [2], $authSwitchRequest))); } } } else { // 账号正确 发送OK报文, 并记录数据 if ($server->exist($fd)) { $server->send($fd, getString(OkPacket::$AUTH_OK)); } $this->source[$fd]->auth = true; $this->source[$fd]->database = $authPacket->database; } } }
当进行tcp握手以及登录认证成功之后,mysql端便可以传输执行语句等操作,而这里SMProxy还对语句进行一定的分析,来判断读,写还是事物等。如果是读语句,并配置了读数据库,那么读语句只会从读池里获取链接,如果读数据库没有配置才会去获取写数据库,所以这里使用的时候需要注意,如果公司的读数据库的配置低于写数据库,可能使用该架构会对读数据库造成一定的压力。
到了这一步,SMProxy的工作也大概做完了,swoole/server会把mysql客户端传送上来的执行命令文本,发送给mysql服务端,mysql服务端返回的数据SMProxy也不再做过多的处理,而又因为客户端是mysql客户端,可以直接解析mysql服务端返回的报文。
SMProxy架构的基本流程描述完毕了,而连接池以及mysql报文等更详细的处理,我也备注在代码里,并上传到github中,有想更深入了解的朋友可以下载下来并查看,注释并没有非常完善, 但是大部分的语句我都添加了自己的见解(也有存在解读错误的情况)
https://github.com/linjinmin/...
总而言之,SMProxy是一个非常优秀的框架。
SMProxy github地址:
https://github.com/louislivi/...
参考文章来源:
https://www.cnblogs.com/JsonM... // tcp粘包问题
https://www.cnblogs.com/davyg... // mysql协议
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于数据库的相关知识,也可关注golang学习网公众号。
版本声明
本文转载于:SegmentFault 如有侵犯,请联系study_golang@163.com删除

- 上一篇
- mysql的replace,存在更新,不存在插入
![[经验栈]SQL语句逻辑运算符"AND"、"&&"兼容性](/uploads/196/db189f289b35dddc87ae874d3f07d9f0.png)
- 下一篇
- [经验栈]SQL语句逻辑运算符"AND"、"&&"兼容性
评论列表
-
- 贤惠的帽子
- 这篇文章出现的刚刚好,太细致了,很好,码起来,关注up主了!希望up主能多写数据库相关的文章。
- 2023-01-20 04:33:15
查看更多
最新文章
-
- 数据库 · MySQL | 18小时前 | 索引 数据类型 字符集 存储引擎 CREATETABLE
- MySQL新建表操作指南与建表技巧
- 462浏览 收藏
-
- 数据库 · MySQL | 1个月前 | 条件判断
- CASEWHEN条件判断的嵌套使用详解与实战场景分析
- 469浏览 收藏
-
- 数据库 · MySQL | 1个月前 | java php
- CSV文件批量导入MySQL的性能优化秘籍大揭秘
- 289浏览 收藏
-
- 数据库 · MySQL | 1个月前 |
- GaleraCluster多主集群配置与冲突解决攻略
- 239浏览 收藏
-
- 数据库 · MySQL | 1个月前 | 窗口函数实战
- MySQL窗口函数实战案例深度剖析
- 315浏览 收藏
-
- 数据库 · MySQL | 1个月前 | 自定义函数
- MySQL插件开发入门:自定义函数(UDF)编写指南
- 184浏览 收藏
-
- 数据库 · MySQL | 1个月前 |
- Windows系统MySQL8.0免安装版配置攻略
- 227浏览 收藏
-
- 数据库 · MySQL | 1个月前 | MySQL错误 数据库诊断
- 深度解析错误代码1045/1217/1205的根本原因及解决方案
- 202浏览 收藏
-
- 数据库 · MySQL | 1个月前 | sql注入 编码规范
- 防范SQL注入必备:编码规范与工具推荐指南
- 140浏览 收藏
查看更多
课程推荐
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 484次学习
查看更多
AI推荐
-
- 笔灵AI生成答辩PPT
- 探索笔灵AI生成答辩PPT的强大功能,快速制作高质量答辩PPT。精准内容提取、多样模板匹配、数据可视化、配套自述稿生成,让您的学术和职场展示更加专业与高效。
- 16次使用
-
- 知网AIGC检测服务系统
- 知网AIGC检测服务系统,专注于检测学术文本中的疑似AI生成内容。依托知网海量高质量文献资源,结合先进的“知识增强AIGC检测技术”,系统能够从语言模式和语义逻辑两方面精准识别AI生成内容,适用于学术研究、教育和企业领域,确保文本的真实性和原创性。
- 24次使用
-
- AIGC检测-Aibiye
- AIbiye官网推出的AIGC检测服务,专注于检测ChatGPT、Gemini、Claude等AIGC工具生成的文本,帮助用户确保论文的原创性和学术规范。支持txt和doc(x)格式,检测范围为论文正文,提供高准确性和便捷的用户体验。
- 30次使用
-
- 易笔AI论文
- 易笔AI论文平台提供自动写作、格式校对、查重检测等功能,支持多种学术领域的论文生成。价格优惠,界面友好,操作简便,适用于学术研究者、学生及论文辅导机构。
- 42次使用
-
- 笔启AI论文写作平台
- 笔启AI论文写作平台提供多类型论文生成服务,支持多语言写作,满足学术研究者、学生和职场人士的需求。平台采用AI 4.0版本,确保论文质量和原创性,并提供查重保障和隐私保护。
- 35次使用
查看更多
相关文章
-
- golang MySQL实现对数据库表存储获取操作示例
- 2022-12-22 499浏览
-
- 搞一个自娱自乐的博客(二) 架构搭建
- 2023-02-16 244浏览
-
- B-Tree、B+Tree以及B-link Tree
- 2023-01-19 235浏览
-
- mysql面试题
- 2023-01-17 157浏览
-
- MySQL数据表简单查询
- 2023-01-10 101浏览