geektutu / blog

极客兔兔的博客,Coding Coding 创建有趣的开源项目。
https://geektutu.com
Apache License 2.0
166 stars 21 forks source link

动手写RPC框架 - GeeRPC第七天 服务发现与注册中心(registry) | 极客兔兔 #98

Open geektutu opened 3 years ago

geektutu commented 3 years ago

https://geektutu.com/post/geerpc-day7.html

7天用 Go语言/golang 从零实现 RPC 框架 GeeRPC 教程(7 days implement golang remote procedure call framework from scratch tutorial),动手写 RPC 框架,参照 golang 标准库 net/rpc 的实现,实现了服务端(server)、支持异步和并发的客户端(client)、消息编码与解码(message encoding and decoding)、服务注册(service register)、支持 TCP/Unix/HTTP 等多种传输协议。第七天实现了一个简单的注册中心(registry),具备超时移除、接收心跳(heartbeat)等能力,并且实现了一个简单的服务发现(server discovery)模块。

Shadow-linux commented 3 years ago

大佬加油,已肝完,期待下一个7days

geektutu commented 3 years ago

@Shadow-linux 正在努力~ 🤡

Pissssofshit commented 3 years ago

@geektutu @Shadow-linux 正在努力~ 🤡

兔兔加油!!!超棒

ppd0705 commented 3 years ago

已跟完,多谢兔兔的分享

yanqic commented 3 years ago

谢谢大佬的教程。想请教一下,这个rpc 框架如果实际应用,如何在不同机器,部署多节点呢,现在看是起一个进程,多个协程实现多节点。

MyShip-jpg commented 3 years ago

照着撸完了,迷迷糊糊懵懵懂懂,感谢大佬!大佬牛逼!

geektutu commented 3 years ago

谢谢大佬的教程。想请教一下,这个rpc 框架如果实际应用,如何在不同机器,部署多节点呢,现在看是起一个进程,多个协程实现多节点。

@yanqi321 多台机器的话,main 函数中在启动 RPC 服务,配置好注册中心,在不同的机器上运行就好了。然后,再实现一个程序去调用服务。

geektutu commented 3 years ago

照着撸完了,迷迷糊糊懵懵懂懂,感谢大佬!大佬牛逼!

@MyShip-jpg 希望对你有帮助,以后使用其他开源的 RPC 框架时,就比较有底气了。

fy403 commented 3 years ago

这里是不是要考虑删除定时器,不然可能会引起内存泄漏

    go func() {
        t := time.NewTicker(duration)
        defer t.Stop()
        for err == nil {
            <-t.C
            err = sendHeartbeat(registry, addr)
        }
    }()
imtzer commented 2 years ago

大部分照着撸完,最近刚开始学go,非常感谢大佬提供的项目练手,虽然还停留在比较迷糊的状态,但是总体上是有了一个了解,学到了挺多东西,日后会重新对项目整体进行一个思考并且对一些模块进行封装,改进并且建立自己的rpc体系框架,再次感谢大佬!

DrGreenpepper commented 2 years ago

大佬你好,day6和day7代码在跑的时候有几率出现以下错误,不知道为什么,希望大佬能指点一下问题可能出在哪里,谢谢大佬。

rpc server: read header error: gob: unknown type id or corrupted data call Foo.Sum error: read tcp [::1]:57188->[::1]:40693: read: connection reset by peer

cyj19 commented 2 years ago

感谢兔大佬的分享!!!

cuglaiyp commented 2 years ago

@DrGreenpepper 大佬你好,day6和day7代码在跑的时候有几率出现以下错误,不知道为什么,希望大佬能指点一下问题可能出在哪里,谢谢大佬。

rpc server: read header error: gob: unknown type id or corrupted data call Foo.Sum error: read tcp [::1]:57188->[::1]:40693: read: connection reset by peer

我感觉是 tcp 粘包的问题。我在 NewClient 阶段,让服务器给客户端一个响应,问题好像就解决了

