V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
lesismal
V2EX  ›  Go 编程语言

RPC 的变革 —— ARPC 项目自荐

  •  
  •   lesismal ·
    lesismal · 2020-12-12 00:31:55 +08:00 · 7035 次点击
    这是一个创建于 1477 天前的主题,其中的信息可能已经有所发展或是发生改变。

    已加入 awesome-go

    RPC 的变革 —— ARPC 项目自荐

    项目地址: https://github.com/lesismal/arpc

    一、ARPC 示例

    echo server

    package main
    
    import (
    	"log"
    
    	"github.com/lesismal/arpc"
    )
    
    func onEcho(ctx *arpc.Context) {
    	str := ""
    	err := ctx.Bind(&str)
    	if err != nil {
    		log.Printf("/echo error: %v", err)
    		return
    	}
    	ctx.Write(str)
    	log.Printf("/echo: %v", str)
    }
    
    func main() {
    	svr := arpc.NewServer()
    
    	// register handler
    	svr.Handler.Handle("/echo", onEcho)
    
    	svr.Run(":8888")
    }
    

    echo client

    package main
    
    import (
    	"context"
    	"log"
    	"net"
    	"time"
    
    	"github.com/lesismal/arpc"
    )
    
    func dialer() (net.Conn, error) {
    	return net.DialTimeout("tcp", "localhost:8888", time.Second*3)
    }
    
    func main() {
    	client, err := arpc.NewClient(dialer)
    	if err != nil {
    		log.Fatalf("NewClient failed: %v", err)
    	}
    	defer client.Stop()
    
    	request := "hello"
    	response := ""
    	// err = client.Call("/echo", &request, &response, time.Second*5)
    	err = client.CallWith(context.Background(), "/echo", &request, &response)
    	if err != nil {
    		log.Fatalf("Call /echo failed: %v", err)
    	} else {
    		log.Printf("Call /echo Response: \"%v\"", response)
    	}
    }
    

    二、传统主流的 RPC 框架的局限 /不爽

    1. 网络交互模式单一,无法支持更丰富的场景

    传统主流 RPC 的网络交互主要是 [客户端到服务端,请求-应答] 的模式,比较单一。按照这个模式以及顾名思义“远程过程调用”,其实 HTTP 也算是 RPC 的一种,只是由于其短连接和 HTTP 协议的文本编码格式等原因导致性能和资源的浪费,所以很少有直接把 HTTP 称为 RPC

    网络通信的本质是数据收发,客户端和服务端都可以随时主动发送消息给对方,比如推送服务IM游戏等;而且一方发送数据给另一方,有时是不需要回复的,比如VOIP 电话,其他不要求强一致的消息广播、推送等。

    这里把需要应答的通信定义为 Call ,不需要应答的通信定义为 Notify,则网络交互按照发起方、是否需要应答,可以分为以下四种基本模式:

    1 )客户端发起请求,服务端应答

    2 )服务端发起请求,客户端应答

    3 )客户端向服务端发出通知 /推送,无需应答

    4 )客户端向服务端发出通知 /推送,无需应答

    如此看来,传统主流 RPC 就像是个大内男公务员,因为它只支持了第一种基本模式,只覆盖了 25%——甚至说它的网络通讯模式有点不完整,都算是一种褒奖,因为 25%的模式支持那是相当不完整。

    而只支持请求-应答的模式也限制了很多业务场景,其他更广泛的业务场景比如推送服务IM游戏,我们还需要自定义各种协议。

    2. Server 端函数调用的写法,函数返回即是调用结束,不够灵活

    传统主流 RPC 服务端的写法通常是一个函数,函数返回后框架层把返回值打包发送给客户端作为应答,不支持在该函数中进行异步响应,尤其是 golang 的 RPC ,有的框架为了代码简单,没有写协程,发送数据时直接写到 Conn,高并发写时竞争比较明显会增加时延;有的框架默认采用读协程收到数据 one-by-one 的方式处理,存在线头阻塞的问题,有的框架采用每个消息新开一个 go 协程处理,高并发时协程数量可能暴增、比较浪费,不支持按单个 Method/Router 定制同步或者异步处理,也不支持在 Method/Router handler 内由业务层自主选择同步处理、新开 go 协程异步或者协程池等异步等方式的处理。

    三、关于 ARPC

    1. 高性能

    想说 ARPC 比其他流行的 golang RPC 性能都好,但是自吹最强好像没有说服力,感谢 rpcx 有做一些主流 RPC 框架的性能对比,老仓库 已经废弃并且那时 ARPC 还没有出生,有兴趣的同学可以到 新仓库 跑下代码进行对比,测试时请注意排除其他程序的干扰。

    目前最为主流的 gRPC 因为官方综合考量使用了 HTTP2 ,详情参考 gRPC 的动机和设计原则,注定了不能很高性能。而很多 RPC 的业务场景,是基于内部服务集群, HTTP2 的加密流程等显得有些性能浪费。而 ARPC 更注重性能和灵活性,通信协议部分交由业务层决定,通常建议使用 TCP 作为基础通信协议,如有需要,业务层也可以使用 TLSWebsocket 或者 KCP 等 。

    2. 网络交互模式全面

    上面在 不爽 的部分提到了传统主流 RPC 的不完整, ARPC 当然要比较全面的支持这四种基本交互模式:

    1 )客户端发起请求,服务端应答

    2 )服务端发起请求,客户端应答

    3 )客户端向服务端发出通知 /推送,无需应答

    4 )客户端向服务端发出通知 /推送,无需应答

    3. 丰富的业务场景支持

    由于网络交互模式相对全面,ARPC 可以用于处理多种常规业务场景而不受类似 HTTP 短链接、单向请求-应答方式的限制。比如:

    1 )推送服务

    2 )游戏服务

    3 )聊天服务

    4 )其他需要长连接、双向、广播等灵活交互方式的业务

    4. 写法简单

    如[示例](#rpc 进化--arpc-项目自荐)所示,Handler 不采用函数返回即调用结束的形式,写法简单、更像 HTTP Handler 。由于也不强制使用编解码器,甚至不必生成结构化消息或者服务如 Protobuf 的 Message 、Service 等,这样也带来一些额外的好处,比如热点的结构化数据,业务层可以在数据更新时序列化一次并缓存起来,有需要时直接发送序列化之后的数据给需求方,避免每次发送给每个连接都需要进行一次序列化的浪费,在高在线量的广播类业务中这点尤为明显。

    其他一些 RPC 框架喜欢注册对象的方式,由框架层通过反射去解析符合 Handler 格式的方法进行隐式注册,由于早年被 C++的各种语言标准、机制等背后动作玩弄得辛苦,golang 项目中希望框架层和业务层都尽量不让用户增加没有必要的心智负担(比如通过对象隐式注册的方式:没有带来性能提升,没有架构设计模块设计的解耦或者其他优化), ARPC 的设计遵循简单、透明的原则,所以像 HTTP 一样进行显式注册,如果有的同行喜欢玩弄语法糖技巧或者被语法糖技巧玩弄,可以自行定制。

    5. 更灵活的同步异步

    支持单个 Method/Router Handler 级别设置同步或者异步处理,也支持 Handler 内由业务层自主控制同步或异步回包、从而针对性定制快 /慢接口的协程数量控制与线头阻塞问题处理。

    6. 最少依赖

    目前如果只使用 ARPC 默认参数,则只使用了 golang 标准库,不需要依赖其他第三方 package 。

    7. 易扩展

    1 )网络协议支持:由用户自主决定,服务端实现 net.Listener 、客户端实现 net.Conn 即可做为 ARPC 的网络载体,arpc/examples/protocols 已经提供了 KCPQUICTLSUnixSocketUTPWebsocket 等示例,欢迎参考。

    2 )非结构化的消息体编解码支持:可以直接用 string 、 *string 、 []byte 、 *[]byte 、error 、 *error 等作为消息体参数。

    3 )结构化的消息体编解码支持:为了最少依赖, ARPC 默认使用了 encoding/json 作为结构化消息体的编解码器,性能不够强,但是业务层可以很方便地设置使用 json-iter 、Protobuf 等作为结构化消息地编解码器。

    4 )消息体编解码中间件支持: ARPC 提供了消息体编解码中间件机制, arpc/extension/middleware/coder 子包实现了 GzipTracer 作为默认示例,有需要的用户可以参考实现自行定制,使用示例在 arpc/examples/middleware/coder

    5 ) Method/Router 的中间件支持: ARPC 提供了类似流行的 golang HTTP 框架的中间件,方便业务层自行扩展, arpc/extension/middleware/router 子包实现了 LoggerRecoverGraceful 作为默认示例,有需要的用户可以参考并实现自行定制,使用示例在 arpc/examples/middleware/router

    6 ) Web JS Client 支持: ARPC 提供了 JS Client 及示例:API 示例聊天示例。有了 JS Client, 不需要类似其他 RPC 框架那样部署 HTTP 转换 RPC 的网关,前端可以直接通过 WebsocketARPC 服务进行交互,而且因为 ARPC 已经包括了消息的编解码、Method/Router Handler,比 melody 等只封装了收发数据的基础 websocket 框架更方便。

    其他扩展不一一列举了,欢迎有兴趣的同学查看代码或者 New issue 。

    8. 更多示例

    arpc/examples提供了较为丰富的示例,如 通知广播优雅退出服务注册与发现连接池kcp/quic/tls/websocket 等协议支持发布订阅JS Web Chat 等,请见 arpc/examples

    9. 其他

    个人精力有限,并且 golang 是世界上第二好的编程语言,所以暂时不考虑对其他语言的支持,欢迎 pr 、issue 。

    66 条回复    2020-12-14 14:38:03 +08:00
    wellsc
        1
    wellsc  
       2020-12-12 01:39:30 +08:00
    安利一下 rSocket
    no1xsyzy
        2
    no1xsyzy  
       2020-12-12 01:47:18 +08:00
    主要是 RPC 的含义就是 Remote Procedure Call
    无需应答是否要求保证送达?要保证送达需要回执,这个从 TCP 层面做还是从你应用层做多大额外开销的,把应答丢弃就行了
    不要求送达,那 TCP 相比 UDP 的开销比你应用层更明显,游戏恐怕不太可能用你这个方法。

    另外,ARPC 让人感觉这个是 ARP 的 Client 。
    so1n
        3
    so1n  
       2020-12-12 02:00:31 +08:00   ❤️ 2
    如果不等函数调用结束就返回 那不就是消息队列吗...
    lesismal
        4
    lesismal  
    OP
       2020-12-12 06:55:02 +08:00
    @wellsc 看了下 rsocket-go,风格比较 java,有点臃肿了,使用起来不够方便简洁

    ```golang
    package main

    import (
    "context"
    "log"

    "github.com/rsocket/rsocket-go"
    "github.com/rsocket/rsocket-go/payload"
    )

    func main() {
    // Connect to server
    cli, err := rsocket.Connect().
    SetupPayload(payload.NewString("Hello", "World")).
    Transport(rsocket.TCPClient().SetHostAndPort("127.0.0.1", 7878).Build()).
    Start(context.Background())
    if err != nil {
    panic(err)
    }
    defer cli.Close()
    // Send request
    result, err := cli.RequestResponse(payload.NewString("你好", "世界")).Block(context.Background())
    if err != nil {
    panic(err)
    }
    log.Println("response:", result)
    }
    ```
    lesismal
        5
    lesismal  
    OP
       2020-12-12 06:58:39 +08:00
    @so1n “不等函数调用结束就返回” —— 这个可能是兄弟误解了,ARPC 说的不是不等结束就返回,而是可以收到请求后自由选择什么时候回复,可以在处理函数中回包也可以在处理函数之外异步回包之类的,不像其他 RPC 那样函数调用结束了就返回了。
    lesismal
        6
    lesismal  
    OP
       2020-12-12 07:25:47 +08:00
    @no1xsyzy "主要是 RPC 的含义就是 Remote Procedure Call"
    —— 这个定义没错,但正式因为这个定义,限制了很多场景的网络交互方式,比如 web 技术栈,在不同的年代以不同的方式去支持连接复用(比如 http 1.x keepalive, 2.0 以后的长连接)、非 RPC 方式的协议(比如 socketio 、websocket ),ARPC 就是为了简化这种交互模式来反推 RPC 场景,希望能够简化依赖、一个库能支持多数业务、而不是我用了 http 满足不了需求还得使用 grpc 然后还需要再使用个自定义长连接协议或者 websocket 等,这就是为什么我说传统主流 RPC 不爽的原因

    "无需应答是否要求保证送达?要保证送达需要回执,这个从 TCP 层面做还是从你应用层做多大额外开销的,把应答丢弃就行了"
    "不要求送达,那 TCP 相比 UDP 的开销比你应用层更明显,游戏恐怕不太可能用你这个方法。"
    —— 先说"送达"吧,这个是误解,ARPC 没说不要求送达,transport 层使用长连接或者可靠连接作为载体的应用层业务,transport 层本身来负责正常情况时连接能够保持情况下的送达,但是任何 transport 层也都无法保证自己这一层的 100%送达,比如链路中断、设备掉电、切换网络。应用层更没法保证送达,基于正常情况下的业务逻辑交互作业、加上完善的状态管理、错误处理之类的就可以了
    —— 再说 "不要求送达",这个是兄弟的误解,"不需要应答" != "不要求送达",所以基于"不要求送达"推断"游戏能不太可能用 ARPC"也是不必要的。而且,ARPC 本身就是基于游戏和其他一些有状态服务的框架、为了反推那些被 RPC 限制了的场景技术栈和资源优化而实现的

    另外,ARPC 让人感觉这个是 ARP 的 Client 。
    —— 名字这个没办法,众口难调,每个人都会有不同的看法。我定义成 ARPC 大概有两层意思:一是希望像 "老 A"一样,努力做成同类基础设施中最好的;二是 "Async RPC" 的缩写,因为可以自由选择同步还是异步回包、回包方式不受传统 RPC 那种处理函数结束就意味着调用结束的限制
    lesismal
        7
    lesismal  
    OP
       2020-12-12 07:33:08 +08:00
    @wellsc 另外,还没有深入研究 rsocket,不知道是否方便支持服务端广播之类的
    mepwang
        8
    mepwang  
       2020-12-12 07:53:40 +08:00 via iPhone
    传统的分布式调用有很多坑要填的,比如 90 年代的 CORBA 技术就试图搭出一套完全的分布式对象体系。比如你的框架里面,假如服务器端单个请求处理比较耗时的话,多个客户端的并发请求是否会被阻塞?当个客户端的同一个远程方法的多个并发调用,是否会被阻塞? RPC 框架一定和应用场景紧密相连的,能满足几个实际应用场景需求的框架,就是好框架
    lesismal
        9
    lesismal  
    OP
       2020-12-12 09:03:44 +08:00
    @mepwang

    5. 更灵活的同步异步
    支持单个 Method/Router Handler 级别设置同步或者异步处理,也支持 Handler 内由业务层自主控制同步或异步回包、从而针对性定制快 /慢接口的协程数量控制与线头阻塞问题处理。

    —— 主贴介绍的这部分有讲,每个 handler 都支持灵活的同步异步策略,用户可以自己设定请求来了是 one by one 的方式处理还是每个请求开一个新协程,甚至单个请求的处理函数里业务层自己根据实际情况选择同步还是新开协程或者协程池处理。至少 ARPC 框架层不存在这些坑点。除了 erlang golang 的其他语言可选的方式不多,要么像 node 这种也是语言层面提供异步以及各种语法糖,要么像 c++这种自己定制线程 /任务池、连接池各种基础轮子

    另外 “多个客户端的并发请求是否会被阻塞” 不只是服务端框架的问题。请求处理对应的业务逻辑比如数据层、其他调用链等耗时操作也可能导致响应慢,还有比如业务层没有做限流导致的请求量爆炸,甚至部署的网络链路不稳定导致 transport 层拥塞,这些非框架原因导致的超载是框架自己不可控的、需要留给业务层处理。当然,有的微服务框架做得大而全,框架本身包含了限流熔断负载均衡等全家桶组件,ARPC 的定位则是网络和协议交互这一层的小而美,毕竟大而全太耗精力而且并不是所有项目都需要大而全,绝大多数团队的业务场景直接拿别人大而全的框架反倒成本太高、不太合适
    lesismal
        10
    lesismal  
    OP
       2020-12-12 09:30:59 +08:00
    @mepwang 不同领域的分布式系统差别太大了,比如 web 、游戏、分布式数据库或者计算引擎。每种领域又有很多具体的子场景,业务耦合的方式不同,架构设计的方式也都不同,甚至天差地别。通常在 web 领域流行的分布式理论,拿到 MMORPG 里就完全不能用

    近十年的互联网发展速度太快了,最基本的一个需求,比如推送,很多应用可能都需要,但是传统的 RPC,server 端是不支持广播的,所以要使用 http static,http api,websocket,服务集群内部还要 RPC,而除了 http static,其他的场景 ARPC 都可以支撑,能很大程度简化技术栈,并且性能更高
    Catstyle
        11
    Catstyle  
       2020-12-12 09:43:17 +08:00
    啊,13 年开始就这么用了
    XiLingHost
        12
    XiLingHost  
       2020-12-12 10:20:53 +08:00
    @lesismal 名字可以改成 aRPC,就类似于 gRPC
    kevinwan
        13
    kevinwan  
       2020-12-12 10:34:54 +08:00
    我们有 zRPC,基于 gRPC 的,我觉得 gRPC 还是比较通用解决方案,当然也鼓励创新
    https://github.com/tal-tech/go-zero
    fox0001
        14
    fox0001  
       2020-12-12 10:41:15 +08:00
    感觉是造了双筷子,然后改个名叫“叉子”……纯直觉,没别的意思
    lesismal
        15
    lesismal  
    OP
       2020-12-12 10:44:56 +08:00
    @XiLingHost 这个都行,代码 package 都是 arpc,个人开发者没那么大的社区影响力,ARPC/aRPC/arpc 都随意 :joy:
    lesismal
        16
    lesismal  
    OP
       2020-12-12 10:50:02 +08:00
    @kevinwan 嗯嗯 star 支持了。前阵子还提了个服务注册发现确保强一致的 [issues/227]( https://github.com/tal-tech/go-zero/issues/227) ,其实加上强一致保障不复杂,etcd 的 "go.etcd.io/etcd/client/v3/concurrency" 子包自带了分布式锁的实现:

    ```golang
    client, err := clientv3.New(clientv3.Config{
    Endpoints: endpoints,
    DialTimeout: 5 * time.Second,
    })
    if err != nil {
    log.Error("NewRegister [%v, %v] clientv3.New failed: %v", key, value, err)
    return nil, err
    }

    session, err := concurrency.NewSession(client)
    if err != nil {
    log.Error("NewRegister [%v, %v] concurrency.NewSession failed: %v", key, value, err)
    return nil, err
    }

    mux := concurrency.NewMutex(session, RegisterMutexPrefix)
    err = mux.Lock(context.TODO())
    if err != nil {
    log.Error("NewRegister [%v, %v] Lock failed: %v", key, value, err)
    return nil, err
    }
    defer mux.Unlock(context.TODO())
    ```

    然后先 Get 判断再 Put 就行了
    lesismal
        17
    lesismal  
    OP
       2020-12-12 10:57:57 +08:00
    @fox0001 这么说也对,因为其实我最早不是做 web 服务为主的业务的,最开始做就都是有状态的长连接服务,后面再做 web 类的,觉得 http 相关的 api 、rpc 技术栈虽然工程实践上积累了很多,但是基础设施还是太低效了。这几年又是挖矿,又是全球升温环境恶化,而且以后随着大数据、AI 、5G+这些的更加普及,计算量会越来越大,对应的能源消耗也会越来越大。以前的 http 因其文本协议的便利极大促进了互联网的发展,但也正是由于它短链接、文本格式等低效浪费问题,会造成日趋爆发的数据和计算量的巨大浪费,所以才会有 http 2.0 3.0 quic mqtt 各种升级方案。
    lesismal
        18
    lesismal  
    OP
       2020-12-12 10:59:29 +08:00
    @lesismal @kevinwan @fox0001

    网络交互是通用的基础设施,4 种基础网络交互模式也早已有之,所以我这个也不算创新 😂😂,只是希望把通用的基础设施能推向 web 等其他曾经受限的领域,让做业务更容易、并且性能更高、更节能。
    lesismal
        19
    lesismal  
    OP
       2020-12-12 11:02:23 +08:00
    @kevinwan 还是举推送的例子,gRPC 这些好像不太便利。很多接口类业务,其实如果换成有状态服务,性能和软硬件消耗都能节省不少,但是难度当然也略高些,如果是用户之间还存在复杂交互耦合的(比如游戏),则业务复杂度和编码难度更高
    lesismal
        20
    lesismal  
    OP
       2020-12-12 11:03:41 +08:00
    欢迎各位体验、尝试 😊
    kevinwan
        21
    kevinwan  
       2020-12-12 11:04:40 +08:00 via iPhone
    @lesismal 感谢,晚点我看看,也 star 一下 arpc🤝
    DoctorCat
        22
    DoctorCat  
       2020-12-12 11:06:20 +08:00
    @kevinwan 看着不错啊,有支持 Tracing 么?
    lesismal
        23
    lesismal  
    OP
       2020-12-12 11:07:43 +08:00
    @kevinwan 感谢支持!多多交流跟各位大佬学习!
    lesismal
        24
    lesismal  
    OP
       2020-12-12 11:12:40 +08:00
    @DoctorCat 没有默认支持其他 tracing,有中间件机制可以进行扩展。

    这里有个简单 tracing 的扩展示例:
    https://github.com/lesismal/arpc/tree/master/extension/middleware/coder/tracer

    这里是简单 tracing 的使用示例:
    https://github.com/lesismal/arpc/tree/master/examples/middleware/coder/trace

    欢迎各路道友 pr 或者自己项目扩展实现、交流 😁
    kevinwan
        25
    kevinwan  
       2020-12-12 11:23:12 +08:00
    @DoctorCat 有的,不过正在适配到 opentracing,我们需要通过 kibana 查询到调用关系,所以 span id 是类似:0, 0.1, 0.1.1, 0.2 这种,最近会适配到 opentracing.
    teawithlife
        26
    teawithlife  
       2020-12-12 11:26:14 +08:00
    看得出楼主花了不少精力,点个 star 支持一下
    kevinwan
        27
    kevinwan  
       2020-12-12 11:26:38 +08:00
    @lesismal 已 star 。go-zero 的重点是把我们千万级日活的工程经验和最佳实践通过开源的方式带给大家,通过工具帮助大家更关注业务。不太会针对某个特定领域做深度定制,这可能是咱们两个框架的区别,不知道我是不是说错了,请指正哈
    DoctorCat
        28
    DoctorCat  
       2020-12-12 11:27:49 +08:00
    @kevinwan 等你们适配好的,我们这边试试,现在人手少没法参与到开源 PR
    lesismal
        29
    lesismal  
    OP
       2020-12-12 11:43:59 +08:00
    @DoctorCat 你们使用的哪些 tracing ?我空了学习研究下看看自己能不能加上
    lesismal
        30
    lesismal  
    OP
       2020-12-12 11:44:33 +08:00
    @teawithlife 感谢支持!😊😊😊
    lesismal
        31
    lesismal  
    OP
       2020-12-12 11:50:16 +08:00
    @kevinwan 感谢大佬支持!😊😊😊

    go-zero 是比较完整的一套微服务方案,是面向业务的一套整体架构
    arpc 轻量,目前只做网络交互的部分,相当于对标 gRPC 、rpcx 、标准库 rpc 之类的,是面向架构的一个基础设施

    所以咱们没什么可比性,要说关系的话,就是 go-zero 可以考虑使用 arpc 😊😊😊
    DoctorCat
        32
    DoctorCat  
       2020-12-12 11:50:26 +08:00
    @lesismal 亚马逊 xray 或者 sentry 这类的,要么就是开源的一些支持 OpenTracing 的平台
    tkl
        33
    tkl  
       2020-12-12 12:30:19 +08:00
    推广挺用心 支持
    kevinwan
        34
    kevinwan  
       2020-12-12 12:30:50 +08:00
    @lesismal go-zero 的 goctl 是支持自定义模板的,完全可以自定义为基于 arpc
    lesismal
        35
    lesismal  
    OP
       2020-12-12 12:30:51 +08:00
    @DoctorCat 好,闲了研究下
    lesismal
        36
    lesismal  
    OP
       2020-12-12 12:31:46 +08:00
    @tkl 感谢支持!😊😊😊
    lesismal
        37
    lesismal  
    OP
       2020-12-12 12:32:46 +08:00
    @kevinwan 赞,这个够灵活,哈哈哈 👍👍👍
    kevinwan
        38
    kevinwan  
       2020-12-12 12:51:49 +08:00 via iPhone
    @lesismal 你甚至可以通过插件生成 php 代码😂
    kevinwan
        39
    kevinwan  
       2020-12-12 12:53:31 +08:00 via iPhone
    @DoctorCat 全链路追踪已经有的,只是要用 jaeger 展示的话需要适配一下
    lesismal
        40
    lesismal  
    OP
       2020-12-12 13:24:15 +08:00
    @kevinwan 我选择把世界上最好的语言供奉起来,干业务这种还是交给 go 好些 😂
    xeaglex
        41
    xeaglex  
       2020-12-12 13:47:20 +08:00
    赞,和我之前用 gRPC 时的感想不谋而合。区别是你真的做出来了
    no1xsyzy
        42
    no1xsyzy  
       2020-12-12 13:51:31 +08:00
    @lesismal
    行吧…… 看上去想要弄个任何交互底层上的通用抽象层。

    “无需应答是否要求保证送达?”是个分支,分别分析了 “要求送达” 和 “不要求送达” 的情况

    送达的话,依赖送达回执和避免重复送达就成,我是指(类似 MQTT 的) QoS 。
    传输层送达不会反应在应用上,会导致重传浪费时间。

    不要求送达是指前提就是不要求送达(游戏通常是不要求送达的),那么解决方案一般不会是 TCP

    (其实 ARP 好像并不分 C/S )
    IamYourDad
        43
    IamYourDad  
       2020-12-12 14:31:05 +08:00
    楼主, 我没看懂, 样例里面 server->client 是不是多次一举啊, 你想调用 client.dosomthing, 直接在 response 返回不就行了吗, client 读 response 自己 dosomthing, 有什么应用场景, 能不能举例呀
    lesismal
        44
    lesismal  
    OP
       2020-12-12 14:43:28 +08:00
    @xeaglex 哈哈哈,欢迎品尝 ^_^
    lesismal
        45
    lesismal  
    OP
       2020-12-12 15:06:20 +08:00
    @no1xsyzy 嗯嗯,文字交流、一些场景没有既定的“黑话”,大家可能会理解出一些歧义 😅

    其实需要确认对方执行了的场景,使用 Call 的方式等应答结果就行了.MQTT 既然是基于 tcp,业务层再去设计重传相关的我始终感觉有点别扭😅
    我个人对 tcp 的设计也不太满意,有了 BBR 之后传输效率更合理些了,但是 三次握手四次断开 依旧很浪费,2 次握手就足够了,毕竟后面每次都会带 ack ;断开也是 2 次就够了——两边各断开一次、一次断双工,因为工作十几年了,我自己业务还从没遇到过需要半双工分别断开的场景,或者说没遇到过半双工分别断开优于双工同时断开的场景,并且,由于仍然可能存在掉电、线路故障等情况导致任何数据的无法送达,所以关闭一半反倒是作茧自缚 😅。
    所以,超脱到 4 层之上,再看送达相关的问题可能会简单些:非事务性的业务,送不送大无所谓;事务 /弱事务性的,业务层的自行保障措施省略不掉。所以对于网络交互层的关注,放在保障线路、设备稳定性等工程属性上就好了

    游戏、VOIP 电话之类的丢帧就丢帧吧,后面的状态同步过来正确就好,这种场景我也会选择使用 udp,这些特殊场景还是得自家定制才对性能最友好,否则就 pb 咱都觉得性能不够
    lesismal
        46
    lesismal  
    OP
       2020-12-12 15:16:52 +08:00
    @IamYourDad 兄弟,我也没太看懂,能详细描述下不?比如

    “样例里面 server->client 是不是多次一举啊“,这里的多此一举是不是指 ctx.Write()?比如,return xxx 然后框架层自己 Write 给 client 就行了、不需要让用户自己去 Write ?如果是这个意思,请看主贴的这个部分: "2. Server 端函数调用的写法,函数返回即是调用结束,不够灵活" 。而且,这里是可以异步回包的,方便业务层灵活定制业务模块的任务池、流控等,比如:

    func onEcho(ctx *arpc.Context) {
    str := ""
    err := ctx.Bind(&str)
    if err != nil {
    log.Printf("/echo error: %v", err)
    return
    }

    // 这里也可能不直接用 go 、而是使用其他业务模块的协程池异步回包
    go func() {
    // do something.
    ctx.Write(str)
    }()
    }

    另外,兄弟,建议改个 ID,你这个 ID 别人读出来或者看文字心里默读的时候其实是你自己吃亏啊。。。😅😂
    iyangyuan
        47
    iyangyuan  
       2020-12-12 15:21:14 +08:00
    Spring Cloud 使用 http 协议怎么说
    lesismal
        48
    lesismal  
    OP
       2020-12-12 15:26:48 +08:00
    @iyangyuan 我不用 java 😂,随便搜了下 Spring Cloud HTTP2 相关,https://www.jianshu.com/p/ed3f8f983764,好像 Spring Cloud 还是有很大提升空间。
    并且,"能罩得住" 和 "能罩得住得更好" 也不是同一件事 😁
    lesismal
        49
    lesismal  
    OP
       2020-12-12 15:28:30 +08:00
    @iyangyuan 上一条 url 跟文字连一起了无法跳转,而且我还没有编辑权限,重新贴下 url:

    https://www.jianshu.com/p/ed3f8f983764
    kevinwan
        50
    kevinwan  
       2020-12-12 15:43:01 +08:00 via iPhone
    @lesismal 我只是举个例子,go-zero 可以生成任意语言的服务端和客户端代码,插件即可
    lesismal
        51
    lesismal  
    OP
       2020-12-12 15:44:37 +08:00
    @kevinwan 嗯嗯了解😆😁
    robot9
        52
    robot9  
       2020-12-12 16:20:57 +08:00
    感觉 rpc 最重要的是能方便准确的定义 interface 和 data structure
    so1n
        53
    so1n  
       2020-12-12 20:37:37 +08:00 via Android
    @lesismal 是指有序的 request→response→request→response 变为 request,request 然后等处理完了再 response,response 吗?忘记叫啥了,io 多路复用?
    lesismal
        54
    lesismal  
    OP
       2020-12-12 21:03:57 +08:00
    @so1n
    单个连接上的消息是顺序读取的,你说的这种是 one-by-one 的方式进行处理,处理完一个再处理下一个,http 是这样子的,一个处理完之前下一个要排队等待,如果一个处理慢了会导致其他消息也被阻塞、这个问题通常叫线头阻塞
    io 多路复用是指 select 、poll 、epoll 、iocp 、kqueue 等,通过事件机制、异步 io 对多个文件描述符(网络连接也是文件描述符的一种)进行高效的异步 io 操作
    兄弟概念有点迷茫,可以多看些好书比如 CSAPP 、APUE 、UNP 之类的,啃下来消化消化应该会有很大帮助😁😁
    so1n
        55
    so1n  
       2020-12-12 21:07:21 +08:00 via Android
    @lesismal 我是突然忘记了那种该叫啥,所以你的异步是不是指这个方式?
    lesismal
        56
    lesismal  
    OP
       2020-12-12 21:09:19 +08:00
    @robot9 这两点对于非 rpc 的领域好像也同样重要,所以,反倒不是 rpc 的重点了,而是通用场景的重点 😅😅
    lesismal
        57
    lesismal  
    OP
       2020-12-12 21:18:13 +08:00
    @so1n 兄弟,先读点好书,或者搜知识点,阻塞、非阻塞、同步、异步,还有其他的很多基础知识补上再交流,否则问出来的问题容易把我问死,不是不想回答,但基础知识的科普,我没那么多时间啊,而且零碎的知识不如你自己系统性学习的效果好 😂😂
    或者可以直接来读源码,标准库的源码很赞、很值得学习,或者比如你想问的 ARPC 的问题,ARPC 源码也相对简单,你按照 server 的启动流程看进去,很快就能找到答案了
    so1n
        58
    so1n  
       2020-12-12 21:20:56 +08:00 via Android
    @lesismal 那算了,我时间也不多……我只是觉得你那个说不清,很难理解异步返回是代表什么
    lesismal
        59
    lesismal  
    OP
       2020-12-12 22:02:32 +08:00
    @so1n

    一次完整的调用过程指 client 端 Call 调用的返回,其中大致包括了几个主要流程:
    1. client 发送请求数据
    2. server 接收到请求数据
    3. server 处理请求
    4. server 发送响应数据
    5. client 收到请求数据,调用结束

    因为传统 RPC server 端的流程,以 go 的 "net/rpc" 为例,对于每个连接的流程大概是
    go func() {
    for {
    2. server 接收到请求数据
    go func() {
    3. server 处理请求 // 这里是回调业务层注册进来的 handler
    4. server 发送响应数据 // 业务层处理完之后,框架层把 3 中的 handler 返回值打包成响应数据,发送给 client
    }()
    }
    }()

    3 中 handler 是默认 go 一个新的协程进行处理,这种在连接数非常多、并发请求量大的时候协程数量多、各项资源消耗比较浪费
    但是业务层处理 3 的流程,又不能自主选择是否每次 go 一个新的协程


    我在主帖中说的是:"异步响应"和"异步回包"。ARPC 提供了更灵活的机制,大概如下:
    处理请求的 handler = 3+4 ( server 处理请求+server 发送响应数据)
    go func() {
    for {
    2. server 接收到请求数据
    if 注册时设置为异步处理 {
    // 此处是指 "异步响应"
    go handler() // 3+4
    } else {
    handler() // 3+4
    }
    }
    }()
    而 handler 的实现中也可以自主选择同步或者异步回包,比如:
    func handler(ctx *arpc.Context) {
    // do something
    if condition {
    go ctx.Write(...) // 此处指"异步回包"
    或者
    // 此处是指 "异步响应"
    go func() {
    // do something
    go ctx.Write(...) // 此处是指 "异步回包"
    }
    } else {
    // do something
    ctx.Write(...)
    }
    }

    晚上状态有点懵逼,不知道有没有写错的地方,先大概看下吧 😁😁
    lesismal
        60
    lesismal  
    OP
       2020-12-12 22:04:23 +08:00
    v 站玩的少,markdown 和代码段的格式不知道怎么搞,编辑好的缩进发出来都给我整没了 😂😂
    lesismal
        61
    lesismal  
    OP
       2020-12-12 22:11:32 +08:00
    @lesismal 或者分成"异步请求处理" 和 "异步回包"来描述更清楚些,但是这个处理流程比较简单,就几行代码,就不做太强的概念辨识度相关的解释了 😂
    so1n
        62
    so1n  
       2020-12-12 22:36:27 +08:00 via Android
    @lesismal 感谢你在很忙时详细的回复我的信息,非常抱歉我没有经过详细的思考就做了回复。

    大概懂了你的意思。不过我一般考虑到资源隔离,以及协程的资源占用不高,3 都是开个协程来处理
    lesismal
        63
    lesismal  
    OP
       2020-12-12 23:26:10 +08:00
    @lesismal 描述清楚了就好回答了,文字沟通不方便,如果是面对面沟通可能早就讲清楚了 😁😁

    "不过我一般考虑到资源隔离,以及协程的资源占用不高,3 都是开个协程来处理"
    —— 嗯嗯,这种用于 RPC 的场景,请求和依赖的下游模块做好限流之类的,系统就不会爆

    ARPC 主要是考虑通用场景比如包括游戏领域的业务,请求方的频率可能会非常高,热点时段协程数量可能会爆,所以留给了业务层根据实际场景做更灵活的同步异步定制

    周末愉快 ^_^
    lesismal
        64
    lesismal  
    OP
       2020-12-14 14:15:23 +08:00
    @kevinwan @DoctorCat ARPC 也实现了个编码中间件的扩展示例,把 opentracing 的支持加上了,例子:

    https://github.com/lesismal/arpc/tree/master/examples/middleware/coder/tracing
    kevinwan
        66
    kevinwan  
       2020-12-14 14:38:03 +08:00
    @lesismal 赞!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1769 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 81ms · UTC 16:28 · PVG 00:28 · LAX 08:28 · JFK 11:28
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.