go-micro client 客户端
go-micro 支持很多通信协议:http、tcp、grpc等,支持的编码方式也很多有json、protobuf、bytes、jsonrpc等。也可以根据自己的需要实现通信协议和编码方式。go-micro 默认的通信协议是http,默认的编码方式是protobuf。
主要代码定义如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | // Client is the interface used to make requests to services. // It supports Request/Response via Transport and Publishing via the Broker. // It also supports bidiectional streaming of requests. type Client interface { Init(...Option) error Options() Options NewMessage(topic string, msg interface {}, opts ...MessageOption) Message NewRequest(service, method string, req interface {}, reqOpts ...RequestOption) Request Call(ctx context.Context, req Request, rsp interface {}, opts ...CallOption) error Stream(ctx context.Context, req Request, opts ...CallOption) (Stream, error) Publish(ctx context.Context, msg Message, opts ...PublishOption) error String() string } // Message is the interface for publishing asynchronously type Message interface { Topic() string Payload() interface {} ContentType() string } // Request is the interface for a synchronous request used by Call or Stream type Request interface { Service() string Method() string ContentType() string Request() interface {} // indicates whether the request will be a streaming one rather than unary Stream() bool } // Stream is the inteface for a bidirectional synchronous stream type Stream interface { Context() context.Context Request() Request Send( interface {}) error Recv( interface {}) error Error() error Close() error } // Option used by the Client type Option func (*Options) // CallOption used by Call or Stream type CallOption func (*CallOptions) // PublishOption used by Publish type PublishOption func (*PublishOptions) // MessageOption used by NewMessage type MessageOption func (*MessageOptions) // RequestOption used by NewRequest type RequestOption func (*RequestOptions) |
client的连接使用了pool,减少生成和销毁的开销,不够用的话就另外生成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | func newPool(size int, ttl time.Duration) *pool { return &pool{ size: size, ttl: int64(ttl.Seconds()), conns: make( map [string][]*poolConn), } } // NoOp the Close since we manage it func (p *poolConn) Close() error { return nil } func (p *pool) getConn(addr string, tr transport.Transport, opts ...transport.DialOption) (*poolConn, error) { p.Lock() conns := p.conns[addr] now := time.Now().Unix() // while we have conns check age and then return one // otherwise we'll create a new conn for len(conns) > 0 { conn := conns[len(conns)-1] conns = conns[:len(conns)-1] p.conns[addr] = conns // if conn is old kill it and move on if d := now - conn.created; d > p.ttl { conn.Client.Close() continue } // we got a good conn, lets unlock and return it p.Unlock() return conn, nil } p.Unlock() // create new conn c, err := tr.Dial(addr, opts...) if err != nil { return nil, err } return &poolConn{c, time.Now().Unix()}, nil } func (p *pool) release(addr string, conn *poolConn, err error) { // don't store the conn if it has errored if err != nil { conn.Client.Close() return } // otherwise put it back for reuse p.Lock() conns := p.conns[addr] if len(conns) >= p.size { p.Unlock() conn.Client.Close() return } p.conns[addr] = append(conns, conn) p.Unlock() } |
封装request:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | type rpcRequest struct { service string method string contentType string request interface {} opts RequestOptions } func newRequest(service, method string, request interface {}, contentType string, reqOpts ...RequestOption) Request { var opts RequestOptions for _, o := range reqOpts { o(&opts) } // set the content-type specified if len(opts.ContentType) > 0 { contentType = opts.ContentType } return &rpcRequest{ service: service, method: method, request: request, contentType: contentType, opts: opts, } } |
调用接口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | func (r *rpcClient) Call(ctx context.Context, request Request, response interface {}, opts ...CallOption) error { // make a copy of call opts callOpts := r.opts.CallOptions for _, opt := range opts { opt(&callOpts) } next, err := r.next(request, callOpts) if err != nil { return err } // check if we already have a deadline d, ok := ctx.Deadline() if !ok { // no deadline so we create a new one ctx, _ = context.WithTimeout(ctx, callOpts.RequestTimeout) } else { // got a deadline so no need to setup context // but we need to set the timeout we pass along opt := WithRequestTimeout(d.Sub(time.Now())) opt(&callOpts) } // should we noop right here? select { case <-ctx.Done(): return errors.New( "go.micro.client" , fmt.Sprintf( "%v" , ctx.Err()), 408) default : } // make copy of call method rcall := r.call // wrap the call in reverse for i := len(callOpts.CallWrappers); i > 0; i-- { rcall = callOpts.CallWrappers[i-1](rcall) } // return errors.New("go.micro.client", "request timeout", 408) call := func (i int) error { // call backoff first. Someone may want an initial start delay t, err := callOpts.Backoff(ctx, request, i) if err != nil { return errors.InternalServerError( "go.micro.client" , "backoff error: %v" , err.Error()) } // only sleep if greater than 0 if t.Seconds() > 0 { time.Sleep(t) } // select next node node, err := next() if err != nil && err == selector.ErrNotFound { return errors.NotFound( "go.micro.client" , "service %s: %v" , request.Service(), err.Error()) } else if err != nil { return errors.InternalServerError( "go.micro.client" , "error getting next %s node: %v" , request.Service(), err.Error()) } // set the address address := node.Address if node.Port > 0 { address = fmt.Sprintf( "%s:%d" , address, node.Port) } // make the call err = rcall(ctx, address, request, response, callOpts) r.opts.Selector.Mark(request.Service(), node, err) return err } ch := make( chan error, callOpts.Retries) var gerr error for i := 0; i <= callOpts.Retries; i++ { go func (i int) { ch <- call(i) }(i) select { case <-ctx.Done(): return errors.New( "go.micro.client" , fmt.Sprintf( "call timeout: %v" , ctx.Err()), 408) case err := <-ch: // if the call succeeded lets bail early if err == nil { return nil } retry, rerr := callOpts.Retry(ctx, request, i, err) if rerr != nil { return rerr } if !retry { return err } gerr = err } } return gerr } |
you are the best!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现