grpchan

repository·master·Indexed 42 days ago

https://github.com/fullstorydev/grpchan

A transport-agnostic abstraction for gRPC connections in Go, allowing developers to use alternative transports such as HTTP 1.1, WebSockets, or in-process communication instead of standard HTTP/2. It includes the protoc-gen-grpchan plugin for generating client stubs and provides an httpgrpc package for implementing gRPC-over-HTTP servers and clients with support for protobuf and JSON encoding.

Tokens
5.5K
Snippets
22
Records
29
Agent score
53%

What's inside grpchan

  1. What are gRPC Channels and when to use them

    master

    The grpchan library provides a Channel abstraction for RPC connections. While standard gRPC uses an HTTP/2-based transport via google.golang.org/grpc, grpchan allows you to swap the underlying transport layer.

    Common use cases include:

    • Testing: Using an in-process transport to dispatch handlers within the same program, avoiding the overhead of serialization and network loopback.
    • Alternative Transports: Using HTTP 1.1 (which supports most stream types except full-duplex bidirectional streams) or WebSockets.
    • Custom Protocols: Implementing entirely new transport mechanisms for your RPC services.
  2. Generate grpchan code using protoc

    master

    After installing the plugin, use the --grpchan_out parameter during your protoc execution. You should specify the same output directory for --grpchan_out as you do for --go_out.

    This process generates *.pb.grpchan.go files alongside your standard *.pb.go files. These generated files contain the methods required to invoke your service methods over non-standard transports.

    //go:generate protoc --go_out=plugins=grpc:. --grpchan_out=. my.proto
  3. Install the protoc-gen-grpchan plugin

    master

    To use grpchan with your proto-defined services, you must install the protoc-gen-grpchan plugin. This plugin generates the necessary Go code to enable alternate transport support for your services.

    go install github.com/fullstorydev/grpchan/cmd/protoc-gen-grpchan
  4. What is a grpchan Channel?

    master

    A grpchan.Channel is an abstraction for a gRPC transport. It is designed to be more general than the standard *grpc.ClientConn and *grpc.Server types provided by the official gRPC implementation. This abstraction allows gRPC to run over alternate substrates, such as in-process communication or HTTP 1.1.

    To implement a new transport, you must provide implementations for two interfaces:

    1. Client-side: Implement grpc.ClientConnInterface.
    2. Server-side: Implement grpchan.ServiceRegistry (which is an alternate implementation of the grpc.ServiceRegistrar interface).
  5. Use HandlerMap to manage and reuse service handlers

    master

    A HandlerMap is a collection of service handlers that can be registered once and then reused to configure multiple servers or transport channels (e.g., standard gRPC, in-process gRPC, or HTTP/1.1). It implements the grpc.ServiceRegistrar interface, allowing it to be passed directly to server registration methods.

    To use a HandlerMap for multiple servers, register your services to the map first, then use the ForEach method to apply those services to your target servers.

    // 1. Initialize the map
    reg := grpchan.HandlerMap{}
    
    // 2. Register services (using generated registration functions)
    foo.RegisterHandlerFooBar(newFooBarImpl(), reg)
    
    // 3. Reuse the same handlers for multiple servers
    // For a standard gRPC server:
    svr := grpc.NewServer()
    reg.ForEach(svr.RegisterService)
    
    // For an in-process channel:
    ipch := &inprocgrpc.Channel{}
    reg.ForEach(ipch.RegisterService)
  6. Implement a custom gRPC transport

    master

    To create a custom transport substrate for gRPC using grpchan, you need to implement the following interfaces:

    Client-Side

    Implement grpc.ClientConnInterface. This requires providing methods for:

    • Unary RPCs
    • Streaming RPCs

    Note: When using unary interceptors on a channel that is not a *grpc.ClientConn, the parameter of that type will be nil.

    Server-Side

    Implement grpchan.ServiceRegistry. This interface allows you to accumulate service registrations for use with an implementation other than *grpc.Server. The transport must be able to:

    • Invoke method and stream handlers for a given service implementation.
    • Supply a grpc.ServerStream implementation for streaming operations.
    • Provide a custom implementation of grpc.ServerTransportStream within the server stream to handle headers and trailers for unary calls.
  7. Use protoc-gen-grpchan to generate gRPC client stubs

    master

    The protoc-gen-grpchan plugin is a protoc generator that produces Go client stubs using github.com/fullstorydev/grpchan.Channel as the transport abstraction instead of the standard *grpc.ClientConn. This allows you to carry RPC requests and streams over alternative transports like HTTP 1.1 or in-process communication.

    To use it, include it in your protoc command invocation as a plugin. The generated files will have a .pb.grpchan.go extension.

    protoc --go_out=. --grpchan_out=. path/to/your/service.proto
  8. Create an HTTP/1.1 gRPC Channel

    master

    Use NewChannel to create a Channel instance that implements the grpc.ClientConnInterface. This channel allows you to issue gRPC requests over HTTP/1.1. You must provide a base *url.URL and an http.RoundTripper (transport). You can optionally provide ChannelOption functions to configure the channel, such as changing the encoding.

    Note: While you can construct a Channel using a struct literal for backwards compatibility, it is highly recommended to use NewChannel to ensure all options and validations are correctly applied.

    import (
    	"net/http"
    	"net/url"
    	"github.com/fullstorydev/grpchan/httpgrpc"
    )
    
    baseURL, _ := url.Parse("https://api.example.com")
    transport := http.DefaultTransport
    
    // Create a channel with default protobuf encoding
    ch, err := httpgrpc.NewChannel(baseURL, transport)
    if err != nil {
    	panic(err)
    }
  9. Initialize a gRPC-over-HTTP server with NewServer

    master

    Use NewServer to create a new instance of a gRPC-over-HTTP server. This server implements both grpc.ServiceRegistrar (to register your gRPC services) and http.Handler (to expose those services via HTTP). You can customize the server using ServerOption functions.

    Common options include:

    • WithBasePath(path string): Sets the base URL path for all services. Defaults to /.
    • WithServerUnaryInterceptor(interceptor grpc.UnaryServerInterceptor): Adds a gRPC interceptor for unary RPCs.
    • WithServerStreamInterceptor(interceptor grpc.StreamServerInterceptor): Adds a gRPC interceptor for streaming RPCs.
    import "github.com/fullstorydev/grpchan/httpgrpc"
    
    // Create a server with a custom base path and unary interceptor
    server := httpgrpc.NewServer(
        httpgrpc.WithBasePath("/api"),
        httpgrpc.WithServerUnaryInterceptor(myUnaryInterceptor),
    )
  10. Use the deprecated protoc-gen-grpchan plugin

    master

    The protoc-gen-grpchan plugin is deprecated because modern protoc-gen-go-grpc generates code using interfaces (grpc.ClientConnInterface and grpc.ServiceRegistrar) rather than concrete types. However, it can still be used to support legacy codebases.

    Installation

    go install github.com/fullstorydev/grpchan/cmd/protoc-gen-grpchan
    # Ensure it is in your PATH
    export PATH=$PATH:$GOPATH/bin

    Usage

    When running protoc, include the --grpchan_out flag. This will generate *.pb.grpchan.go files alongside your standard *.pb.go files. Use the same output directory for both.

    protoc foo.proto --grpchan_out=./output/dir --go_out=./output/dir

    Generating Legacy Stubs

    If your code relies on New<ServiceName>ChannelClient methods produced by earlier versions, you can generate them by passing the legacy_stubs option:

    protoc foo.proto --grpchan_out=legacy_stubs:./output/dir
  11. Configure JSON encoding with WithJSONEncoding

    master

    By default, the httpgrpc.Channel uses protobuf binary encoding. You can configure the channel to use JSON encoding instead by passing WithJSONEncoding(true) to NewChannel.

    When JSON encoding is enabled:

    • Unary calls: The request and response are JSON values.
    • Streaming calls: The request is a series of JSON values, and the response is a series of SSE (Server-Sent Events) containing JSON values.
    ch, err := httpgrpc.NewChannel(baseURL, transport, httpgrpc.WithJSONEncoding(true))
  12. Invoke unary RPCs via Channel.Invoke

    master

    The Invoke method performs a unary gRPC call over HTTP/1.1. It handles marshaling the request, sending the POST request to the constructed URL (base URL + method name), and unmarshaling the response.

    Signature: func (ch *Channel) Invoke(ctx context.Context, methodName string, req, resp interface{}, opts ...grpc.CallOption) error

    err := ch.Invoke(ctx, "/package.Service/Method", &req, &resp)
    if err != nil {
    	// Handle error
    }