前言
游戏服务器一个配置存储服务,使用 mysql-connector-c++ 8.0
连接 MySQL 8.0
数据库,使用 MySQL X Protocol
, 它是 MySQL 8.0 引入的一种新的通信协议,它是为了支持现代应用程序开发需求而设计的。除了关系型数据存储外,MySQL X Protocol 可以方便地处理文档存储,允许存储和检索 JSON 等文档类型的数据,使其更适合现代的 NoSQL 风格的开发模式。该协议支持异步操作,提高了性能,允许更高效的并发处理,特别是在处理大量请求时。支持更丰富的数据操作,包括 CRUD 操作、事务处理、SQL 语句执行,并且可以处理复杂的查询,例如对文档存储的查询。
mysqlx
是 MySQL 提供的用于与 MySQL 服务器进行交互的 C++ 客户端库,基于 MySQL X Protocol 开发。它提供了一组 C++ API,使开发人员可以在 C++ 程序中使用 MySQL 服务器的功能。而我就是用这套API是遇到了问题,本来这套功能在本地测试时很顺畅,没有发现问题,但是放到线上环境时就发现无法存储数据,调试线上环境还是有一些难度的。
因为线上程序编译为release版本,几乎没有可以调试的符号,起初还怀疑是release版本有问题,但是本地编译成release版本依旧正常,所以我就迅速用一个debug版本的so库替换了线上的release版本库,结果废了半天劲就找到了 CDK Error: Connection refused (generic:111)
这个错误,对于这个问题的处理还查了很多解决办法,但回过头来看,这个提示已经很明显了,就是不让连接呗,应该第一时间想到连接权限问题的,但是这有点马后炮,关于这个问题还是走了不少弯路,不过也学到了很多,总结一下做个备忘录。
问题现场
查找object时的示例代码:
1 | mysqlx::Session sess = sqlClient->getSession(); |
添加新object时的实例代码:
1 | mysqlx::Session sess = sqlClient->getSession(); |
报错的函数主要集中在上面两段逻辑,实际上通过debug库断点,已经能定位到 mysqlx::Session sess = sqlClient->getSession();
这一句就开始报异常了,异常类型 catch (const mysqlx::Error& err) { ... }
,打印信息也就是标题中看到的 CDK Error: Connection refused (generic:111)。
这里的Collection相当于关系数据库中的一个表,实际上可以通过关系数据库导出的方式,将Collection导出或者导出,仅导出表结构的命令
1 | mysqldump -u username -p --no-data source_database table_name > table_structure.sql |
导出的 table_structure.sql 内容示例
1 | -- MySQL dump 10.13 Distrib 8.0.24, for Linux (x86_64) |
简单的导入命令
1 | mysql -u username -p target_database < table_name.sql |
解决问题
有经验之后可以从提示 CDK Error: Connection refused (generic:111)
推断出是连接权限问题,但要准确找到问题点还需要逐步排查
- MySQL绑定的IP地址是多少?允许哪些主机访问
- MySQL监听的端口是多少?允许通过哪些端口访问
- MySQL中定义了哪些用户?哪些用户可以访问
- MySQL中定义的用户绑定了哪些主机?已存在的用户可以通过哪些主机访问
- MySQL中绑定了主机的用户有哪些权限?可以访问的用户是否有指定的操作权限,比如增加、删除等
这么一看还真是挺麻烦的,不过不用担心,当处理的次数多了自然而然就理解了,先看前两个问题,MySQL绑定的IP和端口吧
MySQL绑定IP和端口
他们配置在文件 /etc/mysql/mysql.conf.d/mysqld.cnf
中,这里面文件名很相似,我的环境是在Ubuntu20.04,版本 目录结构如下:
1 | # tree /etc/mysql |
部分文件内容
1 | [mysqld] |
无论是传统的mysql协议还是新的mysqlx协议,都默认允许本机回环地址 127.0.0.1
连接,mysql协议默认端口3306,mysqlx默认端口33060,如果想修改在文件 mysqld.cnf
修改后重启MySQL服务 sudo systemctl restart mysql
既可以了,可以查询MySQL变量看看目前生效的配置值。
1 | mysql> SHOW VARIABLES LIKE '%bind%'; |
对于 MySQL 服务绑定地址 0.0.0.0
、192.168.1.100
、127.0.0.1
和 localhost
有一些差异,其中192.168.1.100为本机内网地址,对比结果整理如下:
对比维度 | 0.0.0.0 | 192.168.1.100 | 127.0.0.1 | localhost |
---|---|---|---|---|
含义 | 监听所有网络接口(本机、内网、外网)。 | 监听特定的本机网络接口,仅允许特定 IP 连接。 | 仅监听本机回环接口,仅限本机访问。 | 通常解析为 Unix Socket 或 127.0.0.1 。 |
访问范围 | 所有来源(包括本地和远程)。 | 仅允许通过指定的网络接口访问(如内网 IP)。 | 仅限本机访问,无法从外部访问。 | 仅限本机访问,无法从外部访问。 |
使用协议 | 强制使用 TCP/IP 协议。 | 强制使用 TCP/IP 协议。 | 强制使用 TCP/IP 协议。 | 优先使用 Unix Socket,无法使用时退回到 TCP/IP 协议。 |
性能 | 可能因监听所有接口稍有性能影响。 | 仅监听单个接口,性能略高于 0.0.0.0 。 |
受限于网络栈处理效率。 | 使用 Unix Socket 时性能最高;TCP/IP 时与 127.0.0.1 相同。 |
安全性 | 最不安全,需防火墙限制来源以防外网暴露。 | 较为安全,仅允许指定接口连接,适合内网使用。 | 高安全性,仅限本机访问,不暴露给外部网络。 | 高安全性,仅限本机访问,Unix Socket 更加安全。 |
适用场景 | 内网/外网连接,需严格配置防火墙规则。 | 局域网访问,仅需特定网络接口的连接。 | 单机应用或本地调试环境,仅限本机访问。 | 本地高频操作,优先使用更高效的 Unix Socket。 |
配置方法 | bind-address = 0.0.0.0 |
bind-address = 192.168.1.100 |
bind-address = 127.0.0.1 |
bind-address = 127.0.0.1 (间接实现)。 |
访问示例 | 所有主机(如 mysql -h <任意IP> )。 |
指定内网 IP(如 mysql -h 192.168.1.100 )。 |
本机访问(如 mysql -h 127.0.0.1 )。 |
本机访问(如 mysql -h localhost )。 |
MySQL用户和绑定的主机
在MySQL中不仅定义了用户和对应权限,还规定了用户绑定的主机,也就是说权限不仅仅分配给一个用户的,而是分配给通过指定机器访问的用户的,单单这么说对于没有操作的人来说可能有些懵,我们可以实际操作一下:
通过 SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user;
可以查询用户
1 | mysql> SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user; |
用户名加上后面的主机名才是权限授予的主体,可以看到comm这个用户名可以通过localhost本机访问,也可以通过 192.168.1.100 这台机器访问,如果还需要通过 192.168.1.200,那么还需要创建一个 'comm'@'192.168.1.200'
的用户并授予权限才可以。
如果想要一个用户可以在任何主机上登录访问MySQL,主机部分用 %
代替,就比如 'webview'@'%'
这个用户,不过不建议这样做,安全性明显降低。
MySQL授予和回收权限
查询权限可以通过 show grants for 用户
查询,测试如下:
1 | mysql> show grants for 'comm'@'localhost'; |
创建用户和授予权限的方法
1 | CREATE USER 'comm'@'192.168.1.200' IDENTIFIED BY 'mypassword'; |
这样就创建了一个 'comm'@'192.168.1.200'
用户,并授予用户 comm 在 192.168.1.200 主机上对所有数据库和表的所有权限,如果允许用户将自己拥有的权限授予其他用户,可以加上 WITH GRANT OPTION
语句,完整命令如下:
1 | GRANT ALL PRIVILEGES ON *.* TO 'comm'@'192.168.1.200' WITH GRANT OPTION; |
回收权限的的命令如下:
1 | REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'comm'@'192.168.1.200'; |
总结
CDK Error: Connection refused (generic:111)
表示目标数据库拒绝连接,查询MySQL配置和权限分配是否正确- 导出MySQL指定表结构
mysqldump -u username -p --no-data source_database table_name > table_structure.sql
- 查询MySQL用户
SELECT DISTINCT CONCAT('User: ''',user,'''@''',host,''';') AS query FROM mysql.user;
- 查询指定用户的权限
show grants for 'root'@'localhost';
程序是很单纯很单纯的,哪里能有什么坏心思,如果不能按照预期的情况表现,99.99%的情况下是使用方法不对,程序单纯到只会按照指令形式,如果表现不好,一定是指令不好~
2024-12-20 19:50:50