1
0
mirror of https://shylinux.com/x/ContextOS synced 2025-04-25 16:58:06 +08:00

add ssh.multi

This commit is contained in:
shylinux 2019-04-29 21:28:05 +08:00
parent a968cc6096
commit ed027c8c9d
23 changed files with 644 additions and 144 deletions

View File

@ -2,4 +2,6 @@
config save tmp/flash.json flash
~aaa
config save tmp/auth.json auth
~cli
config save tmp/runtime.json runtime

View File

@ -1,3 +1,5 @@
~cli
config load tmp/runtime.json runtime
~aaa
config load tmp/auth.json auth
~code

View File

@ -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)

View File

@ -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...)
}

View File

@ -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, "<table>")
result = append(result, fmt.Sprintf("<table class='%s'>", style))
result = append(result, "<caption>", cmd, "</caption>")
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:

View File

@ -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"

View File

@ -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") {

View File

@ -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 {

View File

@ -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", ")"}},

View File

@ -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

View File

@ -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",

View File

@ -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
}},

View File

@ -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) {

View File

@ -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;
}

View File

@ -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) {

View File

@ -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
}

View File

@ -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")

View File

@ -20,6 +20,11 @@
}
</script>
{{end}}
{{define "banner"}}
<fieldset class="{{options . "componet_view"}}" data-init="{{options . "componet_init"}}">
<ul>{{range $key, $item := conf . "componet"}}<li>{{$key}}</li>{{end}}</ul>
</fieldset>
{{end}}
{{define "fieldset"}}
<fieldset class="{{options . "componet_view"}}" data-init="{{options . "componet_init"}}">
<form class="option {{options . "componet_name"}}"

View File

@ -1,12 +1,84 @@
## 后端技术栈》简介
## 后端技术栈
后端技术栈,是对后端服务器开发中所用到的技术进行组合,不断的优化框架结构与服务水平。
- Linux
- Nginx
- Python 是一种脚本语言,
- MySQL
## 单机架构
### LAMP
#### Linux
#### Apache
#### Mysql
#### PHP
### OpenResty
#### Nginx
#### Sqlite3
#### Lua
### Flask
#### Python
### NodeJS+MongoDB
#### NodeJS
#### MongoDB
## 集群架构
### 平台
#### 应用接口
- REST API
#### 反向代理
- nginx
#### 服务发现
- consul
#### 服务治理
#### 代码仓库
- gitlab
#### 虚拟容器
- docker
- linux
- K8S
#### 配置中心
- ETCD
#### 日志监控
#### 同步通信
- Thrift
- gRPC
#### 异步通信
- Kafka
#### 缓冲存储
- Redis
#### 事务存储
- MySQL
#### 搜索引擎
- es
#### 推荐引擎
### 设计
#### DDD
#### 事务性
#### 分布式
#### 高并发
#### 高可用
### 开发
#### 逻辑性
#### 可读性
### 测试
#### 单元测试
#### 集成测试
### 部署
#### 持续集成
#### 持续部署
### 运维
#### DevOps
## 基础入门
### Linux
Linux系统的应用十分广泛有很多流行的桌面操作系统与移动操作系统尤其是在云计算的服务器领域和物联网的嵌入式设备中

View File

@ -0,0 +1,8 @@
## 简介
Kafka
- 官网: <https://kafka.apache.org/>
- 文档: <https://kafka.apache.org/documentation/>
- 源码: <https://archive.apache.org/dist/kafka/0.11.0.3/kafka-0.11.0.3-src.tgz>
- 开源: <https://github.com/apache/kafka/tree/0.11.0>

View File

@ -3,9 +3,9 @@
MySQL 是一个开源的关系型数据库管理系统。
- 官网: <https://www.mysql.com/>
- 源码: <https://dev.mysql.com/downloads/file/?id=482483>
- 文档: <https://dev.mysql.com/doc/refman/5.6/en>
- 开源: <https://github.com/mysql/mysql-server>
- 源码: <https://dev.mysql.com/downloads/file/?id=480541>
- 文档: <https://dev.mysql.com/doc/refman/5.5/en>
- 开源: <https://github.com/mysql/mysql-server/tree/5.5>
## 下载安装
### 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
## 存储引擎
- 源码:<https://github.com/mysql/mysql-server/tree/5.5/storage>
- 文档:<https://dev.mysql.com/doc/refman/5.5/en/storage-engines.html>
```
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事务的存储引擎
- 源码:<https://github.com/mysql/mysql-server/tree/5.5/storage/innobase>
- 文档:<https://dev.mysql.com/doc/refman/5.5/en/innodb-storage-engine.html>
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
- 下载: <https://dev.mysql.com/downloads/mysql/5.6.html#downloads>
- 博客: <http://blog.codinglabs.org/articles/theory-of-mysql-index.html>
变量的定义与引用: <https://www.cnblogs.com/EasonJim/p/7966918.html>
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
- 源码:<https://github.com/mysql/mysql-server/tree/5.5/storage/myisam>
- 文档:<https://dev.mysql.com/doc/refman/5.5/en/myisam-storage-engine.html>

View File

@ -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/)
- 官网: <https://www.nginx.org/>
- 文档: <https://nginx.org/en/docs/>
- 源码: <https://nginx.org/download/nginx-1.0.15.tar.gz>
- 开源: <https://github.com/nginx/nginx/tree/branches/stable-1.0>
## 源码安装
```
$ 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

View File

@ -6,13 +6,12 @@ Redis是最流行的键值对存储数据库。
- 官网: <https://redis.io>
- 源码: <http://download.redis.io/releases/redis-4.0.9.tar.gz>
- 文档: <https://redis.io/documentation>
- 开源: <https://github.com/antirez/redis/tree/4.0>
## 源码安装
```
$ 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;
```
## 源码解析
### 目录解析