github.com/rolandhe/saber@v0.0.4/nfour/README.md (about)

     1  # nfour 是一个基于tcp的网络开发框架
     2  因为它是运行在4层上的,所以命名问题nfour。它包含三层:
     3  * 通信层,负责二进制数据的通信,支持多路复用模式和传统的单路模式,
     4    * 多路复用利用单个连接并发执行多个任务请求,请求发送和结果接收并发执行,可以利用较少的资源支撑大量并发,推荐使用这种模式。同时支持服务端和客户端。
     5    * 单路模式,即传统的request/response模式,在一个连接上同步的发送request,等接收到response后再发送下一个request。只实现了服务端。
     6  * rpc层, 基于通信层的抽象层,它负责发送业务对象到服务端,响应数据也是业务对象,所谓业务对象,即struct,或者string、int等,是业务调用所看到的那一层。
     7  * 协议层,它位于rpc和通信层,负责讲业务对象转换成二进制。nfour利用了依赖倒置的设计原则,在rpc层定义一组协议接口,可以由业务使用这自由扩展。
     8    * nfour实现了基于json转换协议的缺省实现。
     9  
    10  一般业务使用只需要实现协议层即可,也可以使用开箱即可的json协议。
    11  
    12  # 基于json协议的示例
    13  ## 服务端
    14  
    15  ```
    16      type Result[T any] struct {
    17          Code    int    `json:"code"`
    18          Message string `json:"message"`
    19          Data    T      `json:"data"`
    20      }
    21      
    22      func JsonRpcErrHandler(err error, interfaceName any) *proto.JsonProtoRes {
    23          ret := &Result[string]{
    24              Code:    500,
    25              Message: err.Error(),
    26          }
    27      
    28          method := "can't parse request, so don't know"
    29          if interfaceName != nil {
    30              method = interfaceName.(string)
    31          }
    32      
    33          body, _ := json.Marshal(ret)
    34          return &proto.JsonProtoRes{
    35              Key:  method,
    36              Body: body,
    37          }
    38      }
    39      
    40      
    41      func RegisterAll(router *rpc.SrvRouter[proto.JsonProtoReq, proto.JsonProtoRes]) {
    42          router.Register("rpc.test", func(req *proto.JsonProtoReq) (*proto.JsonProtoRes, error) {
    43      
    44              rpcResult, _ := json.Marshal(&Result[string]{
    45                  Code: 200,
    46                  Data: string(req.Body),
    47              })
    48              res := &proto.JsonProtoRes{
    49                  Key:  req.Key,
    50                  Body: rpcResult,
    51              }
    52              return res, nil
    53          })
    54      }
    55      
    56      func main() {
    57          working, handlerErrFunc, router := proto.NewJsonRpcSrvWorking(handler.JsonRpcErrHandler)
    58          handler.RegisterAll(router)
    59          conf := nfour.NewSrvConf(working, handlerErrFunc, 10000)
    60      
    61          duplex.Startup(11011, conf)
    62      }
    63  ```
    64  
    65  
    66  ## 客户端示例
    67  
    68  ```
    69      func core(name string) {
    70          conf := duplex.NewTransConf(time.Second*2, 5000)
    71          t, err := duplex.NewTrans("localhost:11011", conf, name)
    72          if err != nil {
    73              log.Println(err)
    74              return
    75          }
    76      
    77          client := proto.NewJsonRpcClient(t)
    78      
    79          concurrentSend(50000, client)
    80      
    81          client.Shutdown(name + "-main")
    82      
    83      }
    84      
    85      type req struct {
    86          val    string
    87          sortId int
    88      }
    89      
    90      func concurrentSend(taskCount int, c proto.JsonClient) {
    91          reqs := buildRequests(taskCount)
    92          reqTimeout := &duplex.ReqTimeout{
    93              WaitConcurrent: time.Millisecond * 1000,
    94          }
    95      
    96          wg := sync.WaitGroup{}
    97          wg.Add(taskCount)
    98      
    99          lock := sync.Mutex{}
   100      
   101          var rarray []string
   102      
   103          start := time.Now().UnixNano()
   104          trigger := sync.WaitGroup{}
   105          trigger.Add(1)
   106          for _, req := range reqs {
   107              go func(r *proto.JsonProtoReq) {
   108                  trigger.Wait()
   109                  s := ""
   110                  tryCount := 0
   111                  for {
   112                      resp, err := c.SendRequest(r, reqTimeout)
   113                      if err != nil {
   114                          s = "err:" + "###" + err.Error()
   115                          if tryCount < 2 {
   116                              time.Sleep(time.Millisecond * 10)
   117                              tryCount++
   118                          } else {
   119                              break
   120                          }
   121                      } else {
   122                          jsonResult, _ := proto.ParseJsonProtoRes[handler.Result[string]](resp, func() *handler.Result[string] {
   123                              return &handler.Result[string]{}
   124                          })
   125                          if jsonResult.Code == 500 {
   126                              s = "err:" + "###" + jsonResult.Message
   127                          } else {
   128                              s = jsonResult.Data
   129                          }
   130                          break
   131                      }
   132                  }
   133      
   134                  lock.Lock()
   135                  rarray = append(rarray, s)
   136                  lock.Unlock()
   137                  wg.Done()
   138              }(req)
   139      
   140          }
   141          trigger.Done()
   142      
   143          wg.Wait()
   144      
   145          cost := time.Now().UnixNano() - start
   146      
   147          nfour.NFourLogger.InfoLn("--------------------------------")
   148          nArrays := convertBatchResult(rarray)
   149          errCount := 0
   150          last := -1
   151          lostCount := 0
   152          for _, v := range nArrays {
   153              if strings.HasPrefix(v.val, "err:") {
   154                  nfour.NFourLogger.InfoLn(v.val)
   155                  errCount++
   156              } else {
   157                  //fmt.Println(v.val)
   158                  if (v.sortId - last) != 1 {
   159                      lostCount++
   160                  }
   161                  last = v.sortId
   162              }
   163          }
   164          nfour.NFourLogger.Info("......end(%d)..error(%d)..lost(%d)..\n", len(rarray), errCount, lostCount)
   165          nfour.NFourLogger.InfoLn(cost, "nano")
   166      }
   167      
   168      func convertBatchResult(res []string) []*req {
   169          var array []*req
   170          for _, s := range res {
   171              pos := strings.LastIndex(s, "-")
   172              ids := s[pos+1:]
   173              id, _ := strconv.Atoi(strings.TrimLeft(ids, "0"))
   174              array = append(array, &req{s, id})
   175          }
   176      
   177          sortutil.Cmp[*req](func(p1, p2 **req) bool {
   178              return (*p1).sortId < (*p2).sortId
   179          }).Sort(array)
   180      
   181          return array
   182      }
   183      
   184      func buildRequests(num int) []*proto.JsonProtoReq {
   185          var ret []*proto.JsonProtoReq
   186          for i := 0; i < num; i++ {
   187              num := fmt.Sprintf("%08d", i)
   188              v := "hello worldjjjjjjjjjjjjjjkadsjfkdjlasfjkldklsafjkdsafjkldsajlfjkdsajkfdjksafjkldjkslafjkldsajkfjkldsajkfjkdlsajkfdjkasfjkdsajkfjkldsajklfdjksafjkdjkasfjkdasjkfjkldsajkfjkdlasjfkfdasjkfjdklasfjkdsaf ok-" + num
   189              ret = append(ret, &proto.JsonProtoReq{"rpc.test", []byte(v)})
   190          }
   191          return ret
   192      }
   193      
   194      func main() {
   195          core("single")
   196      }
   197  ```