前言
Dubbo/Dubbox、Google gRPC、RPCX、Spring Boot/Spring Cloud 如此多的RPC框架 我们当然要了解下他们的原理呀
我们以 LightRPC github.com/dollarkillerx/light 为例 写一个属于自己的RPC
最后的成果是怎样的
payload:
// 请求体
strcut Request {
Msg string
}
// 返回体
struct Response {
Msg string
}
复制代码
server:
// 定义基础服务
struct HelloWorldServer {}
func (h *HelloWorldServer) HelloWorld(ctx *light.Context, request *Request, response *Response) error {
response.Msg = fmt.Sprintf("request: %s ",request.Msg)
return nil
}
// 启动服务
func main() {
ser := server.NewServer()
err := ser.RegisterName(&HelloWorldServer{}, "HelloWorldServer") // 注册 服务
if err != nil {
log.Fatalln(err)
}
// 监听服务
if err := ser.Run(server.UseTCP("0.0.0.0:8074")); err != nil {
log.Fatalln(err) }
}
复制代码
client:
client := client.NewClient(discovery.NewSimplePeerToPeer("127.0.0.1:8074", transport.TCP))
connect, err := client.NewConnect("HelloWorldServer") // 对特定服务 建立连接
if err != nil {
log.Fatalln(err)
return
}
req := Request{
Msg: "hello",
}
resp := Response{}
err = connect.Call(light.DefaultCtx(), "HelloWorld", &req, &resp) // 调用某个服务
if err != nil {
log.Fatalln(err)
}
fmt.Println(resp)
复制代码
这就是我们要实现的基础服务, 我们可以在这个服务之上添加 服务发现与注册 熔断器 监控 等等...
1. 实现服务内部注册
先看看server端的基础需求:
- 1. 一个server端可以注册多个服务
- 2. middleware
我们先定义一个服务管理器 以管理多个服务
type MiddlewareFunc func(ctx *light.Context, request interface{}, response interface{}) error
// 定义服务管理器
type Server struct {
serviceMap map[string]*service // 服务使用map的方式进行注册
options *Options // 相关的配置
beforeMiddleware []MiddlewareFunc // 全局前置middleware
afterMiddleware []MiddlewareFunc // 全局后置middleware
beforeMiddlewarePath map[string][]MiddlewareFunc // 特定路由 前置middleware
afterMiddlewarePath map[string][]MiddlewareFunc // 特定路由 后置middleware
}
// 定义单个服务
type service struct {
name string // server name 服务名称
refVal reflect.Value // server reflect value
refType reflect.Type // server reflect type
methodType map[string]*methodType // server method 服务具体的方法
}
复制代码
我们刚刚定义了这个服务 现在开始 完成服务的初始化代码
func NewServer() *Server {
return &Server{
serviceMap: map[string]*service{},
options: defaultOptions(),
beforeMiddleware: []MiddlewareFunc{},
afterMiddleware: []MiddlewareFunc{},
beforeMiddlewarePath: map[string][]MiddlewareFunc{},
afterMiddlewarePath: map[string][]MiddlewareFunc{},
}
}
复制代码
现在来编写服务注册 这是本章的重点 !!!
我们会定义两个 服务注册的方式
- 通过 Register() 直接注册服务, 服务名称 设置为当前结构体的名称
- 通过 RegisterName() 进行服务注册, 可以传入服务名称 进行设置
func (s *Server) Register(server interface{}) error {
return s.register(server, "", false)
}
func (s *Server) RegisterName(server interface{}, serverName string) error {
return s.register(server, serverName, true)
}
// 具体服务注册方法
func (s *Server) register(server interface{}, serverName string, useName bool) error {
ser, err := newService(server, serverName, useName) // 生成一个服务
if err != nil {
return err
}
s.serviceMap[ser.name] = ser // 放到serviceMap中去
return nil
}
复制代码
构造具体服务
func newService(server interface{}, serverName string, useName bool) (*service, error) {
ser := &service{
refVal: reflect.ValueOf(server),
refType: reflect.TypeOf(server),
}
// 获取服务名称
sName := reflect.Indirect(ser.refVal).Type().Name()
if !utils.IsPublic(sName) { // IsPublic 判断 是否是Public
return nil, pkg.ErrNonPublic
}
if useName {
if serverName == "" {
return nil, errors.New("Server Name is null")
}
sName = serverName
}
ser.name = sName
// constructionMethods 获取当前结构体的 合规方法 进行注册
methods, err := constructionMethods(ser.refType)
if err != nil {
return nil, err
}
ser.methodType = methods
for _, v := range methods {
log.Println("Registry Service: ", ser.name, " method: ", v.method.Name)
}
return ser, nil
}
// constructionMethods Get specific method
func constructionMethods(typ reflect.Type) (map[string]*methodType, error) {
methods := make(map[string]*methodType)
for idx := 0; idx < typ.NumMethod(); idx++ { // 我们对当前struct的方法进行遍历 找到符合的方法进行注册
method := typ.Method(idx)
mType := method.Type
mName := method.Name
if !utils.IsPublic(mName) {
return nil, pkg.ErrNonPublic
}
// 默认是4个
if mType.NumIn() != 4 { // func(*server.MethodTest, *light.Context, *server.MethodTestReq, *server.MethodTestResp) error
continue
}
// 检验它第一个参数是否是ctx
ctxType := mType.In(1)
if !(ctxType.Elem() == typeOfContext) {
continue
}
// request 参数检查
requestType := mType.In(2)
if requestType.Kind() != reflect.Ptr {
continue
}
// 穷举的检查是否均所有参数均为 public
if !utils.IsPublicOrBuiltinType(requestType) {
continue
}
// response 参数检查
responseType := mType.In(3)
if responseType.Kind() != reflect.Ptr {
continue
}
if !utils.IsPublicOrBuiltinType(responseType) {
continue
}
// 校验返回参数
if mType.NumOut() != 1 {
continue
}
returnType := mType.Out(0)
if returnType != typeOfError {
continue
}
methods[mName] = &methodType{
method: method,
RequestType: requestType,
ResponseType: responseType,
}
}
if len(methods) == 0 {
return nil, pkg.ErrNoAvailable
}
return methods, nil
}
复制代码
上面我们就完成了基础的服务注册了