etcd_selector.go
package clientselector
import (
"errors"
"math/rand"
"net"
"net/rpc"
"net/url"
"strconv"
"strings"
"time"
"golang.org/x/net/context"
"github.com/coreos/etcd/client"
"github.com/smallnest/rpcx"
)
// EtcdClientSelector is used to select a rpc server from etcd.
//etcd 负载均衡器 结构体
type EtcdClientSelector struct {
EtcdServers []string //etcd 客户端连接地址列表
KeysAPI client.KeysAPI //etcd 连接客户端
ticker *time.Ticker //周期执行器
sessionTimeout time.Duration //连接超时时间
BasePath string //should endwith serviceName 服务路径或者服务名称
Servers []string //具体微服务地址列表
Group string //组名
clientAndServer map[string]*rpc.Client //map key 就提微服务地址 value 客户端连接
metadata map[string]string //
Latitude float64 //纬度
Longitude float64 //经度
WeightedServers []*Weighted //权重
SelectMode rpcx.SelectMode //选择器
dailTimeout time.Duration //连接超时时间
rnd *rand.Rand //序列号
currentServer int //当前第几个服务
len int //服务个数
HashServiceAndArgs HashServiceAndArgs //权重选择器
Client *rpcx.Client //rpcx客户端
}
// NewEtcdClientSelector creates a EtcdClientSelector
// 创建EtcdClientSelector结构体
func NewEtcdClientSelector(etcdServers []string, basePath string, sessionTimeout time.Duration, sm rpcx.SelectMode, dailTimeout time.Duration) *EtcdClientSelector {
selector := &EtcdClientSelector{
EtcdServers: etcdServers,
BasePath: basePath,
sessionTimeout: sessionTimeout,
SelectMode: sm,
dailTimeout: dailTimeout,
clientAndServer: make(map[string]*rpc.Client),
metadata: make(map[string]string),
rnd: rand.New(rand.NewSource(time.Now().UnixNano()))}
selector.start()
return selector
}
//SetClient set a Client in order that clientSelector can uses it
//客户端设置
func (s *EtcdClientSelector) SetClient(c *rpcx.Client) {
s.Client = c
}
//SetSelectMode sets SelectMode
//设置重选算法
func (s *EtcdClientSelector) SetSelectMode(sm rpcx.SelectMode) {
s.SelectMode = sm
}
//AllClients returns rpc.Clients to all servers
//直接返回微服务对应的直连客户端集合
func (s *EtcdClientSelector) AllClients(clientCodecFunc rpcx.ClientCodecFunc) []*rpc.Client {
var clients []*rpc.Client
for _, sv := range s.Servers {
ss := strings.Split(sv, "@")
//创建直连的客户端 http kcp (基于udp实现的协议) 或者https 以及golang支持的协议
c, err := rpcx.NewDirectRPCClient(s.Client, clientCodecFunc, ss[0], ss[1], s.dailTimeout)
if err == nil {
clients = append(clients, c)
}
}
return clients
}
//获取一个ETCD客户端
func (s *EtcdClientSelector) start() {
cli, err := client.New(client.Config{
Endpoints: s.EtcdServers,
Transport: client.DefaultTransport,
HeaderTimeoutPerRequest: s.sessionTimeout,
})
if err != nil {
return
}
s.KeysAPI = client.NewKeysAPI(cli)
s.pullServers()
// s.ticker = time.NewTicker(s.sessionTimeout)
// go func() {
// for range s.ticker.C {
// s.pullServers()
// }
// }()
go s.watch()
}
func (s *EtcdClientSelector) watch() {
watcher := s.KeysAPI.Watcher(s.BasePath, &client.WatcherOptions{
Recursive: true,
})
for {
res, err := watcher.Next(context.Background())
if err != nil {
break
}
//services are changed, we pull service again instead of processing single node
if res.Action == "expire" {
s.pullServers()
if !res.Node.Dir {
// clientAndServer delete the invalid client connection
removedServer := strings.TrimPrefix(res.Node.Key, s.BasePath+"/")
delete(s.clientAndServer, removedServer)
}
} else if res.Action == "set" || res.Action == "update" {
s.pullServers()
} else if res.Action == "delete" {
s.pullServers()
}
}
}
//
func (s *EtcdClientSelector) pullServers() {
//获取key对应的
resp, err := s.KeysAPI.Get(context.TODO(), s.BasePath, &client.GetOptions{
Recursive: true,
Sort: true,
})
//
if err == nil && resp.Node != nil {
if len(resp.Node.Nodes) > 0 {
var servers []string
for _, n := range resp.Node.Nodes {
servers = append(servers, strings.TrimPrefix(n.Key, s.BasePath+"/"))
}
s.Servers = servers
s.createWeighted(resp.Node.Nodes)
//set weight based on ICMP result
if s.SelectMode == rpcx.WeightedICMP {
for _, w := range s.WeightedServers {
server := w.Server.(string)
ss := strings.Split(server, "@")
host, _, _ := net.SplitHostPort(ss[1])
rtt, _ := Ping(host)
rtt = CalculateWeight(rtt)
w.Weight = rtt
w.EffectiveWeight = rtt
}
}
s.len = len(s.Servers)
if s.len > 0 {
s.currentServer = s.currentServer % s.len
}
} else {
// when the last instance is down, it should be deleted
s.clientAndServer = map[string]*rpc.Client{}
}
}
}
func (s *EtcdClientSelector) createWeighted(nodes client.Nodes) {
s.WeightedServers = make([]*Weighted, len(s.Servers))
var inactiveServers []int
for i, n := range nodes {
key := strings.TrimPrefix(n.Key, s.BasePath+"/")
s.WeightedServers[i] = &Weighted{Server: key, Weight: 1, EffectiveWeight: 1}
s.metadata[key] = n.Value
if v, err := url.ParseQuery(n.Value); err == nil {
w := v.Get("weight")
state := v.Get("state")
group := v.Get("group")
if (state != "" && state != "active") || (s.Group != group) {
inactiveServers = append(inactiveServers, i)
}
if w != "" {
if weight, err := strconv.Atoi(w); err == nil {
s.WeightedServers[i].Weight = weight
s.WeightedServers[i].EffectiveWeight = weight
}
}
}
}
s.removeInactiveServers(inactiveServers)
}
func (s *EtcdClientSelector) removeInactiveServers(inactiveServers []int) {
i := len(inactiveServers) - 1
for ; i >= 0; i-- {
k := inactiveServers[i]
removedServer := s.Servers[k]
s.Servers = append(s.Servers[0:k], s.Servers[k+1:]...)
s.WeightedServers = append(s.WeightedServers[0:k], s.WeightedServers[k+1:]...)
c := s.clientAndServer[removedServer]
if c != nil {
delete(s.clientAndServer, removedServer)
c.Close() //close connection to inactive server
}
}
}
func (s *EtcdClientSelector) getCachedClient(server string, clientCodecFunc rpcx.ClientCodecFunc) (*rpc.Client, error) {
c := s.clientAndServer[server]
if c != nil {
return c, nil
}
ss := strings.Split(server, "@") //
c, err := rpcx.NewDirectRPCClient(s.Client, clientCodecFunc, ss[0], ss[1], s.dailTimeout)
s.clientAndServer[server] = c
return c, err
}
// Select returns a rpc client
func (s *EtcdClientSelector) Select(clientCodecFunc rpcx.ClientCodecFunc, options ...interface{}) (*rpc.Client, error) {
if s.len == 0 {
return nil, errors.New("No available service")
}
switch s.SelectMode {
case rpcx.RandomSelect:
s.currentServer = s.rnd.Intn(s.len)
server := s.Servers[s.currentServer]
return s.getCachedClient(server, clientCodecFunc)
case rpcx.RoundRobin:
s.currentServer = (s.currentServer + 1) % s.len //not use lock for performance so it is not precise even
server := s.Servers[s.currentServer]
return s.getCachedClient(server, clientCodecFunc)
case rpcx.ConsistentHash:
if s.HashServiceAndArgs == nil {
s.HashServiceAndArgs = JumpConsistentHash
}
s.currentServer = s.HashServiceAndArgs(s.len, options)
server := s.Servers[s.currentServer]
return s.getCachedClient(server, clientCodecFunc)
case rpcx.WeightedRoundRobin, rpcx.WeightedICMP:
server := nextWeighted(s.WeightedServers).Server.(string)
return s.getCachedClient(server, clientCodecFunc)
case rpcx.Closest:
closestServers := getClosestServer(s.Latitude, s.Longitude, s.metadata)
selected := s.rnd.Intn(len(closestServers))
return s.getCachedClient(closestServers[selected], clientCodecFunc)
default:
return nil, errors.New("not supported SelectMode: " + s.SelectMode.String())
}
}
etcd_selector.go的更多相关文章
随机推荐
- 属性动画基础之ValueAnimator
概述 属性动画是谷歌在android3.0(API level 11)时候给我们带来了属性动画,真正意义上带来了"动画",以前的帧动画也就4中效果的组合(旋转.淡入淡出.放大缩小. ...
- gcc或clang中消除特定警告的方法
一般在编译代码时会有相当多的警告信息,尤其当我们使用了-Wall选项的时候.-Wall绝不是像其字面意思一样打开所有警告.不过它打开的警告也相当多了.对于一些我们已知"无害"但仍然 ...
- C# 设置Word文档保护(加密、解密、权限设置)
对于一些重要的word文档,出于防止资料被他人查看,或者防止文档被修改的目的,我们在选择文档保护时可以选择文档打开添加密码或者设置文档操作权限等,在下面的文章中将介绍如何使用类库Free Spire. ...
- Redis客户端ServiceStack.Redis的简单使用
在nuget中下载ServiceStack.Redis,但是运行之后会出现一个问题: Exception: "Com.JinYiWei.Cache.RedisHelper"的类型初 ...
- Python 3.7 将引入 dataclass 装饰器
简评:Python 3.7 将于今年夏天发布,Python 3.7 中将会有许多新东西,最激动人心的新功能之一是 dataclass 装饰器. 什么是 Data Class 大多数 Python 开发 ...
- CRM客户关系管理系统(十二)
十二章.学员报名流程开发 2 12.1.学员报名合同和证件信息上传 功能: 必须勾选报名合同协议 必须上传个人证件信息 最多只能上传三个文件 文件大小2M以内 列出已上传文件 (1)crm/urls ...
- balanced binary tree(判断是否是平衡二叉树)
Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...
- SpringBoot集成jsp
一.springBoot集成jsp: 1.修改pom文件 <!--集成jsp所需jar包--> <!--jsp页面使用jstl标签--> <dependency> ...
- 使用LSTM和Softmx来进行意图识别
前言 在前面我们大致介绍了什么是意图识别,把这个问题抽象出来其实是一个分类问题.在结构上面,我们使用LSTM来提取特征,Softmax来进行最后的多分类.由于语料的限制,我们目前仅考虑电台,音乐,问答 ...
- AWS技术会议笔记
Intel和云: SDI:软件定义架构 3D-XPointer:可以媲美内存速度的SSD 应用可以控制L3 Cache的使用 Helix物联网设备用 精益创业之路: 如何快速获得第一批用户---先要养 ...