diff --git a/etc/exit.shy b/etc/exit.shy index a754ac6e..5755da1f 100644 --- a/etc/exit.shy +++ b/etc/exit.shy @@ -2,4 +2,6 @@ config save tmp/flash.json flash ~aaa config save tmp/auth.json auth +~cli + config save tmp/runtime.json runtime diff --git a/etc/init.shy b/etc/init.shy index e478c98c..d01c8442 100644 --- a/etc/init.shy +++ b/etc/init.shy @@ -1,3 +1,5 @@ +~cli + config load tmp/runtime.json runtime ~aaa config load tmp/auth.json auth ~code diff --git a/src/contexts/cli/cli.go b/src/contexts/cli/cli.go index fd499a09..562fecd0 100644 --- a/src/contexts/cli/cli.go +++ b/src/contexts/cli/cli.go @@ -485,7 +485,7 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", return } - now := int64(m.Sess("cli").Cmd("time").Appendi("timestamp")) + now := m.Sess("cli").Cmd("time").Appendi("timestamp") begin := now if len(arg) > 0 && arg[0] == "begin" { begin, arg = int64(m.Sess("cli").Cmd("time", arg[1]).Appendi("timestamp")), arg[2:] @@ -524,7 +524,6 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", m.GoLoop(m, func(m *ctx.Message) { select { case <-cli.Timer.C: - m.Log("info", "timer %s", m.Conf("timer_next")) if m.Conf("timer_next") == "" { break } @@ -675,10 +674,11 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", }}, "quit": &ctx.Command{Name: "quit code", Help: "停止服务", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { m.Cmd("cli.source", m.Conf("system", "script.exit")) - go func() { + + m.GoFunc(m, func(m *ctx.Message) { time.Sleep(time.Second * 3) os.Exit(kit.Int(arg[0])) - }() + }) return }}, @@ -761,7 +761,7 @@ var Index = &ctx.Context{Name: "cli", Help: "管理中心", // 解析路由 if msg == m { - if routes := strings.Split(detail[0], "."); len(routes) > 1 { + if routes := strings.Split(detail[0], "."); len(routes) > 1 && !strings.Contains(detail[0], ":") { route := strings.Join(routes[:len(routes)-1], ".") if msg = m.Find(route, false); msg == nil { msg = m.Find(route, true) diff --git a/src/contexts/ctx/ctx_init.go b/src/contexts/ctx/ctx_init.go index bb62d4dc..f4ac8bd1 100644 --- a/src/contexts/ctx/ctx_init.go +++ b/src/contexts/ctx/ctx_init.go @@ -335,9 +335,14 @@ var Index = &Context{Name: "ctx", Help: "模块中心", Server: &CTX{}, } msg := m.message + keys := map[string]bool{} for msg = msg; msg != nil; msg = msg.message { for _, k := range msg.Meta["option"] { if len(arg) == 0 { + if keys[k] { + continue + } + keys[k] = true m.Add("append", "key", k) m.Add("append", "len", len(msg.Meta[k])) m.Add("append", "value", fmt.Sprintf("%v", msg.Meta[k])) @@ -601,9 +606,8 @@ var Index = &Context{Name: "ctx", Help: "模块中心", Server: &CTX{}, switch action { case "cmd": componet := "source" - if m.Options("bench") && m.Options("username") && - !m.Cmds("aaa.work", m.Option("bench"), "right", m.Option("username"), componet, arg[0]) { - m.Log("info", "check %v: %v failure", componet, arg[0]) + if m.Options("bench") && m.Options("username") && !m.Cmds("aaa.work", "right", componet, arg[0]) { + m.Log("info", "%s no right [%v: %v]", m.Option("username"), componet, arg[0]) m.Echo("error: ").Echo("no right [%s: %s]", componet, arg[0]) break } @@ -1407,6 +1411,7 @@ func Start(args ...string) bool { args = args[1:] } + Pulse.Option("routine", 0) if Index.Begin(Pulse, args...); Index.Start(Pulse, args...) { return Index.Close(Pulse, args...) } diff --git a/src/contexts/ctx/ctx_type.go b/src/contexts/ctx/ctx_type.go index 8f7252ee..41c231dd 100644 --- a/src/contexts/ctx/ctx_type.go +++ b/src/contexts/ctx/ctx_type.go @@ -2,8 +2,10 @@ package ctx import ( "fmt" + "math/rand" "regexp" "runtime" + "strconv" "strings" "errors" @@ -409,7 +411,7 @@ func (m *Message) Format(arg ...interface{}) string { case "code": meta = append(meta, kit.Format(m.code)) case "ship": - meta = append(meta, fmt.Sprintf("%d(%s->%s)", m.code, m.source.Name, m.target.Name)) + meta = append(meta, fmt.Sprintf("%s:%d(%s->%s)", m.Option("routine"), m.code, m.source.Name, m.target.Name)) case "source": target := m.target m.target = m.source @@ -903,9 +905,9 @@ func (m *Message) Append(key string, arg ...interface{}) string { } return "" } -func (m *Message) Appendi(key string, arg ...interface{}) int { - return kit.Int(m.Append(key, arg...)) - +func (m *Message) Appendi(key string, arg ...interface{}) int64 { + i, _ := strconv.ParseInt(m.Append(key, arg...), 10, 64) + return i } func (m *Message) Appends(key string, arg ...interface{}) bool { return kit.Right(m.Append(key, arg...)) @@ -1123,11 +1125,11 @@ func (m *Message) Parse(arg interface{}) string { } return "" } -func (m *Message) ToHTML() string { +func (m *Message) ToHTML(style string) string { cmd := strings.Join(m.Meta["detail"], " ") result := []string{} if len(m.Meta["append"]) > 0 { - result = append(result, "") + result = append(result, fmt.Sprintf("
", style)) result = append(result, "") m.Table(func(maps map[string]string, list []string, line int) bool { if line == -1 { @@ -1212,8 +1214,7 @@ func (m *Message) Assert(e interface{}, msg ...string) bool { } func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) *Message { defer func() { - e := recover() - switch e { + switch e := recover(); e { case io.EOF: case nil: default: @@ -1221,9 +1222,7 @@ func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) m.Log("bench", "catch: %s", e) m.Log("bench", "stack: %s", msg.Format("stack")) - m.Log("error", "catch: %s", e) - - if len(hand) > 1 { + if m.Log("error", "catch: %s", e); len(hand) > 1 { m.TryCatch(msg, safe, hand[1:]...) } else if !safe { msg.Assert(e) @@ -1234,23 +1233,25 @@ func (m *Message) TryCatch(msg *Message, safe bool, hand ...func(msg *Message)) if len(hand) > 0 { hand[0](msg) } - return m } func (m *Message) GoFunc(msg *Message, hand ...func(msg *Message)) *Message { go func() { + ngo := msg.Option("routine", m.Capi("ngo", 1)) + msg.Log("info", "%v safe go begin", ngo) + kit.Log("error", "%s ngo %s start", msg.Format(), ngo) m.TryCatch(msg, true, hand...) + kit.Log("error", "%s ngo %s end", msg.Format(), ngo) + msg.Log("info", "%v safe go end", ngo) }() return m } func (m *Message) GoLoop(msg *Message, hand ...func(msg *Message)) *Message { - go func() { - m.Log("info", "%v safe go begin", m.Capi("ngo", 1)) + m.GoFunc(msg, func(msg *Message) { for { - m.TryCatch(msg, true, hand...) + hand[0](msg) } - m.Log("info", "%v safe go end", m.Capi("ngo", -1)+1) - }() + }) return m } func (m *Message) Start(name string, help string, arg ...string) bool { @@ -1464,14 +1465,14 @@ func (m *Message) CallBack(sync bool, cb func(msg *Message) (sub *Message), arg } wait := make(chan *Message, 10) - m.GoFunc(m, func(m *Message) { - m.Call(func(sub *Message) *Message { - msg := cb(sub) - m.Log("sync", m.Format("done", "result", "append")) - wait <- m - return msg - }, arg...) - }) + // m.GoFunc(m, func(m *Message) { + m.Call(func(sub *Message) *Message { + msg := cb(sub) + m.Log("sync", m.Format("done", "result", "append")) + wait <- m + return msg + }, arg...) + // }) m.Log("sync", m.Format("wait", "result", "append")) select { @@ -1538,20 +1539,27 @@ func (m *Message) Cmd(args ...interface{}) *Message { } key, arg := m.Meta["detail"][0], m.Meta["detail"][1:] - if strings.Contains(key, ".") { + msg := m + if strings.Contains(key, ":") { + ps := strings.Split(key, ":") + msg, key, arg = msg.Sess("ssh"), "sh", append([]string{"node", ps[0], "sync", ps[1]}, arg...) + defer func() { m.Copy(msg, "append").Copy(msg, "result") }() + m.Hand = true + + } else if strings.Contains(key, ".") { arg := strings.Split(key, ".") - m, key = m.Sess(arg[0]), arg[1] - m.Option("remote_code", "") + msg, key = msg.Sess(arg[0]), arg[1] + msg.Option("remote_code", "") } - if m == nil { - return m + if msg == nil { + return msg } - m = m.Match(key, true, func(m *Message, s *Context, c *Context, key string) bool { - m.Hand = false + msg = msg.Match(key, true, func(msg *Message, s *Context, c *Context, key string) bool { + msg.Hand = false if x, ok := c.Commands[key]; ok && x.Hand != nil { - m.TryCatch(m, true, func(m *Message) { - m.Log("cmd", "%s %s %v %v", c.Name, key, arg, m.Meta["option"]) + msg.TryCatch(msg, true, func(msg *Message) { + msg.Log("cmd", "%s %s %v %v", c.Name, key, arg, msg.Meta["option"]) if args := []string{}; x.Form != nil { for i := 0; i < len(arg); i++ { @@ -1565,9 +1573,9 @@ func (m *Message) Cmd(args ...interface{}) *Message { } } if i+1+n > len(arg) { - m.Add("option", arg[i], arg[i+1:]) + msg.Add("option", arg[i], arg[i+1:]) } else { - m.Add("option", arg[i], arg[i+1:i+1+n]) + msg.Add("option", arg[i], arg[i+1:i+1+n]) } i += n } else { @@ -1577,37 +1585,38 @@ func (m *Message) Cmd(args ...interface{}) *Message { arg = args } - target := m.target - m.target = s + target := msg.target + msg.target = s - m.Hand = true - switch v := m.Gdb("command", key, arg).(type) { + msg.Hand = true + switch v := msg.Gdb("command", key, arg).(type) { case string: - m.Echo(v) + msg.Echo(v) case nil: - if m.Options("auto_cmd") { + if msg.Options("auto_cmd") { if x.Auto != nil { - x.Auto(m, c, key, arg...) + x.Auto(msg, c, key, arg...) } } else { - x.Hand(m, c, key, arg...) + x.Hand(msg, c, key, arg...) } } - if m.target == s { - m.target = target + if msg.target == s { + msg.target = target } }) } - return m.Hand + return msg.Hand }) - if !m.Hand { - m.Log("error", "cmd run error %s", m.Format()) + if !msg.Hand { + msg.Log("error", "cmd run error %s", msg.Format()) } - return m + return msg } func (m *Message) Confm(key string, args ...interface{}) map[string]interface{} { + random := "" var chain interface{} if len(args) > 0 { switch arg := args[0].(type) { @@ -1616,7 +1625,12 @@ func (m *Message) Confm(key string, args ...interface{}) map[string]interface{} case []string: chain, args = arg, args[1:] case string: - chain, args = arg, args[1:] + switch arg { + case "%", "*": + random, args = arg, args[1:] + default: + chain, args = arg, args[1:] + } } } @@ -1660,9 +1674,23 @@ func (m *Message) Confm(key string, args ...interface{}) map[string]interface{} } fun(value) case func(string, map[string]interface{}): - for k, v := range value { - if val, ok := v.(map[string]interface{}); ok { - fun(k, val) + switch random { + case "%": + n, i := rand.Intn(len(value)), 0 + for k, v := range value { + if val, ok := v.(map[string]interface{}); i == n && ok { + fun(k, val) + break + } + i++ + } + case "*": + fallthrough + default: + for k, v := range value { + if val, ok := v.(map[string]interface{}); ok { + fun(k, val) + } } } case func(string, map[string]interface{}) bool: diff --git a/src/contexts/nfs/nfs.go b/src/contexts/nfs/nfs.go index db65ce6b..7f6c34b0 100644 --- a/src/contexts/nfs/nfs.go +++ b/src/contexts/nfs/nfs.go @@ -923,7 +923,7 @@ func (nfs *NFS) Start(m *ctx.Message, arg ...string) bool { nfs.printf(msg.Meta["result"]) if msg.Appends("file_pos0") { - i = msg.Appendi("file_pos0") - 1 + i = int(msg.Appendi("file_pos0")) - 1 msg.Append("file_pos0", "") } } @@ -984,16 +984,15 @@ func (nfs *NFS) Start(m *ctx.Message, arg ...string) bool { if head == "detail" { // 接收请求 msg.Detail(-1, "remote") msg.Option("remote_code", code) - go msg.Call(func(msg *ctx.Message) *ctx.Message { - nfs.echo <- msg - return nil + m.GoFunc(msg, func(msg *ctx.Message) { + msg.Call(func(msg *ctx.Message) *ctx.Message { + nfs.echo <- msg + return nil + }) }) } else { // 接收响应 h := nfs.hand[kit.Int(code)] - h.Copy(msg, "result").Copy(msg, "append") - go func() { - h.Back(h) - }() + h.Copy(msg, "result").Copy(msg, "append").Back(h) } msg, code, head, body = nil, "0", "result", "append" diff --git a/src/contexts/ssh/ssh.go b/src/contexts/ssh/ssh.go index 90381850..66372718 100644 --- a/src/contexts/ssh/ssh.go +++ b/src/contexts/ssh/ssh.go @@ -37,11 +37,10 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心", "nnode": &ctx.Cache{Name: "nnode", Value: "0", Help: "节点数量"}, }, Configs: map[string]*ctx.Config{ - "node": &ctx.Config{Name: "node", Value: map[string]interface{}{}, Help: "主机信息"}, - "trust": &ctx.Config{Name: "trust", Value: map[string]interface{}{}, Help: "主机信息"}, - "current": &ctx.Config{Name: "current", Value: "", Help: "当前主机"}, - "timer": &ctx.Config{Name: "timer", Value: "", Help: "断线重连"}, - "timer_interval": &ctx.Config{Name: "timer_interval", Value: "10s", Help: "断线重连"}, + "current": &ctx.Config{Name: "current", Value: "", Help: "当前主机"}, + "trust": &ctx.Config{Name: "trust", Value: map[string]interface{}{}, Help: "可信主机"}, + "node": &ctx.Config{Name: "node", Value: map[string]interface{}{}, Help: "主机信息"}, + "timer": &ctx.Config{Name: "timer", Value: map[string]interface{}{"interval": "10s", "timer": ""}, Help: "断线重连"}, }, Commands: map[string]*ctx.Command{ "init": &ctx.Command{Name: "init", Help: "启动", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { @@ -94,8 +93,8 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心", case "dial": // 连接主机 m.Call(func(nfs *ctx.Message) *ctx.Message { // 断线重连 - if m.Confs("timer") { - m.Conf("timer", m.Cmdx("cli.timer", "delete", m.Conf("timer"))) + if m.Confs("timer", "timer") { + m.Conf("timer", "timer", m.Cmdx("cli.timer", "delete", m.Conf("timer", "timer"))) } m.Spawn(nfs.Target()).Call(func(node *ctx.Message) *ctx.Message { @@ -126,7 +125,7 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心", // 清理主机 nfs.Free(func(nfs *ctx.Message) bool { - m.Conf("timer", m.Cmdx("cli.timer", "repeat", m.Conf("timer_interval"), "context", "ssh", "remote", "redial", arg[1:])) + m.Conf("timer", "timer", m.Cmdx("cli.timer", "repeat", m.Conf("timer", "interval"), "context", "ssh", "remote", "redial", arg[1:])) m.Cmd("aaa.auth", m.Cmdx("aaa.auth", "nodes", node.Append("node.name")), "delete", "node") m.Log("info", "delete node %s", node.Append("node.name")) delete(m.Confm("node"), node.Append("node.name")) @@ -213,11 +212,11 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心", } } - if names[0] == "*" { // 广播命令 - m.Confm("node", func(name string, node map[string]interface{}) { + if names[0] == "%" || names[0] == "*" { // 广播命令 + m.Confm("node", names[0], func(name string, node map[string]interface{}) { m.Find(kit.Format(node["module"]), true).Copy(m, "option").CallBack(sync, func(sub *ctx.Message) *ctx.Message { - return m.Copy(sub, "append").Copy(sub, "result") - }, "send", "", arg) + return m.CopyFuck(sub, "append").CopyFuck(sub, "result").Echo("\n\n") + }, "send", rest, arg) }) } else if m.Confm("node", names[0], func(node map[string]interface{}) { // 单播命令 @@ -344,7 +343,6 @@ var Index = &ctx.Context{Name: "ssh", Help: "集群中心", m.Cmd("aaa.auth", sess, "proxy", m.Option("node.route")) m.Echo(sess) return - } if m.Options("remote_code") { diff --git a/src/contexts/tcp/tcp.go b/src/contexts/tcp/tcp.go index 714c6d7d..bf759aa1 100644 --- a/src/contexts/tcp/tcp.go +++ b/src/contexts/tcp/tcp.go @@ -25,7 +25,7 @@ func (tcp *TCP) Fuck(address []string, action func(address string) (net.Conn, er for _, p := range address { m.Cap("address", p) for i := 0; i < m.Confi("retry", "counts"); i++ { - go func() { + m.GoFunc(m, func(m *ctx.Message) { p := m.Cap("address") if c, e := action(p); e == nil { tcp.Conn = c @@ -34,7 +34,7 @@ func (tcp *TCP) Fuck(address []string, action func(address string) (net.Conn, er m.Log("info", "dial %s:%s %s", m.Cap("protocol"), p, e) fuck <- false } - }() + }) select { case ok := <-fuck: @@ -176,6 +176,8 @@ func (tcp *TCP) Start(m *ctx.Message, arg ...string) bool { ports = append(ports, fmt.Sprintf("%s:%s", "127.0.0.1", addr[len(addr)-1])) } m.Back(m.Spawn(m.Source()).Put("option", "node.port", ports)) + default: + return true } for { diff --git a/src/contexts/yac/yac.go b/src/contexts/yac/yac.go index 7133855a..acf23056 100644 --- a/src/contexts/yac/yac.go +++ b/src/contexts/yac/yac.go @@ -310,7 +310,7 @@ var Index = &ctx.Context{Name: "yac", Help: "语法中心", map[string]interface{}{"page": "stm", "hash": "expr", "word": []interface{}{"expr", "rep{", "exp", "}"}}, map[string]interface{}{"page": "stm", "hash": "return", "word": []interface{}{"return", "rep{", "exp", "}"}}, - map[string]interface{}{"page": "word", "hash": "word", "word": []interface{}{"mul{", "~", "!", "=", "\\?\\?", "\\?", "<", ">$", ">@", ">", "\\|", "%", "exe", "str", "[a-zA-Z0-9_/\\-.:]+", "}"}}, + map[string]interface{}{"page": "word", "hash": "word", "word": []interface{}{"mul{", "~", "!", "=", "\\?\\?", "\\?", "<", ">$", ">@", ">", "\\|", "%", "exe", "str", "[a-zA-Z0-9_/\\-.:*%]+", "}"}}, map[string]interface{}{"page": "cmd", "hash": "cmd", "word": []interface{}{"rep{", "word", "}"}}, map[string]interface{}{"page": "exe", "hash": "exe", "word": []interface{}{"$", "(", "cmd", ")"}}, diff --git a/src/examples/chat/chat.go b/src/examples/chat/chat.go index eea7b217..1d6d672b 100644 --- a/src/examples/chat/chat.go +++ b/src/examples/chat/chat.go @@ -116,7 +116,7 @@ var Index = &ctx.Context{Name: "chat", Help: "会议中心", if kit.Int(access["expire"]) < now { msg := m.Cmd("web.get", "wexin", access["url"], "appid", m.Conf("chat", "appid"), "secret", m.Conf("chat", "appmm"), "temp", "data") access["token"] = msg.Append("access_token") - access["expire"] = msg.Appendi("expires_in") + now + access["expire"] = int(msg.Appendi("expires_in")) + now } m.Echo("%v", access["token"]) return @@ -129,7 +129,7 @@ var Index = &ctx.Context{Name: "chat", Help: "会议中心", if kit.Int(ticket["expire"]) < now { msg := m.Cmd("web.get", "wexin", ticket["url"], "access_token", m.Cmdx(".access"), "temp", "data") ticket["value"] = msg.Append("ticket") - ticket["expire"] = msg.Appendi("expires_in") + now + ticket["expire"] = int(msg.Appendi("expires_in")) + now } m.Echo("%v", ticket["value"]) return diff --git a/src/examples/code/code.go b/src/examples/code/code.go index 8bea8bc0..8314adf0 100644 --- a/src/examples/code/code.go +++ b/src/examples/code/code.go @@ -88,7 +88,7 @@ var Index = &ctx.Context{Name: "code", Help: "代码中心", "componet_view": "ScheduleList", "componet_init": "initScheduleList", "componet_ctx": "web.code", "componet_cmd": "schedule", "inputs": []interface{}{ - map[string]interface{}{"type": "choice", "name": "view", "value": "summary", "label": "显示字段", "choice": []interface{}{ + map[string]interface{}{"type": "choice", "name": "view", "value": "order", "label": "显示字段", "choice": []interface{}{ map[string]interface{}{"name": "默认", "value": "default"}, map[string]interface{}{"name": "行程", "value": "order"}, map[string]interface{}{"name": "总结", "value": "summary"}, @@ -106,6 +106,9 @@ var Index = &ctx.Context{Name: "code", Help: "代码中心", map[string]interface{}{"componet_name": "code", "componet_tmpl": "head", "metas": []interface{}{ map[string]interface{}{"name": "viewport", "content": "width=device-width, initial-scale=0.7, user-scalable=no"}, }, "favicon": "favicon.ico", "styles": []interface{}{"example.css", "code.css"}}, + map[string]interface{}{"componet_name": "banner", "componet_help": "banner", "componet_tmpl": "banner", + "componet_view": "Banner", "componet_init": "initBanner", + }, map[string]interface{}{"componet_name": "toolkit", "componet_help": "Ctrl+B", "componet_tmpl": "toolkit", "componet_view": "KitList", "componet_init": "initKitList", diff --git a/src/examples/wiki/wiki.go b/src/examples/wiki/wiki.go index 0d40c59d..adcae0b3 100644 --- a/src/examples/wiki/wiki.go +++ b/src/examples/wiki/wiki.go @@ -62,7 +62,7 @@ var Index = &ctx.Context{Name: "wiki", Help: "文档中心", }, Commands: map[string]*ctx.Command{ "wiki_tree": &ctx.Command{Name: "wiki_tree", Help: "wiki_tree", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { - m.Cmdy("nfs.dir", path.Join(m.Confx("wiki_level"), kit.Select(m.Option("wiki_class"), arg, 0)), "dir_sort", "time_r") + m.Cmdy("nfs.dir", path.Join(m.Confx("wiki_level"), kit.Select(m.Option("wiki_class"), arg, 0)), "dir_sort", "time", "time_r") return }}, "wiki_text": &ctx.Command{Name: "wiki_text", Help: "wiki_text", Hand: func(m *ctx.Message, c *ctx.Context, key string, arg ...string) (e error) { @@ -80,8 +80,12 @@ var Index = &ctx.Context{Name: "wiki", Help: "文档中心", ls = markdown.ToHTML(ls, nil, nil) m.Echo(string(ls)) } else { - m.Echo(m.Cmd("nfs.dir", path.Join(m.Confx("wiki_level"), m.Option("wiki_class")), - "dir_deep", "dir_type", "dir", "time", "path").ToHTML()) + msg := m.Cmd("nfs.dir", path.Join(m.Confx("wiki_level"), m.Option("wiki_class")), + "dir_deep", "dir_type", "dir", "time", "path") + msg.Table(func(index int, value map[string]string) { + msg.Meta["path"][index] = strings.TrimPrefix(value["path"], path.Join("usr", m.Confx("wiki_level"))) + }) + m.Echo(msg.ToHTML("wiki_list")) } return }}, diff --git a/usr/librarys/context.js b/usr/librarys/context.js index c7aa9231..ed7c6e6d 100644 --- a/usr/librarys/context.js +++ b/usr/librarys/context.js @@ -70,6 +70,9 @@ ctx = context = { var searchs = search[1].split("&"); for (var i = 0; i < searchs.length; i++) { var keys = searchs[i].split("="); + if (keys[1]=="") { + continue + } args[keys[0]] = decodeURIComponent(keys[1]); } } @@ -93,6 +96,7 @@ ctx = context = { arg.push(k+"="+encodeURIComponent(args[k])); } location.search = arg.join("&"); + return value }, Cookie: function(key, value) { if (key == undefined) { diff --git a/usr/librarys/example.css b/usr/librarys/example.css index 744ece1d..dafd2c53 100644 --- a/usr/librarys/example.css +++ b/usr/librarys/example.css @@ -50,3 +50,16 @@ fieldset pre code, fieldset code pre { border-left:solid 4px green; display:block; } + +fieldset.Banner>ul { + margin:0; + padding:0; +} +fieldset.Banner>ul>li { + display:inline; + padding:5px; + margin:0; +} +fieldset.Banner>ul>li:hover { + background-color:red; +} diff --git a/usr/librarys/example.js b/usr/librarys/example.js index 8b2568c8..7cd4b148 100644 --- a/usr/librarys/example.js +++ b/usr/librarys/example.js @@ -9,13 +9,21 @@ exp = example = { } return this }, - initHeader: function(field, option, output) { + initHeader: function(page, field, option, output) { return [{"text": ["shylinux", "div", "title"]}] }, - initBanner: function(field, option, output) { + initBanner: function(page, field, option, output) { + field.querySelectorAll("li").forEach(function(item) { + item.onclick = function(event) { + ctx.Search("componet_group", item.innerText) + if (item.innerText == "login") { + ctx.Cookie("sessid", "") + } + } + }) return [{"text": ["shylinux", "div", "title"]}] }, - initFooter: function(field, option) { + initFooter: function(page, field, option) { return [{"text": ["shycontext", "div", "title"]}] }, onscroll: function(event, target, action) { diff --git a/usr/librarys/toolkit.js b/usr/librarys/toolkit.js index 1940a2b4..7a2566c4 100644 --- a/usr/librarys/toolkit.js +++ b/usr/librarys/toolkit.js @@ -313,10 +313,16 @@ kit = toolkit = { input.onclick = input.onclick || function(event) { if (index == array.length-1) { if (input.value == "login") { - option.ondaemon = function(msg) { - page.reload() - } + page.Runs(page, option, function(msg) { + if (document.referrer) { + location.href = document.referrer + } else { + m.Search("componet_group", "") + } + }) + return } + page.Runs(page, option) return } diff --git a/usr/librarys/wiki.js b/usr/librarys/wiki.js index 97247cbe..4c42f0d1 100644 --- a/usr/librarys/wiki.js +++ b/usr/librarys/wiki.js @@ -3,7 +3,7 @@ var page = Page({ ctx.Runs(page, option, function(msg) { output.innerHTML = "" var back = [{"button": ["知识", function(event) { - ctx.Search({"wiki_class": "", "wiki_favor": ""}) + ctx.Search({"wiki_level": "", "wiki_class": "", "wiki_favor": ""}) }]}] ctx.Search("wiki_class").split("/").forEach(function(value, index, array) { if (value) { @@ -29,7 +29,7 @@ var page = Page({ })}]}, {"view": ["list"], "list": [{"tree": ctx.Table(msg, function(value, index) { if (!value.filename.endsWith("/")) { - return {"leaf": [value.filename, function(event, target) { + return {"leaf": [value.time.substr(5, 5)+" "+value.filename, function(event, target) { ctx.Search("wiki_favor", value.filename) }]} } @@ -61,7 +61,13 @@ var page = Page({ ]) ui.text.querySelectorAll("table").forEach(function(value, index, array) { - kit.OrderTable(value, field) + kit.OrderTable(value) + }) + ui.text.querySelectorAll("table.wiki_list").forEach(function(value, index, array) { + kit.OrderTable(value, "path", function(event) { + var text = event.target.innerText + ctx.Search({"wiki_class": text}) + }) }) ui.text.querySelectorAll("a").forEach(function(value, index, array) { @@ -96,7 +102,7 @@ var page = Page({ location.hash = id }]}) } else if (value.tagName == "H4") { - id = i+"."+(++j)+"."+(++k) + id = i+"."+j+"."+(++k) text = id+" "+text h3.push({"leaf": [text+" ("+ratio+"%)", function(event) { console.log(text) @@ -120,6 +126,7 @@ var page = Page({ }, init: function(exp) { var page = this + document.querySelectorAll("body>fieldset").forEach(function(field) { var option = field.querySelector("form.option") var output = field.querySelector("div.output") diff --git a/usr/template/common.tmpl b/usr/template/common.tmpl index 112e4117..b4d8e82c 100644 --- a/usr/template/common.tmpl +++ b/usr/template/common.tmpl @@ -20,6 +20,11 @@ } {{end}} +{{define "banner"}} +
+ +
+{{end}} {{define "fieldset"}}
+- 文档: +- 源码: +- 开源: diff --git a/usr/wiki/自然/编程/后端技术栈/mysql.md b/usr/wiki/自然/编程/后端技术栈/mysql.md index 9d88ef1b..301c3b05 100644 --- a/usr/wiki/自然/编程/后端技术栈/mysql.md +++ b/usr/wiki/自然/编程/后端技术栈/mysql.md @@ -3,9 +3,9 @@ MySQL 是一个开源的关系型数据库管理系统。 - 官网: -- 源码: -- 文档: -- 开源: +- 源码: +- 文档: +- 开源: ## 下载安装 ### Ubuntu安装MySQL @@ -44,6 +44,9 @@ mysql> help - connect 重新连接服务器 - use 切换数据库 +### 配置操作 +mysql启动时会从/etc/mysql/my.cnf加载配置,show variables;可以查看当前配置 + ### 数据库操作 - 查看 show databases @@ -70,8 +73,10 @@ mysql> drop database demo; ### 关系表操作 +- 查看 show tables - 创建 create table demo(a int) - 修改 alter table add column b int +- 查看 desc demo - 删除 drop table demo ### 数据操作 @@ -82,31 +87,181 @@ mysql> drop database demo; - 删除 delete from demo where a=1 ## 存储引擎 + +- 源码: +- 文档: + +``` +mysql> show engines; +``` + +|engine|comment| +|------|-------| +|InnoDB| supports transactions, row-level locking, and foreign keys| +|MyISAM| | +|MEMORY|hash based, stored in memory, useful for temporary tables| +|BLACKHOLE| | +|ARCHIVE| | +|CSV| | +|MRG_MYISAM| | +|PERFORMANCE_SCHEMA| | +|FEDERATED| | + ### InnoDB +InnoDB是一种支持ACID事务的存储引擎 + +- 源码: +- 文档: + +InnoDB是多线程的,通过命令可以看各线程的状态 +``` +mysql> show engine innodb status\G +``` +|meta|task| +|------|----| +|BACKGROUND| 主线程 | +|FILE I/O| IO线程 | +|INSERT BUFFER AND HASH INDEX| 插入缓存与哈希索引 | +|SEMAPHORES| | +|TRANSACTIONS| | +|BUFFER POOL AND MEMORY| | +|ROW OPERATIONS| | +|LOG| | + +#### 线程与缓存 +MySQL的存储引擎是模块化,所以需要注册模块信息 +``` +// 注册模块 struct st_mysql_plugin {} // mysql-5.5.62/include/mysql/plugin.h:423 +mysql_declare_plugin(innobase) { // mysql-5.5.62/storage/innobase/handler/ha_innodb.cc:11925 + MYSQL_STORAGE_ENGINE_PLUGIN, + &innobase_storage_engine, + innobase_hton_name, + plugin_author, + "Supports transactions, row-level locking, and foreign keys", + PLUGIN_LICENSE_GPL, + innobase_init, /* Plugin Init */ + NULL, /* Plugin Deinit */ + INNODB_VERSION_SHORT, + innodb_status_variables_export,/* status variables */ + innobase_system_variables, /* system variables */ + NULL, /* reserved */ + 0, /* flags */ +} +... +mysql_declare_plugin_end; +``` + +InnoDB在初始化函数中,会注册各种回调函数,并启动各种工作线程 +``` +innobase_init() // mysql-5.5.62/storage/innobase/handler/ha_innodb.cc:2218 + // 注册各种回调函数 struct handlerton {} // mysql-5.5.62/sql/handler.h:705 + innobase_hton->state = SHOW_OPTION_YES; + innobase_hton->db_type= DB_TYPE_INNODB; + innobase_hton->savepoint_offset=sizeof(trx_named_savept_t); + innobase_hton->close_connection=innobase_close_connection; + innobase_hton->savepoint_set=innobase_savepoint; + innobase_hton->savepoint_rollback=innobase_rollback_to_savepoint; + innobase_hton->savepoint_release=innobase_release_savepoint; + innobase_hton->commit=innobase_commit; + innobase_hton->rollback=innobase_rollback; + innobase_hton->prepare=innobase_xa_prepare; + innobase_hton->recover=innobase_xa_recover; + innobase_hton->commit_by_xid=innobase_commit_by_xid; + innobase_hton->rollback_by_xid=innobase_rollback_by_xid; + innobase_hton->create_cursor_read_view=innobase_create_cursor_view; + innobase_hton->set_cursor_read_view=innobase_set_cursor_view; + innobase_hton->close_cursor_read_view=innobase_close_cursor_view; + innobase_hton->create=innobase_create_handler; + innobase_hton->drop_database=innobase_drop_database; + innobase_hton->panic=innobase_end; + innobase_hton->start_consistent_snapshot=innobase_start_trx_and_assign_read_view; + innobase_hton->flush_logs=innobase_flush_logs; + innobase_hton->show_status=innobase_show_status; + innobase_hton->flags=HTON_SUPPORTS_FOREIGN_KEYS; + innobase_hton->release_temporary_latches=innobase_release_temporary_latches; + + // 启动各种工作线程 + innobase_start_or_create_for_mysql() // srv/srv0start.c:1028 + os_thread_create(io_handler_thread, NULL, NULL); + os_thread_create(&srv_lock_time_thread, NULL, NULL); + os_thread_create(&srv_error_monitor_thread, NULL, NULL); + os_thread_create(&srv_monitor_thread, NULL, NULL); + os_thread_create(&srv_master_thread, NULL, NULL); + os_thread_create(&srv_purge_thread, NULL, NULL); +``` + +主线程 +``` +srv_master_thread() // mysql-5.5.62/storage/innobase/srv/srv0srv.c:2748 +loop: + srv_main_1_second_loops++; + + // 同步日志缓存 + srv_sync_log_buffer_in_background(); // srv/srv0srv.c:2702 + log_buffer_sync_in_background(TRUE); // log/log0log.c:1689 + log_write_up_to(lsn, LOG_NO_WAIT, flush); + mutex_enter(&(log_sys->mutex)); + log_sys->write_lsn = log_sys->lsn; + fil_flush(group->space_id); // fil/fil0fil.c:4755 + mutex_enter(&fil_system->mutex); + os_file_flush(file); // os/os0file.c:2119 + os_file_fsync(file); + fsync(file); + + // 合并插入缓存 + ibuf_contract_for_n_pages(FALSE, PCT_IO(5)); + + // 刷新数据缓存 + buf_flush_list(PCT_IO(100), IB_ULONGLONG_MAX); // buf/buf0flu.c:1928 + buf_flush_batch(buf_pool, BUF_FLUSH_LRU, min_n, 0); + buf_pool_mutex_enter(buf_pool); + buf_flush_LRU_list_batch(buf_pool, min_n); + buf_flush_flush_list_batch(buf_pool, min_n, lsn_limit); + buf_flush_buffered_writes(); + mutex_enter(&(trx_doublewrite->mutex)); + buf_flush_sync_datafiles(); + + // 执行清理任务 + srv_master_do_purge(); + trx_purge(srv_purge_batch_size); // trx/trx0purge.c:1139 + rw_lock_x_lock(&purge_sys->latch); + thr = que_fork_start_command(purge_sys->query); + que_run_threads(thr); + +background_loop: + srv_main_background_loops++; + row_drop_tables_for_mysql_in_background(); // row/row0mysql.c:2253 + mem_free(drop->table_name); + +flush_loop: + srv_main_flush_loops++; + log_checkpoint(TRUE, FALSE); // log/log0log.c:2077 + log_groups_write_checkpoint_info(); + log_group_checkpoint(group); + +suspend_thread: + srv_suspend_thread(slot); + +``` + +- 插入缓存与两次写 +- 自适应哈希索引 +- 启动、恢复、关闭 +- 引擎插件升级 + +#### 各种文件 + +配置文件,MySQL在启动时会加载配置文件,这些配置在运行时也可以随时读写 + +- show variables; +- show variables like 'timeout'; +- set [global|session] var=val + +日志文件 +数据文件 + ### MyISAM -- 下载: - -- 博客: - -变量的定义与引用: - -show engines -show engine innodb status -show variables - -show databases -create database demo -drop database demo -use demo - -show tables -create table t(a int unsigned not null, b char(10), primary key(a)) -drop table t - -select * from t; -insert into t values() -update t set b='1234' -delete from t - +- 源码: +- 文档: diff --git a/usr/wiki/自然/编程/后端技术栈/nginx.md b/usr/wiki/自然/编程/后端技术栈/nginx.md index bb35c91b..4dc7fd8e 100644 --- a/usr/wiki/自然/编程/后端技术栈/nginx.md +++ b/usr/wiki/自然/编程/后端技术栈/nginx.md @@ -1,16 +1,16 @@ ## 简介 Nginx 是一个异步框架的Web服务器,也可以用作反向代理,负载均衡和HTTP缓存。 -- 维基百科: [https://zh.wikipedia.org/wiki/Nginx](https://zh.wikipedia.org/wiki/Nginx) -- 官网: [https://www.nginx.org/](https://www.nginx.org/) +- 官网: +- 文档: +- 源码: +- 开源: ## 源码安装 ``` -$ wget http://nginx.org/download/nginx-1.15.2.tar.gz -$ tar xzf nginx-1.15.2.tar.gz -$ cd nginx-1.15.2 -$ ./configure -$ make +$ wget http://nginx.org/download/nginx-1.0.15.tar.gz +$ tar xzf nginx-1.0.15.tar.gz && cd nginx-1.0.15 +$ ./configure && make $ sudo make install $ sudo nginx $ curl localhost diff --git a/usr/wiki/自然/编程/后端技术栈/redis.md b/usr/wiki/自然/编程/后端技术栈/redis.md index 68df9ee3..a5fe904e 100644 --- a/usr/wiki/自然/编程/后端技术栈/redis.md +++ b/usr/wiki/自然/编程/后端技术栈/redis.md @@ -6,13 +6,12 @@ Redis是最流行的键值对存储数据库。 - 官网: - 源码: - 文档: +- 开源: ## 源码安装 ``` $ wget http://download.redis.io/releases/redis-4.0.11.tar.gz -$ tar xzf redis-4.0.11.tar.gz -$ cd redis-4.0.11 -$ make +$ tar xzf redis-4.0.11.tar.gz && cd redis-4.0.11 && make ``` #### 启动服务端 ``` @@ -32,6 +31,186 @@ OK 127.0.0.1:6379> get employee_name "shy" ``` +## 源码解析 +### 数据结构 +#### sds +#### dict +#### intset +#### adlist +#### ziplist +#### geohash +#### geo + +### 数据类型 +#### string +#### list +#### hash +#### set +#### zset + +### 数据存储 +#### db +#### expire +#### notify +#### script +#### multi +#### sort + +### 服务框架 +#### server +#### config +#### module +#### ae +#### aof +#### rdb + +### 服务集群 +#### replication +#### sentinel +#### cluster +#### pubsub + +### 应用场景 +#### 数据缓存 +#### 分布式锁 +#### 计数统计 +#### 消息队列 + +基本类型 +``` +typedef struct redisObject { // server.h:585 + unsigned type:4; + unsigned encoding:4; + unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or + * LFU data (least significant 8 bits frequency + * and most significant 16 bits access time). */ + int refcount; + void *ptr; +} robj; +``` +``` +typedef struct redisDb { // server.h:611 + dict *dict; /* The keyspace for this DB */ + dict *expires; /* Timeout of keys with a timeout set */ + dict *blocking_keys; /* Keys with clients waiting for data (BLPOP)*/ + dict *ready_keys; /* Blocked keys that received a PUSH */ + dict *watched_keys; /* WATCHED keys for MULTI/EXEC CAS */ + int id; /* Database ID */ + long long avg_ttl; /* Average TTL, just for stats */ +} redisDb; + +struct redisServer { // server.h:874 + ... + redisDb *db; + ... + int dbnum; /* Total number of configured DBs */ + ... +} +``` + + +## 基本类型 +### 字符串sds +``` +struct __attribute__ ((__packed__)) sdshdr64 { // sds.h:68 + uint64_t len; /* used */ + uint64_t alloc; /* excluding the header and null terminator */ + unsigned char flags; /* 3 lsb of type, 5 unused bits */ + char buf[]; +}; +``` + +### 双链表adlist +``` +typedef struct listNode { // adlist.h + struct listNode *prev; + struct listNode *next; + void *value; +} listNode; + +typedef struct list { + listNode *head; + listNode *tail; + void *(*dup)(void *ptr); + void (*free)(void *ptr); + int (*match)(void *ptr, void *key); + unsigned long len; +} list; +``` + +``` +struct redisCommand redisCommandTable[] = { +} +``` + +### 哈希表dict +``` +typedef struct dictEntry { // dict.h + void *key; + union { + void *val; + uint64_t u64; + int64_t s64; + double d; + } v; + struct dictEntry *next; +} dictEntry; + +typedef struct dictType { + uint64_t (*hashFunction)(const void *key); + void *(*keyDup)(void *privdata, const void *key); + void *(*valDup)(void *privdata, const void *obj); + int (*keyCompare)(void *privdata, const void *key1, const void *key2); + void (*keyDestructor)(void *privdata, void *key); + void (*valDestructor)(void *privdata, void *obj); +} dictType; + +/* This is our hash table structure. Every dictionary has two of this as we + * implement incremental rehashing, for the old to the new table. */ +typedef struct dictht { + dictEntry **table; + unsigned long size; + unsigned long sizemask; + unsigned long used; +} dictht; + +typedef struct dict { + dictType *type; + void *privdata; + dictht ht[2]; + long rehashidx; /* rehashing not in progress if rehashidx == -1 */ + unsigned long iterators; /* number of iterators currently running */ +} dict; +``` + +### 跳跃表zskiplist +``` +zskiplistNode { // server.h + sds ele; + double score; + struct zskiplistNode *backward; + struct zskiplistLevel { + struct zskiplistNode *forward; + unsigned int span; + } level[]; +} zskiplistNode; + +typedef struct zskiplist { + struct zskiplistNode *header, *tail; + unsigned long length; + int level; +} zskiplist; +``` + +### 整数集合intset +``` +typedef struct intset { // intset.h + uint32_t encoding; + uint32_t length; + int8_t contents[]; +} intset; +``` + ## 源码解析 ### 目录解析
", cmd, "