libf 2 years ago
parent
commit
e39943066e

+ 64 - 0
chatgpt/add/add.go

@@ -0,0 +1,64 @@
+package main
+
+import (
+	"fmt"
+	"strings"
+)
+
+func add(a, b string) string {
+	// 反转字符串,方便从低位到高位计算
+	a = reverse(a)
+	b = reverse(b)
+
+	// 把字符串转换为数组,方便运算
+	aArr := strings.Split(a, "")
+	bArr := strings.Split(b, "")
+
+	// 判断谁的长度更长,确定循环次数
+	n := len(aArr)
+	if m := len(bArr); m > n {
+		n = m
+	}
+
+	// 初始化结果字符数组,用于存储计算结果
+	res := make([]string, n)
+
+	// 进位标志
+	carry := 0
+
+	// 从低位到高位进行计算
+	for i := 0; i < n; i++ {
+		x := 0
+		if i < len(aArr) {
+			x = int(a[i] - '0')
+		}
+		y := 0
+		if i < len(bArr) {
+			y = int(b[i] - '0')
+		}
+		sum := x + y + carry
+		res[i] = string(sum%10 + '0')
+		carry = sum / 10
+	}
+
+	// 如果最高位有进位,则把进位加入结果字符数组的最前面
+	if carry > 0 {
+		res = append([]string{string(carry + '0')}, res...)
+	}
+
+	// 反转字符数组,得到最终结果
+	return reverse(strings.Join(res, ""))
+}
+
+// 字符串反转函数
+func reverse(s string) string {
+	runes := []rune(s)
+	for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
+		runes[i], runes[j] = runes[j], runes[i]
+	}
+	return string(runes)
+}
+
+func main() {
+	fmt.Println(add("12345678901234567890", "98765432109876543210"))
+}

File diff suppressed because it is too large
+ 1 - 0
chatgpt/iotest/_test/0/1003.txt


File diff suppressed because it is too large
+ 1 - 0
chatgpt/iotest/_test/0/1004.txt


+ 153 - 0
chatgpt/iotest/iotest.go

@@ -0,0 +1,153 @@
+package main
+
+import (
+	"bytes"
+	"encoding/base64"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"math"
+	"math/rand"
+	"os"
+	"path"
+	"path/filepath"
+	"strconv"
+	"sync/atomic"
+	"time"
+
+	"git.wecise.com/wecise/common/matrix/logger"
+)
+
+// 一个持续并发文件读写验证的程序
+// 在指定目录下写入一些随机内容的文本文件
+// 写入完成后读取并验证写入文件内容的正确性
+// 验证正确后在指定的延时时间后将文件删除
+// 文件尺寸可以在指定的范围内随机生成
+// 所有可指定变量以命令行参数形式传入,并设有默认值
+// 每秒在控制台输出验证成功的文件计数信息
+// 验证内容不一致时保留文件不删除,提示验证失败的文件路径,程序结束。
+
+func init() {
+	logger.SetFormat("yyyy-MM-dd HH:mm:ss [pid] [level] msg", "\n")
+	logger.SetRollingFile("", "iotest.log", -1, 1*1024*1024, math.MaxInt64, 1)
+}
+
+func writefile(filePath string, content []byte) (err error) {
+	dir := path.Dir(filePath)
+	f, e := os.Stat(dir)
+	if os.IsNotExist(e) || !f.IsDir() {
+		if err = os.MkdirAll(dir, os.ModePerm); err != nil {
+			return
+		}
+	}
+	// 写入文件
+	err = ioutil.WriteFile(filePath, content, 0777)
+	return
+}
+
+func readfile(filePath string) ([]byte, error) {
+	return ioutil.ReadFile(filePath)
+}
+
+func main() {
+	// 设置命令行参数
+	rootPath := flag.String("root-path", "_test", "文件目录")
+	delay := flag.Duration("delay", 30*time.Second, "删除延迟时间")
+	concurLimit := flag.Int("concur-limit", 10, "最大并发数")
+	fileSizeMin := flag.Int("file-size-min", 1024, "最小文件尺寸")
+	fileSizeMax := flag.Int("file-size-max", 10240, "最大文件尺寸")
+	flag.Parse()
+
+	// 初始化随机数生成器
+	rand.Seed(time.Now().UnixNano())
+
+	type fileinfo struct {
+		filepath string
+		filesize int
+		checksum uint64
+		content  string
+	}
+	checkChan := make(chan *fileinfo, *concurLimit)
+
+	logger.Info("开始验证")
+
+	// 并发写入文件
+	go func() {
+		writeChan := make(chan struct{}, *concurLimit)
+		for i := 0; ; i++ {
+			// 并发控制
+			writeChan <- struct{}{}
+			go func(i int) {
+				defer func() {
+					<-writeChan
+				}()
+				// 随机生成文件名和文件内容
+				dirName := strconv.Itoa(rand.Intn(100))
+				fileName := strconv.Itoa(i) + ".txt"
+				filePath := filepath.Join(*rootPath, dirName, fileName)
+				fileBinarySize := (rand.Intn(*fileSizeMax-*fileSizeMin) + *fileSizeMin) / 4 * 3 // 二进制长度
+
+				// 随机生成文件名和文件内容
+				bs := make([]byte, fileBinarySize)
+				rand.Read(bs)
+				content := base64.RawURLEncoding.EncodeToString(bs)
+
+				// 写入文件
+				if err := writefile(filePath, []byte(content)); err != nil {
+					logger.Error("写入文件", filePath, "失败:", err)
+					return
+				}
+
+				// 写入完成
+				checkChan <- &fileinfo{
+					filepath: filePath,
+					filesize: len(content),
+					content:  content,
+				}
+			}(i)
+		}
+	}()
+
+	var successCount int32
+
+	// 并发验证和删除文件
+	go func() {
+		for fi := range checkChan {
+			go func(fi *fileinfo) {
+				// 读取文件
+				content, err := readfile(fi.filepath)
+				if err != nil {
+					logger.Error("读取文件", fi.filepath, "失败:", err)
+					return
+				}
+
+				// 验证文件内容是否正确
+				expectedSize := fi.filesize
+				if len(content) != expectedSize || !bytes.Equal(content, []byte(fi.content)) {
+					logger.Error("验证失败", fi.filepath, fmt.Sprintf("文件内容与期望值不一致,写入内容为:\n%s\n读出内容为:\n%s", string(content), fi.content))
+					os.Exit(1)
+					return
+				}
+
+				// 验证成功,统计计数
+				atomic.AddInt32(&successCount, 1)
+
+				// 延迟删除文件
+				time.AfterFunc(*delay, func() {
+					if err := os.Remove(fi.filepath); err != nil {
+						logger.Error("删除文件", fi.filepath, "失败:", err)
+					}
+				})
+			}(fi)
+		}
+	}()
+
+	t := time.NewTicker(2 * time.Second)
+	for {
+		select {
+		case <-t.C:
+			// 输出计数信息
+			logger.Info("验证成功的文件数:", successCount)
+		}
+	}
+}

+ 29 - 6
go.mod

