OpenAPI and gRPC Side-by-Side

Posted Megadotnet

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenAPI and gRPC Side-by-Side相关的知识,希望对你有一定的参考价值。

Spend some time working with OpenAPI and gRPC and you’ll notice that these two technologies have a lot in common. Both are open source efforts, both describe APIs, and both promise better experiences for API producers and consumers. So why do we need both? If we do, what value does each provide? What can each project learn from the other?

We need to talk about APIs.

OpenAPI and gRPC are two approaches to building networked APIs. Quoting the Google API Design Guide, networked APIs are:

Application Programming Interfaces that operate across a network of computers. They communicate using network protocols including HTTP, and are frequently produced by different organizations than the ones that consume them.

That last phrase is a big deal: it’s the key to the value and challenge of networked APIs. Because they are frequently produced by different organizations than the ones that consume them, networked APIs make it possible for us to work together with people anywhere in the world, and they allow us to make things that are bigger than any of us could create individually… but we have to be able to talk to each other about what we’re doing. We need a language for describing APIs.


OpenAPI is a language for describing REST APIs.

If you’re working with REST APIs, you’ve probably heard of the OpenAPI Specification or its precursor, Swagger. It’s a structured way of describing REST APIs using JSON or YAML that was originally created to better document APIs. But people quickly saw other uses for it — API validation, code generation, mocking, testing — lots of great applications that make it easier to create and use APIs. Now the OpenAPI Specification is owned and administered by the Open API Initiative, a project of the Linux Foundation. It is managed by a team of technical leads, and is supported by over two dozen Open API Initiative member companies.

The OpenAPI Specification wasn’t the first API description format, but so far it has been the most widely used. Before we had API description formats, people hand-wrote the code for their APIs. Then they hand-wrote descriptions of those APIs and handed those to people who wanted to use the APIs, who then hand-wrote code to call the APIs. All of that hand-writing led to a lot of variation and error. As a formal description format, OpenAPI is giving us a great way to communicate about APIs and to have fewer errors and more successes in our API-based systems.

gRPC grew from the way Google builds APIs.

The other project that we’re discussing is gRPC. Originally developed at Google, gRPC is an open sourced system for making APIs that is based on the way Google builds APIs internally. Inside Google, gRPC is used to describe APIs from design through implementation and deployment. API documentation is generated from gRPC API descriptions. gRPC includes code generators that automatically produce API clients and scaffolding that can be filled in to create API servers. Then gRPC API descriptions are used by API test systems, API proxies, and all kinds of API support systems that provide services like authentication, quotas, billing, and security.

Now gRPC belongs to the Cloud Native Computing Foundation and has been adopted by many groups and companies outside of Google. Because gRPC is the public version of what Google has been using internally, Google is also converting to gRPC. Two of the first users of gRPC were the mobile apps GBoard and Allo with many more following.

API methodology, have you heard of it?

Early in my career I worked on electronic design automation software. I helped make chip design tools, but it was never enough to just write a tool. Each tool had to fit into a methodology, a set of tools and best practices for doing something — in our case, chip design. A chip design tool might assign locations to cells on a chip, route the connections between those cells, or check a design for performance problems. But this is done in many stages, and everything has to be done at the right point in the process. The person who develops and manages that process is called a methodologist.

Whether we recognize it our not, we’re also using a methodology to build our API systems. We might be writing code first, perhaps starting with a prototype or a mock server, and then writing documentation or building an OpenAPI description. At some point, we might write some tests and set up a continuous integration system to build and verify our API implementation. We might follow an API style guide, and some parts of our work will probably be automated. All of this is part of our API methodology.

OpenAPI in depth.

Because of its popularity, you’ve probably already heard a few things about OpenAPI or its precursor, Swagger. Here are some of the most important features of OpenAPI:

OpenAPI provides an API description format.

The OpenAPI Specification provides a way of describing REST APIs using JSON or YAML documents. Documents follow a standard form that is described by a JSON Schema and a textual specification (ironically, the text document is the official form of the OpenAPI Specification, which seeks to replace text definitions of APIs with machine-readable OpenAPI descriptions).

