当前位置: 首页 > news >正文

【原创】MySQL Proxy - Administration Interface


       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>

相关文章:

  • 详解6大安全场景:移动app安全、防DDoS、防入侵、数据加密、业务反欺诈、内容安全...
  • linux ulimit 的设置
  • 二次登陆验证
  • 电脑配置
  • 解决spring jpa中配置文件报'jpa:repositories'的问题
  • 启用约束时使用exceptions表来跟踪不符合约束的数据并修正
  • Combination Sum系列问题
  • js中容易被忽视的事件问题总结
  • Web Service 接口安全与解决方案
  • B树、B-树、B+树、B*树的定义和区分
  • 史上最全大数据学习资源整理(1)
  • Hive操作表部分总结
  • 电邮欺诈需重视 TurboMail邮件系统保护您
  • IOS-利用AFNetworking监听网络状态
  • WCF学习之旅—WCF服务部署到应用程序(十)
  • (ckeditor+ckfinder用法)Jquery,js获取ckeditor值
  • Docker 笔记(2):Dockerfile
  • Git 使用集
  • java8-模拟hadoop
  • Laravel Mix运行时关于es2015报错解决方案
  • mysql 5.6 原生Online DDL解析
  • nfs客户端进程变D,延伸linux的lock
  • Spring Boot快速入门(一):Hello Spring Boot
  • Spring框架之我见(三)——IOC、AOP
  • SQLServer之创建数据库快照
  • 闭包--闭包之tab栏切换(四)
  • 分布式熔断降级平台aegis
  • 将 Measurements 和 Units 应用到物理学
  • 数据科学 第 3 章 11 字符串处理
  • 算法-插入排序
  • PostgreSQL之连接数修改
  • 我们雇佣了一只大猴子...
  • #我与Java虚拟机的故事#连载15:完整阅读的第一本技术书籍
  • (C++)栈的链式存储结构(出栈、入栈、判空、遍历、销毁)(数据结构与算法)
  • (LeetCode) T14. Longest Common Prefix
  • (超详细)2-YOLOV5改进-添加SimAM注意力机制
  • (转)Linq学习笔记
  • (转)Oracle 9i 数据库设计指引全集(1)
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .Net Attribute详解(上)-Attribute本质以及一个简单示例
  • .skip() 和 .only() 的使用
  • @NestedConfigurationProperty 注解用法
  • @selector(..)警告提示
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [2016.7.Test1] T1 三进制异或
  • [acwing周赛复盘] 第 69 场周赛20220917
  • [bug总结]: Feign调用GET请求找不到请求体实体类
  • [BZOJ5250][九省联考2018]秘密袭击(DP)
  • [C#][DevPress]事件委托的使用
  • [C#]winform使用引导APSF和梯度自适应卷积增强夜间雾图像的可见性算法实现夜间雾霾图像的可见度增强
  • [Excel VBA]单元格区域引用方式的小结
  • [Flexbox] Using order to rearrange flexbox children
  • [hdu 4552] 怪盗基德的挑战书
  • [Hibernate] - Fetching strategies
  • [HNOI2008]水平可见直线