@@ -3,8 +3,7 @@ module test
 go 1.19
 
 require (
-	git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10
-	git.wecise.com/wecise/odbserver v0.0.0-20221026163706-f70060221cba
+	git.wecise.com/wecise/common v0.0.0-20221210133957-12acc5754f5b
 	github.com/alphadose/haxmap v1.0.0
 	github.com/atrox/homedir v1.0.0
 	github.com/cornelk/hashmap v1.0.8
@@ -14,7 +13,31 @@ require (
 )
 
 require (
+	github.com/cespare/xxhash v1.1.0 // indirect
+	github.com/cespare/xxhash/v2 v2.1.1 // indirect
+	github.com/dgraph-io/badger/v3 v3.2103.2 // indirect
+	github.com/dgraph-io/ristretto v0.1.0 // indirect
+	github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
+	github.com/dustin/go-humanize v1.0.0 // indirect
+	github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b // indirect
+	github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 // indirect
+	github.com/golang/snappy v0.0.3 // indirect
+	github.com/google/flatbuffers v2.0.0+incompatible // indirect
+	github.com/google/uuid v1.1.2 // indirect
+	github.com/kavu/go_reuseport v1.5.0 // indirect
+	github.com/klauspost/compress v1.13.4 // indirect
+	github.com/mattn/go-runewidth v0.0.9 // indirect
+	github.com/olekukonko/tablewriter v0.0.4 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
+	github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
+	github.com/xtaci/smux v1.5.16 // indirect
+	go.opencensus.io v0.22.5 // indirect
 	google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
 	rsc.io/pdf v0.1.1 // indirect
 )
 
@@ -27,14 +50,14 @@ require (
 	github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80
 	github.com/mattn/go-colorable v0.1.7 // indirect
 	github.com/mattn/go-isatty v0.0.12 // indirect
-	go.etcd.io/etcd/api/v3 v3.5.1 // indirect
-	go.etcd.io/etcd/client/pkg/v3 v3.5.1 // indirect
-	go.etcd.io/etcd/client/v3 v3.5.1 // indirect
+	go.etcd.io/etcd/api/v3 v3.5.6 // indirect
+	go.etcd.io/etcd/client/pkg/v3 v3.5.6 // indirect
+	go.etcd.io/etcd/client/v3 v3.5.6 // indirect
 	go.uber.org/atomic v1.7.0 // indirect
 	go.uber.org/multierr v1.6.0 // indirect
 	go.uber.org/zap v1.17.0 // indirect
 	golang.org/x/net v0.1.0 // indirect
-	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/sys v0.2.0 // indirect
 	golang.org/x/term v0.1.0 // indirect
 	golang.org/x/text v0.4.0 // indirect
 	google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c // indirect

+ 66 - 0
go.sum

@@ -7,12 +7,20 @@ git.wecise.com/wecise/common v0.0.0-20220208112539-631931c6a1b3/go.mod h1:Klk84G
 git.wecise.com/wecise/common v0.0.0-20220922070633-840e60243e5a/go.mod h1:GbtDqXBvApsvqEkkQmo/yjKPgtalRB+h8b4Yu8j9EGc=
 git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10 h1:IGD+Yh12p2kMhORpom+mNzaxex2cmwH5L3akDw37RaQ=
 git.wecise.com/wecise/common v0.0.0-20221012050044-272bf5785d10/go.mod h1:iZzB7N61v5oSQUruMRlqAVDCLDWKvaquiHwaznqg9PM=
+git.wecise.com/wecise/common v0.0.0-20221210133957-12acc5754f5b h1:+4wu4/MRz/KgJwztdAwqITSsCccZ5JmXt0kfFL4nuo0=
+git.wecise.com/wecise/common v0.0.0-20221210133957-12acc5754f5b/go.mod h1:KhkuF5pRD2nwL9WpIU57nhmiXwlQuyoFuwScqiMDr4g=
+git.wecise.com/wecise/common v0.0.0-20230118063853-7221c086e1e6 h1:boC8K7bGiVNOuwMx44qWGgz668fsfK5S16lu8YiqvLk=
+git.wecise.com/wecise/common v0.0.0-20230118063853-7221c086e1e6/go.mod h1:KhkuF5pRD2nwL9WpIU57nhmiXwlQuyoFuwScqiMDr4g=
 git.wecise.com/wecise/odb-go v0.0.0-20211124085409-0d3e38018145/go.mod h1:RUaugeb49Q7CKD1szlFvoVmw657wrVGnt0QMMZrhMTg=
 git.wecise.com/wecise/odb-go v0.0.0-20220825070427-e443cba6de6d/go.mod h1:RUaugeb49Q7CKD1szlFvoVmw657wrVGnt0QMMZrhMTg=
 git.wecise.com/wecise/odbserver v0.0.0-20221017135833-5936b510aa0f h1:C5nR+6yqciHqL1ZHdvRut2Gxp+/HfO79jX2lWkU9di0=
 git.wecise.com/wecise/odbserver v0.0.0-20221017135833-5936b510aa0f/go.mod h1:R+Zt92XIgakZndnhlV59E3weRPo9ZZAoAtIaFyYDQQs=
 git.wecise.com/wecise/odbserver v0.0.0-20221026163706-f70060221cba h1:2joDxINKdBImdLUU3kMrZyUhfgSU3o0sib0yeEYTeZQ=
 git.wecise.com/wecise/odbserver v0.0.0-20221026163706-f70060221cba/go.mod h1:R+Zt92XIgakZndnhlV59E3weRPo9ZZAoAtIaFyYDQQs=
+git.wecise.com/wecise/odbserver v0.0.0-20221211103945-2ca1f096b0a0 h1:FENVhNmdhlIGo7BiSFu6Ck8xMuqtyxyICUakkw9Y9WE=
+git.wecise.com/wecise/odbserver v0.0.0-20221211103945-2ca1f096b0a0/go.mod h1:5v/fNzE+0+8m81/zD40weTrFfP07AJEmyco+xtCKnwA=
+git.wecise.com/wecise/odbserver v0.0.0-20230205114513-b2d67a9e2b75 h1:3Aq9tvJfQmSyl6lsNRtIHTveeuog4cOlQeOnuWxpZ6s=
+git.wecise.com/wecise/odbserver v0.0.0-20230205114513-b2d67a9e2b75/go.mod h1:bfYmGaCYuEWPTFuKfLW/9El5go0sXeA6EHnMfoO3Sf4=
 git.wecise.com/wecise/platform v0.0.0-20220218084958-57446e70d9ea/go.mod h1:9AJEeYzbrNe7vIl7v/3MArD6cJDCc37WX9S1hfot8XA=
 github.com/360EntSecGroup-Skylar/excelize/v2 v2.1.0/go.mod h1:NRW1nxuHjsv3AUisgUneVjItiDPsxKSBBypDwX46bf4=
 github.com/AlekSi/pointer v1.0.0/go.mod h1:1kjywbfcPFCmncIxtk6fIEub6LKrfMz3gc5QKVOSOA8=
@@ -82,7 +90,9 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dR
 github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
 github.com/cdipaolo/goml v0.0.0-20210723214924-bf439dd662aa/go.mod h1:sduMkaHcXDIWurl/Bd/z0rNEUHw5tr6LUA9IO8E9o0o=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
 github.com/chrusty/go-tableprinter v0.0.0-20190528113659-0de6c8f09400/go.mod h1:50rBr+hh7FVL5JPCc7EF7/6b9x+8ChfEOFCsDdf/CyM=
@@ -94,7 +104,12 @@ github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
 github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
 github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
+github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -120,11 +135,15 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dchest/siphash v1.1.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4=
 github.com/ddliu/go-httpclient v0.6.6/go.mod h1:zM9P0OxV4OGGz1pt/ibuj0ooX2SWH9a6MvXZLbT0JMc=
+github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8=
 github.com/dgraph-io/badger/v3 v3.2103.2/go.mod h1:RHo4/GmYcKKh5Lxu63wLEMHJ70Pac2JqZRYGhlyAo2M=
+github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
 github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
 github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
 github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 github.com/elgs/gojq v0.0.0-20201120033525-b5293fef2759/go.mod h1:rQELVIqRXpraeUryHOBadz99ePvEVQmTVpGr8M9QQ4Q=
 github.com/elgs/gosplitargs v0.0.0-20161028071935-a491c5eeb3c8/go.mod h1:o4DgpccPNAQAlPSxo7I4L/LWNh2oyr/BBGSynrLTmZM=
@@ -136,6 +155,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
+github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
+github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
 github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
@@ -183,7 +204,9 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a
 github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
 github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6 h1:ZgQEtGgCBiWRM39fZuwSd1LwSqqSW0hOdXCYYDX0R3I=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
@@ -204,10 +227,12 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
 github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
 github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
 github.com/gomodule/redigo v1.8.5/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
 github.com/google/flatbuffers v1.12.1/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
+github.com/google/flatbuffers v2.0.0+incompatible h1:dicJ2oXwypfwUGnB2/TYWYEKiuk9eYQlQO/AnOHl5mI=
 github.com/google/flatbuffers v2.0.0+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -218,8 +243,10 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
 github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
@@ -271,6 +298,7 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8
 github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
 github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
+github.com/kavu/go_reuseport v1.5.0 h1:UNuiY2OblcqAtVDE8Gsg1kZz8zbBWg907sP1ceBV+bk=
 github.com/kavu/go_reuseport v1.5.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
 github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
 github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@@ -281,6 +309,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
 github.com/klauspost/compress v1.11.12/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
 github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
+github.com/klauspost/compress v1.13.4 h1:0zhec2I8zGnjWcKyLl6i3gPqKANCCn5e9xmviEEeX6s=
 github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
 github.com/kljensen/snowball v0.6.0/go.mod h1:27N7E8fVU5H68RlUmnWwZCfxgt4POBJfENGMvNRhldw=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -315,6 +344,7 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky
 github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
+github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
 github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -338,6 +368,7 @@ github.com/nats-io/nats-server/v2 v2.3.0/go.mod h1:7v4HvHI2Zu4n1775982gHbvBNXywH
 github.com/nats-io/nats.go v1.11.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nats.go v1.13.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
 github.com/nats-io/nats.go v1.17.0/go.mod h1:BPko4oXsySz4aSWeFgOHLZs3G4Jq4ZAyE6/zMCxRT6w=
+github.com/nats-io/nats.go v1.20.0/go.mod h1:tLqubohF7t4z3du1QDPYJIQQyhb4wl6DhjxEajSI7UA=
 github.com/nats-io/nkeys v0.2.0/go.mod h1:XdZpAbhgyyODYqjTawOnIOI7VlbKSarI9Gfy1tqEu/s=
 github.com/nats-io/nkeys v0.3.0/go.mod h1:gvUNGjVcM2IPr5rCsRsC6Wb3Hr2CQAm08dsxtV6A5y4=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
@@ -345,6 +376,7 @@ github.com/nleeper/goment v1.4.2/go.mod h1:zDl5bAyDhqxwQKAvkSXMRLOdCowrdZz53ofRJ
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/olekukonko/tablewriter v0.0.0-20180506121414-d4647c9c7a84/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
+github.com/olekukonko/tablewriter v0.0.4 h1:vHD/YYe1Wolo78koG299f7V/VAS08c6IpCLn+Ejf/w8=
 github.com/olekukonko/tablewriter v0.0.4/go.mod h1:zq6QwlOf5SlnkVbMSr5EoBv3636FWnp+qbPhuoO21uA=
 github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
@@ -378,6 +410,7 @@ github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4
 github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
 github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
+github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
 github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -402,6 +435,7 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L
 github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
+github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/shawnsmithdev/zermelo v1.0.4/go.mod h1:q5IpahxP6gXqPPW7vt+CwZsFFYtWAL+yfMFX/yrNLOo=
@@ -418,6 +452,7 @@ github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasO
 github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
 github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
 github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
@@ -463,7 +498,9 @@ github.com/unidoc/pkcs7 v0.0.0-20200411230602-d883fd70d1df/go.mod h1:UEzOZUEpJfD
 github.com/unidoc/timestamp v0.0.0-20200412005513-91597fd3793a/go.mod h1:j+qMWZVpZFTvDey3zxUkSgPJZEX33tDgU/QIA0IzCUw=
 github.com/unidoc/unitype v0.2.0/go.mod h1:mafyug7zYmDOusqa7G0dJV45qp4b6TDAN+pHN7ZUIBU=
 github.com/vcaesar/tt v0.11.0/go.mod h1:GHPxQYhn+7OgKakRusH7KJ0M5MhywoeLb8Fcffs/Gtg=
+github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
 github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
+github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 github.com/wangjia184/sortedset v0.0.0-20160527075905-f5d03557ba30/go.mod h1:YkocrP2K2tcw938x9gCOmT5G5eCD6jsTz0SZuyAqwIE=
 github.com/weaveworks/mesh v0.0.0-20191105120815-58dbcc3e8e63/go.mod h1:RZebXKv56dax5zXcLIJZm1Awk28sx0XODXF94Z8WssY=
@@ -471,19 +508,28 @@ github.com/willf/bitset v1.1.10/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPy
 github.com/willf/bloom v2.0.3+incompatible/go.mod h1:MmAltL9pDMNTrvUkxdg0k0q5I0suxmuwp3KbyrZLOZ8=
 github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/xtaci/smux v1.5.16 h1:FBPYOkW8ZTjLKUM4LI4xnnuuDC8CQ/dB04HD519WoEk=
 github.com/xtaci/smux v1.5.16/go.mod h1:OMlQbT5vcgl2gb49mFkYo6SMf+zP3rcjcwQz7ZU7IGY=
 github.com/yourbasic/radix v0.0.0-20180308122924-cbe1cc82e907/go.mod h1:/7Fy/4/OyrkguTf2i2pO4erUD/8QAlrlmXSdSJPu678=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
 github.com/yuin/gopher-lua v0.0.0-20210529063254-f4c35e4016d9/go.mod h1:E1AXubJBdNmFERAOucpDIxNzeGfLzg0mYh+UfMWdChA=
 go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
 go.etcd.io/etcd/api/v3 v3.5.1 h1:v28cktvBq+7vGyJXF8G+rWJmj+1XUmMtqcLnH8hDocM=
 go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
+go.etcd.io/etcd/api/v3 v3.5.6 h1:Cy2qx3npLcYqTKqGJzMypnMv2tiRyifZJ17BlWIWA7A=
+go.etcd.io/etcd/api/v3 v3.5.6/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8=
 go.etcd.io/etcd/client/pkg/v3 v3.5.1 h1:XIQcHCFSG53bJETYeRJtIxdLv2EWRGxcfzR8lSnTH4E=
 go.etcd.io/etcd/client/pkg/v3 v3.5.1/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/pkg/v3 v3.5.6 h1:TXQWYceBKqLp4sa87rcPs11SXxUA/mHwH975v+BDvLU=
+go.etcd.io/etcd/client/pkg/v3 v3.5.6/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ=
 go.etcd.io/etcd/client/v3 v3.5.1 h1:oImGuV5LGKjCqXdjkMHCyWa5OO1gYKCnC/1sgdfj1Uk=
 go.etcd.io/etcd/client/v3 v3.5.1/go.mod h1:OnjH4M8OnAotwaB2l9bVgZzRFKru7/ZMoS46OtKyd3Q=
+go.etcd.io/etcd/client/v3 v3.5.6 h1:coLs69PWCXE9G4FKquzNaSHrRyMCAXwF+IX1tAPVO8E=
+go.etcd.io/etcd/client/v3 v3.5.6/go.mod h1:f6GRinRMCsFVv9Ht42EyY7nfsVGwrNO0WEoS2pRKzQk=
+go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
@@ -509,6 +555,7 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP
 golang.org/x/crypto v0.0.0-20210314154223-e6e6c4f2bb5b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
 golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
 golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
 golang.org/x/crypto v0.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU=
 golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
 golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -517,6 +564,7 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL
 golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20191002040644-a1355ae1e2c3/go.mod h1:NOZ3BPKG0ec/BKJQgnvsSFpcKLM5xXVWnvZS97DWHgE=
+golang.org/x/exp v0.0.0-20221204150635-6dcec336b2bb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
 golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20181116024801-cd38e8056d9b/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -538,6 +586,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
 golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -567,6 +617,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
 golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
 golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
 golang.org/x/net v0.0.0-20210716203947-853a461950ff/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -581,6 +632,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -616,6 +668,7 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210304124612-50617c2ba197/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -625,11 +678,16 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
+golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -637,6 +695,7 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -661,6 +720,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -694,6 +755,7 @@ google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
 google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.39.0 h1:Klz8I9kdtkIN6EpHHUOMLCYhTn/2WAe5a0s1hcBkdTI=
 google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
+google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
 google.golang.org/grpc v1.50.1 h1:DS/BukOZWp8s6p4Dt/tOaJaTQyPyOoCcrjroHuCeLzY=
 google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
 google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0=
@@ -722,6 +784,8 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 gopkg.in/ini.v1 v1.52.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
 gopkg.in/readline.v1 v1.0.0-20160726135117-62c6fe619375/go.mod h1:lNEQeAhU009zbRxng+XOj5ITVgY24WcbNnQopyfKoYQ=
 gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
@@ -743,6 +807,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
 gopkg.in/yaml.v3 v3.0.0-20200602174320-3e3e88ca92fa/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=

+ 16 - 0
grpc-odbserver/api/api.go

@@ -0,0 +1,16 @@
+package api
+
+type Service interface {
+	ID() int64
+	Name() string
+	Init()
+	Destroy()
+	Start()
+	Stop()
+}
+
+type Node interface {
+	Service
+	RegisterService(Service)
+	UnregisterService(Service)
+}

+ 307 - 0
grpc-odbserver/grpc/api/mesh.pb.go

@@ -0,0 +1,307 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.28.1-devel
+// 	protoc        v3.21.9
+// source: proto/mesh.proto
+
+package api
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type MeshData struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to Message:
+	//
+	//	*MeshData_NodeInfo
+	Message isMeshData_Message `protobuf_oneof:"Message"`
+}
+
+func (x *MeshData) Reset() {
+	*x = MeshData{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_mesh_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MeshData) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MeshData) ProtoMessage() {}
+
+func (x *MeshData) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_mesh_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MeshData.ProtoReflect.Descriptor instead.
+func (*MeshData) Descriptor() ([]byte, []int) {
+	return file_proto_mesh_proto_rawDescGZIP(), []int{0}
+}
+
+func (m *MeshData) GetMessage() isMeshData_Message {
+	if m != nil {
+		return m.Message
+	}
+	return nil
+}
+
+func (x *MeshData) GetNodeInfo() *NodeInfo {
+	if x, ok := x.GetMessage().(*MeshData_NodeInfo); ok {
+		return x.NodeInfo
+	}
+	return nil
+}
+
+type isMeshData_Message interface {
+	isMeshData_Message()
+}
+
+type MeshData_NodeInfo struct {
+	NodeInfo *NodeInfo `protobuf:"bytes,1,opt,name=NodeInfo,proto3,oneof"`
+}
+
+func (*MeshData_NodeInfo) isMeshData_Message() {}
+
+type NodeInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	NodeID    int64    `protobuf:"varint,1,opt,name=NodeID,proto3" json:"NodeID,omitempty"`
+	NodeName  string   `protobuf:"bytes,2,opt,name=NodeName,proto3" json:"NodeName,omitempty"`
+	StartTime int64    `protobuf:"varint,3,opt,name=StartTime,proto3" json:"StartTime,omitempty"`
+	Address   []string `protobuf:"bytes,4,rep,name=Address,proto3" json:"Address,omitempty"`
+	DC        []string `protobuf:"bytes,5,rep,name=DC,proto3" json:"DC,omitempty"`
+	Group     []string `protobuf:"bytes,6,rep,name=Group,proto3" json:"Group,omitempty"`
+	Service   []string `protobuf:"bytes,7,rep,name=Service,proto3" json:"Service,omitempty"`
+	PeerIDs   []int64  `protobuf:"varint,8,rep,packed,name=PeerIDs,proto3" json:"PeerIDs,omitempty"`
+}
+
+func (x *NodeInfo) Reset() {
+	*x = NodeInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_proto_mesh_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NodeInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NodeInfo) ProtoMessage() {}
+
+func (x *NodeInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_proto_mesh_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NodeInfo.ProtoReflect.Descriptor instead.
+func (*NodeInfo) Descriptor() ([]byte, []int) {
+	return file_proto_mesh_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *NodeInfo) GetNodeID() int64 {
+	if x != nil {
+		return x.NodeID
+	}
+	return 0
+}
+
+func (x *NodeInfo) GetNodeName() string {
+	if x != nil {
+		return x.NodeName
+	}
+	return ""
+}
+
+func (x *NodeInfo) GetStartTime() int64 {
+	if x != nil {
+		return x.StartTime
+	}
+	return 0
+}
+
+func (x *NodeInfo) GetAddress() []string {
+	if x != nil {
+		return x.Address
+	}
+	return nil
+}
+
+func (x *NodeInfo) GetDC() []string {
+	if x != nil {
+		return x.DC
+	}
+	return nil
+}
+
+func (x *NodeInfo) GetGroup() []string {
+	if x != nil {
+		return x.Group
+	}
+	return nil
+}
+
+func (x *NodeInfo) GetService() []string {
+	if x != nil {
+		return x.Service
+	}
+	return nil
+}
+
+func (x *NodeInfo) GetPeerIDs() []int64 {
+	if x != nil {
+		return x.PeerIDs
+	}
+	return nil
+}
+
+var File_proto_mesh_proto protoreflect.FileDescriptor
+
+var file_proto_mesh_proto_rawDesc = []byte{
+	0x0a, 0x10, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6d, 0x65, 0x73, 0x68, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x05, 0x6d, 0x72, 0x69, 0x6e, 0x67, 0x22, 0x44, 0x0a, 0x08, 0x4d, 0x65, 0x73,
+	0x68, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2d, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66,
+	0x6f, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x6d, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+	0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08, 0x4e, 0x6f, 0x64, 0x65,
+	0x49, 0x6e, 0x66, 0x6f, 0x42, 0x09, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22,
+	0xd0, 0x01, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06,
+	0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x4e, 0x6f,
+	0x64, 0x65, 0x49, 0x44, 0x12, 0x1a, 0x0a, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x4e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65,
+	0x12, 0x1c, 0x0a, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x03, 0x52, 0x09, 0x53, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x18,
+	0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x44, 0x43, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x02, 0x44, 0x43, 0x12, 0x14, 0x0a, 0x05, 0x47, 0x72, 0x6f, 0x75,
+	0x70, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x47, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x18,
+	0x0a, 0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x07, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x65, 0x65, 0x72,
+	0x49, 0x44, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x03, 0x52, 0x07, 0x50, 0x65, 0x65, 0x72, 0x49,
+	0x44, 0x73, 0x32, 0x6e, 0x0a, 0x04, 0x4d, 0x65, 0x73, 0x68, 0x12, 0x33, 0x0a, 0x09, 0x42, 0x72,
+	0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x0f, 0x2e, 0x6d, 0x72, 0x69, 0x6e, 0x67, 0x2e,
+	0x4d, 0x65, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0f, 0x2e, 0x6d, 0x72, 0x69, 0x6e, 0x67,
+	0x2e, 0x4d, 0x65, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x12,
+	0x31, 0x0a, 0x07, 0x55, 0x6e, 0x69, 0x63, 0x61, 0x73, 0x74, 0x12, 0x0f, 0x2e, 0x6d, 0x72, 0x69,
+	0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x1a, 0x0f, 0x2e, 0x6d, 0x72,
+	0x69, 0x6e, 0x67, 0x2e, 0x4d, 0x65, 0x73, 0x68, 0x44, 0x61, 0x74, 0x61, 0x22, 0x00, 0x28, 0x01,
+	0x30, 0x01, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+	file_proto_mesh_proto_rawDescOnce sync.Once
+	file_proto_mesh_proto_rawDescData = file_proto_mesh_proto_rawDesc
+)
+
+func file_proto_mesh_proto_rawDescGZIP() []byte {
+	file_proto_mesh_proto_rawDescOnce.Do(func() {
+		file_proto_mesh_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_mesh_proto_rawDescData)
+	})
+	return file_proto_mesh_proto_rawDescData
+}
+
+var file_proto_mesh_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_proto_mesh_proto_goTypes = []interface{}{
+	(*MeshData)(nil), // 0: mring.MeshData
+	(*NodeInfo)(nil), // 1: mring.NodeInfo
+}
+var file_proto_mesh_proto_depIdxs = []int32{
+	1, // 0: mring.MeshData.NodeInfo:type_name -> mring.NodeInfo
+	0, // 1: mring.Mesh.Broadcast:input_type -> mring.MeshData
+	0, // 2: mring.Mesh.Unicast:input_type -> mring.MeshData
+	0, // 3: mring.Mesh.Broadcast:output_type -> mring.MeshData
+	0, // 4: mring.Mesh.Unicast:output_type -> mring.MeshData
+	3, // [3:5] is the sub-list for method output_type
+	1, // [1:3] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_proto_mesh_proto_init() }
+func file_proto_mesh_proto_init() {
+	if File_proto_mesh_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_proto_mesh_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MeshData); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_proto_mesh_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NodeInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_proto_mesh_proto_msgTypes[0].OneofWrappers = []interface{}{
+		(*MeshData_NodeInfo)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_proto_mesh_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   1,
+		},
+		GoTypes:           file_proto_mesh_proto_goTypes,
+		DependencyIndexes: file_proto_mesh_proto_depIdxs,
+		MessageInfos:      file_proto_mesh_proto_msgTypes,
+	}.Build()
+	File_proto_mesh_proto = out.File
+	file_proto_mesh_proto_rawDesc = nil
+	file_proto_mesh_proto_goTypes = nil
+	file_proto_mesh_proto_depIdxs = nil
+}

