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 ```