用GO写一个RPC框架 s01(服务内部注册实现)

dollarkillerx

Dubbo/Dubbox、Google gRPC、RPCX、Spring Boot/Spring Cloud 如此多的RPC框架 我们当然要了解下他们的原理呀

我们以 LightRPC github.com/dollarkillerx/light 为例 写一个属于自己的RPC

最后的成果是怎样的

https://github.com/dollarkill...

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{},
    }
}

现在来编写服务注册 这是本章的重点 !!!

我们会定义两个 服务注册的方式

  1. 通过 Register() 直接注册服务, 服务名称 设置为当前结构体的名称
  2. 通过 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
}

上面我们就完成了基础的服务注册了

阅读 764

手把手教你用GO编写RPC框架
手把手教你用GO编写RPC框架

坎坷之路,终抵群星

44 声望
3 粉丝
0 条评论

坎坷之路,终抵群星

44 声望
3 粉丝
文章目录
宣传栏