Initial commit

This commit is contained in:
Cruise Zhao
2020-04-06 19:57:19 +08:00
commit 2fc86fd0a1
42 changed files with 4056 additions and 0 deletions

66
qgrpc/config.go Normal file
View File

@@ -0,0 +1,66 @@
package qgrpc
import (
"fmt"
"net"
"os"
"strings"
)
// 基础配置,公共字段
type BasicConfig struct {
DialTimeout int `toml:"dial_timeout"` // 连接超时时间
KeepAlive int `toml:"keep_alive"` // 保活时间
EndPoints []string `toml:"endpoints"` // etcd的地址
Prefix string `toml:"prefix"` // etcd服务的路径前缀区分环境用
}
// 服务端配置
type ServerConfig struct {
BasicConfig
Servers []string `toml:"servers"` // 注册的服务名称,可以是多个
Addr string `toml:"addr"` // 服务地址
}
func (self *ServerConfig) GetAddr() string {
if strings.HasPrefix(self.Addr, ":") {
localIP := getLocalIP()
return fmt.Sprintf("%s%s", localIP, self.Addr)
}
return self.Addr
}
// 客户端配置
type ClientConfig struct {
BasicConfig
WatchServers []string `toml:"watch_servers"` // 依赖的服务名称,多个服务用','分割;每个服务名后增加'|ip:port'表示默认连接的服务地址
}
func getHostName() string {
if h, err := os.Hostname(); err == nil {
return h
}
return getLocalIP()
}
//获取本机的内网Ip, 如果发现对方的ip 和自己的ip 相同用127.0.0.1 替代
func getLocalIP() string {
ifaces, _ := net.Interfaces()
for _, i := range ifaces {
addrs, _ := i.Addrs()
for _, addr := range addrs {
var ip net.IP
switch v := addr.(type) {
case *net.IPNet:
ip = v.IP
case *net.IPAddr:
ip = v.IP
}
ipAddr := ip.String()
if strings.HasPrefix(ipAddr, "172.") || strings.HasPrefix(ipAddr, "192.") || strings.HasPrefix(ipAddr, "10.") {
return ipAddr
}
}
}
return "127.0.0.1"
}

141
qgrpc/op.go Normal file
View File

