

ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理
source link: https://blog.yuanpei.me/posts/3942175942/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

ASP.NET Core 搭载 Envoy 实现 gRPC 服务代理
在构建以 gRPC 为核心的微服务架构的过程中,博主曾经写过一篇名为 ASP.NET Core gRPC 打通前端世界的尝试 的文章,主要是希望打通 gRPC 和 前端这样两个异次元世界,因为无论我们构建出怎样高大上的微服务架构,最终落地的时候,我们还是要面对当下前后端分离的浪潮。所以,在那篇文章中,博主向大家介绍过 gRPC-Web 、gRPC-Gateway 、封装 API 、编写中间件 这样四种方案。我个人当时更喜欢编写中间件这种方案,甚至后来博主进一步实现了 gRPC 的 “扫描” 功能。
当时,博主曾模糊地提到过,Envoy 可以提供容器级别的某种实现,这主要是指 Envoy 独有的 gRPC-JSON Transcoder 功能。考虑到 Envoy 是一个同时支持 HTTP/1.1 和 HTTP/2 的代理软件,所以,它天然地支持基于 HTTP/2 实现的 gRPC。所谓 gRPC-JSON Transcoder,其实指 Envoy 充当了 JSON 到 Protobuf 间互相转换的角色,而它利用的正是 Envoy 中的 过滤器 这一重要组件。好了,在今天这篇文章中,博主就为大家介绍一下这种基于 Envoy 的方案,如果大家困惑于如何把 gRPC 提供给前端同事使用,不妨稍事休息、冲一杯卡布奇诺,一起来探索这广阔无垠的技术世界。
从 Envoy 说起
开辟鸿蒙,始有天地。上帝说,要有光,于是,就有了光。而故事的起源,则要追溯到我们最早提出的那个问题:假设我们有下面的 gRPC 服务,我们能否让它像一个 JSON API 一样被调用? 通过查阅 Protobuf 的 官方文档,我们可以发现 Protobuf 与 JSON间存在着对应关系,这是两者可以相互转化的前提。博主在编写 中间件 时,同样借助了 Protobuf 暴露出来的接口 MessageParser:
syntax = "proto3";
option csharp_namespace = "GrpcService";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
接下来,这个 gPRC 服务如何和 Envoy 这个代理服务器产生关联呢?首当其冲的自然是一个路由啦:
routes:
- match:
prefix: "/greet"
route:
cluster: grpc_service
timeout:
seconds: 60
这表示以 /greet
开头的请求会被路由到 grpc_service
这个集群,如果按照一般的 Envoy 使用流程,接下来,我们只需要配置对应的集群节点即可。我们前面提到过,Envoy 的这个 gRPC-JSON Transcoder 功能,是通过过滤器来实现的,更确切地说,它是一个 HTTP 级别的过滤器,所以,我们继续耐心往下看:
http_filters:
- name: envoy.filters.http.grpc_json_transcoder
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
proto_descriptor: "/etc/descriptor/greet.pb"
services:
- "greet.Greeter"
print_options:
add_whitespace: true
always_print_primitive_fields: true
always_print_enums_as_ints: false
preserve_proto_field_names: false
auto_mapping: true
- name: envoy.filters.http.router
可以注意到,这里使用了一个叫做 envoy.filters.http.grpc_json_transcoder
的过滤器。对于这个过滤器而言,核心的、需要注意的地方有两个:
proto_descriptor
指向一个 Protobuf 的描述文件,这是一个二进制文件,可以由protoc
编译器生成。services
表示一组服务,必须按照包名.服务名
的格式进行填写,这里的示例为:greet.Greeter
。
关于如何生成二进制的 Protobuf 描述文件,我们专门放在下一节来讲,在此基础上,我们只要增加集群即可完成 Envoy 的配置:
clusters:
- name: grpc_service
connect_timeout: 0.25s
type: LOGICAL_DNS
lb_policy: ROUND_ROBIN
dns_lookup_family: V4_ONLY
http2_protocol_options: {}
upstream_connection_options:
tcp_keepalive:
keepalive_time: 300
load_assignment:
cluster_name: grpc_service
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: grpc_service
port_value: 80
完整的 Envoy 配置文件,请参考 这里,不再占用篇幅进行说明。
准备描述文件
生成 Protobuf 的二进制描述文件,需要借助 protoc 这个命令行工具,此前我们介绍 gRPC 生态中的 gRPC-Web、gRPC-Gateway 时曾经接触过它。Envoy 正是通过这个描述文件来处理 JSON
和 Protobuf
的相互转换,博主猜测这里可能用到了类似 MessageParser 的东西,Envoy 从这个二进制的描述文件中获取 gRPC 的元数据信息,并由此从 JSON
构建出 Protobuf
。这里,我们还是以本文开始的 .proto
文件为例:
protoc --descriptor_set_out=./Protos/descriptor/greet.pb --include_imports Protos\greet.proto
这条命令行的含义是,为 Protos\greet.proto
生成对应的服务描述文件 /Protos/descriptor/greet.pb
。下图即为博主生成的服务描述文件:

此时,我们只需要将其放到 Envoy 的目录中即可,本文中的示例位于以下路径:/etc/descriptor/greet.pb
。好了,现在 Envoy 和 gRPC 均已就绪,我们通过 docker-compose
对服务进行编排:
version: '3'
services:
envoygateway:
build: Envoy/
ports:
- "9090:9090"
- "9091:9091"
volumes:
- ./Envoy/envoy.yaml:/etc/envoy/envoy.yaml
grpcservice:
build: GrpcService/GrpcService/
ports:
- "8082:80"
environment:
ASPNETCORE_URLS: "http://+"
ASPNETCORE_ENVIRONMENT: "Development"
启动服务后,如果我们像调用 gRPC 服务中的 SayHello()
方法,此时,对应的路由为:/greet.Greeter/SayHello
,即:包名.服务名/方法名
。好了,我们用 Postman 或者 Apifox 对接口进行测试:

至此,我们实现一开始的目的,通过 Envoy 代理 gRPC 服务以后,对于前端而言,它已不再关心,这个服务背后的服务提供者到底是什么?因为对它而言,JSON API 还是 Protobuf 已经完全没有差别。博主曾经评价它是容器级别的方案,因为它可以将多个 gRPC 服务统一到一个入口中,非常适合充当整个微服务的网关,如果你正在使用 gRPC,相信我,这会是一条必由之路。
目前,博主所在的公司,已经全面采用了这种方案,而博主则进一步在团队中推广了Docker-Compose
,换言之,我们将多个微服务通过Docker-Compose
进行编排,并通过 Envoy 为所有微服务提供统一入口,唯一的遗憾是,通过protoc
生成服务描述文件这个过程没有纳入到 CI/CD 环节,靠手动生成、复制服务描述文件,到底还是会有点失落呢?如果结合前面分享过的 Envoy 身份认证,整个微服务架构终于看起来形成闭环啦!
本文分享了 Envoy 中的 gRPC-JSON Transcoder 功能,它可以将一个 gRPC 服务代理成一个 JSON API,从而方便前端或者是客户端去消费一个 gRPC 服务。其原理是,Envoy 中可以通过配置过滤器来实现 JSON 和 Protobuf 的相互转换,这一过程依赖 Protobuf 的元数据,故而,我们需要通过命令行工具protoc
生成服务描述文件,我们只需要在 Envoy 中添加相关配置,就可以像调用一个 JSON API 一样调用 gRPC。至此, gRPC 与 Web 世界彻底打通,我们可以用我们熟悉的技术去消费一个 gRPC 服务。博主的 Grpc.Gateway 实现了类似的功能,如果大家感兴趣,欢迎大家前去体验一番。好了,以上就是这篇博客的全部内容啦,谢谢大家,祝各位晚安!
Recommend
-
47
-
47
-
9
ASP.NET Core 搭载 Envoy 实现微服务的反向代理2021-07-0139 22 min.话说,博主第一次接触到Envoy,其实是在微软的示例项目 eShopOnContainers,在...
-
8
ASP.NET Core 搭载 Envoy 实现微服务的负载均衡2021-07-0518 22 min.如果说,我们一定要找出一个词来形容这纷繁复杂的世界,我希望它会是熵。有人说,熵增定律是宇宙中最绝望的定律,所谓熵,即是指事物混乱或者无序的程度。在一...
-
6
ASP.NET Core 搭载 Envoy 实现微服务的监控预警2021-07-1037 21 min.在构建微服务架构的过程中,我们会接触到服务划分、服务编写以及服务治理这一系列问题。其中,服务治理是工作量最密集的一个环节,无论是服务发现、配置中心、故障转移...
-
7
ASP.NET Core 搭载 Envoy 实现微服务身份认证(JWT)2021-07-2516 33 min.在构建以 gRPC 为核心的微服务架构的过程中,得益于 Envoy 对 gRPC 的“一等公民”支持,我们可以在过滤器中对 gRPC 服务进行转码,进而可以像调用 W...
-
7
基于 gRPC 和 Istio 的无 Sidecar 代理的服务网格 ...
-
11
试用gRPC构建服务时要在.proto文件中定义消息(message)和服务(service)。gRPC支持多种语言自动生成客户端、服务端和DTO实现。在读完这篇文章后,你将了解到使用Envoy作为转码代理,使gRPC API也可以通过HTTP/JSON的方式访问。你可以通过github代码库中的Jav...
-
12
Envoy 中的 xDS REST 和 gRPC 协议详解 · Service Mesh|服务网格中文社区 2018年9月28日 ...
-
8
traefik 设计实现之 http、http2 和 grpc 代理
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK