微服务之服务注册和服务发现篇

二维TE波ADI-FDTD程序包使用指南:解压前的三件事 时域有限差分(FDTD)是电磁仿真的核心方法,但传统显式格式受Courant稳定条件约束,网格细化后时间步长被迫缩小,计算成本剧增。交替方向隐式时域有限差分(ADI-FDTD)通过将每个时间步分裂为两个半步,以隐式更新换取对稳定条件的突破,在保持精度的同时显著放大时间步长。针对二维TE波(仅含Ex、Ey、Hz分量),ADI-FDTD只需交替求解两个方向的三对角方程,非常适合波导、光子晶体、微带电路等纵向均匀结构的建模。然而,“无条件稳定”不等于无条件准确,程序落地仍需验证场分量类型、时间步放大倍数及PML吸 阅读详情

有了服务注册和发现机制,消费者不需要知道具体服务提供者的真实物理地址就可以进行调用,也无须知道具体有多少个服务者可用;而服务提供者只需要注册到注册中心,就可以对外提供服务,在对外服务时不需要知道具体是哪些服务调用了自己。

RPC 配置

Etcd:
  Hosts:
  - 127.0.0.1:2379
  Key: user.rpc
  • 这里分析 go-zero 的 etcd 部分源码, 源码引用 https://github.com/zeromicro/go-zero-demo/tree/master/mall

被调方-服务注册

  • mall/user/rpc/user.go 源码如下
package main

import (
	"flag"
	"fmt"

	"go-zero-demo-rpc/mall/user/rpc/internal/config"
	"go-zero-demo-rpc/mall/user/rpc/internal/server"
	"go-zero-demo-rpc/mall/user/rpc/internal/svc"
	"go-zero-demo-rpc/mall/user/rpc/types/user"

	"github.com/zeromicro/go-zero/core/conf"
	"github.com/zeromicro/go-zero/core/service"
	"github.com/zeromicro/go-zero/zrpc"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

var configFile = flag.String("f", "etc/user.yaml", "the config file")

func main() {
    flag.Parse()
    
    var c config.Config
    conf.MustLoad(*configFile, &c)
    ctx := svc.NewServiceContext(c)
    svr := server.NewUserServer(ctx)
    
    s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {
        user.RegisterUserServer(grpcServer, svr)
    
        if c.Mode == service.DevMode || c.Mode == service.TestMode {
            reflection.Register(grpcServer)
        }
    })
    defer s.Stop()
    
    fmt.Printf("Starting rpc server at %s...\n", c.ListenOn)
    s.Start()
}
  • MustNewServer 内部实现调用了 NewServer 方法, 这里我们关注 NewServer 通过 internal.NewRpcPubServer 方法实例化了 internal.Server
if c.HasEtcd() {
    server, err = internal.NewRpcPubServer(c.Etcd, c.ListenOn, serverOptions...)
    if err != nil {
        return nil, err
    }
}
  • internal.NewRpcPubServer 中的 registerEtcd 会调用 Publisher.KeepAlive 方法
// KeepAlive keeps key:value alive.
func (p *Publisher) KeepAlive() error {
    // 这里获取 etcd 的连接
    cli, err := internal.GetRegistry().GetConn(p.endpoints)
    if err != nil {
        return err
    }
    
    p.lease, err = p.register(cli)
    if err != nil {
        return err
    }
    
    proc.AddWrapUpListener(func() {
        p.Stop()
    })
    
    return p.keepAliveAsync(cli)
}
  • p.register 这里把自己注册到服务中
func (p *Publisher) register(client internal.EtcdClient) (clientv3.LeaseID, error) {

    // 这里新建一个租约
    resp, err := client.Grant(client.Ctx(), TimeToLive)
    if err != nil {
        return clientv3.NoLease, err
    }
    
    // 得到租约的 ID
    lease := resp.ID
    
    // 这里拼接出实际存储的 key
    if p.id > 0 {
        p.fullKey = makeEtcdKey(p.key, p.id)
    } else {
        p.fullKey = makeEtcdKey(p.key, int64(lease))
    }

    // p.value 是前面的 figureOutListenOn 方法获取到自己的地址
	_, err = client.Put(client.Ctx(), p.fullKey, p.value, clientv3.WithLease(lease))

    return lease, err
}
  • 注册完之后, keepAliveAsync 开了一个协程保活这个服务
  • 当这个服务意外宕机时, 就不会再向 etcd 保活, etcd 就会删除这个 key
  • 注册好的服务如图

1.png

调用方-服务发现

  • order/api/order.go 源码如下
package main

import (
	"flag"
	"fmt"

	"go-zero-demo-rpc/order/api/internal/config"
	"go-zero-demo-rpc/order/api/internal/handler"
	"go-zero-demo-rpc/order/api/internal/svc"

	"github.com/zeromicro/go-zero/core/conf"
	"github.com/zeromicro/go-zero/rest"
)