+ 205 - 0
grpc-odbserver/grpc/api/mesh_grpc.pb.go

@@ -0,0 +1,205 @@
+// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
+// versions:
+// - protoc-gen-go-grpc v1.2.0
+// - protoc             v3.21.9
+// source: proto/mesh.proto
+
+package api
+
+import (
+	context "context"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+)
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+// Requires gRPC-Go v1.32.0 or later.
+const _ = grpc.SupportPackageIsVersion7
+
+// MeshClient is the client API for Mesh service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
+type MeshClient interface {
+	Broadcast(ctx context.Context, opts ...grpc.CallOption) (Mesh_BroadcastClient, error)
+	Unicast(ctx context.Context, opts ...grpc.CallOption) (Mesh_UnicastClient, error)
+}
+
+type meshClient struct {
+	cc grpc.ClientConnInterface
+}
+
+func NewMeshClient(cc grpc.ClientConnInterface) MeshClient {
+	return &meshClient{cc}
+}
+
+func (c *meshClient) Broadcast(ctx context.Context, opts ...grpc.CallOption) (Mesh_BroadcastClient, error) {
+	stream, err := c.cc.NewStream(ctx, &Mesh_ServiceDesc.Streams[0], "/mring.Mesh/Broadcast", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &meshBroadcastClient{stream}
+	return x, nil
+}
+
+type Mesh_BroadcastClient interface {
+	Send(*MeshData) error
+	Recv() (*MeshData, error)
+	grpc.ClientStream
+}
+
+type meshBroadcastClient struct {
+	grpc.ClientStream
+}
+
+func (x *meshBroadcastClient) Send(m *MeshData) error {
+	return x.ClientStream.SendMsg(m)
+}
+
+func (x *meshBroadcastClient) Recv() (*MeshData, error) {
+	m := new(MeshData)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func (c *meshClient) Unicast(ctx context.Context, opts ...grpc.CallOption) (Mesh_UnicastClient, error) {
+	stream, err := c.cc.NewStream(ctx, &Mesh_ServiceDesc.Streams[1], "/mring.Mesh/Unicast", opts...)
+	if err != nil {
+		return nil, err
+	}
+	x := &meshUnicastClient{stream}
+	return x, nil
+}
+
+type Mesh_UnicastClient interface {
+	Send(*MeshData) error
+	Recv() (*MeshData, error)
+	grpc.ClientStream
+}
+
+type meshUnicastClient struct {
+	grpc.ClientStream
+}
+
+func (x *meshUnicastClient) Send(m *MeshData) error {
+	return x.ClientStream.SendMsg(m)
+}
+
+func (x *meshUnicastClient) Recv() (*MeshData, error) {
+	m := new(MeshData)
+	if err := x.ClientStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// MeshServer is the server API for Mesh service.
+// All implementations must embed UnimplementedMeshServer
+// for forward compatibility
+type MeshServer interface {
+	Broadcast(Mesh_BroadcastServer) error
+	Unicast(Mesh_UnicastServer) error
+	mustEmbedUnimplementedMeshServer()
+}
+
+// UnimplementedMeshServer must be embedded to have forward compatible implementations.
+type UnimplementedMeshServer struct {
+}
+
+func (UnimplementedMeshServer) Broadcast(Mesh_BroadcastServer) error {
+	return status.Errorf(codes.Unimplemented, "method Broadcast not implemented")
+}
+func (UnimplementedMeshServer) Unicast(Mesh_UnicastServer) error {
+	return status.Errorf(codes.Unimplemented, "method Unicast not implemented")
+}
+func (UnimplementedMeshServer) mustEmbedUnimplementedMeshServer() {}
+
+// UnsafeMeshServer may be embedded to opt out of forward compatibility for this service.
+// Use of this interface is not recommended, as added methods to MeshServer will
+// result in compilation errors.
+type UnsafeMeshServer interface {
+	mustEmbedUnimplementedMeshServer()
+}
+
+func RegisterMeshServer(s grpc.ServiceRegistrar, srv MeshServer) {
+	s.RegisterService(&Mesh_ServiceDesc, srv)
+}
+
+func _Mesh_Broadcast_Handler(srv interface{}, stream grpc.ServerStream) error {
+	return srv.(MeshServer).Broadcast(&meshBroadcastServer{stream})
+}
+
+type Mesh_BroadcastServer interface {
+	Send(*MeshData) error
+	Recv() (*MeshData, error)
+	grpc.ServerStream
+}
+
+type meshBroadcastServer struct {
+	grpc.ServerStream
+}
+
+func (x *meshBroadcastServer) Send(m *MeshData) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func (x *meshBroadcastServer) Recv() (*MeshData, error) {
+	m := new(MeshData)
+	if err := x.ServerStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+func _Mesh_Unicast_Handler(srv interface{}, stream grpc.ServerStream) error {
+	return srv.(MeshServer).Unicast(&meshUnicastServer{stream})
+}
+
+type Mesh_UnicastServer interface {
+	Send(*MeshData) error
+	Recv() (*MeshData, error)
+	grpc.ServerStream
+}
+
+type meshUnicastServer struct {
+	grpc.ServerStream
+}
+
+func (x *meshUnicastServer) Send(m *MeshData) error {
+	return x.ServerStream.SendMsg(m)
+}
+
+func (x *meshUnicastServer) Recv() (*MeshData, error) {
+	m := new(MeshData)
+	if err := x.ServerStream.RecvMsg(m); err != nil {
+		return nil, err
+	}
+	return m, nil
+}
+
+// Mesh_ServiceDesc is the grpc.ServiceDesc for Mesh service.
+// It's only intended for direct use with grpc.RegisterService,
+// and not to be introspected or modified (even as a copy)
+var Mesh_ServiceDesc = grpc.ServiceDesc{
+	ServiceName: "mring.Mesh",
+	HandlerType: (*MeshServer)(nil),
+	Methods:     []grpc.MethodDesc{},
+	Streams: []grpc.StreamDesc{
+		{
+			StreamName:    "Broadcast",
+			Handler:       _Mesh_Broadcast_Handler,
+			ServerStreams: true,
+			ClientStreams: true,
+		},
+		{
+			StreamName:    "Unicast",
+			Handler:       _Mesh_Unicast_Handler,
+			ServerStreams: true,
+			ClientStreams: true,
+		},
+	},
+	Metadata: "proto/mesh.proto",
+}

+ 17 - 0
grpc-odbserver/grpc/gen.sh

@@ -0,0 +1,17 @@
+
+p() {
+    protoname=$1
+    protofile=proto/${protoname}.proto
+    gopackage=./api
+    echo protoc --go_out=M${protofile}=${gopackage}:. --go-grpc_out=M${protofile}=${gopackage}:. ${protofile}
+    protoc --go_out=M${protofile}=${gopackage}:. --go-grpc_out=M${protofile}=${gopackage}:. ${protofile}
+}
+
+pv() {
+    files=(` ls proto | sed 's/.proto//' `)
+    for file in ${files[@]}; do
+        p ${file}
+    done
+}
+
+pv

+ 21 - 0
grpc-odbserver/grpc/proto/mesh.proto

@@ -0,0 +1,21 @@
+syntax = "proto3"; //The .proto file version is defined as v3
+package mring; //The package name
+service Mesh { //The service name
+    rpc Broadcast(stream MeshData) returns(stream MeshData) {};
+    rpc Unicast(stream MeshData) returns(stream MeshData) {};
+}
+message MeshData {
+    oneof Message {
+        NodeInfo NodeInfo = 1;
+    }
+}
+message NodeInfo {
+    int64  NodeID = 1;
+    string NodeName = 2;
+    int64  StartTime = 3;
+    repeated string Address = 4;
+    repeated string DC = 5;
+    repeated string Group = 6;
+    repeated string Service = 7;
+    repeated int64 PeerIDs = 8;
+}

+ 21 - 0
grpc-odbserver/grpc/proto/msn.proto

@@ -0,0 +1,21 @@
+syntax = "proto3"; //The .proto file version is defined as v3
+package mring; //The package name
+service Msn { //The service name
+    rpc Broadcast(stream MeshData) returns(stream MeshData) {};
+    rpc Unicast(stream MeshData) returns(stream MeshData) {};
+}
+message MeshData {
+    oneof Message {
+        NodeInfo NodeInfo = 1;
+    }
+}
+message NodeInfo {
+    int64  NodeID = 1;
+    string NodeName = 2;
+    int64  StartTime = 3;
+    repeated string Address = 4;
+    repeated string DC = 5;
+    repeated string Group = 6;
+    repeated string Service = 7;
+    repeated int64 PeerIDs = 8;
+}

+ 85 - 0
grpc-odbserver/grpc/test/test.go

@@ -0,0 +1,85 @@
+package main
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"time"
+
+	"git.wecise.com/wecise/odbserver/trial/grpc/api"
+	"google.golang.org/grpc"
+	"gopkg.in/yaml.v3"
+)
+
+type MeshServer struct {
+	api.UnimplementedMeshServer
+}
+
+func (ts *MeshServer) Broadcast(tss api.Mesh_BroadcastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+func (ts *MeshServer) Unicast(tss api.Mesh_UnicastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+var listen_ready = make(chan struct{})
+
+func server() {
+	svr := grpc.NewServer()
+	api.RegisterMeshServer(svr, &MeshServer{})
+	lis, _ := net.Listen("tcp", ":11111")
+	listen_ready <- struct{}{}
+	log.Printf("listen: %v", lis.Addr())
+	err := svr.Serve(lis)
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+}
+
+func client() {
+	cc, err := grpc.Dial("test", grpc.WithInsecure(), grpc.WithContextDialer(func(ctx context.Context, s string) (net.Conn, error) {
+		return net.Dial("tcp", "127.0.0.1:11111")
+	}))
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+	defer cc.Close()
+	tc := api.NewMeshClient(cc)
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	tsc, _ := tc.Broadcast(ctx)
+	n := 0
+	for {
+		n++
+		tsc.Send(&api.MeshData{
+			Message: &api.MeshData_NodeInfo{NodeInfo: &api.NodeInfo{
+				NodeID:   time.Now().UnixNano(),
+				NodeName: fmt.Sprint("xxx", n),
+			}},
+		})
+		log.Print("send", n)
+		time.Sleep(5 * time.Second)
+	}
+}
+
+func main() {
+	go server()
+	<-listen_ready
+	client()
+}

+ 59 - 0
grpc-odbserver/main.go

@@ -0,0 +1,59 @@
+package main
+
+import (
+	"os"
+	"os/signal"
+	"syscall"
+
+	"git.wecise.com/wecise/common/matrix/logger"
+	"git.wecise.com/wecise/common/matrix/util"
+	z "git.wecise.com/wecise/odbserver/mring/z/m"
+	"git.wecise.com/wecise/odbserver/trial/mnode"
+	"git.wecise.com/wecise/odbserver/trial/msn"
+)
+
+var _ = z.RunShadowCount(5, 0, 0, -1)
+
+var log = logger.New()
+
+var node = &mnode.MNode{}
+var svcmsn = &msn.MessengerService{}
+
+func start() {
+	node.Init()
+	node.RegisterService(svcmsn)
+	node.Start()
+}
+
+func run() {
+	msgr := msn.GetMessenger()
+	msgr.Subscribe()
+	msgr.Publish()
+}
+
+func stop() {
+	node.Stop()
+	node.Destroy()
+}
+
+func main() {
+	defer func() {
+		e := util.ErrorWithSourceLine(recover())
+		if e != nil {
+			log.Error(e)
+		}
+	}()
+	exitChan := make(chan os.Signal)
+	signal.Notify(exitChan, os.Interrupt, os.Kill, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGABRT, syscall.SIGTERM)
+	start()
+	log.Print(node.Name(), "启动")
+	//
+	go run()
+	//
+	s := <-exitChan
+	if log != nil {
+		log.Print("收到退出信号", s)
+	}
+	stop()
+	log.Print(node.Name(), "停止")
+}

+ 83 - 0
grpc-odbserver/matrix/topo.go

@@ -0,0 +1,83 @@
+package mnode
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"net"
+	"time"
+
+	"git.wecise.com/wecise/odbserver/trial/grpc/api"
+	"google.golang.org/grpc"
+	"gopkg.in/yaml.v3"
+)
+
+type MeshServer struct {
+	api.UnimplementedMeshServer
+}
+
+func (ts *MeshServer) Broadcast(tss api.Mesh_BroadcastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+func (ts *MeshServer) Unicast(tss api.Mesh_UnicastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+var listen_ready = make(chan struct{})
+
+func server() {
+	svr := grpc.NewServer()
+	api.RegisterMeshServer(svr, &MeshServer{})
+	lis, _ := net.Listen("tcp", ":11111")
+	listen_ready <- struct{}{}
+	log.Printf("listen: %v", lis.Addr())
+	err := svr.Serve(lis)
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+}
+
+func client() {
+	cc, err := grpc.Dial("127.0.0.1:11111", grpc.WithInsecure())
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+	defer cc.Close()
+	tc := api.NewMeshClient(cc)
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	tsc, _ := tc.Broadcast(ctx)
+	n := 0
+	for {
+		n++
+		tsc.Send(&api.MeshData{
+			Message: &api.MeshData_NodeInfo{NodeInfo: &api.NodeInfo{
+				NodeID:   time.Now().UnixNano(),
+				NodeName: fmt.Sprint("xxx", n),
+			}},
+		})
+		log.Print("send", n)
+		time.Sleep(5 * time.Second)
+	}
+}
+
+func main() {
+	go server()
+	<-listen_ready
+	client()
+}

+ 97 - 0
grpc-odbserver/mnode/mnode.go

@@ -0,0 +1,97 @@
+package mnode
+
+import (
+	"sync"
+
+	"git.wecise.com/wecise/odbserver/mring/matrix"
+	"git.wecise.com/wecise/odbserver/mring/mnet"
+	"git.wecise.com/wecise/odbserver/trial/api"
+)
+
+const UNKOWN = 0
+const RUNNING = 1
+const SUSPEND = 2
+
+type MNode struct {
+	listener mnet.Listener
+	mutex    sync.Mutex
+	status   int
+	services map[string]api.Service
+}
+
+var _ = api.Node((*MNode)(nil))
+
+func (mn *MNode) ID() int64 {
+	return int64(matrix.ID)
+}
+
+func (mn *MNode) Name() string {
+	return mnet.MClusterNode(matrix.ID).Name()
+}
+
+func (mn *MNode) Init() {
+	mn.services = make(map[string]api.Service)
+}
+
+func (mn *MNode) Destroy() {
+	mn.Stop()
+	for _, svc := range mn.services {
+		svc.Destroy()
+	}
+	mn.services = nil
+}
+
+func (mn *MNode) RegisterService(svc api.Service) {
+	svc.Init()
+	mn.mutex.Lock()
+	defer mn.mutex.Unlock()
+	if osvc, ok := mn.services[svc.Name()]; ok {
+		osvc.Stop()
+		osvc.Destroy()
+	}
+	mn.services[svc.Name()] = svc
+	if mn.status == RUNNING {
+		svc.Start()
+	}
+}
+
+func (mn *MNode) UnregisterService(svc api.Service) {
+	mn.mutex.Lock()
+	defer mn.mutex.Unlock()
+	if osvc, ok := mn.services[svc.Name()]; ok && osvc == svc {
+		osvc.Stop()
+		osvc.Destroy()
+	}
+}
+
+func (mn *MNode) Start() {
+	mn.mutex.Lock()
+	defer mn.mutex.Unlock()
+	if mn.status == RUNNING {
+		return
+	}
+	cfg := matrix.Config()
+	listener, err := mnet.MListener(cfg.NodeName, cfg.Port, nil).Listen()
+	if err != nil {
+		panic(err)
+	}
+	mn.listener = listener
+	for _, svc := range mn.services {
+		svc.Start()
+	}
+	mn.status = RUNNING
+}
+
+func (mn *MNode) Stop() {
+	mn.mutex.Lock()
+	defer mn.mutex.Unlock()
+	if mn.status == SUSPEND {
+		return
+	}
+	mn.status = SUSPEND
+	for _, svc := range mn.services {
+		svc.Stop()
+	}
+	mn.listener.Close()
+	mn.listener = nil
+}

+ 9 - 0
grpc-odbserver/mnode/net.go

@@ -0,0 +1,9 @@
+package mnode
+
+import (
+	"net"
+)
+
+func Listen(address string) (net.Listener, error) {
+	return net.Listen("tcp", address)
+}

+ 82 - 0
grpc-odbserver/mnode/topo.go

@@ -0,0 +1,82 @@
+package mnode
+
+import (
+	"context"
+	"fmt"
+	"log"
+	"time"
+
+	"git.wecise.com/wecise/odbserver/trial/grpc/api"
+	"google.golang.org/grpc"
+	"gopkg.in/yaml.v3"
+)
+
+type MeshServer struct {
+	api.UnimplementedMeshServer
+}
+
+func (ts *MeshServer) Broadcast(tss api.Mesh_BroadcastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+func (ts *MeshServer) Unicast(tss api.Mesh_UnicastServer) error {
+	for {
+		ti, e := tss.Recv()
+		if e != nil {
+			return e
+		}
+		bs, _ := yaml.Marshal(ti.GetNodeInfo())
+		fmt.Println(string(bs))
+	}
+}
+
+var listen_ready = make(chan struct{})
+
+func server() {
+	svr := grpc.NewServer()
+	api.RegisterMeshServer(svr, &MeshServer{})
+	lis, _ := Listen(":11111")
+	listen_ready <- struct{}{}
+	log.Printf("listen: %v", lis.Addr())
+	err := svr.Serve(lis)
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+}
+
+func client() {
+	cc, err := grpc.Dial("127.0.0.1:11111", grpc.WithInsecure())
+	if err != nil {
+		log.Printf("did not connect: %v", err)
+	}
+	defer cc.Close()
+	tc := api.NewMeshClient(cc)
+	ctx, cancel := context.WithCancel(context.Background())
+	defer cancel()
+	tsc, _ := tc.Broadcast(ctx)
+	n := 0
+	for {
+		n++
+		tsc.Send(&api.MeshData{
+			Message: &api.MeshData_NodeInfo{NodeInfo: &api.NodeInfo{
+				NodeID:   time.Now().UnixNano(),
+				NodeName: fmt.Sprint("xxx", n),
+			}},
+		})
+		log.Print("send", n)
+		time.Sleep(5 * time.Second)
+	}
+}
+
+func main() {
+	go server()
+	<-listen_ready
+	client()
+}

+ 17 - 0
grpc-odbserver/msn/msgr.go

@@ -0,0 +1,17 @@
+package msn
+
+type Messenger interface {
+	Subscribe()
+	Publish()
+}
+
+type mMessenger struct {
+}
+
+func (mm *mMessenger) Subscribe() {
+
+}
+
+func (mm *mMessenger) Publish() {
+
+}

+ 28 - 0
grpc-odbserver/msn/msn.go

@@ -0,0 +1,28 @@
+package msn
+
+type MessengerService struct {
+}
+
+func (ms *MessengerService) ID() int64 {
+	return 0
+}
+
+func (ms *MessengerService) Name() string {
+	return ""
+}
+
+func (ms *MessengerService) Init() {
+
+}
+
+func (ms *MessengerService) Destroy() {
+
+}
+
+func (ms *MessengerService) Start() {
+
+}
+
+func (ms *MessengerService) Stop() {
+
+}

+ 12 - 0
grpc-odbserver/msn/pool.go

@@ -0,0 +1,12 @@
+package msn
+
+type MessengerPool struct {
+}
+
+func (mp *MessengerPool) GetMessenger() Messenger {
+	return nil
+}
+
+func GetMessenger() Messenger {
+	return &mMessenger{}
+}

+ 162 - 0
mgossip/router_floyd.go

@@ -0,0 +1,162 @@
+package mgossip
+
+import (
+	"math"
+
+	"git.wecise.com/wecise/odbserver/mring/matrix"
+	"git.wecise.com/wecise/odbserver/mring/mgossip"
+)
+
+type Router interface {
+	Init() error
+	Start() error
+}
+
+type mRouter struct {
+	cnode matrix.ClusterNode
+	mtopo mgossip.Topology
+}
+
+func NewMRouter(cnode matrix.ClusterNode) Router {
+	mtopo := mgossip.MTopology(cnode)
+	mr := &mRouter{
+		cnode: cnode,
+		mtopo: mtopo}
+	mtopo.OnTopoChange(func(connectedPeers map[matrix.PeerID]matrix.PeerIDs, update *mgossip.TopoInfo) {
+		// peers := matrix.MPeerIDSet(cnode.Peers())
+		// if !mpeers.Equal(peers) {
+		// 	mpeers = peers
+		// 	logger.Info(cnode.Name(), cnode.ID(), ":\n", mpeers.List())
+		// }
+	})
+	return mr
+}
+
+func (mr *mRouter) Init() (err error) {
+	// 拓扑管理初始化,开启本地网络监听端口,
+	if err = mr.mtopo.Init(); err != nil {
+		return
+	}
+
+	return
+}
+
+func (mr *mRouter) Start() (err error) {
+	// 拓扑管理初始化,开启本地网络监听端口,
+	if err = mr.mtopo.Start(); err != nil {
+		return
+	}
+
+	return
+}
+
+func (mr *mRouter) topoGraph(ti *mgossip.TopoInfo) {
+	if ti == nil {
+		return
+	}
+	fw := newFloydWarshall(len(ti.Nodes))
+	for pid, ni := range ti.Nodes {
+		bi := ni.BaseInfo
+		if bi == nil {
+			continue // 正常情况下不会为空
+		}
+		for rid, pi := range ni.Peers {
+			// TODO 统计分析得出连接权重
+			if len(pi.Connections) > 0 {
+				fw.addEdge(pid, rid, 1)
+				fw.addEdge(rid, pid, 1)
+			}
+		}
+	}
+	return
+}
+
+type graph struct {
+	to int
+	wt float64
+}
+
+type FloydWarshall struct {
+	mindex map[matrix.PeerID]int
+	rindex []matrix.PeerID
+	mgraph [][]graph
+	mpath  [][][]matrix.PeerID
+	mdist  [][]float64
+}
+
+func newFloydWarshall(nodescount int) *FloydWarshall {
+	return &FloydWarshall{
+		mindex: map[matrix.PeerID]int{},
+		rindex: make([]matrix.PeerID, nodescount+1),
+		mgraph: make([][]graph, nodescount+1),
+	}
+}
+
+func (fw *FloydWarshall) index(pid matrix.PeerID) int {
+	fi, has := fw.mindex[pid]
+	if !has {
+		fi = len(fw.mindex) + 1
+		fw.mindex[pid] = fi
+	}
+	if len(fw.rindex) < fi+1 {
+		fw.rindex = append(fw.rindex, pid)
+	} else {
+		fw.rindex[fi] = pid
+	}
+	return fi
+}
+
+func (fw *FloydWarshall) addEdge(from, to matrix.PeerID, weight float64) {
+	fi := fw.index(from)
+	ti := fw.index(to)
+	if len(fw.mgraph) < fi+1 {
+		fw.mgraph = append(fw.mgraph, []graph{})
+	}
+	fw.mgraph[fi] = append(fw.mgraph[fi], graph{ti, weight})
+}
+
+func (fw *FloydWarshall) calculate() {
+	fw.mpath = make([][][]matrix.PeerID, len(fw.mgraph))
+	fw.mdist = make([][]float64, len(fw.mgraph))
+	for i := range fw.mdist {
+		pi := make([][]matrix.PeerID, len(fw.mgraph))
+		di := make([]float64, len(fw.mgraph))
+		for j := range di {
+			di[j] = math.Inf(1)
+			pi[j] = []matrix.PeerID{}
+		}
+		di[i] = 0
+		fw.mdist[i] = di
+		fw.mpath[i] = pi
+	}
+	for u, graphs := range fw.mgraph {
+		for _, v := range graphs {
+			fw.mdist[u][v.to] = v.wt
+			fw.mpath[u][v.to] = []matrix.PeerID{fw.rindex[u], fw.rindex[v.to]}
+		}
+	}
+	for k, dk := range fw.mdist {
+		pk := fw.mpath[k]
+		for i, di := range fw.mdist {
+			pi := fw.mpath[i]
+			for j, dij := range di {
+				if d := di[k] + dk[j]; dij > d {
+					di[j] = d
+					pi[j] = append(pi[k], pk[j][1:]...)
+				}
+			}
+		}
+	}
+}
+
+func (fw *FloydWarshall) getPath(from, to matrix.PeerID) (dist float64, path matrix.PeerIDs) {
+	fi := fw.index(from)
+	ti := fw.index(to)
+	if fi >= len(fw.mpath) || fi >= len(fw.mdist) {
+		return 0, nil
+	}
+	if ti >= len(fw.mpath[fi]) || ti >= len(fw.mdist[fi]) {
+		return 0, nil
+	}
+	return fw.mdist[fi][ti], fw.mpath[fi][ti]
+}

+ 0 - 456
msh/client.go

@@ -1,456 +0,0 @@
-package main
-
-import (
-	"bufio"
-	"encoding/base64"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"net"
-	"os"
-	"os/user"
-	"path"
-	"regexp"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
-
-	"git.wecise.com/wecise/common/matrix/logger"
-	"golang.org/x/crypto/ssh"
-	"golang.org/x/crypto/ssh/terminal"
-)
-
-var (
-	DefaultCiphers = []string{
-		"aes128-ctr",
-		"aes192-ctr",
-		"aes256-ctr",
-		"aes128-gcm@openssh.com",
-		"chacha20-poly1305@openssh.com",
-		"arcfour256",
-		"arcfour128",
-		"arcfour",
-		"aes128-cbc",
-		"3des-cbc",
-		"blowfish-cbc",
-		"cast128-cbc",
-		"aes192-cbc",
-		"aes256-cbc",
-	}
-)
-
-var regxyesno = &Regexp{regexp.MustCompile(`.*(\(yes\/no\)\?)\s*$`)}
-var regxpassword = &Regexp{regexp.MustCompile(`.*([Pp]assword:)\s*$`)}
-var regxprompt = &Regexp{regexp.MustCompile(`.*([%#\$\>])\s*$`)}
-
-type Client interface {
-	Login()
-}
-
-type defaultClient struct {
-	clientConfig *ssh.ClientConfig
-	node         *Node
-}
-
-func genSSHConfig(node *Node) *defaultClient {
-	u, err := user.Current()
-	if err != nil {
-		logger.Error(err)
-		return nil
-	}
-
-	var authMethods []ssh.AuthMethod
-
-	var pemBytes []byte
-	if node.KeyPath == "" {
-		pemBytes, err = ioutil.ReadFile(path.Join(u.HomeDir, ".ssh/id_rsa"))
-	} else {
-		pemBytes, err = ioutil.ReadFile(node.KeyPath)
-	}
-	if err != nil && !os.IsNotExist(err) {
-		logger.Error(err)
-	} else if len(pemBytes) > 0 {
-		var signer ssh.Signer
-		if node.Passphrase != "" {
-			signer, err = ssh.ParsePrivateKeyWithPassphrase(pemBytes, []byte(node.Passphrase))
-		} else {
-			signer, err = ssh.ParsePrivateKey(pemBytes)
-		}
-		if err != nil {
-			logger.Error(err)
-		} else {
-			authMethods = append(authMethods, ssh.PublicKeys(signer))
-		}
-	}
-
-	password := node.password()
-
-	if password != nil {
-		authMethods = append(authMethods, password)
-	}
-
-	authMethods = append(authMethods, ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) ([]string, error) {
-		answers := make([]string, 0, len(questions))
-		for i, q := range questions {
-			fmt.Print(q)
-			if echos[i] {
-				scan := bufio.NewScanner(os.Stdin)
-				if scan.Scan() {
-					answers = append(answers, scan.Text())
-				}
-				err := scan.Err()
-				if err != nil {
-					return nil, err
-				}
-			} else {
-				b, err := terminal.ReadPassword(int(syscall.Stdin))
-				if err != nil {
-					return nil, err
-				}
-				fmt.Println()
-				answers = append(answers, string(b))
-			}
-		}
-		return answers, nil
-	}))
-
-	config := &ssh.ClientConfig{
-		User:            node.user(),
-		Auth:            authMethods,
-		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
-		Timeout:         time.Second * 10,
-	}
-
-	config.SetDefaults()
-	config.Ciphers = append(config.Ciphers, DefaultCiphers...)
-
-	return &defaultClient{
-		clientConfig: config,
-		node:         node,
-	}
-}
-
-func NewClient(node *Node) Client {
-	return genSSHConfig(node)
-}
-
-func (c *defaultClient) Login() {
-	host := c.node.Host
-	port := strconv.Itoa(c.node.port())
-	jNodes := c.node.Jump
-
-	var client *ssh.Client
-
-	if len(jNodes) > 0 {
-		jNode := jNodes[0]
-		jc := genSSHConfig(jNode)
-		proxyClient, err := ssh.Dial("tcp", net.JoinHostPort(jNode.Host, strconv.Itoa(jNode.port())), jc.clientConfig)
-		if err != nil {
-			logger.Error(err)
-			return
-		}
-		conn, err := proxyClient.Dial("tcp", net.JoinHostPort(host, port))
-		if err != nil {
-			logger.Error(err)
-			return
-		}
-		ncc, chans, reqs, err := ssh.NewClientConn(conn, net.JoinHostPort(host, port), c.clientConfig)
-		if err != nil {
-			logger.Error(err)
-			return
-		}
-		client = ssh.NewClient(ncc, chans, reqs)
-	} else {
-		client1, err := ssh.Dial("tcp", net.JoinHostPort(host, port), c.clientConfig)
-		client = client1
-		if err != nil {
-			msg := err.Error()
-			// use terminal password retry
-			if strings.Contains(msg, "no supported methods remain") && !strings.Contains(msg, "password") {
-				fmt.Printf("%s@%s's password:", c.clientConfig.User, host)
-				var b []byte
-				b, err = terminal.ReadPassword(int(syscall.Stdin))
-				if err == nil {
-					p := string(b)
-					if p != "" {
-						c.clientConfig.Auth = append(c.clientConfig.Auth, ssh.Password(p))
-					}
-					fmt.Println()
-					client, err = ssh.Dial("tcp", net.JoinHostPort(host, port), c.clientConfig)
-				}
-			}
-		}
-		if err != nil {
-			logger.Error(err)
-			return
-		}
-	}
-	defer client.Close()
-
-	if strings.Index(c.node.Commands[0].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 {
-		logger.Trace("connect server ssh", fmt.Sprintf("-p %d %s@%s version: %s", c.node.port(), c.node.user(), host, string(client.ServerVersion())))
-	}
-
-	session, err := client.NewSession()
-	if err != nil {
-		logger.Error(err)
-		return
-	}
-	defer session.Close()
-
-	fd := int(os.Stdin.Fd())
-	w, h, err := terminal.GetSize(fd)
-	if err != nil {
-		logger.Error(err)
-		return
-	}
-
-	modes := ssh.TerminalModes{
-		ssh.ECHO:          0,
-		ssh.TTY_OP_ISPEED: 14400,
-		ssh.TTY_OP_OSPEED: 14400,
-	}
-	err = session.RequestPty("xterm", h, w, modes)
-	if err != nil {
-		logger.Error(err)
-		return
-	}
-
-	stdinPipe, err := session.StdinPipe()
-	if err != nil {
-		logger.Error(err)
-		session.Stdin = os.Stdin
-	}
-	stdoutPipe, err := session.StdoutPipe()
-	if err != nil {
-		logger.Error(err)
-		session.Stdout = os.Stdout
-	}
-	stderrPipe, err := session.StderrPipe()
-	if err != nil {
-		logger.Error(err)
-		session.Stderr = os.Stderr
-	}
-
-	err = session.Shell()
-	if err != nil {
-		logger.Error(err)
-		return
-	}
-
-	cmdidx := 0
-	outputproc := func(stdoutbtr *BTReader, localstdout io.Writer, stdinPipe io.Writer) {
-		as := ""
-		change := false
-		for {
-			bs, err := stdoutbtr.ReadTimeout(10 * time.Millisecond)
-			if err != nil {
-				if err == io.EOF {
-					return
-				}
-				logger.Error(err)
-				return
-			}
-			if len(bs) > 0 {
-				change = true
-				s := string(bs)
-				localstdout.Write([]byte(s))
-				as += s
-			} else if change {
-				change = false
-				if cmdidx < len(c.node.Commands) {
-					if msi := c.node.Commands[cmdidx].Endregx.FindStringSubmatchIndex(as); msi != nil {
-						match := as[msi[0]:msi[1]]
-						if strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
-							logger.Trace("match end:", "'"+match+"'")
-						}
-						as = "" // 全清,开始新的命令
-						cmdidx++
-						if cmdidx >= len(c.node.Commands) {
-							continue
-						}
-						if strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0 {
-							logger.Trace("command:", c.node.Commands[cmdidx].Cmd)
-						}
-						localstdout.Write([]byte(c.node.Commands[cmdidx].Cmd + "\n"))
-						stdinPipe.Write([]byte(c.node.Commands[cmdidx].Cmd + "\n"))
-						continue
-					}
-					for _, regex := range c.node.Commands[cmdidx].Regexps {
-						if regex == nil {
-							continue
-						}
-						if regex.Regexp == nil {
-							// like match all
-							if regex.Output != "" {
-								localstdout.Write([]byte(regex.Output))
-								stdinPipe.Write([]byte(regex.Output))
-							}
-						} else if msi := regex.Regexp.FindStringSubmatchIndex(as); msi != nil {
-							match := as[msi[0]:msi[1]]
-							if len(msi) >= 4 {
-								match = as[msi[2]:msi[3]]
-								as = as[msi[3]:] // 清除已处理完的内容
-							} else {
-								as = as[msi[1]:] // 清除已处理完的内容
-							}
-							if strings.Index(regex.Debug, "m") >= 0 || strings.Index(regex.Debug, "1") >= 0 {
-								logger.Trace("match regex:", "'"+match+"'")
-							}
-							if regex.Output != "" {
-								localstdout.Write([]byte(regex.Output))
-								stdinPipe.Write([]byte(regex.Output))
-							}
-						}
-					}
-				}
-				if msi := regxyesno.FindStringSubmatchIndex(as); msi != nil {
-					match := as[msi[0]:msi[1]]
-					if len(msi) >= 4 {
-						as = as[msi[3]:] // 清除已处理完的内容
-					} else {
-						as = as[msi[1]:] // 清除已处理完的内容
-					}
-					if strings.Index(c.node.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 ||
-						cmdidx < len(c.node.Commands) &&
-							(strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
-						logger.Trace("match yesno:", "'"+match+"'")
-					}
-					os.Stdout.Write([]byte("yes\n"))
-					stdinPipe.Write([]byte("yes\n"))
-				}
-				if msi := regxpassword.FindStringSubmatchIndex(as); msi != nil {
-					match := as[msi[0]:msi[1]]
-					if len(msi) >= 4 {
-						as = as[msi[3]:] // 清除已处理完的内容
-					} else {
-						as = as[msi[1]:] // 清除已处理完的内容
-					}
-					p := c.node.Commands[0].Password
-					if cmdidx < len(c.node.Commands) {
-						p = c.node.Commands[cmdidx].Password
-					}
-					if strings.Index(c.node.Commands[0].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 ||
-						cmdidx < len(c.node.Commands) &&
-							(strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "m") >= 0 || strings.Index(c.node.Commands[cmdidx].Regexps[0].Debug, "1") >= 0) {
-						logger.Trace("match password:", "'"+match+"'")
-					}
-					if p != "" {
-						if p[0:1] == "=" {
-							p = p[1:]
-						} else {
-							x, e := base64.RawStdEncoding.DecodeString(p)
-							if e == nil {
-								p = string(x)
-							}
-							// else 不是Base64编码,保持原值
-						}
-						// don't echo password
-						if c.node.Commands[0].Regexps[0].Debug != "" || cmdidx < len(c.node.Commands) && c.node.Commands[cmdidx].Regexps[0].Debug != "" {
-							os.Stdout.Write([]byte(p + "\n"))
-						}
-						stdinPipe.Write([]byte(p + "\n"))
-					}
-				}
-				if len(as) > 1024 {
-					as = as[len(as)-1024:]
-				}
-			}
-		}
-	}
-	go outputproc(&BTReader{Reader: bufio.NewReader(stdoutPipe)}, os.Stdout, stdinPipe)
-	go outputproc(&BTReader{Reader: bufio.NewReader(stderrPipe)}, os.Stderr, stdinPipe)
-
-	go func() {
-		for {
-			bs := make([]byte, 1024)
-			n, err := os.Stdin.Read(bs)
-			if err != nil {
-				if err == io.EOF {
-					return
-				}
-				logger.Error(err)
-				return
-			}
-			s := string(bs[:n])
-			stdinPipe.Write([]byte(s))
-		}
-	}()
-
-	// interval get terminal size
-	// fix resize issue
-	go func() {
-		var (
-			ow = w
-			oh = h
-		)
-		for {
-			cw, ch, err := terminal.GetSize(fd)
-			if err != nil {
-				break
-			}
-
-			if cw != ow || ch != oh {
-				err = session.WindowChange(ch, cw)
-				if err != nil {
-					break
-				}
-				ow = cw
-				oh = ch
-			}
-			time.Sleep(time.Second)
-		}
-	}()
-
-	// send keepalive
-	go func() {
-		for {
-			time.Sleep(time.Second * 10)
-			client.SendRequest("nop", false, nil)
-		}
-	}()
-
-	session.Wait()
-	if strings.Index(c.node.Commands[0].Regexps[0].Debug, "p") >= 0 || strings.Index(c.node.Commands[0].Regexps[0].Debug, "1") >= 0 {
-		logger.Trace("disconnected")
-	}
-}
-
-type BTReader struct {
-	*bufio.Reader
-	bufop int32
-	chbs  chan []byte
-}
-
-func (me *BTReader) ReadTimeout(d time.Duration) (rbs []byte, err error) {
-	if me.chbs == nil {
-		me.chbs = make(chan []byte)
-		go func() {
-			n := 0
-			bs := make([]byte, me.Size())
-			for {
-				_, err = me.ReadByte()
-				if err != nil {
-					return
-				}
-				err = me.UnreadByte()
-				if err != nil {
-					return
-				}
-				n, err = me.Read(bs[0:me.Buffered()])
-				if err != nil {
-					return
-				}
-				me.chbs <- bs[0:n]
-			}
-		}()
-	}
-	t := time.NewTimer(d)
-	select {
-	case rbs = <-me.chbs:
-		return
-	case <-t.C:
-		return
-	}
-}

+ 0 - 174
msh/config.go

@@ -1,174 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"os"
-	"os/user"
-	"path"
-	"regexp"
-	"strconv"
-
-	"git.wecise.com/wecise/common/matrix/logger"
-	"github.com/atrox/homedir"
-	"github.com/kevinburke/ssh_config"
-	"golang.org/x/crypto/ssh"
-	"gopkg.in/yaml.v2"
-)
-
-type Node struct {
-	Name       string     `yaml:"name"`
-	Alias      string     `yaml:"alias"`
-	Host       string     `yaml:"host"`
-	User       string     `yaml:"user"`
-	Port       int        `yaml:"port"`
-	KeyPath    string     `yaml:"keypath"`
-	Passphrase string     `yaml:"passphrase"`
-	Password   string     `yaml:"password"`
-	Commands   []*Command `yaml:"commands"`
-	Children   []*Node    `yaml:"children"`
-	Jump       []*Node    `yaml:"jump"`
-}
-
-type Regexp struct {
-	*regexp.Regexp
-}
-
-func (r *Regexp) MarshalJSON() ([]byte, error) {
-	if r == nil || r.Regexp == nil {
-		return json.Marshal(nil)
-	}
-	return json.Marshal(r.String())
-}
-
-func (r *Regexp) MarshalYAML() (interface{}, error) {
-	if r == nil || r.Regexp == nil {
-		return nil, nil
-	}
-	return r.String(), nil
-}
-
-type Matcher struct {
-	Regexp *Regexp
-	Output string `yaml:"output"`
-	Debug  string `yaml:"debug"`
-}
-
-type Command struct {
-	Cmd      string `yaml:"cmd"`
-	Password string `yaml:"password"`
-	Regexps  []*Matcher
-	Endregx  *Regexp
-}
-
-func (n *Node) String() string {
-	return n.Name
-}
-
-func (n *Node) user() string {
-	if n.User == "" {
-		return "root"
-	}
-	return n.User
-}
-
-func (n *Node) port() int {
-	if n.Port <= 0 {
-		return 22
-	}
-	return n.Port
-}
-
-func (n *Node) password() ssh.AuthMethod {
-	if n.Password == "" {
-		return nil
-	}
-	return ssh.Password(n.Password)
-}
-
-func (n *Node) alias() string {
-	return n.Alias
-}
-
-var (
-	config []*Node
-)
-
-func GetConfig() []*Node {
-	return config
-}
-
-func LoadConfig() error {
-	b, err := LoadConfigBytes(".sshw", ".sshw.yml", ".sshw.yaml")
-	if err != nil {
-		return err
-	}
-	var c []*Node
-	err = yaml.Unmarshal(b, &c)
-	if err != nil {
-		return err
-	}
-
-	config = c
-
-	return nil
-}
-
-func LoadSshConfig() error {
-	u, err := user.Current()
-	if err != nil {
-		logger.Error(err)
-		return nil
-	}
-	f, _ := os.Open(path.Join(u.HomeDir, ".ssh/config"))
-	cfg, _ := ssh_config.Decode(f)
-	var nc []*Node
-	for _, host := range cfg.Hosts {
-		alias := fmt.Sprintf("%s", host.Patterns[0])
-		hostName, err := cfg.Get(alias, "HostName")
-		if err != nil {
-			return err
-		}
-		if hostName != "" {
-			port, _ := cfg.Get(alias, "Port")
-			if port == "" {
-				port = "22"
-			}
-			var c = new(Node)
-			c.Name = alias
-			c.Alias = alias
-			c.Host = hostName
-			c.User, _ = cfg.Get(alias, "User")
-			c.Port, _ = strconv.Atoi(port)
-			keyPath, _ := cfg.Get(alias, "IdentityFile")
-			c.KeyPath, _ = homedir.Expand(keyPath)
-			nc = append(nc, c)
-			// fmt.Println(c.Alias, c.Host, c.User, c.Port, c.KeyPath)
-		}
-	}
-	config = nc
-	return nil
-}
-
-func LoadConfigBytes(names ...string) ([]byte, error) {
-	u, err := user.Current()
-	if err != nil {
-		return nil, err
-	}
-	// homedir
-	for i := range names {
-		sshw, err := ioutil.ReadFile(path.Join(u.HomeDir, names[i]))
-		if err == nil {
-			return sshw, nil
-		}
-	}
-	// relative
-	for i := range names {
-		sshw, err := ioutil.ReadFile(names[i])
-		if err == nil {
-			return sshw, nil
-		}
-	}
-	return nil, err
-}

+ 0 - 208
msh/main.go

@@ -1,208 +0,0 @@
-package main
-
-import (
-	"encoding/base64"
-	"fmt"
-	"os"
-	"os/user"
-	"regexp"
-	"strings"
-
-	"git.wecise.com/wecise/common/matrix/logger"
-	"gopkg.in/yaml.v2"
-)
-
-func init() {
-	logger.SetConsole(true)
-	logger.SetLevel(logger.TRACE)
-	logger.SetFormat("yyyy-MM-dd HH:mm:SS.sss [level] msg")
-}
-
-func uchi() (u, c, h string, i int) {
-	if len(os.Args) < 2 {
-		return
-	}
-	if msi := regexp.MustCompile(`^([^:]+):(.*)@([^@]*)$`).FindStringSubmatchIndex(os.Args[1]); msi != nil {
-		u = os.Args[1][msi[2]:msi[3]]
-		c = "=" + os.Args[1][msi[4]:msi[5]]
-		h = os.Args[1][msi[6]:msi[7]]
-		i = 2
-		return
-	}
-	if msi := regexp.MustCompile(`^([^:]+)@([^@]*)$`).FindStringSubmatchIndex(os.Args[1]); msi != nil {
-		u = os.Args[1][msi[2]:msi[3]]
-		h = os.Args[1][msi[6]:msi[7]]
-		i = 2
-	} else if os.Args[1][0] != '-' && strings.Index(os.Args[1], "=") < 0 {
-		h = os.Args[1]
-		i = 2
-	} else {
-		i = 1
-	}
-	if u == "" {
-		user, e := user.Current()
-		if e != nil {
-			logger.Error(e)
-			return "", "", "", 0
-		}
-		u = user.Username
-	}
-	if h == "" {
-		h = "127.0.0.1"
-	}
-	return
-}
-
-type KV struct{ Key, Val string }
-
-func parseArgs(args []string) (kvs []*KV) {
-	argk := ""
-	argv := ""
-	for _, arg := range args {
-		if argk != "" {
-			argv = arg
-		} else if regexp.MustCompile(`^\-\w+$`).MatchString(arg) {
-			argk = arg[1:]
-			continue
-		} else {
-			kv := strings.SplitN(arg, "=", 2)
-			if len(kv) == 2 {
-				argk = kv[0]
-				argv = kv[1]
-			} else {
-				argk = ""
-				argv = arg
-			}
-		}
-		kvs = append(kvs, &KV{argk, argv})
-		argk, argv = "", ""
-	}
-	if argk != "" {
-		kvs = append(kvs, &KV{argk, argv})
-	}
-	return
-}
-
-func main() {
-	u, c, h, i := uchi()
-	if i == 0 {
-		fmt.Println("usage:")
-		fmt.Println("  msh user:password@host|p=password|a=passcode [[c=]command [p=password|a=passcode] [x=cmd-end-regexp] [[r=regexp] [o=output|n=outputline]]...]...")
-		fmt.Println("  a=passcode should be base64 encoded, or use p=password")
-		fmt.Println("  debug info include: p(progress) a(argments) m(match) 1(all)")
-		return
-	}
-	kvs := parseArgs(os.Args[i:])
-	if c == "" {
-		for _, kv := range kvs {
-			if kv.Key == "a" {
-				c = kv.Val
-				if c == "" {
-					c = "="
-				}
-				break
-			}
-			if kv.Key == "p" {
-				c = "=" + kv.Val
-				break
-			}
-		}
-	}
-	p := c
-	if p[0:1] == "=" {
-		p = p[1:]
-	} else {
-		x, e := base64.RawStdEncoding.DecodeString(p)
-		if e == nil {
-			p = string(x)
-		}
-		// else 不是Base64编码,保持原值
-	}
-	// explainArgs key, val
-	cmds := []*Command{{Cmd: "ssh " + u + ":" + p + "@" + h, Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt}}
-	for _, kv := range kvs {
-		key, val := kv.Key, kv.Val
-		switch key {
-		case "", "cmd", "command", "c":
-			cmds = append(cmds, &Command{Cmd: val, Password: c, Regexps: []*Matcher{{Regexp: nil, Output: ""}}, Endregx: regxprompt})
-		case "ry":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, err)
-				return
-			} else {
-				regxyesno = &Regexp{re}
-			}
-		case "rc":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, err)
-				return
-			} else {
-				regxpassword = &Regexp{re}
-			}
-		case "rp":
-			re, err := regexp.Compile(val)
-			if err != nil {
-				logger.Error("arg", i, err)
-				return
-			} else {
-				regxprompt = &Regexp{re}
-			}
-		case "password", "code", "pass", "p":
-			cmds[len(cmds)-1].Password = "=" + val
-		case "passcode", "b64code", "a":
-			cmds[len(cmds)-1].Password = val
-		case "re", "r", "regex":
-			if val == "" {
-				cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: nil})
-			} else {
-				re, err := regexp.Compile(val)
-				if err != nil {
-					logger.Error("arg", i, err)
-					return
-				} else {
-					cmds[len(cmds)-1].Regexps = append(cmds[len(cmds)-1].Regexps, &Matcher{Regexp: &Regexp{re}})
-				}
-			}
-		case "x", "end":
-			if val == "" {
-				cmds[len(cmds)-1].Endregx = regxprompt
-			} else {
-				re, err := regexp.Compile(val)
-				if err != nil {
-					logger.Error("arg", i, err)
-					return
-				} else {
-					cmds[len(cmds)-1].Endregx = &Regexp{re}
-				}
-			}
-		case "out", "o", "output":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val
-		case "outln", "n", "outputline":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Output += val + "\n"
-		case "debug", "d":
-			cmds[len(cmds)-1].Regexps[len(cmds[len(cmds)-1].Regexps)-1].Debug += val
-		}
-	}
-	if strings.Index(cmds[0].Regexps[0].Debug, "a") >= 0 || strings.Index(cmds[0].Regexps[0].Debug, "1") >= 0 {
-		//bs, _ := json.MarshalIndent(cmds, "", "  ")
-		bs, _ := yaml.Marshal(cmds)
-		logger.Debug("arguments:\n" + string(bs))
-	}
-	node := &Node{
-		Name:       "x",
-		Alias:      "x",
-		Host:       h,
-		User:       u,
-		Port:       22,
-		KeyPath:    "",
-		Passphrase: "",
-		Password:   p,
-		Commands:   cmds,
-		Children:   []*Node{},
-		Jump:       []*Node{},
-	}
-	sshc := NewClient(node)
-	sshc.Login()
-}