resovler 服务名称解析
grpc微服务部署的时候,访问对应服务肯定不可能用ip的形式去访问,尤其云环境下,ip经常变动。采用resovler可以让我们客户端动态的去获取服务的地址去访问rpc服务。可以搭配服务中心来使用。具体框架图如下
resolver
例子如下,nameServer部分可以替换成地址数组即可,实际环境中就是向另一个服务器拿服务的地址
name_resolver.go
package client
import (
"context"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/resolver"
"grpc/name"
"log"
"time"
)
const (
MySchema = "myschema"
MyServiceName = "myecho"
)
//var adddrs = []string{"localhost:50053", "localhost:50054", "localhost:50055"}
type MyResolverBuilder struct {
}
func GetNameResolver(ns *NameServer) grpc.DialOption {
nameServer = ns
return grpc.WithResolvers(&MyResolverBuilder{})
}
func (*MyResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
r := &MyResolver{
target: target,
cc: cc,
addrsStore: map[string][]string{MyServiceName: nameServer.getAddressByServiceName(MyServiceName)},
}
r.start()
return r, nil
}
func (r *MyResolver) start() {
// 集合地址 转resolver addrs
log.Println("resolver start")
addrStrs := r.addrsStore[r.target.Endpoint()]
addrs := make([]resolver.Address, len(addrStrs))
for i,s := range addrStrs {
addrs[i] = resolver.Address{
Addr: s,
}
}
// 更新客户端连接地址
r.cc.UpdateState(resolver.State{
Addresses: addrs,
})
}
func (*MyResolverBuilder) Scheme() string {
return MySchema
}
type MyResolver struct {
target resolver.Target
cc resolver.ClientConn
addrsStore map[string][]string
}
func (r *MyResolver)ResolveNow(o resolver.ResolveNowOptions) {
log.Println("resolver now")
// 在服务,连接中断的时候,做一次名称解析
log.Println(r.cc)
// 重新获取可用的服务地址
r.addrsStore = map[string][]string{MyServiceName: nameServer.getAddressByServiceName(MyServiceName)}
// 更新连接状态
r.start()
log.Println("update:", r.cc)
}
func (r *MyResolver) Close() {
nameServer.Close()
}
type NameServer struct {
conn *grpc.ClientConn
}
var nameServer *NameServer
// NewNameServer 创建连接名称服务器连接
func NewNameServer(addr string) *NameServer {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
conn, err := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalln(err)
}
return &NameServer{
conn: conn,
}
}
func (ns *NameServer) Close() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
ns.conn.Close()
}
func (ns *NameServer) RegisterName(serviceName, address string) {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
client := name.NewNameClient(ns.conn)
in := &name.NameRequest{
ServiceName: serviceName,
Address: []string{address},
}
_, err := client.Register(context.Background(), in)
if err != nil {
log.Fatalln(err)
}
}
func (ns *NameServer) DeleteName(serviceName, address string) {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
client := name.NewNameClient(ns.conn)
in := &name.NameRequest{
ServiceName: serviceName,
Address: []string{address},
}
_, err := client.Delete(context.Background(), in)
if err != nil {
log.Fatalln(err)
}
}
func (ns *NameServer) Keepalive(serviceName, address string) {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
client := name.NewNameClient(ns.conn)
in := &name.NameRequest{
ServiceName: serviceName,
Address: []string{address},
}
stream, err := client.Keepalive(context.Background())
if err != nil {
log.Fatalln(err)
return
}
for {
err := stream.Send(in)
if err != nil {
log.Fatalln(err)
}
time.Sleep(time.Second * 2)
}
}
func (ns *NameServer) getAddressByServiceName(serviceName string) []string {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
client := name.NewNameClient(ns.conn)
in := &name.NameRequest{
ServiceName: serviceName,
}
res, err := client.GetAddress(context.Background(), in)
if err != nil {
log.Println(err)
return []string{}
}
log.Println(res.Address)
return res.Address
}
client.go
package main
import (
"flag"
"fmt"
"google.golang.org/grpc"
"grpc/echo"
"grpc/echo-client-practice/client"
"log"
"time"
)
func getDiaOption() []grpc.DialOption {
dialOptions := make([]grpc.DialOption, 0)
dialOptions = append(dialOptions, client.GetMTlsOpt())
dialOptions = append(dialOptions, grpc.WithUnaryInterceptor(client.UnaryInterceptor))
dialOptions = append(dialOptions, grpc.WithStreamInterceptor(client.StreamInterceptor))
dialOptions = append(dialOptions, client.GetAuth(client.FetchToken()))
dialOptions = append(dialOptions, client.GetKeepAliveOpt())
// 服务名称配置
dialOptions = append(dialOptions, client.GetNameResolver(client.NewNameServer("localhost:60051")))
return dialOptions
}
func main() {
flag.Parse()
// 根据协议+服务名 通过服务名称解析 访问服务器
conn, err := grpc.Dial(fmt.Sprintf("%s:///%s", client.MySchema, client.MyServiceName), getDiaOption()...)
if err != nil {
log.Fatal(err)
}
defer conn.Close()
c := echo.NewEchoClient(conn)
fmt.Println("1")
client.CallUnary(c)
time.Sleep(5 * time.Second)
fmt.Println("2")
client.CallUnary(c)
time.Sleep(5 * time.Second)
fmt.Println("3")
client.CallUnary(c)
time.Sleep(5 * time.Second)
fmt.Println("4")
client.CallUnary(c)
//client.CallServerStream(c)
//client.CallClientSteam(c)
//client.CallBidirectional(c)
}