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的更多相关文章
随机推荐
- ubuntu12.04:Tomcat 7服务器:手动安装
1.下载tomcat7.0.34. 网址:http://tomcat.apache.org/ 2.下载的文件解压在下载: 进入目录: cd /usr/local 创建目录 : sudo mkdir d ...
- Django之跨域请求
同源策略 首先基于安全的原因,浏览器是存在同源策略这个机制的,同源策略阻止从一个源加载的文档或脚本获取或设置另一个源加载的文档的属性. 而如果我们要跳过这个策略,也就是说非要跨域请求,那么就需要通过J ...
- python 之路,200行Python代码写了个打飞机游戏!
早就知道pygame模块,就是没怎么深入研究过,恰逢这周未没约到妹子,只能自己在家玩自己啦,一时兴起,花了几个小时写了个打飞机程序. 很有意思,跟大家分享下. 先看一下项目结构 "" ...
- UITableViewCell嵌套UITableView的正确姿势
内嵌UiTableView的高度计算起来太麻烦了,如何解决,就是把二级TableVIew里面的model item做到一级,然后对不同的item类型做不同的Cell,这样就Ok了.给一个得到Cell的 ...
- win10更新失败——适用于Windows 10 Version 1709 的03累积更新,适合基于x64系统(KB4088776)更新失败
相信最近很多人被windows的更新折磨坏了,下面来介绍一下解决办法,有用的话请点赞! 首先将C盘中的这个文件夹删除:"C:\Windows\System32\Tasks\Microsoft ...
- POST与PUT
POST和PUT都是HTTP中客户端向服务器发送请求的方法 POST : 向指定资源提交数据,请求服务器进行处理(例如提交表单或者上传文件).数据被包含在请求本文中.这个请求可能会创建新的 资源或修改 ...
- Lua读取CSV文件到table中
创建Lua函数载入CSV文件并保存到表中的函数: function GetLines(fileName) indx = 0 myLines ={} for line in io.line(string ...
- ubuntu 16.04安装smatrgitHG工具
SmartGit/HG 是一款开放源代码的.跨平台的.支持 Git 和 Mercurial 的 SVN 图形客户端,可运行在Windows.Linux 和 MAC OS X 系统上. 1.安装 Ubu ...
- 用Python+qrcode库创建一个包含信息的二维码
安装qrcode库和PIL库 在命令行中分别输入pip install qrcode 和pip install pillow 导入库格式如下: import PIL import qrcode 下面以 ...
- 关于overfit的随笔
看到@ 爱可可-爱生活转发的文章.稍微看了下,在这里记录下. overfit是机器学习的一个重要概念.在狭义上可以定义为模型过于复杂,导致模型的generalization不够好.我认为应采用一个更广 ...