yarpc

package module
v1.0.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 11, 2017 License: MIT Imports: 21 Imported by: 0

README

yarpc-go GoDoc Build Status Coverage Status

With hundreds to thousands of services communicating with RPC, transport protocols (like HTTP and TChannel), encoding protocols (like JSON or Thrift), and peer choosers are the concepts that vary year over year. Separating these concerns allows services to change transports and wire protocols without changing call sites or request handlers, build proxies and wire protocol bridges, or experiment with load balancing strategies. YARPC is a toolkit for services and proxies.

YARPC breaks RPC into interchangeable encodings, transports, and peer choosers. YARPC for Go provides reference implementations for HTTP/1.1 and TChannel transports, and also raw, JSON, and Thrift encodings. YARPC for Go provides experimental implementations for a Redis transport and a round robin peer chooser. YARPC for Go plans to provide a Protobuf 3 encoding, a gRPC transport, and a load balancer that uses a least-pending-requests strategy. Peer choosers can implement any strategy, including load balancing or sharding, in turn backed by a pluggable peer provider.

Regardless of transport, every RPC has some common properties: caller name, service name, procedure name, encoding name, deadline or TTL, headers, baggage (multi-hop headers), and tracing. Each RPC can also have an optional shard key, routing key, or routing delegate for advanced routing. YARPC transports use a shared API for capturing RPC metadata, so middleware can apply to requests over any transport.

Each YARPC transport protocol can implement inbound handlers and outbound callers. Each of these can support different RPC types, like unary (request and response) or oneway (request and receipt) RPC. A future release of YARPC will add support for other RPC types including variations on streaming and pubsub.

Installation

go get -u go.uber.org/yarpc

If using Glide, at least glide version 0.12.3 is required to install:

$ glide --version
glide version 0.12.3

$ glide get 'go.uber.org/yarpc#^1'

To use Thrift code generation, you will need to install plugins. These cannot be vendored since go depends on the binaries being available on the path.

$ go get 'go.uber.org/thriftrw'
$ go get 'go.uber.org/yarpc/encoding/thrift/thriftrw-plugin-yarpc'

Examples

This example illustrates a simple service that implements a handler for a Hello::echo Thrift procedure.

service Hello {
    EchoResponse echo(1:EchoRequest echo)
}

struct EchoRequest {
    1: required string message;
    2: required i16 count;
}

struct EchoResponse {
    1: required string message;
    2: required i16 count;
}

A go:generate directive informs go generate how to produce the Thrift models and YARPC bindings for the echo service.

//go:generate thriftrw --plugin=yarpc echo.thrift
$ go generate echo.thrift

Setting up a YARPC dispatcher configures inbounds and outbounds for supported transport protocols and RPC types. This sets a service up to receive HTTP requests on port 8080 and send requests to itself. YARPC funnels requests from all inbound transports into its routing table, and organizes outbounds by name.

httpTransport := http.NewTransport()
dispatcher := yarpc.NewDispatcher(yarpc.Config{
    Name: "hello",
    Inbounds: yarpc.Inbounds{
        httpTransport.NewInbound(":8080"),
    },
    Outbounds: yarpc.Outbounds{
        "hello": {
            Unary: httpTransport.NewSingleOutbound("http://127.0.0.1:8080"),
        },
    },
})

The dispatcher governs the lifecycle of every inbound, outbound, and the singleton for each transport protocol. The singleton can manage the lifecycle of shared peers and connections.

if err := dispatcher.Start(); err != nil {
    log.Fatal(err)
}
defer dispatcher.Stop()

At the end of main, we block until we receive a signal to exit, then unravel anything deferred like dispatcher.Stop(), shutting down gracefully.

signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
<-signals
Handle

To receive requests from any inbound, we register a handler object using the Thrift generated server.

dispatcher.Register(helloserver.New(&helloHandler{}))

The handler must implement the Hello service from the Thrift IDL. The generated code requires a handler that implements helloserver.Interface.

