From a780c3ec9c47bca03566587b2cac2af85d49c76e Mon Sep 17 00:00:00 2001 From: flike Date: Sun, 2 Aug 2015 21:02:26 +0800 Subject: [PATCH 01/13] add management interface of kingshard --- backend/node.go | 56 +++++- core/errors/errors.go | 2 + proxy/server/conn.go | 3 - proxy/server/conn_admin.go | 175 ++++++++++++++----- proxy/server/server.go | 20 ++- sqlparser/ast.go | 7 +- sqlparser/sql.go | 338 ++++++++++++++++++------------------- sqlparser/sql.y | 4 +- 8 files changed, 387 insertions(+), 218 deletions(-) diff --git a/backend/node.go b/backend/node.go index bbbe2018..83cd8ca5 100644 --- a/backend/node.go +++ b/backend/node.go @@ -147,13 +147,65 @@ func (n *Node) checkSlave() { golog.Info("Node", "checkMaster", "Master down", 0, "db.Addr", slaves[i].Addr(), "slave_down_time", int64(n.DownAfterNoAlive/time.Second)) - //If can't ping slave after DownAfterNoAlive set slave Down + //If can't ping slave after DownAfterNoAlive, set slave Down n.DownSlave(slaves[i].addr) } } } +func (n *Node) AddSlave(addr string) error { + var weight int + var err error + if len(addr) == 0 { + return ErrAddressNull + } + n.Lock() + defer n.Unlock() + addrAndWeight := strings.Split(addr, WeightSplit) + if len(addrAndWeight) == 2 { + weight, err = strconv.Atoi(addrAndWeight[1]) + if err != nil { + return err + } + } else { + weight = 1 + } + n.SlaveWeights = append(n.SlaveWeights, weight) + db := n.OpenDB(addrAndWeight[0]) + n.Slave = append(n.Slave, db) + n.InitBalancer() + return nil +} + +func (n *Node) DeleteSlave(addr string) error { + n.Lock() + defer n.Unlock() + slaveCount := len(n.Slave) + if slaveCount == 0 { + return ErrNoSlaveDb + } else if slaveCount == 1 { + n.Slave = nil + n.SlaveWeights = nil + n.RoundRobinQ = nil + return nil + } + + s := make([]*DB, 0, slaveCount-1) + sw := make([]int, 0, slaveCount-1) + for i := 0; i < slaveCount; i++ { + if n.Slave[i].addr != addr { + s = append(s, n.Slave[i]) + sw = append(sw, n.SlaveWeights[i]) + } + } + + n.Slave = s + n.SlaveWeights = sw + n.InitBalancer() + return nil +} + func (n *Node) OpenDB(addr string) *DB { db := Open(addr, n.Cfg.User, n.Cfg.Password, "") @@ -242,7 +294,7 @@ func (n *Node) ParseMaster(masterStr string) error { return nil } -//slaveStr(127.0.0.1:3306@2) +//slaveStr(127.0.0.1:3306@2,192.168.0.12:3306@3) func (n *Node) ParseSlave(slaveStr string) error { var db *DB var weight int diff --git a/core/errors/errors.go b/core/errors/errors.go index d4c2c7d0..2e5d2851 100644 --- a/core/errors/errors.go +++ b/core/errors/errors.go @@ -13,6 +13,8 @@ var ( ErrNoDatabase = errors.New("no database") ErrMasterDown = errors.New("master is down") ErrSlaveDown = errors.New("slave is down") + ErrAddressNull = errors.New("address is nil") + ErrCmdUnsupport = errors.New("command unsupport") ErrLocationsCount = errors.New("locations count are not equal") ErrNoCriteria = errors.New("plan have no criteria") diff --git a/proxy/server/conn.go b/proxy/server/conn.go index 36db7aeb..a363306b 100644 --- a/proxy/server/conn.go +++ b/proxy/server/conn.go @@ -71,9 +71,6 @@ func (c *ClientConn) IsAllowConnect() bool { } for _, ip := range ipVec { if ip.Equal(clientIP) { - golog.Info("server", "IsAllowConnect", "info", - c.connectionId, "client", - c.c.RemoteAddr().String(), " login success.") return true } } diff --git a/proxy/server/conn_admin.go b/proxy/server/conn_admin.go index d1a6260c..dae267b5 100644 --- a/proxy/server/conn_admin.go +++ b/proxy/server/conn_admin.go @@ -2,70 +2,169 @@ package server import ( "fmt" + . "github.com/flike/kingshard/core/errors" + "github.com/flike/kingshard/core/golog" "github.com/flike/kingshard/sqlparser" "strings" ) const ( - Master = "master" - Slave = "slave" + Master = "master" + Slave = "slave" + ServerRegion = "server" + NodeRegion = "node" + + ADMIN_OPT_ADD = "add" + ADMIN_OPT_DEL = "del" + ADMIN_OPT_UP = "up" + ADMIN_OPT_DOWN = "down" ) -func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { - name := string(admin.Name) +var cmdServerOrder = []string{"opt", "k", "v"} +var cmdNodeOrder = []string{"opt", "node", "k", "v"} +func (c *ClientConn) handleNodeCmd(rows sqlparser.InsertRows) error { var err error - switch strings.ToLower(name) { - case "upnode": - err = c.adminUpNodeServer(admin.Values) - case "downnode": - err = c.adminDownNodeServer(admin.Values) + var opt, nodeName, role, addr string + + vals := rows.(sqlparser.Values) + if len(vals) == 0 { + return ErrCmdUnsupport + } + + tuple := vals[0].(sqlparser.ValTuple) + if len(tuple) != len(cmdNodeOrder) { + return ErrCmdUnsupport + } + + opt = sqlparser.String(tuple[0]) + opt = strings.Trim(opt, "'") + + nodeName = sqlparser.String(tuple[1]) + nodeName = strings.Trim(nodeName, "'") + + role = sqlparser.String(tuple[2]) + role = strings.Trim(role, "'") + + addr = sqlparser.String(tuple[3]) + addr = strings.Trim(addr, "'") + + switch strings.ToLower(opt) { + case ADMIN_OPT_ADD: + err = c.AddDatabase( + nodeName, + role, + addr, + ) + case ADMIN_OPT_DEL: + err = c.DeleteDatabase( + nodeName, + role, + addr, + ) + + case ADMIN_OPT_UP: + err = c.UpDatabase( + nodeName, + role, + addr, + ) + case ADMIN_OPT_DOWN: + err = c.DownDatabase( + nodeName, + role, + addr, + ) default: - return fmt.Errorf("admin %s not supported now", name) + err = ErrCmdUnsupport + golog.Error("ClientConn", "handleNodeCmd", err.Error(), + c.connectionId, "opt", opt) } + return err +} - if err != nil { - return err +func (c *ClientConn) AddDatabase(nodeName string, role string, addr string) error { + //can not add a new master database + if role != Slave { + return ErrCmdUnsupport } - return c.writeOK(nil) + return c.proxy.AddSlave(nodeName, addr) } -func (c *ClientConn) adminUpNodeServer(values sqlparser.ValExprs) error { - if len(values) != 3 { - return fmt.Errorf("upnode needs 3 args, not %d", len(values)) +func (c *ClientConn) DeleteDatabase(nodeName string, role string, addr string) error { + //can not delete a master database + if role != Slave { + return ErrCmdUnsupport } - nodeName := nstring(values[0]) - sType := strings.ToLower(nstring(values[1])) - addr := strings.ToLower(nstring(values[2])) + return c.proxy.DeleteSlave(nodeName, addr) +} - switch sType { - case Master: +func (c *ClientConn) UpDatabase(nodeName string, role string, addr string) error { + if role != Master && role != Slave { + return ErrCmdUnsupport + } + if role == Master { return c.proxy.UpMaster(nodeName, addr) - case Slave: - return c.proxy.UpSlave(nodeName, addr) - default: - return fmt.Errorf("invalid server type %s", sType) } + + return c.proxy.UpSlave(nodeName, addr) } -func (c *ClientConn) adminDownNodeServer(values sqlparser.ValExprs) error { - if len(values) != 3 { - return fmt.Errorf("upnode needs 2 args, not %d", len(values)) +func (c *ClientConn) DownDatabase(nodeName string, role string, addr string) error { + if role != Master && role != Slave { + return ErrCmdUnsupport + } + if role == Master { + return c.proxy.DownMaster(nodeName, addr) } - nodeName := nstring(values[0]) - sType := strings.ToLower(nstring(values[1])) + return c.proxy.DownSlave(nodeName, addr) +} - switch sType { - case Master: - masterAddr := strings.ToLower(nstring(values[2])) - return c.proxy.DownMaster(nodeName, masterAddr) - case Slave: - slaveAddr := strings.ToLower(nstring(values[2])) - return c.proxy.DownSlave(nodeName, slaveAddr) +func (c *ClientConn) checkCmdOrder(region string, columns sqlparser.Columns) error { + var cmdOrder []string + node := sqlparser.SelectExprs(columns) + + switch region { + case NodeRegion: + cmdOrder = cmdNodeOrder default: - return fmt.Errorf("invalid server type %s", sType) + return ErrCmdUnsupport + } + + for i := 0; i < len(node); i++ { + val := sqlparser.String(node[i]) + if val != cmdOrder[i] { + return ErrCmdUnsupport + } } + + return nil +} + +func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { + var err error + region := sqlparser.String(admin.Region) + + err = c.checkCmdOrder(region, admin.Columns) + if err != nil { + return err + } + + switch strings.ToLower(region) { + case NodeRegion: + err = c.handleNodeCmd(admin.Rows) + default: + return fmt.Errorf("admin %s not supported now", region) + } + + if err != nil { + golog.Error("ClientConn", "handleAdmin", err.Error(), + c.connectionId, "sql", sqlparser.String(admin)) + return err + } + + return c.writeOK(nil) } diff --git a/proxy/server/server.go b/proxy/server/server.go index ce93cc73..e80509cb 100644 --- a/proxy/server/server.go +++ b/proxy/server/server.go @@ -266,6 +266,24 @@ func (s *Server) Close() { } } +func (s *Server) DeleteSlave(node string, addr string) error { + n := s.GetNode(node) + if n == nil { + return fmt.Errorf("invalid node %s", node) + } + + return n.DeleteSlave(addr) +} + +func (s *Server) AddSlave(node string, addr string) error { + n := s.GetNode(node) + if n == nil { + return fmt.Errorf("invalid node %s", node) + } + + return n.AddSlave(addr) +} + func (s *Server) UpMaster(node string, addr string) error { n := s.GetNode(node) if n == nil { @@ -283,12 +301,12 @@ func (s *Server) UpSlave(node string, addr string) error { return n.UpSlave(addr) } + func (s *Server) DownMaster(node, masterAddr string) error { n := s.GetNode(node) if n == nil { return fmt.Errorf("invalid node %s", node) } - // n.db = nil return n.DownMaster(masterAddr) } diff --git a/sqlparser/ast.go b/sqlparser/ast.go index 64e28a45..0eb5144a 100644 --- a/sqlparser/ast.go +++ b/sqlparser/ast.go @@ -914,14 +914,15 @@ func (*SimpleSelect) ISelectStatement() {} func (*SimpleSelect) IInsertRows() {} type Admin struct { - Name []byte - Values ValExprs + Region *TableName + Columns Columns + Rows InsertRows } func (*Admin) IStatement() {} func (node *Admin) Format(buf *TrackedBuffer) { - buf.Fprintf("admin %s(%v)", node.Name, node.Values) + buf.Fprintf("admin %v%v %v", node.Region, node.Columns, node.Rows) } type Show struct { diff --git a/sqlparser/sql.go b/sqlparser/sql.go index b2274196..aadbe729 100644 --- a/sqlparser/sql.go +++ b/sqlparser/sql.go @@ -263,120 +263,120 @@ const yyPrivate = 57344 var yyTokenNames []string var yyStates []string -const yyLast = 568 +const yyLast = 563 var yyAct = []int{ - 101, 301, 170, 368, 336, 68, 172, 255, 98, 109, - 246, 211, 128, 293, 248, 187, 182, 99, 70, 195, - 87, 377, 377, 173, 3, 377, 88, 34, 35, 36, - 37, 82, 56, 58, 75, 269, 270, 271, 272, 273, - 92, 274, 275, 104, 72, 146, 147, 77, 108, 141, - 79, 114, 71, 49, 83, 50, 299, 59, 91, 105, - 106, 107, 141, 141, 93, 209, 262, 96, 209, 379, - 378, 112, 44, 376, 46, 280, 347, 127, 47, 240, - 131, 238, 322, 346, 345, 135, 52, 53, 54, 138, - 95, 143, 130, 137, 110, 111, 89, 326, 201, 174, - 76, 115, 327, 175, 298, 65, 318, 320, 247, 78, - 288, 286, 51, 239, 178, 73, 208, 199, 181, 72, - 202, 113, 72, 145, 185, 191, 190, 71, 146, 147, - 71, 247, 124, 291, 119, 126, 169, 171, 192, 342, - 189, 319, 138, 329, 93, 217, 191, 294, 205, 344, - 215, 221, 57, 206, 226, 227, 258, 230, 231, 232, - 233, 234, 235, 236, 237, 228, 69, 222, 216, 134, - 117, 146, 147, 120, 343, 198, 200, 197, 316, 93, - 93, 159, 160, 161, 72, 72, 315, 219, 220, 251, - 218, 136, 71, 253, 121, 121, 259, 242, 244, 312, - 81, 254, 314, 209, 313, 294, 250, 229, 72, 260, - 310, 188, 265, 264, 188, 311, 71, 352, 353, 140, - 331, 263, 18, 215, 123, 279, 266, 282, 283, 257, - 250, 214, 154, 155, 156, 157, 158, 159, 160, 161, - 213, 281, 34, 35, 36, 37, 93, 157, 158, 159, - 160, 161, 214, 18, 267, 84, 290, 121, 207, 183, - 363, 213, 300, 141, 287, 297, 362, 296, 104, 184, - 184, 361, 139, 108, 179, 177, 114, 215, 215, 308, - 309, 176, 86, 73, 105, 106, 107, 325, 292, 116, - 57, 278, 96, 144, 328, 374, 112, 73, 323, 321, - 72, 305, 333, 304, 204, 334, 337, 277, 332, 57, - 203, 186, 66, 375, 330, 95, 132, 338, 129, 110, - 111, 125, 122, 80, 118, 350, 115, 348, 18, 85, - 324, 64, 349, 154, 155, 156, 157, 158, 159, 160, - 161, 285, 381, 60, 138, 193, 113, 357, 359, 351, - 223, 249, 224, 225, 133, 365, 337, 62, 366, 367, - 302, 341, 369, 369, 369, 72, 370, 371, 303, 243, - 372, 104, 256, 71, 340, 307, 108, 188, 382, 114, - 67, 358, 383, 360, 384, 104, 91, 105, 106, 107, - 108, 380, 364, 114, 38, 96, 18, 39, 17, 112, - 73, 105, 106, 107, 16, 15, 14, 13, 18, 96, - 12, 194, 45, 112, 40, 41, 42, 43, 95, 261, - 196, 48, 110, 111, 89, 55, 74, 252, 108, 115, - 373, 114, 95, 354, 335, 339, 110, 111, 73, 105, - 106, 107, 306, 115, 289, 108, 180, 139, 114, 113, - 245, 112, 241, 103, 100, 73, 105, 106, 107, 18, - 19, 20, 21, 113, 139, 355, 356, 102, 112, 295, - 97, 148, 94, 317, 110, 111, 269, 270, 271, 272, - 273, 115, 274, 275, 212, 268, 210, 22, 90, 276, - 142, 110, 111, 61, 33, 63, 11, 10, 115, 9, - 8, 113, 149, 153, 151, 152, 7, 154, 155, 156, - 157, 158, 159, 160, 161, 6, 32, 5, 113, 4, - 2, 165, 166, 167, 168, 1, 162, 163, 164, 154, - 155, 156, 157, 158, 159, 160, 161, 27, 28, 29, - 0, 30, 31, 23, 24, 26, 25, 0, 150, 154, - 155, 156, 157, 158, 159, 160, 161, 284, 0, 0, - 154, 155, 156, 157, 158, 159, 160, 161, + 103, 100, 174, 302, 367, 336, 176, 68, 298, 258, + 129, 111, 89, 253, 138, 216, 86, 101, 70, 190, + 198, 150, 151, 90, 376, 376, 177, 3, 34, 35, + 36, 37, 82, 58, 376, 94, 273, 274, 275, 276, + 277, 75, 278, 279, 72, 71, 145, 77, 145, 250, + 79, 106, 145, 49, 83, 50, 110, 243, 245, 116, + 60, 265, 214, 132, 347, 95, 93, 107, 108, 109, + 346, 345, 378, 377, 76, 98, 78, 128, 51, 114, + 246, 127, 375, 324, 329, 136, 254, 284, 72, 142, + 131, 73, 149, 147, 328, 204, 293, 254, 97, 296, + 291, 178, 112, 113, 91, 179, 244, 59, 125, 117, + 213, 320, 322, 140, 202, 150, 151, 205, 183, 88, + 186, 72, 71, 72, 71, 342, 194, 193, 189, 115, + 331, 299, 248, 173, 175, 56, 187, 195, 44, 261, + 46, 192, 69, 135, 47, 223, 321, 208, 95, 222, + 194, 52, 53, 54, 209, 226, 220, 212, 231, 232, + 344, 235, 236, 237, 238, 239, 240, 241, 242, 221, + 343, 227, 201, 203, 200, 233, 318, 66, 161, 162, + 163, 164, 165, 247, 95, 95, 224, 225, 317, 72, + 71, 163, 164, 165, 249, 251, 256, 150, 151, 262, + 314, 119, 255, 121, 122, 315, 316, 191, 122, 257, + 263, 72, 71, 81, 140, 72, 269, 234, 267, 299, + 312, 137, 245, 106, 266, 313, 191, 260, 110, 144, + 220, 116, 286, 287, 283, 270, 140, 352, 93, 107, + 108, 109, 34, 35, 36, 37, 285, 98, 290, 268, + 271, 114, 326, 95, 124, 158, 159, 160, 161, 162, + 163, 164, 165, 292, 118, 300, 295, 301, 84, 122, + 97, 210, 362, 145, 112, 113, 91, 361, 18, 360, + 18, 117, 87, 220, 220, 219, 307, 310, 311, 180, + 297, 327, 184, 188, 218, 106, 182, 181, 87, 330, + 110, 115, 282, 116, 87, 333, 334, 337, 219, 59, + 73, 107, 108, 109, 373, 73, 338, 218, 281, 98, + 148, 325, 323, 114, 273, 274, 275, 276, 277, 348, + 278, 279, 374, 380, 349, 306, 59, 305, 207, 206, + 143, 57, 97, 133, 247, 130, 112, 113, 358, 356, + 126, 123, 80, 117, 364, 337, 120, 350, 365, 332, + 366, 368, 368, 368, 72, 71, 369, 370, 85, 65, + 18, 371, 289, 115, 106, 196, 357, 381, 359, 110, + 18, 382, 116, 383, 38, 228, 134, 229, 230, 73, + 107, 108, 109, 139, 63, 61, 303, 341, 98, 304, + 110, 259, 114, 116, 40, 41, 42, 43, 340, 309, + 73, 107, 108, 109, 191, 55, 67, 110, 379, 180, + 116, 97, 363, 114, 18, 112, 113, 73, 107, 108, + 109, 39, 117, 17, 16, 15, 180, 14, 13, 12, + 114, 18, 19, 20, 21, 197, 112, 113, 45, 264, + 199, 48, 115, 117, 74, 153, 157, 155, 156, 141, + 372, 353, 335, 112, 113, 339, 308, 294, 185, 22, + 117, 252, 105, 115, 169, 170, 171, 172, 102, 166, + 167, 168, 104, 211, 99, 152, 96, 354, 355, 319, + 115, 217, 272, 215, 92, 351, 280, 146, 32, 62, + 33, 154, 158, 159, 160, 161, 162, 163, 164, 165, + 158, 159, 160, 161, 162, 163, 164, 165, 64, 27, + 28, 29, 11, 30, 31, 23, 24, 26, 25, 158, + 159, 160, 161, 162, 163, 164, 165, 288, 10, 9, + 158, 159, 160, 161, 162, 163, 164, 165, 158, 159, + 160, 161, 162, 163, 164, 165, 8, 7, 6, 5, + 4, 2, 1, } var yyPact = []int{ - 454, -1000, -1000, 193, -1000, -1000, -1000, -1000, -1000, -1000, + 436, -1000, -1000, 193, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -21, -42, 19, -7, -1000, -1000, -1000, - -1000, 255, 255, 391, 326, -1000, -1000, -1000, 339, -1000, - 302, 277, 371, 80, -64, 6, 255, -1000, 16, 255, - -1000, 288, -67, 255, -67, 300, 238, -1000, -1000, -1000, - -1000, 23, -1000, 250, 277, 291, 58, 277, 142, 287, - -1000, 179, -1000, 56, 286, 68, 255, -1000, 283, -1000, - -16, 281, 334, 105, 255, 277, 420, 210, -1000, -1000, - 274, 47, 106, 481, -1000, 365, 248, -1000, -1000, -1000, - 420, 237, 231, -1000, 230, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, 420, -1000, 226, 262, 276, - 367, 262, -1000, 420, 255, -1000, 325, -81, -1000, 85, - -1000, 275, -1000, -1000, 269, -1000, 225, 15, 461, 403, - 196, 23, -1000, -1000, 255, 117, 365, 365, 420, 228, - 329, 420, 420, 140, 420, 420, 420, 420, 420, 420, - 420, 420, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, - 481, -20, 12, -22, 481, -1000, 351, 23, -1000, 391, - 29, 461, 323, 262, 262, 204, -1000, 359, 365, -1000, - 461, -1000, -1000, -1000, 92, 255, -1000, -30, -1000, -1000, - -1000, -1000, -1000, -1000, -1000, -1000, 323, 262, -1000, 420, - 201, 422, 272, 217, -1, -1000, -1000, -1000, -1000, -1000, - -1000, 461, -1000, 228, 420, 420, 461, 492, -1000, 316, - 176, 176, 176, 108, 108, -1000, -1000, -1000, -1000, -1000, - -1000, -1000, 10, 23, 9, 52, -1000, 365, 83, 228, - 193, 141, 3, -1000, 359, 345, 354, 106, 268, -1000, - -1000, 266, -1000, -1000, 142, 461, 364, 196, 196, -1000, - -1000, 156, 145, 148, 132, 124, 44, -1000, 264, -19, - 263, -1000, 461, 265, 420, -1000, -1000, -4, -1000, 20, - -1000, 420, 63, -1000, 284, 167, -1000, -1000, -1000, 262, - 345, -1000, 420, 420, -1000, -1000, 362, 347, 422, 75, - -1000, 120, -1000, 95, -1000, -1000, -1000, -1000, -10, -11, - -18, -1000, -1000, -1000, 420, 461, -1000, -1000, 461, 420, - 294, 228, -1000, -1000, 164, 165, -1000, 439, -1000, 359, - 365, 420, 365, -1000, -1000, 227, 222, 216, 461, 461, - 385, -1000, 420, 420, -1000, -1000, -1000, 345, 106, 150, - 106, 255, 255, 255, 262, 461, -1000, 279, -28, -1000, - -31, -32, 142, -1000, 384, 321, -1000, 255, -1000, -1000, - -1000, 255, -1000, 255, -1000, + -1000, -1000, -1000, 45, -42, -15, 58, -1000, -1000, -1000, + -1000, 306, 274, 419, 378, -1000, -1000, -1000, 376, -1000, + 340, 306, 407, 56, -57, -20, 274, -1000, -17, 274, + -1000, 317, -66, 274, -66, 339, 254, 43, -1000, -1000, + -1000, -1000, 203, -1000, 225, 306, 323, 306, 151, 316, + -1000, 209, -1000, 32, 315, 14, 274, -1000, 310, -1000, + -33, 308, 366, 79, 274, 306, 365, 280, 305, 220, + -1000, -1000, 301, 16, 132, 434, -1000, 354, 275, -1000, + -1000, -1000, 392, 253, 252, -1000, 248, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, -1000, -1000, -1000, 392, -1000, 260, + 280, 404, 280, -1000, 392, 274, -1000, 355, -80, -1000, + 82, -1000, 304, -1000, -1000, 303, -1000, 238, -1000, 245, + 193, 9, -1000, -1000, 250, 203, -1000, -1000, 274, 72, + 354, 354, 392, 245, 364, 392, 392, 150, 392, 392, + 392, 392, 392, 392, 392, 392, -1000, -1000, -1000, -1000, + -1000, -1000, -1000, -1000, 434, -44, 5, -21, 434, -1000, + 375, 31, 203, -1000, 419, 7, 480, 365, 280, 216, + 388, 354, -1000, 480, -1000, -1000, -1000, 75, 274, -1000, + -35, -1000, -1000, -1000, -1000, -1000, -1000, -1000, -1000, 365, + 280, 196, -1000, -1000, 280, 197, 270, 283, 273, 11, + -1000, -1000, -1000, -1000, -1000, -1000, 480, -1000, 245, 392, + 392, 480, 472, -1000, 347, 107, 107, 107, 118, 118, + -1000, -1000, -1000, -1000, -1000, 392, -1000, 480, -1000, -1, + 203, -5, 18, -1000, 354, 67, 155, 388, 381, 385, + 132, 302, -1000, -1000, 300, -1000, -1000, 151, 245, -1000, + 398, 250, 250, -1000, -1000, 166, 146, 152, 134, 122, + 49, -1000, 287, -18, 286, -1000, 480, 187, 392, -1000, + 480, -1000, -7, -1000, 2, -1000, 392, 50, -1000, 329, + -1000, 381, -1000, 392, 392, -1000, -1000, -1000, 396, 383, + 270, 61, -1000, 116, -1000, 106, -1000, -1000, -1000, -1000, + -23, -24, -30, -1000, -1000, -1000, 392, 480, -1000, -1000, + 480, 392, 326, -1000, 442, 184, -1000, 461, -1000, 388, + 354, 392, 354, -1000, -1000, 235, 233, 228, 480, 480, + 415, 392, 392, -1000, -1000, -1000, 381, 132, 169, 132, + 274, 274, 274, 280, 480, -1000, 298, -19, -1000, -28, + -29, 151, -1000, 411, 312, -1000, 274, -1000, -1000, -1000, + 274, -1000, 274, -1000, } var yyPgo = []int{ - 0, 525, 520, 23, 519, 517, 515, 506, 500, 499, - 497, 496, 394, 495, 494, 493, 20, 26, 490, 489, - 488, 486, 11, 485, 484, 105, 473, 3, 15, 40, - 472, 471, 14, 470, 2, 17, 6, 469, 467, 9, - 454, 8, 453, 450, 10, 446, 444, 442, 435, 7, - 434, 4, 433, 1, 430, 16, 427, 13, 5, 18, - 200, 426, 421, 420, 419, 412, 411, 0, 12, 410, - 407, 406, 405, 404, 398, 397, + 0, 562, 561, 26, 560, 559, 558, 557, 556, 539, + 538, 522, 384, 518, 500, 499, 12, 23, 497, 496, + 494, 493, 15, 492, 491, 135, 489, 4, 19, 35, + 486, 485, 14, 484, 2, 17, 6, 483, 482, 11, + 478, 1, 472, 471, 13, 468, 467, 466, 465, 9, + 462, 5, 461, 3, 460, 16, 459, 8, 7, 18, + 213, 454, 451, 450, 449, 448, 445, 0, 10, 439, + 438, 437, 435, 434, 433, 431, } var yyR1 = []int{ @@ -407,7 +407,7 @@ var yyR2 = []int{ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 12, 3, 7, 7, 6, 6, 8, 7, 3, 4, 1, 1, - 1, 5, 2, 5, 8, 4, 6, 7, 4, 5, + 1, 4, 2, 5, 8, 4, 6, 7, 4, 5, 4, 5, 5, 0, 2, 0, 2, 1, 2, 1, 1, 1, 0, 1, 1, 3, 1, 2, 3, 1, 1, 0, 1, 2, 1, 3, 3, 3, 3, 5, @@ -433,40 +433,40 @@ var yyChk = []int{ 7, 8, 33, 89, 90, 92, 91, 83, 84, 85, 87, 88, 62, -14, 49, 50, 51, 52, -12, -75, -12, -12, -12, -12, 93, -65, 95, 99, -62, 95, - 97, 93, 93, 94, 95, -12, -67, 35, -67, -3, - 17, -15, 18, -13, 29, -25, 35, 9, -58, 86, + 97, 93, 93, 94, 95, -12, -25, 35, -67, 35, + -3, 17, -15, 18, -13, 29, -25, 9, -58, 86, -59, -41, -67, 35, -61, 98, 94, -67, 93, -67, - 35, -60, 98, -67, -60, 29, 44, -16, -17, 73, - -20, 35, -29, -34, -30, 67, 44, -33, -41, -35, - -40, -67, -38, -42, 20, 36, 37, 38, 25, -39, - 71, 72, 48, 98, 28, 78, 39, -25, 33, 76, - -25, 53, 35, 45, 76, 35, 67, -67, -68, 35, - -68, 96, 35, 20, 64, -67, -25, -36, -34, 44, - 9, 53, -18, -67, 19, 76, 65, 66, -31, 21, - 67, 23, 24, 22, 68, 69, 70, 71, 72, 73, - 74, 75, 45, 46, 47, 40, 41, 42, 43, -29, - -34, -29, -36, -3, -34, -34, 44, 44, -39, 44, - -45, -34, -55, 33, 44, -58, 35, -28, 10, -59, - -34, -67, -68, 20, -66, 100, -63, 92, 90, 32, - 91, 13, 35, 35, 35, -68, -55, 33, 101, 53, - -21, -22, -24, 44, 35, -39, -17, -67, 73, -29, - -29, -34, -35, 21, 23, 24, -34, -34, 25, 67, - -34, -34, -34, -34, -34, -34, -34, -34, 101, 101, - 101, 101, -16, 18, -16, -43, -44, 79, -32, 28, - -3, -58, -56, -41, -28, -49, 13, -29, 64, -67, - -68, -64, 96, -32, -58, -34, -28, 53, -23, 54, - 55, 56, 57, 58, 60, 61, -19, 35, 19, -22, - 76, -35, -34, -34, 65, 25, 101, -16, 101, -46, - -44, 81, -29, -57, 64, -37, -35, -57, 101, 53, - -49, -53, 15, 14, 35, 35, -47, 11, -22, -22, - 54, 59, 54, 59, 54, 54, 54, -26, 62, 97, - 63, 35, 101, 35, 65, -34, 101, 82, -34, 80, - 30, 53, -41, -53, -34, -50, -51, -34, -68, -48, + 35, -60, 98, -67, -60, 29, -55, 44, 76, -16, + -17, 73, -20, 35, -29, -34, -30, 67, 44, -33, + -41, -35, -40, -67, -38, -42, 20, 36, 37, 38, + 25, -39, 71, 72, 48, 98, 28, 78, 39, -25, + 33, -25, 53, 35, 45, 76, 35, 67, -67, -68, + 35, -68, 96, 35, 20, 64, -67, -25, -32, 28, + -3, -56, -41, 35, 9, 53, -18, -67, 19, 76, + 65, 66, -31, 21, 67, 23, 24, 22, 68, 69, + 70, 71, 72, 73, 74, 75, 45, 46, 47, 40, + 41, 42, 43, -29, -34, -29, -36, -3, -34, -34, + 44, 44, 44, -39, 44, -45, -34, -55, 33, -58, + -28, 10, -59, -34, -67, -68, 20, -66, 100, -63, + 92, 90, 32, 91, 13, 35, 35, 35, -68, -55, + 33, -37, -35, 101, 53, -21, -22, -24, 44, 35, + -39, -17, -67, 73, -29, -29, -34, -35, 21, 23, + 24, -34, -34, 25, 67, -34, -34, -34, -34, -34, + -34, -34, -34, 101, 101, 53, 101, -34, 101, -16, + 18, -16, -43, -44, 79, -32, -58, -28, -49, 13, + -29, 64, -67, -68, -64, 96, -32, -58, 53, -41, + -28, 53, -23, 54, 55, 56, 57, 58, 60, 61, + -19, 35, 19, -22, 76, -35, -34, -34, 65, 25, + -34, 101, -16, 101, -46, -44, 81, -29, -57, 64, + -57, -49, -53, 15, 14, 35, 35, -35, -47, 11, + -22, -22, 54, 59, 54, 59, 54, 54, 54, -26, + 62, 97, 63, 35, 101, 35, 65, -34, 101, 82, + -34, 80, 30, -53, -34, -50, -51, -34, -68, -48, 12, 14, 64, 54, 54, 94, 94, 94, -34, -34, - 31, -35, 53, 53, -52, 26, 27, -49, -29, -36, - -29, 44, 44, 44, 7, -34, -51, -53, -27, -67, - -27, -27, -58, -54, 16, 34, 101, 53, 101, 101, - 7, 21, -67, -67, -67, + 31, 53, 53, -52, 26, 27, -49, -29, -36, -29, + 44, 44, 44, 7, -34, -51, -53, -27, -67, -27, + -27, -58, -54, 16, 34, 101, 53, 101, 101, 7, + 21, -67, -67, -67, } var yyDef = []int{ @@ -475,40 +475,40 @@ var yyDef = []int{ 43, 43, 43, 202, 193, 0, 0, 28, 29, 30, 43, 0, 0, 0, 47, 49, 50, 51, 52, 45, 0, 0, 0, 0, 191, 0, 0, 203, 0, 0, - 194, 0, 189, 0, 189, 0, 0, 206, 32, 19, - 48, 0, 53, 44, 0, 0, 85, 0, 26, 0, + 194, 0, 189, 0, 189, 0, 180, 85, 32, 206, + 19, 48, 0, 53, 44, 0, 0, 0, 26, 0, 186, 0, 156, 206, 0, 0, 0, 207, 0, 207, - 0, 0, 0, 0, 0, 0, 0, 17, 54, 56, - 61, 206, 59, 60, 95, 0, 0, 126, 127, 128, - 0, 156, 0, 142, 0, 158, 159, 160, 161, 122, - 145, 146, 147, 143, 144, 149, 46, 180, 0, 0, - 93, 0, 27, 0, 0, 207, 0, 204, 35, 0, - 38, 0, 40, 190, 0, 207, 180, 0, 124, 0, - 0, 0, 57, 62, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, + 54, 56, 61, 206, 59, 60, 95, 0, 0, 126, + 127, 128, 0, 156, 0, 142, 0, 158, 159, 160, + 161, 122, 145, 146, 147, 143, 144, 149, 46, 180, + 0, 93, 0, 27, 0, 0, 207, 0, 204, 35, + 0, 38, 0, 40, 190, 0, 207, 180, 31, 0, + 118, 0, 182, 86, 0, 0, 57, 62, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 110, 111, 112, 113, 114, 115, 116, 98, - 0, 0, 0, 0, 124, 137, 0, 0, 109, 0, - 0, 150, 0, 0, 0, 93, 86, 166, 0, 187, - 188, 157, 33, 192, 0, 0, 207, 200, 195, 196, - 197, 198, 199, 39, 41, 42, 0, 0, 31, 0, - 93, 64, 70, 0, 82, 84, 55, 63, 58, 96, - 97, 100, 101, 0, 0, 0, 103, 0, 107, 0, - 129, 130, 131, 132, 133, 134, 135, 136, 99, 121, - 123, 138, 0, 0, 0, 154, 151, 0, 184, 0, - 118, 184, 0, 182, 166, 174, 0, 94, 0, 205, - 36, 0, 201, 22, 23, 125, 162, 0, 0, 73, - 74, 0, 0, 0, 0, 0, 87, 71, 0, 0, - 0, 102, 104, 0, 0, 108, 139, 0, 141, 0, - 152, 0, 0, 20, 0, 117, 119, 21, 181, 0, - 174, 25, 0, 0, 207, 37, 164, 0, 65, 68, - 75, 0, 77, 0, 79, 80, 81, 66, 0, 0, - 0, 72, 67, 83, 0, 105, 140, 148, 155, 0, - 0, 0, 183, 24, 175, 167, 168, 171, 34, 166, + 0, 0, 0, 0, 0, 0, 110, 111, 112, 113, + 114, 115, 116, 98, 0, 0, 0, 0, 124, 137, + 0, 0, 0, 109, 0, 0, 150, 0, 0, 93, + 166, 0, 187, 188, 157, 33, 192, 0, 0, 207, + 200, 195, 196, 197, 198, 199, 39, 41, 42, 0, + 0, 117, 119, 181, 0, 93, 64, 70, 0, 82, + 84, 55, 63, 58, 96, 97, 100, 101, 0, 0, + 0, 103, 0, 107, 0, 129, 130, 131, 132, 133, + 134, 135, 136, 99, 121, 0, 123, 124, 138, 0, + 0, 0, 154, 151, 0, 184, 184, 166, 174, 0, + 94, 0, 205, 36, 0, 201, 22, 23, 0, 183, + 162, 0, 0, 73, 74, 0, 0, 0, 0, 0, + 87, 71, 0, 0, 0, 102, 104, 0, 0, 108, + 125, 139, 0, 141, 0, 152, 0, 0, 20, 0, + 21, 174, 25, 0, 0, 207, 37, 120, 164, 0, + 65, 68, 75, 0, 77, 0, 79, 80, 81, 66, + 0, 0, 0, 72, 67, 83, 0, 105, 140, 148, + 155, 0, 0, 24, 175, 167, 168, 171, 34, 166, 0, 0, 0, 76, 78, 0, 0, 0, 106, 153, - 0, 120, 0, 0, 170, 172, 173, 174, 165, 163, - 69, 0, 0, 0, 0, 176, 169, 177, 0, 91, - 0, 0, 185, 18, 0, 0, 88, 0, 89, 90, - 178, 0, 92, 0, 179, + 0, 0, 0, 170, 172, 173, 174, 165, 163, 69, + 0, 0, 0, 0, 176, 169, 177, 0, 91, 0, + 0, 185, 18, 0, 0, 88, 0, 89, 90, 178, + 0, 92, 0, 179, } var yyTok1 = []int{ @@ -890,7 +890,7 @@ yydefault: case 31: //line sql.y:277 { - yyVAL.statement = &Admin{Name: yyS[yypt-3].bytes, Values: yyS[yypt-1].valExprs} + yyVAL.statement = &Admin{Region: yyS[yypt-2].tableName, Columns: yyS[yypt-1].columns, Rows: yyS[yypt-0].insRows} } case 32: //line sql.y:283 diff --git a/sqlparser/sql.y b/sqlparser/sql.y index 78c49b08..de41c787 100644 --- a/sqlparser/sql.y +++ b/sqlparser/sql.y @@ -273,9 +273,9 @@ rollback_statement: } admin_statement: - ADMIN sql_id '(' value_expression_list ')' + ADMIN dml_table_expression column_list_opt row_list { - $$ = &Admin{Name : $2, Values : $4} + $$ = &Admin{Region : $2, Columns : $3,Rows:$4} } use_statement: From 9ef8e1124aaf7caaa44d570187dbca5e61a60306 Mon Sep 17 00:00:00 2001 From: flike Date: Sun, 2 Aug 2015 21:19:08 +0800 Subject: [PATCH 02/13] add document of admin command --- README_ZH.md | 4 +++- doc/KingDoc/admin_command_introduce.md | 24 ++++++++++++++++++++++++ doc/KingDoc/change_log_CN.md | 4 ++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 doc/KingDoc/admin_command_introduce.md diff --git a/README_ZH.md b/README_ZH.md index f4d27b4c..525aec09 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -23,7 +23,9 @@ kingshard是一个由Go开发高性能MySQL Proxy项目,kingshard在满足基 [4.功能FAQ](./doc/KingDoc/function_FAQ.md) -[5.ChangeLog](./doc/KingDoc/change_log_CN.md) +[5.管理端命令介绍](./doc/KingDoc/admin_command_introduce.md) + +[6.ChangeLog](./doc/KingDoc/change_log_CN.md) ## 反馈 目前kingshard还是1.0版本,比较核心的功能已经实现了。但还有很多地方不完善。如果您在使用kingshard的过程中发现BUG或者有新的功能需求,非常欢迎您发邮件至flikecn#126.com与作者取得联系,或者加入QQ群(147926796)交流。 diff --git a/doc/KingDoc/admin_command_introduce.md b/doc/KingDoc/admin_command_introduce.md new file mode 100644 index 00000000..91306b67 --- /dev/null +++ b/doc/KingDoc/admin_command_introduce.md @@ -0,0 +1,24 @@ +# 管理端命令 + +kingshard的管理端口复用了工作端口,通过特定的关键字来标示,目前支持对后端DB常用的管理操作: + +``` +#添加一个新的slave到node1 +admin node(opt,node,k,v) values(‘add’,’node1’,’slave’,’127.0.0.1:3306’) + +#删除node1上的一个slave。注意:只能删除slave,不能删除master +admin node(opt,node,k,v) values(‘del’,’node1’,’slave’,’127.0.0.1:3306’) + +#将一个slave设置为下线状态 +admin node(opt,node,k,v) values(‘down’,’node1’,’slave’,’127.0.0.1:3306’) + +#将一个slave设置为上线状态 +admin node(opt,node,k,v) values(‘up’,’node1’,’slave’,’127.0.0.1:3306’) + +#将master设置为下线状态 +admin node(opt,node,k,v) values(‘down’,’node1’,’master’,’127.0.0.1:3306’) + +#将master设置为上线状态 +admin node(opt,node,k,v) values(‘up’,’node1’,’master’,’127.0.0.1:3306’) + +``` \ No newline at end of file diff --git a/doc/KingDoc/change_log_CN.md b/doc/KingDoc/change_log_CN.md index 88c328c0..c91fe519 100644 --- a/doc/KingDoc/change_log_CN.md +++ b/doc/KingDoc/change_log_CN.md @@ -1,5 +1,9 @@ # ChangeLog +## 2015-08-02 +### feature +1. 增加管理后端DB的命令。 + ## 2015-07-26 ### feature 1. 增加多个slave支持,并且可以设置每个slave的负载权重。 From 0535048c463cfc983f754d03c5e8a2ac888bea50 Mon Sep 17 00:00:00 2001 From: Gavin Tao Date: Mon, 3 Aug 2015 02:10:58 +0800 Subject: [PATCH 03/13] fix call mysql_list_fields error in php code; Signed-off-by: Gavin Tao --- proxy/server/conn_select.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proxy/server/conn_select.go b/proxy/server/conn_select.go index 8f8a585e..5c561e75 100644 --- a/proxy/server/conn_select.go +++ b/proxy/server/conn_select.go @@ -95,7 +95,7 @@ func (c *ClientConn) handleFieldList(data []byte) error { } defer co.Close() - if err = co.UseDB(c.schema.db); err != nil { + if err = co.UseDB(c.db); err != nil { return err } From 90585c9964dbe9a9055d85628637994a33ad8c45 Mon Sep 17 00:00:00 2001 From: flike Date: Tue, 4 Aug 2015 21:02:10 +0800 Subject: [PATCH 04/13] add admin command 'show' --- core/errors/errors.go | 10 ++- proxy/server/conn_admin.go | 137 ++++++++++++++++++++++++++++++++- proxy/server/conn_resultset.go | 1 + proxy/server/conn_show.go | 69 ----------------- 4 files changed, 142 insertions(+), 75 deletions(-) diff --git a/core/errors/errors.go b/core/errors/errors.go index 2e5d2851..94fa00df 100644 --- a/core/errors/errors.go +++ b/core/errors/errors.go @@ -11,10 +11,12 @@ var ( ErrNoMasterDb = errors.New("no master database") ErrNoSlaveDb = errors.New("no slave database") ErrNoDatabase = errors.New("no database") - ErrMasterDown = errors.New("master is down") - ErrSlaveDown = errors.New("slave is down") - ErrAddressNull = errors.New("address is nil") - ErrCmdUnsupport = errors.New("command unsupport") + + ErrMasterDown = errors.New("master is down") + ErrSlaveDown = errors.New("slave is down") + + ErrAddressNull = errors.New("address is nil") + ErrCmdUnsupport = errors.New("command unsupport") ErrLocationsCount = errors.New("locations count are not equal") ErrNoCriteria = errors.New("plan have no criteria") diff --git a/proxy/server/conn_admin.go b/proxy/server/conn_admin.go index dae267b5..7124db2b 100644 --- a/proxy/server/conn_admin.go +++ b/proxy/server/conn_admin.go @@ -4,13 +4,18 @@ import ( "fmt" . "github.com/flike/kingshard/core/errors" "github.com/flike/kingshard/core/golog" + "github.com/flike/kingshard/mysql" "github.com/flike/kingshard/sqlparser" "strings" + "time" ) const ( - Master = "master" - Slave = "slave" + Master = "master" + Slave = "slave" + Proxy = "proxy" + Config = "config" + ServerRegion = "server" NodeRegion = "node" @@ -18,6 +23,7 @@ const ( ADMIN_OPT_DEL = "del" ADMIN_OPT_UP = "up" ADMIN_OPT_DOWN = "down" + ADMIN_OPT_SHOW = "show" ) var cmdServerOrder = []string{"opt", "k", "v"} @@ -83,6 +89,45 @@ func (c *ClientConn) handleNodeCmd(rows sqlparser.InsertRows) error { return err } +func (c *ClientConn) handleServerCmd(rows sqlparser.InsertRows) (*mysql.Resultset, error) { + var err error + var result *mysql.Resultset + var opt, k, v string + + vals := rows.(sqlparser.Values) + if len(vals) == 0 { + return nil, ErrCmdUnsupport + } + + tuple := vals[0].(sqlparser.ValTuple) + if len(tuple) != len(cmdServerOrder) { + return nil, ErrCmdUnsupport + } + + opt = sqlparser.String(tuple[0]) + opt = strings.Trim(opt, "'") + + k = sqlparser.String(tuple[1]) + k = strings.Trim(k, "'") + + v = sqlparser.String(tuple[2]) + v = strings.Trim(v, "'") + + switch strings.ToLower(opt) { + case ADMIN_OPT_SHOW: + result, err = c.handleAdminShow(k, v) + default: + err = ErrCmdUnsupport + golog.Error("ClientConn", "handleNodeCmd", err.Error(), + c.connectionId, "opt", opt) + } + if err != nil { + return nil, err + } + + return result, nil +} + func (c *ClientConn) AddDatabase(nodeName string, role string, addr string) error { //can not add a new master database if role != Slave { @@ -130,6 +175,8 @@ func (c *ClientConn) checkCmdOrder(region string, columns sqlparser.Columns) err switch region { case NodeRegion: cmdOrder = cmdNodeOrder + case ServerRegion: + cmdOrder = cmdServerOrder default: return ErrCmdUnsupport } @@ -146,6 +193,8 @@ func (c *ClientConn) checkCmdOrder(region string, columns sqlparser.Columns) err func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { var err error + var result *mysql.Resultset + region := sqlparser.String(admin.Region) err = c.checkCmdOrder(region, admin.Columns) @@ -156,6 +205,8 @@ func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { switch strings.ToLower(region) { case NodeRegion: err = c.handleNodeCmd(admin.Rows) + case ServerRegion: + result, err = c.handleServerCmd(admin.Rows) default: return fmt.Errorf("admin %s not supported now", region) } @@ -166,5 +217,87 @@ func (c *ClientConn) handleAdmin(admin *sqlparser.Admin) error { return err } + if result != nil { + return c.writeResultset(c.status, result) + } + return c.writeOK(nil) } + +func (c *ClientConn) handleAdminShow(k, v string) (*mysql.Resultset, error) { + if len(k) == 0 || len(v) == 0 { + return nil, ErrCmdUnsupport + } + if k == Proxy && v == Config { + return c.handleShowProxyConfig() + } + return nil, ErrCmdUnsupport +} + +func (c *ClientConn) handleShowProxyConfig() (*mysql.Resultset, error) { + var names []string = []string{"Section", "Key", "Value"} + var rows [][]string + const ( + Column = 3 + ) + + rows = append(rows, []string{"Global_Config", "Addr", c.proxy.cfg.Addr}) + rows = append(rows, []string{"Global_Config", "User", c.proxy.cfg.User}) + rows = append(rows, []string{"Global_Config", "Password", c.proxy.cfg.Password}) + rows = append(rows, []string{"Global_Config", "LogLevel", c.proxy.cfg.LogLevel}) + rows = append(rows, []string{"Global_Config", "Schemas_Count", fmt.Sprintf("%d", len(c.proxy.schemas))}) + rows = append(rows, []string{"Global_Config", "Nodes_Count", fmt.Sprintf("%d", len(c.proxy.nodes))}) + + for db, schema := range c.proxy.schemas { + rows = append(rows, []string{"Schemas", "DB", db}) + + var nodeNames []string + var nodeRows [][]string + for name, node := range schema.nodes { + nodeNames = append(nodeNames, name) + var nodeSection = fmt.Sprintf("Schemas[%s]-Node[ %v ]", db, name) + + if node.Master != nil { + nodeRows = append(nodeRows, []string{nodeSection, "Master", node.Master.String()}) + } + + if node.Slave != nil { + nodeRows = append(nodeRows, []string{nodeSection, "Slave", node.FormatSlave()}) + } + nodeRows = append(nodeRows, []string{nodeSection, "Last_Master_Ping", fmt.Sprintf("%v", time.Unix(node.LastMasterPing, 0))}) + + nodeRows = append(nodeRows, []string{nodeSection, "Last_Slave_Ping", fmt.Sprintf("%v", time.Unix(node.LastSlavePing, 0))}) + + nodeRows = append(nodeRows, []string{nodeSection, "down_after_noalive", fmt.Sprintf("%v", node.DownAfterNoAlive)}) + + } + rows = append(rows, []string{fmt.Sprintf("Schemas[%s]", db), "Nodes_List", strings.Join(nodeNames, ",")}) + + var defaultRule = schema.rule.DefaultRule + if defaultRule.DB == db { + if defaultRule.DB == db { + rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Default", db), + "Default_Table", defaultRule.String()}) + } + } + for tb, r := range schema.rule.Rules { + if r.DB == db { + rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Table", db), + fmt.Sprintf("Table[ %s ]", tb), r.String()}) + } + } + + rows = append(rows, nodeRows...) + + } + + var values [][]interface{} = make([][]interface{}, len(rows)) + for i := range rows { + values[i] = make([]interface{}, Column) + for j := range rows[i] { + values[i][j] = rows[i][j] + } + } + + return c.buildResultset(names, values) +} diff --git a/proxy/server/conn_resultset.go b/proxy/server/conn_resultset.go index e0698563..18369d3b 100644 --- a/proxy/server/conn_resultset.go +++ b/proxy/server/conn_resultset.go @@ -76,6 +76,7 @@ func (c *ClientConn) buildResultset(names []string, values [][]interface{}) (*Re var row []byte for j, value := range vs { + //列的定义 if i == 0 { field := &Field{} r.Fields[j] = field diff --git a/proxy/server/conn_show.go b/proxy/server/conn_show.go index eeb035e5..86b54764 100644 --- a/proxy/server/conn_show.go +++ b/proxy/server/conn_show.go @@ -8,7 +8,6 @@ import ( "github.com/flike/kingshard/sqlparser" "sort" "strings" - "time" ) func (c *ClientConn) handleShow(sql string, stmt *sqlparser.Show) error { @@ -105,74 +104,6 @@ func (c *ClientConn) handleShowProxy(sql string, stmt *sqlparser.Show) (*Results return r, err } -func (c *ClientConn) handleShowProxyConfig() (*Resultset, error) { - var names []string = []string{"Section", "Key", "Value"} - var rows [][]string - const ( - Column = 3 - ) - - rows = append(rows, []string{"Global_Config", "Addr", c.proxy.cfg.Addr}) - rows = append(rows, []string{"Global_Config", "User", c.proxy.cfg.User}) - rows = append(rows, []string{"Global_Config", "Password", c.proxy.cfg.Password}) - rows = append(rows, []string{"Global_Config", "LogLevel", c.proxy.cfg.LogLevel}) - rows = append(rows, []string{"Global_Config", "Schemas_Count", fmt.Sprintf("%d", len(c.proxy.schemas))}) - rows = append(rows, []string{"Global_Config", "Nodes_Count", fmt.Sprintf("%d", len(c.proxy.nodes))}) - - for db, schema := range c.proxy.schemas { - rows = append(rows, []string{"Schemas", "DB", db}) - - var nodeNames []string - var nodeRows [][]string - for name, node := range schema.nodes { - nodeNames = append(nodeNames, name) - var nodeSection = fmt.Sprintf("Schemas[%s]-Node[ %v ]", db, name) - - if node.Master != nil { - nodeRows = append(nodeRows, []string{nodeSection, "Master", node.Master.String()}) - } - - if node.Slave != nil { - nodeRows = append(nodeRows, []string{nodeSection, "Slave", node.FormatSlave()}) - } - nodeRows = append(nodeRows, []string{nodeSection, "Last_Master_Ping", fmt.Sprintf("%v", time.Unix(node.LastMasterPing, 0))}) - - nodeRows = append(nodeRows, []string{nodeSection, "Last_Slave_Ping", fmt.Sprintf("%v", time.Unix(node.LastSlavePing, 0))}) - - nodeRows = append(nodeRows, []string{nodeSection, "down_after_noalive", fmt.Sprintf("%v", node.DownAfterNoAlive)}) - - } - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]", db), "Nodes_List", strings.Join(nodeNames, ",")}) - - var defaultRule = schema.rule.DefaultRule - if defaultRule.DB == db { - if defaultRule.DB == db { - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Default", db), - "Default_Table", defaultRule.String()}) - } - } - for tb, r := range schema.rule.Rules { - if r.DB == db { - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Table", db), - fmt.Sprintf("Table[ %s ]", tb), r.String()}) - } - } - - rows = append(rows, nodeRows...) - - } - - var values [][]interface{} = make([][]interface{}, len(rows)) - for i := range rows { - values[i] = make([]interface{}, Column) - for j := range rows[i] { - values[i][j] = rows[i][j] - } - } - - return c.buildResultset(names, values) -} - func (c *ClientConn) handleShowProxyStatus(sql string, stmt *sqlparser.Show) (*Resultset, error) { // TODO: handle like_or_where expr return nil, nil From 463eb33db4a7fd5412cd9952be86760ed19b702d Mon Sep 17 00:00:00 2001 From: flike Date: Tue, 4 Aug 2015 21:13:36 +0800 Subject: [PATCH 05/13] modify doc --- doc/KingDoc/admin_command_introduce.md | 11 ++++++++++- doc/KingDoc/change_log_CN.md | 5 +++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/KingDoc/admin_command_introduce.md b/doc/KingDoc/admin_command_introduce.md index 91306b67..de4d2125 100644 --- a/doc/KingDoc/admin_command_introduce.md +++ b/doc/KingDoc/admin_command_introduce.md @@ -1,6 +1,8 @@ # 管理端命令 -kingshard的管理端口复用了工作端口,通过特定的关键字来标示,目前支持对后端DB常用的管理操作: +kingshard的管理端口复用了工作端口,通过特定的关键字来标示,目前支持对后端DB常用的管理操作。 + +## 平滑上(下)线后端DB ``` #添加一个新的slave到node1 @@ -21,4 +23,11 @@ admin node(opt,node,k,v) values(‘down’,’node1’,’master’,’127.0.0.1 #将master设置为上线状态 admin node(opt,node,k,v) values(‘up’,’node1’,’master’,’127.0.0.1:3306’) +``` + +## 查看kingshard配置 + +``` +admin server(opt,k,v) values('show','proxy','config') + ``` \ No newline at end of file diff --git a/doc/KingDoc/change_log_CN.md b/doc/KingDoc/change_log_CN.md index c91fe519..bb88d4a8 100644 --- a/doc/KingDoc/change_log_CN.md +++ b/doc/KingDoc/change_log_CN.md @@ -1,5 +1,10 @@ # ChangeLog + +## 2015-08-04 +### feature +1. 增加查看proxy配置的命令。 + ## 2015-08-02 ### feature 1. 增加管理后端DB的命令。 From 1f4a56c0b731776597abd523cd0d752c42598280 Mon Sep 17 00:00:00 2001 From: flike Date: Thu, 6 Aug 2015 19:40:25 +0800 Subject: [PATCH 06/13] add show command --- backend/db.go | 22 ++++- backend/node.go | 15 +-- config/config.go | 2 +- proxy/router/router.go | 15 +-- proxy/server/conn_admin.go | 195 ++++++++++++++++++++++++++++--------- 5 files changed, 179 insertions(+), 70 deletions(-) diff --git a/backend/db.go b/backend/db.go index 80701cea..80a37f06 100644 --- a/backend/db.go +++ b/backend/db.go @@ -2,7 +2,6 @@ package backend import ( "container/list" - "fmt" . "github.com/flike/kingshard/mysql" "sync" "sync/atomic" @@ -48,9 +47,24 @@ func (db *DB) Addr() string { return db.addr } -func (db *DB) String() string { - return fmt.Sprintf("%s:%s@%s/%s?maxIdleConns=%v", - db.user, db.password, db.addr, db.db, db.maxIdleConns) +func (db *DB) State() string { + var state string + switch db.state { + case Up: + state = "up" + case Down: + state = "down" + case Unknown: + state = "unknow" + } + return state +} + +func (db *DB) IdleConnCount() int { + db.Lock() + defer db.Unlock() + + return db.idleConns.Len() } func (db *DB) Close() error { diff --git a/backend/node.go b/backend/node.go index 83cd8ca5..d7bd6fc0 100644 --- a/backend/node.go +++ b/backend/node.go @@ -42,7 +42,7 @@ func (n *Node) Run() { n.checkMaster() n.checkSlave() - t := time.NewTicker(3000 * time.Second) + t := time.NewTicker(30 * time.Second) defer t.Stop() n.LastMasterPing = time.Now().Unix() @@ -60,15 +60,6 @@ func (n *Node) String() string { return n.Cfg.Name } -func (n *Node) FormatSlave() string { - s := make([]byte, 0, 16) - for _, v := range n.Slave { - s = append(s, []byte(v.addr)...) - s = append(s, []byte("\n")...) - } - return string(s) -} - func (n *Node) GetMasterConn() (*BackendConn, error) { db := n.Master if db == nil { @@ -113,6 +104,8 @@ func (n *Node) checkMaster() { golog.Error("Node", "checkMaster", "Ping", 0, "db.Addr", db.Addr(), "error", err.Error()) } else { n.LastMasterPing = time.Now().Unix() + atomic.StoreInt32(&(db.state), Up) + return } if int64(n.DownAfterNoAlive) > 0 && time.Now().Unix()-n.LastMasterPing > int64(n.DownAfterNoAlive) { @@ -141,6 +134,8 @@ func (n *Node) checkSlave() { golog.Error("Node", "checkSlave", "Ping", 0, "db.Addr", slaves[i].Addr(), "error", err.Error()) } else { n.LastSlavePing = time.Now().Unix() + atomic.StoreInt32(&(slaves[i].state), Up) + continue } if int64(n.DownAfterNoAlive) > 0 && time.Now().Unix()-n.LastSlavePing > int64(n.DownAfterNoAlive) { diff --git a/config/config.go b/config/config.go index 8664fa49..cd6f62e1 100644 --- a/config/config.go +++ b/config/config.go @@ -36,7 +36,7 @@ type NodeConfig struct { type SchemaConfig struct { DB string `yaml:"db"` Nodes []string `yaml:"nodes"` - RulesConifg RulesConfig `yaml:"rules"` + RulesConfig RulesConfig `yaml:"rules"` } //路由规则 diff --git a/proxy/router/router.go b/proxy/router/router.go index e35c2154..d0198ec3 100644 --- a/proxy/router/router.go +++ b/proxy/router/router.go @@ -60,11 +60,6 @@ func (r *Rule) FindTableIndex(key interface{}) int { return r.Shard.FindForKey(key) } -func (r *Rule) String() string { - return fmt.Sprintf("%s.%s?key=%v&shard=%s&nodes=%s", - r.DB, r.Table, r.Key, r.Type, strings.Join(r.Nodes, ", ")) -} - /*UpdateExprs对应set后面的表达式*/ func (r *Rule) checkUpdateExprs(exprs sqlparser.UpdateExprs) error { if r.Type == DefaultRuleType { @@ -85,18 +80,18 @@ func (r *Rule) checkUpdateExprs(exprs sqlparser.UpdateExprs) error { /*根据配置文件建立路由规则*/ func NewRouter(schemaConfig *config.SchemaConfig) (*Router, error) { //default节点是否是节点列表中的一个 - if !includeNode(schemaConfig.Nodes, schemaConfig.RulesConifg.Default) { + if !includeNode(schemaConfig.Nodes, schemaConfig.RulesConfig.Default) { return nil, fmt.Errorf("default node[%s] not in the nodes list.", - schemaConfig.RulesConifg.Default) + schemaConfig.RulesConfig.Default) } rt := new(Router) rt.DB = schemaConfig.DB //对应schema中的db rt.Nodes = schemaConfig.Nodes //对应schema中的nodes - rt.Rules = make(map[string]*Rule, len(schemaConfig.RulesConifg.ShardRule)) - rt.DefaultRule = NewDefaultRule(rt.DB, schemaConfig.RulesConifg.Default) + rt.Rules = make(map[string]*Rule, len(schemaConfig.RulesConfig.ShardRule)) + rt.DefaultRule = NewDefaultRule(rt.DB, schemaConfig.RulesConfig.Default) - for _, shard := range schemaConfig.RulesConifg.ShardRule { + for _, shard := range schemaConfig.RulesConfig.ShardRule { //rc := &RuleConfig{shard} for _, node := range shard.Nodes { //rules中的nodes是不是都在schema中的nodes if !includeNode(rt.Nodes, node) { diff --git a/proxy/server/conn_admin.go b/proxy/server/conn_admin.go index 7124db2b..562df4e5 100644 --- a/proxy/server/conn_admin.go +++ b/proxy/server/conn_admin.go @@ -6,6 +6,7 @@ import ( "github.com/flike/kingshard/core/golog" "github.com/flike/kingshard/mysql" "github.com/flike/kingshard/sqlparser" + "strconv" "strings" "time" ) @@ -13,8 +14,6 @@ import ( const ( Master = "master" Slave = "slave" - Proxy = "proxy" - Config = "config" ServerRegion = "server" NodeRegion = "node" @@ -24,6 +23,12 @@ const ( ADMIN_OPT_UP = "up" ADMIN_OPT_DOWN = "down" ADMIN_OPT_SHOW = "show" + + ADMIN_PROXY = "proxy" + ADMIN_NODE = "node" + ADMIN_SCHEMA = "schema" + + ADMIN_CONFIG = "config" ) var cmdServerOrder = []string{"opt", "k", "v"} @@ -228,67 +233,155 @@ func (c *ClientConn) handleAdminShow(k, v string) (*mysql.Resultset, error) { if len(k) == 0 || len(v) == 0 { return nil, ErrCmdUnsupport } - if k == Proxy && v == Config { + if k == ADMIN_PROXY && v == ADMIN_CONFIG { return c.handleShowProxyConfig() } + + if k == ADMIN_NODE && v == ADMIN_CONFIG { + return c.handleShowNodeConfig() + } + + if k == ADMIN_SCHEMA && v == ADMIN_CONFIG { + return c.handleShowSchemaConfig() + } + return nil, ErrCmdUnsupport } func (c *ClientConn) handleShowProxyConfig() (*mysql.Resultset, error) { - var names []string = []string{"Section", "Key", "Value"} + var names []string = []string{"Key", "Value"} var rows [][]string + var nodeNames []string + const ( - Column = 3 + Column = 2 ) + for name := range c.schema.nodes { + nodeNames = append(nodeNames, name) + } - rows = append(rows, []string{"Global_Config", "Addr", c.proxy.cfg.Addr}) - rows = append(rows, []string{"Global_Config", "User", c.proxy.cfg.User}) - rows = append(rows, []string{"Global_Config", "Password", c.proxy.cfg.Password}) - rows = append(rows, []string{"Global_Config", "LogLevel", c.proxy.cfg.LogLevel}) - rows = append(rows, []string{"Global_Config", "Schemas_Count", fmt.Sprintf("%d", len(c.proxy.schemas))}) - rows = append(rows, []string{"Global_Config", "Nodes_Count", fmt.Sprintf("%d", len(c.proxy.nodes))}) - - for db, schema := range c.proxy.schemas { - rows = append(rows, []string{"Schemas", "DB", db}) - - var nodeNames []string - var nodeRows [][]string - for name, node := range schema.nodes { - nodeNames = append(nodeNames, name) - var nodeSection = fmt.Sprintf("Schemas[%s]-Node[ %v ]", db, name) - - if node.Master != nil { - nodeRows = append(nodeRows, []string{nodeSection, "Master", node.Master.String()}) - } + rows = append(rows, []string{"Addr", c.proxy.cfg.Addr}) + rows = append(rows, []string{"User", c.proxy.cfg.User}) + rows = append(rows, []string{"Password", c.proxy.cfg.Password}) + rows = append(rows, []string{"LogLevel", c.proxy.cfg.LogLevel}) + rows = append(rows, []string{"Nodes_Count", fmt.Sprintf("%d", len(c.proxy.nodes))}) + rows = append(rows, []string{"Nodes_List", strings.Join(nodeNames, ",")}) - if node.Slave != nil { - nodeRows = append(nodeRows, []string{nodeSection, "Slave", node.FormatSlave()}) - } - nodeRows = append(nodeRows, []string{nodeSection, "Last_Master_Ping", fmt.Sprintf("%v", time.Unix(node.LastMasterPing, 0))}) + var values [][]interface{} = make([][]interface{}, len(rows)) + for i := range rows { + values[i] = make([]interface{}, Column) + for j := range rows[i] { + values[i][j] = rows[i][j] + } + } - nodeRows = append(nodeRows, []string{nodeSection, "Last_Slave_Ping", fmt.Sprintf("%v", time.Unix(node.LastSlavePing, 0))}) + return c.buildResultset(names, values) +} - nodeRows = append(nodeRows, []string{nodeSection, "down_after_noalive", fmt.Sprintf("%v", node.DownAfterNoAlive)}) +func (c *ClientConn) handleShowNodeConfig() (*mysql.Resultset, error) { + var names []string = []string{ + "Node", + "Address", + "Type", + "State", + "LastPing", + "MaxIdleConn", + "IdleConn", + } + var rows [][]string + const ( + Column = 7 + ) + //var nodeRows [][]string + for name, node := range c.schema.nodes { + //"master" + rows = append( + rows, + []string{ + name, + node.Master.Addr(), + "master", + node.Master.State(), + fmt.Sprintf("%v", time.Unix(node.LastMasterPing, 0)), + strconv.Itoa(node.Cfg.IdleConns), + strconv.Itoa(node.Master.IdleConnCount()), + }) + //"slave" + for _, slave := range node.Slave { + rows = append( + rows, + []string{ + name, + slave.Addr(), + "slave", + slave.State(), + fmt.Sprintf("%v", time.Unix(node.LastSlavePing, 0)), + strconv.Itoa(node.Cfg.IdleConns), + strconv.Itoa(slave.IdleConnCount()), + }) } - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]", db), "Nodes_List", strings.Join(nodeNames, ",")}) - - var defaultRule = schema.rule.DefaultRule - if defaultRule.DB == db { - if defaultRule.DB == db { - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Default", db), - "Default_Table", defaultRule.String()}) - } - } - for tb, r := range schema.rule.Rules { - if r.DB == db { - rows = append(rows, []string{fmt.Sprintf("Schemas[%s]_Rule_Table", db), - fmt.Sprintf("Table[ %s ]", tb), r.String()}) - } + } + //rows = append(rows, nodeRows...) + var values [][]interface{} = make([][]interface{}, len(rows)) + for i := range rows { + values[i] = make([]interface{}, Column) + for j := range rows[i] { + values[i][j] = rows[i][j] } + } + + return c.buildResultset(names, values) +} - rows = append(rows, nodeRows...) +func (c *ClientConn) handleShowSchemaConfig() (*mysql.Resultset, error) { + var Column = 7 + var rows [][]string + var names []string = []string{ + "DB", + "Table", + "Type", + "Key", + "Nodes_List", + "Locations", + "TableRowLimit", + } + + //default Rule + var defaultRule = c.schema.rule.DefaultRule + rows = append( + rows, + []string{ + defaultRule.DB, + defaultRule.Table, + defaultRule.Type, + defaultRule.Key, + strings.Join(defaultRule.Nodes, ", "), + "", + "0", + }, + ) + //shard rule + if len(c.proxy.cfg.Schemas) != 1 { + return nil, ErrCmdUnsupport + } + schemaConfig := c.proxy.cfg.Schemas[0] + shardRule := schemaConfig.RulesConfig.ShardRule + + for _, r := range shardRule { + rows = append( + rows, + []string{ + schemaConfig.DB, + r.Table, + r.Type, + r.Key, + strings.Join(r.Nodes, ", "), + arrayToString(r.Locations), + strconv.Itoa(r.TableRowLimit), + }, + ) } var values [][]interface{} = make([][]interface{}, len(rows)) @@ -301,3 +394,15 @@ func (c *ClientConn) handleShowProxyConfig() (*mysql.Resultset, error) { return c.buildResultset(names, values) } + +func arrayToString(array []int) string { + if len(array) == 0 { + return "" + } + var strArray []string + for _, v := range array { + strArray = append(strArray, strconv.FormatInt(int64(v), 10)) + } + + return strings.Join(strArray, ", ") +} From 5a3213fa94e3819d6763416e715b8859e523c837 Mon Sep 17 00:00:00 2001 From: flike Date: Fri, 7 Aug 2015 15:49:39 +0800 Subject: [PATCH 07/13] add function[send query to specified node] --- mysql/const.go | 3 +++ proxy/server/conn_query.go | 37 +++++++++++++++++++++++++------------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/mysql/const.go b/mysql/const.go index f3809c8f..75e1a111 100644 --- a/mysql/const.go +++ b/mysql/const.go @@ -171,4 +171,7 @@ var ( "select": KS_TK_SELECT, "use": KS_TK_USE, } + // '/' + COMMENT_PREFIX uint8 = 47 + COMMENT_STRING = "/*" ) diff --git a/proxy/server/conn_query.go b/proxy/server/conn_query.go index a1de3653..2d771f2e 100644 --- a/proxy/server/conn_query.go +++ b/proxy/server/conn_query.go @@ -39,8 +39,7 @@ func (c *ClientConn) handleQuery(sql string) (err error) { }() sql = strings.TrimRight(sql, ";") //删除sql语句最后的分号 - - hasHandled, err := c.handleUnsupport(sql) + hasHandled, err := c.handleUnShard(sql) if err != nil { golog.Error("server", "parse", err.Error(), 0, "hasHandled", hasHandled) return err @@ -326,9 +325,11 @@ func (c *ClientConn) newEmptyResultset(stmt *sqlparser.Select) *Resultset { } //返回true表示已经处理,false表示未处理 -func (c *ClientConn) handleUnsupport(sql string) (bool, error) { +func (c *ClientConn) handleUnShard(sql string) (bool, error) { var rs []*Result var TK_FROM string = "from" + var execNode *backend.Node + var fromSlave bool = false sql = strings.ToLower(sql) tokens := strings.Fields(sql) @@ -348,26 +349,39 @@ func (c *ClientConn) handleUnsupport(sql string) (bool, error) { } } } + //get node + if 2 <= tokensLen { + if tokens[0][0] == COMMENT_PREFIX { + nodeName := strings.Trim(tokens[0], COMMENT_STRING) + if c.schema.nodes[nodeName] != nil { + execNode = c.schema.nodes[nodeName] + } + //select + if WHITE_TOKEN_MAP[tokens[1]] == 2 { + fromSlave = true + } + } + } - defaultRule := c.schema.rule.DefaultRule - if len(defaultRule.Nodes) == 0 { - - return false, ErrNoDefaultNode + if execNode == nil { + defaultRule := c.schema.rule.DefaultRule + if len(defaultRule.Nodes) == 0 { + return false, ErrNoDefaultNode + } + execNode = c.proxy.GetNode(defaultRule.Nodes[0]) } - defaultNode := c.proxy.GetNode(defaultRule.Nodes[0]) //execute in Master DB - conn, err := c.getBackendConn(defaultNode, false) + conn, err := c.getBackendConn(execNode, fromSlave) if err != nil { return false, err } - rs, err = c.executeInNode(conn, sql, nil) if err != nil { return false, err } - c.closeConn(conn, false) + if len(rs) == 0 { msg := fmt.Sprintf("result is empty") golog.Error("ClientConn", "handleUnsupport", msg, c.connectionId) @@ -379,7 +393,6 @@ func (c *ClientConn) handleUnsupport(sql string) (bool, error) { } else { err = c.writeOK(rs[0]) } - if err != nil { return false, err } From 1218ffaea71d9d4f2f6227c459c2002622c004df Mon Sep 17 00:00:00 2001 From: flike Date: Fri, 7 Aug 2015 16:03:09 +0800 Subject: [PATCH 08/13] modify doc --- README_ZH.md | 1 + doc/KingDoc/admin_command_introduce.md | 36 ++++++++++++++- doc/KingDoc/change_log_CN.md | 11 +++-- doc/KingDoc/kingshard_sharding_introduce.md | 50 +++++++++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) diff --git a/README_ZH.md b/README_ZH.md index 525aec09..09022701 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -12,6 +12,7 @@ kingshard是一个由Go开发高性能MySQL Proxy项目,kingshard在满足基 4. 平滑上线DB或下线DB,前端应用无感知。 5. 支持多个slave,slave之间通过权值进行负载均衡。 6. 支持强制读主库。 + 7. 支持将sql发送到特定的node ## kinshard详细说明 diff --git a/doc/KingDoc/admin_command_introduce.md b/doc/KingDoc/admin_command_introduce.md index de4d2125..f3f3bf1e 100644 --- a/doc/KingDoc/admin_command_introduce.md +++ b/doc/KingDoc/admin_command_introduce.md @@ -28,6 +28,40 @@ admin node(opt,node,k,v) values(‘up’,’node1’,’master’,’127.0.0.1:3 ## 查看kingshard配置 ``` -admin server(opt,k,v) values('show','proxy','config') +#查看kingshard全局配置 +mysql> admin server(opt,k,v) values('show','proxy','config'); ++-------------+----------------+ +| Key | Value | ++-------------+----------------+ +| Addr | 127.0.0.1:9696 | +| User | kingshard | +| Password | kingshard | +| LogLevel | debug | +| Nodes_Count | 2 | +| Nodes_List | node1,node2 | ++-------------+----------------+ +6 rows in set (0.00 sec) + +#查看node状态 +mysql> admin server(opt,k,v) values('show','node','config'); ++-------+---------------------+--------+-------+-------------------------------+-------------+----------+ +| Node | Address | Type | State | LastPing | MaxIdleConn | IdleConn | ++-------+---------------------+--------+-------+-------------------------------+-------------+----------+ +| node1 | 127.0.0.1:3306 | master | up | 2015-08-07 15:54:44 +0800 CST | 16 | 1 | +| node2 | 192.168.59.103:3307 | master | up | 2015-08-07 15:54:44 +0800 CST | 16 | 1 | ++-------+---------------------+--------+-------+-------------------------------+-------------+----------+ +2 rows in set (0.00 sec) + +#查看schema配置 + +mysql> admin server(opt,k,v) values('show','schema','config'); ++-----------+------------------+---------+------+--------------+-----------+---------------+ +| DB | Table | Type | Key | Nodes_List | Locations | TableRowLimit | ++-----------+------------------+---------+------+--------------+-----------+---------------+ +| kingshard | | default | | node1 | | 0 | +| kingshard | test_shard_hash | hash | id | node1, node2 | 4, 4 | 0 | +| kingshard | test_shard_range | range | id | node1, node2 | 4, 4 | 10000 | ++-----------+------------------+---------+------+--------------+-----------+---------------+ +3 rows in set (0.00 sec) ``` \ No newline at end of file diff --git a/doc/KingDoc/change_log_CN.md b/doc/KingDoc/change_log_CN.md index bb88d4a8..b7cffb5b 100644 --- a/doc/KingDoc/change_log_CN.md +++ b/doc/KingDoc/change_log_CN.md @@ -1,17 +1,20 @@ # ChangeLog +## 2015-08-08 +### feature +* 将sql发送到指定的node。 ## 2015-08-04 ### feature -1. 增加查看proxy配置的命令。 +* 增加查看proxy配置的命令。 ## 2015-08-02 ### feature -1. 增加管理后端DB的命令。 +* 增加管理后端DB的命令。 ## 2015-07-26 ### feature -1. 增加多个slave支持,并且可以设置每个slave的负载权重。 -2. 完善日志输出格式。 +* 增加多个slave支持,并且可以设置每个slave的负载权重。 +* 完善日志输出格式。 \ No newline at end of file diff --git a/doc/KingDoc/kingshard_sharding_introduce.md b/doc/KingDoc/kingshard_sharding_introduce.md index ce731d94..7eff36b3 100644 --- a/doc/KingDoc/kingshard_sharding_introduce.md +++ b/doc/KingDoc/kingshard_sharding_introduce.md @@ -155,3 +155,53 @@ mysql> show tables; ``` +## 将SQL路由到指定node上 + +在kingshard中允许用户将特定的sql路由到指定的node上。只需要在sql语句前面加上包含node名称的注释。 + +``` +mysql> /*node2*/show tables; ++-----------------------+ +| Tables_in_kingshard | ++-----------------------+ +| kingshard_test_conn | +| test_shard_hash_0004 | +| test_shard_hash_0005 | +| test_shard_hash_0006 | +| test_shard_hash_0007 | +| test_shard_range_0004 | +| test_shard_range_0005 | +| test_shard_range_0006 | +| test_shard_range_0007 | ++-----------------------+ +9 rows in set (0.03 sec) + +mysql> /*node2*/select * from kingshard_test_conn; +Empty set (0.01 sec) + +mysql> /*node2*/desc kingshard_test_conn; ++-------+-----------------------+------+-----+---------+-------+ +| Field | Type | Null | Key | Default | Extra | ++-------+-----------------------+------+-----+---------+-------+ +| id | bigint(20) unsigned | NO | PRI | NULL | | +| str | varchar(256) | YES | | NULL | | +| f | double | YES | | NULL | | +| e | enum('test1','test2') | YES | | NULL | | +| u | tinyint(3) unsigned | YES | | NULL | | +| i | tinyint(4) | YES | | NULL | | ++-------+-----------------------+------+-----+---------+-------+ +6 rows in set (0.00 sec) + +mysql> /*node2*/insert into kingshard_test_conn values(10,"hello",10.2,'test1',1,1); +Query OK, 1 row affected (0.00 sec) + +mysql> /*node2*/select * from kingshard_test_conn; ++----+-------+------+-------+------+------+ +| id | str | f | e | u | i | ++----+-------+------+-------+------+------+ +| 10 | hello | 10.2 | test1 | 1 | 1 | ++----+-------+------+-------+------+------+ +1 row in set (0.00 sec) + +``` + From 6e0072c5911db2367c2e731e228ea2d7359c52e9 Mon Sep 17 00:00:00 2001 From: flike Date: Sat, 8 Aug 2015 15:00:12 +0800 Subject: [PATCH 09/13] add banner --- cmd/kingshard/main.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cmd/kingshard/main.go b/cmd/kingshard/main.go index 377afb18..f49b9a44 100644 --- a/cmd/kingshard/main.go +++ b/cmd/kingshard/main.go @@ -16,9 +16,18 @@ import ( var configFile *string = flag.String("config", "/etc/kingshard.conf", "kingshard config file") var logLevel *string = flag.String("log-level", "", "log level [debug|info|warn|error], default error") +const banner string = ` + __ _ __ __ + / /__(_)___ ____ ______/ /_ ____ __________/ / + / //_/ / __ \/ __ \/ ___/ __ \ / __\/ ___/ __ / + / ,< / / / / / /_/ (__ ) / / / /_/ / / / /_/ / +/_/|_/_/_/ /_/\__, /____/_/ /_/\__,_/_/ \__,_/ + /____/ +` + func main() { + fmt.Print(banner) runtime.GOMAXPROCS(runtime.NumCPU()) - flag.Parse() if len(*configFile) == 0 { From c00749e0af17972bff60fb3ada2a8f5ea7fbe7c1 Mon Sep 17 00:00:00 2001 From: flike Date: Sun, 9 Aug 2015 09:51:59 +0800 Subject: [PATCH 10/13] support transaction in single node --- core/errors/errors.go | 10 +++-- doc/KingDoc/change_log_CN.md | 4 ++ proxy/router/planbuilder.go | 3 +- proxy/server/conn_query.go | 79 +++++++++++++++++++++++++++++++----- 4 files changed, 79 insertions(+), 17 deletions(-) diff --git a/core/errors/errors.go b/core/errors/errors.go index 94fa00df..165097f8 100644 --- a/core/errors/errors.go +++ b/core/errors/errors.go @@ -27,8 +27,10 @@ var ( ErrDeleteInMulti = errors.New("delete in multi node") ErrReplaceInMulti = errors.New("replace in multi node") ErrExecInMulti = errors.New("exec in multi node") - ErrNoPlan = errors.New("statement have no plan") - ErrUpdateKey = errors.New("routing key in update expression") - ErrStmtConvert = errors.New("statement fail to convert") - ErrConnNotEqual = errors.New("the length of conns not equal sqls") + ErrTransInMulti = errors.New("transaction in multi node") + + ErrNoPlan = errors.New("statement have no plan") + ErrUpdateKey = errors.New("routing key in update expression") + ErrStmtConvert = errors.New("statement fail to convert") + ErrConnNotEqual = errors.New("the length of conns not equal sqls") ) diff --git a/doc/KingDoc/change_log_CN.md b/doc/KingDoc/change_log_CN.md index b7cffb5b..73e59ef6 100644 --- a/doc/KingDoc/change_log_CN.md +++ b/doc/KingDoc/change_log_CN.md @@ -1,5 +1,9 @@ # ChangeLog +## 2015-08-09 +### feature +* 支持在单node上的事务,不支持跨多个node执行事务。 + ## 2015-08-08 ### feature * 将sql发送到指定的node。 diff --git a/proxy/router/planbuilder.go b/proxy/router/planbuilder.go index 1a995be0..88adf277 100644 --- a/proxy/router/planbuilder.go +++ b/proxy/router/planbuilder.go @@ -126,13 +126,12 @@ func (plan *Plan) calRouteIndexs() error { plan.RouteNodeIndexs = []int{0} return nil } - if plan.Criteria == nil { //如果没有分表条件,则是全表扫描 + if plan.Criteria == nil { //如果没有分表条件,则是全子表扫描 if plan.Rule.Type != DefaultRuleType { golog.Error("Plan", "calRouteIndexs", "plan have no criteria", 0, "type", plan.Rule.Type) return ErrNoCriteria } - } switch criteria := plan.Criteria.(type) { diff --git a/proxy/server/conn_query.go b/proxy/server/conn_query.go index 2d771f2e..e7426618 100644 --- a/proxy/server/conn_query.go +++ b/proxy/server/conn_query.go @@ -324,15 +324,38 @@ func (c *ClientConn) newEmptyResultset(stmt *sqlparser.Select) *Resultset { return r } -//返回true表示已经处理,false表示未处理 -func (c *ClientConn) handleUnShard(sql string) (bool, error) { - var rs []*Result - var TK_FROM string = "from" +func (c *ClientConn) getTransNode(tokens []string, sql string) (*backend.Node, error) { var execNode *backend.Node - var fromSlave bool = false - sql = strings.ToLower(sql) - tokens := strings.Fields(sql) + tokensLen := len(tokens) + if 2 <= tokensLen { + if tokens[0][0] == COMMENT_PREFIX { + nodeName := strings.Trim(tokens[0], COMMENT_STRING) + if c.schema.nodes[nodeName] != nil { + execNode = c.schema.nodes[nodeName] + } + } + } + + if execNode == nil { + defaultRule := c.schema.rule.DefaultRule + if len(defaultRule.Nodes) == 0 { + return nil, ErrNoDefaultNode + } + execNode = c.proxy.GetNode(defaultRule.Nodes[0]) + } + if len(c.txConns) == 1 && c.txConns[execNode] == nil { + return nil, ErrTransInMulti + } + return execNode, nil +} + +func (c *ClientConn) getNotransNode(tokens []string, + sql string, fromSlave *bool) (*backend.Node, error) { + + var execNode *backend.Node + var TK_FROM string = "from" + tokensLen := len(tokens) if 0 < tokensLen { //token is in WHITE_TOKEN_MAP @@ -341,11 +364,11 @@ func (c *ClientConn) handleUnShard(sql string) (bool, error) { if 1 < WHITE_TOKEN_MAP[tokens[0]] { for i := 1; i < tokensLen; i++ { if tokens[i] == TK_FROM { - return false, nil + return nil, nil } } } else { - return false, nil + return nil, nil } } } @@ -358,7 +381,7 @@ func (c *ClientConn) handleUnShard(sql string) (bool, error) { } //select if WHITE_TOKEN_MAP[tokens[1]] == 2 { - fromSlave = true + *fromSlave = true } } } @@ -366,11 +389,45 @@ func (c *ClientConn) handleUnShard(sql string) (bool, error) { if execNode == nil { defaultRule := c.schema.rule.DefaultRule if len(defaultRule.Nodes) == 0 { - return false, ErrNoDefaultNode + return nil, ErrNoDefaultNode } execNode = c.proxy.GetNode(defaultRule.Nodes[0]) } + return execNode, nil +} + +//返回true表示已经处理,false表示未处理 +func (c *ClientConn) handleUnShard(sql string) (bool, error) { + var rs []*Result + var err error + + var execNode *backend.Node + var fromSlave bool = false + + if len(sql) == 0 { + return false, ErrCmdUnsupport + } + sql = strings.ToLower(sql) + tokens := strings.Fields(sql) + if len(tokens) == 0 { + return false, ErrCmdUnsupport + } + + if c.needBeginTx() { + execNode, err = c.getTransNode(tokens, sql) + } else { + execNode, err = c.getNotransNode(tokens, sql, &fromSlave) + } + + if err != nil { + return false, err + } + //need shard sql + if execNode == nil { + return false, nil + } + //execute in Master DB conn, err := c.getBackendConn(execNode, fromSlave) if err != nil { From f8b9adfcf0800928017b810b424d5f5ced313817 Mon Sep 17 00:00:00 2001 From: flike Date: Thu, 13 Aug 2015 18:50:07 +0800 Subject: [PATCH 11/13] support log write into file --- README.md | 1 + README_ZH.md | 3 ++- cmd/kingshard/main.go | 39 ++++++++++++++++++++++++++++++------ config/config.go | 1 + core/golog/log.go | 9 +++++---- core/golog/log_test.go | 6 +++--- doc/KingDoc/change_log_CN.md | 3 +++ etc/multi.yaml | 4 ++++ 8 files changed, 52 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cea662cd..8fa86c88 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ kingshard is a high-performance proxy for MySQL powered by Go. Just like other m - splits reads and writes - sharding table across multiple nodes - client's ip ACL control. +- transaction in single node. - supports prepared statement: COM_STMT_PREPARE, COM_STMT_EXECUTE, etc. - MySQL HA diff --git a/README_ZH.md b/README_ZH.md index 09022701..b826effe 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -12,7 +12,8 @@ kingshard是一个由Go开发高性能MySQL Proxy项目,kingshard在满足基 4. 平滑上线DB或下线DB,前端应用无感知。 5. 支持多个slave,slave之间通过权值进行负载均衡。 6. 支持强制读主库。 - 7. 支持将sql发送到特定的node + 7. 支持将sql发送到特定的node。 + 8. 支持在单个node上执行事务,不支持跨多个node执行事务。 ## kinshard详细说明 diff --git a/cmd/kingshard/main.go b/cmd/kingshard/main.go index f49b9a44..7c95e392 100644 --- a/cmd/kingshard/main.go +++ b/cmd/kingshard/main.go @@ -8,6 +8,7 @@ import ( "github.com/flike/kingshard/proxy/server" "os" "os/signal" + "path" "runtime" "strings" "syscall" @@ -16,6 +17,12 @@ import ( var configFile *string = flag.String("config", "/etc/kingshard.conf", "kingshard config file") var logLevel *string = flag.String("log-level", "", "log level [debug|info|warn|error], default error") +const ( + sqlLogName = "sql.log" + sysLogName = "sys.log" + MaxLogSize = 1024 * 1024 * 1024 +) + const banner string = ` __ _ __ __ / /__(_)___ ____ ______/ /_ ____ __________/ / @@ -41,6 +48,25 @@ func main() { return } + //when the log file size greater than 1GB, kingshard will generate a new file + if len(cfg.LogPath) != 0 { + sysFilePath := path.Join(cfg.LogPath, sysLogName) + sysFile, err := golog.NewRotatingFileHandler(sysFilePath, MaxLogSize, 1) + if err != nil { + fmt.Printf("new log file error:%v\n", err.Error()) + return + } + golog.GlobalSysLogger = golog.New(sysFile, golog.Lfile|golog.Ltime|golog.Llevel) + + sqlFilePath := path.Join(cfg.LogPath, sqlLogName) + sqlFile, err := golog.NewRotatingFileHandler(sqlFilePath, MaxLogSize, 1) + if err != nil { + fmt.Printf("new log file error:%v\n", err.Error()) + return + } + golog.GlobalSqlLogger = golog.New(sqlFile, golog.Lfile|golog.Ltime|golog.Llevel) + } + if *logLevel != "" { setLogLevel(*logLevel) } else { @@ -64,7 +90,8 @@ func main() { go func() { sig := <-sc golog.Info("main", "main", "Got signal", 0, "signal", sig) - golog.GlobalLogger.Close() + golog.GlobalSysLogger.Close() + golog.GlobalSqlLogger.Close() svr.Close() }() @@ -74,14 +101,14 @@ func main() { func setLogLevel(level string) { switch strings.ToLower(level) { case "debug": - golog.GlobalLogger.SetLevel(golog.LevelDebug) + golog.GlobalSysLogger.SetLevel(golog.LevelDebug) case "info": - golog.GlobalLogger.SetLevel(golog.LevelInfo) + golog.GlobalSysLogger.SetLevel(golog.LevelInfo) case "warn": - golog.GlobalLogger.SetLevel(golog.LevelWarn) + golog.GlobalSysLogger.SetLevel(golog.LevelWarn) case "error": - golog.GlobalLogger.SetLevel(golog.LevelError) + golog.GlobalSysLogger.SetLevel(golog.LevelError) default: - golog.GlobalLogger.SetLevel(golog.LevelError) + golog.GlobalSysLogger.SetLevel(golog.LevelError) } } diff --git a/config/config.go b/config/config.go index cd6f62e1..b24e19c7 100644 --- a/config/config.go +++ b/config/config.go @@ -10,6 +10,7 @@ type Config struct { Addr string `yaml:"addr"` User string `yaml:"user"` Password string `yaml:"password"` + LogPath string `yaml:"log_path"` LogLevel string `yaml:"log_level"` AllowIps string `yaml:"allow_ips"` diff --git a/core/golog/log.go b/core/golog/log.go index e8941b5d..d3c2eaa5 100644 --- a/core/golog/log.go +++ b/core/golog/log.go @@ -213,7 +213,8 @@ func GetLevel() int { } //全局变量 -var GlobalLogger *Logger = StdLogger() +var GlobalSysLogger *Logger = StdLogger() +var GlobalSqlLogger *Logger = GlobalSysLogger func escape(s string, filterEqual bool) string { dest := make([]byte, 0, 2*len(s)) @@ -237,7 +238,7 @@ func escape(s string, filterEqual bool) string { } func OutputSql(state string, format string, v ...interface{}) { - l := GlobalLogger + l := GlobalSqlLogger buf := l.popBuf() if l.flag&Ltime > 0 { @@ -263,7 +264,7 @@ func OutputSql(state string, format string, v ...interface{}) { } func output(level int, module string, method string, msg string, reqId uint32, args ...interface{}) { - if level < GlobalLogger.Level() { + if level < GlobalSysLogger.Level() { return } @@ -282,7 +283,7 @@ func output(level int, module string, method string, msg string, reqId uint32, a content := fmt.Sprintf(`[%s] "%s" "%s" "%s" conn_id=%d`, module, method, msg, argsBuff.String(), reqId) - GlobalLogger.Output(3, level, content) + GlobalSysLogger.Output(3, level, content) } func Trace(module string, method string, msg string, reqId uint32, args ...interface{}) { diff --git a/core/golog/log_test.go b/core/golog/log_test.go index 0c68571f..28643225 100644 --- a/core/golog/log_test.go +++ b/core/golog/log_test.go @@ -49,11 +49,11 @@ func TestRotatingFileLog(t *testing.T) { t.Fatal(err) } - GlobalLogger = New(h, Lfile|Ltime|Llevel) - GlobalLogger.SetLevel(LevelTrace) + GlobalSysLogger = New(h, Lfile|Ltime|Llevel) + GlobalSysLogger.SetLevel(LevelTrace) Debug("log", "hello,world", "OK", 0, "fileName", fileName, "fileName2", fileName, "fileName3", fileName) - GlobalLogger.Close() + GlobalSysLogger.Close() //os.RemoveAll(path) } diff --git a/doc/KingDoc/change_log_CN.md b/doc/KingDoc/change_log_CN.md index 73e59ef6..8bf67664 100644 --- a/doc/KingDoc/change_log_CN.md +++ b/doc/KingDoc/change_log_CN.md @@ -1,4 +1,7 @@ # ChangeLog +## 2015-08-13 +### feature +* 支持日志输出到文件。 ## 2015-08-09 ### feature diff --git a/etc/multi.yaml b/etc/multi.yaml index cb2825bb..6e3323ec 100755 --- a/etc/multi.yaml +++ b/etc/multi.yaml @@ -5,6 +5,10 @@ addr : 127.0.0.1:9696 user : kingshard password : kingshard +#if set log_path, the sql log will write into log_path/sql.log,the system log +#will write into log_path/sys.log +#log_path : /Users/flike/log + # log level[debug|info|warn|error],default error log_level : debug # only allow this ip list ip to connect kingshard From d890a305712568066c63aed82185b2091a6118d6 Mon Sep 17 00:00:00 2001 From: flike Date: Fri, 14 Aug 2015 15:00:57 +0800 Subject: [PATCH 12/13] refactor code --- backend/node.go | 2 +- etc/multi.yaml | 2 +- proxy/server/conn_query.go | 8 ++++---- proxy/server/server.go | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/node.go b/backend/node.go index d7bd6fc0..7b34b81c 100644 --- a/backend/node.go +++ b/backend/node.go @@ -34,7 +34,7 @@ type Node struct { LastSlavePing int64 } -func (n *Node) Run() { +func (n *Node) CheckNode() { //to do //1 check connection alive //2 check remove mysql server alive diff --git a/etc/multi.yaml b/etc/multi.yaml index 6e3323ec..0b75df47 100755 --- a/etc/multi.yaml +++ b/etc/multi.yaml @@ -35,7 +35,7 @@ nodes : # slave represents a real mysql salve server,and the number after '@' is # read load weight of this slave. slave : - #down_after_noalive : 300 + down_after_noalive : 100 - name : node2 diff --git a/proxy/server/conn_query.go b/proxy/server/conn_query.go index e7426618..5eb32539 100644 --- a/proxy/server/conn_query.go +++ b/proxy/server/conn_query.go @@ -324,7 +324,7 @@ func (c *ClientConn) newEmptyResultset(stmt *sqlparser.Select) *Resultset { return r } -func (c *ClientConn) getTransNode(tokens []string, sql string) (*backend.Node, error) { +func (c *ClientConn) GetTransNode(tokens []string, sql string) (*backend.Node, error) { var execNode *backend.Node tokensLen := len(tokens) @@ -350,7 +350,7 @@ func (c *ClientConn) getTransNode(tokens []string, sql string) (*backend.Node, e return execNode, nil } -func (c *ClientConn) getNotransNode(tokens []string, +func (c *ClientConn) GetNotransNode(tokens []string, sql string, fromSlave *bool) (*backend.Node, error) { var execNode *backend.Node @@ -415,9 +415,9 @@ func (c *ClientConn) handleUnShard(sql string) (bool, error) { } if c.needBeginTx() { - execNode, err = c.getTransNode(tokens, sql) + execNode, err = c.GetTransNode(tokens, sql) } else { - execNode, err = c.getNotransNode(tokens, sql, &fromSlave) + execNode, err = c.GetNotransNode(tokens, sql, &fromSlave) } if err != nil { diff --git a/proxy/server/server.go b/proxy/server/server.go index e80509cb..294c105b 100644 --- a/proxy/server/server.go +++ b/proxy/server/server.go @@ -70,7 +70,7 @@ func (s *Server) parseNode(cfg config.NodeConfig) (*backend.Node, error) { return nil, err } - go n.Run() + go n.CheckNode() return n, nil } From 8ad539282b8003271f065c7f3e237a02971c5f2f Mon Sep 17 00:00:00 2001 From: flike Date: Sun, 16 Aug 2015 08:59:48 +0800 Subject: [PATCH 13/13] add doc --- README_ZH.md | 10 ++- doc/KingDoc/architecture_of_kingshard_CN.md | 86 +++++++++++++++++++++ 2 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 doc/KingDoc/architecture_of_kingshard_CN.md diff --git a/README_ZH.md b/README_ZH.md index b826effe..42dca2ea 100755 --- a/README_ZH.md +++ b/README_ZH.md @@ -21,13 +21,15 @@ kingshard是一个由Go开发高性能MySQL Proxy项目,kingshard在满足基 [2.kingshard 快速入门](./doc/KingDoc/kingshard_quick_try.md) -[3.kingshard sharding介绍](./doc/KingDoc/kingshard_sharding_introduce.md) +[3.kingshard架构设计和功能实现](./doc/KingDoc/architecture_of_kingshard_CN.md) -[4.功能FAQ](./doc/KingDoc/function_FAQ.md) +[4.kingshard sharding介绍](./doc/KingDoc/kingshard_sharding_introduce.md) -[5.管理端命令介绍](./doc/KingDoc/admin_command_introduce.md) +[5.功能FAQ](./doc/KingDoc/function_FAQ.md) -[6.ChangeLog](./doc/KingDoc/change_log_CN.md) +[6.管理端命令介绍](./doc/KingDoc/admin_command_introduce.md) + +[7.ChangeLog](./doc/KingDoc/change_log_CN.md) ## 反馈 目前kingshard还是1.0版本,比较核心的功能已经实现了。但还有很多地方不完善。如果您在使用kingshard的过程中发现BUG或者有新的功能需求,非常欢迎您发邮件至flikecn#126.com与作者取得联系,或者加入QQ群(147926796)交流。 diff --git a/doc/KingDoc/architecture_of_kingshard_CN.md b/doc/KingDoc/architecture_of_kingshard_CN.md new file mode 100644 index 00000000..d3f8845f --- /dev/null +++ b/doc/KingDoc/architecture_of_kingshard_CN.md @@ -0,0 +1,86 @@ +# kingshard架构设计和功能实现 + +kingshard开源有一段时间了,有些热心的用户发邮件来咨询kingshard的设计和实现问题。于是周末抽空写了一篇介绍kingshard架构和功能实现的文章,希望通过本文能够让用户对kingshard有更深的了解。下面分模块来介绍kingshard的核心组件的设计和实现。 + +## 1. 整体架构 + +kingshard采用Go开发,充分地利用了Go语言的并发特性。Go语言在并发方面,做了很好的封装,这大大简化了kingshard的开发工作。kingshard的整体工作流程入下所述: + +1. 读取配置文件并启动,在配置文件中设置的监听端口监听客户端请求。 +2. 收到客户端连接请求后,启动一个goroutine单独处理该请求。 +3. 首选进行登录验证,验证过程完全兼容MySQL认证协议,由于用户名和密码在配置文件中已经设置好,所以可以利用该信息验证连接请求是否合法。 +当用户名和密码都正确时,转入下面的步骤,否则返回出错信息给客户端。 +4. 认证通过后,客户端发送SQL语句。 +5. kingshard对客户端发送过来的SQL语句,进行词法和语义分析,识别出SQL的类型和生成SQL的路由计划。如果有必要还会改写SQL,然后转发到相应的DB。也有可能不做词法和语义分析直接转发到相应的后端DB。如果转发SQL是分表且跨多个DB,则每个DB对应启动一个goroutine发送SQL和接收该DB返回的结果。 +6. 接收并合并结果,然后转发给客户端。 + +kingshard工作整体流程可参考下面这幅图。 +![kingshard流程图](http://ww3.sinaimg.cn/large/6e5705a5gw1ev27000rvvj20qo0k0dgh.jpg) +kingshard整体架构图如下所示 +![kingshard架构图](http://ww4.sinaimg.cn/large/6e5705a5gw1ev26zhyml3j20qo0k0dgr.jpg) + +## 2. 词法和语义分析 + +要将kingshard的功能做的足够强大,就不得不进行SQL的词法和语义分析。SQL语句的词法分析指的是将SQL语句切分成一个一个的关键字。例如对SQL语句:`select name from stu where id < 13`进行词法分析,得到的结果是:`{"select","name","from","stu","where","id","<","13"}`。 +这样做的目的主要为了生成一棵抽象语法树,也就是大家常说的AST(abstract syntax tree),语义分析就是基于这棵语法树来操作的。语义分析的目的主要有以下几个方面: + +1. 读写分离,只有识别出SQL语句的类型,才能进行正确的读写分离操作。 +2. 数据分片,解析出表名和查询条件,将SQL路由到正确的DB。 +3. SQL黑名单,通过词法和语义分析,也可以快速识别出需要屏蔽的SQL语句。例如,检测到delete语句不带where操作,就可以直接阻断该SQL的转发。 + +kingshard并没有考虑完全兼容MySQL所有语法,因为完全兼容MySQL语法会使得词法和语义分析模块变得异常复杂,而且低效。对于DDL语句其实没必要解析,只要能正确转发到后端相应的DB上就可以。 + +kingshard只对部分DML语句`(select,update,insert,delete,replace)`进行了解析,这样可以满足绝大部分的分表操作。对于其他语句,kingshard会将其发送到一个默认的DB,或者通过kingshard特有的方式将其发送到指定的DB上,例如: +`/*node2*/insert into stu(id,name) values(12,'xiaoming')`,对于这种带有注释的的sql语句,kingshard能够识别出,然后将这条sql语句发送到node2节点的Master DB上。 + +## 3. 负载均衡 + +用户使用Mysql Proxy目的很大一部分就是为了降低单台DB的负载,将读压力分担到多台DB上。kingshard支持多个slave,不同的slave可以配置不同的读权重,权重越大分担的读请求越多。kingshard读请求负载均衡采用的是权重轮询调度算法。 + +大部分系统采用该算法时,都是转发SQL语句时,动态地计算出本次选取DB的序号。然后将读请求的SQL语句发送到该DB。仔细分析一下,这样做其实是没有必要的。因为DB的权重是相对固定的,不会经常变动,所以完全可以计算出一个固定的轮询序列,然后将这个序列保存在一个数组中。这样不需要动态计算,每次读取数组就可以。举个例子来说,在kingshard的node配置项中配置slave选项: +`slave:192.168.0.12@2,192.168.0.13@3` +kingshard在读取配置信息初始化系统的时候,就生成了一个轮询数组:[0,0,1,1,1]。在kingshard中会将这个数组打乱顺序,变成:[0,1,1,0,1]。这样就避免了动态计算DB下标的问题,对性能提升有一定帮助。 + +## 4.sharding实现 + +首选需要介绍两个概念: + +1. **子表**,在kingshard中一张逻辑上的大表由若干张小的子表组成。例如:将stu表分成stu_0000,stu_0001,stu_0002,stu_0003。 +在数据库中stu表是不存在的,它只是一张逻辑上的表。数据库中只存在四张子表(stu_0000,stu_0001,stu_0002,stu_0003)。 +发送SQL语句时,kingshard会识别出需要分表的SQL语句,并改写该SQL。例如,客户端发送过来的SQL语句是`:select name from stu where id =10;` +kingshard收到该SQL语句后,从配置信息中识别出该表是一个Hash类型的分表。根据分表规则,将该SQL改写成:`select name from stu_2 where id =10;` +然后发送给对应的DB。 + +2. **Node**,子表分布在各个node上,每个node包含一个maser server和若干个slave server(slave个数可以为0)。写请求会发往该node中master server,读请求会发往该node中的slave server。 + +kingshard的sharding采用了两级映射的思想,首选根据SQL语句的分表条件计算出这条SQL语句落在哪个子表上,然后再根据配置信息找到该子表 +落在哪个node上。采用两级映射的思想,对于MySQL的扩容和缩容都能很方便地支持。目前kingshard sharding支持insert, delete, select, update和replace语句, 所有这五类操作都支持跨子表。但写操作仅支持单node上的跨子表,select操作则可以跨node,跨子表。 + +对于有些表没有分表,操作该表的SQL语句会发往default node。或者用户可以选择在SQL语句前面加上注释,指定该SQL要发往的node,kingshard接收到语句后,识别出注释中指定的node,然后将该SQL发往对应node中合适的DB。例如用户发送`/*node1*/select * from member where id=100`,kingshard接收到该SQL后会将其发送到node1的salve上。这样kingshard就能很好地兼容分表和不分表的各种应用场景了。 + + +## 5. 事务的实现 + +所有proxy支持shard后都会面临一个问题:支不支持分布式事务?出于性能和可用性考虑, +kingshard只支持单台DB上的事务,不允许跨DB的事务。kingshard处理单DB上的事务流程如下: + +1. 用户发送begin语句。 +2. kingshard接收到SQL语句后,将该连接的状态设置为事务。 +3. 用户发送DML语句,kingshard识别出语句需要发送到的DB,然后kingshard新建一个与后端DB的连接中取一个连接,利用该连接发送语句。 +4. 收取SQL语句的结果后,将连接放回。 +5. kingshard收到下一条SQL语句,判断该SQL是不是发往同一个DB,如果不是则报错。如果是发往同一个DB,则利用该连接发送语句。 +6. 收到用户发送的commit语句,将该连接的状态设置为非事务,事务结束。 + +## 6. 后端DB存活检测 + +kingshard每个node启动了一个goroutine用于检测后端master和slave的状态。当goroutine持续一段时间(由配置文件中down_after_noalive参数设置)ping不通后端的DB后,会将该DB的状态设置为down,后续kingshard就不会将sql语句发往该DB了。 + +## 7. 客户端白名单机制 + +有时候用户为了安全考虑,希望只能某几台server能够连接kingshard。在kingshard的配置文件中有一个参数:allow_ips,用于实现客户端白名单机制。当管理员设置了该参数,则意味着只有allow_ips指定的IP能够连接kingshard,其他IP都会被kingshard拒绝连接。如果不设置该参数,则连接kingshard的客户端不受限制。 + +## 8. 管理端设计和实现 +kingshard的管理端口复用了工作端口,通过特定的关键字(admin)来标示。kingshard是通过对管理端特定的SQL进行词法和语义分析,将SQL语句解析为一条kingshard可以识别的命令。目前支持平滑上下线master和slave,和查看kingshard配置和后端DB状态。后续打算将web页面集成到管理端,这样用户就可以不用输入命令行操作,而是在网页上操作。大大降低用户使用kingshard的门槛。 + +上述各个模块都是kingshard中比较核心的模块,通过这篇文章的介绍,我想读者应该对kingshard的架构和实现有了初步的了解。很多功能的设计和实现,都是作者慢慢地摸索和实践。如果有读者对kingshard的设计或实现感兴趣或者对上述设计有不同的想法,欢迎发邮件(flikecn#126.com)给我。 +