刘沙河 刘沙河
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希

花开半夏,半夏花开
首页
  • Go语言基础

    • 数据类型
    • 反射
    • Go指针
  • Go语言进阶

    • go泛型
    • go条件编译
    • cgo教程
    • Go协程调度原理及GPM模型
    • Go内存管理
    • Go垃圾回收机制
    • Go语言内存对齐
  • Go语言实现原理

    • channel 实现原理
    • slice 实现原理
    • map 实现原理
    • sync.Mutex 实现原理
    • 乐观锁CAS 实现原理
    • singlefight 实现原理
  • gin框架

    • gin中间件原理
    • gin路由原理
  • gorm

    • GORM介绍和使用
    • GORM_CURD操作指南
  • go测试

    • benchmark基准测试
    • pprof 性能分析
  • python进阶

    • Numpy&Pandas
    • celery分布式任务队列
  • Django

    • Django 常见命令
    • middleware中间件
    • Django缓存系统
    • Django信号系统
    • Django REST Framework
  • Flask

    • Flask基础知识总结
    • Flask-SQLAlchemy
  • 爬虫

    • aiohttp
    • scrapy框架
  • Mysql

    • Mysql存储引擎和索引
    • MySQL主从复制
    • Mysql读写分离
    • 数据库分库分表
    • Mysql锁
    • Mysql事务和MVCC原理
    • 分库分表带来的读扩散问题
  • Redis

    • redis基础和数据类型
    • redis主从架构
    • redis哨兵架构
    • redis集群模式
    • 如何保证缓存和数据库双写一致
    • redis底层数据结构
    • redis分布式锁
  • Elasticsearch

    • es基本概念
    • es基础语法
    • es倒排索引
  • etcd

    • Go操作etcd
    • Raft原理
    • etcd分布式锁
  • kafka

    • 消息队列MQ总结
    • kafka 概述及原理
    • kafka 消费问题记录
    • 零拷贝技术
    • kafka分区规范
  • RabbitMQ

    • rabbitMQ基础
    • Go操作rabbitmq
  • RocketMQ

    • 可靠消息队列 rocketMQ
  • Http&Https

    • http&https
    • TCP和UDP
    • Ping 原理
  • RPC

    • RPC初识
    • grpc初识和实现
  • gRPC

    • grpc 初识
    • grpc 上下文 metadata
    • grpc 健康检查
    • grpc keepalive
    • grpc 命名解析
    • grpc 中间件&拦截器
    • grpc 负载均衡
    • grpc 身份认证
    • grpc 超时重试
    • grpc 链路追踪
    • grpc-gw将gRPC转RESTfu api
    • grpc-gw自定义选项
  • protobuf

    • protobuf 进阶
    • protobuf 编码原理
  • Docker

    • Docker基础
    • Docker常用命令
    • Dockerfile
    • Docker-Compose
    • Docker多阶段构建
    • Docker Config 教程
    • Docker Swarm 教程
    • Docker Stack 教程
    • Docker Buildx 教程
  • k8s

    • k8s 基础概念
    • k8s 集群架构
    • k8s 工作负载
    • Pod 网络
    • Service 网络
    • 外部接入网络
    • 一张图搞懂k8s各种pod
    • k8s 存储抽象
    • mac快速启动k8s
    • 自制申威架构k8s-reloader
  • go-kit

    • go-kit初识
    • go-kit启动http服务
    • go-kit集成gin启动服务
    • go-kit集成grpc和protobuf
    • go-kit中间件
    • go-kit服务注册发现与负载均衡
    • go-kit限流和熔断
    • go-kit链路追踪
    • go-kit集成Prometheus
  • 设计模式

    • 初识设计模式
    • 创建型模式
    • 结构型模式
    • 行为模式
  • 数据结构

    • 时间轮
    • 堆、双向链表、环形队列
    • 队列:优先队列
    • 队列:延迟队列
  • 算法

    • 递归算法
    • 枚举算法
    • 动态规划
    • 回溯算法
    • 分治算法
    • 贪心算法
    • LRU和LFU
    • 一致性哈希
  • rpc

  • grpc

    • 【grpc】1.初识
    • 【grpc】2.上下文 metadata
    • 【grpc】3.健康检查
    • 【grpc】 4.keepalive
    • 【grpc】 5.命名解析
      • 官方实现
      • 命名解析方式
      • 自定义命名解析
    • 【grpc】 6.中间件&拦截器
    • 【grpc】 7.负载均衡
    • 【grpc】 8.身份认证
    • 【grpc】 9.超时重试
    • 【grpc】 10.链路追踪
    • 【grpc】11.grpc-gw将gRPC 转 RESTful api
    • 【grpc】12.grpc-gw自定义选项
  • protobuf

  • rpc+grpc
  • grpc
