Golang Go语言 RPC 的变革 —— ARPC 项目自荐

发布于 1周前 作者 bupafengyu 来自 Go语言

Golang Go语言 RPC 的变革 —— ARPC 项目自荐

已加入 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 。


更多关于Golang Go语言 RPC 的变革 —— ARPC 项目自荐的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

67 回复

安利一下 rSocket

更多关于Golang Go语言 RPC 的变革 —— ARPC 项目自荐的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


主要是 RPC 的含义就是 Remote Procedure Call
无需应答是否要求保证送达?要保证送达需要回执,这个从 TCP 层面做还是从你应用层做多大额外开销的,把应答丢弃就行了
不要求送达,那 TCP 相比 UDP 的开销比你应用层更明显,游戏恐怕不太可能用你这个方法。

另外,ARPC 让人感觉这个是 ARP 的 Client 。

如果不等函数调用结束就返回 那不就是消息队列吗…

看了下 rsocket-go,风格比较 java,有点臃肿了,使用起来不够方便简洁

golang<br>package main<br><br>import (<br> "context"<br> "log"<br><br> "<a target="_blank" href="http://github.com/rsocket/rsocket-go" rel="nofollow noopener">github.com/rsocket/rsocket-go</a>"<br> "<a target="_blank" href="http://github.com/rsocket/rsocket-go/payload" rel="nofollow noopener">github.com/rsocket/rsocket-go/payload</a>"<br>)<br><br>func main() {<br> // Connect to server<br> cli, err := rsocket.Connect().<br> SetupPayload(payload.NewString("Hello", "World")).<br> Transport(rsocket.TCPClient().SetHostAndPort("127.0.0.1", 7878).Build()).<br> Start(context.Background())<br> if err != nil {<br> panic(err)<br> }<br> defer cli.Close()<br> // Send request<br> result, err := cli.RequestResponse(payload.NewString("你好", "世界")).Block(context.Background())<br> if err != nil {<br> panic(err)<br> }<br> log.Println("response:", result)<br>}<br>

“不等函数调用结束就返回” —— 这个可能是兄弟误解了,ARPC 说的不是不等结束就返回,而是可以收到请求后自由选择什么时候回复,可以在处理函数中回包也可以在处理函数之外异步回包之类的,不像其他 RPC 那样函数调用结束了就返回了。

“主要是 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 那种处理函数结束就意味着调用结束的限制

另外,还没有深入研究 rsocket,不知道是否方便支持服务端广播之类的

传统的分布式调用有很多坑要填的,比如 90 年代的 CORBA 技术就试图搭出一套完全的分布式对象体系。比如你的框架里面,假如服务器端单个请求处理比较耗时的话,多个客户端的并发请求是否会被阻塞?当个客户端的同一个远程方法的多个并发调用,是否会被阻塞? RPC 框架一定和应用场景紧密相连的,能满足几个实际应用场景需求的框架,就是好框架



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

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

另外 “多个客户端的并发请求是否会被阻塞” 不只是服务端框架的问题。请求处理对应的业务逻辑比如数据层、其他调用链等耗时操作也可能导致响应慢,还有比如业务层没有做限流导致的请求量爆炸,甚至部署的网络链路不稳定导致 transport 层拥塞,这些非框架原因导致的超载是框架自己不可控的、需要留给业务层处理。当然,有的微服务框架做得大而全,框架本身包含了限流熔断负载均衡等全家桶组件,ARPC 的定位则是网络和协议交互这一层的小而美,毕竟大而全太耗精力而且并不是所有项目都需要大而全,绝大多数团队的业务场景直接拿别人大而全的框架反倒成本太高、不太合适

不同领域的分布式系统差别太大了,比如 web 、游戏、分布式数据库或者计算引擎。每种领域又有很多具体的子场景,业务耦合的方式不同,架构设计的方式也都不同,甚至天差地别。通常在 web 领域流行的分布式理论,拿到 MMORPG 里就完全不能用

近十年的互联网发展速度太快了,最基本的一个需求,比如推送,很多应用可能都需要,但是传统的 RPC,server 端是不支持广播的,所以要使用 http static,http api,websocket,服务集群内部还要 RPC,而除了 http static,其他的场景 ARPC 都可以支撑,能很大程度简化技术栈,并且性能更高

啊,13 年开始就这么用了

名字可以改成 aRPC,就类似于 gRPC

我们有 zRPC,基于 gRPC 的,我觉得 gRPC 还是比较通用解决方案,当然也鼓励创新
https://github.com/tal-tech/go-zero

感觉是造了双筷子,然后改个名叫“叉子”……纯直觉,没别的意思

这个都行,代码 package 都是 arpc,个人开发者没那么大的社区影响力,ARPC/aRPC/arpc 都随意 :joy:

嗯嗯 star 支持了。前阵子还提了个服务注册发现确保强一致的 [issues/227]( https://github.com/tal-tech/go-zero/issues/227) ,其实加上强一致保障不复杂,etcd 的 “go.etcd.io/etcd/client/v3/concurrency” 子包自带了分布式锁的实现:

golang<br>client, err := clientv3.New(clientv3.Config{<br> Endpoints: endpoints,<br> DialTimeout: 5 * time.Second,<br>})<br>if err != nil {<br> log.Error("NewRegister [%v, %v] clientv3.New failed: %v", key, value, err)<br> return nil, err<br>}<br><br>session, err := concurrency.NewSession(client)<br>if err != nil {<br> log.Error("NewRegister [%v, %v] concurrency.NewSession failed: %v", key, value, err)<br> return nil, err<br>}<br><br>mux := concurrency.NewMutex(session, RegisterMutexPrefix)<br>err = mux.Lock(context.TODO())<br>if err != nil {<br> log.Error("NewRegister [%v, %v] Lock failed: %v", key, value, err)<br> return nil, err<br>}<br>defer mux.Unlock(context.TODO())<br>

然后先 Get 判断再 Put 就行了

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



网络交互是通用的基础设施,4 种基础网络交互模式也早已有之,所以我这个也不算创新 😂😂,只是希望把通用的基础设施能推向 web 等其他曾经受限的领域,让做业务更容易、并且性能更高、更节能。

还是举推送的例子,gRPC 这些好像不太便利。很多接口类业务,其实如果换成有状态服务,性能和软硬件消耗都能节省不少,但是难度当然也略高些,如果是用户之间还存在复杂交互耦合的(比如游戏),则业务复杂度和编码难度更高

欢迎各位体验、尝试 😊

感谢,晚点我看看,也 star 一下 arpc🤝

看着不错啊,有支持 Tracing 么?

感谢支持!多多交流跟各位大佬学习!

没有默认支持其他 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 或者自己项目扩展实现、交流 😁

有的,不过正在适配到 opentracing,我们需要通过 kibana 查询到调用关系,所以 span id 是类似:0, 0.1, 0.1.1, 0.2 这种,最近会适配到 opentracing.

看得出楼主花了不少精力,点个 star 支持一下

已 star 。go-zero 的重点是把我们千万级日活的工程经验和最佳实践通过开源的方式带给大家,通过工具帮助大家更关注业务。不太会针对某个特定领域做深度定制,这可能是咱们两个框架的区别,不知道我是不是说错了,请指正哈

等你们适配好的,我们这边试试,现在人手少没法参与到开源 PR

你们使用的哪些 tracing ?我空了学习研究下看看自己能不能加上

感谢支持!😊😊😊

感谢大佬支持!😊😊😊

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

所以咱们没什么可比性,要说关系的话,就是 go-zero 可以考虑使用 arpc 😊😊😊

亚马逊 xray 或者 sentry 这类的,要么就是开源的一些支持 OpenTracing 的平台

推广挺用心 支持

go-zero 的 goctl 是支持自定义模板的,完全可以自定义为基于 arpc

好,闲了研究下

感谢支持!😊😊😊

赞,这个够灵活,哈哈哈 👍👍👍

你甚至可以通过插件生成 php 代码😂

全链路追踪已经有的,只是要用 jaeger 展示的话需要适配一下

我选择把世界上最好的语言供奉起来,干业务这种还是交给 go 好些 😂

赞,和我之前用 gRPC 时的感想不谋而合。区别是你真的做出来了


行吧…… 看上去想要弄个任何交互底层上的通用抽象层。

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

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

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

(其实 ARP 好像并不分 C/S )

楼主, 我没看懂, 样例里面 server->client 是不是多次一举啊, 你想调用 client.dosomthing, 直接在 response 返回不就行了吗, client 读 response 自己 dosomthing, 有什么应用场景, 能不能举例呀

哈哈哈,欢迎品尝 ^_^

嗯嗯,文字交流、一些场景没有既定的“黑话”,大家可能会理解出一些歧义 😅

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

游戏、VOIP 电话之类的丢帧就丢帧吧,后面的状态同步过来正确就好,这种场景我也会选择使用 udp,这些特殊场景还是得自家定制才对性能最友好,否则就 pb 咱都觉得性能不够

兄弟,我也没太看懂,能详细描述下不?比如

“样例里面 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 别人读出来或者看文字心里默读的时候其实是你自己吃亏啊。。。😅😂

Spring Cloud 使用 http 协议怎么说

我不用 java 😂,随便搜了下 Spring Cloud HTTP2 相关,https://www.jianshu.com/p/ed3f8f983764,好像 Spring Cloud 还是有很大提升空间。
并且,“能罩得住” 和 “能罩得住得更好” 也不是同一件事 😁

上一条 url 跟文字连一起了无法跳转,而且我还没有编辑权限,重新贴下 url:

https://www.jianshu.com/p/ed3f8f983764

我只是举个例子,go-zero 可以生成任意语言的服务端和客户端代码,插件即可

嗯嗯了解😆😁

感觉 rpc 最重要的是能方便准确的定义 interface 和 data structure

是指有序的 request→response→request→response 变为 request,request 然后等处理完了再 response,response 吗?忘记叫啥了,io 多路复用?


单个连接上的消息是顺序读取的,你说的这种是 one-by-one 的方式进行处理,处理完一个再处理下一个,http 是这样子的,一个处理完之前下一个要排队等待,如果一个处理慢了会导致其他消息也被阻塞、这个问题通常叫线头阻塞
io 多路复用是指 select 、poll 、epoll 、iocp 、kqueue 等,通过事件机制、异步 io 对多个文件描述符(网络连接也是文件描述符的一种)进行高效的异步 io 操作
兄弟概念有点迷茫,可以多看些好书比如 CSAPP 、APUE 、UNP 之类的,啃下来消化消化应该会有很大帮助😁😁

我是突然忘记了那种该叫啥,所以你的异步是不是指这个方式?

这两点对于非 rpc 的领域好像也同样重要,所以,反倒不是 rpc 的重点了,而是通用场景的重点 😅😅

兄弟,先读点好书,或者搜知识点,阻塞、非阻塞、同步、异步,还有其他的很多基础知识补上再交流,否则问出来的问题容易把我问死,不是不想回答,但基础知识的科普,我没那么多时间啊,而且零碎的知识不如你自己系统性学习的效果好 😂😂
或者可以直接来读源码,标准库的源码很赞、很值得学习,或者比如你想问的 ARPC 的问题,ARPC 源码也相对简单,你按照 server 的启动流程看进去,很快就能找到答案了

那算了,我时间也不多……我只是觉得你那个说不清,很难理解异步返回是代表什么



一次完整的调用过程指 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(…)
}
}

