├── README.md ├── client.go ├── connPool.go ├── driverConn.go ├── error.go ├── example ├── clientDemo │ └── main.go ├── data │ └── data.go └── serverDemo │ └── main.go ├── gorpc_test.go ├── labs ├── address_test.go ├── newObject_test.go └── writeblock.go ├── memPool ├── memPool.go └── memPool_test.go ├── monitor.go ├── request.go ├── response.go ├── server.go ├── service.go ├── setting.go └── utility ├── calculator └── calculator.go ├── convert └── convert.go └── pprof ├── pprof.go └── profile.go /README.md: -------------------------------------------------------------------------------- 1 | # gorpc 2 | a golang rpc framework designed for Large-scale distributed system 3 | 4 | ## run a gorpc test 5 | `` 6 | go test -v github.com/johntech-o/gorpc 7 | `` 8 | 9 | command above will show performance info at the end of test output 10 | 11 | ``` 12 | client conn status: {"Result":{"127.0.0.1:6668":{"creating":0,"idle":0,"readAmount":907910,"working":30}},"Errno":0} 13 | client conn Qps : {"Result":73011,"Errno":0} 14 | HeapObjects: 2981940 - 21634 = 2960306 15 | TotalAlloc: 789211080 - 50114848 = 739096232 16 | PauseTotalMs: 1 - 0 = 1 17 | NumGC: 12 - 1 = 11 18 | server call status: {"Result":{"Call/s":71929,"CallAmount":980847,"Err/s":0,"ErrorAmount":0,"ReadBytes":50014893,"ReadBytes/s":3668379,"WriteBytes":30399247,"WriteBytes/s":2229799},"Errno":0} 19 | client conn status: {"Result":{"127.0.0.1:6668":{"creating":0,"idle":30,"readAmount":1000008,"working":0}},"Errno":0} 20 | client conn Qps : {"Result":19164,"Errno":0} 21 | 10% calls consume less than 12 ms 22 | 20% calls consume less than 13 ms 23 | 30% calls consume less than 13 ms 24 | 40% calls consume less than 14 ms 25 | 50% calls consume less than 14 ms 26 | 60% calls consume less than 14 ms 27 | 70% calls consume less than 14 ms 28 | 80% calls consume less than 15 ms 29 | 90% calls consume less than 15 ms 30 | 100% calls consume less than 76 ms 31 | request amount: 1000000, cost times : 14 second, average Qps: 69428 32 | Max Client Qps: 73011 33 | --- PASS: TestEchoStruct (15.44s) 34 | gorpc_test.go:240: TestEchoStruct result: hello echo struct ,count: 1000000 35 | PASS 36 | ok github.com/johntech-o/gorpc 15.481s 37 | 38 | ``` 39 | 40 | ## example 41 | 42 | run command on terminal to start the rpc server 43 | 44 | `go run example/serverDemo/main.go` 45 | 46 | run command on another terminal to see result: 47 | 48 | `go run example/clientDemo/main.go` 49 | 50 | ### rpc service and method define in data/data.go 51 | 52 | 53 | ``` 54 | // service TestRpcABC Has a method EchoStruct 55 | type TestRpcABC struct { 56 | A, B, C string 57 | } 58 | 59 | func (r *TestRpcABC) EchoStruct(arg TestRpcABC, res *string) error { 60 | *res = EchoContent 61 | return nil 62 | } 63 | 64 | // service TestRpcInt Has a method Update 65 | type TestRpcInt struct { 66 | i int 67 | } 68 | 69 | func (r *TestRpcInt) Update(n int, res *int) error { 70 | r.i = n 71 | *res = r.i + 100 72 | return nil 73 | } 74 | 75 | ``` 76 | 77 | 78 | ### client example example/client.go 79 | ``` 80 | var client *gorpc.Client 81 | var A string 82 | 83 | func init() { 84 | netOptions := gorpc.NewNetOptions(time.Second*10, time.Second*20, time.Second*20) 85 | client = gorpc.NewClient(netOptions) 86 | flag.StringVar(&A, "a", "127.0.0.1:6668", "remote address:port") 87 | flag.Parse() 88 | } 89 | 90 | func main() { 91 | var strReturn string 92 | // call remote service TestRpcABC and method EchoStruct 93 | err := client.CallWithAddress(A, "TestRpcABC", "EchoStruct", data.TestRpcABC{"aaa", "bbb", "ccc"}, &strReturn) 94 | if err != nil { 95 | println(err.Error(),err.Errno()) 96 | } 97 | println(strReturn) 98 | 99 | 100 | var intReturn *int 101 | // call remote service TestRpcInt and method Update 102 | client.CallWithAddress(A,"TestRpcInt","Update",5,&intReturn) 103 | if err != nil { 104 | println(err.Error(),err.Errno()) 105 | } 106 | println(*intReturn) 107 | 108 | } 109 | ``` 110 | 111 | ### server example example/server.go 112 | 113 | ``` 114 | func main() { 115 | s := gorpc.NewServer(L) 116 | // register the services: TestRpcInt and TestRpcABC 117 | s.Register(new(data.TestRpcInt)) 118 | s.Register(new(data.TestRpcABC)) 119 | s.Serve() 120 | panic("server fail") 121 | } 122 | ``` 123 | 124 | 125 | -------------------------------------------------------------------------------- /client.go: -------------------------------------------------------------------------------- 1 | // @todo while conn broke ,move the pending request to other -> done 2 | // @todo call with retry setting -> done 3 | // @todo send only call -> done 4 | // @todo calculate Bandwidth consumption 5 | 6 | package gorpc 7 | 8 | import ( 9 | "encoding/json" 10 | "math/rand" 11 | "sync" 12 | "time" 13 | 14 | "github.com/johntech-o/timewheel" 15 | ) 16 | 17 | const ( 18 | CALL_RETRY_TIMES = 1 19 | ) 20 | 21 | type NetOptions struct { 22 | connectTimeout time.Duration 23 | readTimeout time.Duration 24 | writeTimeout time.Duration 25 | } 26 | 27 | func NewNetOptions(connectTimeout, readTimeOut, writeTimeout time.Duration) *NetOptions { 28 | return &NetOptions{connectTimeout, readTimeOut, writeTimeout} 29 | } 30 | 31 | type ServerOptions struct { 32 | address string 33 | maxOpenConns int 34 | maxIdleConns int 35 | } 36 | 37 | func NewServerOptions(serverAddress string, maxOpenConns, maxIdleConns int) *ServerOptions { 38 | return &ServerOptions{serverAddress, maxOpenConns, maxIdleConns} 39 | } 40 | 41 | type Client struct { 42 | sync.RWMutex // guard following 43 | cpMap map[string]*ConnPool // connection pool map 44 | addressSlice []string 45 | serviceOptions map[string]*NetOptions 46 | methodOptions map[string]map[string]*NetOptions 47 | serverOptions *NetOptions 48 | } 49 | 50 | func NewClient(netOptions *NetOptions) *Client { 51 | c := Client{ 52 | cpMap: make(map[string]*ConnPool), 53 | serverOptions: netOptions, 54 | serviceOptions: make(map[string]*NetOptions), 55 | methodOptions: make(map[string]map[string]*NetOptions), 56 | } 57 | return &c 58 | } 59 | 60 | func (this *Client) AddServers(servers []*ServerOptions) *ConnPool { 61 | this.Lock() 62 | var cp *ConnPool 63 | var ok bool 64 | for _, server := range servers { 65 | if cp, ok = this.cpMap[server.address]; ok { 66 | cp.Lock() 67 | cp.maxOpenConns = server.maxOpenConns 68 | cp.maxIdleConns = server.maxIdleConns 69 | cp.Unlock() 70 | } else { 71 | cp = NewConnPool(server.address, server.maxOpenConns, server.maxIdleConns) 72 | cp.client = this 73 | this.cpMap[server.address] = cp 74 | this.addressSlice = append(this.addressSlice, server.address) 75 | } 76 | } 77 | this.Unlock() 78 | return cp 79 | } 80 | 81 | func (this *Client) RemoveServers(addresses map[string]struct{}) { 82 | s := []string{} 83 | this.Lock() 84 | for address, _ := range addresses { 85 | delete(this.cpMap, address) 86 | } 87 | for _, address := range this.addressSlice { 88 | if _, ok := addresses[address]; ok { 89 | continue 90 | } 91 | s = append(s, address) 92 | } 93 | this.addressSlice = s 94 | this.Unlock() 95 | } 96 | 97 | func (this *Client) SetServerNetOptions(netOptions *NetOptions) error { 98 | this.Lock() 99 | this.serverOptions = netOptions 100 | this.Unlock() 101 | return nil 102 | } 103 | 104 | func (this *Client) SetServiceNetOptions(service string, netOptions *NetOptions) error { 105 | this.Lock() 106 | this.serviceOptions[service] = netOptions 107 | this.Unlock() 108 | return nil 109 | } 110 | 111 | func (this *Client) SetMethodNetOptinons(service, method string, netOptions *NetOptions) error { 112 | this.Lock() 113 | if _, ok := this.methodOptions[service]; !ok { 114 | this.methodOptions[service] = make(map[string]*NetOptions) 115 | } 116 | this.methodOptions[service][method] = netOptions 117 | this.Unlock() 118 | return nil 119 | } 120 | 121 | // set reply to nil means server send response immediately before execute service.method 122 | func (this *Client) Call(service, method string, args interface{}, reply interface{}) *Error { 123 | serverAddress, err := this.getAddress() 124 | if err != nil { 125 | return err 126 | } 127 | return this.CallWithAddress(serverAddress, service, method, args, reply) 128 | } 129 | 130 | // set reply to nil means server send response immediately then exec service.method 131 | func (this *Client) CallWithAddress(serverAddress, service, method string, args interface{}, reply interface{}) *Error { 132 | if serverAddress == "" { 133 | return ErrInvalidAddress.SetReason("client remote address is empty") 134 | } 135 | var ( 136 | err *Error 137 | rpcConn *ConnDriver 138 | presp *PendingResponse 139 | request *Request 140 | ) 141 | connectTimeout, readTimeout, writeTimeout := this.getTimeout(service, method) 142 | this.RLock() 143 | cp, ok := this.cpMap[serverAddress] 144 | this.RUnlock() 145 | if !ok { 146 | cp = this.AddServers([]*ServerOptions{NewServerOptions(serverAddress, DefaultMaxOpenConns, DefaultMaxIdleConns)}) 147 | } 148 | rpcConn, err = cp.Conn(connectTimeout, false) 149 | if err != nil { 150 | return err 151 | } 152 | // init request 153 | request = NewRequest() 154 | request.header.Service = service 155 | request.header.Method = method 156 | if reply == nil { 157 | request.header.CallType = RequestSendOnly 158 | } 159 | request.body = args 160 | request.writeTimeout = writeTimeout 161 | // init pending response 162 | presp = NewPendingResponse() 163 | presp.reply = reply 164 | retryTimes := 0 165 | timer := timewheel.NewTimer(readTimeout + writeTimeout) 166 | Retry: 167 | if err = this.transfer(rpcConn, request, presp); err != nil { 168 | goto final 169 | } 170 | 171 | select { 172 | // overload of server will cause timeout 173 | case <-timer.C: 174 | request.freePending() 175 | err = ErrRequestTimeout 176 | goto final 177 | case <-presp.done: 178 | if presp.err == nil { 179 | goto final 180 | } 181 | err = presp.err 182 | presp.err = nil 183 | goto final 184 | } 185 | final: 186 | // fmt.Println("client error: ", err) 187 | if err == nil { 188 | // can free request/presp object 189 | return nil 190 | } 191 | if err == ErrRequestTimeout { 192 | // can not free request/presp object left to gc 193 | return err 194 | } 195 | if !CanRetry(err) || retryTimes == CALL_RETRY_TIMES { 196 | // can free request/presp object left to gc 197 | return err 198 | } 199 | time.Sleep(time.Millisecond * 5) 200 | retryTimes++ 201 | if rpcConn, err = cp.Conn(connectTimeout, true); err != nil { 202 | // can free request/presp object 203 | return err 204 | } 205 | goto Retry 206 | return nil 207 | } 208 | 209 | // connections status statistics 210 | func (this *Client) ConnsStatus() string { 211 | status := make(map[string]*ClientStatus) 212 | this.RLock() 213 | for serverAddress, cp := range this.cpMap { 214 | status[serverAddress] = cp.poolStatus() 215 | } 216 | this.RUnlock() 217 | 218 | var connsStatus = struct { 219 | Result map[string]map[string]uint64 220 | Errno int 221 | }{Result: make(map[string]map[string]uint64)} 222 | 223 | for address, s := range status { 224 | connsStatus.Result[address] = make(map[string]uint64) 225 | connsStatus.Result[address]["idle"] = s.idleAmount 226 | connsStatus.Result[address]["working"] = s.workingAmount 227 | connsStatus.Result[address]["creating"] = s.creatingAmount 228 | connsStatus.Result[address]["readAmount"] = s.readAmount 229 | } 230 | result, err := json.Marshal(connsStatus) 231 | if err != nil { 232 | return `{"Result":"inner error marshal error ` + err.Error() + `","Errno":500}` 233 | } 234 | return string(result) 235 | } 236 | 237 | // client qps statistics 238 | func (this *Client) Qps() string { 239 | var qps = struct { 240 | Result uint64 241 | Errno int 242 | }{} 243 | 244 | addressesReadAmount := make(map[string]uint64) 245 | this.RLock() 246 | for address, cp := range this.cpMap { 247 | addressesReadAmount[address] = cp.status.ReadAmount() 248 | } 249 | this.RUnlock() 250 | time.Sleep(time.Second) 251 | this.RLock() 252 | for address, cp := range this.cpMap { 253 | if readAmount, ok := addressesReadAmount[address]; ok { 254 | qps.Result += cp.status.ReadAmount() - readAmount 255 | continue 256 | } 257 | qps.Result += cp.status.ReadAmount() 258 | } 259 | this.RUnlock() 260 | result, err := json.Marshal(qps) 261 | if err != nil { 262 | return `{"Result":"inner error marshal error ` + err.Error() + `","Errno":500}` 263 | } 264 | return string(result) 265 | } 266 | 267 | func (this *Client) transfer(rpcConn *ConnDriver, request *Request, presp *PendingResponse) *Error { 268 | var ( 269 | sequence uint64 270 | err error 271 | ) 272 | rpcConn.Lock() 273 | if rpcConn.netError != nil { 274 | err = rpcConn.netError 275 | rpcConn.Unlock() 276 | goto fail 277 | } 278 | sequence = rpcConn.Sequence() 279 | request.header.Seq = sequence 280 | // add request and wait response 281 | if err = rpcConn.AddPendingRequest(request); err != nil { 282 | rpcConn.Unlock() 283 | goto fail 284 | } 285 | presp.seq = sequence 286 | presp.connId = rpcConn.connId 287 | rpcConn.AddPendingResponse(presp) 288 | rpcConn.Unlock() 289 | return nil 290 | fail: 291 | if e, ok := err.(*Error); ok { 292 | return e 293 | } 294 | return ErrUnknow.SetError(err) 295 | } 296 | 297 | func (this *Client) writePing(rpcConn *ConnDriver) error { 298 | // init request 299 | request := NewRequest() 300 | request.header.Service = "go" 301 | request.header.Method = "p" 302 | request.header.CallType = RequestSendOnly 303 | request.body = nil 304 | request.writeTimeout = time.Second * 5 305 | // init pending response 306 | presp := NewPendingResponse() 307 | return this.transfer(rpcConn, request, presp) 308 | } 309 | 310 | // get readTimeout writeTimeout 311 | func (this *Client) getTimeout(service, method string) (time.Duration, time.Duration, time.Duration) { 312 | this.RLock() 313 | var netOption *NetOptions 314 | if netOption = this.methodOptions[service][method]; netOption != nil { 315 | this.RUnlock() 316 | // println("service method") 317 | return netOption.connectTimeout, netOption.readTimeout, netOption.writeTimeout 318 | } 319 | if netOption = this.serviceOptions[service]; netOption != nil { 320 | this.RUnlock() 321 | goto final 322 | } 323 | netOption = this.serverOptions 324 | this.RUnlock() 325 | if netOption == nil { 326 | panic("invalid netOption") 327 | } 328 | 329 | final: 330 | this.Lock() 331 | if serviceNetOption, ok := this.methodOptions[service]; ok { 332 | serviceNetOption[method] = netOption 333 | } else { 334 | option := make(map[string]*NetOptions) 335 | option[method] = netOption 336 | this.methodOptions[service] = option 337 | } 338 | this.Unlock() 339 | return netOption.connectTimeout, netOption.readTimeout, netOption.writeTimeout 340 | } 341 | 342 | func (this *Client) getAddress() (string, *Error) { 343 | this.RLock() 344 | defer this.RUnlock() 345 | count := len(this.addressSlice) 346 | if count == 0 { 347 | return "", ErrInvalidAddress.SetReason("empty address") 348 | } 349 | return this.addressSlice[rand.Intn(count)], nil 350 | } 351 | -------------------------------------------------------------------------------- /connPool.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "container/list" 5 | "net" 6 | "sync" 7 | "time" 8 | ) 9 | 10 | const ( 11 | TimeWheelBucketSize = 600 12 | TimeWheelInterval = time.Second 13 | ) 14 | 15 | type ConnPool struct { 16 | address string 17 | sync.Mutex // protects following fields 18 | openConnsPool *OpensPool 19 | maxOpenConns int 20 | maxIdleConns int 21 | creatingConns int 22 | client *Client 23 | status *ClientStatus 24 | } 25 | 26 | // new connection pool and start async-ping goroutine and timer-garbage-collect goroutine 27 | func NewConnPool(address string, maxOpenConns, maxIdleConns int) *ConnPool { 28 | cp := &ConnPool{ 29 | openConnsPool: NewOpenPool(), 30 | maxOpenConns: maxOpenConns, 31 | maxIdleConns: maxIdleConns, 32 | address: address, 33 | status: &ClientStatus{}, 34 | } 35 | go cp.ServeIdlePing() 36 | go cp.GCTimer() 37 | return cp 38 | } 39 | 40 | func (cp *ConnPool) poolStatus() *ClientStatus { 41 | cp.Lock() 42 | workingAmount := cp.openConnsPool.workingList.Len() 43 | idleAmount := cp.openConnsPool.idleList.Len() 44 | creatingAmount := cp.creatingConns 45 | cp.Unlock() 46 | return &ClientStatus{ 47 | uint64(idleAmount), 48 | uint64(workingAmount - idleAmount), 49 | uint64(creatingAmount), 50 | cp.status.ReadAmount(), 51 | } 52 | } 53 | 54 | func (cp *ConnPool) connect(address string, connectTimeout time.Duration) (*net.TCPConn, error) { 55 | c, err := net.DialTimeout("tcp", address, connectTimeout) 56 | if err != nil { 57 | return nil, err 58 | } 59 | return c.(*net.TCPConn), nil 60 | } 61 | 62 | func (cp *ConnPool) createConn(connectTimeout time.Duration) (*ConnDriver, *Error) { 63 | conn, err := cp.connect(cp.address, connectTimeout) 64 | if err == nil { 65 | var rpcConn *ConnDriver = NewConnDriver(conn, nil) 66 | rpcConn.connId = clientConnId.Incr() 67 | go cp.serveRead(rpcConn) 68 | go cp.serveWrite(rpcConn) 69 | cp.Lock() 70 | cp.creatingConns-- 71 | cp.openConnsPool.WorkingPushBack(rpcConn) 72 | cp.Unlock() 73 | return rpcConn, nil 74 | } 75 | cp.Lock() 76 | cp.creatingConns-- 77 | cp.Unlock() 78 | return nil, ErrNetConnectFail.SetReason(err.Error()) 79 | } 80 | 81 | func (cp *ConnPool) Conn(connectTimeout time.Duration, useOpenedConn bool) (*ConnDriver, *Error) { 82 | var rpcConn *ConnDriver 83 | var err *Error 84 | cp.Lock() 85 | // cannot use defer createConn cause block 86 | if rpcConn, err = cp.IdleConn(); err == nil { 87 | cp.Unlock() 88 | return rpcConn, nil 89 | } 90 | if useOpenedConn { 91 | if rpcConn, err = cp.WorkingConn(); err == nil { 92 | cp.Unlock() 93 | return rpcConn, nil 94 | } 95 | cp.Unlock() 96 | return nil, ErrNoWorkingConn.SetReason("use opened conn fail" + err.Error()) 97 | } 98 | if (cp.openConnsPool.Len() + cp.creatingConns) < cp.maxOpenConns { 99 | cp.creatingConns++ 100 | cp.Unlock() 101 | rpcConn, err := cp.createConn(connectTimeout) // add to working slice and creatingConns-- 102 | return rpcConn, err 103 | } 104 | if cp.openConnsPool.Len() == cp.maxOpenConns { 105 | if rpcConn, err = cp.WorkingConn(); err == nil { 106 | cp.Unlock() 107 | return rpcConn, nil 108 | } 109 | } 110 | cp.Unlock() 111 | // the pool is createing connection ,wait 112 | timer := time.NewTimer(connectTimeout) 113 | defer timer.Stop() 114 | for { 115 | cp.Lock() 116 | if rpcConn, err = cp.WorkingConn(); err == nil { 117 | cp.Unlock() 118 | return rpcConn, nil 119 | } 120 | select { 121 | case <-timer.C: 122 | cp.Unlock() 123 | return nil, ErrCallConnectTimeout 124 | default: 125 | } 126 | cp.Unlock() 127 | time.Sleep(2e8) 128 | } 129 | } 130 | 131 | func (cp *ConnPool) IdleConn() (*ConnDriver, *Error) { 132 | conn := cp.openConnsPool.IdlePopFront() 133 | if conn == nil { 134 | return nil, ErrNoIdleConn 135 | } 136 | return conn, nil 137 | } 138 | 139 | func (cp *ConnPool) WorkingConn() (*ConnDriver, *Error) { 140 | rpcConn := cp.openConnsPool.WorkingMoveFrontToBack() 141 | if rpcConn == nil { 142 | return nil, ErrNoWorkingConn 143 | } 144 | return rpcConn, nil 145 | } 146 | 147 | func (cp *ConnPool) MarkAsIdle(conn *ConnDriver) { 148 | cp.openConnsPool.IdlePushBack(conn) 149 | } 150 | 151 | func (cp *ConnPool) RemoveConn(conn *ConnDriver) { 152 | cp.openConnsPool.RemoveFromList(conn) 153 | } 154 | 155 | // serve read wait response from server 156 | func (cp *ConnPool) serveRead(rpcConn *ConnDriver) { 157 | var err error 158 | for { 159 | // if write encode error, set err info from write goroutine 160 | rpcConn.Lock() 161 | if rpcConn.netError != nil { 162 | err = rpcConn.netError 163 | rpcConn.Unlock() 164 | break 165 | } 166 | rpcConn.Unlock() 167 | respHeader := NewResponseHeader() 168 | // pipeline so use the const read timeout 169 | if err = rpcConn.SetReadDeadline(time.Now().Add(DefaultClientWaitResponseTimeout)); err != nil { 170 | break 171 | } 172 | if err = rpcConn.ReadResponseHeader(respHeader); err != nil { 173 | // println("read response header error: ", err.Error()) 174 | break 175 | } 176 | cp.status.IncreReadAmount() 177 | rpcConn.Lock() 178 | pendingResponse := rpcConn.RemovePendingResponse(respHeader.Seq) 179 | rpcConn.Unlock() 180 | if respHeader.ReplyType == ReplyTypePong { 181 | continue 182 | } 183 | pendingResponse.err = respHeader.Error 184 | // @todo call do not observes this pending response,ReadResponseBody use nil instead of pendingResponse.reply 185 | if respHeader.HaveReply() { 186 | if err = rpcConn.ReadResponseBody(pendingResponse.reply); err != nil { 187 | if isNetError(err) { 188 | pendingResponse.err = ErrNetReadFail.SetError(err) 189 | pendingResponse.done <- true 190 | break 191 | } 192 | pendingResponse.err = ErrGobParseErr.SetError(err) 193 | } 194 | } 195 | pendingResponse.done <- true 196 | cp.Lock() 197 | rpcConn.Lock() 198 | if rpcConn.netError == nil { 199 | rpcConn.lastUseTime = time.Now() 200 | rpcConn.callCount++ 201 | if len(rpcConn.pendingResponses) == 0 { 202 | cp.MarkAsIdle(rpcConn) 203 | } 204 | } 205 | rpcConn.Unlock() 206 | cp.Unlock() 207 | } 208 | rpcConn.exitWriteNotify <- true // forbidden write request to this connection 209 | 210 | if rpcConn.isCloseByGCTimer() { 211 | err = ErrNetReadDeadlineArrive 212 | } 213 | rpcConn.Lock() 214 | rpcConn.netError = err 215 | rmap := rpcConn.ClearPendingResponses() 216 | rpcConn.Unlock() 217 | cp.Lock() 218 | cp.RemoveConn(rpcConn) 219 | cp.Unlock() 220 | rpcConn.Close() 221 | close(rpcConn.pendingRequests) 222 | for _, resp := range rmap { 223 | resp.err = ErrPendingWireBroken 224 | resp.done <- true 225 | } 226 | } 227 | 228 | // serve write connection 229 | func (cp *ConnPool) serveWrite(rpcConn *ConnDriver) { 230 | var err error 231 | for { 232 | select { 233 | case request, ok := <-rpcConn.pendingRequests: 234 | if !ok { 235 | err = &Error{500, ErrTypeLogic, "client write channel close"} 236 | goto fail 237 | } 238 | if isPending := request.IsPending(); !isPending { 239 | rpcConn.Lock() 240 | rpcConn.RemovePendingResponse(request.header.Seq) 241 | rpcConn.Unlock() 242 | continue 243 | } 244 | // write request 245 | if err = rpcConn.SetWriteDeadline(time.Now().Add(request.writeTimeout)); err != nil { 246 | goto fail 247 | } 248 | if err = rpcConn.WriteRequestHeader(request.header); err != nil { 249 | // println("write request: ", err.Error()) 250 | goto fail 251 | } 252 | if request.header.IsPing() { 253 | if err = rpcConn.FlushWriteToNet(); err != nil { 254 | goto fail 255 | } 256 | break 257 | } 258 | if err = rpcConn.WriteRequestBody(request.body); err != nil { 259 | goto fail 260 | } 261 | if err = rpcConn.FlushWriteToNet(); err != nil { 262 | goto fail 263 | } 264 | case <-rpcConn.exitWriteNotify: 265 | goto fail 266 | } 267 | } 268 | fail: 269 | // close by gc overwrite the common net error 270 | if rpcConn.isCloseByGCTimer() { 271 | err = ErrNetTimerGCArrive 272 | } 273 | if err == nil { 274 | return 275 | } 276 | rpcConn.Lock() 277 | if rpcConn.netError == nil { 278 | rpcConn.netError = err 279 | } 280 | rpcConn.Unlock() 281 | } 282 | 283 | // GC the timer which is timeout 284 | // review_deadlock cp.lock() -> conn.timeLock.lock() 285 | func (cp *ConnPool) GCTimer() { 286 | connsTimeout := []*ConnDriver{} 287 | for { 288 | now := time.Now() 289 | cp.Lock() 290 | for e := cp.openConnsPool.workingList.Front(); e != nil; e = e.Next() { 291 | conn := e.Value.(*ConnDriver) 292 | conn.timeLock.RLock() 293 | if conn.readDeadline.Before(now) || conn.writeDeadline.Before(now) { 294 | connsTimeout = append(connsTimeout, conn) 295 | } 296 | conn.timeLock.RUnlock() 297 | } 298 | for _, conn := range connsTimeout { 299 | cp.openConnsPool.RemoveFromList(conn) 300 | } 301 | cp.Unlock() 302 | for _, conn := range connsTimeout { 303 | conn.timeLock.Lock() 304 | conn.closeByTimerGC = true 305 | conn.timeLock.Unlock() 306 | conn.Close() 307 | } 308 | connsTimeout = connsTimeout[0:0] 309 | time.Sleep(DefaultTimerGCInterval) 310 | } 311 | } 312 | 313 | func (cp *ConnPool) ServeIdlePing() { 314 | for { 315 | connPingSlice := []*ConnDriver{} 316 | connCloseSlice := []*ConnDriver{} 317 | idleCount := 0 318 | now := time.Now() 319 | cp.Lock() 320 | for e := cp.openConnsPool.idleList.Front(); e != nil; e = e.Next() { 321 | idleCount++ 322 | if idleCount <= cp.maxIdleConns { 323 | connPingSlice = append(connPingSlice, e.Value.(*ConnDriver)) 324 | } else { 325 | conn := e.Value.(*ConnDriver) 326 | connCloseSlice = append(connCloseSlice, conn) 327 | } 328 | } 329 | for _, conn := range connCloseSlice { 330 | cp.openConnsPool.RemoveFromList(conn) 331 | } 332 | cp.Unlock() 333 | go func(pingSlice, closeSlice []*ConnDriver) { 334 | for _, rpcConn := range pingSlice { 335 | rpcConn.Lock() 336 | interval := now.Sub(rpcConn.lastUseTime) 337 | rpcConn.Unlock() 338 | if interval > DefaultPingInterval && 339 | interval < 2*DefaultServerIdleTimeout { 340 | cp.client.writePing(rpcConn) 341 | continue 342 | } 343 | } 344 | for _, rpcConn := range closeSlice { 345 | rpcConn.Lock() 346 | rpcConn.netError = ErrIdleClose 347 | rpcConn.Close() 348 | rpcConn.Unlock() 349 | } 350 | }(connPingSlice, connCloseSlice) 351 | time.Sleep(DefaultPingInterval) 352 | } 353 | 354 | } 355 | 356 | // conn opened pool 357 | type OpensPool struct { 358 | workingList *list.List 359 | idleList *list.List 360 | } 361 | 362 | func NewOpenPool() *OpensPool { 363 | return &OpensPool{ 364 | workingList: list.New(), 365 | idleList: list.New(), 366 | } 367 | } 368 | 369 | func (op *OpensPool) WorkingPushBack(conn *ConnDriver) { 370 | e := op.workingList.PushBack(conn) 371 | conn.workingElement = e 372 | } 373 | 374 | // move the front connection to back then return the back connection 375 | func (op *OpensPool) WorkingMoveFrontToBack() (conn *ConnDriver) { 376 | e := op.workingList.Front() 377 | if e == nil { 378 | return nil 379 | } 380 | op.workingList.MoveToBack(e) 381 | e = op.workingList.Back() 382 | conn = e.Value.(*ConnDriver) 383 | conn.workingElement = e 384 | return 385 | } 386 | 387 | func (op *OpensPool) IdlePopFront() (conn *ConnDriver) { 388 | e := op.idleList.Front() 389 | if e == nil { 390 | return nil 391 | } 392 | conn = e.Value.(*ConnDriver) 393 | op.idleList.Remove(e) 394 | conn.idleElement = nil 395 | return 396 | } 397 | 398 | func (op *OpensPool) IdlePushBack(conn *ConnDriver) { 399 | if conn.idleElement == nil { 400 | e := op.idleList.PushBack(conn) 401 | conn.idleElement = e 402 | } 403 | 404 | } 405 | 406 | // remove the conn from working list and idle list 407 | func (op *OpensPool) RemoveFromList(conn *ConnDriver) { 408 | if conn.workingElement != nil { 409 | op.workingList.Remove(conn.workingElement) 410 | conn.workingElement = nil 411 | 412 | } 413 | if conn.idleElement != nil { 414 | op.idleList.Remove(conn.idleElement) 415 | conn.idleElement = nil 416 | } 417 | } 418 | 419 | func (op *OpensPool) Len() int { 420 | return op.workingList.Len() 421 | } 422 | -------------------------------------------------------------------------------- /driverConn.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "bufio" 5 | "container/list" 6 | "encoding/gob" 7 | // "fmt" 8 | "io" 9 | "net" 10 | "sync" 11 | "sync/atomic" 12 | "time" 13 | ) 14 | 15 | type ConnId uint64 16 | 17 | func (this *ConnId) Incr() ConnId { 18 | return ConnId(atomic.AddUint64((*uint64)(this), 1)) 19 | } 20 | 21 | // use by client 22 | var clientConnId ConnId 23 | 24 | // use by server 25 | var serverConnId ConnId 26 | 27 | type Connection struct { 28 | *net.TCPConn 29 | server *Server 30 | } 31 | 32 | func NewConnection(conn *net.TCPConn, server *Server) *Connection { 33 | return &Connection{conn, server} 34 | } 35 | 36 | type ConnDriver struct { 37 | *net.TCPConn 38 | writeBuf *bufio.Writer 39 | dec *gob.Decoder 40 | enc *gob.Encoder 41 | exitWriteNotify chan bool 42 | pendingRequests chan *Request 43 | sync.Mutex // protects following 44 | pendingResponses map[uint64]*PendingResponse 45 | connId ConnId 46 | netError error 47 | sequence uint64 48 | lastUseTime time.Time 49 | callCount int // for priority 50 | workingElement *list.Element 51 | idleElement *list.Element 52 | 53 | timeLock sync.RWMutex // protects followinng 54 | readDeadline time.Time 55 | writeDeadline time.Time 56 | closeByTimerGC bool 57 | } 58 | 59 | // for flow control 60 | func (conn *Connection) Write(p []byte) (n int, err error) { 61 | n, err = conn.TCPConn.Write(p) 62 | conn.server.status.IncrWriteBytes(uint64(n)) 63 | return 64 | } 65 | 66 | // for flow control 67 | func (conn *Connection) Read(p []byte) (n int, err error) { 68 | n, err = conn.TCPConn.Read(p) 69 | conn.server.status.IncrReadBytes(uint64(n)) 70 | return 71 | } 72 | 73 | func NewConnDriver(conn *net.TCPConn, server *Server) *ConnDriver { 74 | var c io.ReadWriter 75 | if server != nil { 76 | c = NewConnection(conn, server) 77 | } else { 78 | c = conn 79 | } 80 | buf := bufio.NewWriter(c) 81 | return &ConnDriver{ 82 | TCPConn: conn, 83 | connId: serverConnId.Incr(), 84 | writeBuf: buf, 85 | dec: gob.NewDecoder(c), 86 | enc: gob.NewEncoder(buf), 87 | exitWriteNotify: make(chan bool, 1), 88 | pendingResponses: make(map[uint64]*PendingResponse), 89 | pendingRequests: make(chan *Request, MaxPendingRequest), 90 | // timer-gc to close timeout socket 91 | readDeadline: time.Now().Add(DefaultReadTimeout), 92 | writeDeadline: time.Now().Add(DefaultWriteTimeout), 93 | } 94 | } 95 | 96 | func (conn *ConnDriver) Sequence() uint64 { 97 | conn.sequence += 1 98 | return conn.sequence 99 | } 100 | 101 | // timer-gc to close timeout socket 102 | // deadlock-review conn.timeLock.Lock 103 | func (conn *ConnDriver) SetReadDeadline(time time.Time) (err error) { 104 | conn.timeLock.Lock() 105 | if conn.closeByTimerGC == false { 106 | if time.After(conn.readDeadline) { 107 | conn.readDeadline = time 108 | } 109 | } else { 110 | err = ErrNetTimerGCArrive 111 | } 112 | conn.timeLock.Unlock() 113 | return 114 | } 115 | 116 | // timer-gc to close timeout socket 117 | // deadlock-review conn.timeLock.Lock 118 | func (conn *ConnDriver) SetWriteDeadline(time time.Time) (err error) { 119 | conn.timeLock.Lock() 120 | if !conn.closeByTimerGC { 121 | if time.After(conn.writeDeadline) { 122 | conn.writeDeadline = time 123 | } 124 | } else { 125 | err = ErrNetTimerGCArrive 126 | } 127 | conn.timeLock.Unlock() 128 | return 129 | } 130 | 131 | // deadlock-review conn.timeLock.RLock 132 | func (conn *ConnDriver) isCloseByGCTimer() bool { 133 | conn.timeLock.RLock() 134 | isLocked := conn.closeByTimerGC 135 | conn.timeLock.RUnlock() 136 | return isLocked 137 | } 138 | 139 | func (conn *ConnDriver) ReadRequestHeader(reqHeader *RequestHeader) error { 140 | return conn.dec.Decode(reqHeader) 141 | } 142 | 143 | func (conn *ConnDriver) ReadRequestBody(body interface{}) error { 144 | return conn.dec.Decode(body) 145 | } 146 | 147 | func (conn *ConnDriver) WriteResponseHeader(respHeader *ResponseHeader) error { 148 | return conn.enc.Encode(respHeader) 149 | } 150 | 151 | func (conn *ConnDriver) WriteResponseBody(body interface{}) error { 152 | return conn.enc.Encode(body) 153 | } 154 | 155 | func (conn *ConnDriver) WriteRequestHeader(ReqHeader *RequestHeader) error { 156 | return conn.enc.Encode(ReqHeader) 157 | } 158 | 159 | func (conn *ConnDriver) WriteRequestBody(body interface{}) error { 160 | return conn.enc.Encode(body) 161 | } 162 | 163 | func (conn *ConnDriver) FlushWriteToNet() error { 164 | return conn.writeBuf.Flush() 165 | } 166 | 167 | func (conn *ConnDriver) ReadResponseHeader(RespHeader *ResponseHeader) error { 168 | return conn.dec.Decode(RespHeader) 169 | } 170 | 171 | func (conn *ConnDriver) ReadResponseBody(body interface{}) error { 172 | return conn.dec.Decode(body) 173 | } 174 | 175 | func (conn *ConnDriver) PendingResponseCount() int { 176 | return len(conn.pendingResponses) 177 | } 178 | func (conn *ConnDriver) AddPendingResponse(pr *PendingResponse) { 179 | conn.pendingResponses[pr.seq] = pr 180 | } 181 | 182 | func (conn *ConnDriver) RemovePendingResponse(seq uint64) *PendingResponse { 183 | if conn.pendingRequests == nil { 184 | return nil 185 | } 186 | if presp, ok := conn.pendingResponses[seq]; ok { 187 | delete(conn.pendingResponses, seq) 188 | return presp 189 | } 190 | return nil 191 | } 192 | 193 | func (conn *ConnDriver) ClearPendingResponses() map[uint64]*PendingResponse { 194 | presps := conn.pendingResponses 195 | conn.pendingResponses = nil 196 | return presps 197 | } 198 | 199 | func (conn *ConnDriver) AddPendingRequest(request *Request) error { 200 | select { 201 | case conn.pendingRequests <- request: 202 | return nil 203 | default: 204 | return ErrPendingRequestFull 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | const ( 8 | ErrTypeCritical = 1 // 0001 9 | ErrTypeLogic = 2 // 0010 10 | ErrTypeCanRetry = 4 // 0100 11 | ErrTypeNet = 8 // 1000 12 | ) 13 | 14 | // client error,error code < 400 15 | var ( 16 | ErrRequestTimeout = &Error{100, ErrTypeLogic, "client request time out"} 17 | ErrNoIdleConn = &Error{101, ErrTypeLogic, "client no dile connection"} 18 | ErrNoWorkingConn = &Error{102, ErrTypeLogic, "client no working connection"} 19 | ErrCallConnectTimeout = &Error{103, ErrTypeLogic, "client no connect timeout"} 20 | ErrIdleClose = &Error{104, ErrTypeLogic, "client idle pool full close"} 21 | ErrUnknow = &Error{107, ErrTypeLogic, ""} 22 | // critical error unexpected error 23 | ErrGobParseErr = &Error{106, ErrTypeCritical, ""} 24 | ErrInvalidAddress = &Error{108, ErrTypeCritical, "client invalid address"} 25 | 26 | ErrNetConnectFail = &Error{109, ErrTypeNet, ""} 27 | ErrNetReadFail = &Error{110, ErrTypeNet, ""} 28 | ErrNetReadDeadlineArrive = &Error{111, ErrTypeNet, ""} 29 | ErrNetWriteDeadlineArrive = &Error{112, ErrTypeNet, ""} 30 | ErrNetTimerGCArrive = &Error{113, ErrTypeNet, ""} 31 | // client can retry once after receiving following errors 32 | ErrPendingWireBroken = &Error{111, ErrTypeCanRetry, ""} 33 | ErrPendingRequestFull = &Error{121, ErrTypeCanRetry, "client pending request full"} 34 | ) 35 | 36 | // server error,error code >= 400 37 | var ( 38 | ErrNotFound = &Error{400, ErrTypeCritical, "server invalid service or method"} 39 | ) 40 | 41 | // user defined error, error code > 10000 42 | type Error struct { 43 | Code int 44 | Type int 45 | Reason string 46 | } 47 | 48 | func NewError(code, typ int, reason string) *Error { 49 | return &Error{code, typ, reason} 50 | } 51 | 52 | //params err can not be a nil of *Error 53 | func (er *Error) SetError(err error) *Error { 54 | e := *er 55 | if err != nil { 56 | e.Reason = err.Error() 57 | } 58 | return &e 59 | } 60 | 61 | func (er *Error) SetReason(err string) *Error { 62 | e := *er 63 | e.Reason = err 64 | return &e 65 | } 66 | 67 | // gob error stop retry 68 | // error type is not ErrTypeCanRetry stop retry 69 | func CanRetry(err *Error) bool { 70 | if err == nil { 71 | return false 72 | } 73 | if len(err.Reason) > 4 && err.Reason[0:4] == "gob:" { 74 | return false 75 | } 76 | return (err.Type & ErrTypeCanRetry) > 0 77 | } 78 | 79 | func (er Error) Error() string { 80 | return fmt.Sprintf("code: %d type: %d reason: %s", er.Code, er.Type, er.Reason) 81 | } 82 | 83 | func (er Error) Errno() int { 84 | return er.Code 85 | } 86 | 87 | func IsRpcError(err interface{}) bool { 88 | switch err.(type) { 89 | case *Error: 90 | return true 91 | case Error: 92 | return true 93 | default: 94 | return false 95 | } 96 | } 97 | 98 | func isNetError(err error) bool { 99 | e := err.Error() 100 | if len(e) >= 4 && e[0:4] == "gob:" { 101 | return false 102 | } 103 | return true 104 | } 105 | -------------------------------------------------------------------------------- /example/clientDemo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | "github.com/johntech-o/gorpc" 6 | "github.com/johntech-o/gorpc/example/data" 7 | "flag" 8 | ) 9 | 10 | var client *gorpc.Client 11 | var A string 12 | 13 | func init() { 14 | netOptions := gorpc.NewNetOptions(time.Second*10, time.Second*20, time.Second*20) 15 | client = gorpc.NewClient(netOptions) 16 | flag.StringVar(&A, "a", "127.0.0.1:6668", "remote address:port") 17 | flag.Parse() 18 | } 19 | 20 | func main() { 21 | var strReturn string 22 | err := client.CallWithAddress(A, "TestRpcABC", "EchoStruct", data.TestRpcABC{"aaa", "bbb", "ccc"}, &strReturn) 23 | if err != nil { 24 | println(err.Error(),err.Errno()) 25 | } 26 | println(strReturn) 27 | 28 | 29 | var intReturn *int 30 | client.CallWithAddress(A,"TestRpcInt","Update",5,&intReturn) 31 | if err != nil { 32 | println(err.Error(),err.Errno()) 33 | } 34 | println(*intReturn) 35 | 36 | } 37 | -------------------------------------------------------------------------------- /example/data/data.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | const EchoContent = "hello echo struct" 4 | 5 | var variable int32 = 0 6 | 7 | type TestRpcABC struct { 8 | A, B, C string 9 | } 10 | 11 | func (r *TestRpcABC) EchoStruct(arg TestRpcABC, res *string) error { 12 | *res = EchoContent 13 | return nil 14 | } 15 | 16 | 17 | type TestRpcInt struct { 18 | i int 19 | } 20 | 21 | func (r *TestRpcInt) Update(n int, res *int) error { 22 | r.i = n 23 | *res = r.i + 100 24 | return nil 25 | } 26 | 27 | 28 | 29 | 30 | // 31 | -------------------------------------------------------------------------------- /example/serverDemo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/johntech-o/gorpc" 6 | "github.com/johntech-o/gorpc/example/data" 7 | ) 8 | 9 | var L string 10 | 11 | func init() { 12 | flag.StringVar(&L, "l", "127.0.0.1:6668", "remote address:port") 13 | flag.Parse() 14 | } 15 | 16 | func main() { 17 | s := gorpc.NewServer(L) 18 | s.Register(new(data.TestRpcInt)) 19 | s.Register(new(data.TestRpcABC)) 20 | s.Serve() 21 | panic("server fail") 22 | } 23 | -------------------------------------------------------------------------------- /gorpc_test.go: -------------------------------------------------------------------------------- 1 | // go test -v github.com/johntech-o/gorpc 2 | package gorpc 3 | 4 | import ( 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "log" 9 | "net/http" 10 | _ "net/http/pprof" 11 | "runtime" 12 | "sync" 13 | "testing" 14 | "time" 15 | 16 | "github.com/johntech-o/gorpc/utility/pprof" 17 | cal"github.com/johntech-o/gorpc/utility/calculator" 18 | ) 19 | 20 | var client *Client 21 | 22 | const ( 23 | ExecGoroutines = 1000 24 | ExecPerGoroutines = 1000 25 | ) 26 | 27 | func init() { 28 | runtime.GOMAXPROCS(runtime.NumCPU()) 29 | go func() { 30 | log.Println(http.ListenAndServe(":6789", nil)) 31 | }() 32 | time.Sleep(time.Microsecond * 2) 33 | } 34 | 35 | type TestABC struct { 36 | A, B, C string 37 | } 38 | 39 | type TestRpcInt struct { 40 | i int 41 | } 42 | 43 | func (r *TestRpcInt) Update(n int, res *int) error { 44 | r.i = n 45 | *res = r.i + 100 46 | return nil 47 | } 48 | 49 | var callTimes = 0 50 | 51 | func (r *TestRpcInt) ReturnErr(n int, res *int) error { 52 | *res = 100 53 | if n == 1 { 54 | if callTimes == 0 { 55 | callTimes++ 56 | return &Error{10000, ErrTypeCanRetry, "user defined retry error"} 57 | } else { 58 | return Error{100001, ErrTypeLogic, "after retry user logic error"} 59 | } 60 | } 61 | return errors.New("user defined common error") 62 | 63 | } 64 | 65 | const EchoContent = "hello echo struct" 66 | 67 | func (r *TestRpcInt) EchoStruct(arg TestABC, res *string) error { 68 | *res = EchoContent 69 | return nil 70 | } 71 | 72 | var StopClient2 = make(chan struct{}) 73 | var MaxQps uint64 74 | 75 | func TestStartServerClient(t *testing.T) { 76 | go func() { 77 | s := NewServer("127.0.0.1:6668") 78 | s.Register(new(TestRpcInt)) 79 | s.Serve() 80 | panic("server fail") 81 | }() 82 | time.Sleep(time.Millisecond * 2) 83 | netOptions := NewNetOptions(time.Second*10, time.Second*20, time.Second*20) 84 | // client to ben test server 85 | client = NewClient(netOptions) 86 | 87 | // client2 to get go gorpc status 88 | client2 := NewClient(netOptions) 89 | go func() { 90 | timer := time.NewTicker(time.Second) 91 | defer timer.Stop() 92 | for { 93 | select { 94 | case <-StopClient2: 95 | return 96 | case <-timer.C: 97 | var reply string 98 | var err *Error 99 | 100 | err = client2.CallWithAddress("127.0.0.1:6668", "RpcStatus", "CallStatus", false, &reply) 101 | if err != nil { 102 | fmt.Println("server call amount error: ", err.Error()) 103 | continue 104 | } 105 | var qps = struct { 106 | Result uint64 107 | Errno int 108 | }{} 109 | qpsStr := client.Qps() 110 | if err := json.Unmarshal([]byte(qpsStr), &qps); err != nil { 111 | fmt.Println(err) 112 | } 113 | if qps.Result > MaxQps { 114 | MaxQps = qps.Result 115 | } 116 | fmt.Println("server call status: ", reply) 117 | fmt.Println("client conn status: ", client.ConnsStatus()) 118 | fmt.Println("client conn Qps : ", qpsStr) 119 | 120 | default: 121 | } 122 | } 123 | }() 124 | } 125 | 126 | // common case test fault-tolerant 127 | func TestInvalidParams(t *testing.T) { 128 | var up int 129 | t.Log("test invalid service") 130 | e := client.CallWithAddress("127.0.0.1:6668", "xxxx", "Update", 5, &up) 131 | if e.Errno() == 400 { 132 | t.Log("ok", e.Error()) 133 | } else { 134 | t.Error("fail", e) 135 | } 136 | 137 | t.Log("test invalid method") 138 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "xxxx", 5, &up) 139 | if e.Errno() == 400 { 140 | t.Log("ok", e.Error()) 141 | } else { 142 | t.Error("fail", e) 143 | } 144 | 145 | t.Log("test invalid args") 146 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "Update", "5", &up) 147 | if e.Errno() == 400 { 148 | t.Log("ok", e.Error()) 149 | } else { 150 | t.Error("fail", e) 151 | } 152 | 153 | t.Log("test invalid reply") 154 | var upStr string 155 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "Update", 5, &upStr) 156 | if e.Errno() == 106 { 157 | t.Log("ok", e.Error()) 158 | } else { 159 | t.Error("fail", e) 160 | } 161 | 162 | t.Log("test normal update") 163 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "Update", 5, &up) 164 | if e != nil { 165 | t.Error("fail", e, up) 166 | } else { 167 | if up == 105 { 168 | t.Log("ok", up, e) 169 | } 170 | } 171 | 172 | var res int 173 | t.Log("test remote return can retry error") 174 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "ReturnErr", 1, &res) 175 | if e.Errno() == 100001 { 176 | t.Log("ok", e.Error()) 177 | } else { 178 | t.Error("fail", e) 179 | } 180 | t.Log("test remote return error") 181 | e = client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "ReturnErr", 2, &res) 182 | if e.Errno() == 500 { 183 | t.Log("ok", e.Error()) 184 | } else { 185 | t.Error("fail", e) 186 | } 187 | } 188 | 189 | func TestEchoStruct(t *testing.T) { 190 | 191 | var results = struct { 192 | content map[string]int 193 | sync.Mutex 194 | }{content: make(map[string]int, 1000)} 195 | 196 | var counter = cal.NewCallCalculator() 197 | var wgCreate sync.WaitGroup 198 | var wgFinish sync.WaitGroup 199 | var startRequestCh = make(chan struct{}) 200 | for i := 0; i < ExecGoroutines; i++ { 201 | wgCreate.Add(1) 202 | go func() { 203 | wgCreate.Done() 204 | wgFinish.Add(1) 205 | defer wgFinish.Done() 206 | <-startRequestCh 207 | for i := 0; i < ExecPerGoroutines; i++ { 208 | var res string 209 | id := counter.Start() 210 | err := client.CallWithAddress("127.0.0.1:6668", "TestRpcInt", "EchoStruct", TestABC{"aaa", "bbb", "ccc"}, &res) 211 | counter.End(id) 212 | if err != nil { 213 | results.Lock() 214 | results.content[err.Error()] += 1 215 | results.Unlock() 216 | continue 217 | } 218 | results.Lock() 219 | results.content[res] += 1 220 | results.Unlock() 221 | } 222 | 223 | }() 224 | } 225 | wgCreate.Wait() 226 | // pprof result 227 | pprof.MemStats() 228 | // start to send request 229 | close(startRequestCh) 230 | wgFinish.Wait() 231 | close(StopClient2) 232 | pprof.MemStats() 233 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc, pprof.PauseTotalMs, pprof.NumGC) 234 | 235 | // output rpc result 236 | if len(results.content) > 1 { 237 | t.Error("have failed call") 238 | } 239 | for result, count := range results.content { 240 | t.Logf("TestEchoStruct result: %s ,count: %d \n", result, count) 241 | } 242 | // client request result 243 | counter.Summary() 244 | fmt.Printf("Max Client Qps: %d \n", MaxQps) 245 | time.Sleep(time.Microsecond) 246 | } 247 | -------------------------------------------------------------------------------- /labs/address_test.go: -------------------------------------------------------------------------------- 1 | package labs 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | /* 9 | used in timewheel 10 | a is 0x0, &a is 0xc8200121a0 11 | s is 0x0, &s is 0xc82002c008 12 | a[0] is 0xc82002c018, &a[0] is 0xc82008a000 13 | a[1] is 0xc82002c028, &a[1] is 0xc82008a008 14 | a[2] is 0xc82002c030, &a[2] is 0xc82008a010 15 | a[3] is 0xc82002c038, &a[3] is 0xc82008a018 16 | s is 0xc82002c038, &s is 0xc82002c008 17 | a[3].C is 0xc82001a180, &(a[3].C) is 0xc82002c038 18 | a[3] is 0xc82002c070, &a[3] is 0xc82008a018 19 | s is 0xc82002c038, &s is 0xc82002c008 20 | a[3].C is 0xc82001a420, &(a[3].C) is 0xc82002c070 21 | */ 22 | func TestChanAssign(t *testing.T) { 23 | type Time struct { 24 | C <-chan struct{} 25 | } 26 | var a []*Time 27 | var s *Time 28 | fmt.Printf("a is %p, &a is %p\n", a, &a) 29 | fmt.Printf("s is %p, &s is %p\n", s, &s) 30 | for i := 0; i < 10; i++ { 31 | a = append(a, &Time{C: make(chan struct{}, 1)}) 32 | } 33 | s = a[3] 34 | fmt.Printf("a[0] is %p, &a[0] is %p\n", a[0], &a[0]) 35 | fmt.Printf("a[1] is %p, &a[1] is %p\n", a[1], &a[1]) 36 | fmt.Printf("a[2] is %p, &a[2] is %p\n", a[2], &a[2]) 37 | fmt.Printf("a[3] is %p, &a[3] is %p\n", a[3], &a[3]) 38 | fmt.Printf("s is %p, &s is %p\n", s, &s) 39 | fmt.Printf("a[3].C is %p, &(a[3].C) is %p\n", a[3].C, &(a[3].C)) 40 | a[3] = &Time{C: make(chan struct{}, 1)} 41 | fmt.Printf("a[3] is %p, &a[3] is %p\n", a[3], &a[3]) 42 | fmt.Printf("s is %p, &s is %p\n", s, &s) 43 | fmt.Printf("a[3].C is %p, &(a[3].C) is %p\n", a[3].C, &(a[3].C)) 44 | 45 | } 46 | 47 | func TestChanClose(t *testing.T) { 48 | var b <-chan struct{} 49 | a := make(chan struct{}, 1) 50 | b = a 51 | // close(b) // can not be closed 52 | close(a) 53 | if _, ok := <-b; !ok { 54 | t.Log("b has been closed") 55 | } 56 | fmt.Println(a, b) 57 | } 58 | -------------------------------------------------------------------------------- /labs/newObject_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "testing" 7 | "time" 8 | 9 | "github.com/johntech-o/gorpc/utility/pprof" 10 | ) 11 | 12 | // new object in goroutines then view gc statistics 13 | 14 | type Object struct { 15 | a string 16 | b int 17 | c map[string]string 18 | d interface{} 19 | e *string 20 | f []string 21 | next *Object 22 | } 23 | 24 | type objectPool struct { 25 | head *Object 26 | count int 27 | sync.Mutex 28 | } 29 | 30 | var freeObjects objectPool 31 | 32 | func newObjectInHeap() *Object { 33 | return &Object{} 34 | } 35 | 36 | func lenPool() int { 37 | freeObjects.Lock() 38 | count := freeObjects.count 39 | freeObjects.Unlock() 40 | return count 41 | } 42 | 43 | func newObjectFromPool() *Object { 44 | freeObjects.Lock() 45 | ob := freeObjects.head 46 | if ob == nil { 47 | ob = &Object{} 48 | } else { 49 | freeObjects.head = freeObjects.head.next 50 | freeObjects.count -= 1 51 | // *ob = Object{} 52 | } 53 | freeObjects.Unlock() 54 | return ob 55 | } 56 | 57 | func freeObject(ob *Object) { 58 | freeObjects.Lock() 59 | ob.next = freeObjects.head 60 | freeObjects.head = ob 61 | freeObjects.count += 1 62 | freeObjects.Unlock() 63 | } 64 | 65 | // list amount of goroutines and memory occupied 66 | func TestGoroutineCost(t *testing.T) { 67 | amount := 100000 68 | pprof.MemStats() 69 | fmt.Println(pprof.Current()) 70 | fmt.Printf("create goroutines amount: %d\n", amount) 71 | var wg sync.WaitGroup 72 | for i := 0; i < amount; i++ { 73 | wg.Add(1) 74 | go func() { 75 | defer wg.Done() 76 | time.Sleep(time.Second * 10) 77 | }() 78 | } 79 | wg.Wait() 80 | fmt.Println(pprof.Current()) 81 | pprof.ProcessStats() 82 | 83 | pprof.MemStats() 84 | pprof.StatIncrement(pprof.TotalAlloc) 85 | fmt.Print("\n\n") 86 | } 87 | 88 | // test malloc in heap or stack function 89 | func TestIfObjectInStack(t *testing.T) { 90 | amount := 10000 91 | for i := 0; i < amount; i++ { 92 | ob := Object{} 93 | if ob.a == "aaa" { 94 | ob.a = "bbb" 95 | } 96 | freeObject(&ob) 97 | } 98 | fmt.Println(lenPool()) 99 | 100 | ob := &Object{} 101 | fmt.Println("----------------expect malloc in heap--------------") 102 | pprof.MemStats() 103 | 104 | for i := 0; i < amount; i++ { 105 | s := expectMallocInHeap(ob) 106 | // never hit this statment use s to prevent compile optimize code 107 | if s.a == "aaa" { 108 | fmt.Println("sssss") 109 | } 110 | } 111 | 112 | pprof.MemStats() 113 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc) 114 | fmt.Println("-------------- expect malloc in stack--------------") 115 | pprof.MemStats() 116 | 117 | for i := 0; i < amount; i++ { 118 | expectMallocInStack(ob) 119 | } 120 | pprof.MemStats() 121 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc) 122 | pprof.ProcessStats() 123 | 124 | fmt.Println("----------------expect malloc in mem pool--------------") 125 | pprof.MemStats() 126 | for i := 0; i < amount; i++ { 127 | s := newObjectFromPool() 128 | // never hit this statment use s to prevent compile optimize code 129 | if s.a == "aaa" { 130 | fmt.Println("sssss") 131 | } 132 | freeObject(s) 133 | } 134 | pprof.MemStats() 135 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc) 136 | fmt.Print("\n\n") 137 | } 138 | 139 | // test malloc use object pool 140 | func TestObjectPool(t *testing.T) { 141 | time.Sleep(time.Second) 142 | amount := 1000 143 | var wg sync.WaitGroup 144 | for i := 0; i < 5; i++ { 145 | fmt.Println("--------------malloc object without object pool-------------") 146 | pprof.MemStats() 147 | for i := 0; i < amount; i++ { 148 | wg.Add(1) 149 | go func() { 150 | defer wg.Done() 151 | for i := 0; i < 1000; i++ { 152 | ob := new(Object) 153 | if ob.a == "aaa" { 154 | ob.a = "bbb" 155 | } 156 | } 157 | 158 | }() 159 | 160 | } 161 | wg.Wait() 162 | pprof.MemStats() 163 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc) 164 | } 165 | for i := 0; i < 5; i++ { 166 | fmt.Println("----------------malloc object use object pool------------ ") 167 | pprof.MemStats() 168 | for i := 0; i < amount; i++ { 169 | wg.Add(1) 170 | go func() { 171 | defer wg.Done() 172 | for i := 0; i < 1000; i++ { 173 | ob := newObjectFromPool() 174 | if ob.a == "aaa" { 175 | ob.a = "bbb" 176 | } 177 | freeObject(ob) 178 | } 179 | 180 | }() 181 | } 182 | wg.Wait() 183 | pprof.MemStats() 184 | pprof.StatIncrement(pprof.HeapObjects, pprof.TotalAlloc) 185 | } 186 | fmt.Println("\n\n") 187 | } 188 | 189 | // malloc a object in heap 190 | func expectMallocInHeap(ob *Object) *Object { 191 | new := &Object{} 192 | return new 193 | } 194 | 195 | // malloc a object in stack 196 | func expectMallocInStack(ob *Object) *Object { 197 | *ob = Object{} 198 | // _ = make([]Object, 1000) 199 | return ob 200 | } 201 | -------------------------------------------------------------------------------- /labs/writeblock.go: -------------------------------------------------------------------------------- 1 | // 测试写阻塞情况下的现象 2.6.32-220.4.2.el6.x86_64 系统参数为为默认情况 2 | // linux下,client写入,最大packet大小在1024~16384,服务端最后确认的包大小65536~65793以内,可以认为是64k,有点零头不看源码估计是不知道原因了.. 3 | // 客户端写入大小为65536左右的时候阻塞,此后每6s发送一个空包,服务端返回ack包,但win大小为0,120s时候结束整个过程 4 | // 服务端会返回rst包,client端返回connection reset by peer 5 | // 写入大小是1k的时候,传输过程中,packet大小都有1024~16384情况,但如果一次写入出现16384后会递减回每次传输1024。写入大小是400k时候,每次都会写入16384的包 6 | // 设置setWriteBuffer时候,可以写入更多数据,但实际上是缓存在客户端的,有个问题是,你设置大小和缓存是两倍关系,设置64k,实际可以写入的是 64k+64k*2 7 | // 设置为32k,写入的大小为64k + 32k*2 其中固定的64k是对方已经确认的大小,应该已经在缓冲区去除了. 但这个值最小是12k,也就是你设置为小于12k的数字,比如设置为0,实际上 8 | // 没有效果,最终写入大小大概是 64k + 12k * 2 = 88k 在我的服务器上设置为大于12k以上会增加缓冲效果 9 | // 以上测试都是使用默认的服务端readbuffer情况 10 | // 如果设置了服务端的readbuffer size,抓包看服务端buffer缓存了更多数据,但这个数值和设置大小也是略有出入,如果不设置默认是64KB,设置为42K服务端会缓存64K, 11 | // 设置超过128KB,服务端缓冲大概是242K不变 12 | 13 | package main 14 | 15 | import ( 16 | "crypto/rand" 17 | "fmt" 18 | "net" 19 | "runtime" 20 | "time" 21 | ) 22 | 23 | const ( 24 | KB = 1024 25 | WRITE_BLOCK = 16 * KB 26 | WRITE_BUF_SIZE = 32 * KB 27 | READ_BUF_SIZE = 128 * KB 28 | ) 29 | 30 | const ( 31 | SERVER_ADDR = "127.0.0.1:10240" 32 | ) 33 | 34 | func init() { 35 | fmt.Println("cpu num is: ", runtime.NumCPU()) 36 | runtime.GOMAXPROCS(runtime.NumCPU()) 37 | } 38 | 39 | func main() { 40 | clientStartNotify := make(chan struct{}) 41 | go server(clientStartNotify) 42 | <-clientStartNotify 43 | go client() 44 | select {} 45 | } 46 | 47 | func client() { 48 | addr, err := net.ResolveTCPAddr("tcp", SERVER_ADDR) 49 | if err != nil { 50 | panic(err) 51 | } 52 | conn, err := net.DialTCP("tcp", nil, addr) 53 | conn.SetWriteBuffer(WRITE_BUF_SIZE) 54 | if err != nil { 55 | panic(err) 56 | } 57 | b := make([]byte, WRITE_BLOCK) 58 | if _, err := rand.Read(b); err != nil { 59 | panic(err) 60 | } 61 | amount := 0 62 | for i := 1; ; i++ { 63 | n, e := conn.Write(b) 64 | if e != nil { 65 | fmt.Println("client write error: ", e) 66 | break 67 | } 68 | amount += n 69 | fmt.Println("write amount KB: ", amount/KB, amount) 70 | // time.Sleep(time.Millisecond) 71 | } 72 | fmt.Println("client exit") 73 | } 74 | 75 | func server(notify chan struct{}) { 76 | addr, _ := net.ResolveTCPAddr("tcp", SERVER_ADDR) 77 | ln, err := net.ListenTCP("tcp", addr) 78 | if err != nil { 79 | panic(err) 80 | // handle error 81 | } 82 | notify <- struct{}{} 83 | for { 84 | conn, err := ln.AcceptTCP() 85 | conn.SetReadBuffer(0) 86 | if err != nil { 87 | fmt.Println("accept error: ", err) 88 | } 89 | go func(conn *net.TCPConn) { 90 | time.Sleep(time.Second * 5) 91 | conn.SetReadBuffer(READ_BUF_SIZE) 92 | fmt.Println(conn.LocalAddr()) 93 | }(conn) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /memPool/memPool.go: -------------------------------------------------------------------------------- 1 | package memPool 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io" 7 | "math" 8 | "sync/atomic" 9 | "unsafe" 10 | "github.com/johntech-o/gorpc/utility/convert" 11 | ) 12 | 13 | func init() { 14 | fmt.Println("start") 15 | } 16 | 17 | const ( 18 | CLASSMASK, CLASSCLEAN Property = 0xF, 0xFFFFFFF0 19 | ) 20 | 21 | type Property uint32 22 | 23 | func (p Property) index() int { 24 | return int(p & CLASSMASK) 25 | } 26 | 27 | func (p *Property) setIndex(index int) { 28 | *p &= CLASSCLEAN 29 | *p |= Property(index) & CLASSMASK 30 | } 31 | 32 | type stat struct { 33 | count int64 34 | } 35 | 36 | func (s *stat) incre() { 37 | atomic.AddInt64(&s.count, 1) 38 | } 39 | 40 | func (s *stat) decre() { 41 | atomic.AddInt64(&s.count, -1) 42 | } 43 | 44 | func (s *stat) load() int64 { 45 | return atomic.LoadInt64(&s.count) 46 | } 47 | 48 | type MemPool struct { 49 | baseSize int 50 | classAmount int 51 | bufLists []unsafe.Pointer 52 | bufStat []stat 53 | missStat int64 54 | } 55 | 56 | func New(baseSize int, classAmount int) *MemPool { 57 | if classAmount < 1 { 58 | panic("new MemMool invalid params ") 59 | } 60 | mp := MemPool{ 61 | baseSize: baseSize, 62 | classAmount: classAmount, 63 | bufLists: make([]unsafe.Pointer, classAmount), 64 | bufStat: make([]stat, classAmount), 65 | } 66 | return &mp 67 | } 68 | 69 | // @todo verify size limit 70 | func (mp *MemPool) ChunkSize(index int) int { 71 | return mp.baseSize * (index + 1) 72 | } 73 | 74 | func (mp *MemPool) MaxChunkSize() int { 75 | return mp.baseSize * mp.classAmount 76 | } 77 | 78 | func (mp *MemPool) Malloc(length int) *ElasticBuf { 79 | if length <= mp.baseSize { 80 | return mp.popFromList(0) 81 | } 82 | index := math.Ceil(float64(length)/float64(mp.baseSize)) - 1 83 | if length > mp.MaxChunkSize() { 84 | // println("exceed max chunk size") 85 | atomic.AddInt64(&mp.missStat, 1) 86 | return NewElasticBuf(int(index), mp) 87 | } 88 | fmt.Println("malloc lenght is ", length, "index is ", index) 89 | return mp.popFromList(int(index)) 90 | } 91 | 92 | // @todo caculate miss or hit statistics 93 | func (mp *MemPool) popFromList(index int) *ElasticBuf { 94 | bufList := &mp.bufLists[index] 95 | // fmt.Printf("pop bufList %p mp.bufList[index] %p \n", bufList, &mp.bufLists[index]) 96 | var ptr unsafe.Pointer 97 | for { 98 | ptr = atomic.LoadPointer(bufList) 99 | if ptr == nil { 100 | // fmt.Println("pop ptr is nil index is ", index) 101 | goto final 102 | } 103 | // fmt.Printf("pop load bufList ptr %p", ptr) 104 | if atomic.CompareAndSwapPointer(bufList, ptr, ((*ElasticBuf)(ptr)).next) { 105 | // fmt.Println("pop malloc from pool success") 106 | mp.bufStat[index].decre() 107 | goto final 108 | } 109 | } 110 | final: 111 | if ptr == nil { 112 | p := NewElasticBuf(index, mp) 113 | // fmt.Printf("pop ptr==nil make a new buf pointer address %p, buf address %p\n", &p, p) 114 | return p 115 | } 116 | return (*ElasticBuf)(ptr) 117 | } 118 | 119 | func (mp *MemPool) Free(buf *ElasticBuf) { 120 | index := buf.Index() 121 | if index >= mp.classAmount { 122 | // fmt.Println("memPool free discard index is ", index) 123 | return 124 | } 125 | bufList := &mp.bufLists[index] 126 | buf.Reset() 127 | for { 128 | buf.next = atomic.LoadPointer(bufList) 129 | if atomic.CompareAndSwapPointer(bufList, buf.next, unsafe.Pointer(buf)) { 130 | // fmt.Printf("free bufList %p mp.bufList[index] %p unsafe.Pointer buf %p \n", bufList, &mp.bufLists[index], unsafe.Pointer(buf)) 131 | mp.bufStat[index].incre() 132 | break 133 | } 134 | } 135 | return 136 | } 137 | 138 | func (mp *MemPool) Status(output bool) (s []int64) { 139 | for _, stat := range mp.bufStat { 140 | count := stat.load() 141 | s = append(s, count) 142 | } 143 | s = append(s, atomic.LoadInt64(&mp.missStat)) 144 | if output { 145 | for index, result := range s { 146 | if index == len(s)-1 { 147 | fmt.Printf("chunk stat missed %d, count %d\n", index, result) 148 | continue 149 | } 150 | fmt.Printf("chunk stat index %d, count %d\n", index, result) 151 | 152 | } 153 | } 154 | return s 155 | } 156 | 157 | type ElasticBuf struct { 158 | buf []byte 159 | writePos int 160 | property Property 161 | pp *MemPool 162 | next unsafe.Pointer 163 | } 164 | 165 | func (eb *ElasticBuf) Reset() { 166 | eb.writePos = 0 167 | eb.next = nil 168 | } 169 | 170 | func (eb *ElasticBuf) free() { 171 | eb.pp.Free(eb) 172 | } 173 | 174 | func NewElasticBuf(index int, pool *MemPool) *ElasticBuf { 175 | buf := ElasticBuf{buf: make([]byte, pool.ChunkSize(index)), pp: pool} 176 | buf.SetIndex(index) 177 | return &buf 178 | } 179 | 180 | // ElasticBuffer index in pool 181 | func (eb ElasticBuf) Index() int { 182 | return eb.property.index() 183 | } 184 | 185 | func (eb *ElasticBuf) SetIndex(index int) { 186 | eb.property.setIndex(index) 187 | } 188 | 189 | func (eb *ElasticBuf) ReadInt16(reader io.Reader, order convert.ByteOrder) (int16, error) { 190 | tmpBuf, err := eb.ReadBytes(reader, 2) 191 | if err != nil { 192 | return 0, err 193 | } 194 | return convert.StreamToInt16(tmpBuf, order), nil 195 | } 196 | 197 | func (eb *ElasticBuf) ReadInt32(reader io.Reader, order convert.ByteOrder) (int32, error) { 198 | tmpBuf, err := eb.ReadBytes(reader, 4) 199 | if err != nil { 200 | return 0, err 201 | } 202 | return convert.StreamToInt32(tmpBuf, order), nil 203 | } 204 | 205 | // the slice returned by this function is valid before next read 206 | func (eb *ElasticBuf) ReadBytes(reader io.Reader, n int) ([]byte, error) { 207 | tmpBuf := eb.MallocTmpBytes(n) 208 | c, err := reader.Read(tmpBuf) 209 | if c != n { 210 | return nil, errors.New("read fail to buffer") 211 | } 212 | if err != nil { 213 | return nil, err 214 | } 215 | return tmpBuf, nil 216 | } 217 | 218 | func (eb *ElasticBuf) AppendInt16(v int16, order convert.ByteOrder) error { 219 | buffer := eb.MallocStreamBytes(2) 220 | return convert.Int16ToStreamEx(buffer, v, order) 221 | } 222 | 223 | func (eb *ElasticBuf) AppendInt32(v int32, order convert.ByteOrder) error { 224 | buffer := eb.MallocStreamBytes(4) 225 | return convert.Int32ToStreamEx(buffer, v, order) 226 | } 227 | 228 | func (eb *ElasticBuf) FlushToWriter(writer io.Writer) error { 229 | _, err := writer.Write(eb.buf[0:eb.writePos]) 230 | eb.writePos = 0 231 | return err 232 | } 233 | 234 | // @todo length check and change from global 235 | func (eb *ElasticBuf) MallocStreamBytes(n int) []byte { 236 | start := eb.writePos 237 | eb.writePos += n 238 | return eb.buf[start:eb.writePos] 239 | } 240 | 241 | // @todo length check and change from global 242 | func (eb *ElasticBuf) MallocTmpBytes(n int) []byte { 243 | if cap(eb.buf) < n { 244 | newBuf := eb.pp.Malloc(n) 245 | eb.swapWithOther(newBuf) 246 | } 247 | return eb.buf[0:n] 248 | } 249 | 250 | func (eb *ElasticBuf) swapWithOther(newBuf *ElasticBuf) { 251 | tmpIndex := newBuf.Index() 252 | tmpBuf := newBuf.buf 253 | newBuf.SetIndex(eb.Index()) 254 | newBuf.buf = eb.buf 255 | eb.SetIndex(tmpIndex) 256 | eb.buf = tmpBuf 257 | newBuf.free() 258 | return 259 | } 260 | -------------------------------------------------------------------------------- /memPool/memPool_test.go: -------------------------------------------------------------------------------- 1 | package memPool 2 | 3 | import ( 4 | "bytes" 5 | "crypto/rand" 6 | "fmt" 7 | "reflect" 8 | "runtime" 9 | "testing" 10 | ) 11 | 12 | func init() { 13 | runtime.GOMAXPROCS(runtime.NumCPU()) 14 | } 15 | 16 | // 测试给elasticBuf设置属性,获取buf,属性争取 17 | func TestElasticBuf(t *testing.T) { 18 | pool := New(512, 5) 19 | buf := NewElasticBuf(3, pool) 20 | assert("", func() (bool, string) { 21 | if buf.Index() == 3 { 22 | return true, "test new elasticBuf success" 23 | } 24 | return false, "set index fail" 25 | }) 26 | } 27 | 28 | func TestSetGet(t *testing.T) { 29 | pool := New(512, 5) 30 | a := pool.Malloc(512) 31 | a.free() 32 | b := pool.Malloc(512) 33 | b.free() 34 | assert("", func() (bool, string) { 35 | if a == b { 36 | return true, "a == b success" 37 | } 38 | return false, "not use buffer in pool fail" 39 | }) 40 | return 41 | } 42 | 43 | func TestMallocFree(t *testing.T) { 44 | pool := New(512, 5) 45 | a := pool.Malloc(512 - 1) 46 | a.free() 47 | a1 := pool.Malloc(512) 48 | a1.free() 49 | b := pool.Malloc(512 * 2) 50 | b.free() 51 | b1 := pool.Malloc(512*2 + 1) 52 | b1.free() 53 | c := pool.Malloc(512*3 + 1) 54 | c.free() 55 | d := pool.Malloc(512 * 4) 56 | d.free() 57 | e := pool.Malloc(512 * 5) 58 | e.free() 59 | f := pool.Malloc(512*5 + 1) 60 | f.free() 61 | res := []int64{2, 1, 1, 2, 1, 1} 62 | assert("", func() (bool, string) { 63 | for index, count := range pool.Status(true) { 64 | if res[index] != count { 65 | return false, "malloc free error not expected result" 66 | } 67 | } 68 | return true, "" 69 | }) 70 | } 71 | 72 | // 测试申请一个buf,但使用的时候buf不够用,自动从底层扩展这个buf,再次申请扩展的buf,使用的是之前底层申请的buf 73 | func TestSwapCurrentBuf(t *testing.T) { 74 | pool := New(512, 5) 75 | buf1 := pool.Malloc(512) 76 | readerBuf := make([]byte, 512*2) 77 | rand.Read(readerBuf) 78 | reader := bytes.NewBuffer(readerBuf) 79 | buf1.ReadBytes(reader, 512*2) 80 | buf1.free() 81 | buf2 := pool.Malloc(512 * 2) 82 | buf2.free() 83 | assert("", func() (bool, string) { 84 | if buf1 == buf2 { 85 | return true, "buf1== buf2 success" 86 | } 87 | return false, "buf1 != buf2 fail" 88 | }) 89 | } 90 | 91 | // monitor the gc trace time 92 | // GODEBUG=gctrace=1 GOGC=20000 ./make 1.4 93 | func TestGCEnableMemBuf(t *testing.T) { 94 | pool := New(512, 5) 95 | ebs := map[int]*ElasticBuf{} 96 | MallocEnablePool(ebs, pool) 97 | MallocEnablePool(ebs, pool) 98 | for i := 0; i <= 50000; i++ { 99 | ebs[i] = pool.Malloc(512) 100 | } 101 | ebs = nil 102 | runtime.GC() 103 | runtime.GC() 104 | pool.Status(true) 105 | // fmt.Println("in use object is", (&runtime.MemProfileRecord).InUseObjects()) 106 | } 107 | 108 | // func TestGCDisableMemBuf(t *testing.T) { 109 | // pool := New(512, 5) 110 | // ebs := map[int]*ElasticBuf{} 111 | // MallocDisablePool(ebs, pool) 112 | // MallocDisablePool(ebs, pool) 113 | // pool.Status(true) 114 | // } 115 | 116 | func MallocEnablePool(ebs map[int]*ElasticBuf, pool *MemPool) { 117 | runtime.GC() 118 | runtime.GC() 119 | pprof() 120 | for i := 0; i <= 100000; i++ { 121 | ebs[i] = pool.Malloc(512) 122 | } 123 | for _, b := range ebs { 124 | b.free() 125 | } 126 | runtime.GC() 127 | runtime.GC() 128 | } 129 | 130 | func MallocDisablePool(ebs map[int]*ElasticBuf, pool *MemPool) { 131 | runtime.GC() 132 | runtime.GC() 133 | pprof() 134 | for i := 0; i <= 100000; i++ { 135 | ebs[i] = NewElasticBuf(1, pool) 136 | } 137 | ebs = make(map[int]*ElasticBuf) 138 | runtime.GC() 139 | runtime.GC() 140 | } 141 | 142 | func assert(content string, f func() (bool, string)) { 143 | if content != "" { 144 | fmt.Println(content) 145 | } 146 | isTrue, output := f() 147 | if isTrue { 148 | fmt.Println(output) 149 | return 150 | } 151 | fmt.Errorf("error %s", output) 152 | } 153 | 154 | func pprof() { 155 | var memoryStats runtime.MemStats 156 | runtime.ReadMemStats(&memoryStats) 157 | s := reflect.ValueOf(memoryStats) 158 | filterFields := map[string]struct{}{ 159 | // "PauseNs": struct{}{}, 160 | "PauseEnd": struct{}{}, 161 | "BySize": struct{}{}, 162 | "Lookups": struct{}{}, 163 | "Sys": struct{}{}, 164 | "LastGC": struct{}{}, 165 | "NextGC": struct{}{}, 166 | "OtherSys": struct{}{}, 167 | "Alloc": struct{}{}, 168 | "StackSys": struct{}{}, 169 | "HeapReleased": struct{}{}, 170 | "MSpanInuse": struct{}{}, 171 | "MCacheSys": struct{}{}, 172 | "MCacheInuse": struct{}{}, 173 | "BuckHashSys": struct{}{}, 174 | "GCSys": struct{}{}, 175 | "EnableGC": struct{}{}, 176 | "DebugGC": struct{}{}, 177 | "HeapSys": struct{}{}, 178 | "HeapIdle": struct{}{}, 179 | "HeapInuse": struct{}{}, 180 | "StackInuse": struct{}{}, 181 | "MSpanSys": struct{}{}, 182 | "HeapAlloc": struct{}{}, 183 | } 184 | typeOfT := s.Type() 185 | for i := 0; i < s.NumField(); i++ { 186 | f := s.Field(i) 187 | if _, ok := filterFields[typeOfT.Field(i).Name]; ok { 188 | continue 189 | } 190 | fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) 191 | } 192 | return 193 | } 194 | 195 | // type myType struct { 196 | // a int 197 | // b []byte 198 | // } 199 | 200 | // func (mt *myType) Set(n int) { 201 | // fmt.Printf("in set pointer address %p src address %p src []byte pointer address %p src []byte address %p\n", &mt, mt, &mt.b, mt.b) 202 | // mt.a = n 203 | // t := myType{a: 3, b: make([]byte, 10)} 204 | // fmt.Printf("in set new object myType address %p []byte pointer address %p []byte address %p\n", &t, &t.b, t.b) 205 | // mt.a = t.a 206 | // mt.b = t.b 207 | // fmt.Printf("in set after swap pointer address %p src address %p src []byte pointer address %p src []byte address %p\n", &mt, mt, &mt.b, mt.b) 208 | // } 209 | 210 | // func TestSwap(t *testing.T) { 211 | // var p *myType = &myType{a: 0, b: make([]byte, 10)} 212 | // fmt.Printf("src pointer address %p src address %p src []byte pointer address %p src []byte address %p\n", &p, p, &p.b, p.b) 213 | // p.b = make([]byte, 1000) 214 | // fmt.Printf("src pointer address %p src address %p src []byte pointer address %p src []byte address %p\n", &p, p, &p.b, p.b) 215 | // p.Set(5) 216 | // fmt.Printf("src pointer address %p src address %p src []byte pointer address %p src []byte address %p\n", &p, p, &p.b, p.b) 217 | // } 218 | -------------------------------------------------------------------------------- /monitor.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "encoding/json" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | type ClientStatus struct { 10 | idleAmount uint64 11 | workingAmount uint64 12 | creatingAmount uint64 13 | readAmount uint64 14 | } 15 | 16 | func (cs *ClientStatus) IncreReadAmount() { 17 | atomic.AddUint64(&cs.readAmount, 1) 18 | } 19 | 20 | func (cs *ClientStatus) ReadAmount() uint64 { 21 | return atomic.LoadUint64(&cs.readAmount) 22 | } 23 | 24 | type ServerStatus struct { 25 | CallAmount uint64 26 | WriteAmount uint64 27 | ErrorAmount uint64 28 | ReadBytes uint64 29 | WriteBytes uint64 30 | } 31 | 32 | type ServerStatusPerSecond struct { 33 | Result map[string]uint64 34 | Errno int 35 | } 36 | 37 | func (ss *ServerStatus) IncrCallAmount() { 38 | atomic.AddUint64(&ss.CallAmount, 1) 39 | } 40 | 41 | func (ss *ServerStatus) IncrErrorAmount() { 42 | atomic.AddUint64(&ss.ErrorAmount, 1) 43 | } 44 | 45 | func (ss *ServerStatus) IncrReadBytes(bytes uint64) { 46 | atomic.AddUint64(&ss.ReadBytes, bytes) 47 | } 48 | 49 | func (ss *ServerStatus) IncrWriteBytes(bytes uint64) { 50 | atomic.AddUint64(&ss.WriteBytes, bytes) 51 | } 52 | 53 | // detail calculate tx,rx,qps/s 54 | func (ss *ServerStatus) String() string { 55 | status := ss.Status() 56 | result, err := json.Marshal(status) 57 | if err != nil { 58 | return `{"Result":"inner error marshal error ` + err.Error() + `","Errno":500}` 59 | } 60 | return string(result) 61 | } 62 | 63 | // detail calculate tx,rx,qps/s 64 | func (ss *ServerStatus) Status() *ServerStatusPerSecond { 65 | var status = ServerStatusPerSecond{Result: make(map[string]uint64)} 66 | 67 | callAmount := atomic.LoadUint64(&ss.CallAmount) 68 | errAmount := atomic.LoadUint64(&ss.ErrorAmount) 69 | readBytes := atomic.LoadUint64(&ss.ReadBytes) 70 | writeBytes := atomic.LoadUint64(&ss.WriteBytes) 71 | time.Sleep(time.Second) 72 | status.Result["CallAmount"] = atomic.LoadUint64(&ss.CallAmount) 73 | status.Result["ErrorAmount"] = atomic.LoadUint64(&ss.ErrorAmount) 74 | status.Result["ReadBytes"] = atomic.LoadUint64(&ss.ReadBytes) 75 | status.Result["WriteBytes"] = atomic.LoadUint64(&ss.WriteBytes) 76 | status.Result["Call/s"] = status.Result["CallAmount"] - callAmount 77 | status.Result["Err/s"] = status.Result["ErrorAmount"] - errAmount 78 | status.Result["ReadBytes/s"] = status.Result["ReadBytes"] - readBytes 79 | status.Result["WriteBytes/s"] = status.Result["WriteBytes"] - writeBytes 80 | return &status 81 | } 82 | -------------------------------------------------------------------------------- /request.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | ) 7 | 8 | const ( 9 | RequestSendOnly int16 = 1 10 | MaxPendingRequest int = 500 11 | ) 12 | 13 | type Request struct { 14 | header *RequestHeader 15 | body interface{} 16 | writeTimeout time.Duration 17 | readTimeout time.Duration 18 | pending int32 19 | } 20 | 21 | func NewRequest() *Request { 22 | return &Request{ 23 | pending: 1, 24 | header: NewRequestHeader(), 25 | writeTimeout: DefaultWriteTimeout, 26 | } 27 | } 28 | 29 | func (request *Request) IsPending() bool { 30 | return (atomic.LoadInt32(&request.pending) > 0) 31 | } 32 | 33 | func (request *Request) freePending() { 34 | atomic.AddInt32(&request.pending, -1) 35 | } 36 | 37 | type RequestHeader struct { 38 | Service string 39 | Method string 40 | Seq uint64 41 | CallType int16 42 | } 43 | 44 | func NewRequestHeader() *RequestHeader { 45 | return &RequestHeader{} 46 | } 47 | 48 | func (reqheader *RequestHeader) IsPing() bool { 49 | if reqheader.Service == "go" && reqheader.Method == "p" { 50 | return true 51 | } 52 | return false 53 | } 54 | -------------------------------------------------------------------------------- /response.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | const ( 4 | ReplyTypeData = 0x01 5 | ReplyTypePong = 0x10 6 | ReplyTypeAck = 0x100 7 | ) 8 | 9 | type ResponseHeader struct { 10 | Error *Error 11 | Seq uint64 12 | ReplyType int16 13 | } 14 | 15 | func (respHeader *ResponseHeader) HaveReply() bool { 16 | return (respHeader.ReplyType & ReplyTypeData) > 0 17 | } 18 | 19 | func NewResponseHeader() *ResponseHeader { 20 | return &ResponseHeader{} 21 | } 22 | 23 | type PendingResponse struct { 24 | connId ConnId 25 | seq uint64 26 | reply interface{} 27 | done chan bool 28 | err *Error 29 | } 30 | 31 | func NewPendingResponse() *PendingResponse { 32 | return &PendingResponse{done: make(chan bool, 1)} 33 | } 34 | -------------------------------------------------------------------------------- /server.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "errors" 5 | "log" 6 | "net" 7 | "reflect" 8 | "sync" 9 | "time" 10 | "unicode" 11 | "unicode/utf8" 12 | ) 13 | 14 | var typeOfError = reflect.TypeOf((*error)(nil)).Elem() 15 | 16 | const ( 17 | GobCodec = 1 18 | CmdTypePing = "ping" 19 | CmdTypeErr = "err" 20 | CmdTypeAck = "ack" 21 | ) 22 | 23 | const ( 24 | TimerPoolSize = 10 25 | ) 26 | 27 | type connsMap struct { 28 | conns map[ConnId]*ConnDriver 29 | sync.RWMutex 30 | } 31 | 32 | type TimerPool [TimerPoolSize]*connsMap 33 | 34 | func NewTimerPool() *TimerPool { 35 | tp := &TimerPool{} 36 | for index, _ := range tp { 37 | tp[index] = &connsMap{conns: make(map[ConnId]*ConnDriver)} 38 | } 39 | return tp 40 | } 41 | 42 | func (tp *TimerPool) AddConn(conn *ConnDriver) { 43 | index := conn.connId % TimerPoolSize 44 | tp[index].Lock() 45 | tp[index].conns[conn.connId] = conn 46 | tp[index].Unlock() 47 | 48 | } 49 | 50 | func (tp *TimerPool) RemoveConn(conn *ConnDriver) { 51 | index := conn.connId % TimerPoolSize 52 | tp[index].Lock() 53 | delete(tp[index].conns, conn.connId) 54 | tp[index].Unlock() 55 | 56 | } 57 | 58 | type Server struct { 59 | serviceMap map[string]*service 60 | listener *net.TCPListener 61 | codec int 62 | status *ServerStatus 63 | timerPool *TimerPool 64 | } 65 | 66 | func NewServer(Address string) *Server { 67 | addr, err := net.ResolveTCPAddr("tcp", Address) 68 | if err != nil { 69 | panic("NewServer error:" + err.Error()) 70 | } 71 | listener, err := net.ListenTCP("tcp", addr) 72 | if err != nil { 73 | panic("NewServer error:" + err.Error()) 74 | } 75 | s := &Server{ 76 | serviceMap: make(map[string]*service), 77 | listener: listener, 78 | codec: GobCodec, 79 | status: &ServerStatus{}, 80 | timerPool: NewTimerPool(), 81 | } 82 | s.Register(&RpcStatus{s}) 83 | go s.GCTimer() 84 | return s 85 | } 86 | 87 | func (server *Server) Serve() { 88 | for { 89 | conn, err := server.listener.Accept() 90 | if err != nil { 91 | //log.Print("Serv:", err.Error()) 92 | continue 93 | } 94 | connTcp := conn.(*net.TCPConn) 95 | go server.serveConn(connTcp) 96 | } 97 | } 98 | 99 | func (server *Server) Close() error { 100 | return server.listener.Close() 101 | } 102 | 103 | // serve read write deadline-timer of conn 104 | func (server *Server) serveConn(conn *net.TCPConn) { 105 | rpcConn := NewConnDriver(conn, server) 106 | server.timerPool.AddConn(rpcConn) 107 | server.ServeLoop(rpcConn) 108 | server.timerPool.RemoveConn(rpcConn) 109 | } 110 | 111 | // dead-lock review timerPool.lock -> conn.timeLock 112 | func (server *Server) GCTimer() { 113 | for i := 0; i < TimerPoolSize; i++ { 114 | go func(pool *connsMap) { 115 | connsTimeout := make([]*ConnDriver, 0, 10) 116 | for { 117 | now := time.Now() 118 | pool.Lock() 119 | for _, conn := range pool.conns { 120 | conn.timeLock.RLock() 121 | if conn.readDeadline.Before(now) || conn.writeDeadline.Before(now) { 122 | connsTimeout = append(connsTimeout, conn) 123 | } 124 | conn.timeLock.RUnlock() 125 | } 126 | pool.Unlock() 127 | for _, conn := range connsTimeout { 128 | conn.timeLock.Lock() 129 | conn.closeByTimerGC = true 130 | conn.timeLock.Unlock() 131 | conn.Close() 132 | } 133 | connsTimeout = connsTimeout[0:0] 134 | time.Sleep(DefaultServerTimerGCInterval) 135 | } 136 | 137 | }(server.timerPool[i]) 138 | } 139 | 140 | } 141 | 142 | // serve connection, read request and call service 143 | func (server *Server) ServeLoop(conn *ConnDriver) { 144 | var err error 145 | var methodType *methodType 146 | var service *service 147 | for { 148 | reqHeader := NewRequestHeader() 149 | if err = conn.SetReadDeadline(time.Now().Add(DefaultServerIdleTimeout)); err != nil { 150 | goto fail 151 | } 152 | err = conn.ReadRequestHeader(reqHeader) 153 | if err != nil { 154 | goto fail 155 | } 156 | server.status.IncrCallAmount() 157 | if reqHeader.IsPing() { 158 | server.replyCmd(conn, reqHeader.Seq, nil, CmdTypePing) 159 | continue 160 | } 161 | // verify service and method 162 | service = server.serviceMap[reqHeader.Service] 163 | if service != nil { 164 | methodType = service.method[reqHeader.Method] 165 | } 166 | if service == nil || methodType == nil { 167 | err = conn.ReadRequestBody(nil) 168 | if err != nil { 169 | goto fail 170 | } 171 | server.replyCmd(conn, reqHeader.Seq, ErrNotFound, CmdTypeErr) 172 | continue 173 | } 174 | 175 | var argv, replyv reflect.Value 176 | // Decode the argument value. 177 | argIsValue := false // if true, need to indirect before calling. 178 | if methodType.ArgType.Kind() == reflect.Ptr { 179 | argv = reflect.New(methodType.ArgType.Elem()) 180 | } else { 181 | argv = reflect.New(methodType.ArgType) 182 | argIsValue = true 183 | } 184 | err = conn.ReadRequestBody(argv.Interface()) 185 | if err != nil { 186 | if isNetError(err) { 187 | log.Println("read request body with net error:", err, "method:", reqHeader.Method) 188 | goto fail 189 | } else { 190 | server.replyCmd(conn, reqHeader.Seq, &Error{400, ErrTypeCritical, err.Error()}, CmdTypeErr) 191 | continue 192 | } 193 | } 194 | if argIsValue { 195 | argv = argv.Elem() 196 | } 197 | replyv = reflect.New(methodType.ReplyType.Elem()) 198 | if reqHeader.CallType == RequestSendOnly { 199 | go server.asyncCallService(conn, reqHeader.Seq, service, methodType, argv, replyv) 200 | continue 201 | } 202 | go server.callService(conn, reqHeader.Seq, service, methodType, argv, replyv) 203 | } 204 | fail: 205 | server.status.IncrErrorAmount() 206 | conn.Lock() 207 | if conn.netError != nil { 208 | conn.netError = err 209 | } 210 | conn.Unlock() 211 | conn.Close() // close by timerGC will double close 212 | return 213 | } 214 | 215 | func (server *Server) Status() *ServerStatusPerSecond { 216 | return server.status.Status() 217 | } 218 | 219 | // send respHeader to client with nil response body 220 | func (server *Server) replyCmd(conn *ConnDriver, seq uint64, serverErr *Error, cmd string) { 221 | 222 | respHeader := NewResponseHeader() 223 | respHeader.Seq = seq 224 | switch cmd { 225 | case CmdTypePing: 226 | respHeader.ReplyType = ReplyTypePong 227 | case CmdTypeAck: 228 | respHeader.ReplyType = ReplyTypeAck 229 | case CmdTypeErr: 230 | respHeader.ReplyType = ReplyTypeAck 231 | respHeader.Error = serverErr 232 | // fmt.Println("replycmd send respHeader type error") 233 | } 234 | conn.Lock() 235 | err := server.SendFrame(conn, respHeader, reflect.ValueOf(nil)) 236 | conn.Unlock() 237 | if err != nil && !isNetError(err) { 238 | log.Fatalln("encoding error:" + err.Error()) 239 | } 240 | return 241 | } 242 | 243 | // send response first telling client that server has received the request,then execute the service 244 | func (server *Server) asyncCallService(conn *ConnDriver, seq uint64, service *service, methodType *methodType, argv, replyv reflect.Value) { 245 | server.replyCmd(conn, seq, nil, CmdTypeAck) 246 | function := methodType.method.Func 247 | // Invoke the method, providing a new value for the reply. 248 | function.Call([]reflect.Value{service.rcvr, argv, replyv}) 249 | return 250 | } 251 | 252 | // do service and send response to client 253 | func (server *Server) callService(conn *ConnDriver, seq uint64, service *service, methodType *methodType, argv, replyv reflect.Value) { 254 | function := methodType.method.Func 255 | 256 | returnValues := function.Call([]reflect.Value{service.rcvr, argv, replyv}) 257 | // The return value for the method is an error. 258 | errInter := returnValues[0].Interface() 259 | 260 | respHeader := NewResponseHeader() 261 | respHeader.ReplyType = ReplyTypeData 262 | respHeader.Seq = seq 263 | if errInter != nil { 264 | switch errInter.(type) { 265 | case *Error: 266 | server.replyCmd(conn, seq, errInter.(*Error), CmdTypeErr) 267 | case Error: 268 | e := errInter.(Error) 269 | server.replyCmd(conn, seq, &e, CmdTypeErr) 270 | case error: 271 | server.replyCmd(conn, seq, &Error{500, ErrTypeLogic, errInter.(error).Error()}, CmdTypeErr) 272 | } 273 | return 274 | } 275 | conn.Lock() 276 | err := server.SendFrame(conn, respHeader, replyv) 277 | conn.Unlock() 278 | if err != nil && !isNetError(err) { 279 | log.Fatalln("encoding error:" + err.Error()) 280 | } 281 | return 282 | } 283 | 284 | // send request Header and body to client if encoding error not net error 285 | // stop write subsequence frame and panic 286 | func (server *Server) SendFrame(conn *ConnDriver, respHeader *ResponseHeader, replyv reflect.Value) error { 287 | var err error 288 | if conn.netError != nil { 289 | return conn.netError 290 | } 291 | err = conn.SetWriteDeadline(time.Now().Add(DefaultServerIdleTimeout)) 292 | if err != nil { 293 | goto final 294 | } 295 | err = conn.WriteResponseHeader(respHeader) 296 | if err != nil { 297 | goto final 298 | } 299 | if respHeader.HaveReply() { 300 | err = conn.WriteResponseBody(replyv.Interface()) 301 | if err != nil { 302 | goto final 303 | } 304 | } 305 | err = conn.FlushWriteToNet() 306 | if err != nil { 307 | goto final 308 | } 309 | final: 310 | if err != nil { 311 | conn.netError = err 312 | } 313 | return err 314 | } 315 | 316 | func (server *Server) Register(rcvr interface{}) error { 317 | s := new(service) 318 | s.typ = reflect.TypeOf(rcvr) 319 | s.rcvr = reflect.ValueOf(rcvr) 320 | sname := reflect.Indirect(s.rcvr).Type().Name() 321 | if sname == "" { 322 | s := "rpc.Register: no service name for type " + s.typ.String() 323 | log.Print(s) 324 | return errors.New(s) 325 | } 326 | if !isExported(sname) { 327 | s := "rpc.Register: type " + sname + " is not exported" 328 | log.Print(s) 329 | return errors.New(s) 330 | } 331 | if _, present := server.serviceMap[sname]; present { 332 | return errors.New("rpc: service already defined: " + sname) 333 | } 334 | s.name = sname 335 | // Install the methods 336 | s.method = suitableMethods(s.typ, true) 337 | if len(s.method) == 0 { 338 | str := "" 339 | // To help the user, see if a pointer receiver would work. 340 | method := suitableMethods(reflect.PtrTo(s.typ), false) 341 | if len(method) != 0 { 342 | str = "rpc.Register: type " + sname + " has no exported methods of suitable type (hint: pass a pointer to value of that type)" 343 | } else { 344 | str = "rpc.Register: type " + sname + " has no exported methods of suitable type" 345 | } 346 | log.Print(str) 347 | return errors.New(str) 348 | } 349 | server.serviceMap[s.name] = s 350 | return nil 351 | } 352 | 353 | // suitableMethods returns suitable Rpc methods of typ, it will report 354 | // error using log if reportErr is true. 355 | func suitableMethods(typ reflect.Type, reportErr bool) map[string]*methodType { 356 | methods := make(map[string]*methodType) 357 | for m := 0; m < typ.NumMethod(); m++ { 358 | method := typ.Method(m) 359 | mtype := method.Type 360 | mname := method.Name 361 | // Method must be exported. 362 | if method.PkgPath != "" { 363 | continue 364 | } 365 | // Method needs three ins: receiver, *args, *reply. 366 | if mtype.NumIn() != 3 { 367 | if reportErr { 368 | log.Println("method", mname, "has wrong number of ins:", mtype.NumIn()) 369 | } 370 | continue 371 | } 372 | // First arg need not be a pointer. 373 | argType := mtype.In(1) 374 | if !isExportedOrBuiltinType(argType) { 375 | if reportErr { 376 | log.Println(mname, "argument type not exported:", argType) 377 | } 378 | continue 379 | } 380 | // Second arg must be a pointer. 381 | replyType := mtype.In(2) 382 | if replyType.Kind() != reflect.Ptr { 383 | if reportErr { 384 | log.Println("method", mname, "reply type not a pointer:", replyType) 385 | } 386 | continue 387 | } 388 | // Reply type must be exported. 389 | if !isExportedOrBuiltinType(replyType) { 390 | if reportErr { 391 | log.Println("method", mname, "reply type not exported:", replyType) 392 | } 393 | continue 394 | } 395 | // Method needs one out. 396 | if mtype.NumOut() != 1 { 397 | if reportErr { 398 | log.Println("method", mname, "has wrong number of outs:", mtype.NumOut()) 399 | } 400 | continue 401 | } 402 | // The return type of the method must be error. 403 | if returnType := mtype.Out(0); returnType != typeOfError { 404 | if reportErr { 405 | log.Println("method", mname, "returns", returnType.String(), "not error") 406 | } 407 | continue 408 | } 409 | methods[mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType} 410 | } 411 | return methods 412 | } 413 | 414 | // Is this an exported - upper case - name 415 | func isExported(name string) bool { 416 | rune, _ := utf8.DecodeRuneInString(name) 417 | return unicode.IsUpper(rune) 418 | } 419 | 420 | // Is this type exported or a builtin? 421 | func isExportedOrBuiltinType(t reflect.Type) bool { 422 | for t.Kind() == reflect.Ptr { 423 | t = t.Elem() 424 | } 425 | // PkgPath will be non-empty even for an exported type, 426 | // so we need to check the type name as well. 427 | return isExported(t.Name()) || t.PkgPath() == "" 428 | } 429 | 430 | // require err != nil 431 | -------------------------------------------------------------------------------- /service.go: -------------------------------------------------------------------------------- 1 | package gorpc 2 | 3 | import ( 4 | "reflect" 5 | "sync" 6 | ) 7 | 8 | type methodType struct { 9 | sync.Mutex // protects counters 10 | method reflect.Method 11 | ArgType reflect.Type 12 | ReplyType reflect.Type 13 | numCalls uint 14 | } 15 | 16 | type service struct { 17 | name string // name of service 18 | rcvr reflect.Value // receiver of methods for the service 19 | typ reflect.Type // type of the receiver 20 | method map[string]*methodType // registered methods 21 | } 22 | 23 | type RpcStatus struct{ server *Server } 24 | 25 | func (inner *RpcStatus) CallStatus(detail bool, serverStatus *string) error { 26 | *serverStatus = inner.server.status.String() 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /setting.go: -------------------------------------------------------------------------------- 1 | // client and server settings 2 | // follow the rules below 3 | // DefaultMaxOpenConns > defaultIdleConns 4 | // DefaultServerTimeout > 2*DefaultPingInterval 5 | package gorpc 6 | 7 | import ( 8 | "time" 9 | ) 10 | 11 | // client setting 12 | const ( 13 | DefaultMaxOpenConns = 30 // max conns 14 | DefaultMaxIdleConns = 20 // max idle conns 15 | DefaultReadTimeout = 30 * time.Second 16 | DefaultWriteTimeout = 30 * time.Second 17 | DefaultConnectTimeout = 30 * time.Second // default connect timeout 18 | DefaultPingInterval = 50 * time.Second // conn idle beyond DefaultPingInterval send a ping packet to server 19 | DefaultTimerGCInterval = time.Second 20 | ) 21 | 22 | // server setting 23 | const ( 24 | DefaultServerIdleTimeout = time.Second * 300 25 | // client wait server to close the connection 26 | DefaultClientWaitResponseTimeout = DefaultServerIdleTimeout + time.Second*10 27 | 28 | DefaultServerTimerGCInterval = DefaultServerIdleTimeout / 2 29 | ) 30 | -------------------------------------------------------------------------------- /utility/calculator/calculator.go: -------------------------------------------------------------------------------- 1 | package calculator 2 | 3 | import ( 4 | "time" 5 | "sync" 6 | "sort" 7 | "fmt" 8 | ) 9 | 10 | type CallTimer struct { 11 | id uint64 12 | startTime time.Time 13 | endTime time.Time 14 | } 15 | 16 | type CallCalculator struct { 17 | sync.Mutex 18 | id int 19 | Fields map[int]*CallTimer 20 | FieldsSorted []*CallTimer 21 | RangeResult map[float64]time.Duration // ratio:qps 22 | } 23 | 24 | var SummaryRatio = []float64{0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0} 25 | 26 | func NewCallCalculator() *CallCalculator { 27 | c := &CallCalculator{ 28 | Fields: make(map[int]*CallTimer, 1000000), 29 | FieldsSorted: make([]*CallTimer, 0, 1000000), 30 | RangeResult: map[float64]time.Duration{}, 31 | } 32 | for _, ratio := range SummaryRatio { 33 | c.RangeResult[ratio] = 0 34 | } 35 | return c 36 | } 37 | 38 | func (c CallCalculator) Len() int { return len(c.FieldsSorted) } 39 | 40 | func (c CallCalculator) Swap(i, j int) { 41 | c.FieldsSorted[i], c.FieldsSorted[j] = c.FieldsSorted[j], c.FieldsSorted[i] 42 | } 43 | func (c CallCalculator) Less(i, j int) bool { 44 | return c.FieldsSorted[i].endTime.Sub(c.FieldsSorted[i].startTime) < c.FieldsSorted[j].endTime.Sub(c.FieldsSorted[j].startTime) 45 | } 46 | 47 | func (c *CallCalculator) Start() (index int) { 48 | c.Lock() 49 | index = c.id 50 | c.Fields[c.id] = &CallTimer{startTime: time.Now()} 51 | c.id++ 52 | c.Unlock() 53 | return 54 | } 55 | 56 | func (c *CallCalculator) End(index int) { 57 | c.Lock() 58 | c.Fields[index].endTime = time.Now() 59 | c.Unlock() 60 | } 61 | 62 | func (c *CallCalculator) sort() { 63 | if len(c.FieldsSorted) == 0 { 64 | for _, v := range c.Fields { 65 | c.FieldsSorted = append(c.FieldsSorted, v) 66 | } 67 | sort.Sort(c) 68 | } 69 | } 70 | 71 | func (c *CallCalculator) Summary() { 72 | c.sort() 73 | 74 | var timeCost time.Duration 75 | indexToCal := make(map[int]float64) 76 | for ratio, _ := range c.RangeResult { 77 | index := int(float64(len(c.FieldsSorted)) * ratio) 78 | indexToCal[index-1] = ratio 79 | } 80 | 81 | minStartTime, maxEndTime := time.Now(), time.Time{} 82 | 83 | for index, v := range c.FieldsSorted { 84 | if v.startTime.Before(minStartTime) { 85 | minStartTime = v.startTime 86 | } 87 | if v.endTime.After(maxEndTime) { 88 | maxEndTime = v.endTime 89 | } 90 | if v.endTime.Sub(v.startTime) > timeCost { 91 | timeCost = v.endTime.Sub(v.startTime) 92 | } 93 | if ratio, ok := indexToCal[index]; ok { 94 | c.RangeResult[ratio] = timeCost 95 | } 96 | } 97 | 98 | for _, ratio := range SummaryRatio { 99 | timeCost = c.RangeResult[ratio] 100 | callsRatio := int(100 * ratio) 101 | maxTimeCost := int(timeCost / time.Millisecond) 102 | 103 | fmt.Printf("%3d%% calls consume less than %d ms \n", callsRatio, maxTimeCost) 104 | } 105 | costSeconds := int64(maxEndTime.Sub(minStartTime)) / int64(time.Second) 106 | qps := int64(len(c.FieldsSorted)) * int64(time.Second) / int64(maxEndTime.Sub(minStartTime)) 107 | fmt.Printf("request amount: %d, cost times : %d second, average Qps: %d \n", len(c.FieldsSorted), costSeconds, qps) 108 | } 109 | -------------------------------------------------------------------------------- /utility/convert/convert.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type ByteOrder int 8 | 9 | const ( 10 | BigEndian ByteOrder = iota 11 | LittleEndian 12 | ) 13 | 14 | func StreamToInt64(stream []byte, byteOrder ByteOrder) int64 { 15 | if len(stream) != 8 { 16 | return 0 17 | } 18 | var u uint64 19 | if byteOrder == LittleEndian { 20 | for i := 0; i < 8; i++ { 21 | u += uint64(stream[i]) << uint(i*8) 22 | } 23 | } else { 24 | for i := 0; i < 8; i++ { 25 | u += uint64(stream[i]) << uint(8*(7-i)) 26 | } 27 | } 28 | return int64(u) 29 | } 30 | 31 | func Int64ToStream(i int64, byteOrder ByteOrder) []byte { 32 | u := uint64(i) 33 | stream := [8]byte{0, 0, 0, 0, 0, 0, 0, 0} 34 | if byteOrder == LittleEndian { 35 | for i := 0; i < 8; i++ { 36 | stream[i] = byte(u >> uint(8*i)) 37 | } 38 | } else { 39 | for i := 0; i < 8; i++ { 40 | stream[i] = byte(u >> uint(8*(7-i))) 41 | } 42 | } 43 | return stream[:] 44 | } 45 | 46 | func Int64ToStreamEx(stream []byte, i int64, byteOrder ByteOrder) error { 47 | if len(stream) != 8 { 48 | return errors.New("bad stream") 49 | } 50 | u := uint64(i) 51 | if byteOrder == LittleEndian { 52 | for i := 0; i < 8; i++ { 53 | stream[i] = byte(u >> uint(8*i)) 54 | } 55 | } else { 56 | for i := 0; i < 8; i++ { 57 | stream[i] = byte(u >> uint(8*(7-i))) 58 | } 59 | } 60 | return nil 61 | } 62 | 63 | func StreamToInt32(stream []byte, byteOrder ByteOrder) int32 { 64 | if len(stream) != 4 { 65 | return 0 66 | } 67 | var u uint32 68 | if byteOrder == LittleEndian { 69 | for i := 0; i < 4; i++ { 70 | u += uint32(stream[i]) << uint(i*8) 71 | } 72 | } else { 73 | for i := 0; i < 4; i++ { 74 | u += uint32(stream[i]) << uint(8*(3-i)) 75 | } 76 | } 77 | return int32(u) 78 | } 79 | 80 | func Int32ToStream(i int32, byteOrder ByteOrder) []byte { 81 | u := uint32(i) 82 | stream := [4]byte{0, 0, 0, 0} 83 | if byteOrder == LittleEndian { 84 | for i := 0; i < 4; i++ { 85 | stream[i] = byte(u >> uint(8*i)) 86 | } 87 | } else { 88 | for i := 0; i < 4; i++ { 89 | stream[i] = byte(u >> uint(8*(3-i))) 90 | } 91 | } 92 | return stream[:] 93 | } 94 | 95 | func Int32ToStreamEx(stream []byte, i int32, byteOrder ByteOrder) error { 96 | if len(stream) != 4 { 97 | return errors.New("bad stream") 98 | } 99 | u := uint32(i) 100 | if byteOrder == LittleEndian { 101 | for i := 0; i < 4; i++ { 102 | stream[i] = byte(u >> uint(8*i)) 103 | } 104 | } else { 105 | for i := 0; i < 4; i++ { 106 | stream[i] = byte(u >> uint(8*(3-i))) 107 | } 108 | } 109 | return nil 110 | } 111 | 112 | func StreamToInt16(stream []byte, byteOrder ByteOrder) int16 { 113 | if len(stream) != 2 { 114 | return 0 115 | } 116 | var u uint16 117 | if byteOrder == LittleEndian { 118 | for i := 0; i < 2; i++ { 119 | u += uint16(stream[i]) << uint(i*8) 120 | } 121 | } else { 122 | for i := 0; i < 2; i++ { 123 | u += uint16(stream[i]) << uint(8*(1-i)) 124 | } 125 | } 126 | return int16(u) 127 | } 128 | 129 | func Int16ToStream(i int16, byteOrder ByteOrder) []byte { 130 | u := uint16(i) 131 | stream := [2]byte{0, 0} 132 | if byteOrder == LittleEndian { 133 | for i := 0; i < 2; i++ { 134 | stream[i] = byte(u >> uint(8*i)) 135 | } 136 | } else { 137 | for i := 0; i < 2; i++ { 138 | stream[i] = byte(u >> uint(8*(1-i))) 139 | } 140 | } 141 | return stream[:] 142 | } 143 | 144 | func Int16ToStreamEx(stream []byte, i int16, byteOrder ByteOrder) error { 145 | if len(stream) != 2 { 146 | return errors.New("bad stream") 147 | } 148 | u := uint16(i) 149 | if byteOrder == LittleEndian { 150 | for i := 0; i < 2; i++ { 151 | stream[i] = byte(u >> uint(8*i)) 152 | } 153 | } else { 154 | for i := 0; i < 2; i++ { 155 | stream[i] = byte(u >> uint(8*(1-i))) 156 | } 157 | } 158 | return nil 159 | } 160 | func StreamToUint16(stream []byte, byteOrder ByteOrder) uint16 { 161 | if len(stream) != 2 { 162 | return 0 163 | } 164 | var u uint16 165 | if byteOrder == LittleEndian { 166 | for i := 0; i < 2; i++ { 167 | u += uint16(stream[i]) << uint(i*8) 168 | } 169 | } else { 170 | for i := 0; i < 2; i++ { 171 | u += uint16(stream[i]) << uint(8*(1-i)) 172 | } 173 | } 174 | return u 175 | } 176 | 177 | func Uint16ToStreamEx(stream []byte, u uint16, byteOrder ByteOrder) error { 178 | if len(stream) != 2 { 179 | return errors.New("bad stream") 180 | } 181 | if byteOrder == LittleEndian { 182 | for i := 0; i < 2; i++ { 183 | stream[i] = byte(u >> uint(8*i)) 184 | } 185 | } else { 186 | for i := 0; i < 2; i++ { 187 | stream[i] = byte(u >> uint(8*(1-i))) 188 | } 189 | } 190 | return nil 191 | } 192 | 193 | func StreamToUint32(stream []byte, byteOrder ByteOrder) uint32 { 194 | if len(stream) != 4 { 195 | return 0 196 | } 197 | var u uint32 198 | if byteOrder == LittleEndian { 199 | for i := 0; i < 4; i++ { 200 | u += uint32(stream[i]) << uint(i*8) 201 | } 202 | } else { 203 | for i := 0; i < 4; i++ { 204 | u += uint32(stream[i]) << uint(8*(3-i)) 205 | } 206 | } 207 | return u 208 | } 209 | func Uint32ToStream(u int32, byteOrder ByteOrder) []byte { 210 | stream := [4]byte{0, 0, 0, 0} 211 | if byteOrder == LittleEndian { 212 | for i := 0; i < 4; i++ { 213 | stream[i] = byte(u >> uint(8*i)) 214 | } 215 | } else { 216 | for i := 0; i < 4; i++ { 217 | stream[i] = byte(u >> uint(8*(3-i))) 218 | } 219 | } 220 | return stream[:] 221 | } 222 | -------------------------------------------------------------------------------- /utility/pprof/pprof.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "runtime" 7 | ) 8 | 9 | func init() { 10 | Proc.Init() 11 | } 12 | 13 | const ( 14 | TotalAlloc = "TotalAlloc" 15 | HeapObjects = "HeapObjects" 16 | StackInuse = "StackInuse" 17 | NumGC = "NumGC" 18 | PauseTotalMs = "PauseTotalMs" 19 | ) 20 | 21 | type Stats struct { 22 | index int 23 | content map[int]map[string]int 24 | lastNumGc uint32 25 | maxGoroutineNum int 26 | lastLoopGoroutineNum int 27 | } 28 | 29 | var memStats = func() *Stats { 30 | return &Stats{content: make(map[int]map[string]int)} 31 | }() 32 | 33 | func MemStats() { 34 | memStats.DoMemoryStats() 35 | } 36 | 37 | func ProcessStats() { 38 | memStats.DoProcessStat() 39 | } 40 | 41 | // if gc happend 42 | func (s *Stats) DoMemoryStats() { 43 | 44 | var memoryStats runtime.MemStats 45 | runtime.ReadMemStats(&memoryStats) 46 | v := reflect.ValueOf(memoryStats) 47 | t := reflect.TypeOf(memoryStats) 48 | if s.lastNumGc != memoryStats.NumGC { 49 | s.lastNumGc = memoryStats.NumGC 50 | } 51 | s.content[s.index] = make(map[string]int) 52 | for i := 0; i < v.NumField(); i++ { 53 | field := v.Field(i) 54 | fieldType := t.Field(i) 55 | switch fieldType.Name { 56 | case "TotalAlloc", "HeapAlloc", "HeapObjects", "Mallocs", "StackInuse", "MSpanInuse", "NumGC": 57 | v := field.Interface() 58 | switch t := v.(type) { 59 | case uint32: 60 | s.content[s.index][fieldType.Name] = int(v.(uint32)) 61 | case uint64: 62 | s.content[s.index][fieldType.Name] = int(v.(uint64)) 63 | default: 64 | fmt.Println(t) 65 | } 66 | case "PauseNs": 67 | s.content[s.index]["PauseMs"] = int(memoryStats.PauseNs[(memoryStats.NumGC+255)%256]) / 1e6 68 | case "PauseTotalNs": 69 | s.content[s.index]["PauseTotalMs"] = int(field.Interface().(uint64)) / 1e6 70 | } 71 | } 72 | s.index += 1 73 | } 74 | 75 | func StatIncrement(keys ...string) { 76 | for _, key := range keys { 77 | if memStats.index == 1 { 78 | fmt.Printf("%s: %d - %d = %d\n", key, memStats.content[memStats.index-1][key], 0, memStats.content[memStats.index-1][key]) 79 | continue 80 | } 81 | // fmt.Println(memStats.index-1, memStats.index-2) 82 | // fmt.Println(memStats.content[memStats.index-1]) 83 | // fmt.Println(memStats.content[memStats.index-2]) 84 | res := memStats.content[memStats.index-1][key] - memStats.content[memStats.index-2][key] 85 | fmt.Printf("%s: %d - %d = %d\n", key, memStats.content[memStats.index-1][key], memStats.content[memStats.index-2][key], res) 86 | } 87 | 88 | } 89 | 90 | func Current() string { 91 | result := "---------------NumGC memStats.lastNumGc ------------------\n" 92 | result = fmt.Sprintf("%v\n", memStats.content) 93 | result += "-------------------------------------------------------" 94 | return result 95 | } 96 | 97 | func (s *Stats) DoProcessStat() { 98 | n := runtime.NumGoroutine() 99 | if n > s.maxGoroutineNum { 100 | s.maxGoroutineNum = n 101 | } 102 | if n != s.lastLoopGoroutineNum { 103 | fmt.Printf("current goutines:%10d, max goroutine num: %10d\n", n, s.maxGoroutineNum) 104 | s.lastLoopGoroutineNum = n 105 | Proc.Cpu.Refresh() 106 | Proc.Mem.Refresh() 107 | fmt.Println("process cpu and mem: ", Proc.Cpu, Proc.Mem) 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /utility/pprof/profile.go: -------------------------------------------------------------------------------- 1 | package pprof 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "io" 7 | "io/ioutil" 8 | "os" 9 | "strconv" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | /* 15 | #include 16 | #include 17 | #include 18 | #include 19 | */ 20 | // import "C" 21 | 22 | func init() { 23 | } 24 | 25 | var Proc *ProcInfo = NewProcInfo() 26 | 27 | type ProcInfo struct { 28 | Base *ProcBase 29 | Cpu *ProcCpu 30 | Mem *ProcMem 31 | } 32 | 33 | func NewProcInfo() *ProcInfo { 34 | return &ProcInfo{} 35 | } 36 | 37 | func (this *ProcInfo) Init() { 38 | this.Base = &ProcBase{} 39 | this.Cpu = &ProcCpu{} 40 | this.Mem = &ProcMem{} 41 | this.Base.GetProcInfo() 42 | } 43 | 44 | type ProcMem struct { 45 | VmSize int //Virtual memory size 46 | VmRss int //Resident set size 47 | VmData int //Size of data 48 | VmStk int //Size of Stack 49 | VmExe int //Size of text segments 50 | VmLib int //Shared library code size 51 | } 52 | 53 | func (pm *ProcMem) Refresh() error { 54 | file, err := os.Open("/proc/" + Proc.Base.Pid + "/status") 55 | if err != nil { 56 | return err 57 | } 58 | defer file.Close() 59 | 60 | reader := bufio.NewReader(file) 61 | for { 62 | line, err := reader.ReadString('\n') 63 | if err != nil || err == io.EOF { 64 | break 65 | } 66 | fields := strings.Fields(line) 67 | if len(fields) != 3 { 68 | continue 69 | } 70 | if strings.Trim(fields[1], " ") == "0" { 71 | continue 72 | } 73 | switch strings.Trim(fields[0], ":") { 74 | case "VmSize": 75 | pm.VmSize, _ = strconv.Atoi(fields[1]) 76 | case "VmRSS": 77 | pm.VmRss, _ = strconv.Atoi(fields[1]) 78 | case "VmData": 79 | pm.VmData, _ = strconv.Atoi(fields[1]) 80 | case "VmStk": 81 | pm.VmStk, _ = strconv.Atoi(fields[1]) 82 | case "VmExe": 83 | pm.VmExe, _ = strconv.Atoi(fields[1]) 84 | case "VmLib": 85 | pm.VmLib, _ = strconv.Atoi(fields[1]) 86 | } 87 | } 88 | return nil 89 | } 90 | 91 | func (pm *ProcMem) ReSet() { 92 | pm.VmData = 0 93 | pm.VmSize = 0 94 | pm.VmRss = 0 95 | pm.VmLib = 0 96 | pm.VmExe = 0 97 | pm.VmStk = 0 98 | } 99 | 100 | func (pm *ProcMem) String() string { 101 | return fmt.Sprintf("VIRT:%d KB, RES:%d KB, Data:%d KB, Stack:%d KB, Text Segment:%d KB, Lib:%d KB", 102 | pm.VmSize, pm.VmRss, pm.VmData, pm.VmStk, pm.VmExe, pm.VmLib) 103 | } 104 | 105 | type ProcCpu struct { 106 | Utime uint64 107 | Stime uint64 108 | Cutime uint64 109 | Cstime uint64 110 | StartTime uint64 111 | LastUS uint64 //Utime+Stime 112 | LastTimer time.Time //time.Now() 113 | } 114 | 115 | func (pc *ProcCpu) Refresh() error { 116 | file, err := os.Open("/proc/" + Proc.Base.Pid + "/stat") 117 | if err != nil { 118 | return err 119 | } 120 | defer file.Close() 121 | 122 | reader := bufio.NewReader(file) 123 | line, err := reader.ReadString('\n') 124 | if err != nil && err != io.EOF { 125 | return nil 126 | } 127 | fields := strings.Fields(line) 128 | if utime, err := strconv.ParseUint(fields[13], 10, 64); err == nil { 129 | pc.Utime = utime 130 | } 131 | if stime, err := strconv.ParseUint(fields[14], 10, 64); err == nil { 132 | pc.Stime = stime 133 | } 134 | if cutime, err := strconv.ParseUint(fields[15], 10, 64); err == nil { 135 | pc.Cutime = cutime 136 | } 137 | if cstime, err := strconv.ParseUint(fields[16], 10, 64); err == nil { 138 | pc.Cstime = cstime 139 | } 140 | if starttime, err := strconv.ParseUint(fields[21], 10, 64); err == nil { 141 | pc.StartTime = starttime 142 | } 143 | return nil 144 | } 145 | 146 | func (pc *ProcCpu) ReSet() { 147 | pc.Utime = 0 148 | pc.Stime = 0 149 | pc.Cutime = 0 150 | pc.Cstime = 0 151 | pc.StartTime = 0 152 | } 153 | 154 | /** 155 | * 采样时间段内的cpu使用率,算法与top命令一致。 156 | * top计算进程cpu使用率源码参见:procs的top.c:prochlp() 157 | */ 158 | func (pc *ProcCpu) CurrentUsage() float64 { 159 | nowTime := time.Now() 160 | totalTime := pc.Utime + pc.Stime 161 | sub := nowTime.Sub(pc.LastTimer).Seconds() 162 | sec := 100 / (float64(Machine.Hertz) * sub) 163 | pcpu := float64(totalTime) - float64(pc.LastUS) 164 | pc.LastUS = totalTime 165 | pc.LastTimer = nowTime 166 | return pcpu * sec 167 | } 168 | 169 | func (pc *ProcCpu) String() string { 170 | return fmt.Sprintf("Cpu:%0.2f%%", pc.CurrentUsage()) 171 | } 172 | 173 | type ProcBase struct { 174 | Pid string 175 | PPid string 176 | Command string 177 | State string 178 | } 179 | 180 | func (pb *ProcBase) GetProcInfo() error { 181 | pb.Pid = strconv.Itoa(os.Getpid()) 182 | file, err := os.Open("/proc/" + pb.Pid + "/stat") 183 | if err != nil { 184 | return err 185 | } 186 | defer file.Close() 187 | 188 | reader := bufio.NewReader(file) 189 | line, err := reader.ReadString('\n') 190 | if err != nil && err != io.EOF { 191 | return nil 192 | } 193 | fields := strings.Fields(line) 194 | pb.PPid = fields[3] 195 | pb.Command = pb.GetCommand() 196 | pb.State = fields[2] 197 | return nil 198 | } 199 | 200 | func (pb *ProcBase) GetCommand() string { 201 | command, _ := ioutil.ReadFile("/proc/" + pb.Pid + "/cmdline") 202 | return string(command) 203 | } 204 | 205 | type MachineCpu struct { 206 | User uint64 207 | Nice uint64 208 | System uint64 209 | Idle uint64 210 | Iowait uint64 211 | Irq uint64 212 | SoftIrq uint64 213 | Stealstolen uint64 214 | Guest uint64 215 | } 216 | 217 | func (mc *MachineCpu) Refresh() error { 218 | file, err := os.Open("/proc/stat") 219 | if err != nil { 220 | return err 221 | } 222 | defer file.Close() 223 | 224 | reader := bufio.NewReader(file) 225 | line, err := reader.ReadString('\n') 226 | if err != nil && err != io.EOF { 227 | return nil 228 | } 229 | fields := strings.Fields(line) 230 | 231 | if user, err := strconv.ParseUint(fields[1], 10, 64); err == nil { 232 | mc.User = user 233 | } 234 | if nice, err := strconv.ParseUint(fields[2], 10, 64); err == nil { 235 | mc.Nice = nice 236 | } 237 | if system, err := strconv.ParseUint(fields[3], 10, 64); err == nil { 238 | mc.System = system 239 | } 240 | if idle, err := strconv.ParseUint(fields[4], 10, 64); err == nil { 241 | mc.Idle = idle 242 | } 243 | if iowait, err := strconv.ParseUint(fields[5], 10, 64); err == nil { 244 | mc.Iowait = iowait 245 | } 246 | if irq, err := strconv.ParseUint(fields[6], 10, 64); err == nil { 247 | mc.Irq = irq 248 | } 249 | if softirq, err := strconv.ParseUint(fields[7], 10, 64); err == nil { 250 | mc.SoftIrq = softirq 251 | } 252 | if stealstolen, err := strconv.ParseUint(fields[8], 10, 64); err == nil { 253 | mc.Stealstolen = stealstolen 254 | } 255 | if guest, err := strconv.ParseUint(fields[9], 10, 64); err == nil { 256 | mc.Guest = guest 257 | } 258 | return nil 259 | } 260 | 261 | func (mc *MachineCpu) ReSet() { 262 | mc.User = 0 263 | mc.Nice = 0 264 | mc.System = 0 265 | mc.Idle = 0 266 | mc.Iowait = 0 267 | mc.Irq = 0 268 | mc.SoftIrq = 0 269 | mc.Stealstolen = 0 270 | mc.Guest = 0 271 | } 272 | 273 | var Machine *MachineInfo = NewMachineInfo() 274 | 275 | type MachineInfo struct { 276 | Uptime float64 277 | Hertz int 278 | Cpu *MachineCpu 279 | } 280 | 281 | func NewMachineInfo() *MachineInfo { 282 | // return &MachineInfo{Hertz: int(C.sysconf(C._SC_CLK_TCK)), Cpu: &MachineCpu{}} 283 | return &MachineInfo{Hertz: 100, Cpu: &MachineCpu{}} 284 | } 285 | 286 | func (this *MachineInfo) GetUptime() float64 { 287 | if uptime, err := ioutil.ReadFile("/proc/uptime"); err == nil { 288 | fields := strings.Fields(string(uptime)) 289 | this.Uptime, _ = strconv.ParseFloat(fields[0], 64) 290 | } 291 | return this.Uptime 292 | } 293 | --------------------------------------------------------------------------------