网关
网关是指rpcx服务的http网关。
你可以使用任何语言编写rpc的http客户端,例如Java、Python、C#、Node.js、Php、C/C++、Rust和其他语言。这里查看很多语言的例程。
这意味着,你可以不需要实现协议栈而是通过其他方式来访问rpcx服务。
部署模型
有两种部署模型:网关和代理。
网关
你可以使用网关模型部署。网关作为一个独立的服务器运行。客户端给服务器发送http请求,网关把http请求转换为原生的rpcx请求。然后网关给rpcx服务器发送原生的rpcx请求。
当网关接收到rpcx响应时,就把rpcx响应转换为http响应,然后返回给客户端。
你可以很容易伸缩网关因为它是没有状态的。
网关模型工作像是一个http负载均衡器。
代理
你可以使用代理模型部署。代理与客户端部署在一起。他们作为一个守护应用安装在客户端节点。如果一个节点有多个客户端,你只需要安装一个代理。
客户端发送http请求到他们的本地代理。本地代理把http请求转换为rpcx请求,然后给rpcx服务器发送rpcx请求,最后把rpcx响应转换成http响应。
代理模型工作像是服务器网络。
协议
你可以使用任何语言发送http请求。你必须在请求中设置一些额外的http头部信息。
- X-RPCX-Version:rpcx版本
- X-RPCX-MesssageType:请求的话设为0
- X-RPCX-Heartbeat:是否是心跳请求,默认为否
- X-RPCX-Oneway:是否是单向请求,默认为否
- X-RPCX-SerializeType:序列化方式,0为原始字节 ,1为JSON,2为protobuf,3为msgpack
- X-RPCX-MessageID:message ID,uint64类型
- X-RPCX-ServicePath:服务路径
- X-RPCX-ServiceMethod:服务方法
- X-RPCX-Meta:额外的元信息
对于http响应,有一些其他的http头:
- X-RPCX-Version:rpcx版本
- X-RPCX-MesssageType:1作为响应
- X-RPCX-Heartbeat:是否是心跳响应
- X-RPCX-MessageStatusType:Error或者Normal。错误意味着调用失败
- X-RPCX-SerializeType:序列化方式,0为原始字节 ,1为JSON,2为protobuf,3为msgpack
- X-RPCX-MessageID:message ID,uint64类型
- X-RPCX-ServicePath:服务路径
- X-RPCX-ServiceMethod:服务方法
- X-RPCX-Meta:额外的元信息
- X-RPCX-ErrorMessage:如果X-RPCX-MessageStatusType是Error,那么此处包含错误信息
示例
此处为一个golang的http客户端的示例:
package main
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"github.com/rpcx-ecosystem/rpcx-gateway"
"github.com/smallnest/rpcx/codec"
)
type Args struct
{
A int
B int
}
type Reply struct
{
C int
}
func main() {
cc := &codec.MsgpackCodec{}
// request
args := &Args{
A: 10,
B: 20,
}
data, _ := cc.Encode(args)
req, err := http.NewRequest("POST", "http://127.0.0.1:9981/", bytes.NewReader(data))
if err != nil {
log.Fatal("failed to create request: ", err)
return
}
// set extra headers
h := req.Header
h.Set(gateway.XMessageID, "10000")
h.Set(gateway.XMessageType, "0")
h.Set(gateway.XSerializeType, "3")
h.Set(gateway.XServicePath, "Arith")
h.Set(gateway.XServiceMethod, "Mul")
// send to gateway
res, err := http.DefaultClient.Do(req)
if err != nil {
log.Fatal("failed to call: ", err)
}
defer res.Body.Close()
// handle http response
replyData, err := ioutil.ReadAll(res.Body)
if err != nil
{
log.Fatal("failed to read response: ", err)
}
// parse reply
reply := &Reply{}
err = cc.Decode(replyData, reply)
if err != nil
{
log.Fatal("failed to decode reply: ", err)
}
log.Printf("%d * %d = %d", args.A, args.B, reply.C)
}