阅读 659

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

前言

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

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

最后的成果是怎样的

github.com/dollarkille…

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
}
复制代码

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

专栏: juejin.cn/column/6986…

文章分类
后端
文章标签