晚上状态有点懵逼,不知道有没有写错的地方,先大概看下吧 😁😁

v 站玩的少,markdown 和代码段的格式不知道怎么搞,编辑好的缩进发出来都给我整没了 😂😂

或者分成"异步请求处理" 和 "异步回包"来描述更清楚些,但是这个处理流程比较简单,就几行代码,就不做太强的概念辨识度相关的解释了 😂

感谢你在很忙时详细的回复我的信息,非常抱歉我没有经过详细的思考就做了回复。

大概懂了你的意思。不过我一般考虑到资源隔离,以及协程的资源占用不高,3 都是开个协程来处理

描述清楚了就好回答了,文字沟通不方便,如果是面对面沟通可能早就讲清楚了 😁😁

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

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

周末愉快 ^_^

ARPC 也实现了个编码中间件的扩展示例,把 opentracing 的支持加上了,例子:

https://github.com/lesismal/arpc/tree/master/examples/middleware/coder/tracing

在IT领域,特别是Golang(Go语言)的开发中,RPC(远程过程调用)技术一直扮演着重要的角色。传统的RPC机制虽然能实现跨服务器的服务调用,但在效率、延迟及连接复用等方面存在不足。HTTP API虽然能解决部分问题,但其在高并发、低延迟方面的表现并不理想。