type helloHandler struct{}

func (h *helloHandler) Echo(ctx context.Context, e *echo.EchoRequest) (*echo.EchoResponse, error) {
	return &echo.EchoResponse{Message: e.Message, Count: e.Count + 1}, nil
}
Call

To send a request on an outbound, we construct a client using the corresponding named outbound from the dispatcher. The client will use that name for the outbound request service name.

client := helloclient.New(dispatcher.ClientConfig("hello"))

To call a remote procedure, the context must have a deadline. We create a context with a one second deadline and call a method of the client. The client will use the dispatcher name for the caller name, Thrift for the encoding, and infer the procedure names from the Echo method (Hello::echo).

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()

res, err := client.Echo(ctx, &echo.EchoRequest{Message: "Hello world", Count: 1})
if err != nil {
    log.Fatal(err)
}
fmt.Println(res)
Other Examples

YARPC also provides examples for the oneway RPC type and a key value service using both the Thrift and JSON encodings.

Development Status: Stable

Ready for most users. No breaking changes to stable APIs will be made before 2.0.

Stable:

  • handler and call sites for unary and oneway
  • dispatcher constructor and config type
  • transport constructors (including the tchannel.NewTransportChannel(...) although YARPC will eventually also have tchannel.NewTransport(chooser, ...))
  • interfaces for "go.uber.org/yarpc/api/transport" Transport (for lifecycle management), Inbound, Outbound, Request, Response, ResponseWriter, Router, RouteTable, Procedure, and Lifecycle
  • interfaces for "go.uber.org/yarpc/api/peer" Transport (for peer management), Chooser, List
  • the middleware API
  • wire representation of RPC for HTTP and TChannel, including all required headers: Rpc-Caller, Rpc-Service, Rpc-Procedure, and Context-TTL-MS.

Unstable:

  • Any package in an x directory, including the experimental Redis transport and the round-robin peer chooser.
  • debug and introspection APIs (these are internal to prevent external implementations of transports, inbounds, and outbounds from making use of them, but we further do not guarantee the content of debug pages)

Upcoming:

  • peer choosers for TChannel
  • handle-or-forward request handlers, possibly using per-procedure middleware
  • streaming RPC type for some transports (gRPC, WebSocket)
  • pubsub RPC type for some transports (Redis)

Documentation

Overview

Package yarpc provides the YARPC service framework.

With hundreds to thousands of services communicating with RPC, transport protocols (like HTTP and TChannel), encoding protocols (like JSON or Thrift), and peer choosers are the concepts that vary year over year. Separating these concerns allows services to change transports and wire protocols without changing call sites or request handlers, build proxies and wire protocol bridges, or experiment with load balancing strategies. YARPC is a toolkit for services and proxies.

YARPC breaks RPC into interchangeable encodings, transports, and peer choosers. YARPC for Go provides reference implementations for HTTP/1.1 and TChannel transports, and also raw, JSON, and Thrift encodings. YARPC for Go provides experimental implementations for a Redis transport and a round robin peer chooser. YARPC for Go plans to provide a Protobuf 3 encoding, a gRPC transport, and a load balancer that uses a least-pending-requests strategy. Peer choosers can implement any strategy, including load balancing or sharding, in turn backed by a pluggable peer provider.

Regardless of transport, every RPC has some common properties: caller name, service name, procedure name, encoding name, deadline or TTL, headers, baggage (multi-hop headers), and tracing. Each RPC can also have an optional shard key, routing key, or routing delegate for advanced routing. YARPC transports use a shared API for capturing RPC metadata, so middleware can apply to requests over any transport.

Each YARPC transport protocol can implement inbound handlers and outbound callers. Each of these can support different RPC types, like unary (request and response) or oneway (request and receipt) RPC. A future release of YARPC will add support for other RPC types including variations on streaming and pubsub.

Index

Examples

Constants

View Source
const Version = "1.0.1"