There are many sample OpenAPI descriptions online. You can find a large archive at apis.guru/openapi-directory, and many companies and projects publish and host OpenAPI descriptions of the APIs that they publish. For example, the Kubernetes project includes an OpenAPI description of the Kubernetes API in its main git repository. Unfortunately, there’s no standard place to look for an OpenAPI description of an API, but descriptions are often found in files named openapi.json and openapi.yaml (or swagger.json and swagger.yaml for older versions).

Building on JSON and YAML makes OpenAPI equally accessible to humans and machines, perhaps by compromising a bit for both. These are text formats, so people can read OpenAPI descriptions, but sometimes authors are tripped up by the excessive punctuation in JSON or the indentation requirements of YAML. From the machine’s perspective, it’s really easy to import a YAML or a JSON file in most programming languages. But in strongly typed languages — I tend to work a lot in Swift and Go — it can be awkward to import a JSON or YAML file into a generic data structure and then have to cast these generic data structures into their typed components. In environments like these, it’s helpful to have a strongly-typed data structure for your API description. That’s still possible with OpenAPI but isn’t part of the specification or official tooling.

OpenAPI assumes HTTP for transport.

The goal of the Open API Initiative is to standardize “how REST APIs are described” and thus HTTP is generally assumed to be the transport layer for APIs described by the OpenAPI Specification. This means that API requests are transactional: a request is sent and a response is received. Because it is focused on REST APIs, OpenAPI describes APIs in terms of paths and HTTP verbs (like GET, POST, PUT, DELETE).

OpenAPI (generally) assumes JSON for data representation.

OpenAPI provides for different response types, but for nearly all practical purposes, structured messages are sent and received as JSON documents.

OpenAPI makes code generation possible.

Officially, the Open API Initiative only manages the specification; there is no official OpenAPI tooling. But as part of a methodology, when you start thinking about what else you need around an API description format, you don’t want to write your API support code by hand. It’s tedious, error-prone, expensive, and nobody wants to own and maintain it. So there are tools in the OpenAPI world for generating code. Usually they start by generating client libraries, the code that you need to call an API, but you can also generate server-side implementations of support code that helps you publish an API. The biggest OpenAPI code generation project is called swagger-codegen, but there are several other code generators available including AutoRest by Microsoft, a project from Capital One called oas-nodegen, and a commercial code generation system from APIMATIC that supports OpenAPI and other formats. More will surely be coming.

Many API publishers are already using swagger-codegen and similar tools to produce the SDKs that they give to their users. Blog posts from Lyft and Square describe how they use swagger-codegen to generate their client libraries. But they’re not really using swagger-codegen out of the box: often there are special things that an API producer wants to do, so there’s some customization of the code generation that’s needed.

“Generating client libraries involves customizing the provided language-specific templates…
The amount of modification each template needs varies by language and we’re looking forward to working with the Swagger Codegen community to share our refinements.”  Val Polouchkine, Lyft
“…Swagger Codegen is a pretty active project. If you don’t check in your templates, things are gonna break unexpectedly because Swagger Codegen just uses the latest and greatest templates that are out there. So if you don’t sort of manually make sure that those things work, you’re gonna have an issue there.”  Tristan Sokol, Square

So as part of the Lyft and Square methodologies for publishing SDKs, they have essentially forked a version of swagger-codegen that they use to produce their APIs. That can be challenging because swagger-codegen is a big project. It has at least 70 different targets, all of different levels of quality.

Monolithic code generation is hard.