var configFile = flag.String("f", "etc/order.yaml", "the config file")

func main() {
	flag.Parse()

	var c config.Config
	conf.MustLoad(*configFile, &c)

	server := rest.MustNewServer(c.RestConf)
	defer server.Stop()

	ctx := svc.NewServiceContext(c)
	handler.RegisterHandlers(server, ctx)

	fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
	server.Start()
}
  • 在 svc.NewServiceContext 方法内部又调用了 zrpc.MustNewClient , zrpc.MustNewClient 主要实现在 zrpc.NewClient
func NewServiceContext(c config.Config) *ServiceContext {
	return &ServiceContext{
		Config:  c,
		UserRpc: user.NewUser(zrpc.MustNewClient(c.UserRpc)),
	}
}
  • 最后实际调用了 internal.NewClient 去实例化 rpc client
func NewClient(c RpcClientConf, options ...ClientOption) (Client, error) {
	var opts []ClientOption
	if c.HasCredential() {
		opts = append(opts, WithDialOption(grpc.WithPerRPCCredentials(&auth.Credential{
			App:   c.App,
			Token: c.Token,
		})))
	}
	if c.NonBlock {
		opts = append(opts, WithNonBlock())
	}
	if c.Timeout > 0 {
		opts = append(opts, WithTimeout(time.Duration(c.Timeout)*time.Millisecond))
	}

	opts = append(opts, options...)

	target, err := c.BuildTarget()
	if err != nil {
		return nil, err
	}

	client, err := internal.NewClient(target, opts...)
	if err != nil {
		return nil, err
	}

	return &RpcClient{
		client: client,
	}, nil
}
  • 在 zrpc/internal/client.go 文件里, 包含一个 init 方法, 这里就是实际发现服务的地方, 在这里注册服务发现者
func init() {
	resolver.Register()
}
  • resolver.Register 方法实现
package resolver

import (
	"github.com/zeromicro/go-zero/zrpc/resolver/internal"
)

// Register registers schemes defined zrpc.
// Keep it in a separated package to let third party register manually.
func Register() {
	internal.RegisterResolver()
}
  • 最后又回到 interval 包的 internal.RegisterResolver 方法, 这里我们关注 etcdResolverBuilder
func RegisterResolver() {
	resolver.Register(&directResolverBuilder)
	resolver.Register(&discovResolverBuilder)
	resolver.Register(&etcdResolverBuilder)
	resolver.Register(&k8sResolverBuilder)
}
  • etcdBuilder 的内嵌了 discovBuilder 结构体,Build 方法调用过程:实例化服务端: internal.NewClient -> client.dial -> grpc.DialContext由于 etcd 是 resolver.BuildDiscovTarget 生成的 taget 所以是类似这样子的: discov://127.0.0.1:2379/user.rpc解析服务发现: ClientConn.parseTargetAndFindResolver -> grpc.parseTarget -> ClientConn.getResolver然后在 grpc.newCCResolverWrapper 调用 resolver.Builder.Build 方法是去发现服务
  • 我们着重关注 discovBuilder.Build 方法
type etcdBuilder struct {
	discovBuilder
}


type discovBuilder struct{}

func (b *discovBuilder) Build(target resolver.Target, cc resolver.ClientConn, _ resolver.BuildOptions) (
	resolver.Resolver, error) {
	hosts := strings.FieldsFunc(targets.GetAuthority(target), func(r rune) bool {
		return r == EndpointSepChar
	})
	sub, err := discov.NewSubscriber(hosts, targets.GetEndpoints(target))
	if err != nil {
		return nil, err
	}

	update := func() {
		var addrs []resolver.Address
		for _, val := range subset(sub.Values(), subsetSize) {
			addrs = append(addrs, resolver.Address{
				Addr: val,
			})
		}
		if err := cc.UpdateState(resolver.State{
			Addresses: addrs,
		}); err != nil {
			logx.Error(err)
		}
	}
	sub.AddListener(update)
	update()

	return &nopResolver{cc: cc}, nil
}

func (b *discovBuilder) Scheme() string {
	return DiscovScheme
}
  • discov.NewSubscriber 方法调用 internal.GetRegistry().Monitor 最后调用 Registry.monitor 方法进行监视cluster.getClient 拿到 etcd 连接cluster.load 作为第一次载入数据cluster.watch 去 watch 监听 etcd 前缀 key 的改动
func (c *cluster) monitor(key string, l UpdateListener) error {
	c.lock.Lock()
	c.listeners[key] = append(c.listeners[key], l)
	c.lock.Unlock()

	cli, err := c.getClient()
	if err != nil {
		return err
	}

	c.load(cli, key)
	c.watchGroup.Run(func() {
		c.watch(cli, key)
	})

	return nil
}
  • 如下图是 cluster.load 的实现, 就是根据前缀拿到 user.prc 服务器注册的所有地址

