commit
539b3d045c
@ -27,13 +27,13 @@ import (
|
||||
|
||||
type Client interface {
|
||||
NewPublication(topic string, msg interface{}) Publication
|
||||
NewRequest(service, method string, req interface{}) Request
|
||||
NewProtoRequest(service, method string, req interface{}) Request
|
||||
NewJsonRequest(service, method string, req interface{}) Request
|
||||
NewRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
||||
NewProtoRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
||||
NewJsonRequest(service, method string, req interface{}, reqOpts ...RequestOption) Request
|
||||
Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error
|
||||
CallRemote(ctx context.Context, addr string, req Request, rsp interface{}, opts ...CallOption) error
|
||||
Stream(ctx context.Context, req Request, rspChan interface{}, opts ...CallOption) (Streamer, error)
|
||||
StreamRemote(ctx context.Context, addr string, req Request, rspChan interface{}, opts ...CallOption) (Streamer, error)
|
||||
Stream(ctx context.Context, req Request, opts ...CallOption) (Streamer, error)
|
||||
StreamRemote(ctx context.Context, addr string, req Request, opts ...CallOption) (Streamer, error)
|
||||
Publish(ctx context.Context, p Publication, opts ...PublishOption) error
|
||||
}
|
||||
|
||||
@ -48,10 +48,15 @@ type Request interface {
|
||||
Method() string
|
||||
ContentType() string
|
||||
Request() interface{}
|
||||
// indicates whether the request will be a streaming one rather than unary
|
||||
Stream() bool
|
||||
}
|
||||
|
||||
type Streamer interface {
|
||||
Context() context.Context
|
||||
Request() Request
|
||||
Send(interface{}) error
|
||||
Recv(interface{}) error
|
||||
Error() error
|
||||
Close() error
|
||||
}
|
||||
@ -59,6 +64,7 @@ type Streamer interface {
|
||||
type Option func(*options)
|
||||
type CallOption func(*callOptions)
|
||||
type PublishOption func(*publishOptions)
|
||||
type RequestOption func(*requestOptions)
|
||||
|
||||
var (
|
||||
DefaultClient Client = newRpcClient()
|
||||
@ -76,13 +82,13 @@ func CallRemote(ctx context.Context, address string, request Request, response i
|
||||
|
||||
// Creates a streaming connection with a service and returns responses on the
|
||||
// channel passed in. It's upto the user to close the streamer.
|
||||
func Stream(ctx context.Context, request Request, responseChan interface{}, opts ...CallOption) (Streamer, error) {
|
||||
return DefaultClient.Stream(ctx, request, responseChan, opts...)
|
||||
func Stream(ctx context.Context, request Request, opts ...CallOption) (Streamer, error) {
|
||||
return DefaultClient.Stream(ctx, request, opts...)
|
||||
}
|
||||
|
||||
// Creates a streaming connection to the address specified.
|
||||
func StreamRemote(ctx context.Context, address string, request Request, responseChan interface{}, opts ...CallOption) (Streamer, error) {
|
||||
return DefaultClient.StreamRemote(ctx, address, request, responseChan, opts...)
|
||||
func StreamRemote(ctx context.Context, address string, request Request, opts ...CallOption) (Streamer, error) {
|
||||
return DefaultClient.StreamRemote(ctx, address, request, opts...)
|
||||
}
|
||||
|
||||
// Publishes a publication using the default client. Using the underlying broker
|
||||
@ -103,16 +109,16 @@ func NewPublication(topic string, message interface{}) Publication {
|
||||
|
||||
// Creates a new request using the default client. Content Type will
|
||||
// be set to the default within options and use the appropriate codec
|
||||
func NewRequest(service, method string, request interface{}) Request {
|
||||
return DefaultClient.NewRequest(service, method, request)
|
||||
func NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return DefaultClient.NewRequest(service, method, request, reqOpts...)
|
||||
}
|
||||
|
||||
// Creates a new protobuf request using the default client
|
||||
func NewProtoRequest(service, method string, request interface{}) Request {
|
||||
return DefaultClient.NewProtoRequest(service, method, request)
|
||||
func NewProtoRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return DefaultClient.NewProtoRequest(service, method, request, reqOpts...)
|
||||
}
|
||||
|
||||
// Creates a new json request using the default client
|
||||
func NewJsonRequest(service, method string, request interface{}) Request {
|
||||
return DefaultClient.NewJsonRequest(service, method, request)
|
||||
func NewJsonRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return DefaultClient.NewJsonRequest(service, method, request, reqOpts...)
|
||||
}
|
||||
|
@ -36,3 +36,6 @@ Example usage:
|
||||
|
||||
// Wrapper wraps a client and returns a client
|
||||
type Wrapper func(Client) Client
|
||||
|
||||
// StreamWrapper wraps a Stream and returns the equivalent
|
||||
type StreamWrapper func(Streamer) Streamer
|
||||
|
@ -24,6 +24,10 @@ type callOptions struct {
|
||||
|
||||
type publishOptions struct{}
|
||||
|
||||
type requestOptions struct {
|
||||
stream bool
|
||||
}
|
||||
|
||||
// Broker to be used for pub/sub
|
||||
func Broker(b broker.Broker) Option {
|
||||
return func(o *options) {
|
||||
@ -80,3 +84,11 @@ func WithSelectOption(so selector.SelectOption) CallOption {
|
||||
o.selectOptions = append(o.selectOptions, so)
|
||||
}
|
||||
}
|
||||
|
||||
// Request Options
|
||||
|
||||
func StreamingRequest() RequestOption {
|
||||
return func(o *requestOptions) {
|
||||
o.stream = true
|
||||
}
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ func (r *rpcClient) call(ctx context.Context, address string, request Request, r
|
||||
return client.Close()
|
||||
}
|
||||
|
||||
func (r *rpcClient) stream(ctx context.Context, address string, request Request, responseChan interface{}) (Streamer, error) {
|
||||
func (r *rpcClient) stream(ctx context.Context, address string, req Request) (Streamer, error) {
|
||||
msg := &transport.Message{
|
||||
Header: make(map[string]string),
|
||||
}
|
||||
@ -124,9 +124,9 @@ func (r *rpcClient) stream(ctx context.Context, address string, request Request,
|
||||
}
|
||||
}
|
||||
|
||||
msg.Header["Content-Type"] = request.ContentType()
|
||||
msg.Header["Content-Type"] = req.ContentType()
|
||||
|
||||
cf, err := r.newCodec(request.ContentType())
|
||||
cf, err := r.newCodec(req.ContentType())
|
||||
if err != nil {
|
||||
return nil, errors.InternalServerError("go.micro.client", err.Error())
|
||||
}
|
||||
@ -136,14 +136,14 @@ func (r *rpcClient) stream(ctx context.Context, address string, request Request,
|
||||
return nil, errors.InternalServerError("go.micro.client", fmt.Sprintf("Error sending request: %v", err))
|
||||
}
|
||||
|
||||
client := newClientWithCodec(newRpcPlusCodec(msg, c, cf))
|
||||
call := client.StreamGo(request.Service(), request.Method(), request.Request(), responseChan)
|
||||
stream := &rpcStream{
|
||||
context: ctx,
|
||||
request: req,
|
||||
codec: newRpcPlusCodec(msg, c, cf),
|
||||
}
|
||||
|
||||
return &rpcStream{
|
||||
request: request,
|
||||
call: call,
|
||||
client: client,
|
||||
}, nil
|
||||
err = stream.Send(req.Request())
|
||||
return stream, err
|
||||
}
|
||||
|
||||
func (r *rpcClient) CallRemote(ctx context.Context, address string, request Request, response interface{}, opts ...CallOption) error {
|
||||
@ -180,11 +180,11 @@ func (r *rpcClient) Call(ctx context.Context, request Request, response interfac
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *rpcClient) StreamRemote(ctx context.Context, address string, request Request, responseChan interface{}, opts ...CallOption) (Streamer, error) {
|
||||
return r.stream(ctx, address, request, responseChan)
|
||||
func (r *rpcClient) StreamRemote(ctx context.Context, address string, request Request, opts ...CallOption) (Streamer, error) {
|
||||
return r.stream(ctx, address, request)
|
||||
}
|
||||
|
||||
func (r *rpcClient) Stream(ctx context.Context, request Request, responseChan interface{}, opts ...CallOption) (Streamer, error) {
|
||||
func (r *rpcClient) Stream(ctx context.Context, request Request, opts ...CallOption) (Streamer, error) {
|
||||
var copts callOptions
|
||||
for _, opt := range opts {
|
||||
opt(&copts)
|
||||
@ -209,7 +209,7 @@ func (r *rpcClient) Stream(ctx context.Context, request Request, responseChan in
|
||||
address = fmt.Sprintf("%s:%d", address, node.Port)
|
||||
}
|
||||
|
||||
stream, err := r.stream(ctx, address, request, responseChan)
|
||||
stream, err := r.stream(ctx, address, request)
|
||||
r.opts.selector.Mark(request.Service(), node, err)
|
||||
return stream, err
|
||||
}
|
||||
@ -247,14 +247,14 @@ func (r *rpcClient) NewPublication(topic string, message interface{}) Publicatio
|
||||
func (r *rpcClient) NewProtoPublication(topic string, message interface{}) Publication {
|
||||
return newRpcPublication(topic, message, "application/octet-stream")
|
||||
}
|
||||
func (r *rpcClient) NewRequest(service, method string, request interface{}) Request {
|
||||
return newRpcRequest(service, method, request, r.opts.contentType)
|
||||
func (r *rpcClient) NewRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return newRpcRequest(service, method, request, r.opts.contentType, reqOpts...)
|
||||
}
|
||||
|
||||
func (r *rpcClient) NewProtoRequest(service, method string, request interface{}) Request {
|
||||
return newRpcRequest(service, method, request, "application/octet-stream")
|
||||
func (r *rpcClient) NewProtoRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return newRpcRequest(service, method, request, "application/octet-stream", reqOpts...)
|
||||
}
|
||||
|
||||
func (r *rpcClient) NewJsonRequest(service, method string, request interface{}) Request {
|
||||
return newRpcRequest(service, method, request, "application/json")
|
||||
func (r *rpcClient) NewJsonRequest(service, method string, request interface{}, reqOpts ...RequestOption) Request {
|
||||
return newRpcRequest(service, method, request, "application/json", reqOpts...)
|
||||
}
|
||||
|
@ -63,6 +63,7 @@ func newRpcPlusCodec(req *transport.Message, client transport.Client, c codec.Ne
|
||||
}
|
||||
|
||||
func (c *rpcPlusCodec) WriteRequest(req *request, body interface{}) error {
|
||||
c.buf.wbuf.Reset()
|
||||
m := &codec.Message{
|
||||
Id: req.Seq,
|
||||
Target: req.Service,
|
||||
|
@ -5,14 +5,22 @@ type rpcRequest struct {
|
||||
method string
|
||||
contentType string
|
||||
request interface{}
|
||||
opts requestOptions
|
||||
}
|
||||
|
||||
func newRpcRequest(service, method string, request interface{}, contentType string) Request {
|
||||
func newRpcRequest(service, method string, request interface{}, contentType string, reqOpts ...RequestOption) Request {
|
||||
var opts requestOptions
|
||||
|
||||
for _, o := range reqOpts {
|
||||
o(&opts)
|
||||
}
|
||||
|
||||
return &rpcRequest{
|
||||
service: service,
|
||||
method: method,
|
||||
request: request,
|
||||
contentType: contentType,
|
||||
opts: opts,
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,3 +39,7 @@ func (r *rpcRequest) Method() string {
|
||||
func (r *rpcRequest) Request() interface{} {
|
||||
return r.request
|
||||
}
|
||||
|
||||
func (r *rpcRequest) Stream() bool {
|
||||
return r.opts.stream
|
||||
}
|
||||
|
@ -1,19 +1,112 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Implements the streamer interface
|
||||
type rpcStream struct {
|
||||
sync.RWMutex
|
||||
seq uint64
|
||||
closed bool
|
||||
err error
|
||||
request Request
|
||||
call *call
|
||||
client *client
|
||||
codec clientCodec
|
||||
context context.Context
|
||||
}
|
||||
|
||||
func (r *rpcStream) Context() context.Context {
|
||||
return r.context
|
||||
}
|
||||
|
||||
func (r *rpcStream) Request() Request {
|
||||
return r.request
|
||||
}
|
||||
|
||||
func (r *rpcStream) Send(msg interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
if r.closed {
|
||||
r.err = errShutdown
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
seq := r.seq
|
||||
r.seq++
|
||||
|
||||
req := request{
|
||||
Service: r.request.Service(),
|
||||
Seq: seq,
|
||||
ServiceMethod: r.request.Method(),
|
||||
}
|
||||
|
||||
if err := r.codec.WriteRequest(&req, msg); err != nil {
|
||||
r.err = err
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rpcStream) Recv(msg interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
if r.closed {
|
||||
r.err = errShutdown
|
||||
return errShutdown
|
||||
}
|
||||
|
||||
var resp response
|
||||
if err := r.codec.ReadResponseHeader(&resp); err != nil {
|
||||
if err == io.EOF && !r.closed {
|
||||
r.err = io.ErrUnexpectedEOF
|
||||
return io.ErrUnexpectedEOF
|
||||
}
|
||||
r.err = err
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(resp.Error) > 0:
|
||||
// We've got an error response. Give this to the request;
|
||||
// any subsequent requests will get the ReadResponseBody
|
||||
// error if there is one.
|
||||
if resp.Error != lastStreamResponseError {
|
||||
r.err = serverError(resp.Error)
|
||||
} else {
|
||||
r.err = io.EOF
|
||||
}
|
||||
if err := r.codec.ReadResponseBody(nil); err != nil {
|
||||
r.err = errors.New("reading error payload: " + err.Error())
|
||||
}
|
||||
default:
|
||||
if err := r.codec.ReadResponseBody(msg); err != nil {
|
||||
r.err = errors.New("reading body " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
if r.err != nil && r.err != io.EOF && !r.closed {
|
||||
log.Println("rpc: client protocol error:", r.err)
|
||||
}
|
||||
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *rpcStream) Error() error {
|
||||
return r.call.Error
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *rpcStream) Close() error {
|
||||
return r.client.Close()
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
r.closed = true
|
||||
return r.codec.Close()
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
"github.com/youtube/vitess/go/trace"
|
||||
@ -38,7 +37,6 @@ type call struct {
|
||||
Reply interface{} // The reply from the function (*struct for single, chan * struct for streaming).
|
||||
Error error // After completion, the error status.
|
||||
Done chan *call // Strobes when call is complete (nil for streaming RPCs)
|
||||
Stream bool // True for a streaming RPC call, false otherwise
|
||||
Subseq uint64 // The next expected subseq in the packets
|
||||
}
|
||||
|
||||
@ -145,28 +143,12 @@ func (client *client) input() {
|
||||
// We've got an error response. Give this to the request;
|
||||
// any subsequent requests will get the ReadResponseBody
|
||||
// error if there is one.
|
||||
if !(call.Stream && resp.Error == lastStreamResponseError) {
|
||||
call.Error = serverError(resp.Error)
|
||||
}
|
||||
call.Error = serverError(resp.Error)
|
||||
err = client.codec.ReadResponseBody(nil)
|
||||
if err != nil {
|
||||
err = errors.New("reading error payload: " + err.Error())
|
||||
}
|
||||
client.done(seq)
|
||||
case call.Stream:
|
||||
// call.Reply is a chan *T2
|
||||
// we need to create a T2 and get a *T2 back
|
||||
value := reflect.New(reflect.TypeOf(call.Reply).Elem().Elem()).Interface()
|
||||
err = client.codec.ReadResponseBody(value)
|
||||
if err != nil {
|
||||
call.Error = errors.New("reading body " + err.Error())
|
||||
} else {
|
||||
// writing on the channel could block forever. For
|
||||
// instance, if a client calls 'close', this might block
|
||||
// forever. the current suggestion is for the
|
||||
// client to drain the receiving channel in that case
|
||||
reflect.ValueOf(call.Reply).Send(reflect.ValueOf(value))
|
||||
}
|
||||
default:
|
||||
err = client.codec.ReadResponseBody(call.Reply)
|
||||
if err != nil {
|
||||
@ -203,12 +185,6 @@ func (client *client) done(seq uint64) {
|
||||
}
|
||||
|
||||
func (call *call) done() {
|
||||
if call.Stream {
|
||||
// need to close the channel. client won't be able to read any more.
|
||||
reflect.ValueOf(call.Reply).Close()
|
||||
return
|
||||
}
|
||||
|
||||
select {
|
||||
case call.Done <- call:
|
||||
// ok
|
||||
@ -270,28 +246,6 @@ func (client *client) Go(ctx context.Context, service, serviceMethod string, arg
|
||||
return cal
|
||||
}
|
||||
|
||||
// StreamGo invokes the streaming function asynchronously. It returns the call structure representing
|
||||
// the invocation.
|
||||
func (client *client) StreamGo(service string, serviceMethod string, args interface{}, replyStream interface{}) *call {
|
||||
// first check the replyStream object is a stream of pointers to a data structure
|
||||
typ := reflect.TypeOf(replyStream)
|
||||
// FIXME: check the direction of the channel, maybe?
|
||||
if typ.Kind() != reflect.Chan || typ.Elem().Kind() != reflect.Ptr {
|
||||
log.Panic("rpc: replyStream is not a channel of pointers")
|
||||
return nil
|
||||
}
|
||||
|
||||
call := new(call)
|
||||
call.Service = service
|
||||
call.ServiceMethod = serviceMethod
|
||||
call.Args = args
|
||||
call.Reply = replyStream
|
||||
call.Stream = true
|
||||
call.Subseq = 0
|
||||
client.send(call)
|
||||
return call
|
||||
}
|
||||
|
||||
// call invokes the named function, waits for it to complete, and returns its error status.
|
||||
func (client *client) Call(ctx context.Context, service string, serviceMethod string, args interface{}, reply interface{}) error {
|
||||
call := <-client.Go(ctx, service, serviceMethod, args, reply, make(chan *call, 1)).Done
|
||||
|
@ -21,14 +21,14 @@ func call(i int) {
|
||||
fmt.Println("Call:", i, "rsp:", rsp.Msg)
|
||||
}
|
||||
|
||||
func stream() {
|
||||
stream, err := cl.Stream(context.Background(), &example.StreamingRequest{Count: int64(10)})
|
||||
func stream(i int) {
|
||||
stream, err := cl.Stream(context.Background(), &example.StreamingRequest{Count: int64(i)})
|
||||
if err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
rsp, err := stream.Next()
|
||||
for j := 0; j < i; j++ {
|
||||
rsp, err := stream.RecvR()
|
||||
if err != nil {
|
||||
fmt.Println("err:", err)
|
||||
break
|
||||
@ -44,6 +44,34 @@ func stream() {
|
||||
}
|
||||
}
|
||||
|
||||
func pingPong(i int) {
|
||||
stream, err := cl.PingPong(context.Background())
|
||||
if err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
for j := 0; j < i; j++ {
|
||||
if err := stream.SendR(&example.Ping{Stroke: int64(j)}); err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
rsp, err := stream.RecvR()
|
||||
if err != nil {
|
||||
fmt.Println("recv err", err)
|
||||
break
|
||||
}
|
||||
fmt.Printf("Sent ping %v got pong %v\n", j, rsp.Stroke)
|
||||
}
|
||||
if stream.Error() != nil {
|
||||
fmt.Println("stream err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := stream.Close(); err != nil {
|
||||
fmt.Println("stream close err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
cmd.Init()
|
||||
|
||||
@ -51,6 +79,10 @@ func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
call(i)
|
||||
}
|
||||
|
||||
fmt.Println("\n--- Streamer example ---\n")
|
||||
stream()
|
||||
stream(10)
|
||||
|
||||
fmt.Println("\n--- Ping Pong example ---\n")
|
||||
pingPong(10)
|
||||
}
|
||||
|
@ -54,22 +54,63 @@ func call(i int) {
|
||||
fmt.Println("Call:", i, "rsp:", rsp.Msg)
|
||||
}
|
||||
|
||||
func stream() {
|
||||
func stream(i int) {
|
||||
// Create new request to service go.micro.srv.example, method Example.Call
|
||||
req := client.NewRequest("go.micro.srv.example", "Example.Stream", &example.StreamingRequest{
|
||||
Count: int64(10),
|
||||
})
|
||||
// Request can be empty as its actually ignored and merely used to call the handler
|
||||
req := client.NewRequest("go.micro.srv.example", "Example.Stream", &example.StreamingRequest{})
|
||||
|
||||
rspChan := make(chan *example.StreamingResponse, 10)
|
||||
stream, err := client.Stream(context.Background(), req)
|
||||
if err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
if err := stream.Send(&example.StreamingRequest{Count: int64(i)}); err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
for stream.Error() == nil {
|
||||
rsp := &example.StreamingResponse{}
|
||||
err := stream.Recv(rsp)
|
||||
if err != nil {
|
||||
fmt.Println("recv err", err)
|
||||
break
|
||||
}
|
||||
fmt.Println("Stream: rsp:", rsp.Count)
|
||||
}
|
||||
|
||||
stream, err := client.Stream(context.Background(), req, rspChan)
|
||||
if stream.Error() != nil {
|
||||
fmt.Println("stream err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := stream.Close(); err != nil {
|
||||
fmt.Println("stream close err:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func pingPong(i int) {
|
||||
// Create new request to service go.micro.srv.example, method Example.Call
|
||||
// Request can be empty as its actually ignored and merely used to call the handler
|
||||
req := client.NewRequest("go.micro.srv.example", "Example.PingPong", &example.StreamingRequest{})
|
||||
|
||||
stream, err := client.Stream(context.Background(), req)
|
||||
if err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
|
||||
for rsp := range rspChan {
|
||||
fmt.Println("Stream: rsp:", rsp.Count)
|
||||
for j := 0; j < i; j++ {
|
||||
if err := stream.Send(&example.Ping{Stroke: int64(j + 1)}); err != nil {
|
||||
fmt.Println("err:", err)
|
||||
return
|
||||
}
|
||||
rsp := &example.Pong{}
|
||||
err := stream.Recv(rsp)
|
||||
if err != nil {
|
||||
fmt.Println("recv err", err)
|
||||
break
|
||||
}
|
||||
fmt.Printf("Sent ping %v got pong %v\n", j+1, rsp.Stroke)
|
||||
}
|
||||
|
||||
if stream.Error() != nil {
|
||||
@ -90,7 +131,10 @@ func main() {
|
||||
}
|
||||
|
||||
fmt.Println("\n--- Streamer example ---\n")
|
||||
stream()
|
||||
stream(10)
|
||||
|
||||
fmt.Println("\n--- Ping Pong example ---\n")
|
||||
pingPong(10)
|
||||
|
||||
fmt.Println("\n--- Publisher example ---\n")
|
||||
pub()
|
||||
|
@ -18,19 +18,41 @@ func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.R
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Example) Stream(ctx context.Context, req *example.StreamingRequest, response func(interface{}) error) error {
|
||||
func (e *Example) Stream(ctx context.Context, stream server.Streamer) error {
|
||||
log.Info("Executing streaming handler")
|
||||
req := &example.StreamingRequest{}
|
||||
|
||||
// We just want to receive 1 request and then process here
|
||||
if err := stream.Recv(req); err != nil {
|
||||
log.Errorf("Error receiving streaming request: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Infof("Received Example.Stream request with count: %d", req.Count)
|
||||
|
||||
for i := 0; i < int(req.Count); i++ {
|
||||
log.Infof("Responding: %d", i)
|
||||
|
||||
r := &example.StreamingResponse{
|
||||
if err := stream.Send(&example.StreamingResponse{
|
||||
Count: int64(i),
|
||||
}
|
||||
|
||||
if err := response(r); err != nil {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *Example) PingPong(ctx context.Context, stream server.Streamer) error {
|
||||
for {
|
||||
req := &example.Ping{}
|
||||
if err := stream.Recv(req); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infof("Got ping %v", req.Stroke)
|
||||
if err := stream.Send(&example.Pong{Stroke: req.Stroke}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -14,6 +14,8 @@ It has these top-level messages:
|
||||
Response
|
||||
StreamingRequest
|
||||
StreamingResponse
|
||||
Ping
|
||||
Pong
|
||||
*/
|
||||
package go_micro_srv_example
|
||||
|
||||
@ -77,12 +79,32 @@ func (m *StreamingResponse) String() string { return proto.CompactTex
|
||||
func (*StreamingResponse) ProtoMessage() {}
|
||||
func (*StreamingResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} }
|
||||
|
||||
type Ping struct {
|
||||
Stroke int64 `protobuf:"varint,1,opt,name=stroke" json:"stroke,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Ping) Reset() { *m = Ping{} }
|
||||
func (m *Ping) String() string { return proto.CompactTextString(m) }
|
||||
func (*Ping) ProtoMessage() {}
|
||||
func (*Ping) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} }
|
||||
|
||||
type Pong struct {
|
||||
Stroke int64 `protobuf:"varint,1,opt,name=stroke" json:"stroke,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Pong) Reset() { *m = Pong{} }
|
||||
func (m *Pong) String() string { return proto.CompactTextString(m) }
|
||||
func (*Pong) ProtoMessage() {}
|
||||
func (*Pong) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} }
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Message)(nil), "go.micro.srv.example.Message")
|
||||
proto.RegisterType((*Request)(nil), "go.micro.srv.example.Request")
|
||||
proto.RegisterType((*Response)(nil), "go.micro.srv.example.Response")
|
||||
proto.RegisterType((*StreamingRequest)(nil), "go.micro.srv.example.StreamingRequest")
|
||||
proto.RegisterType((*StreamingResponse)(nil), "go.micro.srv.example.StreamingResponse")
|
||||
proto.RegisterType((*Ping)(nil), "go.micro.srv.example.Ping")
|
||||
proto.RegisterType((*Pong)(nil), "go.micro.srv.example.Pong")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
@ -95,6 +117,7 @@ var _ server.Option
|
||||
type ExampleClient interface {
|
||||
Call(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
|
||||
Stream(ctx context.Context, in *StreamingRequest, opts ...client.CallOption) (Example_StreamClient, error)
|
||||
PingPong(ctx context.Context, opts ...client.CallOption) (Example_PingPongClient, error)
|
||||
}
|
||||
|
||||
type exampleClient struct {
|
||||
@ -126,59 +149,151 @@ func (c *exampleClient) Call(ctx context.Context, in *Request, opts ...client.Ca
|
||||
}
|
||||
|
||||
func (c *exampleClient) Stream(ctx context.Context, in *StreamingRequest, opts ...client.CallOption) (Example_StreamClient, error) {
|
||||
req := c.c.NewRequest(c.serviceName, "Example.Stream", in)
|
||||
outCh := make(chan *StreamingResponse)
|
||||
stream, err := c.c.Stream(ctx, req, outCh, opts...)
|
||||
req := c.c.NewRequest(c.serviceName, "Example.Stream", &StreamingRequest{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &exampleStreamClient{stream, outCh}, nil
|
||||
if err := stream.Send(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &exampleStreamClient{stream}, nil
|
||||
}
|
||||
|
||||
type Example_StreamClient interface {
|
||||
Next() (*StreamingResponse, error)
|
||||
RecvR() (*StreamingResponse, error)
|
||||
client.Streamer
|
||||
}
|
||||
|
||||
type exampleStreamClient struct {
|
||||
client.Streamer
|
||||
next chan *StreamingResponse
|
||||
}
|
||||
|
||||
func (x *exampleStreamClient) Next() (*StreamingResponse, error) {
|
||||
out, ok := <-x.next
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`chan closed`)
|
||||
func (x *exampleStreamClient) RecvR() (*StreamingResponse, error) {
|
||||
m := new(StreamingResponse)
|
||||
err := x.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (c *exampleClient) PingPong(ctx context.Context, opts ...client.CallOption) (Example_PingPongClient, error) {
|
||||
req := c.c.NewRequest(c.serviceName, "Example.PingPong", &Ping{})
|
||||
stream, err := c.c.Stream(ctx, req, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &examplePingPongClient{stream}, nil
|
||||
}
|
||||
|
||||
type Example_PingPongClient interface {
|
||||
SendR(*Ping) error
|
||||
RecvR() (*Pong, error)
|
||||
client.Streamer
|
||||
}
|
||||
|
||||
type examplePingPongClient struct {
|
||||
client.Streamer
|
||||
}
|
||||
|
||||
func (x *examplePingPongClient) SendR(m *Ping) error {
|
||||
return x.Send(m)
|
||||
}
|
||||
|
||||
func (x *examplePingPongClient) RecvR() (*Pong, error) {
|
||||
m := new(Pong)
|
||||
err := x.Recv(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Server API for Example service
|
||||
|
||||
type ExampleHandler interface {
|
||||
Call(context.Context, *Request, *Response) error
|
||||
Stream(context.Context, func(*StreamingResponse) error) error
|
||||
Stream(context.Context, *StreamingRequest, Example_StreamStream) error
|
||||
PingPong(context.Context, Example_PingPongStream) error
|
||||
}
|
||||
|
||||
func RegisterExampleHandler(s server.Server, hdlr ExampleHandler) {
|
||||
s.Handle(s.NewHandler(hdlr))
|
||||
s.Handle(s.NewHandler(&exampleHandler{hdlr}))
|
||||
}
|
||||
|
||||
type exampleHandler struct {
|
||||
ExampleHandler
|
||||
}
|
||||
|
||||
func (h *exampleHandler) Call(ctx context.Context, in *Request, out *Response) error {
|
||||
return h.ExampleHandler.Call(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *exampleHandler) Stream(ctx context.Context, stream server.Streamer) error {
|
||||
m := new(StreamingRequest)
|
||||
if err := stream.Recv(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return h.ExampleHandler.Stream(ctx, m, &exampleStreamStream{stream})
|
||||
}
|
||||
|
||||
type Example_StreamStream interface {
|
||||
SendR(*StreamingResponse) error
|
||||
server.Streamer
|
||||
}
|
||||
|
||||
type exampleStreamStream struct {
|
||||
server.Streamer
|
||||
}
|
||||
|
||||
func (x *exampleStreamStream) SendR(m *StreamingResponse) error {
|
||||
return x.Streamer.Send(m)
|
||||
}
|
||||
|
||||
func (h *exampleHandler) PingPong(ctx context.Context, stream server.Streamer) error {
|
||||
return h.ExampleHandler.PingPong(ctx, &examplePingPongStream{stream})
|
||||
}
|
||||
|
||||
type Example_PingPongStream interface {
|
||||
SendR(*Pong) error
|
||||
RecvR() (*Ping, error)
|
||||
server.Streamer
|
||||
}
|
||||
|
||||
type examplePingPongStream struct {
|
||||
server.Streamer
|
||||
}
|
||||
|
||||
func (x *examplePingPongStream) SendR(m *Pong) error {
|
||||
return x.Streamer.Send(m)
|
||||
}
|
||||
|
||||
func (x *examplePingPongStream) RecvR() (*Ping, error) {
|
||||
m := new(Ping)
|
||||
if err := x.Streamer.Recv(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
var fileDescriptor0 = []byte{
|
||||
// 230 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x90, 0xcd, 0x4a, 0xc5, 0x30,
|
||||
0x10, 0x85, 0x0d, 0xf7, 0x7a, 0xab, 0xa3, 0x82, 0x06, 0x51, 0x29, 0x28, 0x9a, 0x85, 0xba, 0x31,
|
||||
0x15, 0xf5, 0x0d, 0x44, 0x5c, 0xb9, 0xa9, 0x6b, 0x17, 0xb1, 0x0c, 0xa1, 0xd0, 0x24, 0x35, 0x93,
|
||||
0x16, 0x7d, 0x2c, 0xdf, 0x50, 0x48, 0xd3, 0xa2, 0x52, 0x71, 0x15, 0x98, 0xf3, 0x9d, 0x1f, 0x02,
|
||||
0x77, 0xda, 0x5d, 0x99, 0xba, 0xf2, 0xae, 0xc0, 0x77, 0x65, 0xda, 0x06, 0xa9, 0x20, 0xf4, 0x3d,
|
||||
0xfa, 0xa2, 0xf5, 0x2e, 0x4c, 0xd7, 0xf1, 0x95, 0xf1, 0xca, 0xf7, 0xb5, 0x93, 0xd1, 0x25, 0xc9,
|
||||
0xf7, 0x32, 0x69, 0xe2, 0x00, 0xb2, 0x27, 0x24, 0x52, 0x1a, 0xf9, 0x16, 0x2c, 0x48, 0x7d, 0x1c,
|
||||
0xb1, 0x53, 0x76, 0xb9, 0x29, 0x0e, 0x21, 0x2b, 0xf1, 0xad, 0x43, 0x0a, 0x7c, 0x1b, 0x96, 0x56,
|
||||
0x19, 0x9c, 0x84, 0x8d, 0x12, 0xa9, 0x75, 0x96, 0xa2, 0xc3, 0x90, 0x4e, 0xc2, 0x19, 0xec, 0x3e,
|
||||
0x07, 0x8f, 0xca, 0xd4, 0x56, 0x8f, 0xd6, 0x1d, 0x58, 0xaf, 0x5c, 0x67, 0x43, 0x44, 0x16, 0x42,
|
||||
0xc0, 0xde, 0x37, 0x24, 0x85, 0xfc, 0x64, 0x6e, 0x3e, 0x19, 0x64, 0x0f, 0xc3, 0x38, 0xfe, 0x08,
|
||||
0xcb, 0x7b, 0xd5, 0x34, 0xfc, 0x58, 0xce, 0x6d, 0x97, 0xa9, 0x25, 0x3f, 0xf9, 0x4b, 0x1e, 0x1a,
|
||||
0xc4, 0x1a, 0x7f, 0x81, 0xd5, 0x50, 0xcc, 0xcf, 0xe7, 0xd9, 0xdf, 0xcb, 0xf3, 0x8b, 0x7f, 0xb9,
|
||||
0x31, 0xfc, 0x9a, 0xbd, 0xae, 0xe2, 0x0f, 0xdf, 0x7e, 0x05, 0x00, 0x00, 0xff, 0xff, 0x63, 0x02,
|
||||
0xbf, 0x5f, 0x99, 0x01, 0x00, 0x00,
|
||||
// 270 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x84, 0x91, 0x5f, 0x4b, 0xc3, 0x30,
|
||||
0x14, 0xc5, 0x17, 0x56, 0xdb, 0x79, 0xfd, 0x83, 0x06, 0x99, 0x52, 0x50, 0x34, 0x0f, 0xba, 0x17,
|
||||
0xd3, 0xa1, 0x7e, 0x03, 0x11, 0x7d, 0x11, 0x64, 0x3e, 0xfb, 0x10, 0xc7, 0x25, 0x0c, 0x9b, 0xa6,
|
||||
0xe6, 0x66, 0x43, 0x3f, 0xbb, 0x2f, 0x6e, 0x69, 0x3b, 0xc6, 0xec, 0xf0, 0x29, 0x70, 0x7e, 0xe7,
|
||||
0x5c, 0xce, 0x21, 0x70, 0xa7, 0xed, 0xb5, 0x99, 0x8c, 0x9d, 0xcd, 0xf0, 0x4b, 0x99, 0x32, 0x47,
|
||||
0xca, 0x08, 0xdd, 0x0c, 0x5d, 0x56, 0x3a, 0xeb, 0x97, 0x6a, 0xf3, 0xca, 0xa0, 0xf2, 0x23, 0x6d,
|
||||
0x65, 0x48, 0x49, 0x72, 0x33, 0x59, 0x33, 0xd1, 0x87, 0xe4, 0x19, 0x89, 0x94, 0x46, 0xbe, 0x03,
|
||||
0x5d, 0x52, 0xdf, 0x27, 0xec, 0x9c, 0x0d, 0xb6, 0xc5, 0x31, 0x24, 0x23, 0xfc, 0x9c, 0x22, 0x79,
|
||||
0xbe, 0x0b, 0x51, 0xa1, 0x0c, 0x2e, 0x41, 0x6f, 0x84, 0x54, 0xda, 0x82, 0x42, 0xc2, 0x90, 0xae,
|
||||
0xc1, 0x05, 0x1c, 0xbc, 0x7a, 0x87, 0xca, 0x4c, 0x0a, 0xdd, 0x44, 0xf7, 0x60, 0x6b, 0x6c, 0xa7,
|
||||
0x85, 0x0f, 0x96, 0xae, 0x10, 0x70, 0xb8, 0x62, 0xa9, 0x8f, 0xac, 0x79, 0xfa, 0x10, 0xbd, 0xcc,
|
||||
0x31, 0xdf, 0x87, 0x98, 0xbc, 0xb3, 0x1f, 0xb8, 0xa2, 0xdb, 0xbf, 0xfa, 0xcd, 0x0f, 0x83, 0xe4,
|
||||
0xa1, 0x1a, 0xc3, 0x1f, 0x21, 0xba, 0x57, 0x79, 0xce, 0x4f, 0x65, 0xdb, 0x56, 0x59, 0xb7, 0x4a,
|
||||
0xcf, 0x36, 0xe1, 0xaa, 0x91, 0xe8, 0xf0, 0x37, 0x88, 0xab, 0xa2, 0xfc, 0xb2, 0xdd, 0xbb, 0xbe,
|
||||
0x34, 0xbd, 0xfa, 0xd7, 0xd7, 0x1c, 0x1f, 0x32, 0xfe, 0x04, 0xbd, 0xc5, 0xc6, 0xb0, 0x27, 0x6d,
|
||||
0x0f, 0x2e, 0x78, 0xba, 0x89, 0xcd, 0x73, 0xa2, 0x33, 0x60, 0x43, 0xf6, 0x1e, 0x87, 0xbf, 0xbd,
|
||||
0xfd, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x53, 0xb5, 0xeb, 0x31, 0x13, 0x02, 0x00, 0x00,
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ package go.micro.srv.example;
|
||||
service Example {
|
||||
rpc Call(Request) returns (Response) {}
|
||||
rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
|
||||
rpc PingPong(stream Ping) returns (stream Pong) {}
|
||||
}
|
||||
|
||||
message Message {
|
||||
@ -26,3 +27,11 @@ message StreamingRequest {
|
||||
message StreamingResponse {
|
||||
int64 count = 1;
|
||||
}
|
||||
|
||||
message Ping {
|
||||
int64 stroke = 1;
|
||||
}
|
||||
|
||||
message Pong {
|
||||
int64 stroke = 1;
|
||||
}
|
||||
|
@ -60,9 +60,20 @@ func newRpcPlusCodec(req *transport.Message, socket transport.Socket, c codec.Ne
|
||||
return r
|
||||
}
|
||||
|
||||
func (c *rpcPlusCodec) ReadRequestHeader(r *request) error {
|
||||
m := codec.Message{
|
||||
Headers: c.req.Header,
|
||||
func (c *rpcPlusCodec) ReadRequestHeader(r *request, first bool) error {
|
||||
m := codec.Message{Headers: c.req.Header}
|
||||
|
||||
if !first {
|
||||
var tm transport.Message
|
||||
if err := c.socket.Recv(&tm); err != nil {
|
||||
return err
|
||||
}
|
||||
c.buf.rbuf.Reset()
|
||||
if _, err := c.buf.rbuf.Write(tm.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Headers = tm.Header
|
||||
}
|
||||
|
||||
err := c.codec.ReadHeader(&m, codec.Request)
|
||||
|
78
server/rpc_stream.go
Normal file
78
server/rpc_stream.go
Normal file
@ -0,0 +1,78 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
// Implements the Streamer interface
|
||||
type rpcStream struct {
|
||||
sync.RWMutex
|
||||
seq uint64
|
||||
closed bool
|
||||
err error
|
||||
request Request
|
||||
codec serverCodec
|
||||
context context.Context
|
||||
}
|
||||
|
||||
func (r *rpcStream) Context() context.Context {
|
||||
return r.context
|
||||
}
|
||||
|
||||
func (r *rpcStream) Request() Request {
|
||||
return r.request
|
||||
}
|
||||
|
||||
func (r *rpcStream) Send(msg interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
seq := r.seq
|
||||
r.seq++
|
||||
|
||||
resp := response{
|
||||
ServiceMethod: r.request.Method(),
|
||||
Seq: seq,
|
||||
}
|
||||
|
||||
err := r.codec.WriteResponse(&resp, msg, false)
|
||||
if err != nil {
|
||||
log.Println("rpc: writing response:", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (r *rpcStream) Recv(msg interface{}) error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
|
||||
req := request{}
|
||||
|
||||
if err := r.codec.ReadRequestHeader(&req, false); err != nil {
|
||||
// discard body
|
||||
r.codec.ReadRequestBody(nil)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := r.codec.ReadRequestBody(msg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *rpcStream) Error() error {
|
||||
r.RLock()
|
||||
defer r.RUnlock()
|
||||
return r.err
|
||||
}
|
||||
|
||||
func (r *rpcStream) Close() error {
|
||||
r.Lock()
|
||||
defer r.Unlock()
|
||||
r.closed = true
|
||||
return r.codec.Close()
|
||||
}
|
@ -102,14 +102,19 @@ func prepareMethod(method reflect.Method) *methodType {
|
||||
mtype := method.Type
|
||||
mname := method.Name
|
||||
var replyType, argType, contextType reflect.Type
|
||||
var stream bool
|
||||
|
||||
stream := false
|
||||
// Method must be exported.
|
||||
if method.PkgPath != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch mtype.NumIn() {
|
||||
case 3:
|
||||
// assuming streaming
|
||||
argType = mtype.In(2)
|
||||
contextType = mtype.In(1)
|
||||
stream = true
|
||||
case 4:
|
||||
// method that takes a context
|
||||
argType = mtype.In(2)
|
||||
@ -120,44 +125,34 @@ func prepareMethod(method reflect.Method) *methodType {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First arg need not be a pointer.
|
||||
if !isExportedOrBuiltinType(argType) {
|
||||
log.Println(mname, "argument type not exported:", argType)
|
||||
return nil
|
||||
}
|
||||
if stream {
|
||||
// check stream type
|
||||
streamType := reflect.TypeOf((*Streamer)(nil)).Elem()
|
||||
if !argType.Implements(streamType) {
|
||||
log.Println(mname, "argument does not implement Streamer interface:", argType)
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// if not stream check the replyType
|
||||
|
||||
// the second argument will tell us if it's a streaming call
|
||||
// or a regular call
|
||||
if replyType.Kind() == reflect.Func {
|
||||
// this is a streaming call
|
||||
stream = true
|
||||
if replyType.NumIn() != 1 {
|
||||
log.Println("method", mname, "sendReply has wrong number of ins:", replyType.NumIn())
|
||||
return nil
|
||||
}
|
||||
if replyType.In(0).Kind() != reflect.Interface {
|
||||
log.Println("method", mname, "sendReply parameter type not an interface:", replyType.In(0))
|
||||
return nil
|
||||
}
|
||||
if replyType.NumOut() != 1 {
|
||||
log.Println("method", mname, "sendReply has wrong number of outs:", replyType.NumOut())
|
||||
return nil
|
||||
}
|
||||
if returnType := replyType.Out(0); returnType != typeOfError {
|
||||
log.Println("method", mname, "sendReply returns", returnType.String(), "not error")
|
||||
// First arg need not be a pointer.
|
||||
if !isExportedOrBuiltinType(argType) {
|
||||
log.Println(mname, "argument type not exported:", argType)
|
||||
return nil
|
||||
}
|
||||
|
||||
} else if replyType.Kind() != reflect.Ptr {
|
||||
log.Println("method", mname, "reply type not a pointer:", replyType)
|
||||
return nil
|
||||
if replyType.Kind() != reflect.Ptr {
|
||||
log.Println("method", mname, "reply type not a pointer:", replyType)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reply type must be exported.
|
||||
if !isExportedOrBuiltinType(replyType) {
|
||||
log.Println("method", mname, "reply type not exported:", replyType)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Reply type must be exported.
|
||||
if !isExportedOrBuiltinType(replyType) {
|
||||
log.Println("method", mname, "reply type not exported:", replyType)
|
||||
return nil
|
||||
}
|
||||
// Method needs one out.
|
||||
if mtype.NumOut() != 1 {
|
||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||
@ -242,10 +237,11 @@ func (s *service) call(ctx context.Context, server *server, sending *sync.Mutex,
|
||||
service: s.name,
|
||||
contentType: ct,
|
||||
method: req.ServiceMethod,
|
||||
request: argv.Interface(),
|
||||
}
|
||||
|
||||
if !mtype.stream {
|
||||
r.request = argv.Interface()
|
||||
|
||||
fn := func(ctx context.Context, req Request, rsp interface{}) error {
|
||||
returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(req.Request()), reflect.ValueOf(rsp)})
|
||||
|
||||
@ -276,40 +272,16 @@ func (s *service) call(ctx context.Context, server *server, sending *sync.Mutex,
|
||||
// keep track of the type, to make sure we return
|
||||
// the same one consistently
|
||||
var lastError error
|
||||
var firstType reflect.Type
|
||||
|
||||
sendReply := func(oneReply interface{}) error {
|
||||
|
||||
// we already triggered an error, we're done
|
||||
if lastError != nil {
|
||||
return lastError
|
||||
}
|
||||
|
||||
// check the oneReply has the right type using reflection
|
||||
typ := reflect.TypeOf(oneReply)
|
||||
if firstType == nil {
|
||||
firstType = typ
|
||||
} else {
|
||||
if firstType != typ {
|
||||
log.Println("passing wrong type to sendReply",
|
||||
firstType, "!=", typ)
|
||||
lastError = errors.New("rpc: passing wrong type to sendReply")
|
||||
return lastError
|
||||
}
|
||||
}
|
||||
|
||||
lastError = server.sendResponse(sending, req, oneReply, codec, "", false)
|
||||
if lastError != nil {
|
||||
return lastError
|
||||
}
|
||||
|
||||
// we manage to send, we're good
|
||||
return nil
|
||||
stream := &rpcStream{
|
||||
context: ctx,
|
||||
codec: codec,
|
||||
request: r,
|
||||
}
|
||||
|
||||
// Invoke the method, providing a new value for the reply.
|
||||
fn := func(ctx context.Context, req Request, rspFn interface{}) error {
|
||||
returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(req.Request()), reflect.ValueOf(rspFn)})
|
||||
fn := func(ctx context.Context, req Request, stream interface{}) error {
|
||||
returnValues = function.Call([]reflect.Value{s.rcvr, mtype.prepareContext(ctx), reflect.ValueOf(stream)})
|
||||
if err := returnValues[0].Interface(); err != nil {
|
||||
// the function returned an error, we use that
|
||||
return err.(error)
|
||||
@ -331,7 +303,7 @@ func (s *service) call(ctx context.Context, server *server, sending *sync.Mutex,
|
||||
r.stream = true
|
||||
|
||||
errmsg := ""
|
||||
if err := fn(ctx, r, reflect.ValueOf(sendReply).Interface()); err != nil {
|
||||
if err := fn(ctx, r, stream); err != nil {
|
||||
errmsg = err.Error()
|
||||
}
|
||||
|
||||
@ -417,6 +389,11 @@ func (server *server) readRequest(codec serverCodec) (service *service, mtype *m
|
||||
codec.ReadRequestBody(nil)
|
||||
return
|
||||
}
|
||||
// is it a streaming request? then we don't read the body
|
||||
if mtype.stream {
|
||||
codec.ReadRequestBody(nil)
|
||||
return
|
||||
}
|
||||
|
||||
// Decode the argument value.
|
||||
argIsValue := false // if true, need to indirect before calling.
|
||||
@ -443,7 +420,7 @@ func (server *server) readRequest(codec serverCodec) (service *service, mtype *m
|
||||
func (server *server) readRequestHeader(codec serverCodec) (service *service, mtype *methodType, req *request, keepReading bool, err error) {
|
||||
// Grab the request header.
|
||||
req = server.getRequest()
|
||||
err = codec.ReadRequestHeader(req)
|
||||
err = codec.ReadRequestHeader(req, true)
|
||||
if err != nil {
|
||||
req = nil
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
@ -478,7 +455,7 @@ func (server *server) readRequestHeader(codec serverCodec) (service *service, mt
|
||||
}
|
||||
|
||||
type serverCodec interface {
|
||||
ReadRequestHeader(*request) error
|
||||
ReadRequestHeader(*request, bool) error
|
||||
ReadRequestBody(interface{}) error
|
||||
WriteResponse(*response, interface{}, bool) error
|
||||
|
||||
|
@ -35,6 +35,7 @@ import (
|
||||
|
||||
log "github.com/golang/glog"
|
||||
"github.com/pborman/uuid"
|
||||
"golang.org/x/net/context"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
@ -61,10 +62,23 @@ type Request interface {
|
||||
Method() string
|
||||
ContentType() string
|
||||
Request() interface{}
|
||||
// indicates whether the response should be streaming
|
||||
// indicates whether the request will be streamed
|
||||
Stream() bool
|
||||
}
|
||||
|
||||
// Streamer represents a stream established with a client.
|
||||
// A stream can be bidirectional which is indicated by the request.
|
||||
// The last error will be left in Error().
|
||||
// EOF indicated end of the stream.
|
||||
type Streamer interface {
|
||||
Context() context.Context
|
||||
Request() Request
|
||||
Send(interface{}) error
|
||||
Recv(interface{}) error
|
||||
Error() error
|
||||
Close() error
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
var (
|
||||
|
@ -19,3 +19,9 @@ type HandlerWrapper func(HandlerFunc) HandlerFunc
|
||||
|
||||
// SubscriberWrapper wraps the SubscriberFunc and returns the equivalent
|
||||
type SubscriberWrapper func(SubscriberFunc) SubscriberFunc
|
||||
|
||||
// StreamerWrapper wraps a Streamer interface and returns the equivalent.
|
||||
// Because streams exist for the lifetime of a method invocation this
|
||||
// is a convenient way to wrap a Stream as its in use for trace, monitoring,
|
||||
// metrics, etc.
|
||||
type StreamerWrapper func(Streamer) Streamer
|
||||
|
@ -23,16 +23,21 @@ type httpTransportClient struct {
|
||||
addr string
|
||||
conn net.Conn
|
||||
dialOpts dialOptions
|
||||
r chan *http.Request
|
||||
once sync.Once
|
||||
|
||||
sync.Mutex
|
||||
r chan *http.Request
|
||||
bl []*http.Request
|
||||
buff *bufio.Reader
|
||||
}
|
||||
|
||||
type httpTransportSocket struct {
|
||||
r *http.Request
|
||||
r chan *http.Request
|
||||
conn net.Conn
|
||||
once sync.Once
|
||||
|
||||
sync.Mutex
|
||||
buff *bufio.Reader
|
||||
}
|
||||
|
||||
type httpTransportListener struct {
|
||||
@ -68,7 +73,14 @@ func (h *httpTransportClient) Send(m *Message) error {
|
||||
Host: h.addr,
|
||||
}
|
||||
|
||||
h.r <- req
|
||||
h.Lock()
|
||||
h.bl = append(h.bl, req)
|
||||
select {
|
||||
case h.r <- h.bl[0]:
|
||||
h.bl = h.bl[1:]
|
||||
default:
|
||||
}
|
||||
h.Unlock()
|
||||
|
||||
return req.Write(h.conn)
|
||||
}
|
||||
@ -134,17 +146,23 @@ func (h *httpTransportSocket) Recv(m *Message) error {
|
||||
return errors.New("message passed in is nil")
|
||||
}
|
||||
|
||||
b, err := ioutil.ReadAll(h.r.Body)
|
||||
r, err := http.ReadRequest(h.buff)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.r.Body.Close()
|
||||
|
||||
b, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.Body.Close()
|
||||
|
||||
mr := &Message{
|
||||
Header: make(map[string]string),
|
||||
Body: b,
|
||||
}
|
||||
|
||||
for k, v := range h.r.Header {
|
||||
for k, v := range r.Header {
|
||||
if len(v) > 0 {
|
||||
mr.Header[k] = v[0]
|
||||
} else {
|
||||
@ -152,6 +170,11 @@ func (h *httpTransportSocket) Recv(m *Message) error {
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case h.r <- r:
|
||||
default:
|
||||
}
|
||||
|
||||
*m = *mr
|
||||
return nil
|
||||
}
|
||||
@ -159,8 +182,11 @@ func (h *httpTransportSocket) Recv(m *Message) error {
|
||||
func (h *httpTransportSocket) Send(m *Message) error {
|
||||
b := bytes.NewBuffer(m.Body)
|
||||
defer b.Reset()
|
||||
|
||||
r := <-h.r
|
||||
|
||||
rsp := &http.Response{
|
||||
Header: h.r.Header,
|
||||
Header: r.Header,
|
||||
Body: &buffer{b},
|
||||
Status: "200 OK",
|
||||
StatusCode: 200,
|
||||
@ -174,6 +200,11 @@ func (h *httpTransportSocket) Send(m *Message) error {
|
||||
rsp.Header.Set(k, v)
|
||||
}
|
||||
|
||||
select {
|
||||
case h.r <- r:
|
||||
default:
|
||||
}
|
||||
|
||||
return rsp.Write(h.conn)
|
||||
}
|
||||
|
||||
@ -199,7 +230,14 @@ func (h *httpTransportSocket) error(m *Message) error {
|
||||
}
|
||||
|
||||
func (h *httpTransportSocket) Close() error {
|
||||
return h.conn.Close()
|
||||
err := h.conn.Close()
|
||||
h.once.Do(func() {
|
||||
h.Lock()
|
||||
h.buff.Reset(nil)
|
||||
h.buff = nil
|
||||
h.Unlock()
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (h *httpTransportListener) Addr() string {
|
||||
@ -211,18 +249,19 @@ func (h *httpTransportListener) Close() error {
|
||||
}
|
||||
|
||||
func (h *httpTransportListener) Accept(fn func(Socket)) error {
|
||||
srv := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
conn, _, err := w.(http.Hijacker).Hijack()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
c, err := h.listener.Accept()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sock := &httpTransportSocket{
|
||||
conn: conn,
|
||||
r: r,
|
||||
}
|
||||
sock := &httpTransportSocket{
|
||||
conn: c,
|
||||
buff: bufio.NewReader(c),
|
||||
r: make(chan *http.Request, 1),
|
||||
}
|
||||
|
||||
go func() {
|
||||
// TODO: think of a better error response strategy
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
@ -231,10 +270,9 @@ func (h *httpTransportListener) Accept(fn func(Socket)) error {
|
||||
}()
|
||||
|
||||
fn(sock)
|
||||
}),
|
||||
}()
|
||||
}
|
||||
|
||||
return srv.Serve(h.listener)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *httpTransport) Dial(addr string, opts ...DialOption) (Client, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user