1. Background

1.1 Problem to be solved

More and more cloud-native applications are adopting gRPC as their API
standard, such as etcd, Dapr, and Envoy. In some cases, only the gRPC
API is available. Apache APISIX currently does not support access to
the API exposed by the gRPC service, and can only go through the HTTP
interface or develop a separate service to act as a proxy.

1.2 The benefits of solving this problem

    1. Allow user to connect with their own gRPC services.
    2. Embrace the ecosystem of Envoy and Dapr
    3. Show our leadership of the popular technology and provide
something the rival is missing.

2. How to solve the problem

We will create a new Nginx module called ngx-grpc-client-module so
that we can interact with Nginx's event system. We choose to use a
mature gRPC implementation and write Lua/C binding for it.

Hence we study multiple gRPC clients:

Language:   C++
Repo:       https://github.com/grpc/grpc

Pro:         1. High performance
                2. Widely used

Con:        1. Require nearly 2GB dependencies
                2. Not easy to compile unless you are a C++ developer

Language:   Go
Repo:       https://github.com/grpc/grpc-go

Pro:         1. Written in pure Go
                2. Widely used
                3. Familiar to me

Con:        1. Low performance (We also study connect-go, but it has
worse performance)

Language:   Rust
Repo:       https://github.com/hyperium/tonic

Pro:         1. Written in pure Rust
                2. High performance

Con:        1. Not familiar to me so I can't make sure it is mature enough


If APISIX is a company-inside project, I will choose to use C++.
However, APISIX is an open source project, and it is important to
lower the threshold for participation. People used to complain it is
hard to build APISIX from source, so we can't choose C++.

We can use Go first and switch to Rust in the future if the
performance is critical.
Changing the underneath gRPC implementation won't break the Lua API
provided by this module.

The process of unary request can be done in the steps below:

1. Encode the proto message and start gRPC call
2. Create task ctx, call the Go code and suspend Lua thread
3. Execute gRPC code, put the task ctx and final result to the task done queue
4. Wait on the task done queue
5. Remove the data from task done queue
6. Add the task ctx to Nginx's posted event queue and re-wait on the
task done queue
7. Resume Lua thread
8. Finish the gRPC call and decode proto message

Step 1/8 are done in the Lua.
Step 2/4/6/7 are done in the C code.
Step 3/5 are done in the Go (or other gRPC implementation)

Client / Server stream requests can be considered as one initial call
plus several unary read/write operations.

Reply via email to