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 }