bigox
2023-04-26
目录

【grpc】 5.命名解析

  • 假设一个非常庞大的业务系统,采用微服务的架构,为了提高系统的稳定性,每一个微服务至少部署2个节点以上,那么客户端每次进行RPC调用时,如何感知到后端的两个(甚至多个)节点,然后选择其中一个进行RPC调用。撇开RPC实现的技术,如Dubbo框架、Spring Cloud、gRPC,这里面会涉及两个非常核心的问题, grpc的命名解析就是解决了问题1, 问题2由balance解决,见后续章节

    1. 如何感知后端node数量

    2. 如何选择其中的节点进行RPC

  • 命名解析是指将服务名称转换为对应的网络地址的过程。在分布式系统中,服务通常被部署在多个节点上,每个节点有一个唯一的网络地址(如IP地址或域名)。客户端需要知道服务在哪些节点上运行,才能够与之进行通信。而命名解析的作用就是将服务名称映射到对应的网络地址,从而实现客户端和服务之间的通信。

  • gRPC 的命名解析(Name Resolution)是指将逻辑服务名称(例如 myservice)映射到实际的网络地址(例如 localhost:50051)的过程。

  • 与balancer结合使用来实现进程内负载均衡与服务发现。

# 官方实现

https://pkg.go.dev/google.golang.org/grpc/naming

  • 包:google.golang.org/grpc/naming(v1.29以上没有这个包了)
  • 新包:google.golang.org/grpc/resolver

# 命名解析方式

  • 目前gRPC内置了四种比较常用的命名解析功能
    1. dns:格式为"hostname:port",例如"example.com:50051"。
    2. ipv4 (IPv4 address):格式为"ipv4:address:port",例如"ipv4:192.168.1.1:50051"。
    3. ipv6 (IPv6 address):格式为"ipv6:[address]:port",例如"ipv6:[2001:db8::1]:50051"。
    4. unix (path to unix domain socket – unix systems only):格式为"unix:path",例如"unix:/var/run/my.sock"。

# 自定义命名解析

