Initial commit

This commit is contained in:
Donny
2019-04-22 20:46:32 +08:00
commit 49ab8aadd1
25441 changed files with 4055000 additions and 0 deletions

View File

@@ -0,0 +1,59 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_test",
"go_library",
)
go_test(
name = "go_default_test",
srcs = ["register_test.go"],
embed = [":go_default_library"],
rundir = ".",
tags = ["automanaged"],
deps = [
"//app/infra/discovery/conf:go_default_library",
"//app/infra/discovery/model:go_default_library",
"//library/ecode:go_default_library",
"//library/net/http/blademaster:go_default_library",
"//library/net/netutil/breaker:go_default_library",
"//library/time:go_default_library",
"//vendor/github.com/smartystreets/goconvey/convey:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = [
"register.go",
"service.go",
"syncup.go",
],
importpath = "go-common/app/infra/discovery/service",
tags = ["automanaged"],
visibility = ["//visibility:public"],
deps = [
"//app/infra/discovery/conf:go_default_library",
"//app/infra/discovery/dao:go_default_library",
"//app/infra/discovery/model:go_default_library",
"//library/conf/env:go_default_library",
"//library/ecode:go_default_library",
"//library/log:go_default_library",
"//library/net/http/blademaster:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@@ -0,0 +1,140 @@
package service
import (
"context"
"go-common/app/infra/discovery/model"
"go-common/library/ecode"
"go-common/library/log"
)
// Register a new instance.
func (s *Service) Register(c context.Context, ins *model.Instance, latestTimestamp int64, replication bool) {
s.registry.Register(ins, latestTimestamp)
if ins.Treeid != 0 {
s.tLock.RLock()
appid, ok := s.tree[ins.Treeid]
s.tLock.RUnlock()
if !ok || appid != ins.Appid {
s.tLock.Lock()
s.tree[ins.Treeid] = ins.Appid
s.tLock.Unlock()
}
}
if !replication {
s.nodes.Replicate(c, model.Register, ins, ins.Zone != s.env.Zone)
}
}
// Renew marks the given instance of the given app name as renewed, and also marks whether it originated from replication.
func (s *Service) Renew(c context.Context, arg *model.ArgRenew) (i *model.Instance, err error) {
i, ok := s.registry.Renew(arg)
if !ok {
err = ecode.NothingFound
log.Error("renew appid(%s) hostname(%s) zone(%s) env(%s) error", arg.Appid, arg.Hostname, arg.Zone, arg.Env)
return
}
if !arg.Replication {
s.nodes.Replicate(c, model.Renew, i, arg.Zone != s.env.Zone)
return
}
if arg.DirtyTimestamp > i.DirtyTimestamp {
err = ecode.NothingFound
return
} else if arg.DirtyTimestamp < i.DirtyTimestamp {
err = ecode.Conflict
}
return
}
// Cancel cancels the registration of an instance.
func (s *Service) Cancel(c context.Context, arg *model.ArgCancel) (err error) {
i, ok := s.registry.Cancel(arg)
if !ok {
err = ecode.NothingFound
log.Error("cancel appid(%s) hostname(%s) error", arg.Appid, arg.Hostname)
return
}
if !arg.Replication {
s.nodes.Replicate(c, model.Cancel, i, arg.Zone != s.env.Zone)
}
return
}
// FetchAll fetch all instances of all the department.
func (s *Service) FetchAll(c context.Context) (im map[string][]*model.Instance) {
return s.registry.FetchAll()
}
// Fetchs fetch multi app by appids.
func (s *Service) Fetchs(c context.Context, arg *model.ArgFetchs) (is map[string]*model.InstanceInfo, err error) {
is = make(map[string]*model.InstanceInfo, len(arg.Appid))
for _, appid := range arg.Appid {
i, err := s.registry.Fetch(arg.Zone, arg.Env, appid, 0, arg.Status)
if err != nil {
log.Error("Fetchs fetch appid(%s) err", err)
continue
}
is[appid] = i
}
return
}
// Fetch fetch all instances by appid.
func (s *Service) Fetch(c context.Context, arg *model.ArgFetch) (info *model.InstanceInfo, err error) {
var appid string
if arg.Treeid != 0 {
s.tLock.RLock()
appid = s.tree[arg.Treeid]
s.tLock.RUnlock()
}
if appid == "" {
appid = arg.Appid
}
return s.registry.Fetch(arg.Zone, arg.Env, appid, 0, arg.Status)
}
// Polls hangs request and then write instances when that has changes, or return NotModified.
func (s *Service) Polls(c context.Context, arg *model.ArgPolls) (ch chan map[string]*model.InstanceInfo, new bool, err error) {
var appids []string
s.tLock.RLock()
if len(arg.Treeid) > 0 {
appids = make([]string, 0, len(arg.Treeid))
}
for _, tid := range arg.Treeid {
appid := s.tree[tid]
appids = append(appids, appid)
}
s.tLock.RUnlock()
if len(appids) != 0 {
arg.Appid = appids
}
return s.registry.Polls(arg)
}
// Polling get polling clients.
func (s *Service) Polling(c context.Context, arg *model.ArgPolling) (res []string, err error) {
return s.registry.Polling(arg)
}
// DelConns delete conn of host in appid
func (s *Service) DelConns(arg *model.ArgPolls) {
s.registry.DelConns(arg)
}
// Set set the status of instance by hostnames.
func (s *Service) Set(c context.Context, arg *model.ArgSet) (err error) {
if ok := s.registry.Set(c, arg); !ok {
err = ecode.NothingFound
return
}
if !arg.Replication {
s.nodes.ReplicateSet(c, arg, arg.Zone != s.env.Zone)
}
return
}
// Nodes get all nodes of discovery.
func (s *Service) Nodes(c context.Context) (nsi []*model.Node) {
return s.nodes.Nodes()
}

View File

@@ -0,0 +1,229 @@
package service
import (
"context"
"fmt"
"testing"
"time"
dc "go-common/app/infra/discovery/conf"
"go-common/app/infra/discovery/model"
"go-common/library/ecode"
bm "go-common/library/net/http/blademaster"
"go-common/library/net/netutil/breaker"
xtime "go-common/library/time"
. "github.com/smartystreets/goconvey/convey"
)
var (
ctx = context.TODO()
reg = defRegisArg()
rew = &model.ArgRenew{Appid: "main.arch.test", Hostname: "test1", Region: "shsb", Zone: "sh001", Env: "pre"}
cancel = &model.ArgCancel{Appid: "main.arch.test", Hostname: "test1", Region: "shsb", Zone: "sh001", Env: "pre"}
fet = &model.ArgFetch{Appid: "main.arch.test", Region: "shsb", Zone: "sh001", Env: "pre", Status: 1}
set = &model.ArgSet{Appid: "main.arch.test", Region: "shsb", Hostname: []string{"test1"}, Zone: "sh001", Env: "pre"}
pollArg = newPoll()
)
func newFetchArg() *model.ArgFetchs {
return &model.ArgFetchs{Appid: []string{"main.arch.test"}, Zone: "sh001", Env: "pre", Status: 1}
}
func newPoll() *model.ArgPolls {
return &model.ArgPolls{
Region: "shsb",
Env: "pre",
Appid: []string{"main.arch.test"},
LatestTimestamp: []int64{0},
}
}
func defRegisArg() *model.ArgRegister {
return &model.ArgRegister{
LatestTimestamp: time.Now().Unix(),
Appid: "main.arch.test",
Hostname: "test1", RPC: "127.0.0.1:8080",
Region: "shsb", Zone: "sh001",
Env: "pre", Status: 1,
Metadata: `{"test":"test","weight":"10"}`,
}
}
var config = newConfig()
func newConfig() *dc.Config {
return &dc.Config{HTTPClient: &bm.ClientConfig{Timeout: xtime.Duration(time.Second), Breaker: &breaker.Config{Window: xtime.Duration(time.Second),
Sleep: xtime.Duration(time.Millisecond * 100),
Bucket: 10,
Ratio: 0.5,
Request: 100},
App: &bm.App{Key: "0c4b8fe3ff35a4b6", Secret: "b370880d1aca7d3a289b9b9a7f4d6812"}},
BM: &dc.HTTPServers{Inner: &bm.ServerConfig{Addr: "127.0.0.1:7171"}},
}
}
func TestRegister(t *testing.T) {
Convey("test Register", t, func() {
svr, _ := New(config)
i := model.NewInstance(reg)
svr.Register(context.TODO(), i, reg.LatestTimestamp, false)
ins, err := svr.Fetch(context.TODO(), fet)
for _, i := range ins.Instances {
fmt.Println("ins", i)
}
So(err, ShouldBeNil)
So(len(ins.Instances), ShouldResemble, 1)
Convey("test metadta", func() {
for _, i := range ins.Instances {
So(err, ShouldBeNil)
So(i.Metadata["weight"], ShouldEqual, "10")
So(i.Metadata["test"], ShouldEqual, "test")
}
})
})
}
func TestDiscovery(t *testing.T) {
Convey("test cancel polls", t, func() {
svr, _ := New(config)
reg2 := defRegisArg()
reg2.Hostname = "test2"
i1 := model.NewInstance(reg)
i2 := model.NewInstance(reg2)
svr.Register(context.TODO(), i1, reg.LatestTimestamp, reg.Replication)
svr.Register(context.TODO(), i2, reg2.LatestTimestamp, reg.Replication)
ch, new, err := svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins := <-ch
So(len(ins["main.arch.test"].Instances), ShouldEqual, 2)
pollArg.LatestTimestamp[0] = ins["main.arch.test"].LatestTimestamp
time.Sleep(time.Second)
err = svr.Cancel(context.TODO(), cancel)
So(err, ShouldBeNil)
ch, new, err = svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins = <-ch
So(len(ins["main.arch.test"].Instances), ShouldEqual, 1)
})
Convey("test compatible with treeid polls", t, func() {
svr, _ := New(config)
reg2 := defRegisArg()
reg2.Treeid = 1
i1 := model.NewInstance(reg2)
svr.Register(ctx, i1, reg2.LatestTimestamp, reg2.Replication)
ch, new, err := svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins := <-ch
So(len(ins["main.arch.test"].Instances), ShouldEqual, 1)
treepoll := newPoll()
treepoll.Treeid = []int64{1}
ch, new, err = svr.Polls(context.TODO(), treepoll)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins = <-ch
So(len(ins["1"].Instances), ShouldEqual, 1)
})
}
func TestFetchs(t *testing.T) {
Convey("test fetch multi appid", t, func() {
svr, _ := New(config)
reg2 := defRegisArg()
reg2.Appid = "appid2"
i1 := model.NewInstance(reg)
i2 := model.NewInstance(reg2)
svr.Register(context.TODO(), i1, reg.LatestTimestamp, reg.Replication)
svr.Register(context.TODO(), i2, reg2.LatestTimestamp, reg.Replication)
fetchs := newFetchArg()
fetchs.Appid = append(fetchs.Appid, "appid2")
is, err := svr.Fetchs(ctx, fetchs)
So(err, ShouldBeNil)
So(len(is), ShouldResemble, 2)
})
}
func TestZones(t *testing.T) {
Convey("test multi zone discovery", t, func() {
svr, _ := New(config)
reg2 := defRegisArg()
reg2.Zone = "sh002"
i1 := model.NewInstance(reg)
i2 := model.NewInstance(reg2)
svr.Register(context.TODO(), i1, reg.LatestTimestamp, reg.Replication)
svr.Register(context.TODO(), i2, reg2.LatestTimestamp, reg.Replication)
ch, new, err := svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins := <-ch
So(len(ins["main.arch.test"].ZoneInstances), ShouldEqual, 2)
pollArg.Zone = "sh002"
ch, new, err = svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
So(new, ShouldBeTrue)
ins = <-ch
So(len(ins["main.arch.test"].ZoneInstances), ShouldEqual, 1)
Convey("test zone update", func() {
pollArg.LatestTimestamp = []int64{ins["main.arch.test"].LatestTimestamp}
pollArg.Zone = ""
reg3 := defRegisArg()
reg3.Zone = "sh002"
reg3.Hostname = "test03"
i3 := model.NewInstance(reg3)
svr.Register(context.TODO(), i3, reg3.LatestTimestamp, reg3.Replication)
ch, new, err = svr.Polls(context.TODO(), pollArg)
So(err, ShouldBeNil)
ins = <-ch
So(len(ins["main.arch.test"].ZoneInstances), ShouldResemble, 2)
So(len(ins["main.arch.test"].ZoneInstances["sh002"]), ShouldResemble, 2)
So(len(ins["main.arch.test"].ZoneInstances["sh001"]), ShouldResemble, 1)
pollArg.LatestTimestamp = []int64{ins["main.arch.test"].LatestTimestamp}
_, _, err = svr.Polls(context.TODO(), pollArg)
So(err, ShouldResemble, ecode.NotModified)
})
})
}
func TestRenew(t *testing.T) {
Convey("test Renew", t, func() {
svr, _ := New(config)
i := model.NewInstance(reg)
svr.Register(context.TODO(), i, reg.LatestTimestamp, reg.Replication)
_, err := svr.Renew(context.TODO(), rew)
So(err, ShouldBeNil)
})
}
func TestCancel(t *testing.T) {
Convey("test cancel", t, func() {
svr, _ := New(config)
i := model.NewInstance(reg)
svr.Register(context.TODO(), i, reg.LatestTimestamp, reg.Replication)
err := svr.Cancel(context.TODO(), cancel)
So(err, ShouldBeNil)
_, err = svr.Fetch(context.TODO(), fet)
So(err, ShouldResemble, ecode.NothingFound)
})
}
func TestFetchAll(t *testing.T) {
Convey("test fetch all", t, func() {
svr, _ := New(config)
i := model.NewInstance(reg)
svr.Register(context.TODO(), i, reg.LatestTimestamp, reg.Replication)
fs := svr.FetchAll(context.TODO())
_, ok := fs[reg.Appid]
So(ok, ShouldBeTrue)
})
}
func TestSet(t *testing.T) {
Convey("test set", t, func() {
svr, _ := New(config)
i := model.NewInstance(reg)
svr.Register(context.TODO(), i, reg.LatestTimestamp, reg.Replication)
set.Metadata = []string{`{"weight":"1"}`}
err := svr.Set(context.TODO(), set)
So(err, ShouldBeNil)
cm, err := svr.Fetch(context.TODO(), fet)
So(err, ShouldBeNil)
So(cm.Instances[0].Metadata["weight"], ShouldResemble, "1")
})
}

View File

@@ -0,0 +1,50 @@
package service
import (
"context"
"os"
"sync"
"go-common/app/infra/discovery/conf"
"go-common/app/infra/discovery/dao"
bm "go-common/library/net/http/blademaster"
)
// Service discovery main service
type Service struct {
c *conf.Config
client *bm.Client
registry *dao.Registry
nodes *dao.Nodes
tLock sync.RWMutex
tree map[int64]string // treeid->appid
env *env
}
type env struct {
Region string
Zone string
}
// New get a discovery service
func New(c *conf.Config) (s *Service, cancel context.CancelFunc) {
s = &Service{
c: c,
client: bm.NewClient(c.HTTPClient),
registry: dao.NewRegistry(),
nodes: dao.NewNodes(c),
tree: make(map[int64]string),
}
s.getEnv()
s.syncUp()
cancel = s.regSelf()
go s.nodesproc()
return
}
func (s *Service) getEnv() {
s.env = &env{
Region: os.Getenv("REGION"),
Zone: os.Getenv("ZONE"),
}
}

View File

@@ -0,0 +1,161 @@
package service
import (
"context"
"fmt"
"net/url"
"time"
"go-common/app/infra/discovery/conf"
"go-common/app/infra/discovery/dao"
"go-common/app/infra/discovery/model"
libenv "go-common/library/conf/env"
"go-common/library/ecode"
"go-common/library/log"
)
var (
_fetchAllURL = "http://%s/discovery/fetch/all"
)
// syncUp populates the registry information from a peer eureka node.
func (s *Service) syncUp() (err error) {
for _, node := range s.nodes.AllNodes() {
if s.nodes.Myself(node.Addr) {
continue
}
uri := fmt.Sprintf(_fetchAllURL, node.Addr)
var res struct {
Code int `json:"code"`
Data map[string][]*model.Instance `json:"data"`
}
if err = s.client.Get(context.TODO(), uri, "", nil, &res); err != nil {
log.Error("e.client.Get(%v) error(%v)", uri, err)
continue
}
if res.Code != 0 {
log.Error("service syncup from(%s) failed ", uri)
continue
}
for _, is := range res.Data {
for _, i := range is {
s.tLock.RLock()
appid, ok := s.tree[i.Treeid]
s.tLock.RUnlock()
if !ok || appid != i.Appid {
s.tLock.Lock()
s.tree[i.Treeid] = i.Appid
s.tLock.Unlock()
}
s.registry.Register(i, i.LatestTimestamp)
}
}
// NOTE: no return, make sure that all instances from other nodes register into self.
}
s.nodes.UP()
return
}
func (s *Service) regSelf() context.CancelFunc {
ctx, cancel := context.WithCancel(context.Background())
now := time.Now().UnixNano()
ins := &model.Instance{
Region: libenv.Region,
Zone: libenv.Zone,
Env: libenv.DeployEnv,
Hostname: libenv.Hostname,
Appid: model.AppID,
Addrs: []string{
"http://" + s.c.BM.Inner.Addr,
},
Status: model.InstanceStatusUP,
RegTimestamp: now,
UpTimestamp: now,
LatestTimestamp: now,
RenewTimestamp: now,
DirtyTimestamp: now,
}
s.Register(ctx, ins, now, false)
go func() {
ticker := time.NewTicker(30 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
arg := &model.ArgRenew{
Appid: model.AppID,
Region: libenv.Region,
Zone: libenv.Zone,
Env: libenv.DeployEnv,
Hostname: libenv.Hostname,
}
if _, err := s.Renew(ctx, arg); err != nil && ecode.NothingFound.Equal(err) {
s.Register(ctx, ins, now, false)
}
case <-ctx.Done():
arg := &model.ArgCancel{
Appid: model.AppID,
Region: libenv.Region,
Zone: libenv.Zone,
Env: libenv.DeployEnv,
Hostname: libenv.Hostname,
}
if err := s.Cancel(context.Background(), arg); err != nil {
log.Error("s.Cancel(%+v) error(%v)", arg, err)
}
return
}
}
}()
return cancel
}
func (s *Service) nodesproc() {
var (
lastTs int64
)
for {
arg := &model.ArgPolls{
Appid: []string{model.AppID},
Region: libenv.Region,
Env: libenv.DeployEnv,
Hostname: libenv.Hostname,
LatestTimestamp: []int64{lastTs},
}
ch, _, err := s.registry.Polls(arg)
if err != nil && err != ecode.NotModified {
log.Error("s.registry(%v) error(%v)", arg, err)
time.Sleep(time.Second)
continue
}
apps := <-ch
ins, ok := apps[model.AppID]
if !ok || ins == nil {
return
}
var (
nodes []string
zones = make(map[string][]string)
)
for _, in := range ins.Instances {
for _, addr := range in.Addrs {
u, err := url.Parse(addr)
if err == nil && u.Scheme == "http" {
if in.Zone == libenv.Zone {
nodes = append(nodes, u.Host)
} else if _, ok := s.c.Zones[in.Zone]; ok {
zones[in.Zone] = append(zones[in.Zone], u.Host)
}
}
}
}
lastTs = ins.LatestTimestamp
log.Info("discovery changed nodes:%v zones:%v", nodes, zones)
c := new(conf.Config)
*c = *s.c
c.Nodes = nodes
c.Zones = zones
s.nodes = dao.NewNodes(c)
s.nodes.UP()
}
}