文是一份详尽且深入的指南,旨在帮助读者理解并实现将 gRPC 通过 Maven 集成到 SpringBoot 项目中的全过程。文章首先以高度概括的方式探讨了 gRPC 的理论基础,为读者提供了对其核心概念的清晰认识。随后,我们将转向更为具体的实践层面,展示 gRPC 在实际应用中的多种实现方式。
虽然全文篇幅较长,但我们强烈建议您耐心阅读,以确保您能够全面掌握 gRPC 的技术细节和应用场景。通过本文的学习,您将不仅能够理解 gRPC 的工作原理,还能在您的 SpringBoot 项目中成功集成和应用这一强大的通信协议,从而提升系统的性能和效率。
gRPC 是最初由 Google 开发的开源远程过程调用 (RPC) 框架。它是云原生计算基金会 (CNCF) 的一部分,旨在实现微服务架构中的服务之间高效、稳健的通信。以下是 gRPC 的一些关键功能和概念:
1. 协议缓冲区(Protobuf):
2.基于HTTP/2:
3、四种服务方式:
在这个实际的实现中,我们将看到一元实现它等于发送单个请求和响应。我们现在知道 gRPC 使用ProtocolBuffer来定义服务。首先让我们从定义接口定义开始,然后让我们看看项目结构和脚手架。下面的项目结构中的每个模块都是一个 Maven 模块。
proto-service模块负责保存与 proto 文件相关的所有内容,并将它们编译成 gRPC 相关的存根和接口。
让我们在src/main/proto文件夹中创建一个Product.proto文件并复制以下内容。
/**
* @author vaslabs(M K Pavan Kumar)
* @medium (https://medium.com/@manthapavankumar11)
*/
syntax="proto3";
option java_multiple_files=true;
package com.vaslabs.proto;
message Product {
int32 product_id=1;
string name=2;
string description=4;
float price=3;
int32 category_id=5;
}
message ProductList{
repeated Product product=1;
}
message Category {
int32 category_id=1;
string name=2;
}
service ProductService {
//unary - synchronous
//request-response stype [not streaming]
rpc getProductById(Product) returns(Product){}
rpc getProductByCategoryId(Category) returns(ProductList){}
}提供的 proto 文件采用 Protocol Buffers 版本 3 ( proto3 ) 的语法编写,是与产品和类别相关的 gRPC 服务的结构化定义。以下是其组成部分的简要说明:
文件头:
选项:
包裹声明:
消息定义:
服务定义:
RPC 类型:
让我们使用 maven 插件运行“mvncompile”来编译并获取 gRPC 风格生成的源代码以获取源代码。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.vaslabs</groupId>
<artifactId>springboot-grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>proto-service</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>1.56.1</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>1.54.2</version>
</dependency>
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
</dependencies>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.6.1</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.6.1</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.17.3:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.53.0:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>这是保存原型服务的实现的服务,接收原型请求并将原型响应发送给调用它的客户端。该服务由grpc-spring-boot-starter提供支持
将以下内容复制到该模块的 pom.xml 中。观察 pom 文件,我们已将proto-service模块添加为依赖项,因为我们将在此模块中实现 proto-services。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.vaslabs</groupId>
<artifactId>springboot-grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>product-service</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.vaslabs</groupId>
<artifactId>proto-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>让我们在src/main/java中创建一个ProductServiceImpl.java类。观察该类扩展了由protoc生成的基类ProductServiceGrpc.ProductServiceImplBase
package org.vaslabs;
import com.vaslabs.proto.Category;
import com.vaslabs.proto.Product;
import com.vaslabs.proto.ProductList;
import com.vaslabs.proto.ProductServiceGrpc;
import io.grpc.stub.StreamObserver;
import net.devh.boot.grpc.server.service.GrpcService;
import java.util.List;
@GrpcService
public class ProductServiceImpl extends ProductServiceGrpc.ProductServiceImplBase {
@Override
public void getProductById(Product request, StreamObserver<Product> responseObserver) {
InMemoryData.getProducts()
.stream()
.filter(product -> product.getProductId()==request.getProductId())
.findFirst()
.ifPresent(responseObserver::onNext);
responseObserver.onCompleted();
}
@Override
public void getProductByCategoryId(Category request, StreamObserver<ProductList> responseObserver) {
List<Product> products=InMemoryData.getProducts()
.stream()
.filter(product -> product.getCategoryId()==request.getCategoryId())
.toList();
ProductList productList=ProductList.newBuilder().addAllProduct(products).build();
responseObserver.onNext(productList);
responseObserver.onCompleted();
}
}使用主类,我们可以启动服务ProductServiceApplication.java ,如下所示。
package org.vaslabs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ProductServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ProductServiceApplication.class, args);
}
}如果要更改默认的 gRPC 服务端口,请在src/main/resources/application.yaml中保留以下内容。
grpc:
server:
port: 9090该模块是一个纯Spring Boot Web客户端,它将以Http形式侦听Web请求,并使用proto请求与产品服务通信并接收proto响应,然后转换为DTO并将其作为Http响应发送给调用客户端。
让我们将以下内容复制到pom.xml,该模块由grpc-client-spring-boot-starter提供支持,以发出启用 grpc 的请求和响应。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.vaslabs</groupId>
<artifactId>springboot-grpc-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>product-client</artifactId>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.vaslabs</groupId>
<artifactId>proto-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>2.14.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>让我们创建一个服务,它将使用生成的存根与 grpc 服务进行通信,如下所示。 src/main/java/org/vaslabs/service/ProductServiceGRPC.java
package org.vaslabs.service;
import com.vaslabs.proto.Category;
import com.vaslabs.proto.Product;
import com.vaslabs.proto.ProductList;
import com.vaslabs.proto.ProductServiceGrpc;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Service;
import java.util.List;
import static org.vaslabs.service.helpers.DtoMappingHelper.mapProductListToProductDTO;
import static org.vaslabs.service.helpers.DtoMappingHelper.mapProductToProductDTO;
@Service
public class ProductServiceRPC {
@GrpcClient("grpc-product-service")
ProductServiceGrpc.ProductServiceBlockingStub productServiceBlockingStub;
public org.vaslabs.dto.Product getProductById(int id) {
Product product=Product.newBuilder().setProductId(id).build();
Product response=productServiceBlockingStub.getProductById(product);
return mapProductToProductDTO(response);
}
public List<org.vaslabs.dto.Product> getProductByCategoryId(int id) {
Category category=Category.newBuilder().setCategoryId(id).build();
ProductList response=productServiceBlockingStub.getProductByCategoryId(category);
return mapProductListToProductDTO(response);
}
}一旦收到原始响应,我们将使用以下辅助方法将其转换为 DTO。 **/helpers/DtoMappingHelper.java
package org.vaslabs.service.helpers;
import com.vaslabs.proto.ProductList;
import org.vaslabs.dto.Product;
import java.util.ArrayList;
import java.util.List;
public class DtoMappingHelper {
public static List<org.vaslabs.dto.Product> mapProductListToProductDTO(ProductList productList) {
List<Product> products=new ArrayList<>();
productList.getProductList().forEach(product -> {
Product product1=getProduct();
product1.setId(product.getProductId());
product1.setCategoryId(product.getCategoryId());
product1.setName(product.getName());
product1.setDescription(product.getDescription());
product1.setPrice(product.getPrice());
products.add(product1);
});
return products;
}
public static org.vaslabs.dto.Product mapProductToProductDTO(com.vaslabs.proto.Product product) {
Product product1=getProduct();
product1.setId(product.getProductId());
product1.setCategoryId(product.getCategoryId());
product1.setName(product.getName());
product1.setDescription(product.getDescription());
product1.setPrice(product.getPrice());
return product1;
}
private static Product getProduct(){
return new Product();
}
}**/dto/Product.java
package org.vaslabs.dto;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
@Data
@Setter
@Getter
public class Product {
private int id;
private int categoryId;
private String name;
private String description;
private float price;
}让我们创建一个名为**/ProductController.java的控制器,如下所示。
package org.vaslabs.controller;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.vaslabs.dto.Product;
import org.vaslabs.service.ProductServiceRPC;
import java.util.List;
@RestController
public class ProductController {
private final ProductServiceRPC productServiceRPC;
public ProductController(ProductServiceRPC productServiceRPC) {
this.productServiceRPC=productServiceRPC;
}
@GetMapping("/product/{id}")
public ResponseEntity<Product> getProductById(@PathVariable String id){
return ResponseEntity.ok().body(productServiceRPC.getProductById(Integer.parseInt(id)));
}
@GetMapping(value="/product/category/{id}", produces=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Product>> getProductByCategoryId(@PathVariable String id){
return ResponseEntity.ok().body(productServiceRPC.getProductByCategoryId(Integer.parseInt(id)));
}
}在此模块的资源文件夹中,请将以下内容添加到application.yaml文件中
grpc:
client:
grpc-product-service:
address: static://localhost:9090
negotiationType: plaintext为此,存根将创建一个名为grpc-product-service通道来与服务进行通信。
分别运行产品服务和产品客户端,如果下面的所有内容应该是您应该看到的输出。
我使用 HTTPie 来测试服务。
在微服务架构的背景下,采用 gRPC 和 Protobuf 进行服务间通信已被证明是提升效率、稳健性和跨语言互操作性的关键策略。gRPC 以其基于 HTTP/2 的通信机制和对多种编程语言的广泛支持,极大地促进了高性能和低延迟的服务交互,这与微服务架构的分布式特性完美契合。
Protobuf(Protocol Buffers)通过提供一种紧凑的二进制序列化格式,进一步强化了这一优势,确保服务之间交换的数据不仅体积小,而且序列化和反序列化的速度极快。这种高效的序列化机制使得数据传输更为迅速,同时也减少了网络带宽的占用,对于构建现代、高效且有效的微服务生态系统至关重要。
综上所述,gRPC 与 Protobuf 的结合,不仅为微服务架构提供了一种强大的通信工具,还为构建高性能、低延迟的分布式系统奠定了坚实的基础。这种组合无疑成为了推动微服务生态系统发展的强大动力。
来源:https://spring4all.com/forum-post/7462.html
随着.net core3.0的正式发布,gRPC服务被集成到了VS2019。本文主要演示如何对gRPC的服务进行认证授权。
目前.net core使用最广的认证授权组件是基于OAuth2.0协议的IdentityServer4。而gRPC可以与ASP.NET Core Authentication一起使用来实现认证授权功能。本文将创建3个应用程序来完成gRPC的认证授权演示过程。
<PackageReference Include="IdentityServer4" Version="3.0.1" />
IdentityServer4相关配置,因为是演示所以很简单,生产场景大家根据实际情况配置。
namespace Ids4.Server
{
public class Config
{
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email(),
};
}
public static IEnumerable<ApiResource> GetApis()
{
return new List<ApiResource>
{
new ApiResource("api", "Demo API")
{
ApiSecrets={ new Secret("secret".Sha256()) }
}
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId="client",
ClientSecrets={ new Secret("secret".Sha256()) },
AllowedGrantTypes=GrantTypes.ClientCredentials,
AllowedScopes={ "api" },
},
};
}
}
}
services.AddIdentityServer().AddInMemoryApiResources(Config.GetApis())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryClients(Config.GetClients())
.AddDeveloperSigningCredential(persistKey: false);
app.UseIdentityServer();
POST /connect/token HTTP/1.1
Host: localhost:5000
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=client1&client_secret=secret
{
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IlVyMmxuM2EwNGhWaGdDdWZTVTNtZVEiLCJ0eXAiOiJhdCtqd3QifQ.eyJuYmYiOjE1NzEzMDkwMTMsImV4cCI6MTU3MTMxMjYxMywiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoiYXBpIiwiY2xpZW50X2lkIjoiY2xpZW50Iiwic2NvcGUiOlsiYXBpIl19.X4pg9_FbPbWZl814XC0NYWTslfhMG4aXWEyXLrXhIojPJaL7Qvq9ieDF4S7x0psRcClwbwCg81hTrG3j2Cmcl0nzj_Ic7UY8MfN0dvAuy_fJdUf76TX0oOpir3SxgC8gnfaKyEoWmmbIyvwicWbKp9PP-EeTxG6-oMYn6PO22cwRVHDD28ZdEAq2DEkATOh9XPavoi9vGZhPQ1nviKL1K6tcYUGXSQbhWI9ISEqnTHqMX1xA_gcDIAplGvquXmtXdgyTsRoGolEtzDAYVH4sGUb1SpYx2nc8bgl6Qw27fhe0Uy9MR70kQMcEkCTdXLivjYjkuI9_quUyJHzdi5KgnQ",
"expires_in": 3600,
"token_type": "Bearer",
"scope": "api"
}
本篇不对IdentityServer4做更多的讲解,大家可以参考官方文档了解更多。
<PackageReference Include="Google.Protobuf" Version="3.10.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.23.2" />
<PackageReference Include="Grpc.Tools" Version="2.24.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
这3个核心包是客户端必备的,其中grpc.tools帮我们把proto文件转化成C#代码。
也可以直接使用在项目文件里面代码设置如下:
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
var channel=GrpcChannel.ForAddress("https://localhost:5001");
var client=new Greeter.GreeterClient(channel);
var response=client.SayHello(new HelloRequest { Name="World" });
Console.WriteLine(response.Message);
启动gRPC服务端,在启动gRPC客户端控制台打印hello word表示成功。
identityServer接入gRPC是非常容易,和传统webapi差不多。
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
services.AddGrpc(x=> x.EnableDetailedErrors=false);
services.AddAuthorization();
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options=>
{
options.Authority="http://localhost:5000";
options.RequireHttpsMetadata=false;
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints=>
{
endpoints.MapGrpcService<GreeterService>();
endpoints.MapGet("/", async context=>
{
await context.Response.WriteAsync("Communication with gRPC endpoints must be made through a gRPC client. To learn how to create a client, visit: https://go.microsoft.com/fwlink/?linkid=2086909");
});
});
[Authorize]
public class GreeterService : Greeter.GreeterBase
{
}
这个时候我们启动Grpc.Client访问Grpc.Server服务
发现报错401。说明此服务需要携带令牌才能访问。
//获取token可以直接使用HttpClient来获取,这里使用IdentityModel来获取token
var httpClient=new HttpClient();
var disco=await httpClient.GetDiscoveryDocumentAsync("http://localhost:5000");
if (!disco.IsError)
{
var token=await httpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest()
{
Address=disco.TokenEndpoint,
ClientId="client",
ClientSecret="secret"
});
var tokenValue="Bearer " + token.AccessToken;
var metadata=new Metadata
{
{ "Authorization", tokenValue }
};
var callOptions=new CallOptions(metadata);
var channel=GrpcChannel.ForAddress("https://localhost:5001");
var client=new Greeter.GreeterClient(channel);
var response=client.SayHello(new HelloRequest { Name="World" }, callOptions);
Console.WriteLine(response.Message);
}
执行程序返回hello world表示成功。
传统调用webapi把token放到Header头的Authorization属性里面,grpc是放到Metadata里面,调用方法的时候传入CallOptions。使用上大同小异。
目前gRPC各个语言的支持都已经很完善,因为跨语言,性能更高的特性非常适合做内网的通信。笔者也将继续对gRPC进行跟进,会尝试将部分的内部服务改造成gRPC,关于gRPC的相关问题也可以留言大家一起讨论。 源代码地址:https://github.com/longxianghui/grpc_ientityserver
原文地址:https://www.cnblogs.com/longxianghui/p/11719190.html
在移动端平台开发中,为了增加代码复用,降低开发成本,通常会需要采用跨平台的开发技术,花椒也不例外。本次新的单品开发,由于时间紧,人员有限,经过调研选型,最终确定了 flutter 方案(具体选型过程不在本文讨论之内)。
为了让客户端更专注业务实现,降低接口联调测试成本,我们选用了 gRPC 方案。gRPC是一个高性能、通用的开源 RPC 框架,由 Google 开发并基于 HTTP/2 协议标准而设计,基于 ProtoBuf(Protocol Buffers)序列化协议开发,且支持当前主流开发语言。gRPC通过定义一个服务并指定一个可以远程调用的带有参数和返回类型的的方法,使客户端可以直接调用不同机器上的服务应用的方法,就像是本地对象一样。在服务端,服务实现这个接口并且运行 gRPC 服务处理客户端调用。在客户端,有一个stub提供和服务端相同的方法。
特点
gRPC-Web 为前端浏览器提供了 Javascript 库用来访问 gRPC 服务,但是需要通过 Envoy 提供代理服务。相比 JSON 的方式对前端不够友好,同时也增加了服务端的部署成本。因此在这次项目中前端未使用 gRPC 服务,而是由 gRPC-Gateway 提供代理的 RESTful 接口。
grpc-gateway 是 protoc 的一个插件,它能读取 gRPC 的服务定义并生成反向代理服务器,将 RESTful 的 JSON 请求转换为 gRPC 的方式。这样无需太多工作即可实现一套基于 gRPC 服务的 RESTful 接口,方便前端使用调用接口,同时也方便开发过程中通过 Postman/Paw 之类的工具调试接口。
gateway -> gRPC 映射方式:
例如,gRPC 接口要求的通用的 metadata 参数(如 platform, device_id 等)在 HTTP RESTful 的传递方式如下:
dart
为了便于客户端调用,连接复用及通用参数传递,我们封装了 dart 的基础库。
BaseClient 维护了针对 HOST 缓存的连接池,同时也提供了接口需要传递的 metadata 信息。
golang
golang 后端服务需要同时支持 gRPC 和 gateway 两种请求方式。为了简化部署和上线依赖,gateway 和 gRPC 的功能放在了一起,并通过拦截器注入对应的功能,主要包括 gRPC 统计,访问日志,接口鉴权,请求参数校验,gateway JSON 编码等。
为了提高开发效率,方便维护及模块复用,服务端按功能进行组件化开发。每个组件可以单独运行一个服务,也可以和其它组件共同组成一个服务。每个组件都需要实现 Component 接口:
对应组件开发完成后,需要开发对应的服务容器,步骤如下。
1 base.Init(context.TODO(), cfg, &global.Callback{
2 Authenticator: &auth.Callback{},
3 LogCapture: &log.Capture{},
4 })
base.DefaultServer.AddPublicServer(rpcPort, gatewayPort, setting.TLSConfig)
1 base.DefaultServer.RegisterComponent(&user.Component{})
2 base.DefaultServer.RegisterComponent(&push.Component{})
3 ...
base.DefaultServer.Serve()
proto 规范
gRPC 基于标准化的 IDL(ProtoBuf)来生成服务器端和客户端代码,我们决定将所有的接口描述及文档说明都放到 proto 文件中,便于查看及修改。对 proto 的接口描述及注释的规范如下:
代码生成
golang
1 gengo:
2 @protoc -Iproto \
3 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
4 -I${GOPATH}/src/github.com/lnnujxxy/protoc-gen-validate \
5 -I${GOPATH}/src/github.com/youlu-cn/grpc-gen/protoc-gen-auth \
6 --go_out=plugins=grpc:go/pb \
7 --grpc-gateway_out=logtostderr=true:go/pb \
8 --validate_out="lang=go:go/pb" \
9 --auth_out="lang=go:go/pb" \
10 proto/*.proto
golang 使用 go mod 的方式直接引入 pb 生成的 .go 文件
dart
修改 pubspec.yaml,执行 flutter packages get 或 flutter packages upgrade
1 dependencies: 2 flutter: 3 sdk: flutter 4 5 protobuf: ^0.13.4 6 grpc: ^1.0.1 7 user: 8 git: 9 url: git@github.com:project/repo.git 10 path: dart/user
gRPC gateway 提供了通过 proto 文件生成 swagger API 文档,缺点是只支持 gateway 的 RESTful 接口,并且默认的展示方式有点不符合我们的常规文档使用方式。
我们基于 protoc 插件开发了 protoc-gen-markdown 工具,可以由 proto 文件生成 markdown 文档,提供 gRPC 接口描述,以及 RESTful 接口描述及 JSON 示例,提供全文目录,支持锚点导航等。生成方式如下:
1gendoc:
2 @protoc -Iproto \
3 -I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
4 -I${GOPATH}/src/github.com/lnnujxxy/protoc-gen-validate \
5 -I${GOPATH}/src/github.com/youlu-cn/grpc-gen/protoc-gen-auth \
6 --markdown_out=":doc" \
7 proto/*.proto
文档会在对应路径生成接口列表 README.md,以及每个 protobuf 对应的接口文档。
传统的 RESTful 接口在调试及问题排查时,可以通过抓包或者 MitM(中间人攻击)的方式,配置也比较容易。而 gRPC 因为使用了 HTTP2 及 protobuf 二进制流,抓包及数据流反解难度相对较高,调试及问题排查时会比较复杂。为了解决这个问题,我们通过服务端注入的方式,配合查询后台过滤对应的请求日志,从而实现如下类似抓包的效果。
Go语言中文网,致力于每日分享编码、开源等知识,欢迎关注我,会有意想不到的收获!
本文由花椒服务端团队原创授权发布
*请认真填写需求信息,我们会在24小时内与您取得联系。