github.com/rolandhe/saber@v0.0.4/nfour/rpc/server.go (about)

     1  // rpc abstraction basing nfour
     2  // Copyright 2023 The saber Authors. All rights reserved.
     3  
     4  // Package rpc 基于 duplex基础之上实现的高性能的rpc框架,它操作的是接口和业务对象数据,相比基于二进制的duplex有更好的抽象,更容易规范化。
     5  // rpc框架的底层仍然是二进制通信,因此它抽象了编解码层。
     6  package rpc
     7  
     8  import (
     9  	"errors"
    10  	"github.com/rolandhe/saber/nfour"
    11  	"sync"
    12  )
    13  
    14  var (
    15  	badReqErr = errors.New("bad request")
    16  )
    17  
    18  // SrvCodec rpc服务端编解码抽象
    19  type SrvCodec[REQ any, RES any] interface {
    20  	// Decode 解码二进制为业务对象
    21  	Decode(payload []byte) (*REQ, error)
    22  	// Encode 编码业务对象为二进制
    23  	Encode(res *RES) ([]byte, error)
    24  }
    25  
    26  // HandleBiz 用于处理业务请求的函数,它能够接收业务请求对象,然后进行业务处理,返回响应业务对象
    27  // 与 nfour.WorkingFunc 不同的是 HandleBiz 处理的是业务对象,nfour.WorkingFunc 处理的是二进制,
    28  type HandleBiz[REQ any, RES any] func(req *REQ) (*RES, error)
    29  
    30  // HandleErrorFunc 转换err为需要返回的业务响应对象
    31  type HandleErrorFunc[RES any] func(err error, interfaceName any) *RES
    32  
    33  // NewRpcWorking 基于业务对象构建处底层需要的 nfour.WorkingFunc
    34  //
    35  // codec 对象的编解码工具
    36  //
    37  // kExtractor rpc 方法名称抽取工具。rpc的是在方法级别被调用的,每个方法在服务端对应一个业务处理函数,服务端需要根据这个 "方法名称" 来路由到方法处理函数,"方法名称"会在请求中设置,kExtractor 负责从请求对象中提取"方法名称"
    38  //
    39  // 返回值 *SrvRouter , 返回一个服务端的路由对象,它能帮助正确的路由到方法出来函数
    40  func NewRpcWorking[REQ any, RES any](codec SrvCodec[REQ, RES], kExtractor func(req *REQ) any, errToRes HandleErrorFunc[RES]) (nfour.WorkingFunc, *SrvRouter[REQ, RES]) {
    41  	router := &SrvRouter[REQ, RES]{
    42  		codec:        codec,
    43  		keyExtractor: kExtractor,
    44  		errorToRes:   errToRes,
    45  	}
    46  	return func(task *nfour.Task) ([]byte, error) {
    47  		payload := task.PayLoad
    48  		return workingCore(router, payload)
    49  	}, router
    50  }
    51  
    52  func workingCore[REQ any, RES any](router *SrvRouter[REQ, RES], payload []byte) ([]byte, error) {
    53  	req, err := router.codec.Decode(payload)
    54  	if err != nil {
    55  		nfour.NFourLogger.InfoLn(err)
    56  		response, _ := router.codec.Encode(router.errorToRes(err, nil))
    57  		return response, nil
    58  	}
    59  	return router.run(req), nil
    60  }
    61  
    62  // SrvRouter 服务端的方法路由器,它包含了编解码工具,方法注册表,方法名称提取工具等。
    63  // 服务端需要把方法名称及其对应的方法处理函数注册到 SrvRouter , 当请求进入时,能够使用方法提取工具从请求中提取到方法名称,并正确的找到方法处理函数,然后执行
    64  type SrvRouter[REQ any, RES any] struct {
    65  	codec        SrvCodec[REQ, RES]
    66  	regTable     sync.Map
    67  	keyExtractor func(req *REQ) any
    68  	errorToRes   HandleErrorFunc[RES]
    69  }
    70  
    71  // Register 注册方法名称及方法处理函数
    72  // 如果相同的方法名称注册多个函数,最后一个会覆盖前面的,并输出日志
    73  func (r *SrvRouter[REQ, RES]) Register(key any, fn HandleBiz[REQ, RES]) {
    74  	if _, loaded := r.regTable.LoadOrStore(key, fn); loaded {
    75  		nfour.NFourLogger.Info("%v exists\n", key)
    76  	}
    77  }
    78  
    79  func (r *SrvRouter[REQ, RES]) run(req *REQ) []byte {
    80  	key := r.keyExtractor(req)
    81  	if key == nil {
    82  		return r.handleErr(badReqErr, nil)
    83  	}
    84  	v, ok := r.regTable.Load(key)
    85  	if !ok {
    86  		return r.handleErr(badReqErr, key)
    87  	}
    88  	fn := v.(HandleBiz[REQ, RES])
    89  	res, err := fn(req)
    90  	if err != nil {
    91  		return r.handleErr(err, key)
    92  	}
    93  	buff, _ := r.codec.Encode(res)
    94  	return buff
    95  }
    96  
    97  func (r *SrvRouter[REQ, RES]) handleErr(err error, interfaceName any) []byte {
    98  	buf, _ := r.codec.Encode(r.errorToRes(err, interfaceName))
    99  	return buf
   100  }