gRPC客户端工具包预留命名解析服务接口,供开发人员自定义命名解析功能。

  • 核心接口

    //该接口实时监听指定目标的状态,并及时更新配置
    type Resolver interface {
       // ResolveNow will be called by gRPC to try to resolve the target name
       // again. It's just a hint, resolver can ignore this if it's not necessary.
       //
       // It could be called multiple times concurrently.
       ResolveNow(ResolveNowOptions)
       // Close closes the resolver.
       Close()
    }
    ...
    // 建立scheme 与 service.name之间的关系;并绑定到客户端连接上
    type Builder interface {
    	
    	Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)
    	// 返回命名解析所支持的 scheme信息	
    	Scheme() string
    }
    
    /*
    	Builder 是接口类型,用于创建命名解析器,可监视命名空间是否发生变化,其方法有:
    	1) Scheme() string		// 返回解析器支持的方案
    	2) Build(target Target, cc ClientConn, opts BuildOptions) (Resolver, error)	// 创建解析器
    
    	Resolver 是接口类型,用于监控目标变化,当目标发生变化时,会相应地更新地址、服务配置,其方法有:
    	1) Close()		// 关闭解析器
    	2) ResolveNow(ResolveNowOptions)		// 备用接口,GRPC可以再次调用用于目标的解析
    	*、
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
  • 常量定义

    const (
    	exampleScheme      = "example"
    	exampleServiceName = "resolver.example.grpc.io"
    
    	backendAddr = "localhost:50051"
    )
    
    
    1
    2
    3
    4
    5
    6
    7
  • 自定义resolver

    type exampleResolver struct {
    	target     resolver.Target
    	cc         resolver.ClientConn
    	addrsStore map[string][]string
    }
    
    func (r *exampleResolver) 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 (*exampleResolver) ResolveNow(o resolver.ResolveNowOptions) {}
    func (*exampleResolver) Close()   
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
  • 自定义Builder

    type exampleResolverBuilder struct{}
    
    func (*exampleResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    	r := &exampleResolver{
    		target: target,
    		cc:     cc,
    		addrsStore: map[string][]string{
    			exampleServiceName: {backendAddr},
    		},
    	}
    	r.start()
    	return r, nil
    }
    func (*exampleResolverBuilder) Scheme() string { return exampleScheme }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • 加载命名服务

    func init() {
    	//这一步非常关键,否则就会出现解析不了的情况,错误信息如下
      //Unavailable desc = connection error: desc = "transport: Error while dialing dial tcp: lookup tcpresolver.example.grpc.io: nodename nor servname provided, or not known"
    	resolver.Register(&exampleResolverBuilder{})
    }
    
    
    1
    2
    3
    4
    5
    6
  • 完整客户端

    package main
    
    import (
    	"context"
    	"fmt"
    	"log"
    	"time"
    
    	"google.golang.org/grpc"
    	"google.golang.org/grpc/credentials/insecure"
    	ecpb "google.golang.org/grpc/examples/features/proto/echo"
    	"google.golang.org/grpc/resolver"
    )
    
    const (
    	exampleScheme      = "example"
    	exampleServiceName = "resolver.example.grpc.io"
    
    	backendAddr = "localhost:50051"
    )
    
    func callUnaryEcho(c ecpb.EchoClient, message string) {
    	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    	defer cancel()
    	r, err := c.UnaryEcho(ctx, &ecpb.EchoRequest{Message: message})
    	if err != nil {
    		log.Fatalf("could not greet: %v", err)
    	}
    	fmt.Println(r.Message)
    }
    
    func makeRPCs(cc *grpc.ClientConn, n int) {
    	hwc := ecpb.NewEchoClient(cc)
    	for i := 0; i < n; i++ {
    		callUnaryEcho(hwc, "this is examples/name_resolving")
    	}
    }
    
    func main() {
    	exampleConn, err := grpc.Dial(
    		fmt.Sprintf("%s:///%s", exampleScheme, exampleServiceName), // Dial to "example:///resolver.example.grpc.io"
    		grpc.WithTransportCredentials(insecure.NewCredentials()),
    	)
    	if err != nil {
    		log.Fatalf("did not connect: %v", err)
    	}
    	defer exampleConn.Close()
    
    	fmt.Printf("--- calling helloworld.Greeter/SayHello to \"%s:///%s\"\n", exampleScheme, exampleServiceName)
    	makeRPCs(exampleConn, 5)
    }
    
    type exampleResolverBuilder struct{}
    
    func (*exampleResolverBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
    	r := &exampleResolver{
    		target: target,
    		cc:     cc,
    		addrsStore: map[string][]string{
    			exampleServiceName: {backendAddr},
    		},
    	}
    	r.start()
    	return r, nil
    }
    func (*exampleResolverBuilder) Scheme() string { return exampleScheme }
    
    // exampleResolver is a
    // Resolver(https://godoc.org/google.golang.org/grpc/resolver#Resolver).
    type exampleResolver struct {
    	target     resolver.Target
    	cc         resolver.ClientConn
    	addrsStore map[string][]string
    }
    
    func (r *exampleResolver) 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 (*exampleResolver) ResolveNow(o resolver.ResolveNowOptions) {}
    func (*exampleResolver) Close()                                  {}
    
    func init() {
    	resolver.Register(&exampleResolverBuilder{})
    }
    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
#grpc
上次更新: 2023/05/04, 15:30:48
【grpc】 4.keepalive
【grpc】 6.中间件&拦截器

← 【grpc】 4.keepalive 【grpc】 6.中间件&拦截器→

最近更新
01
go与http代理
05-24
02
自制申威架构k8s-reloader
12-06
03
Docker Buildx 教程
12-01
更多文章>
Theme by Vdoing | Copyright © 2020-2024 小刘扎扎 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式