241 lines
6.0 KiB
Go
241 lines
6.0 KiB
Go
|
package rpc
|
|||
|
|
|||
|
import (
|
|||
|
"context"
|
|||
|
"fmt"
|
|||
|
"net/url"
|
|||
|
"strconv"
|
|||
|
"sync/atomic"
|
|||
|
"time"
|
|||
|
|
|||
|
"go-common/library/conf/env"
|
|||
|
"go-common/library/log"
|
|||
|
"go-common/library/naming"
|
|||
|
"go-common/library/naming/discovery"
|
|||
|
"go-common/library/net/netutil/breaker"
|
|||
|
xtime "go-common/library/time"
|
|||
|
)
|
|||
|
|
|||
|
const (
|
|||
|
scheme = "gorpc"
|
|||
|
_policySharding = "sharding"
|
|||
|
)
|
|||
|
|
|||
|
// ClientConfig rpc client config.
|
|||
|
type ClientConfig struct {
|
|||
|
Policy string
|
|||
|
Zone string
|
|||
|
Cluster string
|
|||
|
Color string
|
|||
|
Timeout xtime.Duration
|
|||
|
Breaker *breaker.Config
|
|||
|
}
|
|||
|
|
|||
|
// Client2 support for load balancing and service discovery.
|
|||
|
type Client2 struct {
|
|||
|
c *ClientConfig
|
|||
|
appID string
|
|||
|
dis naming.Resolver
|
|||
|
balancer atomic.Value
|
|||
|
}
|
|||
|
|
|||
|
// NewDiscoveryCli new discovery client.
|
|||
|
func NewDiscoveryCli(appID string, cf *ClientConfig) (c *Client2) {
|
|||
|
if cf == nil {
|
|||
|
cf = &ClientConfig{Timeout: xtime.Duration(300 * time.Millisecond)}
|
|||
|
} else if cf.Timeout <= 0 {
|
|||
|
cf.Timeout = xtime.Duration(300 * time.Millisecond)
|
|||
|
}
|
|||
|
c = &Client2{
|
|||
|
c: cf,
|
|||
|
appID: appID,
|
|||
|
dis: discovery.Build(appID),
|
|||
|
}
|
|||
|
var pools = make(map[string]*Client)
|
|||
|
fmt.Printf("开始创建:%s 的gorpc client,等待从discovery拉取节点:%s\n", c.appID, time.Now().Format("2006-01-02 15:04:05"))
|
|||
|
event := c.dis.Watch()
|
|||
|
select {
|
|||
|
case _, ok := <-event:
|
|||
|
if ok {
|
|||
|
c.disc(pools)
|
|||
|
fmt.Printf("结束创建:%s 的gorpc client,从discovery拉取节点和创建成功:%s\n", c.appID, time.Now().Format("2006-01-02 15:04:05"))
|
|||
|
} else {
|
|||
|
panic("刚启动就从discovery拉到了关闭的event")
|
|||
|
}
|
|||
|
case <-time.After(10 * time.Second):
|
|||
|
fmt.Printf("失败创建:%s 的gorpc client,竟然从discovery拉取节点超时了:%s\n", c.appID, time.Now().Format("2006-01-02 15:04:05"))
|
|||
|
if env.DeployEnv == env.DeployEnvProd {
|
|||
|
panic("刚启动就从discovery拉节点超时,请检查配置或联系Discovery维护者")
|
|||
|
}
|
|||
|
}
|
|||
|
go c.discproc(event, pools)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// Boardcast boardcast all rpc client.
|
|||
|
func (c *Client2) Boardcast(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) (err error) {
|
|||
|
var (
|
|||
|
ok bool
|
|||
|
b balancer
|
|||
|
)
|
|||
|
if b, ok = c.balancer.Load().(balancer); ok {
|
|||
|
if err = b.Boardcast(ctx, serviceMethod, args, reply); err != ErrNoClient {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
return nil
|
|||
|
}
|
|||
|
|
|||
|
// Call invokes the named function, waits for it to complete, and returns its error status.
|
|||
|
// this include rpc.Client.Call method, and takes a timeout.
|
|||
|
func (c *Client2) Call(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) (err error) {
|
|||
|
var (
|
|||
|
ok bool
|
|||
|
b balancer
|
|||
|
)
|
|||
|
if b, ok = c.balancer.Load().(balancer); ok {
|
|||
|
if err = b.Call(ctx, serviceMethod, args, reply); err != ErrNoClient {
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
stats.Incr(serviceMethod, "no_rpc_client")
|
|||
|
return ErrNoClient
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client2) removeAndClose(pools, dcs map[string]*Client) {
|
|||
|
if len(dcs) == 0 {
|
|||
|
return
|
|||
|
}
|
|||
|
// after rpc timeout(double duration), close no used cliens
|
|||
|
if c.c != nil {
|
|||
|
to := c.c.Timeout
|
|||
|
time.Sleep(2 * time.Duration(to))
|
|||
|
}
|
|||
|
for key, cli := range dcs {
|
|||
|
delete(pools, key)
|
|||
|
cli.Close()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client2) discproc(event <-chan struct{}, pools map[string]*Client) {
|
|||
|
for {
|
|||
|
if _, ok := <-event; ok {
|
|||
|
c.disc(pools)
|
|||
|
continue
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (c *Client2) disc(pools map[string]*Client) (err error) {
|
|||
|
var (
|
|||
|
weights int64
|
|||
|
key string
|
|||
|
i, j, idx int
|
|||
|
nodes map[string]struct{}
|
|||
|
dcs map[string]*Client
|
|||
|
blc balancer
|
|||
|
cli *Client
|
|||
|
cs, wcs []*Client
|
|||
|
svr *naming.Instance
|
|||
|
)
|
|||
|
insMap, ok := c.dis.Fetch(context.Background())
|
|||
|
if !ok {
|
|||
|
log.Error("discovery fetch instance fail(%s)", c.appID)
|
|||
|
return
|
|||
|
}
|
|||
|
zone := env.Zone
|
|||
|
if c.c.Zone != "" {
|
|||
|
zone = c.c.Zone
|
|||
|
}
|
|||
|
tinstance, ok := insMap[zone]
|
|||
|
if !ok {
|
|||
|
for _, value := range insMap {
|
|||
|
tinstance = value
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
instance := make([]*naming.Instance, 0, len(tinstance))
|
|||
|
for _, svr := range tinstance {
|
|||
|
nsvr := new(naming.Instance)
|
|||
|
*nsvr = *svr
|
|||
|
cluster := svr.Metadata[naming.MetaCluster]
|
|||
|
if c.c.Cluster != "" && c.c.Cluster != cluster {
|
|||
|
continue
|
|||
|
}
|
|||
|
instance = append(instance, nsvr)
|
|||
|
}
|
|||
|
log.Info("discovery get %d instances ", len(instance))
|
|||
|
if len(instance) > 0 {
|
|||
|
nodes = make(map[string]struct{}, len(instance))
|
|||
|
cs = make([]*Client, 0, len(instance))
|
|||
|
dcs = make(map[string]*Client, len(pools))
|
|||
|
svrWeights := make([]int, 0, len(instance))
|
|||
|
weights = 0
|
|||
|
for _, svr = range instance {
|
|||
|
weight, err := strconv.ParseInt(svr.Metadata["weight"], 10, 64)
|
|||
|
if err != nil {
|
|||
|
weight = 10
|
|||
|
}
|
|||
|
key = svr.Hostname
|
|||
|
nodes[key] = struct{}{}
|
|||
|
|
|||
|
var addr string
|
|||
|
if cli, ok = pools[key]; !ok {
|
|||
|
for _, saddr := range svr.Addrs {
|
|||
|
u, err := url.Parse(saddr)
|
|||
|
if err == nil && u.Scheme == scheme {
|
|||
|
addr = u.Host
|
|||
|
}
|
|||
|
}
|
|||
|
if addr == "" {
|
|||
|
log.Warn("net/rpc: invalid rpc address(%s,%s,%v) found!", svr.AppID, svr.Hostname, svr.Addrs)
|
|||
|
continue
|
|||
|
}
|
|||
|
cli = Dial(addr, c.c.Timeout, c.c.Breaker)
|
|||
|
pools[key] = cli
|
|||
|
}
|
|||
|
svrWeights = append(svrWeights, int(weight))
|
|||
|
weights += weight // calc all weight
|
|||
|
log.Info("new cli %+v instance info %+v", addr, svr)
|
|||
|
cs = append(cs, cli)
|
|||
|
}
|
|||
|
// delete old nodes
|
|||
|
for key, cli = range pools {
|
|||
|
if _, ok = nodes[key]; !ok {
|
|||
|
log.Info("syncproc will delete node: %s", key)
|
|||
|
dcs[key] = cli
|
|||
|
}
|
|||
|
}
|
|||
|
// new client slice by weights
|
|||
|
wcs = make([]*Client, 0, weights)
|
|||
|
for i, j = 0, 0; i < int(weights); j++ { // j++ means next svr
|
|||
|
idx = j % len(cs)
|
|||
|
if svrWeights[idx] > 0 {
|
|||
|
i++ // i++ means all weights must fill wrrClis
|
|||
|
svrWeights[idx]--
|
|||
|
wcs = append(wcs, cs[idx])
|
|||
|
}
|
|||
|
}
|
|||
|
switch c.c.Policy {
|
|||
|
case _policySharding:
|
|||
|
blc = &sharding{
|
|||
|
pool: wcs,
|
|||
|
weight: int64(weights),
|
|||
|
server: int64(len(instance)),
|
|||
|
}
|
|||
|
log.Info("discovery syncproc sharding weights:%d size:%d raw:%d", weights, weights, len(instance))
|
|||
|
default:
|
|||
|
blc = &wrr{
|
|||
|
pool: wcs,
|
|||
|
weight: int64(weights),
|
|||
|
server: int64(len(instance)),
|
|||
|
}
|
|||
|
log.Info("discovery %s syncproc wrr weights:%d size:%d raw:%d", c.appID, weights, weights, len(instance))
|
|||
|
}
|
|||
|
c.balancer.Store(blc)
|
|||
|
c.removeAndClose(pools, dcs)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|