This is a problem that we’ve also seen at Google. If you have a single code generator with potentially many targets, there are a lot of management, development, and cultural challenges that might drive you break it into pieces.

  • Long generator build times: changing one target can require rebuilding everything.

  • Long generator test times: new builds are usually tested for every target language, even when only a single target was changed.

  • For stability, teams may prefer to archive their own generator builds. As mentioned, a team like the SDK team at Square would probably want to keep its own generator on the side so that other commits to the master branch don’t introduce surprises in Square’s generated client libraries.

  • Forks will abound, and each fork forks the entire generator and all targets.

  • Quality is uneven. Some language targets are mature and well-tested, others are barely into the stage of “hobby project”.

  • Versioning is hard. Typically the generator will have a version, but language targets themselves are not versioned, so its difficult to tell when a new version will include breaking changes for particular language targets.

  • Complexity and potentially unfamiliar build systems deter contributors who may be experts in the generated language but unfamiliar with the languages used to write the generator.

OpenAPI meets developers where they are.

But when you step back and look at OpenAPI, you see a project that has been really good at meeting developers where they are. Lots of people are using JSON and REST to build their APIs and the OpenAPI team has essentially gone to them and said, “What are you doing? Here’s a description for it. Does that work for you?” That listening and refinement has led to OpenAPI 3.0, and now we have a specification that can describe how most of the world is making REST APIs.

gRPC in depth.

gRPC, on the other hand, grew out of an effort to scale at Google. As Google grew and more and more pieces were implemented as microservices, it became important to reduce the cost of building and operating those microservices at very large scale.

You might be surprised to learn that there are no REST APIs used inside Google. That’s right, none of Google’s internal microservices use REST. Instead, everything communicates using gRPC or a precursor of gRPC called “Stubby”.

gRPC uses HTTP/2 for transport.

One of the things that gRPC does differently from its internal predecessor is that it communicates over HTTP/2. Inside Google, the previous generation of Protocol Buffer-based APIs — we’ll talk about Protocol Buffers next— would just open a socket, write a message, and read a message reply. Moving to HTTP/2 allowed streaming APIs: when you open an HTTP/2 connection, the connection can stay open over multiple calls, and these calls can occur simultaneously. You get the efficiency of multiplexing multiple calls over a single sustained connection, and you also can operate streaming APIs over these sustained connections. With gRPC, I can call an API that returns a stream of stock quotes or another that takes a stream of audio samples and returns a stream of interpretations of the words that I’ve spoken.

gRPC uses Protocol Buffers for data representation.

At a low level, gRPC can be thought of as a transport system only: a protocol for making API calls over HTTP/2 with support for optional streaming. But much more commonly, gRPC is considered a methodology, and in the gRPC methodology, messages are encoded with Protocol Buffers.

Protocol Buffers is described as “a language-neutral, platform-neutral extensible mechanism for serializing structured data.” In this sense, Protocol Buffers is also a methodology. Here I use “Protocol Buffers” (capitalized) to describe the methodology and “protocol buffer” (lower case) to describe a serialization of a data structure.

A protocol buffer (the serialization) is an array of bytes that represents a complex and often hierarchical data structure. The encoding is described by a few simple rules. They are simple enough that an encoder or decoder can be written in a day or two. The encoding is a binary format, so it’s hard for humans to read, but it is easily written and quickly read by machines. Although any comparison is structure-dependent, protocol buffers can be orders of magnitude faster to read than corresponding JSON serializations.

Nearly everything that is passed around inside Google is represented using Protocol Buffers. There are no internal REST APIs, and there’s no JSON or XML. Instead, every service and microservice used inside Google receives and sends protocol buffers, and it’s been estimated that across all Google data centers, over ten billion API calls are made every second. That volume of usage is why protocol buffers are used, and it’s why the Protocol Buffer encoding has been optimized for fast writing and reading.

gRPC provides an API description format.

The data encoded in a protocol buffer is not self-describing: to deserialize a protocol buffer, a reader needs a description of the fields in a message, particularly their types and field numbers (each field in a message is given a unique number that is used in the encoding).

These descriptions are written in a special language called the Protocol Buffer Language. Because it describes an API, this is similar to the OpenAPI specification, but instead of a JSON API description, we have something that looks more like a typical programming language. It has a grammar, parsers can be written for it, and there’s a standard compiler. The compiler is called protoc, short for “protocol compiler”.

Here’s a very simple Protocol Buffer Language description of a service:

package echo;
message EchoRequest {
string text = 1;
}
message EchoResponse {
string text = 1;
}
service Echo {
rpc Get(EchoRequest) returns (EchoResponse) {}
rpc Update(stream EchoRequest) returns (stream EchoResponse) {}
}

The service is called Echo because it just sends back what it receives. I’ve redundantly defined two messages here, EchoRequest and EchoResponse. Because they have the same content we could have used just one. Following these message declarations, there’s a service declaration that says, “We’re going to have an API. We’ll call it Echo. It supports two remote procedure calls — one called Get that takes an EchoRequest and returns an EchoResponse, and one called Update that accepts a stream of EchoRequest messages and returns a stream of EchoResponsemessages.” That’s a kind of streaming API that is possible with gRPC.

In gRPC, code generation is the standard practice.

This becomes methodology when you use protoc to compile your Protocol Buffer language description of your API and generate API support code. If you just wanted to run protoc and get its output, you would give the -ooption and protoc would write a binary file describing your API. That binary file is called a FileDescriptorSet and it is described by a Protocol Buffer language file. Protocol Buffers gets used in lots of ways! But what may be more useful is this: if you want to get API support code in a particular language, you would use one of the gRPC protoc plugins, like this:

protoc echo.proto -o echo.out --swift_out=.

In this case, I want some Swift code to implement the Echo protocol buffers so I used the — swift_out option. By convention, this calls a plugin called protoc-gen-swift. That needs to be in your path when you run protoc. When you’ve done this, you get a file called echo.pb.swift which contains the generated code for your message. That code includes a Swift data structure to represent your message and includes support code to turn that structure into serialized data and to turn that serialized data back into structures that you can use in your Swift programs. With that, you as a developer don’t need to know anything about the binary format of Protocol Buffers. You might want to know it just for fun or to write your own Protocol Buffer library, but you don’t need to know it to use Protocol Buffers. You can just use this generated code.

One more thing: protoc is a C++ program and protoc-gen-swift is written using Swift. In general, plugins can be written in any language that supports Protocol Buffers because the protoc plugin interface is itself defined by a Protocol Buffer language file.

gRPC supports distributed code generation.

The protoc plugin architecture brings many advantages:

  • Fast build times: changing one target only requires rebuilding its plugin.

  • Fast test times: new builds need only be tested for the affected targets.

  • For stability, teams can archive their own protoc and plugin builds.

  • New plugins can abound.

  • Separately-maintained plugins can offer different maturity levels.

  • Separately-maintained plugins can be appropriately versioned.

  • Separately-maintained plugins can be written in languages that contributors prefer.

You’ve probably already noticed that this is a point-by-point response to the problems that we saw with monolithic code generators.

So if I want to change the Swift code generator, I don’t have to touch the Go code generator. That’s in a separate git repository, and I don’t have to test it because I know for sure that I didn’t touch it. And if I want to fork one language generator, I haven’t forked the whole world.

Currently gRPC officially supports ten languages with more on the way. Because gRPC communicates using Protocol Buffers, clients and servers can be written in any of these languages and freely interoperate. When APIs use gRPC, no client or server needs any information about the programming languages used to implement other clients or servers.

Build a gRPC service, get a free REST API.

There’s another nice consequence of gRPC’s structured, description-driven approach to APIs. If you build a gRPC service, you can use a third-party project like grpc-gateway to get a JSON/REST API for free! Using special annotations in the API description, grpc-gateway transcodes a gRPC API into a REST API with JSON message encoding. These annotations are documented in google/api/http.proto, and because that is a Google-authored Protocol Buffer language file, you might guess (correctly) that Google’s REST APIs are implemented in a very similar way.

gRPC strives for the highest performance and quality.

Step back and look at gRPC. Like me, I think you’ll see a project offering developers a methodology that’s dedicated to delivering the highest performance and quality for APIs.

Two projects, two corners of the API space.

When we put these two projects together, we see that each excels in a different corner of the API space.

OpenAPI meets developers where they are with an API description format that can be used to create a solid methodology for producing the JSON/REST APIs that they are producing today. gRPC focuses on delivering the highest performance and quality for APIs by asking developers to follow a tight, well-defined methodology that provides powerful streaming API constructs and high-performance implementations.

What can these projects learn from the other? Let’s start with OpenAPI:

  • OpenAPI, as I mentioned, is a very dynamic API description format using YAML and JSON. Using gRPC, when I compile an API description, I get a strongly-typed data structure describing my API. So one way that we can improve OpenAPI is by building similar strongly-typed representations of OpenAPI descriptions for use in our API support tools.

  • We can work on improving the quality of code generation in the OpenAPI space by building more distributed, plugin-oriented code generators that make it easier to use code generation in production and write code generators in the same languages that they generate.

  • Finally, we can go big with OpenAPI by going from thinking about an API description format to thinking about a complete API methodology. What are all the pieces of our API lifecycles? Let’s address them all with OpenAPI-based tooling and organize our tools into methodologies that produce APIs in consistent, well-defined ways.

In its corner, gRPC can learn a few things from OpenAPI:

  • It can be hard to get started using gRPC. More user-friendly tools could help, such as an equivalent to the curl command for making HTTP requests at the command line.

  • Just as OpenAPI representations can be too dynamic, gRPC API representations can be overly static. Most gRPC implementations rely heavily on code generation for performance reasons, but when performance isn’t critical, dynamic gRPC libraries could make it easier to quickly write applications, particularly API clients.

  • It can also be hard to get started writing tools for gRPC. Most programmers know how to read and write JSON and YAML, but working with Protocol Buffers is a bit harder. More simple sample apps and tutorials would make gRPC a more accessible methodology.

How you can help

You’ll find a lot of different perspectives in the API world; some of these are evident in the designs and values behind OpenAPI and gRPC. But in the hopes of finding some agreement, here are six things that I suggest you can do to help us move forward:

First, stop writing interface code for your APIs by hand.

It’s error-prone! Every time we do this, we have the potential of introducing the same mistakes that we introduced the last time we wrote our API clients and servers by hand.

Second, stop writing interface code for your APIs by hand.

When we write interface code by hand, we encourage unnecessary variation, and over time, variation feeds the growth of complexity and cost. If we’re doing the same thing in many different places, we should do that with the same code: either in common libraries or in consistent code produced by code generators.

Third, stop writing interface code for your APIs by hand.

Face it, it’s gross! Hand-written interface code is expensive to write, it is tedious to maintain, and in the long run, nobody wants to own it.

Fourth, mind your API methodology.

Pay attention to the whole process of how you make APIs. Better yet, document it and put someone in charge of overseeing it.

Fifth, develop and use API style guides.

Google has an API style guide and uses it religiously. I think every company should use one, whether you write your own guide or use someone else’s. It’s just like using a style guide for a programming language: sometimes programmers bristle at those but we need them, so if you use language style guides, think about using API style guides. Along with that, think about having a review board in your company that uses your style guide to review all your APIs for consistency. Ask questions like this: “Are we using the same term for the same thing or are we going to confuse our users by having each of our teams do things in its own unique way?”

Finally, use OpenAPI or gRPC.

These two projects have made the most headway in improving the coherence and consistency of the API space, and they’re both open. You can use them without contributing, but if you have improvements to make, that’s even better! The only constraints on contributors are in our contributor license agreements and codes of conduct. If you would like to work with us, we would love to work with you!


以上是关于OpenAPI and gRPC Side-by-Side的主要内容,如果未能解决你的问题,请参考以下文章

REST easy with kbmMW #20 – OpenAPI and Swagger UI

grpc-golang实现账号and密码认证

(译)xDS REST and gRPC protocol

如何从 protobuf (.proto) 文件中生成 (.json/.yaml) 中的 swagger3 (OpenAPI3) 规范?

gRPC helloworld service, RESTful JSON API gateway and swagger UI

gRPC 客户端不返回证书