【原创】MySQL Proxy - Administration Interface

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

       mysql-proxy 的 administration 接口可以通过任何 MySQL 客户端按照标准协议进行访问。你可以通过 administration 接口获取 proxy 服务器的整体信息 - 向 proxy 建立的标准连接是处于隔离状态的,其模拟了直接连接到后端 MySQL 服务器的情形。  


       在 mysql-proxy 0.8.0 或者更早的版本中,一组最基本的接口已被添加到 proxy 中。在后续版本中,这种方式已被替换,你必须要使用的 administration 脚本以连接到 administration 接口。  


       为了使用 administration 接口,需要通过 --admin-username 和 --admin-password 选项指定连接 admin 服务器所需的用户名和密码。同样必须通过 admin-lua-script 脚本选项指定提供访问 administration 服务所需接口的 Lua 脚本。  


       例如,你可以使用下面的脚本创建一个访问 mysql-proxy 系统内部组件基本接口,由 Diego Medina 提供:  


--[[


   Copyright 2008, 2010, Oracle and/or its affiliates. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.


   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.


   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


--]]


-- admin.lua


--[[


    See http://www.chriscalender.com/?p=41
    (Thanks to Chris Calender)
    See http://datacharmer.blogspot.com/2009/01/mysql-proxy-is-back.html
    (Thanks Giuseppe Maxia)


--]]


function set_error(errmsg) 
    proxy.response = {
        type = proxy.MYSQLD_PACKET_ERR,
        errmsg = errmsg or "error"
    }
end


function read_query(packet)
    if packet:byte() ~= proxy.COM_QUERY then
        set_error("[admin] we only handle text-based queries (COM_QUERY)")
        return proxy.PROXY_SEND_RESULT
    end


    local query = packet:sub(2)
    local rows = { }
    local fields = { }


    -- try to match the string up to the first non-alphanum
    local f_s, f_e, command = string.find(packet, "^%s*(%w+)", 2)
    local option


    if f_e then
            -- if that match, take the next sub-string as option
            f_s, f_e, option = string.find(packet, "^%s+(%w+)", f_e + 1)
    end


    -- we got our commands, execute it
    if command == "show" and option == "querycounter" then
            ---
            -- proxy.PROXY_SEND_RESULT requires
            --
            -- proxy.response.type to be either
            -- * proxy.MYSQLD_PACKET_OK or
            -- * proxy.MYSQLD_PACKET_ERR
            --
            -- for proxy.MYSQLD_PACKET_OK you need a resultset
            -- * fields
            -- * rows
            --
            -- for proxy.MYSQLD_PACKET_ERR
            -- * errmsg
            proxy.response.type = proxy.MYSQLD_PACKET_OK
            proxy.response.resultset = {
                    fields = {
                            { type = proxy.MYSQL_TYPE_LONG, name = "query_counter", },
                    },
                    rows = {
                            { proxy.global.query_counter }
                    }
            }


            -- we have our result, send it back
            return proxy.PROXY_SEND_RESULT
    elseif command == "show" and option == "myerror" then
            proxy.response.type = proxy.MYSQLD_PACKET_ERR
            proxy.response.errmsg = "my first error"


            return proxy.PROXY_SEND_RESULT
            
    elseif string.sub(packet, 2):lower() == 'select help' then
            return show_process_help()
    
    elseif string.sub(packet, 2):lower() == 'show proxy processlist' then
            return show_process_table()


    elseif query == "SELECT * FROM backends" then
        fields = { 
            { name = "backend_ndx", 
              type = proxy.MYSQL_TYPE_LONG },


            { name = "address",
              type = proxy.MYSQL_TYPE_STRING },
            { name = "state",
              type = proxy.MYSQL_TYPE_STRING },
            { name = "type",
              type = proxy.MYSQL_TYPE_STRING },
        }


        for i = 1, #proxy.global.backends do
            local b = proxy.global.backends[i]


            rows[#rows + 1] = {
                i, b.dst.name, b.state, b.type 
            }
        end
    else
        set_error()
        return proxy.PROXY_SEND_RESULT
    end


    proxy.response = {
        type = proxy.MYSQLD_PACKET_OK,
        resultset = {
            fields = fields,
            rows = rows
        }
    }
    return proxy.PROXY_SEND_RESULT
end




function make_dataset (header, dataset)
    proxy.response.type = proxy.MYSQLD_PACKET_OK


    proxy.response.resultset = {
        fields = {},
        rows = {}
    }
    for i,v in pairs (header) do
        table.insert(proxy.response.resultset.fields, {type = proxy.MYSQL_TYPE_STRING, name = v})
    end
    for i,v in pairs (dataset) do
        table.insert(proxy.response.resultset.rows, v )
    end
    return proxy.PROXY_SEND_RESULT
end


function show_process_table()
    local dataset = {}
    local header = { 'Id', 'IP Address', 'Time' }
    local rows = {}
    for t_i, t_v in pairs (proxy.global.process) do
        for s_i, s_v in pairs ( t_v ) do
            table.insert(rows, { t_i, s_v.ip, os.date('%c',s_v.ts) })
        end
    end
    return make_dataset(header,rows)
end