2.png

Q

  • 为什么不用 Redis 注册中心(反正只是把被调查方的地址存储, 过期 Redis 也能胜任), 找了很久才找到这个说法

简单从以下几个方面说一下瑞迪斯为啥在微服务中不能取代 etcd:

1、redis 没有版本的概念,历史版本数据在大规模微服务中非常有必要,对于状态回滚和故障排查,甚至定锅都很重要

2、redis 的注册和发现目前只能通过 pub 和 sub 来实现,这两个命令完全不能满足生产环境的要求,具体原因可以 gg 或看源码实现

3、etcd 在 2.+版本时,watch 数据官方文档均建议在 get 一次,因为会存在数据延迟,3.+版本不再需要,可想而知 redis 的 pub 和 sub 能否达到此种低延迟的要求

4、楼主看到的微服务架构应该都是将 etcd 直接暴露给 client 和 server 的,etcd 的性能摆在那,能够承受多少的 c/s 直连呢,更好的做法应该是对 etcd 做一层保护,当然这种做法会损失一些功能

5、redis 和 etcd 的集群实现方案是不一致的,etcd 采用的是 raft 协议,一主多从,只能写主,底层采用 boltdb 作为 k/v 存储,直接落盘

6、redis 的持久化方案有 aof 和 rdb,这两种方案在宕机的时候都或多或少的会丢失数据

【生信】全基因组测序(WGS) 1、全基因组测序(WGS) 的定义 2、GWS流程 2.1准备工作——分析软件 2.2原始数据质控 2.3数据预处理 2.4变异检测 阅读详情

相关推荐

用Navicat轻松管理魔兽世界私服数据库:修改IP、添加装备的实用技巧

本文详细介绍了如何使用Navicat管理魔兽世界私服数据库,包括修改服务器IP、添加自定义装备和调整角色属性等实用技巧。通过专业的SQL操作指南,帮助玩家轻松实现私服的高级定制,提升游戏体验。

fanta的博客 947

SpringCloud微服务——服务注册和发现

在hm-api模块下新建一个配置类,定义Feign的日志级别:@Bean在上述业务中,包含大量的微服务调用,将被调用的接口全部定义为FeignClient,将其与对应的DTO放在hm-api模块。

qq_51773341的博客 1183

Zenoh:统一数据平面协议,重塑物联网与边缘计算架构

在分布式系统与物联网领域,数据通信与处理常面临协议割裂的挑战。传统方案如MQTT、Kafka、gRPC等,分别针对发布/订阅、流处理和RPC等不同场景,导致架构复杂、集成成本高。其核心问题在于未能统一处理“运动中数据”(Data in Motion)与“静止中数据”(Data at Rest)。Zenoh协议通过创新的“Key-Value”寻址与“Selector”查询语言,将发布/订阅、存储、查询和计算四种核心操作融合进单一协议,构建了统一的抽象层。这种设计实现了极低的协议开销(仅5字节头部)和地理分布式

weixin_29597551的博客 291

go-zero http 请求流程简析

go-zero http请求及路由原理简兮

lu0602520的博客 2707

stm32f103运行FreeRTOS使用DMA实现USART1不定长接收

使用DMA是实现USART1的不定长接收和发送数据。接收采用FIFO缓存和队列的方式向应用层抛数据。首先定义接收和发送的缓存区,缓存区的大小根据收发数据量确定。DMA接收开启转移完成和转移一般中断。在中断中从缓存向FIFO转移数据。串口开启空闲中断,实现接收数据的分帧。分帧后的数据节点通过写入到队列。发送数据缓存的大小根据发送数据量调整。然后初始化串口和DMA。串口的发送数据接口函数。...

polaris_zgx的博客 1991

STM32F103 实例应用(10)——I2C读写24C32

