func (r *rpcClient) stream(ctx context.Context, address string, req Request, opts CallOptions) (Streamer, error) { msg := &transport.Message{ Header: make(map[string]string), } md, ok := metadata.FromContext(ctx) if ok { for k, v := range md { msg.Header[k] = v } } // set timeout in nanoseconds msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request msg.Header["Content-Type"] = req.ContentType() cf, err := r.newCodec(req.ContentType()) if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } c, err := r.opts.Transport.Dial(address, transport.WithStream(), transport.WithTimeout(opts.DialTimeout)) if err != nil { return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } stream := &rpcStream{ context: ctx, request: req, closed: make(chan bool), codec: newRpcPlusCodec(msg, c, cf), } ch := make(chan error, 1) go func() { ch <- stream.Send(req.Request()) }() var grr error select { case err := <-ch: grr = err case <-ctx.Done(): grr = errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) } if grr != nil { stream.Close() return nil, grr } return stream, nil }
func limit(b *ratelimit.Bucket, wait bool, errId string) func() error { return func() error { if wait { time.Sleep(b.Take(1)) } else if b.TakeAvailable(1) == 0 { return errors.New(errId, "too many request", 429) } return nil } }
func selector(id int, r router.Router) map[string]int { stats := make(map[string]int) // select the service next, err := r.Select(service) if err != nil { fmt.Println(id, "error selecting", err) return stats } for i := 1; i <= requests; i++ { // get a node node, err := next() if err != nil { fmt.Println(id, "error getting next", err) return stats } stats[node.Id]++ // make some request // client.Call(foo, request) req := client.NewRequest(service, "Router.Stats", &proto.StatsRequest{}) var dur time.Duration // lets set an error if d := (rand.Int() % i); d == 0 { dur = time.Millisecond * time.Duration(rand.Int()%20) err = errors.InternalServerError(service, "err") } else if d == 1 { dur = time.Second * 5 err = errors.New(service, "timed out", 408) } else { dur = time.Millisecond * time.Duration(rand.Int()%10) err = nil } // mark the result r.Mark(service, node, err) // record timing r.Record(req, node, dur, err) //fmt.Println(id, "selected", node.Id) time.Sleep(time.Millisecond*10 + time.Duration(rand.Int()%10)) } return stats }
func (r *rpcClient) stream(ctx context.Context, address string, req Request) (Streamer, error) { msg := &transport.Message{ Header: make(map[string]string), } md, ok := c.GetMetadata(ctx) if ok { for k, v := range md { msg.Header[k] = v } } msg.Header["Content-Type"] = req.ContentType() cf, err := r.newCodec(req.ContentType()) if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } c, err := r.opts.Transport.Dial(address, transport.WithStream(), transport.WithTimeout(r.opts.DialTimeout)) if err != nil { return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } var once sync.Once stream := &rpcStream{ context: ctx, request: req, once: once, closed: make(chan bool), codec: newRpcPlusCodec(msg, c, cf), } ch := make(chan error, 1) go func() { ch <- stream.Send(req.Request()) }() select { case err = <-ch: case <-time.After(r.opts.RequestTimeout): err = errors.New("go.micro.client", "request timeout", 408) } return stream, err }
func (g *grpcClient) Stream(ctx context.Context, req client.Request, opts ...client.CallOption) (client.Streamer, error) { // make a copy of call opts callOpts := g.opts.CallOptions for _, opt := range opts { opt(&callOpts) } // get next nodes from the selector next, err := g.opts.Selector.Select(req.Service(), callOpts.SelectOptions...) if err != nil && err == selector.ErrNotFound { return nil, errors.NotFound("go.micro.client", err.Error()) } else if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } // 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 := client.WithRequestTimeout(d.Sub(time.Now())) opt(&callOpts) } // should we noop right here? select { case <-ctx.Done(): return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) default: } call := func(i int) (client.Streamer, error) { // call backoff first. Someone may want an initial start delay t, err := callOpts.Backoff(ctx, req, i) if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } // only sleep if greater than 0 if t.Seconds() > 0 { time.Sleep(t) } node, err := next() if err != nil && err == selector.ErrNotFound { return nil, errors.NotFound("go.micro.client", err.Error()) } else if err != nil { return nil, errors.InternalServerError("go.micro.client", err.Error()) } addr := node.Address if node.Port > 0 { addr = fmt.Sprintf("%s:%d", addr, node.Port) } stream, err := g.stream(ctx, addr, req, callOpts) g.opts.Selector.Mark(req.Service(), node, err) return stream, err } type response struct { stream client.Streamer err error } ch := make(chan response, callOpts.Retries) var grr error for i := 0; i < callOpts.Retries; i++ { go func() { s, err := call(i) ch <- response{s, err} }() select { case <-ctx.Done(): return nil, errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) case rsp := <-ch: // if the call succeeded lets bail early if rsp.err == nil { return rsp.stream, nil } retry, rerr := callOpts.Retry(ctx, req, i, err) if rerr != nil { return nil, rerr } if !retry { return nil, rsp.err } grr = rsp.err } } return nil, grr }
func (g *grpcClient) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error { // make a copy of call opts callOpts := g.opts.CallOptions for _, opt := range opts { opt(&callOpts) } // get next nodes from the selector next, err := g.opts.Selector.Select(req.Service(), callOpts.SelectOptions...) if err != nil && err == selector.ErrNotFound { return errors.NotFound("go.micro.client", err.Error()) } else if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } // 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 := client.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 gcall := g.call // wrap the call in reverse for i := len(callOpts.CallWrappers); i > 0; i-- { gcall = callOpts.CallWrappers[i-1](gcall) } // 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, req, i) if err != nil { return errors.InternalServerError("go.micro.client", 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", err.Error()) } else if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } // set the address addr := node.Address if node.Port > 0 { addr = fmt.Sprintf("%s:%d", addr, node.Port) } // make the call err = gcall(ctx, addr, req, rsp, callOpts) g.opts.Selector.Mark(req.Service(), node, err) return err } ch := make(chan error, callOpts.Retries) var gerr error for i := 0; i < callOpts.Retries; i++ { go func() { ch <- call(i) }() select { case <-ctx.Done(): return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) case err := <-ch: // if the call succeeded lets bail early if err == nil { return nil } retry, rerr := callOpts.Retry(ctx, req, i, err) if rerr != nil { return rerr } if !retry { return err } gerr = err } } return gerr }
func (r *rpcClient) call(ctx context.Context, address string, req Request, resp interface{}, opts CallOptions) error { msg := &transport.Message{ Header: make(map[string]string), } md, ok := metadata.FromContext(ctx) if ok { for k, v := range md { msg.Header[k] = v } } // set timeout in nanoseconds msg.Header["Timeout"] = fmt.Sprintf("%d", opts.RequestTimeout) // set the content type for the request msg.Header["Content-Type"] = req.ContentType() cf, err := r.newCodec(req.ContentType()) if err != nil { return errors.InternalServerError("go.micro.client", err.Error()) } var grr error c, err := r.pool.getConn(address, r.opts.Transport, transport.WithTimeout(opts.DialTimeout)) if err != nil { return errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err)) } defer func() { // defer execution of release r.pool.release(address, c, grr) }() stream := &rpcStream{ context: ctx, request: req, closed: make(chan bool), codec: newRpcPlusCodec(msg, c, cf), } defer stream.Close() ch := make(chan error, 1) go func() { defer func() { if r := recover(); r != nil { ch <- errors.InternalServerError("go.micro.client", "request error") } }() // send request if err := stream.Send(req.Request()); err != nil { ch <- err return } // recv request if err := stream.Recv(resp); err != nil { ch <- err return } // success ch <- nil }() select { case err := <-ch: grr = err return err case <-ctx.Done(): grr = ctx.Err() return errors.New("go.micro.client", fmt.Sprintf("%v", ctx.Err()), 408) } }