惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

J
Java Code Geeks
IT之家
IT之家
爱范儿
爱范儿
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
博客园_首页
Blog — PlanetScale
Blog — PlanetScale
V
Visual Studio Blog
云风的 BLOG
云风的 BLOG
MyScale Blog
MyScale Blog
阮一峰的网络日志
阮一峰的网络日志
Stack Overflow Blog
Stack Overflow Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
GbyAI
GbyAI
V
V2EX
N
Netflix TechBlog - Medium
Vercel News
Vercel News
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
The Blog of Author Tim Ferriss
量子位
博客园 - Franky
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - 司徒正美
月光博客
月光博客
F
Fortinet All Blogs

kmcd.dev

Joining Buf Let The Gravity of Ashburn, Virginia The CPU Cost of Protobuf Varints in Go It Beating Go gRPC-Web Should Have Fixed gRPC Making Dynamic Protobuf Fast in Go Proxy, Record, and Mock gRPC APIs with FauxRPC Exploring Protocol Buffers Interactively Introducing ProtoDocs Ghost in the Shell: The Manga Behind the Anime The Hidden Cost of google.protobuf.Value Why Networking Built Its Own Data Modeling Language Zero-Friction Demos with WASM Let's Learn About BGP ConnectRPC: Where is it now? Building APIs with Contracts The Case for Greppable Code Unknown Fields in Protobuf IRC Log: Reactionary Faking protobuf data in Go Y'all are Sleeping on Mise-en-Place IRC Log: Standup 2 HTTP/2 From Scratch: Part 4 IRC Log: rm -rf /var/opt/gitlab/postgresql/data HTTP/2 From Scratch: Part 3 Building a Live BGP Map HTTP/2 From Scratch: Part 2 IRC Log: The Cloud Scale Incident
Introducing unknownconnect-go
2024-03-19 · via kmcd.dev

gRPC systems can be quite complex. When making additions to protobuf files the server or the client often gets updated at different times. In a perfect world, this would all be synchronized. But we live in reality. Sometimes release schedules differ between components. Sometimes you just forget to update a component. Many times you might be consuming a gRPC service managed by another team and they don’t tell you that they’re changing things. I made something that will bring unique insight into this problem with very little work.

Let’s make things better

unknownconnect-go is an interceptor for ConnectRPC clients and servers that tells you if you are receiving protobuf messages with unknown fields. Now you can know when you should upgrade your gRPC clients or servers to the latest version. Let’s discuss how to use it.

  1. Install the library:
go get -u github.com/sudorandom/unknownconnect-go
  1. Import the library:
import (
    unknownconnect "github.com/sudorandom/unknownconnect-go"
)

Server-side usage

Here are two examples demonstrating how to use unknownconnect-go on the server side:

Short example:

unknownconnect.NewInterceptor(
    unknownconnect.WithCallback(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
        slog.Warn("received a protobuf message with unknown fields", slog.Any("spec", spec), slog.Any("msg", msg))
        return nil
    }),
)

This example creates a new interceptor using unknownconnect.NewInterceptor. The interceptor function receives an unknownconnect.WithCallback argument that provides a callback function. This function takes three arguments:

In the previous example, when a message with unknown fields is received, the interceptor will log a warning message using the slog.Warn function. It includes information about the message specification and the message itself.

Full example: Here is a full example that shows you how to register the unknownconnect.Interceptor with a ConnectRPC handler:

func main() {
    greeter := &GreetServer{}
    mux := http.NewServeMux()
    path, handler := greetv1connect.NewGreetServiceHandler(greeter, connect.WithInterceptors(
        unknownconnect.NewInterceptor(
            unknownconnect.WithCallback(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
                return connect.NewError(connect.InvalidArgument, errors.New("protobuf version missmatch; received unknown fields"))
            }),
        ),
    ))
    mux.Handle(path, handler)
    http.ListenAndServe("localhost:8080", h2c.NewHandler(mux, &http2.Server{}))
}

The interceptor function in this example returns an error with the connect.InvalidArgument code, which will cause the server to reject the request if it receives a message with unknown fields.

Customization options:

The two examples above show two ways to handle messages with unknown fields but you can customize the behavior of the interceptor to suit your specific needs. Here are some ideas:

  • Log the event: As shown in the first example, you can simply log a warning message when an unknown field is encountered. This can help debug and monitor the cause.
  • Add to a metric With this approach, you can emit metrics whenever unknown fields are encountered. This can be helpful to give more monitoring insight.
  • Fail the request/response: This approach, demonstrated in the second example, can be useful in pre-production environments to prevent unexpected behavior caused by mismatched message definitions.
  • Add an annotation to the context: This allows you to pass information about the unknown field to your service handler.

Client-side usage

And it works the same for clients, too:

package main

import (
    "context"
    "log/slog"
    "net/http"

    greetv1 "example/gen/greet/v1"
    "example/gen/greet/v1/greetv1connect"

    "connectrpc.com/connect"
)

func main() {
    client := greetv1connect.NewGreetServiceClient(
        http.DefaultClient,
        "http://localhost:8080",
        connect.WithInterceptors(
            unknownconnect.NewInterceptor(
                unknownconnect.WithCallback(func(ctx context.Context, spec connect.Spec, msg proto.Message) error {
                    slog.Warn("received a protobuf message with unknown fields", slog.Any("spec", spec), slog.Any("msg", msg))
                    return nil
                })
            ),
        ),
    )
    res, err := client.Greet(
        context.Background(),
        connect.NewRequest(&greetv1.GreetRequest{Name: "Jane"}),
    )
    if err != nil {
        slog.Error(err.Error())
        return
    }
    slog.Info(res.Msg.Greeting)
}

This example works in a similar way to how the server interceptor. It creates a new gRPC client for the Greet service and adds the unknownconnect interceptor using the connect.WithInterceptors function.

Conclusion

unknownconnect-go provides a simple and effective way to identify potential compatibility issues in your gRPC systems by detecting messages with unknown fields. It offers flexibility in how you handle these situations, allowing you to log warnings, reject requests, or implement custom logic as needed. By integrating unknownconnect-go into your development workflow, you can gain valuable insights into potential version mismatches and ensure smoother operation of your gRPC systems.