function show_process_help()
    local dataset = {}
    local header = { 'command',  'description' }
    local rows = {
        {'SELECT HELP',                 'This command.'},
        {'SHOW PROXY PROCESSLIST',      'Show all connections and their true IP Address.'},
    }
    return make_dataset(header,rows)
end


function dump_process_table()
    proxy.global.initialize_process_table()
    print('current contents of process table')
    for t_i, t_v in pairs (proxy.global.process) do
        print ('session id: ', t_i)
        for s_i, s_v in pairs ( t_v ) do
            print ( '\t', s_i, s_v.ip, s_v.ts )
        end
    end
    print ('---END PROCESS TABLE---')
end


--[[    Help


we use a simple string-match to split commands are word-boundaries


mysql> show querycounter


is split into
command = "show"
option  = "querycounter"


spaces are ignored, the case has to be as is.


mysql> show myerror


returns a error-packet


--]]


上述脚本需要配合下面的 proxy 脚本一起使用,名字为 reporter.lua :  


--[[


   Copyright 2008, 2010, Oracle and/or its affiliates. All rights reserved.
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.


   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.


   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA


--]]


-- reporter.lua


--[[


    See http://www.chriscalender.com/?p=41
    (Thanks to Chris Calender)
    See http://datacharmer.blogspot.com/2009/01/mysql-proxy-is-back.html
    (Thanks Giuseppe Maxia)


--]]


proxy.global.query_counter = proxy.global.query_counter or 0


function proxy.global.initialize_process_table()
    if proxy.global.process == nil then
        proxy.global.process = {}
    end
    if proxy.global.process[proxy.connection.server.thread_id] == nil then
        proxy.global.process[proxy.connection.server.thread_id] = {}
    end
end


function read_auth_result( auth )
    local state = auth.packet:byte()
    if state == proxy.MYSQLD_PACKET_OK then
        proxy.global.initialize_process_table()
        table.insert( proxy.global.process[proxy.connection.server.thread_id],
            { ip = proxy.connection.client.src.name, ts = os.time() } )
    end
end


function disconnect_client()
    local connection_id = proxy.connection.server.thread_id
    if connection_id then
        -- client has disconnected, set this to nil
        proxy.global.process[connection_id] = nil
    end
end




---
-- read_query() can return a resultset
--
-- You can use read_query() to return a result-set.
--
-- @param packet the mysql-packet sent by the client
--
-- @return
--   * nothing to pass on the packet as is,
--   * proxy.PROXY_SEND_QUERY to send the queries from the proxy.queries queue
--   * proxy.PROXY_SEND_RESULT to send your own result-set
--
function read_query( packet )
        -- a new query came in in this connection
        -- using proxy.global.* to make it available to the admin plugin
        proxy.global.query_counter = proxy.global.query_counter + 1


end


       为了使用上述脚本,首先需要将第一个脚本保存为文件(在下面的例子中文件名为 admin.lua ),第二个命名为 reporter.lua ,然后通过命令指定 mysql-proxy 执行的 admin 脚本和后端 MySQL 服务器:  


?
1
2
shell> mysql-proxy --admin-lua-script=admin.lua --admin-password=password \ »
      --admin-username=root --proxy-backend-addresses=127.0.0.1:3306 -proxy-lua-script=reporter.lua

   
在另外一个窗口中,通过 proxy 连接 MySQL 服务器:  

?
1
2
3
4
5
6
7
8
9
10
shell> mysql --user=root --password=password --port=4040
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1798669
Server version: 5.0.70-log Gentoo Linux mysql-5.0.70-r1
 
 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
 
 
mysql>


在第三个窗口中,使用指定的用户名和密码连接 mysql-proxy 的 admin 服务:  


?
1
2
3
4
5
6
7
8
9
10
shell> mysql --user=root --password=password --port=4041 --host=localhost
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.0.99-agent-admin
 
 
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
 
 
mysql>


为了监视 proxy 的状态信息,可以请求当前活动进行的列表信息:  


?
1
2
3
4
5
6
7
8
9
10
mysql> show proxy processlist;
+ ---------+---------------------+--------------------------+
| Id      | IP Address          | Time                     |
+ ---------+---------------------+--------------------------+
| 1798669 | 192.168.0.112:52592 | Wed Jan 20 16:58:00 2010 |
+ ---------+---------------------+--------------------------+
1 row in set (0.00 sec)
 
 
mysql>

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
3月前
|
关系型数据库 MySQL 数据安全/隐私保护
已解决:mysql: [Warning] Using a password on the command line interface can be insecure.
已解决:mysql: [Warning] Using a password on the command line interface can be insecure.
226 0
|
存储 数据采集 缓存
【运维知识进阶篇】Zabbix5.0稳定版详解9(Zabbix优化:高并发对MySQL进行拆分、Zabbix-agent主动上报模式、使用proxy代理模式、系统自带监控项优化、进程优化、缓存优化)
【运维知识进阶篇】Zabbix5.0稳定版详解9(Zabbix优化:高并发对MySQL进行拆分、Zabbix-agent主动上报模式、使用proxy代理模式、系统自带监控项优化、进程优化、缓存优化)
617 0
|
监控 MySQL 关系型数据库
|
关系型数据库 MySQL 开发工具
|
关系型数据库 MySQL 开发工具
|
关系型数据库 MySQL 测试技术

热门文章

最新文章