详见快速入门手册 对应章节
g_message/critical/debug/...,参数与printf完全一致,级别可以动态设置,set log-level, 日志文件位置$log_path/$instance.log
- 获取DBProxy进程号
ps -ef|grep proxy
- attach DBProxy进程
gdb -p [pid]
- 设置断点
break [断点]
- 程序继续运行
continue
-
断点触发
-
调试
- 启动
gdb [安装路径]/bin/mysql-proxy
- 设置参数
set args --defaults-file=[配置文件绝对路径]
-
设置断点
-
程序运行
run
-
断点触发
-
调试
文件名 | 作用 | 使用频率 |
---|---|---|
autogen.sh | 用于生成configure文件,修改Makefile.am时使用,通常用于新加文件,修改版本 | 中 |
lib/admin.lua | admin接口的命令分析文件,与admin接口相关的命令都需要修改这个文件 | 高 |
plugins/admin/admin-plugin.c | 实现admin接口的功能 | 高 |
plugins/proxy/proxy-percentile.c plugins/proxy/proxy-percentile.h |
实现percentile功能 | 中 |
plugins/proxy/proxy-plugin.c plugins/proxy/proxy-plugin.h |
实现proxy功能的主要文件 | 高 |
plugins/proxy/proxy-sql-log.c plugins/proxy/proxy-sql-log.h |
实现proxy的SQL日志管理 | 中 |
script/encrypt.c | 加密程序 | 低 |
script/source.cnf.samples | 模板配置文件,参数改变后需要修改此文件 | 低 |
src/chassis-event-thread.c src/chassis-event-thread.h |
工作线程的定义,调度,回收 | 高 |
src/chassis-exports.h | 定义chassis库的导出函数 | 低 |
src/chassis-filemode.c src/chassis-filemode.h |
设置chassis文件的权限 | 低 |
src/chassis-filter.c src/chassis-filter.h |
实现过滤功能 | 中 |
src/chassis-frontend.c src/chassis-frontend.h |
参数读取模块,处理命令行参数和配置文件中的参数 | 中 |
src/chassis-gtimeval.c src/chassis-gtimeval.h |
时间处理 | 低 |
src/chassis-keyfile.c src/chassis-keyfile.h |
配置文件处理 | 低 |
src/chassis-limits.c src/chassis-limits.h |
处理proxy的资源限制功能,如文件句柄个数 | 低 |
src/chassis-log.c src/chassis-log.h |
管理日志处理 | 中 |
src/chassis-mainloop.c src/chassis-mainloop.h |
1. 定义核心数据结构 chassis 2. 定义了启动及运行的主流程 | 高 |
src/chassis-options-utils.c src/chassis-options-utils.h |
参数处理函数 | 高 |
src/chassis-options.c src/chassis-options.h |
参数处理模块 | 中 |
src/chassis-path.c src/chassis-path.h |
路径处理 | 低 |
src/chassis-plugin.c src/chassis-plugin.h |
插件管理 | 中 |
src/chassis-shutdown-hooks.c src/chassis-shutdown-hooks.h |
声明退出时的回调函数 | 低 |
src/chassis-stats.c src/chassis-stats.h |
未知,目前未用到 | 低 |
src/chassis-timings.c src/chassis-timings.h |
chassis_timestamp_t结构体声明 | 低 |
src/chassis-unix-daemon.c src/chassis-unix-daemon.h |
实现daemon和keepalive功能 | 低 |
src/chassis-win32-service.c src/chassis-win32-service.h |
window平台服务配置,目前没有使用 | 低 |
src/disable-dtrace.h | trace接口定义 | 低 |
src/glib-ext-ref.c | 未知,使用频率低 | 低 |
src/glib-ext-ref.h | 未知,使用频率低 | 低 |
src/glib-ext.c | 未知,使用频率低 | 低 |
src/glib-ext.h | 未知,使用频率低 | 低 |
src/lua-env.c | lua接口定义 | 低 |
src/lua-env.h | lua接口声明 | 低 |
src/lua-load-factory.c | 未知,使用频率低 | 低 |
src/lua-load-factory.h | 未知,使用频率低 | 低 |
src/lua-registry-keys.h | 未知,使用频率低 | 低 |
src/lua-scope.c | 未知,使用频率低 | 低 |
src/lua-scope.h | 未知,使用频率低 | 低 |
src/my_rdtsc.c | 时间处理函数 | 低 |
src/my_rdtsc.h | 时间处理函数 | 低 |
src/mysql-binlog-dump.c | 未使用 | 低 |
src/mysql-myisam-dump.c | 未使用 | 低 |
src/mysql-proxy-cli.c | main函数入口 | 高 |
src/network-address-lua.c src/network-address-lua.h |
和lua交互中ip地址的处理 | 中 |
src/network-address.c src/network-address.h |
ip地址的处理 | 中 |
src/network-backend-lua.c src/network-backend-lua.h |
和lua交互中backend相关的操作 | 高 |
src/network-backend.c src/network-backend.h |
实现backend的管理 | 高 |
src/network-conn-errcode.h | MySQL端错误号对应的日志,用于给客户端返回MySQL的日志 | 低 |
src/network-conn-pool-lua.c src/network-conn-pool-lua.h |
连接池管理 | 高 |
src/network-conn-pool.c src/network-conn-pool.h |
连接池管理 | 高 |
src/network-debug.h | 空文件,没有使用 | 低 |
src/network-exports.h | 定义编译库的宏 | 低 |
src/network-injection-lua.c src/network-injection-lua.h |
定义injection与lua交互的模块 | 低 |
src/network-injection.c src/network-injection.h |
injection处理函数 | 低 |
src/network-mysqld-binlog.c src/network-mysqld-binlog.h |
没有使用 | 低 |
src/network-mysqld-lua.c src/network-mysqld-lua.h |
定义与lua交互的主要函数 | 高 |
src/network-mysqld-masterinfo.c src/network-mysqld-masterinfo.h |
未使用 | 低 |
src/network-mysqld-packet.c src/network-mysqld-packet.h |
与MySQL交互的数据包管理 | 中 |
src/network-mysqld-proto.c src/network-mysqld-proto.h |
与MySQL交互的协议 | 中 |
src/network-mysqld-stats.h | DBProxy状态定义 | 中 |
src/network-mysqld.c src/network-mysqld.h |
定义network_mysqld_con及其相关的对象 | 中 |
src/network-queue.c src/network-queue.h |
声明数据包管理缓存队列 | 低 |
src/network-socket-lua.c src/network-socket-lua.h |
没用到,可以忽略 | 低 |
src/network-socket.c src/network-socket.h |
socket连接管理 | 低 |
src/network_mysqld_proto_binary.c src/network_mysqld_proto_binary.h |
处理MySQL数据包的数据类型 | 中 |
src/network_mysqld_type.c src/network_mysqld_type.h |
没用到 | 低 |
src/string-len.h | 没用到 | 低 |
src/sys-pedantic.h | 未用到 | 低 |
src/test-latency.c | 测试代码 | 低 |
- chassis
名称 | 类型 | 含义 | 备注 |
---|---|---|---|
event_base | struct event_base * | 管理 注册事件 的结构体 | |
event_hdr_version | gchar * | ||
modules | GPtrArray * | 管理插件的数组 | module[0]: admin插件 module[1]:proxy插件 |
base_dir | gchar * | DBProxy安装路径 | |
log_path | gchar * | 日志路径 | |
user | gchar * | DBProxy进程的所属用户 | root用户下可以设置DBProxy以非root用户启动 |
instance_name | gchar * | 实例名 | |
log | chassis_log * | 用来管理 日志 相关参数的 数据结构 | 例如可以设置日志rotate的策略、trace的模块等等 |
stats | chassis_stats_t * | ||
event_thread_count | guint | 工作线程的数量 | |
proxy_max_connections | gint | 客户端连接DBProxy的最大连接数 | |
proxy_connections | volatile gint | 当前DBProxy持有的客户端的连接数 | |
proxy_max_used_connections | volatile gint | DBProxy当前实例,历史持有客户端连接的最大数 | |
proxy_attempted_connects | volatile gint | client试图与DBProxy建立连接的数据量 | |
proxy_aborted_connects | volatile gint | client与DBProxy建立连接时,被异常关闭的连接的数量 | |
proxy_closed_clients | volatile gint | client主动发起断开连接时,被关闭的连接数 | 客户端会发送COM_QUIT报文 |
proxy_aborted_clients | volatile gint | 正常关闭的连接数 | |
long_wait_time | gint | DBProxy与backend建立连接的时间 | 后续做监控视图,可以监控这个参数 |
long_query_time | gint | 慢查询的阈值 | 超过阈值,会在sql日志中打印一条慢日志的记录 |
query_response_time_range_base | gint | 查询响应时间的基数 | 默认是2 |
query_response_time_stats | gint | 统计信息的开关 | 0:不做统计1:仅统计总体的响应时间(包括慢查询) 2:直方图的统计 |
db_connection_idle_timeout | gint | 最大空闲连接超时 | |
db_connection_max_age | gint | 连接的生命周期 | |
my_version | MYSQL_VERSION | mysql版本号 | |
threads | GPtrArray * | 工作线程数组 | |
shutdown_hooks | chassis_shutdown_hooks_t * | ||
sc | lua_scope * | ||
backends | network_backends_t * | backend数组 | 数组的数量与event_thread_count一致 |
wait_timeout | volatile gint | 客户端连接超时 | |
shutdown_timeout | volatile gint | 关闭DBProxy时等待的最长时间 | 如果有正在执行中的事务,会等待该时间,如果超过该时间仍旧存在事务连接,则直接关闭DBProxy |
max_backend_tr | gint | backend的threadrunning阈值 | |
thread_running_sleep_delay | gint | backend的threadrunning超过阈值时,等待重试的时间 | |
proxy_filter | sql_filter * | 与sql黑名单相关的结构体 | 黑名单列表、过滤标识、黑名单的文件路径、锁等信息 |
proxy_reserved | sql_reserved_query * | 与查询统计相关的结构体 | 例如保存的最近查询的sql,自动加入黑名单的触发条件等 |
daemon_mode | gint | 标识DBProxy启动方式 | 后台启动或是前台启动 |
max_files_number | gint64 | 所持有的文件句柄数的阈值 | |
auto_restart | guint | 标识是否启动守护进程 启动守护进程时,一旦DBProxy退出,守护进程会重启DBProxy | |
opts | chassis_options_t * | 全局的参数信息 |
- network_mysqld_con
名称 | 类型 | 含义 | 备注 |
---|---|---|---|
con_id | guint64 | 连接的ID | |
state | network_mysqld_con_state_t | 连接的状态 | |
wait_status | network_mysqld_con_wait_t | 等待的状态 | |
server | network_socket * | 服务端连接的管理结构体 | |
client | network_socket * | 客户端连接的管理结构体 | |
plugins | network_mysqld_hooks | 插件提供的一组函数 | |
config | chassis_plugin_config * | 插件的参数信息 | |
srv | chassis * | 指向chassis结构体的指针 | |
is_listen_socket | int | ||
auth_result_state | guint8 | ||
resultset_is_needed | gboolean | 是否需要向客户端发送结果集 | 有些情况下不需要向客户端返回结果集,例如DBProxy隐式发送的一些语句等 |
resultset_is_finished | gboolean | 接收服务端的结果集是否接收完成 | |
com_quit_seen | gboolean | 客户端发送COM_QUIT报文断开连接 | |
parse | struct network_mysqld_con_parse | 存放查询结果相关的结构体 | |
plugin_con_state | void * | ||
conn_status_var | connection_status_var_t | 存储查询语句信息的结构体 | |
conn_status | connection_status_t | 存储连接状态的结构体 | |
locks | GHashTable* | 查询中的锁信息的存储 | |
merge_res | merge_res_t* | 用来存储和合并结果集的结构体 | |
challenge | GString* | challenge包 | |
con_filter_var | conn_filter_t | sql过滤相关的结构体 | |
is_in_wait | gboolean | 是否连接处于等待中 | |
try_send_query_times | gint | 当backend的threadrunning过高,尝试次数 | |
server_lock | GRWLock | db连接的锁 | |
server_error_code | guint16 | db返回的错误码 |
DBProxy的基础数据类型用到Glib的库,官方介绍
DBProxy 使用较多的
- gchar
- g_strdup,g_strdup_printf
- g_free
- GString
- g_string_new, g_string_free
- g_string_append_printf
- PointerAarray
- g_ptr_array_new(), g_ptr_array_sized_new(), g_ptr_array_new_with_free_func()
- g_ptr_array_add() g_ptr_array_insert()
- g_ptr_array_remove()
- g_ptr_array_sort ()
- g_ptr_array_free ()
- 示例: network-backend->raw_pwds
- Hash Tables
- g_hash_table_new(), g_hash_table_new_full()
- g_hash_table_insert(), g_hash_table_lookup(), g_hash_table_lookup_extended ()
- g_hash_table_remove_all()
- g_hash_table_destroy()
- network-backend->pwd_table, network_connection_pool, proxy->config->db_table
- Asynchronous Queues
- g_async_queue_new()
- g_async_queue_pop()
- thread->event_queue
- Doubly-Linked Lists
- Double-ended Queues
- g_queue_new (), g_queue_free()
- g_queue_push_head(), g_queue_push_tail()
- g_queue_pop_head(), g_queue_pop_tail()
- g_queue_clear ()
- Thread相关
- GThread
- g_thread_try_new
- g_thread_join
- g_thread_exit
- GRWLock
- g_rw_lock_init ()
- g_rw_lock_writer_lock ()
- g_rw_lock_writer_unlock ()
- g_rw_lock_reader_lock ()
- g_rw_lock_reader_unlock ()
- g_rw_lock_clear ()
- GMutex
- g_mutex_init ()
- g_mutex_lock ()
- g_mutex_unlock ()
- g_mutex_clear ()
- GThread
- event_base:
保存event的队列 - event_set:
设置event的属性,回调函数 - event_add:
将event添加到event_base中 - 示例: WAIT_FOR_EVENT
Lua 与 C 的简单交互
- C 与 Lua 交互的基础
- 虚拟栈
- C 端处理
- 定义变量
- index: 下标方法
- newindex: 赋值方法
- call: 函数方法
- 其它方法
- 代码参见 *-lua.c/h 的文件
- 示例讲解 selelct * from backend
-
Lua 端参数传递,结果展示
-
命令识别 admin.lua 中 _function read_query,在函数中使用正则匹配的方式来判断输入命令
-
方法调用
for i = 1, #proxy.global.backends do local b = proxy.global.backends[i] rows[#rows + 1 ] = ... end
-
结果展示
proxy.response = { type = proxy.MYSQLD_PACKET_OK, resultset = { fields = fields, rows = rows } }
-
-
C 端定义 backend变量及方法
-
全局变量(network-mysqld-lua.c)
network_mysqld_lua_setup_global proxy --> global --> backends --> raw_ips_p --> raw_pwds_p --> status --> sys_config --> config --> instance --> logpath
-
backend变量及其方法(network-backend-lua.c)
int network_backends_lua_getmetatable(lua_State *L) { static const struct luaL_reg methods[] = { { "__index", proxy_backends_get }, { "__newindex", proxy_backends_set }, { "__len", proxy_backends_len }, { "__call", proxy_backends_call }, { NULL, NULL }, }; return proxy_getmetatable(L, methods); } ```
- 示例: 实现load config 命令
-
admin.lua 的read_query函数中添加语法判断 参照 save config 命令的处理
elseif string.find(query:lower(), "^load%s+config+$") then local ret = proxy.global.sys_config("", "loadconfig") if ret == 1 then set_error("load config-file failed") return proxy.PROXY_SEND_RESULT end fields = { { name = "status", type = proxy.MYSQL_TYPE_STRING }, }
-
proxy_sys_config_call(network-mysqld-lua.c) (network_mysqld_lua_setup_global -> network_sys_config_lua_getmetatable ->proxy_sys_config_call) 函数内添加分支
elseif (strleq(key, keysize, C("loadconfig"))) { ret = load_config(chas); }
load_config函数的定义:
int load_config(chassis *chas) { return 0; }
-
- TODO
- 显示event_thread等待连接的总数
- tips
- admin.lua 中的 show proxy status 命令
- chassis_event_thread_t->event_queue 保了等待的连接
- network-mysqld-lua.c proxy_status 分支添加处理
- tips
- 显示event_thread等待连接的总数
-
-
* 参考文献
* [Lua5.3参考手册](https://cloudwu.github.io/lua53doc/manual.html)
* [Lua C 接口](https://www.lua.org/manual/5.3/manual.html#4)
* [Lua C API 的正确用法](http://blog.codingnow.com/2015/05/lua_c_api.html)
连接池的优化
```
network_connection_pool *network_connection_pool_new(void) {
network_connection_pool *pool =
g_hash_table_new_full((GHashFunc)network_connection_pool_hash_func,
(GEqualFunc)network_connection_pool_equal_func,
(GDestroyNotify)network_connection_pool_key_free,
(GDestroyNotify)network_connection_pool_value_free);
key:value : network_connection_pool_add
```
```
check_flags
skip_comment_token
```
```
is_in_blacklist
```
```
proxy_read_query
if (type == COM_QUERY && tokens->len <= 1) {
```
此问题是Atlas在多个分表merge结果的过程中未处理 NULL 值,导致结果集返回不对,而JDBC接口会认为此种情况下是未收到结果,会处于一直等待状态,触发超时。
```
merge_rows
```
```
combine_sql
for (i = 0; i < num; ++i) mt[i] = g_array_new(FALSE, FALSE, sizeof(guint64));
```
在连接的结构体的释放接口中,lock 的成员变量未释放,导致在连接断开,回收连接对象时会泄漏24个字节。
```
network_mysqld_con_free
g_hash_table_remove_all(con->locks);
g_hash_table_destroy(con->locks);
```
http://wiki.sankuai.com/pages/viewpage.action?pageId=441093332
```
network_mysqld_con_handle
CON_STATE_READ_QUERY:
服务端连接
// 异常,server端收到了数据,直接断开连接
if (con->server && event_fd == con->server->fd) {
gchar *log_str = g_strdup_printf("there is something to read from server(%s).",
NETWORK_SOCKET_SRC_NAME(con->server));
CON_MSG_HANDLE(g_critical, con, log_str);
g_free(log_str);
g_atomic_int_add(&srv->proxy_aborted_clients, 1);
con->state = CON_STATE_ERROR;
break;
}
```
```
show processlist
con->server_lock
```
```
函数 network_mysqld_con_send_resultset
g_string_append_c(s, field->charsetnr & 0xff); /* charset */
g_string_append_c(s, (field->charsetnr >> 8) & 0xff); /* charset */
```
proxy_read_query_result()
case PROXY_SEND_RESULT:
gboolean b_reserve_conn = (inj->qstat.insert_id > 0) || (inj->qstat.warning_count > 0) || (inj->qstat.affected_rows > 0); //found_rows(), last_insert_id(), row_count(), show warnings
if (!con->conn_status.is_in_transaction &&
!con->conn_status.is_in_select_calc_found_rows &&
!b_reserve_conn && g_hash_table_size(con->locks) == 0) {
network_connection_pool_lua_add_connection(con); //放回连接池
}
- 查询上下文信息: found_rows(), last_insert_id(), row_count(), show warnings
- 事务内
- 显示锁
注释:
- 每个backend为每个thread-event线程都保存了独立的连接池,例如backend为event-thread2提供了连接池et2
- 每个连接池中的socket按照用户名来组织,例如客户端需要以user2的用户名连接后端数据库,则从连接池的user2的链表中取出其中的一个socket,分配给客户端
- 在分配event-thread时,是轮询方式分配event-thread的
- 在选择backend的时,最基本的选择方式是轮询方式。