cuglaiyp commented 2 years ago

有个问题,discovery 里面的服务列表还没过期,但是已经有服务宕掉了,请求就只能失败了?

okumiko commented 2 years ago

有个问题,注册中心里的服务列表存储的就是所有注册的地址,似乎没有服务和地址列表的映射关系呀。

klzhu-zz commented 2 years ago

@DrGreenpepper 大佬你好,day6和day7代码在跑的时候有几率出现以下错误,不知道为什么,希望大佬能指点一下问题可能出在哪里,谢谢大佬。

rpc server: read header error: gob: unknown type id or corrupted data call Foo.Sum error: read tcp [::1]:57188->[::1]:40693: read: connection reset by peer

我感觉是 tcp 粘包的问题。我在 NewClient 阶段,让服务器给客户端一个响应,问题好像就解决了

请问具体是怎么解决的啊,能详细解释下嘛,我也遇到了这个问题

liujing-siyang commented 1 year ago

@okumiko 有个问题,注册中心里的服务列表存储的就是所有注册的地址,似乎没有服务和地址列表的映射关系呀。

不影响吧,注册中心只需要告诉客户端哪些服务可用就行了,然后对可用的服务发起访问;假设注册中心的map维护的是服务地址和服务实例,哪些地方需要去拿服务实例呢?我们要的只是客户端实例!

wandering-readily commented 8 months ago

谢谢兔兔,明白了很多概念~ 我在tcp 粘包的问题是这么做的: json.NewDecoder(conn).Decode(&opt) if err := json.NewEncoder(conn).Encode(opt); err != nil,json底层read可能过多取字节缓存,导致readRequestHeader()中cc.ReadHeader(&h)的"encoding/gob" decode(&h)时字节流可能缺少部分开头字节,我这里修改了往conn传入struct Option的方法,直接从conn输入取出option长度声明(option长度int32字段指定),这样完全取出option时不过多取出字节;

不过在DialHTTP中好像有个隐患,这我不太清楚,

  1. NewHTTPClient() --> (1. HTTP CONNECT方法交流,而后传输option字节流)
  2. Server的ServeHTTP(w http.ResponseWriter, req *http.Request)方法先确认CONNECT方法,而后Hijack()剥离conn,再调用ServeConn()方法,第一个步骤仍会是从conn中接受option的字节流
    我的假设是net/http实现的交流会将tcp conn的所有字节取出,那么我下一次在已经建立的tcp conn连接上将会接收所有option字节(我不知道这个假设对不对,有没有后来者能解惑,谢谢了QAQ)
    做了几次测试,发现没有乱码和错误
SCUTking commented 7 months ago

@liujing-siyang

@okumiko 有个问题,注册中心里的服务列表存储的就是所有注册的地址,似乎没有服务和地址列表的映射关系呀。

不影响吧,注册中心只需要告诉客户端哪些服务可用就行了,然后对可用的服务发起访问;假设注册中心的map维护的是服务地址和服务实例,哪些地方需要去拿服务实例呢?我们要的只是客户端实例!

只有一种服务的时候,这种方法可行;但是如果有多种服务的时候,这种方法就不可行了,就应该搞一个服务名和服务实例的映射关系了。

SCUTking commented 7 months ago

感觉registry与discover这两个东西可以合在一起,这样就不用discover再去发给请求获取registry的数据了。

KamePan commented 5 months ago

@SCUTking 感觉registry与discover这两个东西可以合在一起,这样就不用discover再去发给请求获取registry的数据了。

但是 Discovery 用于 Client 客户端负载均衡获取 Server 地址,Registry 则是需要启动 HTTP 服务的注册中心,仅用来返回可用 Server 地址,这两个在这里的 RPC 情况下是不能耦合在一起的

galakelex commented 3 months ago

太帅了

EndlessParadox1 commented 1 month ago

后面两个写的人明显就少了