@@ -0,0 +1,141 @@
package qgrpc
import (
"context"
"fmt"
"github.com/coreos/etcd/clientv3"
"globalfintech/golib/qgrpc/ping"
"globalfintech/golib/tlog"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
"strings"
"time"
)
type Server struct {
userCfg *ServerConfig
etcdCli *clientv3.Client
etcdId clientv3.LeaseID
}
type Client struct {
userCfg *ClientConfig
etcdCli *clientv3.Client
etcdId clientv3.LeaseID
rpcServers map[string]*grpc.ClientConn
}
type RpcServer struct {
Name string //服务的名字
Addrs []string //默认的地址
}
const (
DFT_DIAL_TIMEOUT = 3
)
func etcdDialWrapper(cfg *BasicConfig) (*clientv3.Client, error) {
dialTimeout := DFT_DIAL_TIMEOUT
if cfg.DialTimeout > 0 {
dialTimeout = cfg.DialTimeout
}
return clientv3.New(clientv3.Config{
Endpoints: cfg.EndPoints,
DialTimeout: time.Duration(dialTimeout) * time.Second,
})
}
func ServerRegist(cfg *ServerConfig) (server *Server, err error) {
server = &Server{userCfg: cfg}
if server.etcdCli, err = etcdDialWrapper(&cfg.BasicConfig); err != nil {
return nil, err
}
server.register()
return server, nil
}
func NewClient(cfg *ClientConfig) (client *Client, err error) {
client = &Client{userCfg: cfg}
if client.etcdCli, err = etcdDialWrapper(&cfg.BasicConfig); err != nil {
return nil, err
}
client.rpcServers = make(map[string]*grpc.ClientConn)
keepAlive := 3
if cfg.KeepAlive > 0 {
keepAlive = cfg.KeepAlive
}
for _, watchServer := range cfg.WatchServers {
rpcServer := ParseServerName(watchServer)
resolver := newresolver(client, rpcServer.Name, rpcServer.Addrs)
rrBalancer := grpc.RoundRobin(resolver)
client.rpcServers[rpcServer.Name], err = grpc.Dial("", grpc.WithInsecure(),
grpc.WithTimeout(time.Second),
grpc.WithKeepaliveParams(keepalive.ClientParameters{Time: time.Duration(keepAlive) * time.Second}),
grpc.WithBalancer(rrBalancer))
}
go client.ping()
return
}
//user.User|127.0.0.1:9011 -> Server{Name: user.User Addrs: []string{127.0.0.1:9011}}
func ParseServerName(serverName string) *RpcServer {
ss := strings.Split(serverName, "|")
s := &RpcServer{Name: ss[0]}
//没有配置默认地址
if len(ss) < 2 {
return s
}
s.Addrs = strings.Split(ss[1], ",")
return s
}
//根据server-name 获得对应的连接池
func (self *Client) GetConn(serverName string) *grpc.ClientConn {
return self.rpcServers[serverName]
}
//从etcd注销
func (self *Server) Revoke() error {
_, err := self.etcdCli.Revoke(context.Background(), self.etcdId)
return err
}
func (self *Client) Call(ctx context.Context, method string, args, reply interface{}, opts ...grpc.CallOption) (err error) {
ss := strings.Split(method, "/")
fmt.Println("ss:", ss, len(ss))
if len(ss) < 3 {
tlog.Errorf("no grpc client available:%s", method)
return fmt.Errorf("no grpc client available:%s", method)
}
conn, ok := self.rpcServers[ss[1]]
fmt.Println("conn:", conn)
if !ok {
tlog.Errorf("no grpc client available:%s", method)
return fmt.Errorf("no grpc client available:%s", method)
}
err = grpc.Invoke(ctx, method, args, reply, conn, opts...)
return
}
func (self *Client) ping() {
for range time.NewTicker(time.Second).C {
for server, conn := range self.rpcServers {
req, resp := &ping.NoArgs{}, &ping.NoArgs{}
if err := grpc.Invoke(context.Background(), strings.Join([]string{server, "Ping"}, "/"), req, resp, conn); err != nil {
tlog.Errorf("qgrpc ping||server=%s||err=%s", server, err)
}
}
}
}

51
qgrpc/ping/ping.pb.go Normal file
View File