Version is the current version of YARPC.

Variables

This section is empty.

Functions

func CanonicalizeHeaderKey

func CanonicalizeHeaderKey(k string) string

CanonicalizeHeaderKey canonicalizes the given header key to the same form used by the headers map returned by ResponseHeaders.

var headers map[string]string
res, err := client.Call(ctx, "hello", requestBody, ResponseHeaders(&headers))
email, ok := headers[CanonicalizeHeaderKey("User-Email-Address")]

func InjectClients added in v0.4.0

func InjectClients(src transport.ClientConfigProvider, dest interface{})

InjectClients injects clients from a Dispatcher into the given struct. dest must be a pointer to a struct with zero or more exported fields which hold YARPC client types. This includes json.Client, raw.Client, and any generated Thrift service client. Fields with nil values and a `service` tag will be populated with clients using that service`s ClientConfig.

Given,

type Handler struct {
	KeyValueClient keyvalueclient.Interface `service:"keyvalue"`
	UserClient json.Client `service:"users"`
	TagClient tagclient.Interface  // no tag; will be left unchanged
}

The call,

var h Handler
yarpc.InjectClients(dispatcher, &h)

Is equivalent to,

var h Handler
h.KeyValueClient = keyvalueclient.New(dispatcher.ClientConfig("keyvalue"))
h.UserClient = json.New(dispatcher.ClientConfig("users"))

Builder functions for different client types may be registered using the RegisterClientBuilder function.

This function panics if a field with an unknown type and nil value has the `service` tag.

func IsBadRequestError added in v1.0.0

func IsBadRequestError(err error) bool

IsBadRequestError returns true on an error returned by RPC clients if the request was rejected by YARPC because it was invalid.

res, err := client.Call(...)
if yarpc.IsBadRequestError(err) {
	fmt.Println("invalid request:", err)
}

func IsTimeoutError added in v1.0.0

func IsTimeoutError(err error) bool

IsTimeoutError return true on an error returned by RPC clients if the given error is a TimeoutError.

res, err := client.Call(...)
if yarpc.IsTimeoutError(err) {
	fmt.Println("request timed out:", err)
}

func IsUnexpectedError added in v1.0.0

func IsUnexpectedError(err error) bool

IsUnexpectedError returns true on an error returned by RPC clients if the server panicked or failed with an unhandled error.

res, err := client.Call(...)
if yarpc.IsUnexpectedError(err) {
	fmt.Println("internal server error:", err)
}

func OnewayInboundMiddleware added in v1.0.0

func OnewayInboundMiddleware(mw ...middleware.OnewayInbound) middleware.OnewayInbound

OnewayInboundMiddleware combines the given collection of unary inbound middleware in-order into a single OnewayInbound middleware.

func OnewayOutboundMiddleware added in v1.0.0

func OnewayOutboundMiddleware(mw ...middleware.OnewayOutbound) middleware.OnewayOutbound

OnewayOutboundMiddleware combines the given collection of unary outbound middleware in-order into a single OnewayOutbound middleware.

func RegisterClientBuilder added in v0.4.0

func RegisterClientBuilder(f interface{}) (forget func())

RegisterClientBuilder registers a builder function for a specific client type.

Functions must have the signature,

func(transport.ClientConfig) T

Where T is the type of the client. T MUST be an interface.

This function panics if a client for the given type has already been registered.

After a builder function for a client type is registered, these objects can be instantiated automatically using InjectClients.

A function to unregister the builder function is returned. Note that the function will clear whatever the corresponding type's builder function is at the time it is called, regardless of whether the value matches what was passed to this function or not.

func UnaryInboundMiddleware added in v1.0.0

func UnaryInboundMiddleware(mw ...middleware.UnaryInbound) middleware.UnaryInbound

UnaryInboundMiddleware combines the given collection of unary inbound middleware in-order into a single UnaryInbound middleware.

func UnaryOutboundMiddleware added in v1.0.0

func UnaryOutboundMiddleware(mw ...middleware.UnaryOutbound) middleware.UnaryOutbound

UnaryOutboundMiddleware combines the given collection of unary outbound middleware in-order into a single UnaryOutbound middleware.

Types

type Ack added in v1.0.0

type Ack interface {
	fmt.Stringer
}

Ack represents an acknowledgement from a oneway request.

type Call added in v1.0.0

type Call encoding.Call

Call provides information about the current request inside handlers. An instance of Call for the current request can be obtained by calling CallFromContext on the request context.

func Get(ctx context.Context, req *GetRequest) (*GetResponse, error) {
	call := yarpc.CallFromContext(ctx)
	fmt.Println("Received request from", call.Caller())
	if err := call.WriteResponseHeader("hello", "world"); err != nil {
		return nil, err
	}
	return response, nil
}

func CallFromContext added in v1.0.0

func CallFromContext(ctx context.Context) *Call

CallFromContext retrieves information about the current incoming request from the given context. Returns nil if the context is not a valid request context.

The object is valid only as long as the request is ongoing.

func (*Call) Caller added in v1.0.0

func (c *Call) Caller() string

Caller returns the name of the service making this request.

func (*Call) Encoding added in v1.0.0

func (c *Call) Encoding() transport.Encoding

Encoding returns the encoding for this request.

func (*Call) Header added in v1.0.0

func (c *Call) Header(k string) string

Header returns the value of the given request header provided with the request.

func (*Call) HeaderNames added in v1.0.0

func (c *Call) HeaderNames() []string

HeaderNames returns a sorted list of the names of user defined headers provided with this request.

func (*Call) Procedure added in v1.0.0

func (c *Call) Procedure() string

Procedure returns the name of the procedure being called.

func (*Call) RoutingDelegate added in v1.0.0

func (c *Call) RoutingDelegate() string

RoutingDelegate returns the routing delegate for this request.

func (*Call) RoutingKey added in v1.0.0

func (c *Call) RoutingKey() string

RoutingKey returns the routing key for this request.

func (*Call) Service added in v1.0.0

func (c *Call) Service() string

Service returns the name of the service being called.

func (*Call) ShardKey added in v1.0.0

func (c *Call) ShardKey() string

ShardKey returns the shard key for this request.

func (*Call) WriteResponseHeader added in v1.0.0

func (c *Call) WriteResponseHeader(k, v string) error

WriteResponseHeader writes headers to the response of this call.

type CallOption added in v1.0.0

type CallOption encoding.CallOption

CallOption defines options that may be passed in at call sites to other services.

These may be used to add or alter the request.

func ResponseHeaders added in v1.0.0

func ResponseHeaders(h *map[string]string) CallOption

ResponseHeaders specifies that headers received in response to this request should replace the given map.

Header keys in the map are normalized using the CanonicalizeHeaderKey function.

var resHeaders map[string]string
resBody, err := client.SetValue(ctx, key, value, yarpc.ResponseHeaders(&resHeaders))
value, ok := resHeaders[yarpc.CanonicalizeHeaderKey("foo")]

Note that the map is replaced completely. Entries it had before making the call will not be available afterwards.

headers := map[string]string{"hello": "world"}
resBody, err := client.SetValue(ctx, key, value, yarpc.ResponseHeaders(&headers))
_, ok := headers["hello"]
fmt.Println(ok)  // false

func WithHeader added in v1.0.0

func WithHeader(k, v string) CallOption

WithHeader adds a new header to the request. Header keys are case insensitive.

_, err := client.GetValue(ctx, reqBody, yarpc.WithHeader("Token", "10"))
// ==> {"token": "10"}

If multiple entries have the same normalized header name, newer entries override older ones.

func WithRoutingDelegate added in v1.0.0

func WithRoutingDelegate(rd string) CallOption

WithRoutingDelegate sets the routing delegate for the request.

func WithRoutingKey added in v1.0.0

func WithRoutingKey(rk string) CallOption

WithRoutingKey sets the routing key for the request.

func WithShardKey added in v1.0.0

func WithShardKey(sk string) CallOption

WithShardKey sets the shard key for the request.

type Config

type Config struct {
	// Name of the service. This is the name used by other services when
	// making requests to this service.
	Name string

	// Inbounds define how this service receives incoming requests from other
	// services.
	//
	// This may be nil if this service does not receive any requests.
	Inbounds Inbounds

	// Outbounds defines how this service makes requests to other services.
	//
	// This may be nil if this service does not send any requests.
	Outbounds Outbounds

	// Inbound and Outbound Middleware that will be applied to all incoming
	// and outgoing requests respectively.
	//
	// These may be nil if there is no middleware to apply.
	InboundMiddleware  InboundMiddleware
	OutboundMiddleware OutboundMiddleware

	// Tracer is deprecated. The dispatcher does nothing with this propery.
	Tracer opentracing.Tracer
}

Config specifies the parameters of a new Dispatcher constructed via NewDispatcher.

type Dispatcher

type Dispatcher struct {
	// contains filtered or unexported fields
}

Dispatcher encapsulates a YARPC application. It acts as the entry point to send and receive YARPC requests in a transport and encoding agnostic way.

Example (Minimal)
package main

import (
	"log"

	"go.uber.org/yarpc"
)

func main() {
	dispatcher := yarpc.NewDispatcher(yarpc.Config{Name: "myFancyService"})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()
}
Output:

func NewDispatcher

func NewDispatcher(cfg Config) *Dispatcher

NewDispatcher builds a new Dispatcher using the specified Config. At minimum, a service name must be specified.

Invalid configurations or errors in constructing the Dispatcher will cause panics.

func (*Dispatcher) ClientConfig added in v1.0.0

func (d *Dispatcher) ClientConfig(service string) transport.ClientConfig

ClientConfig provides the configuration needed to talk to the given service. This configuration may be directly passed into encoding-specific RPC clients.

keyvalueClient := json.New(dispatcher.ClientConfig("keyvalue"))

This function panics if the service name is not known.

func (*Dispatcher) Inbounds

func (d *Dispatcher) Inbounds() Inbounds

Inbounds returns a copy of the list of inbounds for this RPC object.

The Inbounds will be returned in the same order that was used in the configuration.

func (*Dispatcher) Register added in v1.0.0

func (d *Dispatcher) Register(rs []transport.Procedure)

Register registers zero or more procedures with this dispatcher. Incoming requests to these procedures will be routed to the handlers specified in the given Procedures.

Example (JSON)
package main

import (
	"context"
	"fmt"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/encoding/json"
)

// global dispatcher used in the registration examples
var dispatcher = yarpc.NewDispatcher(yarpc.Config{Name: "service"})

func main() {
	handler := func(ctx context.Context, key string) (string, error) {
		fmt.Println("key", key)
		return "value", nil
	}

	dispatcher.Register(json.Procedure("get", handler))
}
Output:

Example (Raw)
package main

import (
	"context"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/encoding/raw"
)

// global dispatcher used in the registration examples
var dispatcher = yarpc.NewDispatcher(yarpc.Config{Name: "service"})

func main() {
	handler := func(ctx context.Context, data []byte) ([]byte, error) {
		return data, nil
	}

	dispatcher.Register(raw.Procedure("echo", handler))
}
Output:

func (*Dispatcher) Start

func (d *Dispatcher) Start() error

Start starts the Dispatcher, allowing it to accept and processing new incoming requests.

This starts all inbounds and outbounds configured on this Dispatcher.

This function returns immediately after everything has been started. Servers should add a `select {}` to block to process all incoming requests.

if err := dispatcher.Start(); err != nil {
	log.Fatal(err)
}
defer dispatcher.Stop()

select {}

func (*Dispatcher) Stop

func (d *Dispatcher) Stop() error

Stop stops the Dispatcher.

This stops all outbounds and inbounds owned by this Dispatcher.

This function returns after everything has been stopped.

type InboundMiddleware added in v1.0.0

type InboundMiddleware struct {
	Unary  middleware.UnaryInbound
	Oneway middleware.OnewayInbound
}

InboundMiddleware contains the different types of inbound middlewares.

type Inbounds added in v0.5.0

type Inbounds []transport.Inbound

Inbounds contains a list of inbound transports. Each inbound transport specifies a source through which incoming requests are received.

type MapRouter added in v1.0.0

type MapRouter struct {
	// contains filtered or unexported fields
}

MapRouter is a Router that maintains a map of the registered procedures.

func NewMapRouter added in v1.0.0

func NewMapRouter(defaultService string) MapRouter

NewMapRouter builds a new MapRouter that uses the given name as the default service name.

func (MapRouter) Choose added in v1.0.0

Choose retrives the HandlerSpec for the service and procedure noted on the transport request, or returns an error.

func (MapRouter) Procedures added in v1.0.0

func (m MapRouter) Procedures() []transport.Procedure

Procedures returns a list procedures that have been registered so far.

func (MapRouter) Register added in v1.0.0

func (m MapRouter) Register(rs []transport.Procedure)

Register registers the procedure with the MapRouter.

type OutboundMiddleware added in v1.0.0

type OutboundMiddleware struct {
	Unary  middleware.UnaryOutbound
	Oneway middleware.OnewayOutbound
}

OutboundMiddleware contains the different types of outbound middlewares.

type Outbounds added in v0.4.0

type Outbounds map[string]transport.Outbounds

Outbounds provides access to outbounds for a remote service. Outbounds define how requests are sent from this service to the remote service.

Directories

Path Synopsis
api
encoding
Package encoding provides APIs for encoding authors.
Package encoding provides APIs for encoding authors.
encoding
json
Package json provides the JSON encoding for YARPC.
Package json provides the JSON encoding for YARPC.
raw
Package raw provides the raw encoding for YARPC.
Package raw provides the raw encoding for YARPC.
thrift
Package thrift implements Thrift encoding support for YARPC.
Package thrift implements Thrift encoding support for YARPC.
thrift/thriftrw-plugin-yarpc
thriftrw-plugin-yarpc implements a plugin for ThriftRW that generates code compatible with YARPC.
thriftrw-plugin-yarpc implements a plugin for ThriftRW that generates code compatible with YARPC.
crossdock/thrift/gen-go/echo
Package echo is generated code used to make or handle TChannel calls using Thrift.
Package echo is generated code used to make or handle TChannel calls using Thrift.
crossdock/thrift/gen-go/gauntlet_tchannel
Package gauntlet_tchannel is generated code used to make or handle TChannel calls using Thrift.
Package gauntlet_tchannel is generated code used to make or handle TChannel calls using Thrift.
net
x
Package x contains experimental components.
Package x contains experimental components.
Package transport implements the low level concerns of sending and receiving bytes.
Package transport implements the low level concerns of sending and receiving bytes.
http
Package http implements a YARPC transport based on the HTTP/1.1 protocol.
Package http implements a YARPC transport based on the HTTP/1.1 protocol.
tchannel
Package tchannel implements a YARPC transport based on the TChannel protocol.
Package tchannel implements a YARPC transport based on the TChannel protocol.
x
Package x contains experimental components.
Package x contains experimental components.
x/redis
Package redis provides an simple, EXPERIMENTAL queuing transport backed by a redis list.
Package redis provides an simple, EXPERIMENTAL queuing transport backed by a redis list.
Package yarpctest provides utilities to test YARPC services and clients.
Package yarpctest provides utilities to test YARPC services and clients.
recorder
Package recorder records & replay yarpc requests on the client side.
Package recorder records & replay yarpc requests on the client side.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL