原文作者:Liam Crilly of F5
原文链接:将 NGINX 部署为 API 网关,第 3 部分:发布 gRPC 服务
转载来源:NGINX 官方网站
本文是“将 NGINX 开源版和 NGINX Plus 部署为 API 网关”系列博文的第三篇。
- 第 1 部分详细说明了 NGINX 开源版和 NGINX Plus 作为基于 HTTP 的 RESTful API 的 API 网关的一些用例。
- 第 2 部分对这些用例进行了扩展,探讨了一系列可用于保护生产环境中后端 API 服务的安全措施。
- 本文解释了如何将 NGINX 开源版和 NGINX Plus 部署为 gRPC 服务的 API 网关。文章最初发布于 2018 年,随着 NGINX Plus Release 23 中引入了对原生 gRPC 健康检查协议的支持,特此更新,以方便大家充分利用 NGINX 开源版和 NGINX Plus。更新详情请参阅下文“实施健康检查”一节。
注:除非另有说明,否则本文中的所有信息都适用于 NGINX Plus 和 NGINX 开源版。为了便于阅读,当讨论内容同时适用于两个版本时,下文将它们统称为“NGINX”。
近年来,介绍微服务应用架构的概念和优势的文章非常多,其中以 NGINX 博文居首。微服务应用的核心是 HTTP API,本系列博文的前两篇文章使用了一个假设的 REST API 来说明 NGINX 如何处理此类应用。
尽管基于 JSON 消息格式的 REST API 在现代应用中非常流行,但它并不是所有场景或所有企业的理想之选。最常见的挑战是:
- 文档标准 —— 如果没有良好的开发者制度或强制性的文档要求,最后很容易产生大量缺乏准确定义的 REST API。Open API 规范 已成为 REST API 的通用接口描述语言,但其使用却不是强制性的,需要开发组织内部的有力治理。
- 事件和长连接—— REST API 以及它们使用 HTTP 传输,几乎决定了所有 API 调用都是请求 – 响应模式。当客户端应用需要服务器反馈消息时,使用 HTTP 长轮询和 WebSocket 等解决方案会有所帮助,但使用此类解决方案最终都需要构建一个单独、相邻的 API。
- 复杂事务—— REST API 是围绕唯一资源的概念构建的,每个资源都由一个 URI 表示。当应用需要调用多个资源更新时,要么需要多个 API 调用(效率低下),要么必须在后端实现复杂的事务(与 REST 的核心原则相悖)。
近年来,gRPC 已发展成为构建分布式应用,尤其是微服务应用的替代方法。gRPC 最初由 Google 开发,并于 2015 年开源,现已成为云原生计算基金会的一个项目。值得注意的是,gRPC 使用 HTTP/2 作为传输机制,并利用其二进制数据格式和多路复用流功能。
gRPC 的主要优势包括:
- 紧耦合的接口定义语言(协议缓冲区)
- 对流数据的原生支持(双向)
- 高效的二进制数据格式
- 自动生成多语言的代码,支持真正的多语言开发环境,且不会产生互操作性问题
定义 gRPC 网关
本系列博文的前两篇描述了如何通过单个入口点(例如 https://api.example.com)交付多个 API。当 NGINX 部署为 gRPC 网关时,gRPC 流量的默认行为和特征促使 NGINX 也要采用这种方法。虽然 NGINX 可以在同一主机名和端口上共享 HTTP 和 gRPC 流量,但最好还是将它们分开,主要有以下原因有:
- REST 和 gRPC 应用的 API 客户端需要不同格式的错误响应
- REST 和 gRPC 访问日志的相关字段有所不同
- 因为 gRPC 不涉及旧版 Web 浏览器,因此它可以实施更严格的 TLS 策略
为了实现这种分离,我们需要修改gRPC 网关主配置文件 grpc_gateway.conf 的 server{}
模块,它位于 /etc/nginx/conf.d 目录。
log_format grpc_json escape=json '{"timestamp":"$time_iso8601",' '"client":"$remote_addr","uri":"$uri","http-status":$status,' '"grpc-status":$grpc_status,"upstream":"$upstream_addr"' '"rx-bytes":$request_length,"tx-bytes":$bytes_sent}'; map $upstream_trailer_grpc_status $grpc_status { default $upstream_trailer_grpc_status; # grpc-status is usually a trailer '' $sent_http_grpc_status; # Else use the header, whatever its source } server { listen 50051 http2; # In production, comment out to disable plaintext port listen 443 http2 ssl; server_name grpc.example.com; access_log /var/log/nginx/grpc_log.json grpc_json; # TLS config ssl_certificate /etc/ssl/certs/grpc.example.com.crt; ssl_certificate_key /etc/ssl/private/grpc.example.com.key; ssl_session_cache shared:SSL:10m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_protocols TLSv1.2 TLSv1.3;