@@ -0,0 +1,51 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: ping.proto
/*
Package ping is a generated protocol buffer package.
It is generated from these files:
ping.proto
It has these top-level messages:
NoArgs
*/
package ping
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
type NoArgs struct {
}
func (m *NoArgs) Reset() { *m = NoArgs{} }
func (m *NoArgs) String() string { return proto.CompactTextString(m) }
func (*NoArgs) ProtoMessage() {}
func (*NoArgs) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
func init() {
proto.RegisterType((*NoArgs)(nil), "ping.NoArgs")
}
func init() { proto.RegisterFile("ping.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 66 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0xc8, 0xcc, 0x4b,
0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0xd8, 0xb9, 0x58, 0x5d, 0x73,
0x0b, 0x4a, 0x2a, 0x9d, 0x04, 0xa2, 0xf8, 0x72, 0x32, 0x93, 0xf4, 0xc1, 0x72, 0xfa, 0x20, 0xa9,
0x24, 0x36, 0x30, 0xdb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xb5, 0x65, 0x5a, 0x5a, 0x35, 0x00,
0x00, 0x00,
}

60
qgrpc/register.go Normal file
View File

@@ -0,0 +1,60 @@
package qgrpc
import (
"context"
"github.com/coreos/etcd/clientv3"
"globalfintech/golib/tlog"
"path"
"time"
)
func etcdGrantWrapper(cli *clientv3.Client) (clientv3.LeaseID, error) {
resp, err := cli.Grant(context.Background(), 5)
if err != nil {
return 0, err
}
return resp.ID, nil
}
func (self *Server) register() (err error) {
if len(self.userCfg.Prefix) < 1 || len(self.userCfg.Addr) < 1 {
tlog.Info("qgrpc server||register||prefix or addr less than 1 char")
return nil
}
if self.etcdId, err = etcdGrantWrapper(self.etcdCli); err != nil {
return err
}
for _, server := range self.userCfg.Servers {
addr := self.userCfg.GetAddr()
etcdKey := path.Join(self.userCfg.Prefix, server, addr)
if _, err = self.etcdCli.Put(context.Background(), etcdKey, addr, clientv3.WithLease(self.etcdId)); err != nil {
return err
}
}
ch, err := self.etcdCli.KeepAlive(context.Background(), self.etcdId)
if err != nil {
tlog.Error(err)
}
go self.keepalive(ch)
return err
}
func (self *Server) keepalive(ch <-chan *clientv3.LeaseKeepAliveResponse) {
for {
resp := <-ch
if resp == nil {
tlog.Error("qgrpc server||keepalive||nil response")
self.Revoke()
time.Sleep(time.Second)
self.register()
return
}
}
}

148
qgrpc/watch.go Normal file
View File

@@ -0,0 +1,148 @@
package qgrpc
import (
"context"
"fmt"
"github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
"google.golang.org/grpc/naming"
"path"
"strings"
"time"
)
type watcher struct {
prefix string
addrs []string
c *clientv3.Client
hasStart bool
}
type resolver struct {
client *Client
name string
addrs []string
}
func newresolver(client *Client, name string, addrs []string) *resolver {
return &resolver{client: client, name: name, addrs: addrs}
}
func (self *resolver) Resolve(name string) (naming.Watcher, error) {
return self.client.newwatcher(self.name, self.addrs), nil
}
func (self *Client) newwatcher(name string, addrs []string) naming.Watcher {
prefix := path.Join(self.userCfg.Prefix, name)
return &watcher{prefix: prefix, addrs: addrs, c: self.etcdCli}
}
func (w *watcher) Close() {}
func (w *watcher) Next() ([]*naming.Update, error) {
//第一次启动从etcd上获取所有的地址同时加上默认的地址
if !w.hasStart {
updates := []*naming.Update{}
//etcd 的连接正常
if w.c != nil {
resp, err := w.c.Get(context.Background(), w.prefix, clientv3.WithPrefix())
if err == nil {
addrs := extractAddrs(resp)
for _, addr := range addrs {
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: addr})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), "127.0.0.1:", -1)})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), "localhost:", -1)})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), getHostName()+":", -1)})
}
}
}
//将默认的地址加进入
for _, addr := range w.addrs {
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: addr})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), "127.0.0.1:", -1)})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), "localhost:", -1)})
updates = append(updates, &naming.Update{
Op: naming.Add,
Addr: strings.Replace(addr, fmt.Sprintf("%s:", getLocalIP()), getHostName()+":", -1)})
}
w.hasStart = true
return updates, nil
}
if w.c == nil {
time.Sleep(time.Second)
return nil, nil
}
rch := w.c.Watch(context.Background(), w.prefix, clientv3.WithPrefix())
for wresp := range rch {
for _, ev := range wresp.Events {
switch ev.Type {
case mvccpb.PUT:
_, value := string(ev.Kv.Key), string(ev.Kv.Value)
//fmt.Println(time.Now(), "[PUT]", key, value)
return []*naming.Update{
&naming.Update{
Op: naming.Add,
Addr: value},
&naming.Update{
Op: naming.Add,
Addr: strings.Replace(value, fmt.Sprintf("%s:", getLocalIP()), "127.0.0.1:", -1)},
&naming.Update{
Op: naming.Add,
Addr: strings.Replace(value, fmt.Sprintf("%s:", getLocalIP()), "localhost:", -1)},
&naming.Update{
Op: naming.Add,
Addr: strings.Replace(value, fmt.Sprintf("%s:", getLocalIP()), getHostName()+":", -1)},
}, nil
case mvccpb.DELETE:
_, value := string(ev.Kv.Key), string(ev.Kv.Value)
//fmt.Println(time.Now(), "[DELETE]", key)
return []*naming.Update{
&naming.Update{
Op: naming.Delete,
Addr: value,
},
}, nil
}
}
}
return nil, nil
}
func extractAddrs(resp *clientv3.GetResponse) []string {
addrs := []string{}
if resp == nil || resp.Kvs == nil {
return addrs
}
for _, v := range resp.Kvs {
if v.Value != nil {
addrs = append(addrs, string(v.Value))
}
}
return addrs
}