一個支援主從,事務以及連線池功能的mysql-proxy指令碼

season0891發表於2013-11-05

看了點例子,根據mysql-proxy裡的keepalive例子修改了一個支援主從分離,事務到主庫,非事務查詢到從庫,以及連線斷開時自動回滾的指令碼,分享一下。

 


[python] view plaincopy
  1. --[[ $%BEGINLICENSE%$  
  2.  Copyright (C) 2007-2008 MySQL AB, 2008 Sun Microsystems, Inc  
  3.  This program is free software; you can redistribute it and/or modify  
  4.  it under the terms of the GNU General Public License as published by  
  5.  the Free Software Foundation; version 2 of the License.  
  6.  This program is distributed in the hope that it will be useful,  
  7.  but WITHOUT ANY WARRANTY; without even the implied warranty of  
  8.  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  
  9.  GNU General Public License for more details.  
  10.  You should have received a copy of the GNU General Public License  
  11.  along with this program; if not, write to the Free Software  
  12.  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
  13.  $%ENDLICENSE%$ --]]  
  14. --[[  
  15.      
  16. --]]  
  17. ---  
  18. -- a flexible statement based load balancer with connection pooling  
  19. --  
  20. -- * build a connection pool of min_idle_connections for each backend and   
  21. --   maintain its size  
  22. -- * reusing a server-side connection when it is idling  
  23. --   
  24. --- config  
  25. --  
  26. -- connection pool  
  27. local min_idle_connections = 4  
  28. local max_idle_connections = 8  
  29. -- debug  
  30. local is_debug = true  
  31. --- end of config  
  32. ---  
  33. -- read/write splitting sends all non-transactional SELECTs to the slaves  
  34. --  
  35. -- is_in_transaction tracks the state of the transactions  
  36. local is_in_transaction = 0  
  37. ---   
  38. -- get a connection to a backend  
  39. --  
  40. -- as long as we don't have enough connections in the pool, create new connections  
  41. --  
  42. function connect_server()   
  43.     -- make sure that we connect to each backend at least ones to   
  44.     -- keep the connections to the servers alive  
  45.     --  
  46.     -- on read_query we can switch the backends again to another backend  
  47.     if is_debug then  
  48.         print()  
  49.         print("[connect_server] ")  
  50.     end  
  51.     local least_idle_conns_ndx = 0  
  52.     local least_idle_conns = 0  
  53.     for i = 1, #proxy.global.backends do  
  54.         local s = proxy.global.backends[i]  
  55.         local pool     = s.pool   
  56.         -- we don't have a username yet, try to find a connections which is idling  
  57.         local cur_idle = pool.users[""].cur_idle_connections  
  58.         if cur_idle == nil then  
  59.             cur_idle = 0  
  60.         end  
  61.         if is_debug then  
  62.             print("  [".. i .."].connected_clients = " .. s.connected_clients)  
  63.             print("  [".. i .."].idling_connections = " .. cur_idle)  
  64.             print("  [".. i .."].type = " .. s.type)  
  65.             print("  [".. i .."].state = " .. s.state)  
  66.         end  
  67.         if s.state ~= proxy.BACKEND_STATE_DOWN then  
  68.             -- try to connect to each backend once at least  
  69.             if cur_idle == 0 then  
  70.                 proxy.connection.backend_ndx = i  
  71.                 if is_debug then  
  72.                     print("  [".. i .."] open new connection")  
  73.                 end  
  74.                 return  
  75.             end  
  76.             -- try to open at least min_idle_connections  
  77.             if least_idle_conns_ndx == 0 or  
  78.                ( cur_idle 
  79.                  cur_idle 
  80.                 least_idle_conns_ndx = i  
  81.                 least_idle_conns = s.idling_connections  
  82.                 if least_idle_conns == nil then  
  83.                     least_idle_conns = 0  
  84.                 end  
  85.             end  
  86.         end  
  87.     end  
  88.     if least_idle_conns_ndx > 0 then  
  89.         proxy.connection.backend_ndx = least_idle_conns_ndx  
  90.     end  
  91.     if proxy.connection.backend_ndx > 0 then   
  92.         local s = proxy.global.backends[proxy.connection.backend_ndx]  
  93.         local pool     = s.pool -- we don't have a username yet, try to find a connections which is idling  
  94.         local cur_idle = pool.users[""].cur_idle_connections  
  95.         if cur_idle >= min_idle_connections then  
  96.             -- we have 4 idling connections in the pool, that's good enough  
  97.             if is_debug then  
  98.                 print("  using pooled connection from: " .. proxy.connection.backend_ndx)  
  99.             end  
  100.       
  101.             return proxy.PROXY_IGNORE_RESULT  
  102.         end  
  103.     end  
  104.     if is_debug then  
  105.         print("  opening new connection on: " .. proxy.connection.backend_ndx)  
  106.     end  
  107.     -- open a new connection   
  108. end  
  109. ---   
  110. -- put the successfully authed connection into the connection pool  
  111. --  
  112. -- @param auth the context information for the auth  
  113. --  
  114. -- auth.packet is the packet  
  115. function read_auth_result( auth )  
  116.     if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then  
  117.         -- auth was fine, disconnect from the server  
  118.         proxy.connection.backend_ndx = 0  
  119.     elseif auth.packet:byte() == proxy.MYSQLD_PACKET_EOF then  
  120.         -- we received either a   
  121.         --   
  122.         -- * MYSQLD_PACKET_ERR and the auth failed or  
  123.         -- * MYSQLD_PACKET_EOF which means a OLD PASSWORD (4.0) was sent  
  124.         print("(read_auth_result) ... not ok yet");  
  125.     elseif auth.packet:byte() == proxy.MYSQLD_PACKET_ERR then  
  126.         -- auth failed  
  127.     end  
  128. end  
  129.   
  130. ---   
  131. -- read/write splitting  
  132. function read_query( packet )   
  133.     if is_debug then  
  134.         print("[read_query]")  
  135.         print("  authed backend = " .. proxy.connection.backend_ndx)  
  136.         print("  used db = " .. proxy.connection.client.default_db)  
  137.     end  
  138.     if packet:byte() == proxy.COM_QUIT then  
  139.         -- don't send COM_QUIT to the backend. We manage the connection  
  140.         -- in all aspects.  
  141.         proxy.response = {  
  142.             type = proxy.MYSQLD_PACKET_OK,  
  143.         }  
  144.         if is_in_transaction then  
  145.             print("  transaction on, rollback now")  
  146.             proxy.queries.append(1, string:char(proxy.COM_QUERY) .. "ROLLBACK", { resultset_is_needed = false})       
  147.         end  
  148.         return proxy.PROXY_SEND_RESULT  
  149.     end  
  150.     if proxy.connection.backend_ndx == 0 then  
  151.         if is_debug then  
  152.             print("  no connection, select now")      
  153.         end  
  154.         is_master = true  
  155.         if packet:byte() == proxy.COM_QUERY then  
  156.             command = packet:sub(2, 7)  
  157.             print("  command:" .. command)  
  158.             if string.lower(command) == "select" then  
  159.                 print("  session not in transaction, select go to slave")  
  160.                 is_master = false  
  161.             end  
  162.         end  
  163.         -- we don't have a backend right now  
  164.         --   
  165.         -- let's pick a master as a good default  
  166.         for i = 1, #proxy.global.backends do  
  167.             local s = proxy.global.backends[i]  
  168.             local pool     = s.pool   
  169.             -- we don't have a username yet, try to find a connections which is idling  
  170.             local cur_idle = pool.users[proxy.connection.client.username].cur_idle_connections  
  171.               
  172.             if cur_idle > 0 and s.state ~= proxy.BACKEND_STATE_DOWN then  
  173.                 if s.type == proxy.BACKEND_TYPE_RW and is_master == true then  
  174.                     proxy.connection.backend_ndx = i  
  175.                     break  
  176.                 elseif s.type == proxy.BACKEND_TYPE_RO and is_master == false then  
  177.                     proxy.connection.backend_ndx = i  
  178.                     break  
  179.                 end  
  180.             end  
  181.         end  
  182.     end  
  183.     if is_debug then  
  184.         print("  connection:" .. proxy.connection.backend_ndx)  
  185.     end  
  186.     if true or proxy.connection.client.default_db and   
  187.         proxy.connection.client.default_db ~= proxy.connection.server.default_db then  
  188.         -- sync the client-side default_db with the server-side default_db  
  189.         proxy.queries:append(2, string.char(proxy.COM_INIT_DB) .. proxy.connection.client.default_db,    
  190.             { resultset_is_needed = true })  
  191.     end  
  192.     proxy.queries:append(1, packet,  { resultset_is_needed = true })  
  193.     return proxy.PROXY_SEND_QUERY  
  194. end  
  195. ---  
  196. -- as long as we are in a transaction keep the connection  
  197. -- otherwise release it so another client can use it  
  198. function read_query_result( inj )   
  199.     local res      = assert(inj.resultset)  
  200.     local flags    = res.flags  
  201.     if inj.id ~= 1 then  
  202.         -- ignore the result of the USE   
  203.         return proxy.PROXY_IGNORE_RESULT  
  204.     end  
  205.     is_in_transaction = flags.in_trans  
  206.     if not is_in_transaction then  
  207.         -- release the backend  
  208.         proxy.connection.backend_ndx = 0  
  209.     end  
  210. end  
  211. ---   
  212. -- close the connections if we have enough connections in the pool  
  213. --  
  214. -- @return nil - close connection   
  215. --         IGNORE_RESULT - store connection in the pool  
  216. function disconnect_client()  
  217.     if is_debug then  
  218.         print("[disconnect_client]")  
  219.     end  
  220.     if proxy.connection.backend_ndx == 0 then  
  221.         -- currently we don't have a server backend assigned  
  222.         --  
  223.         -- pick a server which has too many idling connections and close one  
  224.         for i = 1, #proxy.global.backends do  
  225.             local s = proxy.global.backends[i]  
  226.             local pool     = s.pool   
  227.             -- we don't have a username yet, try to find a connections which is idling  
  228.             local cur_idle = pool.users[proxy.connection.client.username].cur_idle_connections  
  229.             if s.state ~= proxy.BACKEND_STATE_DOWN and  
  230.                cur_idle > max_idle_connections then  
  231.                 -- try to disconnect a backend  
  232.                 proxy.connection.backend_ndx = i  
  233.                 if is_debug then  
  234.                     print("  [".. i .."] closing connection, idling: " .. cur_idle)  
  235.                 end  
  236.                 return  
  237.             end  
  238.         end  
  239.     end  
  240. end  


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/90618/viewspace-775782/,如需轉載,請註明出處,否則將追究法律責任。

相關文章