在这种背景下,ARPC项目应运而生。ARPC不仅继承了原生RPC的优势,更在功能上进行了扩展和优化。与原生RPC相比,ARPC支持服务端主动给客户端发消息,客户端也能异步调用服务端的函数,这些特性使得ARPC在处理复杂业务场景时更加灵活和高效。

ARPC的使用也非常简便,开发者只需在服务端定义相应的处理函数,并在客户端根据路由寻址调用即可。这种设计大大降低了开发难度,提高了开发效率。

此外,ARPC的性能表现也十分出色。它采用了高效的内存管理和数据序列化技术,使得单次请求的处理时间大大缩短。同时,ARPC还支持多种主流的服务注册中心,如Etcd和Consul等,方便用户根据实际需求灵活配置。

值得一提的是,ARPC项目还拥有一个活跃的开源社区,不断有新的功能模块被贡献进来,形成了丰富且开放的插件生态系统。这为开发者提供了无限可能,使得ARPC能够持续适应更加复杂多变的应用场景。

综上所述,ARPC项目在Golang RPC领域具有显著的优势。它不仅提高了RPC调用的效率和灵活性,还降低了开发难度和成本。对于正在寻找高性能、易扩展的RPC解决方案的Golang开发者来说,ARPC无疑是一个值得尝试的选择。

回到顶部