一、应用简介 本文介绍24C32的读写,基于野火的软件I2C程序。 board_i2c.h /**=========================================================================== @file board_i2c.h @brief 本文件是用于I2C驱动 @author 青梅煮久 @version r0.1 @date 2021/01/13 -------------------------.

qq_34118600的博客 3447

微服务:实现服务发现与服务注册

一.服务发现的方式: 1.客户端发现:Eureka、ZooKeeper(存在缺陷)原因:http://blog.youkuaiyun.com/whereismatrix/article/details/53305045 2.服务端发现:consul+nginx 描述:Eureka是Netflix开发的服务发现框架,本身是一个基于REST的服务,主要...

kwame211的博客 2609

逆向实战:拼多多anti-content加密参数的全链路解析与动态对抗策略

本文深入解析了拼多多anti-content加密参数的全链路生成逻辑与动态对抗策略。通过逆向工程实战,详细拆解了包含设备指纹、用户行为采集和动态密钥生成在内的12个关键环节,并提供了可持续对抗的精准模拟方案和自动化更新机制,帮助开发者有效应对拼多多的反爬体系。

weixin_30687051的博客 161

微服务之服务注册和服务发现

 目录 一、微服务设计二、技术选型三、关键实现四、服务注册五、服务发现   目标 一、微服务包括那些内容   二、为什么会出现服务注册和服务发现     三、了解关键技术实现原理     一、微服务设计 1、包括那些内容 (1)、服务注册、发现、负载均衡和健康检查,假定采用进程内LB方案,那么服务自注册一般统一做在服务器端框架中,健康检查逻辑由具体业务服务定制,框架...

feng55551111的博客 2569

如何设置Xilinx以太网IP核的Pause帧处理

这里要注意,为什么是或,第一个条件是该功能使能取反,也就是说,如果你设置使能不打开的话,这个条件是直接满足的;先来看图中第二句,判断该以太网数据包为Global Pause帧(gpp),首先要满足Step1,然后是由上图中第二句代码(assign global_pause_packet)后面的所有项“与”(&&)起来的,就是说要满足后面所有的项,分别是DA符合,SA符合,Etype符合,Opcode符合,并且使能了该功能(即设置ctl_rx_enable_gpp=1)。如果没通过,则进入Step3。

qq_43416206的博客 620

【零基础学习CAPL】——CAN报文的发送(CRC——ChecSum值)

本章主要介绍带有CRC值的报文发送一般报文可使用CANoe的IG模块直接发送,但带有CRC值的报文就只能编写脚本进行发送,以下详细讲解在配合Panel面板情况下,如何进行带有循环冗余码的报文进行报文的发送。

LOVE135149的博客 1915

微服务注册与发现

微服务注册与发现 服务发现简介 通过前文的讲解,我们知道硬编码提供者地址的方式有不少问题。想要解决这些问题,服务消费者需要一个强大的服务发现机制,服务消费者使用这种机制获取服务提供者的网络信息。不仅如此,即使服务提供者的信息发生变化,服务消费者也无需修改配置文件。 ...

shi_zi_183的博客 2211

做了一个3DTiles编辑器,支持3DTiles的预览和裁剪导出

3DTiles编辑器的功能极其简洁,核心就是3DTiles格式模型的预览和裁剪。为了演示方便,本文所使用的源数据为mipmap3d[1]网站所提供的无人机拍摄影像,并进行3DTiles构建。打开软件,输入的绝对路径之后,点击添加模型,就可以将模型加载进预览区。之后,点击裁剪模式按钮,默认会出现立方体裁剪框,可通过拖动句柄的方式去调整裁剪框的范围。同时,除了默认的盒体裁切模式外,还支持多边形裁切,点击多边形裁切后,可切换到顶视图,选择重绘多边形,可进行打点绘制。

兴趣使然的创作者 604

微服务架构 SpringCloud(二)Eureka(服务注册和服务发现基础篇)

一:Eureka简介 Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一。用于云端服务发现,一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务系统来说非常重要。有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只需要使用服务的标识符,就可以访问到服务。他的功能类似于dubbo的注册中心(register...

weixin_34072637的博客 113

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

图灵学院:【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)

一:Eureka简介   Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一。用于云端服务发现,一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。服务注册与发现对于微服务系统来说非常重要。有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只需要使用服务的标识符,就可以访问到服务。他的功能类似于dubbo的注册中心(register...

JAVA这点事 898

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)(二)

【微服务架构】SpringCloud之Eureka(服务注册和服务发现基础篇)一:Eureka简介二:Eureka服务发现和注册(创建注册中心)三:Eureka注册中心加入权限四:注册一个服务 原文链接 上篇文章讲解了SpringCloud组件和概念介绍,接下来讲解一下SpringCloud组件相关组件使用、原理和每个组件的作用的,它主要提供的模块包括:服务发现(Eureka),断路器(Hystr...

CodeMeiYang的博客 211

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

微服务架构 SpringCloud(二)Eureka(服务注册和服务发现基础篇) ...

一:Eureka简介 Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一。用于云端服务发现,一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 服务注册与发现对于微服务系统来说非常重要。有了服务发现与注册,你就不需要整天改服务调用的配置文件了,你只需要使用服务的标识符,就可以访问到服务。他的功...

weixin_34247155的博客 89
上一篇: Redis 中的原子操作(3)-使用Redis实现分布式锁
下一篇: 在 Pisa-Proxy 中,如何利用 Rust 实现 MySQL 代理
程序员职业指南
博客等级 码龄4年 2138粉丝 960原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值