英文:
Bi-directional RPC options for cross-language client/server
问题
我正在尝试为一个项目制定一个想法,其中客户端GUI可以用Python开发,并且GUI可以由任何其他语言的后端驱动,通过RPC进行通信。
更具体地说,我目前正在客户端使用PyQt,后端使用Go。
这是我的目标:
- PyQt客户端
- 角色:视图,控制器
- 单向调用(信号/槽),例如:
button.clicked -> RPC.handleSignal
- 请求/回复RPC,例如:
rowCount = model.rowCount -> RPC.call.model.rowCount
- 后端不限(在这种情况下是Go)
- 角色:控制器,模型
- 单向调用(发射信号),例如:
model.dataChanged -> RPC.emitSignal
我将每个角色称为控制器角色,因为用户可以在GUI客户端中定义信号/槽连接,或者后端可以通过RPC定义自己的信号/槽连接,前提是了解客户端的视图。这完全取决于用户想要如何设置控制器。
我现在正在研究Thrift,与没有IDL的轻量级基本RPC相比。但是我的问题主要是,尝试使用两个.thrift文件来实现Thrift是否会变得混乱,以实现双向功能?我认为Thrift的好处在于IDL,这样我就可以专门构建我的接口,后端代码只需实现它们想要的部分。
有人对此有什么解决方案的建议吗?两个Thrift接口?一个Thrift接口,提供一个服务让客户端建立第二个简单的套接字来接收单向调用?或者Thrift在这里是否有点过度?虽然后端->GUI接口实际上只是一个单一的服务函数,但GUI->后端可能会扩展一些(modelHandlers,slotHandlers,一般服务器状态查询)。
(编辑)更多想法:
我有一部分认为这种模式可以使用任何RPC框架来实现,是这样的吗?
- 后端服务器进程启动;监听端口
- 客户端GUI启动;连接到服务器RPC;监听新端口;通过RPC调用将端口发送给服务器
- 服务器从客户端接收到端口,并绑定到该端口以进行第二个RPC连接
- 客户端和服务器有2个连接,用于双向RPC通信
英文:
I'm trying to lay out an idea for a project, where a client-side GUI can be developed in python, and the GUI can be driven by a backend in potentially any other language, over RPC.
More specifically, right now I am working with PyQt on the client side, and Go as the initial backend.
Here are my goals:
- PyQt client
- Roles: View, Controller
- Oneway calls (SIGNAL/SLOT), such as:
button.clicked -> RPC.handleSignal
- Request/Reply RPC, such as:
rowCount = model.rowCount -> RPC.call.model.rowCount
- Agnostic backend (Go in this case)
- Roles: Controller, Model
- Oneway calls (Emit SIGNAL), such as:
model.dataChanged -> RPC.emitSignal
I refer to each of these as having controller roles becase a user could define signal/slot connections in the GUI client side, or the backend could define its own signal/slot connections over RPC, assuming knowledge about the view on the client side. It would depend solely on how the user wants to set up the controller.
I am looking at Thrift right now, vs say a lighter weight basic rpc without an IDL. But mainly my question is whether it would be a messy approach to try and do Thrift as two .thrift files, with client/server <----> client/server (two connections), in order to get the bi-directional functionality? The benefit I see with Thrift is the IDL, so that I can build my interface specifically, and backend code can just implement the parts they want.
Does anyone have a recommendation for a solution to this? Two Thrift interfaces? One Thrift interface, that provides a service to let the client establish a second simple socket to receive oneway calls from the server? Or is Thrift somewhat even overkill here? While the backend -> GUI interface is really only a single service function, the GUI -> backend could expand a bit (modelHandlers, slotHandlers, general server status queries).
(Edit) More Thoughts
Part of me is thinking that the pattern could be done with any RPC framework, and it would be like this?
- Backend server process starts; listens on port
- Client GUI starts; connects to server RPC; listens on new port; sends port to server via RPC call
- Server receives port from client, and binds to it for a second RPC connection
- Client and Server have 2 connections, for bi-directional RPC
答案1
得分: 2
在网络上,当你通过TCP(或任何其他类似的基于连接的协议)连接到服务器时,当连接时,你的计算机已经选择并监听一个(伪/相对)随机的端口,以便接收服务器的回答。所以就这个问题而言,只要服务器保持连接并且双方保持连接活动,你已经有了一个双向连接。所以从技术上来说,你不需要初始化第二个连接。
根据你的RPC系统,它可能使用TCP,也可能不使用TCP,并且它可能具有双向通信(或者更准确地说,双向通信初始化)。
现在,对于thrift,似乎还没有太多的文档(没有API参考)。但是通过检查示例代码,它似乎将连接处理抽象化,并仅使用请求/响应通信。所以在这种情况下,是的,你需要在客户端上监听一个定义的端口,并告诉服务器,以便服务器可以启动请求/响应通信。
Go本身提供了rpc包,它专门用于Go数据,所以对你没有帮助。
然而,它还提供了websocket包。虽然最初是针对Web服务器到Web浏览器的通信,但它是一种双向协议(毕竟这就是它的目的),可以被任何应用程序类型使用。它可能在大小/带宽方面不如其他协议高效,但它可以完成工作。
我不确定go包的实现状态。Conn类型确实有一个origin字段,但是Conn.Dial函数的示例传递了http://localhost
;我不确定这是否会被替换为远程可访问的websocket origin。Handler.ServeHTTP函数提供了一个http.ResponseWriter,你可能也可以用它来初始化连接。至少这是我要测试的第一件事。
另一种选择是,如果你对使用定义的数据格式并自己处理网络感到舒适,可以使用protobuf。有一个用于Go绑定protobuf的社区项目。然后你可以自己处理TCP连接(连接到它并发送数据,接收数据,保存连接信息等)。
至于其他的替代方案,你可以查看社区包页面和/或社区项目页面(特别是Go Ajax和go-xmlrpc - 尽管这两个项目目前都非常基础)。
英文:
On the network when you connect to a server via TCP (or any other similar connection based protocol), when connecting your PC already chooses and listens on a (pseudo/relatively) random port in order to receive the servers answers. So for that matter, as long as the server keeps the connection and both sides keep the connection alive you already have a bi-directional connection. So from a technical, low-level point of view you do not need to initiate a second connection.
Depending on your RPC system it may or may not use TCP and it may or may not feature bi-directional communication (or rather, bi-directional communication initialization).
Now, for thrift, there does not seem to be much documentation there yet (no API reference). But checking the example code it seems to abstract the connection-handling away and use request/response communication only. So in this case, yes, you would have to listen on a defined port client side and tell the server, so the server can initiate a request/response communication.
Go itself provides the rpc package, which is exclusive to go data, so will not help you.
However, it also provides the websocket package. Although initially targeted at webserver to webbrowser communications it is a bi-directional protocol (that’s what it’s about after all) and can be used by any application type. It may not be as efficient size-/bandwidth-wise but it does the job.
I’m not sure about the state of implementation in the go package. The Conn type does have an origin field but the Conn.Dial function example passes http://localhost
; I’m not sure whether this is replaced with a remotely-accessible websocket origin. The Handler.ServeHTTP function provides an http.ResponseWriter, which you can probably use to initiate connection as well. At least that’s the first thing I would test.
An alternative, if you are comfortable with using a defined dataformat and handle networking yourself, is protobuf. There is a community project for go bindings to protobuf. You could then handle the TCP connection yourself (connecting to it and sending your data, receiving the data, holding the connection info etc).
As for other alternatives, you may want to check the community packages page and or the community projects page (especially the Go Ajax and go-xmlrpc – both are very rudimentary at this point though).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论