diff --git a/README-zh-CN.md b/README-zh-CN.md new file mode 100644 index 000000000..63f9ed816 --- /dev/null +++ b/README-zh-CN.md @@ -0,0 +1,227 @@ +# gRPC Spring Boot Starter + +[![Build Status](https://travis-ci.org/yidongnan/grpc-spring-boot-starter.svg?branch=master)](https://travis-ci.org/yidongnan/grpc-spring-boot-starter) +[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/net.devh/grpc-spring-boot-starter.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.devh%22%20grpc) +[![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) +[![Crowdin](https://badges.crowdin.net/grpc-spring-boot-starter/localized.svg)](https://crowdin.com/project/grpc-spring-boot-starter) + +[![Client-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-client-spring-boot-autoconfigure.svg?label=Client-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-client-spring-boot-autoconfigure) +[![Server-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-server-spring-boot-autoconfigure.svg?label=Server-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-server-spring-boot-autoconfigure) +[![Common-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-common-spring-boot.svg?label=Common-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-common-spring-boot) + +README: [English](README.md) | [中文](README-zh-CN.md) + +**文档:** [English](https://yidongnan.github.io/grpc-spring-boot-starter/en/) | [中文](https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/) + +QQ交流群:294712648 + +## 特性 + +* 在 spring boot 应用中,通过 `@GrpcService` 自动配置并运行一个嵌入式的 gRPC 服务。 + +* 使用 `@GrpcClient` 自动创建和管理您的 gRPC Channels 和 stubs + +* 支持[Spring Cloud](https://spring.io/projects/spring-cloud) (向 [Consul](https://github.com/spring-cloud/spring-cloud-consul) 或 [Eureka](https://github.com/spring-cloud/spring-cloud-netflix) 或 [Nacos](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) 注册服务并获取 gRPC 服务端信息) + +* 支持[Spring Sleuth](https://github.com/spring-cloud/spring-cloud-sleuth)作为分布式链路跟踪解决方案(如果[brave-instrument-grpc](https://mvnrepository.com/artifact/io.zipkin.brave/brave-instrumentation-grpc)存在) + +* 支持全局和自定义的 gRPC 服务端/客户端拦截器 + +* 支持 [Spring-Security](https://github.com/spring-projects/spring-security) + +* 支持metric (基于[micrometer](https://micrometer.io/)/[actuator](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-actuator) ) + +* 也适用于 (non-shaded) grpc-netty + +## 版本 + +2.x.x.RELEASE 支持 Spring Boot 2.1.x/2.2.x 和 Spring Cloud Greenwich / Hoxton。 + +最新版本: `2.5.1.RELEASE` + +( `2.4.0.RELEASE` 用于 Spring Boot 2.0.x & Spring Cloud Finchy). + +1.x.x.RELEASE 支持 Spring Boot 1 & Spring Cloud Edgware, Dalston, Camden. + +最新版本: `1.4.2.RELEASE` + +**注意:** 该项目也可以在没有 Spring-Boot 的情况下使用,但是您需要手动配置一些 bean。 + +## 用法 + +### gRPC 服务端 + 客户端 + +使用一下命令添加 Maven 依赖项: + +````xml + + net.devh + grpc-spring-boot-starter + 2.5.1.RELEASE + +```` + +Gradle: + +````gradle +dependencies { + compile 'net.devh:grpc-spring-boot-starter:2.5.1.RELEASE' +} +```` + +### gRPC 服务端 + +使用一下命令添加 Maven 依赖项: + +````xml + + net.devh + grpc-server-spring-boot-starter + 2.5.1.RELEASE + +```` + +Gradle: + +````gradle +dependencies { + compile 'net.devh:grpc-server-spring-boot-starter:2.5.1.RELEASE' +} +```` + +在服务端接口实现类上添加 `@GrpcService` 注解。 + +````java +@GrpcService +public class GrpcServerService extends GreeterGrpc.GreeterImplBase { + + @Override + public void sayHello(HelloRequest req, StreamObserver responseObserver) { + HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + req.getName()).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + +} +```` + +默认情况下,Grpc 服务器将监听端口 `9090`。 端口的配置和其他的 [设置](grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/config/GrpcServerProperties.java) 可以通过 Spring 的属性机制进行更改。 服务端的配置使用 `grpc.server.` 前缀。 + +详情请参阅我们的[文档](https://yidongnan.github.io/grpc-spring-boot-starter/)。 + +### gRPC 客户端 + +使用一下命令添加 Maven 依赖项: + +````xml + + net.devh + grpc-client-spring-boot-starter + 2.5.1.RELEASE + +```` + +Gradle: + +````gradle +dependencies { + compile 'net.devh:grpc-client-spring-boot-starter:2.5.1.RELEASE' +} +```` +在 grpc 客户端的的 stub 字段上添加 `@GrpcClient(serverName)` 注解。 + +* 请不要将 @GrpcClient 与 `@Autowireed` 或 `@Inject` 一起使用。 + + ````java + @GrpcClient("gRPC server name") + private GreeterGrpc.GreeterBlockingStub greeterStub; + ```` + +**注意:** 你可以将相同的 grpc 服务端名称用于多个 channel, 也可以使用不同的 stub (甚至使用不同的 stub 拦截器) + +然后您可以向您的服务器发送查询,就像这样: + +````java +HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName(name).build()); +```` + +可以单独配置每个客户端的目标地址。 但在某些情况下,您可以仅依靠默认配置。 您可以通过 `NameResolver.Factory` Bean 类自定义默认的 url 映射。 如果您没有配置那个Bean,那么默认的 uri 将使用默认方案和名称(如:`dns:`): + +这些配置和其他的 [设置](grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java) 可以通过 Spring 的属性机制进行更改。 客户端使用`grpc.client.(serverName)。` 前缀。 + +详情请参阅我们的[文档](https://yidongnan.github.io/grpc-spring-boot-starter/)。 + +## 使用 (non-shaded) grpc-netty 运行 + +这个库支持`grpc-netty`和`grpc-nety-shaded`。 后一种可能会防止与不兼容的 gRPC 版本冲突或不同 netty 版本之间的冲突。 + +**注意:** 如果在classpath 中存在 shaded netty, 则 shaded netty 将使用有线与 non-shaded grpc-netty。 + +您可以在 Maven 中这样使用。 + +````xml + + io.grpc + grpc-netty + ${grpcVersion} + + + + + net.devh + grpc-spring-boot-starter + ... + + + io.grpc + grpc-netty-shaded + + + + + + net.devh + grpc-server-spring-boot-starter + ... + + + io.grpc + grpc-netty-shaded + + + + + + net.devh + grpc-client-spring-boot-starter + ... + + + io.grpc + grpc-netty-shaded + + + +```` + +Gradle: + +````groovy +compile "io.grpc:grpc-netty:${grpcVersion}" + +compile 'net.devh:grpc-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For both +compile 'net.devh:grpc-client-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the client (only) +compile 'net.devh:grpc-server-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the server (only) +```` + +## 示例项目 + +在 [这里](examples)可以阅读更多关于我们的示例项目。 + +## 排除故障 + +请参阅我们的[文档](https://yidongnan.github.io/grpc-spring-boot-starter/en/trouble-shooting)寻求帮助。 + +## 参与贡献 + +我们始终欢迎您对项目作出任何贡献。 详见[CONTRIBUTING.md](CONTRIBUTING.md)。 diff --git a/README-zh.md b/README-zh.md deleted file mode 100644 index e85ef37bf..000000000 --- a/README-zh.md +++ /dev/null @@ -1,554 +0,0 @@ -# gRPC Spring Boot Starter - -[![Build Status](https://travis-ci.org/yidongnan/grpc-spring-boot-starter.svg?branch=master)](https://travis-ci.org/yidongnan/grpc-spring-boot-starter) -[![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/net.devh/grpc-spring-boot-starter.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.devh%22%20grpc) -[![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) - -[![Client-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-client-spring-boot-autoconfigure.svg?label=Client-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-client-spring-boot-autoconfigure) -[![Server-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-server-spring-boot-autoconfigure.svg?label=Server-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-server-spring-boot-autoconfigure) -[![Common-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-common-spring-boot.svg?label=Common-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-common-spring-boot) - -README: [English](README.md) | [中文](README-zh.md) - -**GitHub地址:https://github.com/yidongnan/grpc-spring-boot-starter** - -Java技术交流群:294712648 Java技术交流群 - -## 特点 - -* 使用`@ GrpcService`自动创建并运行一个 gRPC 服务,内嵌在 spring-boot 应用中 - -* 使用`@ GrpcClient`自动创建和管理你的``channel``和``stub`` - -* 支持 [Spring Cloud](https://spring.io/projects/spring-cloud)(向 [Consul](https://github.com/spring-cloud/spring-cloud-consul) 或 [Eureka](https://github.com/spring-cloud/spring-cloud-netflix) 或 [Nacos](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) 注册服务并获取gRPC服务信息) - -* 支持 [Spring Sleuth](https://github.com/spring-cloud/spring-cloud-sleuth) 进行链路跟踪(需要单独引入 [brave-instrumentation-grpc](https://mvnrepository.com/artifact/io.zipkin.brave/brave-instrumentation-grpc)) - -* 支持对 server、client 分别设置全局拦截器或单个的拦截器 - -* 支持 [Spring-Security](https://github.com/spring-projects/spring-security) - -* 支持 metric ([micrometer](https://micrometer.io/) / [actuator](https://github.com/spring-projects/spring-boot/tree/master/spring-boot-project/spring-boot-actuator)) - -* 可以使用 (non-shaded) grpc-netty - -## 版本 - -2.x.x.RELEASE 支持 Spring Boot 2.1+ & Spring Cloud Greenwich。 - -最新的版本:``2.5.1.RELEASE`` - -(使用 `2.4.0.RELEASE` 版本可以支持 Spring Boot 2.0.X & Spring Cloud Finchley). - -1.x.x.RELEASE 支持 Spring Boot 1 & Spring Cloud Edgware 、Dalston、Camden。 - -最新的版本:``1.4.2.RELEASE`` - -**注意:** 此项目也可以在没有 Spring-Boot 的场景下使用,但需要手动的配置相关的 bean。 - -## 使用方式 - -### gRPC server + client - -如果使用的是 Maven,添加如下依赖 - -````xml - - net.devh - grpc-spring-boot-starter - 2.5.1.RELEASE - -```` - -如果使用的 Gradle,添加如下依赖 - -````gradle -dependencies { - compile 'net.devh:grpc-spring-boot-starter:2.5.1.RELEASE' -} -```` - -### gRPC 服务端 - -如果使用的是 Maven,添加如下依赖 - -````xml - - net.devh - grpc-server-spring-boot-starter - 2.5.1.RELEASE - -```` - -如果使用的 Gradle,添加如下依赖 - -````gradle -dependencies { - compile 'net.devh:grpc-server-spring-boot-starter:2.5.1.RELEASE' -} -```` - -实现 gRPC server 的业务逻辑,并使用 ``@GrpcService`` 注解 - -````java -@GrpcService -public class GrpcServerService extends GreeterGrpc.GreeterImplBase { - - @Override - public void sayHello(HelloRequest req, StreamObserver responseObserver) { - HelloReply reply = HelloReply.newBuilder().setMessage("Hello ==> " + req.getName()).build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } -} -```` - -设置 gRPC 的 host 跟 port ,默认的监听的 port 是 9090。其他配置属性可以参考 -[settings](grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/config/GrpcServerProperties.java)。 -所有的配置属性在 server 中使用需增加 `grpc.server.` 的前缀 - -#### 服务端配置属性示例 - -````properties -grpc.server.port=9090 -grpc.server.address=0.0.0.0 -#grpc.server.inProcessName=test -```` - -#### 对 Server 进行自定义 - -当前项目同样支持对 `ServerBuilder` 的自定义修改,需要在创建的过程中使用 `GrpcServerConfigurer` beans。 - -````java -@Bean -public GrpcServerConfigurer keepAliveServerConfigurer() { - return serverBuilder -> { - if (serverBuilder instanceof NettyServerBuilder) { - ((NettyServerBuilder) serverBuilder) - .keepAliveTime(30, TimeUnit.SECONDS) - .keepAliveTimeout(5, TimeUnit.SECONDS) - .permitKeepAliveWithoutCalls(true); - } - }; -} -```` - -#### Server-Security -支持使用 Spring-Security 加密你的 gRPC 应用。你只需要添加 Spring-Security(core 或者 config)依赖,然后根据需要再增加加密的配置 - -##### 首先需要选择一个认证方案 - - -* **BasicAuth(基础认证)** - - ````java - @Bean - AuthenticationManager authenticationManager() { - final List providers = new ArrayList<>(); - providers.add(...); // Possibly DaoAuthenticationProvider - return new ProviderManager(providers); - } - - @Bean - GrpcAuthenticationReader authenticationReader() { - final List readers = new ArrayList<>(); - readers.add(new BasicGrpcAuthenticationReader()); - return new CompositeGrpcAuthenticationReader(readers); - } - ```` - -* **Bearer Authentication (OAuth2/OpenID-Connect)** - - ````java - @Bean - AuthenticationManager authenticationManager() { - final List providers = new ArrayList<>(); - providers.add(...); // Possibly JwtAuthenticationProvider - return new ProviderManager(providers); - } - - @Bean - GrpcAuthenticationReader authenticationReader() { - final List readers = new ArrayList<>(); - readers.add(new BearerAuthenticationReader(accessToken -> new BearerTokenAuthenticationToken(accessToken))); - return new CompositeGrpcAuthenticationReader(readers); - } - ```` - - 你可能还想定义自己的 *GrantedAuthoritiesConverter* ,将权限和角色的信息映射到 Spring Security 的 `GrantedAuthority` 中 - -* **Certificate Authentication(证书认证)** - - ````java - @Bean - AuthenticationManager authenticationManager() { - final List providers = new ArrayList<>(); - providers.add(new X509CertificateAuthenticationProvider(userDetailsService())); - return new ProviderManager(providers); - } - - @Bean - GrpcAuthenticationReader authenticationReader() { - final List readers = new ArrayList<>(); - readers.add(new SSLContextGrpcAuthenticationReader()); - return new CompositeGrpcAuthenticationReader(readers); - } - ```` - - 相关的配置属性如下: - - ````properties - grpc.server.security.enabled=true - grpc.server.security.certificateChain=file:certificates/server.crt - grpc.server.security.privateKey=file:certificates/server.key - grpc.server.security.trustCertCollection=file:certificates/trusted-clients-collection - grpc.server.security.clientAuth=REQUIRE - ```` - -* **使用 `CompositeGrpcAuthenticationReader` 类链式的调用多个认证方案** -* **自定义认证方式(继承并实现 `GrpcAuthenticationReader` 类)** - -##### 如何去保护你的这些服务 - -* **使用 Spring-Security 的注解** - - ````java - @Configuration - @EnableGlobalMethodSecurity(proxyTargetClass = true, ...) - public class SecurityConfiguration { - ```` - - 如果你想使用 Spring Security 相关的注解的话,`proxyTargetClass` 属性是必须的! - 但是你会受到一条警告,提示 MyServiceImpl#bindService() 方式是用 final 进行修饰的。 - 这条警告目前无法避免,但它是安全的,可以忽略它。 - -* **手动配置** - - ````java - @Bean - AccessDecisionManager accessDecisionManager() { - final List> voters = new ArrayList<>(); - voters.add(new AccessPredicateVoter()); - return new UnanimousBased(voters); - } - - @Bean - GrpcSecurityMetadataSource grpcSecurityMetadataSource() { - final ManualGrpcSecurityMetadataSource source = new ManualGrpcSecurityMetadataSource(); - source.set(MyServiceGrpc.getSecureMethod(), AccessPredicate.hasRole("ROLE_USER")); - source.setDefault(AccessPredicate.permitAll()); - return source; - } - ```` - -### gRPC 客户端 - -如果使用的是 Maven,添加如下依赖 - -````xml - - net.devh - grpc-client-spring-boot-starter - 2.5.1.RELEASE - -```` - -如果使用的 Gradle,添加如下依赖 - -````gradle -dependencies { - compile 'net.devh:grpc-client-spring-boot-starter:2.5.1.RELEASE' -} -```` - -这里有三种方式去或得一个gRPC server的连接 - - -* 使用 `grpcChannelFactory.createChannel(serverName)` 去创建一个 `Channel`,并创建一个自己的 gRPC stub. - - ````java - @Autowired - private GrpcChannelFactory grpcChannelFactory; - - private GreeterGrpc.GreeterBlockingStub greeterStub; - - @PostConstruct - public void init() { - Channel channel = grpcChannelFactory.createChannel("gRPC server name"); - greeterStub = GreeterGrpc.newBlockingStub(channel); - } - ```` - -* 通过在 `Channel` 类型的字段上加入 `@GrpcClient(serverName)` 注解,并创建一个自己的 gRPC stub. - * 不需要使用 `@Autowired` 或者 `@Inject` 来进行注入 - - ````java - @GrpcClient("gRPC server name") - private Channel channel; - - private GreeterGrpc.GreeterBlockingStub greeterStub; - - @PostConstruct - public void init() { - greeterStub = GreeterGrpc.newBlockingStub(channel); - } - ```` - -* 直接将 `@GrpcClient(serverName)` 注解加在调用客户端的 stub 上 - * 不需要使用 `@Autowired` 或者 `@Inject` 来进行注入 - - ````java - @GrpcClient("gRPC server name") - private GreeterGrpc.GreeterBlockingStub greeterStub; - ```` - -**注意:** 你可以为多个 channels 和多个不同的 stubs 使用相同的 serverName (除非他们拦截器不一样). - -然后你可以直接向服务端发起请求,如下: - -````java -HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName(name).build()); -```` - -可以单独为每一个 client 配置对应的 address -但在某些情况下,你可以调整默认的配置。 -你可以通过 `NameResolver.Factory` beans 去自定义默认的 url 映射,如果你没有配置这个 bean,那将会按照下面的方式进行解析: - -* 如果存在一个 `DiscoveryClient` 的 bean,这时会使用 client name 去注册中心上进行获取对应服务的 address -* 否则 client 端将使用 `localhost` 和 `9090` 端口 - -其他的配置属性参考 [settings](grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java),所有的配置文件在 client 端使用时需要增加 `grpc.client.(serverName).`的前缀 - -你也可以配置多个目标地址,请求时会自动使用负载均衡 - -* `static://127.0.0.1:9090,[::1]:9090` - -你也可以使用服务发现去获取目标地址(要求一个 `DiscoveryClient` bean) - -* `discovery:///my-service-name` - -此外,你也可以使用 DNS 的方式去获取目标地址 - -* `dns:///example.com` - -同时,你也可以使用如下方式直接进程内访问 - -* `in-process:test` - -它会通过DNS将域名解析出所有真实的 IP 地址,通过使用这些真实的IP地址去做负载均衡。 -需要注意的是 `grpc-java` 出于性能的考虑对 DNS 返回的结果做缓存。 -有关这些和其他原生支持的 `NameResolverProviders` 参考官方文档 [grpc-java sources](https://github.com/grpc/grpc-java) - -#### 客户端配置属性示例 - -````properties -grpc.client.GLOBAL.enableKeepAlive=true - -grpc.client.(gRPC server name).address=static://localhost:9090 -# Or -grpc.client.myName.address=static://localhost:9090 -```` - -`GLOBAL` 是一个特殊的常量,它可以用于对所有 Client 统一的设置属性。 -属性覆盖的顺序:Client单独的属性 > `GLOBAL`的属性 > 默认的属性 - -#### 自定义 Client - -This library also supports custom changes to the `ManagedChannelBuilder` and gRPC client stubs during creation by creating `GrpcChannelConfigurer` and `StubTransformer` beans. -当前项目支持对 `ManagedChannelBuilder` 的自定义,在 gRPC client stub创建的过程中,通过使用 `GrpcChannelConfigurer` 或 `StubTransformer` bean -来完成自定义操作 - -````java -@Bean -public GrpcChannelConfigurer keepAliveClientConfigurer() { - return (channelBuilder, name) -> { - if (channelBuilder instanceof NettyChannelBuilder) { - ((NettyChannelBuilder) channelBuilder) - .keepAliveTime(15, TimeUnit.SECONDS) - .keepAliveTimeout(5, TimeUnit.SECONDS); - } - }; -} - -@Bean -public StubTransformer authenticationStubTransformer() { - return (clientName, stub) -> stub.withCallCredentials(grpcCredentials(clientName)); -} -```` - -#### 客户端认证 - -**注意:** 以下列出的一些方法仅仅适用于通过注入得到的 stubs,如果你通过注入 Channel,手动的在去创建 stubs,这就需要你自己手动的 -去配置凭证。然而你同样能从目前所提供的一些辅助类方法中收益。 - -客户端有许多不同的认证方式,我们只需定义一个类型为 `CallCredentials` 的 bean,它会自动作用于身份验证。目前通过一些辅助方法可以支持 -下列的认证方式: - -* **BasicAuth** - - ````java - @Bean - CallCredentials grpcCredentials() { - return CallCredentialsHelper.basicAuth(username, password); - } - ```` - -* **Bearer Authentication (OAuth2, OpenID-Connect)** - - ````java - @Bean - CallCredentials grpcCredentials() { - return CallCredentialsHelper.bearerAuth(token); - } - ```` - -* **Certificate Authentication** - - 需要一些配置属性: - - ````properties - #grpc.client.test.security.authorityOverride=localhost - #grpc.client.test.security.trustCertCollection=file:certificates/trusted-servers-collection - grpc.client.test.security.clientAuthEnabled=true - grpc.client.test.security.certificateChain=file:certificates/client.crt - grpc.client.test.security.privateKey=file:certificates/client.key - -* **为每个 client 使用不同的认证** - - 通过定义一个 `StubTransformer` bean 来代替原有的 `CallCredentials` bean - - ````java - @Bean - StubTransformer grpcCredentialsStubTransformer() { - return CallCredentialsHelper.mappedCredentialsStubTransformer( - Map.of( - clientA, callCredentialsAC, - clientB, callCredentialsB, - clientC, callCredentialsAC)); - } - ```` - - **注意:** 如果你配置了 `CallCredentials` bean,然后再使用 `StubTransformer` 的话可能会造成冲突。 - -## 使用 (non-shaded)grpc-netty - -当前项目目前支持 `grpc-netty` 和 `grpc-netty-shaded`。 -使用 `grpc-netty-shaded` 可以防止 grpc 跟 netty 版本的兼容性问题。 - -**注意:** 如果 `grpc-netty-shaded` 已经存在于 classpath 中, 那么将优先使用 shaded-netty - -如果你使用的Maven,你可以使用如下的配置: - -````xml - - io.grpc - grpc-netty - ${grpcVersion} - - - - - net.devh - grpc-spring-boot-starter - ... - - - io.grpc - grpc-netty-shaded - - - - - - net.devh - grpc-server-spring-boot-starter - ... - - - io.grpc - grpc-netty-shaded - - - - - - net.devh - grpc-client-spring-boot-starter - ... - - - io.grpc - grpc-netty-shaded - - - -```` - -如果你使用的 Gradle,你可以使用如下的配置: - -````groovy -compile "io.grpc:grpc-netty:${grpcVersion}" - -compile 'net.devh:grpc-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For both -compile 'net.devh:grpc-client-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the client (only) -compile 'net.devh:grpc-server-spring-boot-starter:...' exclude group: 'io.grpc', module: 'grpc-netty-shaded' // For the server (only) -```` - -## 示例项目 - -查看更多的示例项目 [here](examples). - -## 常见的问题 - -在你深入去查问题之前,请先确保 grpc 跟 netty 的版本是彼此兼容的。 -当前项目自带的依赖会确保 grpc 和 netty 是能一起正常工作。 -但是在某些情况下,你可能需要用到其他库(如 tcnative )或其他依赖项需要用到不同的 netty 版本,这就可能会造成版本冲突。 -为了防止此类问题,gRPC 和 我们建议你使用 grpc-netty-shaded 依赖。 -如果你使用 (non-shaded) grpc-netty,请查看[表格](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty)中展示的[grpc-java](https://github.com/grpc/grpc-java) 的兼容版本 - -### SSL 相关的问题 - -* `java.lang.IllegalStateException: Failed to load ApplicationContext` -* `Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'grpcServerLifecycle'` -* `Caused by: java.lang.IllegalStateException: Could not find TLS ALPN provider; no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available` - -or - -* `AbstractMethodError: io.netty.internal.tcnative.SSL.readFromSSL()` - -or - -* `javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem` - -您的类路径中没有 `netty-tcnative ...`库或者没有正确的版本。 - -(在netty 4.1.24.Final -> 4.1.24.Final 和 netty-tcnative 2.0.8.Final -> 2.0.12.Final 版本之间存在非向下兼容更新) - -查看 [grpc-java: Security](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty). - -### 测试期间 SSL 的问题 - -默认情况下,gRPC 客户端假定服务器使用的 TLS,并尝试使用安全连接,在开发和测试期间,一般都不需要证书,你可以切换到 `PLAINTEXT` 的连接方式。 - -````properties -grpc.client.(gRPC server name).negotiationType=PLAINTEXT -```` - -**注意:** 在生产环境,我们强烈推荐你使用 `TLS` 的模式。 - -### 在测试模式下,端口已经被使用 - -有时候你只想启动你的应用程序去检测服务之间的交互,那么这将启动 gRPC 服务,然后你不需要为每个测试方法或测试类单独的去关闭 gRPC 服务, -在你的测试类或者方法中使用 `@DirtiesContext` 注解可以避免这个问题。 - -### 找不到与 xxx 匹配的名称 - -* `io.grpc.StatusRuntimeException: UNAVAILABLE: io exception` -* `Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem` -* `Caused by: java.security.cert.CertificateException: No name matching gRPC server name found` - -客户端配置的 `gRPC server name` 的名称与服务器上公用或者备用的证书名称不匹配。你必须将 `grpc.client.(gRPC server name).security.authorityOverride` 属性设置成一个存在的名称。 - -## 贡献 - -我们欢迎任何人为这个项目做出自己的贡献! 贡献时需要参考 [CONTRIBUTING.md](CONTRIBUTING.md) 文档. diff --git a/README.md b/README.md index 2b4a83f6b..6d8b41b4b 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,15 @@ [![Build Status](https://travis-ci.org/yidongnan/grpc-spring-boot-starter.svg?branch=master)](https://travis-ci.org/yidongnan/grpc-spring-boot-starter) [![Maven Central with version prefix filter](https://img.shields.io/maven-central/v/net.devh/grpc-spring-boot-starter.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22net.devh%22%20grpc) [![MIT License](https://img.shields.io/github/license/mashape/apistatus.svg)](LICENSE) +[![Crowdin](https://badges.crowdin.net/grpc-spring-boot-starter/localized.svg)](https://crowdin.com/project/grpc-spring-boot-starter) [![Client-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-client-spring-boot-autoconfigure.svg?label=Client-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-client-spring-boot-autoconfigure) [![Server-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-server-spring-boot-autoconfigure.svg?label=Server-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-server-spring-boot-autoconfigure) [![Common-Javadoc](https://www.javadoc.io/badge/net.devh/grpc-common-spring-boot.svg?label=Common-Javadoc)](https://www.javadoc.io/doc/net.devh/grpc-common-spring-boot) -README: [English](README.md) | [中文](README-zh.md) +README: [English](README.md) | [中文](README-zh-CN.md) -**Documentation:** [English](https://yidongnan.github.io/grpc-spring-boot-starter/en/) +**Documentation:** [English](https://yidongnan.github.io/grpc-spring-boot-starter/en/) | [中文](https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/) ## Features diff --git a/docs/index.md b/docs/index.md index e1a646f15..4647ac8b6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,3 +3,4 @@ Please select a language: - [English](en) +- [中文](zh-CN) diff --git a/docs/zh-CN/actuator.md b/docs/zh-CN/actuator.md new file mode 100644 index 000000000..69c1e5dcc --- /dev/null +++ b/docs/zh-CN/actuator.md @@ -0,0 +1,145 @@ +# 支持 Spring Boot Actuator + +[<- 返回索引](index) + +此页面重点介绍与 [Spring-Boot-Actator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html) 的集成。 这是一个可选的功能。 支持的特性 + +- 客户端 + 服务端指标 +- 服务端的`InfoContributor` + +## 目录 + +- [依赖项](#dependencies) +- [指标](#metrics) + - [计数器](#counter) + - [计时器](#timer) + - [查看指标](#viewing-the-metrics) + - [指标配置](#metric-configuration) +- [InfoContributor](#infocontributor) +- [关闭指标功能](#opt-out) + +## 依赖项 + +指标收集和其他执行器一样都是可选的,如果应用程序环境中有 `MeterRegistry` ,它们将自动启用。 + +您可以简单地通过向Maven添加以下依赖来实现这一点: + +````xml + + org.springframework.boot + spring-boot-starter-actuator + +```` + +Gradle: + +````groovy +compile("org.springframework.boot:spring-boot-starter-actuator") +```` + +> **注意:** 在大多数情况下,您还需要 `spring-boot-web` 依赖才能实际查看到指标。 请注意,spring-boot-web 运行的端口于不同的 grpc 服务端 (通常是 `8080`)。 如果您不想添加添加一个 web-server,您仍然可以通过 JMX (如果启用的话) 访问这些指标。 + +## 指标 + +一旦依赖关系被添加,grpc-spring-boot-starter 将自动配置`ClientIntercertor` / `ServerInterceptor` 以收集指标。 + +### 计数器 + +- `grpc.client.requests.sent`: 发送的总请求数。 +- `grpc.client.responses.received`: 接受的总响应数。 +- `grpc.server.requests.received`: 收到的总请求数。 +- `grpc.server.responses.sent`: 发送的总响应数。 + +**标签** + +- `service`: 请求的 grpc 服务名称(使用 protubuf 名称) +- `method`: 请求的 grpc 方法名称(使用 protobuf 名称) +- `methodType`: 请求的 grpc 方法的类型。 + +### 计时器 + +- `grpc.client.processing.duration`: 客户端完成请求所花费的总时间,包括网络延迟。 +- `grpc.server.processing.duration`: 服务端完成请求所花费的时间。 + +**标签** + +- `service`: 请求的 grpc 服务名称(使用 protobuf 名称) +- `method`: 请求的 grpc 方法名称(使用 protobuf 名称) +- `methodType`: 请求的 grpc 方法的类型。 +- `statusCode`: 响应的 `Status.Code` + +### 查看指标 + +您可以在 `/actorator/metrics` (需要一个web-server) 或通过 JMX 查看 grpc 的指标以及其他指标。 + +> **注意:** 你可能需要先启用指标。 +> +> ````properties management.endpoints.web.exposure.include=metrics +> +> # management.endpoints.jmx.exposure.include=metrics +> +> management.endpoint.metrics.enabled=true ```` + +阅读官方文档以了解更多关于[Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-endpoints.html) 的信息。 + +### 指标配置 + +默认情况下,客户端只会为已进行的请求创建指标。 然而,服务端将尝试所有找到并注册的服务,来初始化它们的指标。 + +您可以通过覆盖Bean的创建自定义的行为。 下面使用`MetricCollectingClientInterceptor`来展示这一点: + +````java +@Bean +MetricCollectingClientInterceptor metricCollectingClientInterceptor(MeterRegistry registry) { + MetricCollectingClientInterceptor collector = new MetricCollectingClientInterceptor(registry, + counter -> counter.tag("app", "myApp"), // Customize the Counters + timer -> timer.tag("app", "myApp"), // Customize the Timers + Code.OK, Code.INVALID_ARGUMENT, Code.UNAUTHENTICATED); // Eagerly initialized status codes + // Pre-generate metrics for some services (to avoid missing metrics after restarts) + collector.preregisterService(MyServiceGrpc.getServiceDescriptor()); + return collector; +} +```` + +## InfoContributor + +*仅限服务器* + +服务端会自动配置一个 [`InfoContributor`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/actuate/info/InfoContributor.html) 并公开一下信息: + +- `grpc.server`: + - `port`: grpc 服务端的端口 + - `services`: grpc 的服务列表 + - 方法 + +您可以在 `/actorator/info` (需要一个web-server) 或通过 JMX 查看 grpc 的信息以及其他信息。 + +> **注意:** 你可能需要先启用信息。 +> +> ````properties management.endpoints.web.exposure.include=info +> +> # management.endpoints.jmx.exposure.include=info +> +> management.endpoint.info.enabled=true ```` + +您可以使用 `grpc.server.reflectionServiceEnabled=false` 来打开服务列表(对于 actuator 和 grpc)。 + +## 关闭指标功能 + +您可以选择退出自动配置,使用以下注解: + +````java +@EnableAutoConfiguration(exclude = {GrpcClientMetricAutoConfiguration.class, GrpcServerMetricAutoConfiguration.class}) +```` + +或使用配置: + +````properties +spring.autoconfigure.exclude=\ +net.devh.boot.grpc.client.autoconfigure.GrpcClientMetricAutoConfiguration,\ +net.devh.boot.grpc.server.autoconfigure.GrpcServerMetricAutoConfiguration +```` + +---------- + +[<- 返回索引](index) diff --git a/docs/zh-CN/benchmarking.md b/docs/zh-CN/benchmarking.md new file mode 100644 index 000000000..77dcf1fd7 --- /dev/null +++ b/docs/zh-CN/benchmarking.md @@ -0,0 +1,22 @@ +# 基准测试 + +[<- 返回索引](index) + +这个项目不会给 gRPC-java 增加任何性能开销。 + +请参考官方的 gRPC 基准测试: + +- [grpc.io: Benchmarking](https://grpc.io/docs/guides/benchmarking/) +- [grpc-java: Running Benchmarks](https://github.com/grpc/grpc-java/tree/master/benchmarks#grpc-benchmarks) + +与纯 HTTP 相比,gRPC 具有很多优势,但是很难将它数字化。 经过高度优化的 Web 服务器的性能与普通的 gRPC 服务器可能一样好。 + +下面是普通HTTP与 grpc 的主要优点/差异: + +- 二进制数据格式 (更快, 但不可读) +- Protobuf 定义的数据结构,可以用于为许多语言生成数据类和客户端。 +- HTTP/2 连接和连接池 + +---------- + +[<- 返回索引](index) diff --git a/docs/zh-CN/brave.md b/docs/zh-CN/brave.md new file mode 100644 index 000000000..de0d47b59 --- /dev/null +++ b/docs/zh-CN/brave.md @@ -0,0 +1,72 @@ +# 支持 Brave / Sleuth + +[<- 返回索引](index) + +此页面将着重介绍与 [Brave](https://github.com/openzipkin/brave) / [Sleuth](https://spring.io/projects/spring-cloud-sleuth) 的集成。 这是一个可选的功能。 + +## 目录 + +- [依赖项](#dependencies) + - [Brave](#brave) + - [Spring Cloud Sleuth](#spring-cloud-sleuth) +- [关闭链路跟踪](#opt-out) +- [附加信息](#additional-notes) + +## 依赖项 + +grpc-spring-boot-starter 支持为 `Brave Instrumentation:GRPC` 提供自动配置。 然而,有两个要求: + +1. 您需要在 classpath 添加 Brave 依赖项。 +2. 您需要在应用上下文中有一个 `Trace` bean。 *如果您的 classpath 有 Spring Cloud Sleuth,它将自动为您配置此 Bean* + +### Brave + +您可以在 Maven 中添加 Brave 的依赖项。 + +````xml + + io.zipkin.brave + brave-instrumentation-grpc + +```` + +Gradle: + +````groovy +compile("io.zipkin.brave:brave-instrumentation-grpc") +```` + +### Spring Cloud Sleuth + +您可以在 Maven 中添加 Sleuth 依赖 + +````xml + + org.springframework.cloud + spring-cloud-starter-sleuth + +```` + +Gradle: + +````groovy +compile("org.springframework.cloud:spring-cloud-starter-sleuth") +```` + +请参阅[官方文件](https://spring.io/projects/spring-cloud-sleuth)以了解如何设置 / 配置 Sleuth。 + +## 关闭链路跟踪 + +您可以使用以下属性关闭 grpc 的链路跟踪: + +````property +spring.sleuth.grpc.enabled=false +```` + +## 附加信息 + +Spring-Cloud-Sleuth 提供了一些类,例如[`SpringAwareManagedChannelBuilder`](https://javadoc.io/page/org.springframework.cloud/spring-cloud-sleuth-core/latest/org/springframework/cloud/sleuth/instrument/grpc/SpringAwareManagedChannelBuilder.html),这些类仅仅由于与其他的库兼容而存在。 不要跟那个项目一期使用。 grpc-spring-boot-starter 通过 [`GrpcChannelFactory`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/channelfactory/GrpcChannelFactory.html) 和相关类提供相同 / 扩展的功能提供了开箱即用的能力。 相关阅读 [sleuth's javadoc note](https://github.com/spring-cloud/spring-cloud-sleuth/blob/59216c32f7848ec337fb68d1dbec8e87eeb6bf59/spring-cloud-sleuth-core/src/main/java/org/springframework/cloud/sleuth/instrument/grpc/SpringAwareManagedChannelBuilder.java#L31-L34)。 + +---------- + +[<- 返回索引](index) diff --git a/docs/zh-CN/client/configuration.md b/docs/zh-CN/client/configuration.md new file mode 100644 index 000000000..3ffb81f7d --- /dev/null +++ b/docs/zh-CN/client/configuration.md @@ -0,0 +1,114 @@ +# 配置 + +[<- 返回索引](../index) + +本节描述您如何配置您的 grpc-spring-boot-starter 客户端。 + +## 目录 + +- [通过属性配置](#configuration-via-properties) + - [选择目标](#choosing-the-target) +- [通过Beans 配置](#configuration-via-beans) + - [GrpcChannelConfigurer](#grpcchannelconfigurer) + - [StubTransformer](#stubtransformer) + +## 附加主题 + +- [入门指南](getting-started) +- *配置* +- [安全性](security) + +## 通过属性配置 + +grpc-spring-boot-starter 可以通过 Spring 的 `@ConfigurationProperties` 机制来进行 [配置](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html) + +您可以在这里找到所有内建配置属性: + +- [`GrpcChannelsProperties`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/config/GrpcChannelsProperties.html) +- [`GrpcChannelProperties`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/config/GrpcChannelProperties.html) +- [`GrpcServerProperties.Security`](https://static.javadoc.io/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/config/GrpcChannelProperties.Security.html) + +如果你希望阅读源代码,你可以查阅 [这里](https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/grpc-client-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/client/config/GrpcChannelProperties.java#L58)。 + +Channels 的属性都是以 `grpc.client.__name__.` 或 `grpc.client.__name__.security.` 为前缀。 Channel 的名称从 `@GrpcClient` 注解中获取。 如果您想要配置一些其他的选项,如为所有服务端设置可信证书,并可以使用 `GLOBAL` 作为名称。 单个 channel 的属性配置会覆盖全局配置。 + +### 选择目标 + +您可以使用以下属性指定目标服务器: + +````properties +grpc.client.__name__.address=static://localhost:9090 +```` + +目标服务器支持多种 schemes,您可以使用它们来指定目标服务器(优先级0(低) - 10(高)): + +- `static`(优先级 4): 一个简单的IP静态列表 (v4 和 v6), 可用于连接到服务端 (支持 `localhost`)。 例如:`static://192.168.1:8080,10.0.0.1:1337` +- [`dns`](https://github.com/grpc/grpc-java/blob/master/core/src/main/java/io/grpc/internal/DnsNameResolver.java#L66)(优先级 5):解析并绑定到给定 DNS 名称的所有地址。 地址将被缓存,只有当现有连接被关闭 / 失败时才会刷新。 更多选项,例如 `SVC` 查找(对 kubernetes 有用),可以通过系统属性启用。 例如:`dns:///example.my.company` +- `discovery` (优先级 6):(可选) 使用 Spring Cloud 的`DiscoveryClient` 去查找合适的目标。 在 `HeartbeatEvent` 的时候,连接将自动刷新。 使用 `gRPC.port` 元数据来确定端口,否则使用服务端口。 示例: `discovery:///service-name` +- `self`(优先级 0):如果您使用 `grpc-server-spring-boot-starter` 并且不想指定自己的地址 / 端口,则可以使用 self 关键词作为 scheme 或者 address 。 这对于需要使用随机服务器端口以避免冲突的测试特别有用。 例如:`self`或`self:self` +- `in-process`:这是一个特殊的方案,将使用 `InProcessChannelFactory` 来替代原有正常的 ChannelFactory。 并使用它连接到 [`InProcessServer`](../server/configuration#enabling-the-inprocessserver)。 例如:`in-process:foobar` +- *custom*: 您可以通过 Java 的 `ServiceLoader` 或从 Spring 的应用上下文中选择要自定义的 [`NameResolverProvider`](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/NameResolverProvider.html) ,并将其注册到 `NameResolverRegistry` 上. + +如果您没有定义地址,它将按照如下方式被猜测: + +- 首先它将尝试使用它的名称 (``) +- 如果您配置了默认方案,它将尝试下一个 (`:`) +- 然后它将使用 `NameResolver.Factory` 委托的默认方案(见上面的优先级) + +> **注意:** 斜杠的数量很重要! 还要确保不要连接到普通的 web / REST / 非 grpc 服务器(端口)。 + +[客户端安全性](security) 页面上解释了 `SSL` / `TLS` 和其他与安全相关的配置。 + +## 通过Beans 配置 + +虽然这个项目提供大多数功能作为配置选项,但有时会因为添加它的开销太高了,我们会选择没有添加它。 如果您觉得这是一项重要功能,请随意打开一项功能性 Pull Request。 + +如果您要更改应用程序,而不是通过属性进行更改,则可以使用该项目中现有存在的扩展点。 + +首先,大多数 bean 可以被自定义 bean 替换,您可以按照您想要的任何方式进行配置。 如果您不希望这么麻烦,可以使用 `GrpcChannelConfigurer` 和 `StubTransformer` 等类来配置 channels,stubs 和其他组件,它不会丢失这个项目所提供的任何功能。 + +### GrpcChannelConfigurer + +gRPC 客户端配置器允许您将自定义配置添加到 gRPC 的 `ManagedChannelBuilder` 。 + +````java +@Bean +public GrpcChannelConfigurer keepAliveClientConfigurer() { + return (channelBuilder, name) -> { + if (channelBuilder instanceof NettyChannelBuilder) { + ((NettyChannelBuilder) channelBuilder) + .keepAliveTime(30, TimeUnit.SECONDS) + .keepAliveTimeout(5, TimeUnit.SECONDS); + } + }; +} +```` + +> 注意,根据您的配置,在应用上下文中可能有不同类型的 `ManagedChannelBuilder` (例如`InProcessChannelFactory`)。 + +### StubTransformer + +StubTransformer 允许您在注入您的 Bean 之前修改`Stub`。 + +````java +@Bean +public StubTransformer call() { + return (name, stub) -> { + if ("serviceA".equals(name)) { + return stub.withWaitForReady(); + } else { + return stub; + } + }; +} +```` + +## 附加主题 + +- [入门指南](getting-started) +- *配置* +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/client/getting-started.md b/docs/zh-CN/client/getting-started.md new file mode 100644 index 000000000..788d766b5 --- /dev/null +++ b/docs/zh-CN/client/getting-started.md @@ -0,0 +1,144 @@ +# 入门指南 + +[<- 返回索引](../index) + +本节讨论如何让 Spring 连接到 gRPC 服务端并管理您的连接。 + +## 目录 + +- [项目创建](#project-setup) +- [依赖项](#dependencies) + - [接口项目](#interface-project) + - [服务端项目](#server-project) + - [客户端项目](#client-project) +- [使用 Stubs 连接服务端](#using-the-stubs-to-connect-to-the-server) + - [解释客户端组件](#explaining-the-client-components) + - [访问客户端](#accessing-the-client) + +## 附加主题 + +- *入门指南* +- [配置](configuration) +- [安全性](security) + +## 项目创建 + +在我们开始添加依赖关系之前,让我们项目的一些设置建议开始。 + +![project setup](/grpc-spring-boot-starter/assets/images/client-project-setup.svg) + +我们建议将您的项目分为2至3个不同的模块。 + +1. **interface 项目** 包含原始 protobuf 文件并生成 java model 和 service 类。 你可能会在不同的项目中会共享这个部分。 +2. **Server 项目** 包含项目的业务实现,并使用上面的 Interface 项目作为依赖项。 +3. **Client 项目**(可选,可能很多) 任何使用预生成的 stub 来访问服务器的客户端项目。 + +## 依赖项 + +### 接口项目 + +请参阅 [服务端入门指引](../server/getting-started#interface-project) 页面 + +### 服务端项目 + +请参阅 [服务端入门指引](../server/getting-started#server-project) 页面 + +### 客户端项目 + +#### Maven (客户端) + +````xml + + + net.devh + grpc-client-spring-boot-starter + + + + example + my-grpc-interface + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + +```` + +#### Gradle (客户端) + +````gradle +apply plugin: 'org.springframework.boot' + +dependencies { + compile('org.springframework.boot:spring-boot-starter') + compile('net.devh:grpc-client-spring-boot-starter') + compile('my-example:my-grpc-interface') +} + +buildscript { + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +```` + +## 使用 Stubs 连接服务端 + +本节假定您已经定义并生成了[Protobuf](../server/getting-started#creating-the-grpc-service-definitions)。 + +### 解释客户端组件 + +- [`Channel`](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/Channel.html): Channel 是单个服务端的连接池。 目标服务器可能是多个 gRPC 服务。 +- [`ManagedChannel`](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/ManagedChannel.html): ManagedChannel 是 Channel 的一种特殊变体,因为它允许对连接池进行管理操作,例如将其关闭。 +- [`ClientInterceptor`](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/ClientInterceptor.html): 在每个 `Channel` 处理之前拦截它们。 可以用于日志、监测、元数据处理和请求/响应的重写。 grpc-spring-boot-starter 将自动接收所有带有 [`@GrpcGlobalClientInterceptor`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/interceptor/GrpcGlobalClientInterceptor.html) 注解以及手动注册在[`GlobalClientInterceptorRegistry`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/interceptor/GlobalClientInterceptorRegistry.html) 上的客户拦截器。 +- [`CallCredentials`](https://javadoc.io/page/io.grpc/grpc-all/latest/io/grpc/CallCredentials.html): 管理身份验证的组件。 它可以用于存储凭据和会话令牌。 它还可以用来身份验证,并且使用返回的令牌(例如 OAuth )来授权实际请求。 除此之外,如果令牌过期并且重新发送请求,它可以续签令牌。 如果您的应用程序上下文中只存在一个 `CallCredentials` bean,那么 spring 将会自动将其附加到`Stub`( **非** `Channel` )。 [`CallCredentialsHelper`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/security/CallCredentialsHelper.html)工具类可以帮助您创建常用的 `CallCredentials` 类型和相关的`StubTransformer`。 +- [`StubTransformer`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/inject/StubTransformer.html): 所有客户端的 `Stub` 的注入之前应用的转换器。 +- [`@GrpcClient`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/inject/GrpcClient.html): 这个注解用在你需要注入客户端的字段或者 set 方法上。 支持 `Channel`和各种类型的 `Stub`。 请不要将 `@GrpcClient` 与 `@Autowireed` 或 `@Inject` 一起使用。 + +### 访问客户端 + +我们建议注入 (`@GrpcClient`) `Stub`,而不是纯粹的 `Channel`. + +> **注意:** 存在不同类型的 `Stub`。 并非所有的都支持所有请求类型 (流式调用)。 + +````java +import example.HelloReply; +import example.HelloRequest; +import example.MyServiceGrpc; + +import io.grpc.stub.StreamObserver; + +import net.devh.boot.grpc.server.service.GrpcService; + +@Service +public class FoobarService { + + @GrpcClient("myService") + private MyServiceBlockingStub myServiceStub; + + public String receiveGreeting(String name) { + HelloRequest request = HelloRequest.newBuilder() + .setName(name) + .build() + return myServiceStub.sayHello(request).getMessage(); + } + +} +```` + +## 附加主题 + +- *入门指南* +- [配置](configuration) +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/client/security.md b/docs/zh-CN/client/security.md new file mode 100644 index 000000000..bb7af1472 --- /dev/null +++ b/docs/zh-CN/client/security.md @@ -0,0 +1,115 @@ +# 客户端安全 + +[<- 返回索引](../index) + +此页面描述了您如何连接到 gRPC 服务器并进行身份验证。 + +## 目录 + +- [启用传输图层安全](#enable-transport-layer-security) + - [基础要求](#prerequisites) +- [禁用传输图层安全](#disable-transport-layer-security) + - [信任服务器](#trusting-a-server) +- [双向证书认证](#mutual-certificate-authentication) +- [身份验证](#authentication) + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- *安全性* + +## 启用传输图层安全 + +gRPC 默认使用 `TLS` 连接服务端,因此无需执行其他任何操作。 + +如果您想要检查您是否意外覆盖配置, 请检查给定的属性有这样配置: + +````properties +grpc.client..negotiationType=TLS +```` + +对于服务端的配置,请参考 [服务端安全](../server/security) 页面。 + +### 基础要求 + +如同往常一样,需要满足一些简单的前提条件: + +- 在您的 classpath 上有兼容的 `SSL `/`TLS` 实现 + - 包含 [grpc-netty-shaded](https://mvnrepository.com/artifact/io.grpc/grpc-netty-shaded) + - 对于[`grpc-netty`](https://mvnrepository.com/artifact/io.grpc/grpc-netty),还需要额外添加 [`nety-tcnative-boringssl-static`](https://mvnrepository.com/artifact/io.netty/netty-tcnative-boringssl-static) 依赖。 (请使用 [grpc-java的 Netty 安全部分](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty) 表中列出**完全相同** (兼容)的版本)。 + +## 禁用传输图层安全 + +> **警告:** 请勿在生产环境中这样做。 + +有时您没有可用的证书(例如在开发期间),因此您可能希望禁用传输层安全,您可以这样做: + +````properties +grpc.client.__name__.negotiationType=PLAINTEXT +```` + +下面的示例演示如何在测试中配置此属性: + +````java +@SpringBootTest(properties = "grpc.client.test.negotiationType=PLAINTEXT") +@SpringJUnitConfig(classes = TestConfig.class) +@DirtiesContext +public class PlaintextSetupTest { + + @GrpcClient("test") + private MyServiceBlockingStub myService; +```` + +### 信任服务端 + +如果您信任的证书不在常规信任存储区, 或者您想要限制您信任的 证书。您可以使用以下属性: + +````properties +grpc.client.__name__.security.trustCertCollection=file:trusted-server.crt.collection +```` + +如果您想知道这里支持哪些选项,请阅读 [Spring 的 Resource 文档](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resourceloader)。 + +如果您使用服务标识符,证书可能会出现问题,因为它对内部服务名称无效。 在这种情况下,您可以需要指定证书对哪个名字有效: + +````properties +grpc.client.__name__.security.authorityOverride=localhost +```` + +## 双向证书认证 + +在安全环境中,您可能必须使用客户端证书进行身份验证。 该证书通常由服务端提供,因此您需要为您的应用程序配置如下属性: + +````properties +grpc.client.__name__.security.clientAuthEnabled=true +grpc.client.__name__.security.certificateChain=file:certificates/client.crt +grpc.client.__name__.security.privateKey=file:certificates/client.key +```` + +## 身份验证 + +除了双向证书认证外,还有其他几种认证方式,如 `BasicAuth`。 + +grpc-spring-boot-starter 除了一些帮助方法,同时提供了 BasicAuth 的实现。 然而,这里有很多库可以为 [`CallCredentials`](https://grpc.github.io/grpc-java/javadoc/io/grpc/CallCredentials.html)提供实现功能。 `CallCredentials` 是一个可扩展的组件,因为它们可以使用(第三方)服务队请求进行身份验证,并且可以自己管理和更新会话 token。 + +如果您的应用程序上下文中只有一个`CallCredentials`,我们将自动为您创建一个 `StubTransformer`,并配置到所有的 `Stub`上。 如果您想为每个 Stub 配置不同的凭据,那么您可以使用 [`CallCredentialsHelper`](https://javadoc.io/page/net.devh/grpc-client-spring-boot-autoconfigure/latest/net/devh/boot/grpc/client/security/CallCredentialsHelper.html) 中提供的帮助方法。 + +> **注意:** `StubTransformer` 只能自动配置注入的 `Stub`。 它们无法修改原始的 `Channel`。 + +您还可以配置 `CallCredentials`(例如用于用户的凭据): + +````java +MyServiceBlockingStub myServiceForUser = myService.withCallCredentials(userCredentials); +return myServiceForUser.send(request); +```` + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- *安全性* + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/contributions.md b/docs/zh-CN/contributions.md new file mode 100644 index 000000000..89c58d8ac --- /dev/null +++ b/docs/zh-CN/contributions.md @@ -0,0 +1,13 @@ +# 参与贡献 + +[<- 返回索引](index) + +我们始终欢迎您对项目作出任何贡献。 + +详情请参阅我们的[贡献准则](https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/CONTRIBUTING.md)。 + +**感谢我们所有的[贡献者](https://github.com/yidongnan/grpc-spring-boot-starter/graphs/contributors)!** + +---------- + +[<- 返回索引](index) diff --git a/docs/zh-CN/examples.md b/docs/zh-CN/examples.md new file mode 100644 index 000000000..99c69d10c --- /dev/null +++ b/docs/zh-CN/examples.md @@ -0,0 +1,32 @@ +# 示例 + +示例项目演示如何使用这些项目。 + +这些项目可以作为您自己的项目的模板。 我们使用它们来验证这个库在不同的环境中运行,我们不会在不受注意的情况下改变它的行为。 + +> **注意:** 如果您对这些项目有疑问,或者想要其他的示例,随时可以提出一个 [issue](https://github.com/yidongnan/grpc-spring-boot-starter/issues)。 + +## 本地示例 + +最简单的设置,使用本地服务端和一个或多个客户端 + +- [服务端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/local-grpc-server) +- [客户端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/local-grpc-client) +- [说明](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples#local-mode) + +## Cloud 示例 + +使用 eureka 服务发现的 Cloud 环境。 + +- [Eureka 服务](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/cloud-eureka-server) +- [服务端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/cloud-grpc-server) +- [客户端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/cloud-grpc-client) +- [说明](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples#cloud-mode) + +## 基础认证示例 + +演示了 grpc 跟 spring security 的设置。 为了简单起见,此设置使用 Basic 身份验证,但也可以为其使用其他身份验证机制。 + +- [服务端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/security-grpc-server) +- [客户端](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples/security-grpc-client) +- [说明](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/examples#with-basic-auth-security) diff --git a/docs/zh-CN/index.md b/docs/zh-CN/index.md new file mode 100644 index 000000000..825c04240 --- /dev/null +++ b/docs/zh-CN/index.md @@ -0,0 +1,24 @@ +# gRPC-Spring-Boot-Starter 文档 + +gRPC-Spring-Boot-Starter 将 [Google的开源高性能RPC框架](https://grpc.io) 与 [Spring Boot 进行整合](https://spring.io/projects/spring-boot) 该项目简化了 gRPC 服务端 / 客户端的设置,只需要为项目添加了一个依赖项,并在服务实现类 / 客户 (stub) 字段上添加了一个的注解。 这个项目提供的特性仍然能复用您使用 gRPC 的经验,并且允许您执行任何自定义操作。 + +## 目录 + +- 服务端 + - [入门指南](server/getting-started) + - [配置](server/configuration) + - [上下文数据 / Bean 的作用域](server/contextual-data) + - [测试服务](server/testing) + - [安全性](server/security) +- 客户端 + - [入门指南](client/getting-started) + - [配置](client/configuration) + - [安全性](client/security) +- 其他设置 +- [疑难解答](trouble-shooting) +- [示例项目](examples) +- [版本概述](versions) +- [支持 Spring Boot Actuator / Metrics](actuator) +- [支持 Brave-Tracing / Spring Cloud Sleuth](brave) +- [基准测试](benchmarking) +- [参与贡献](contributions) diff --git a/docs/zh-CN/server/configuration.md b/docs/zh-CN/server/configuration.md new file mode 100644 index 000000000..6d2a9e62e --- /dev/null +++ b/docs/zh-CN/server/configuration.md @@ -0,0 +1,108 @@ +# 配置 + +[<- 返回索引](../index) + +本节描述您如何配置您的 grpc-spring-boot-starter 应用程序。 + +## 目录 + +- [通过属性配置](#configuration-via-properties) + - [更改服务端端口](#changing-the-server-port) + - [启用 InProcessServer](#enabling-the-inprocessserver) +- [通过 Bean 配置](#configuration-via-beans) + - [GrpcServerConfigurer](#grpcserverconfigurer) + +## 附加主题 + +- [入门指南](getting-started) +- *配置* +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- [安全性](security) + +## 通过属性配置 + +grpc-spring-boot-starter 可以通过 Spring 的 `@ConfigurationProperties` 机制来进行 [配置](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html)。 + +您可以在这里找到所有内置配置属性: + +- [`GrpcServerProperties`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/config/GrpcServerProperties.html) +- [`GrpcServerProperties.Security`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/config/GrpcServerProperties.Security.html) + +如果你希望阅读源代码,你可以查阅 [这里](https://github.com/yidongnan/grpc-spring-boot-starter/blob/master/grpc-server-spring-boot-autoconfigure/src/main/java/net/devh/boot/grpc/server/config/GrpcServerProperties.java#L50)。 + +Channels 的属性都是以 `grpc.server..` 或 `grpc.client..security.` 为前缀。 + +### 更改服务端端口 + +如果您想要将 gRPC 服务端端口从默认值(`9090`) 更改为其他的端口,您可以这样做: + +````properties +grpc.server.port=80 +```` + +将端口设置为 `0` 以使用空闲的随机端口。 此功能用于部署了服务发现的服务和并行测试的场景。 + +> 请确保您不会与其他应用程序或其他端点发生冲突,如`spring-web`。 + +[服务端安全性](security) 页面上解释了 `SSL` / `TLS` 和其他与安全相关的配置。 + +### 启用 InProcessServer + +有时,您可能想要在自己的应用程序中调用自己的 grpc 服务。 您可以像调用其他任何 gRPC 服务端一样,您需要使用 grpc 的 `InProcessServer` 来节省网络间开销。 + +您可以使用以下属性将其打开: + +````properties +grpc.server.in-process-name= +# Optional: Turn off the external grpc-server +#grpc.server.port=-1 +```` + +这允许客户端在同一应用程序内使用以下配置连接到服务器: + +````properties +grpc.client.inProcess.address=in-process: +```` + +这对测试特别有用,因为他们不需要打开特定的端口,因此可以并发运行(在构建 服务器上)。 + +## 通过Beans 配置 + +虽然这个项目提供大多数功能作为配置选项,但有时会因为添加它的开销太高了,我们会选择没有添加它。 如果您觉得这是一项重要功能,请随意打开一项功能性 Pull Request。 + +如果您要更改应用程序,而不是通过属性进行更改,则可以使用该项目中现有存在的扩展点。 + +首先,大多数 bean 可以被自定义 bean 替换,您可以按照您想要的任何方式进行配置。 如果您不希望这么麻烦,可以使用 `GrpcServerConfigurer` 来配置你的服务端和其他组件,它不会丢失这个项目所提供的任何功能。 + +### GrpcServerConfigurer + +gRPC 服务端配置器允许您将自定义配置添加到 gRPC 的 `ServerBuilder` 。 + +````java +@Bean +public GrpcServerConfigurer keepAliveServerConfigurer() { + return serverBuilder -> { + if (serverBuilder instanceof NettyServerBuilder) { + ((NettyServerBuilder) serverBuilder) + .keepAliveTime(30, TimeUnit.SECONDS) + .keepAliveTimeout(5, TimeUnit.SECONDS) + .permitKeepAliveWithoutCalls(true); + } + }; +} +```` + +> 注意,根据您的配置,在应用程序上下文中可能有不同类型的 `ServerBuilder` (例如`InProcessServerBuilder`)。 + +## 附加主题 + +- [入门指南](getting-started) +- *配置* +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/server/contextual-data.md b/docs/zh-CN/server/contextual-data.md new file mode 100644 index 000000000..6d8c14924 --- /dev/null +++ b/docs/zh-CN/server/contextual-data.md @@ -0,0 +1,64 @@ +# 上下文数据 / Bean 的作用域 + +[<- 返回索引](../index) + +本节描述您如何保存请求上下文数据 / 每个请求的数据。 + +## 目录 + +- [警告语](#a-word-of-warning) +- [grpcRequest 作用域](#grpcrequest-scope) + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- *上下文数据 / Bean 的作用域* +- [测试服务](testing) +- [安全性](security) + +## 警告语 + +在 grpc-java 中,消息发送 / 请求处理中的不同阶段可能在不同的线程中运行。 流式调用中也是这样。 避免在您的`ServerIntercetor` 和 grpc 服务方法实现中(在整个 gRPC 上下文中)使用 `ThreadLocal`。 When it comes down to it, the preparation phase, every single message and the completion phase might run in different threads. 如果您想要在会话中存储数据,请使用 grpc 的 `Context` 或 `grpcRequest` 作用域。 + +## grpcRequest 作用域 + +该项目添加了一个`grpcRequest`,该功能类似于 Spring Web 的`request` 作用域。 它只适用于单个的请求。 + +首先需要用 `@Scope` 注解定义 Bean: + +````java +@Bean +@Scope(scopeName = "grpcRequest", proxyMode = ScopedProxyMode.TARGET_CLASS) +//@Scope(scopeName = GrpcRequestScope.GRPC_REQUEST_SCOPE_NAME, proxyMode = ScopedProxyMode.TARGET_CLASS) +ScopedBean myScopedBean() { + return new ScopedBean(); +} +```` + +> `proxyMode = TARGET_CLASS` 是必须的,除非在另一个 `grpcRequest` 作用域中配置了它. 请注意,这个`proxyMode` 不适用于 final 修饰的类和方法。 + +之后,您就可以像以前那样使用 Bean: + +````java +@Autowired +private ScopedBean myScopedBean; + +@Override +public void grpcMethod(Request request, StreamObserver responseObserver) { + responseObserver.onNext(myScopedBean.magic(request)); + responseObserver.onCompleted(); +} +```` + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- *上下文数据 / Bean 的作用域* +- [测试服务](testing) +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/server/getting-started.md b/docs/zh-CN/server/getting-started.md new file mode 100644 index 000000000..f6640fb2a --- /dev/null +++ b/docs/zh-CN/server/getting-started.md @@ -0,0 +1,302 @@ +# 入门指南 + +[<- 返回索引](../index) + +本节描述了将您的应用程序接入 grpc-spring-boot-starter 项目的必要步骤。 + +## 目录 + +- [项目创建](#project-setup) +- [依赖项](#dependencies) + - [接口项目](#interface-project) + - [服务端项目](#server-project) + - [客户端项目](#client-project) +- [创建 gRPC 服务定义](#creating-the-grpc-service-definitions) +- [实现服务逻辑](#implementing-the-service) + +## 附加主题 + +- *入门指南* +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- [安全性](security) + +## 项目创建 + +在我们开始添加依赖关系之前,让我们项目的一些设置建议开始。 + +![project setup](/grpc-spring-boot-starter/assets/images/server-project-setup.svg) + +我们建议将您的项目分为2至3个不同的模块。 + +1. **interface 项目** 包含原始 protobuf 文件并生成 java model 和 service 类。 你可能会在不同的项目中会共享这个部分。 +2. **Server 项目** 包含项目的业务实现,并使用上面的 Interface 项目作为依赖项。 +3. **Client 项目**(可选,可能很多) 任何使用预生成的 stub 来访问服务器的客户端项目。 + +## 依赖项 + +### 接口项目 + +#### Maven (Interface) + +````xml + + + io.grpc + grpc-stub + + + io.grpc + grpc-protobuf + + + + javax.annotation + javax.annotation-api + + + + + + + kr.motd.maven + os-maven-plugin + + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier} + + + + + compile + compile-custom + + + + + + +```` + +#### Gradle (Interface) + +````gradle +apply plugin: 'com.google.protobuf' + +dependencies { + compile "io.grpc:grpc-protobuf" + compile "io.grpc:grpc-stub" +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:${protobufVersion}" + } + generatedFilesBaseDir = "$projectDir/src/generated" + clean { + delete generatedFilesBaseDir + } + plugins { + grpc { + artifact = "io.grpc:protoc-gen-grpc-java" + } + } + generateProtoTasks { + all()*.plugins { + grpc {} + } + } +} + +buildscript { + dependencies { + classpath "com.google.protobuf:protobuf-gradle-plugin:${protobufGradlePluginVersion}" + } +} + +// Optional +eclipse { + classpath { + file.beforeMerged { cp -> + def generatedGrpcFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/grpc', null); + generatedGrpcFolder.entryAttributes['ignore_optional_problems'] = 'true'; + cp.entries.add( generatedGrpcFolder ); + def generatedJavaFolder = new org.gradle.plugins.ide.eclipse.model.SourceFolder('src/generated/main/java', null); + generatedJavaFolder.entryAttributes['ignore_optional_problems'] = 'true'; + cp.entries.add( generatedJavaFolder ); + } + } +} + +// Optional +idea { + module { + sourceDirs += file("src/generated/main/java") + sourceDirs += file("src/generated/main/grpc") + generatedSourceDirs += file("src/generated/main/java") + generatedSourceDirs += file("src/generated/main/grpc") + } +} +```` + +### 服务端项目 + +#### Maven (Server) + +````xml + + + net.devh + grpc-server-spring-boot-starter + + + + example + my-grpc-interface + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + +```` + +#### Gradle (Server) + +````gradle +apply plugin: 'org.springframework.boot' + +dependencies { + compile('org.springframework.boot:spring-boot-starter') + compile('net.devh:grpc-server-spring-boot-starter') + compile('my-example:my-grpc-interface') +} + +buildscript { + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + } +} + +```` + +### 客户端项目 + +请参阅 [客户端入门指引](../client/getting-started#client-project) 页面 + +## 创建 gRPC 服务定义 + +将您的 protobuf 定义/`.proto`文件放入`src/main/proto`。 有关编写 protobuf 文件的信息,请参阅官方的 [protobuf 文档](https://developers.google.com/protocol-buffers/docs/proto3)。 + +您的 `.proto` 文件跟如下的示例类似: + +````proto +syntax = "proto3"; + +package net.devh.boot.grpc.example; + +option java_multiple_files = true; +option java_package = "net.devh.boot.grpc.examples.lib"; +option java_outer_classname = "HelloWorldProto"; + +// The greeting service definition. +service MyService { + // Sends a greeting + rpc SayHello (HelloRequest) returns (HelloReply) { + } +} + +// The request message containing the user's name. +message HelloRequest { + string name = 1; +} + +// The response message containing the greetings +message HelloReply { + string message = 1; +} +```` + +配置 maven/gradle protobuf 插件使其调用 [`protoc`](https://mvnrepository.com/artifact/com.google.protobuf/protoc) 编译器,并使用 [`protoc-gen-grpc-java`](https://mvnrepository.com/artifact/io.grpc/protoc-gen-grpc-java) 插件并生成数据类、grpc 服务类 `ImplicBase`s 和 `Stub`。 请注意,其他插件,如 [reactive-grpc](https://github.com/salesforce/reactive-grpc) 可能会生成其他额外 / 替代类。 然而,它们也可以同样的方式使用。 + +- `ImplicBase`类包含基本逻辑,映射虚拟实现到grpc 服务方法。 在 [实现服务逻辑](#implementing-the-service) 章节中有更多关于这个问题的信息。 +- `Stub`类是完整的客户端实现。 更多信息可以参考 [客户端指引](../client/getting-started) 页面。 + +## 实现服务逻辑 + +`protoc-gen-grpc-java` 插件为你的每个 grpc 服务生成一个类。 例如:`MyServiceGrpc` 的 `MyService` 是 proto 文件中的 grpc 服务名称。 这个类 包含您需要扩展的客户端 stub 和服务端的 `ImplicBase`。 + +在这之后,你还有四个步骤: + +1. 请确保您的 `MyServiceImp` 实现了 `MyServiceGrpc.MyServiceImpBase` +2. 将 `@GrpcService` 注解添加到您的 `MyServiceImp` 类上 +3. 请确保 `MyServiceImplic` 已添加到您的应用程序上下文中。 + - 通过在您的 `@Configuration` 类中创建 `@Bean` + - 或者将其放置在 spring 的自动检测到路径中(例如在您`Main`类的相同或子包中) +4. 实现 grpc 服务方法。 + +您的 grpc 服务类将会看起来与下面的例子有些相似: + +````java +import example.HelloReply; +import example.HelloRequest; +import example.MyServiceGrpc; + +import io.grpc.stub.StreamObserver; + +import net.devh.boot.grpc.server.service.GrpcService; + +@GrpcService +public class MyServiceImpl extends MyServiceGrpc.MyServiceImplBase { + + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + HelloReply reply = HelloReply.newBuilder() + .setMessage("Hello ==> " + request.getName()) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + +} +```` + +> **注意**: 理论上来说,不需要拓展 `ImplBase` 而是自己实现 `BindableService`。 但是,这样做可能会导致绕过 Spring Security 的检查。 + +上面就是你接入过程中所有需要做的。 现在您可以启动您的 spring-boot 应用程序并开始向您的 grpc 服务发送请求。 + +默认情况下,grpc-server 将使用 `PLAINTEXT` 模式在端口 `9090` 中启动。 + +您可以通过运行 [grpcurl](https://github.com/fullstorydev/grpcurl) 命令来测试您的应用程序是否正常运行: + +````sh +grpcurl --plaintext localhost:9090 list +grpcurl --plaintext localhost:9090 list net.devh.boot.grpc.example.MyService +grpcurl --plaintext -d '{"name": "test"}' localhost:9090 net.devh.boot.grpc.example.MyService/sayHello +```` + +## 附加主题 + +- *入门指南* +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/server/security.md b/docs/zh-CN/server/security.md new file mode 100644 index 000000000..3cb682534 --- /dev/null +++ b/docs/zh-CN/server/security.md @@ -0,0 +1,255 @@ +# 服务端安全 + +[<- 返回索引](../index) + +本节描述如何使用传输层安全和身份验证来保护您的应用程序。 我们强烈建议至少启用运输层安全。 + +## 目录 + +- [启用传输图层安全](#enable-transport-layer-security) + - [基础要求](#prerequisites) + - [服务端配置](#configuring-the-server) +- [双向证书认证](#mutual-certificate-authentication) +- [认证和授权](#authentication-and-authorization) + - [配置身份验证](#configure-authentication) + - [配置授权](#configure-authorization) + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- *安全性* + +## 启用传输图层安全 + +您可以使用 Spring 的配置机制来配置传输层安全。 与之相关的非安全性配置选项参见 [配置](configuration) 页面。 + +如果你的服务在 TLS 的反向代理后面,你可能不需要设置 `TLS/`。 如果您不熟悉安全,请咨询安全专家。 请不要忘记检查是否存在安全问题。 ^^ + +> **注意: ** 请参考 [官方文档](https://github.com/grpc/grpc-java/blob/master/SECURITY.md) 以获取更多信息! + +### 基础要求 + +- 在您的 classpath 上有兼容的 `SSL `/`TLS` 实现 + - 包含 [grpc-netty-shaded](https://mvnrepository.com/artifact/io.grpc/grpc-netty-shaded) + - 对于[`grpc-netty`](https://mvnrepository.com/artifact/io.grpc/grpc-netty),还需要额外添加 [`nety-tcnative-boringssl-static`](https://mvnrepository.com/artifact/io.netty/netty-tcnative-boringssl-static) 依赖。 (请使用 [grpc-java的 Netty 安全部分](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty) 表中列出**完全相同** (兼容)的版本)。 +- 带有私钥的证书 + +#### 生成一个自签名的证书 + +如果您没有证书(例如内部测试服务器),您可以使用`openssl`生成证书: + +````sh +openssl req -x509 -nodes -subj "//CN=localhost" -newkey rsa:4096 -sha256 -keyout server.key -out server.crt -days 3650 +```` + +请注意,如果没有额外配置,这些证书不受任何应用程序的信任。 我们建议您使用受全球CA或您公司CA信任的证书。 + +### 服务端配置 + +为了允许 grpc-server 使用 `TLS ` 您必须使用以下选项来配置它: + +````properties +grpc.server.security.enabled=true +grpc.server.security.certificateChain=file:certificates/server.crt +grpc.server.security.privateKey=file:certificates/server.key +#grpc.server.security.privateKeyPassword=MyStrongPassword +```` + +如果您想知道这里支持哪些选项,请阅读 [Spring 的 Resource 文档](https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#resources-resourceloader)。 + +对于客户端的配置,请参考 [客户端安全](../client/security) 页面。 + +## 双向证书认证 + +如果您想要确保只有可信的客户端能连接到服务端,您可以启用共同证书进行身份验证。 这允许或强制客户端使用`x509`证书验证自己。 + +要启用相互身份验证,只需将以下属性添加到您的配置: + +````properties +grpc.server.security.trustCertCollection=file:certificates/trusted-clients.crt.collection +grpc.server.security.clientAuth=REQUIRE +```` + +您可以通过简单地绑定客户端证书创建 `trusted-clients.crt.collection` 文件: + +````sh +cat client*.crt > trusted-clients.crt.collection +```` + +`客户端认证`模式定义了服务端的行为: + +- `REQUIRE` 客户端证书必须通过认证。 +- `OPTIONAL` 对客户端的证书进行身份验证,但不会强制这么做。 + +如果您只想保护一些重要的服务或方法,您可以使用 `OPTIONAL`。 + +尤其是在后一种情况下,适当的配置身份认证尤为重要。 + +## 认证和授权 + +`grpc-spring-boot-starter` 原生支持 `spring-security` , 因此您可以使用众所周知的一些注解来保护您的应用程序。 + +![server-request-security](/grpc-spring-boot-starter/assets/images/server-security.svg) + +### 配置身份验证 + +为了支持来自 grpc 客户端的身份验证,您必须定义客户端如何被允许进行身份验证。 您可以通过自定义 [`GrpcAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/GrpcAuthenticationReader.html) 来实现。 + +grpc-spring-boot-starter 提供了一些内置实现: + +- [`AnonymousAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/AnonymousAuthenticationReader.html) Spring 的匿名身份认证。 +- [`BasicGrpcAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/BasicGrpcAuthenticationReader.html) 基础身份认证。 +- [`BearerAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/BearerAuthenticationReader.html) OAuth 以及类似协议的身份认证。 +- [`SSLContextGrpcAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/SSLContextGrpcAuthenticationReader.html) 基于证书的身份认证。 +- [`CompositeGrpcAuthenticationReader`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/authentication/CompositeGrpcAuthenticationReader.html) 依次尝试多个身份验证器。 + +您 Bean 的定义将跟下面这个示例类似: + +````java +@Bean +public GrpcAuthenticationReader grpcAuthenticationReader() { + return new BasicGrpcAuthenticationReader(); +} +```` + +如果您想要强制用户使用 `CompositegrpcAuthenticationReader` ,而其中的一个`GrpcAuthenticationReader` 抛出一个`AuthenticationException`。 那么身份验证将失败,并且停止请求的处理。 如果 `GrpcAuthenticationReader` 返回 null,用户任然是未经验证。 如果身份验证器能够提取凭证/认证,则会交给 Spring 的`AuthenticationManager` 来管理。 由它来决定是否发送有效凭据,并且是否可以继续进行操作。 + +#### 设置示例 + +以下部分包含不同身份认证的配置示例: + +> 注意:不必在`CompositegrpcAuthenticationReader` 中包装阅读器,你可以直接添加多种机制。 + +##### 基本认证 + +````java +@Bean +AuthenticationManager authenticationManager() { + final List providers = new ArrayList<>(); + providers.add(...); // Possibly DaoAuthenticationProvider + return new ProviderManager(providers); +} + +@Bean +GrpcAuthenticationReader authenticationReader() { + final List readers = new ArrayList<>(); + readers.add(new BasicGrpcAuthenticationReader()); + return new CompositeGrpcAuthenticationReader(readers); +} +```` + +##### Bearer 认证 (OAuth2/OpenID-Connect) + +````java +@Bean +AuthenticationManager authenticationManager() { + final List providers = new ArrayList<>(); + providers.add(...); // Possibly JwtAuthenticationProvider + return new ProviderManager(providers); +} + +@Bean +GrpcAuthenticationReader authenticationReader() { + final List readers = new ArrayList<>(); + // The actual token class is dependent on your spring-security library (OAuth2/JWT/...) + readers.add(new BearerAuthenticationReader(accessToken -> new BearerTokenAuthenticationToken(accessToken))); + return new CompositeGrpcAuthenticationReader(readers); +} +```` + +您也可能想要自定义 *GrantedAuthoritiesConverter* 来映射持有者 token 到权限 / 角色到 Spring Security 的 <>GrantedAuthority 中。 + +##### 证书认证 + +````java +@Bean +AuthenticationManager authenticationManager() { + final List providers = new ArrayList<>(); + providers.add(new X509CertificateAuthenticationProvider(userDetailsService())); + return new ProviderManager(providers); +} + +@Bean +GrpcAuthenticationReader authenticationReader() { + final List readers = new ArrayList<>(); + readers.add(new SSLContextGrpcAuthenticationReader()); + return new CompositeGrpcAuthenticationReader(readers); +} +```` + +另见[双向证书认证](#mutual-certificate-authentication)。 + +### 配置授权 + +这个步骤非常重要,因为它实际保护您的应用程序免受不必要的访问。 您可以通过两种方式保护您的 grpc 服务端。 + +#### gRPC 安全检查 + +保护应用程序安全的一种方式是将 [`GrpcSecurityMetadataSource`](https://javadoc.io/page/net.devh/grpc-server-spring-boot-autoconfigure/latest/net/devh/boot/grpc/server/security/check/GrpcSecurityMetadataSource.html) bean 添加到您的应用商家文中。 它允许您在每个 grpc 方法级别返回安全条件。 + +一个示例 bean 定义 (使用硬代码规则) 可能如下所示: + +````java +import net.devh.boot.grpc.server.security.check.AccessPredicate; +import net.devh.boot.grpc.server.security.check.ManualGrpcSecurityMetadataSource; + +@Bean +GrpcSecurityMetadataSource grpcSecurityMetadataSource() { + final ManualGrpcSecurityMetadataSource source = new ManualGrpcSecurityMetadataSource(); + source.set(MyServiceGrpc.getMethodA(), AccessPredicate.authenticated()); + source.set(MyServiceGrpc.getMethodB(), AccessPredicate.hasRole("ROLE_USER")); + source.set(MyServiceGrpc.getMethodC(), AccessPredicate.hasAllRole("ROLE_FOO", "ROLE_BAR")); + source.set(MyServiceGrpc.getMethodD(), auth -> "admin".equals(auth.getName())); + source.setDefault(AccessPredicate.denyAll()); + return source; +} + +@Bean +AccessDecisionManager accessDecisionManager() { + final List> voters = new ArrayList<>(); + voters.add(new AccessPredicateVoter()); + return new UnanimousBased(voters); +} +```` + +您必须配置 `AccessDecisionManager` 否则它不知道如何处理`AccessPredicate`。 + +此方法的好处是您能够将配置移动到外部文件或数据库。 但是你必须自己实现。 + +#### Spring 注解安全性检查 + +当然,也可以仅仅使用 Spring Security 的注解。 对于这种情况,您必须将以下注解添加到您的某个 `@Configuration` 类中: + +````java +@EnableGlobalMethodSecurity(___Enabled = true, proxyTargetClass = true) +```` + +> 请注意 `proxyTargetClass = true` 是必需的! 如果你忘记添加它,你会得到很多 `UNimpleneted` 的响应。 然而,您添加它将收到一个警告,`MyServiceImplic#bindService()` 方法是 final 修饰的。 **不要** 试图取消这些 final 修饰的方法,这将导致安全被绕过。 + +然后您可以简单地在您的 grpc 方法上加注解: + +````java +@Override +@Secured("ROLE_ADMIN") +// MyServiceGrpc.methodX +public void methodX(Request request, StreamObserver responseObserver) { + [...] +} +```` + +> 这个库假定你扩展 `ImplicBase` (由 grpc生成)来实现服务。 不这样做可能会导致绕过 Spring Security 的安全配置。 + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- [测试服务](testing) +- *安全性* + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/server/testing.md b/docs/zh-CN/server/testing.md new file mode 100644 index 000000000..65388f2fa --- /dev/null +++ b/docs/zh-CN/server/testing.md @@ -0,0 +1,283 @@ +# 测试服务 + +[<- 返回索引](../index) + +本节介绍如何为您的 grpc-service 编写测试用例。 + +## 目录 + +- [前言](#introductory-words) +- [测试服务](#the-service-to-test) +- [有用的依赖项](#useful-dependencies) +- [单元测试](#unit-tests) + - [独立测试](#standalone-tests) + - [基于Spring的测试](#spring-based-tests) +- [集成测试](#integration-tests) + +## 附加主题 + +- [入门指南](getting-started) +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- *测试服务* +- [安全性](security) + +## 前言 + +我们都知道测试对我们的应用程序是多么重要,所以我只会在这里向大家介绍几个链接: + +- [Testing Spring](https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html) +- [Testing with JUnit](https://junit.org/junit5/docs/current/user-guide/#writing-tests) +- [grpc-spring-boot-starter's Tests](https://github.com/yidongnan/grpc-spring-boot-starter/tree/master/tests/src/test/java/net/devh/boot/grpc/test) + +通常有两种方法来测试您的 grpc 服务: + +- [直接测试](#unit-tests) +- [通过 grpc 测试](#integration-tests) + +## 测试服务 + +让我们假设,我们希望测试以下服务: + +````java +@GrpcService +public class MyServiceImpl extends MyServiceGrpc.MyServiceImplBase { + + private OtherDependency foobar; + + @Autowired + public void setFoobar(OtherDependency foobar) { + this.foobar = foobar; + } + + @Override + public void sayHello(HelloRequest request, StreamObserver responseObserver) { + HelloReply response = HelloReply.newBuilder() + .setMessage("Hello ==> " + request.getName()) + .setCounter(foobar.getCount()) + .build(); + responseObserver.onNext(response); + responseObserver.onComplete(); + } + +} +```` + +## 有用的依赖项 + +在您开始编写自己的测试框架之前,您可能想要使用以下库来使您的工作更加简单。 + +对于Maven来说,添加以下依赖: + +````xml +<!-- JUnit-elotelFramework --> + + org.junit。 upiter + junit-jupiter-api + test + + + org。 unit.jupiter + junit-jupiter-engine + test + +<- Grpc-extract Support --> + + io. rpc + grpc-testing + test + +<! - Spring-Extract Support (Optional) --> + + org。 pringframework.boot + spring-boot-start-test + test + +```` + +Gradle 使用: + +````groovy +// JUnit-Test-Framework +testImplementation("org.junit.jupiter:junit-jupiter-api") +testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") +// Grpc-Test-Support +testImplementation("io.grpc:grpc-testing") +// Spring-Test-Support (Optional) +testImplementation("org.springframework.boot:spring-boot-starter-test") +```` + +## 单元测试 + +在直接测试中,我们直接在 grpc-service bean/实例上调用方法。 + +> 如果您自己创建的 grpc-service 实例, 请确保您先处理所需的依赖关系。 如果您使用Spring,它会处理您的依赖关系,但作为代价,您必须配置Spring。 + +### 独立测试 + +独立测试对外部库没有任何依赖关系(事实上你甚至不需要这个项目)。 然而,没有外部依赖关系并不总能使您的生活更加容易, 您可能需要复制其他库来执行您的行为。 使用 [Mockito](https://site.mockito.org) 这样的模拟库会简化你的流程,因为它限制依赖树的深度。 + +````java +public class MyServiceTest { + + private MyServiceImpl myService; + + @BeforeEach + public void setup() { + myService = new MyServiceImpl(); + OtherDependency foobar = ...; // mock(OtherDependency.class) + myService.setFoobar(foobar); + } + + @Test + void testSayHellpo() { + HelloRequest request = HelloRequest.newBuilder() + .setName("Test") + .build(); + StreamRecorder responseObserver = StreamRecorder.create(); + myService.sayHello(request, responseObserver); + if (!responseObserver.awaitCompletion(5, TimeUnit.SECONDS); + fail("The call did not terminate in time"); + } + assertNull(responseObserver.getError()); + List results = responseObserver.getValues().size(); + assertEquals(1, results.size()); + HelloReply response = results.get(0); + assertEquals(HelloReply.newBuilder() + .setMessage("Hello ==> Test") + .setCounter(1337) + .build(), response); + } + +} +```` + +### 基于Spring的测试 + +如果您使用Spring来管理您自己的依赖关系,您实际上正在进入集成测试领域。 请确保您不要启动整个应用程序,而只提供所需的依赖关系为 (模拟) 的 Bean 类。 + +> **注意:** 在测试期间,Spring 不会自动配置所有必须的 Bean。 您必须在有`@Configuration` 注解的类中手动创建它们。 + +````java +@SpringBootTest +@SpringJUnitConfig(classes = { MyServiceUnitTestConfiguration.class }) +// Spring doesn't start without a config (might be empty) +// Don't use @EnableAutoConfiguration in this scenario +public class MyServiceTest { + + @Autowired + private MyServiceImpl myService; + + @Test + void testSayHellpo() { + HelloRequest request = HelloRequest.newBuilder() + .setName("Test") + .build(); + StreamRecorder responseObserver = StreamRecorder.create(); + myService.sayHello(request, responseObserver); + if (!responseObserver.awaitCompletion(5, TimeUnit.SECONDS); + fail("The call did not terminate in time"); + } + assertNull(responseObserver.getError()); + List results = responseObserver.getValues().size(); + assertEquals(1, results.size()); + HelloReply response = results.get(0); + assertEquals(HelloReply.newBuilder() + .setMessage("Hello ==> Test") + .setCounter(1337) + .build(), response); + } + +} +```` + +和所需的配置类: + +````java +@Configuration +public class MyServiceUnitTestConfiguration { + + @Bean + OtherDependency foobar() { + // return mock(OtherDependency.class); + } + + @Bean + MyServiceImpl myService() { + return new MyServiceImpl(); + } + +} +```` + +## 集成测试 + +然而,您有时需要测试整个调用栈。 例如,如果认证发挥了作用。 但在这种情况下,建议限制您的测试范围,以避免像 空数据库这样可能的外部影响。 + +在这一点上,不使用 Spring 测试您的 Spring 应用程序是毫无意义的。 + +> **注意:** 在测试期间,Spring 不会自动配置所有必须的 Bean。 您必须在有 `@Configuration` 注解修饰的类中手动创建他们,或显式的包含相关的自动配置类。 + +````java +@SpringBootTest(properties = { + "grpc.server.inProcessName=test", // Enable inProcess server + "grpc.server.port=-1", // Disable external server + "grpc.client.inProcess.address=in-process:test" // Configure the client to connect to the inProcess server + }) +@SpringJUnitConfig(classes = { MyServiceIntegrationTestConfiguration.class }) +// Spring doesn't start without a config (might be empty) +@DirtiesContext // Ensures that the grpc-server is properly shutdown after each test + // Avoids "port already in use" during tests +public class MyServiceTest { + + @GrpcClient("inProcess") + private MyServiceBlockingStub myService; + + @Test + @DirtiesContext + public void testSayHello() { + HelloRequest request = HelloRequest.newBuilder() + .setName("test") + .build(); + HelloReply response = myService.sayHello(request); + assertNotNull(response); + assertEquals("Hello ==> Test", response.getMessage()) + } + +} +```` + +所需的配置看起来像这样: + +````java +@Configuration +@ImportAutoConfiguration({ + GrpcServerAutoConfiguration.class, // Create required server beans + GrpcServerFactoryAutoConfiguration.class, // Select server implementation + GrpcClientAutoConfiguration.class}) // Support @GrpcClient annotation +public class MyServiceIntegrationTestConfiguration { + + @Bean + OtherDependency foobar() { + return ...; // mock(OtherDependency.class); + } + + @Bean + MyServiceImpl myServiceImpl() { + return new MyServiceImpl(); + } + +} +```` + +> 注意:这个代码看起来可能比单元测试更短/更简单,但执行时间要长一些。 + +## 附加主题- [入门指南](getting-started) +- [配置](configuration) +- [上下文数据 / Bean 的作用域](contextual-data) +- *测试服务* +- [安全性](security) + +---------- + +[<- 返回索引](../index) diff --git a/docs/zh-CN/trouble-shooting.md b/docs/zh-CN/trouble-shooting.md new file mode 100644 index 000000000..ca54b308c --- /dev/null +++ b/docs/zh-CN/trouble-shooting.md @@ -0,0 +1,268 @@ +# 疑难解答 + +[<- 返回索引](index) + +本节描述这个项目的一些常见错误,以及如何解决这些错误。 请注意,这个页面永远不能覆盖所有案件,还请搜索现有的 issures/PRs(打开和关闭状态的)。 如果对应的主题已经存在,请给我们留下评论/信息,以便我们知道你也会受到影响。 如果没有这样的主题,请随时打开本页底部描述创建一个的新主题。 + +## 目录 + +- [传输失败](#transport-failed) +- [网络因未知原因关闭](#network-closed-for-unknown-reason) +- [找不到 TLS ALPN 提供商](#could-not-find-tls-alpn-provider) +- [证书不匹配](#dismatching-certificates) +- [不受信任的证书](#untrusted-certificates) +- [服务端端口被占用](#server-port-already-in-use) +- [创建 issues / 提问题](#creating-issues) + +## 传输失败 + +### 服务端 + +````txt +2019-07-07 10:05:46.217 INFO 6552 --- [-worker-ELG-3-5] i.g.n.s.i.g.n.N.connections : Transport failed + +io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception: HTTP/2 client preface string missing or corrupt. Hex dump for received bytes: 16030100820100007e0303aae6126974cbb4638b325d6bdb + at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2Exception.connectionError(Http2Exception.java:85) ~[grpc-netty-shaded-1.21.0.jar:1.21.0] + at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.readClientPrefaceString(Http2ConnectionHandler.java:318) ~[grpc-netty-shaded-1.21.0.jar:1.21.0] + at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler$PrefaceDecoder.decode(Http2ConnectionHandler.java:251) ~[grpc-netty-shaded-1.21.0.jar:1.21.0] + at io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler.decode(Http2ConnectionHandler.java:450) [grpc-netty-shaded-1.21.0.jar:1.21.0] +```` + +### 客户端 + +````txt +io.grpc.StatusRuntimeException: UNAVAILABLE: io exception + at io.grpc.stub.ClientCalls.toStatusRuntimeException(ClientCalls.java:235) + at io.grpc.stub.ClientCalls.getUnchecked(ClientCalls.java:216) + at io.grpc.stub.ClientCalls.blockingUnaryCall(ClientCalls.java:141) + at net.devh.boot.grpc.examples.lib.SimpleGrpc$SimpleBlockingStub.sayHello(SimpleGrpc.java:178) + [...] +Caused by: io.grpc.netty.shaded.io.netty.handler.ssl.NotSslRecordException: not an SSL/TLS record: 00001204000000000000037fffffff000400100000000600002000000004080000000000000f0001 + at io.grpc.netty.shaded.io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1204) + at io.grpc.netty.shaded.io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1272) + at io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502) +```` + +### 问题 + +服务器运行在`PLAINTEXT`模式,但客户端试图在`TLS`(默认)模式中连接它。 + +### 简单的解决办法 + +将客户端配置在`PLAINTEXT`模式下连接(不推荐生产)。 + +添加以下条目到您的客户端应用程序配置: + +````properties +grpc.client.__name__.negotiationType=PLAINTEXT +```` + +### 更好的解决办法 + +将服务端配置在`TLS`模式下运行(推荐)。 + +添加以下条目到您的服务端应用程序配置: + +````properties +grpc.server.security.enabled=true +grpc.server.security.certificateChain=file:certificates/server.crt +grpc.server.security.privateKey=file:certificates/server.key +```` + +## 网络因未知原因关闭 + +### 客户端 + +````txt +io.grpc.StatusRuntimeException: UNAVAILABLE: Network closed for unknown reason +```` + +### 问题 + +您可能是 (1) 尝试通过 `TLS ` 模式连接到 grpc-server 时,使用 `PLAINTE` 客户端 或 (2) 目标不是一个 grpc-server (例如 Web 服务)。 + +### 解决办法 + +1. 配置您的客户端使用`TLS`模式。 + + ````properties + grpc.client.__name__.negotiationType=TLS + ```` + + 或删除`negotiationType`配置,因为默认情况下`TLS`。 +2. 使用 `grpcurl` 或类似工具,验证已配置的服务端正在运行的是 grpc 服务 + +## 找不到 TLS ALPN 提供商 + +### 服务端 + +````txt +org.springframework.context.ApplicationContextException: Failed to start bean 'nettyGrpcServerLifecycle'; nested exception is java.lang.IllegalStateException: Could not find TLS ALPN provider; no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available + at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:185) ~[spring-context-5.1.8.RELEASE.jar:5.1.8.RELEASE] + [...] +Caused by: java.lang.IllegalStateException: Could not find TLS ALPN provider; no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available + at io.grpc.netty.GrpcSslContexts.defaultSslProvider(GrpcSslContexts.java:258) ~[grpc-netty-1.21.0.jar:1.21.0] + at io.grpc.netty.GrpcSslContexts.configure(GrpcSslContexts.java:171) ~[grpc-netty-1.21.0.jar:1.21.0] + at io.grpc.netty.GrpcSslContexts.forServer(GrpcSslContexts.java:130) ~[grpc-netty-1.21.0.jar:1.21.0] + [...] +```` + +### 客户端 + +````txt +[...] +Caused by: java.lang.IllegalStateException: Failed to create channel: + at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.processInjectionPoint(GrpcClientBeanPostProcessor.java:118) ~[grpc-client-spring-boot-autoconfigure-2.4.0.RELEASE.jar:2.4.0.RELEASE] + at net.devh.boot.grpc.client.inject.GrpcClientBeanPostProcessor.postProcessBeforeInitialization(GrpcClientBeanPostProcessor.java:77) + [...] +Caused by: java.lang.IllegalStateException: Could not find TLS ALPN provider; no working netty-tcnative, Conscrypt, or Jetty NPN/ALPN available + at io.grpc.netty.GrpcSslContexts.defaultSslProvider(GrpcSslContexts.java:258) ~[grpc-netty-1.21.0.jar:1.21.0] + at io.grpc.netty.GrpcSslContexts.configure(GrpcSslContexts.java:171) ~[grpc-netty-1.21.0.jar:1.21.0] + at io.grpc.netty.GrpcSslContexts.forClient(GrpcSslContexts.java:120) ~[grpc-netty-1.21.0.jar:1.21.0] + [...] +```` + +### 两端 + +````txt +AbstractMethodError: io.netty.internal.tcnative.SSL.readFromSSL() +```` + +### 问题 + +classpath 上没有 (兼容) netty TLS 实现。 + +### 解决办法 + +从[`grpc-netty`](https://mvnrepository.com/artifact/io.grpc/grpc-netty)切换到[`grpc-netty-shaded`](https://mvnrepository.com/artifact/io.grpc/grpc-netty-shaded) 或添加依赖于[`nety-tcnative-boringssl-static`](https://mvnrepository.com/artifact/io.netty/netty-tcnative-boringssl-static) (请使用与[grpc-java 的netty 安全性部分](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty)**完全相同**(兼容的版本))。 + +> **注意:** 你需要一个 64 位的 Java 虚拟机。 + +## 证书不匹配 + +### 客户端 + +````txt +io.grpc.StatusRuntimeException: UNAVAILABLE: io exception +[...] +Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem +[...] +Caused by: java.security.cert.CertificateException: No subject alternative names present +```` + +或 + +````txt +io.grpc.StatusRuntimeException: UNAVAILABLE: io exception +[...] +Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem +[...] +Caused by: java.security.cert.CertificateException: No name matching found +```` + +### 问题 + +证书与目标地址/名称不匹配。 + +### 解决办法 + +通过在客户端配置中添加以下内容: + +````properties +grpc.client.__name__.security.authorityOverride= +```` + +## 不受信任的证书 + +### 客户端 + +````txt +io.grpc.StatusRuntimeException: UNAVAILABLE: io exception +[...] +Caused by: javax.net.ssl.SSLHandshakeException: General OpenSslEngine problem +[...] +Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target +[...] +Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target +```` + +### 问题 + +服务器使用的证书不在客户端的信任库中。 + +### 解决办法 + +通过使用 java `keytool` 将证书添加到java的信任商店,或配置客户端使用自定义信任的证书文件: + +````properties +grpc.client.__name__.security.trustCertCollection=file:certificates/trusted-servers-collection.crt.list +```` + +> **注意:** 两边的存储库目前在创建时都是只读的,更新不会被应用。 + +## 服务端端口被占用 + +### 服务端 + +````txt +Caused by: java.lang.IllegalStateException: Failed to start the grpc server + at net.devh.boot.grpc.server.serverfactory.GrpcServerLifecycle.start(GrpcServerLifecycle.java:51) ~[grpc-server-spring-boot-autoconfigure-2.4.0.RELEASE.jar:2.4.0.RELEASE] + [...] +Caused by: java.io.IOException: Failed to bind + at io.grpc.netty.shaded.io.grpc.netty.NettyServer.start(NettyServer.java:246) ~[grpc-netty-shaded-1.21.0.jar:1.21.0] + at io.grpc.internal.ServerImpl.start(ServerImpl.java:177) ~[grpc-core-1.21.0.jar:1.21.0] + at io.grpc.internal.ServerImpl.start(ServerImpl.java:85) ~[grpc-core-1.21.0.jar:1.21.0] + at net.devh.boot.grpc.server.serverfactory.GrpcServerLifecycle.createAndStartGrpcServer(GrpcServerLifecycle.java:90) ~[grpc-server-spring-boot-autoconfigure-2.4.0.RELEASE.jar:2.4.0.RELEASE] + at net.devh.boot.grpc.server.serverfactory.GrpcServerLifecycle.start(GrpcServerLifecycle.java:49) ~[grpc-server-spring-boot-autoconfigure-2.4.0.RELEASE.jar:2.4.0.RELEASE] + ... 13 common frames omitted +Caused by: java.net.BindException: Address already in use: bind +```` + +### 问题 + +grpc 服务端尝试使用的端口被占用。 + +有四种常见情况可能发生这种错误。 + +1. 应用程序已在运行 +2. 另一个应用程序正在使用该端口 +3. grpc 服务器使用了一个已经用于其他用途的端口(例如spring-web) +4. 你正在运行测试,每次测试后你都没有关闭 grpc-server + +### 解决办法 + +1. 尝试使用任务管理器或`jps`搜索应用程序 +2. 尝试使用 `netstat` 搜索端口 +3. 检查/更改您的配置。 此库默认使用端口 `9090` +4. 添加`@DirtiesContext`到您的测试类和方法中,请注意,这个错误只会从第二次测试开始发生,因此你必须在你的第一个测试类上也加上这个注解! + +## 创建 issue + +在 GitHub 上创建问题/提问并不难,但你可以稍微努力帮助我们更快地解决您的 个问题。 + +如果您的问题/疑问一般都是关于 grpc 的问题,请考虑在 [grpc-java](https://github.com/grpc/grpc-java) 上提问。 + +使用提供的模板来创建新问题,其中包含我们需要的必需/有用信息的部分。 + +通常来说,你应该在你的问题上包括以下信息: + +1. 您有什么类型的诉求? + - 问题 + - Bug 反馈 + - 功能​​​​​​​​​​​请求 +2. 你希望的结果是什么? +3. 问题是什么? 什么不起作用? 缺少什么东西,为什么需要? +4. 任何相关堆栈/日志(非常重要) +5. 您使用的是哪个版本? + - Spring (boot) + - grpc-java + - grpc-spring-boot-starter + - 其他相关库 +6. 其他背景 + - 它以前是否正常运行过? + - 我们如何重现? + - 有 demo 演示吗? + +---------- + +[<- 返回索引](index) diff --git a/docs/zh-CN/versions.md b/docs/zh-CN/versions.md new file mode 100644 index 000000000..a949cabc8 --- /dev/null +++ b/docs/zh-CN/versions.md @@ -0,0 +1,81 @@ +# 版本 + +[<- 返回索引](index) + +此页显示关于我们版本策略和生命周期等额外信息。 + +## 目录 + +- [版本策略](#versioning-policy) +- [版本列表](#version-table) + - [2.x 版本](#version-2x) + - [1.x 版本](#version-1x) + - [升级依赖关系](#upgrading-dependencies) + - [发布日志](#release-notes) + +## 版本策略 + +这个项目的主要版本定义了我们与哪个Spring-boot版本兼容。 + +- 1.x.x 版本为 EOL,不会再收到任何更新。 +- 2.x.x 是当前的维护版本,如果有 spring-boot 或者 gRPC 版本,将进行更新。 + +次版本定义了此项目的功能版本。 每次我们更改 spring boot 或 gRPC 版本时,我们也会增加我们的功能版本。 如果我们增加/改变主要特征,也是如此。 在大多数情况下,你不会通过升级获得任何不兼容之处,但因为gRPC 就像它的 API 一样, 这个问题不能被排除在外。 我们试图尽量减少这种影响,但不能排除这种影响。 如果您不使用高级功能,您通常不会收到通知。 + +我们通常不发布补丁版本,但在下次发布时包含这些补丁。 如果你需要一个修补过的版本,请新开一个 issue。 + +## 版本列表 + +下表显示了该项目和 spring boot 以及 gRPC 版本的关系。 在大多数情况下,你可以升级到较新的版本,但如果是 gRPC 改变了其 API。 请将任何问题报告给我们[仓库](https://github.com/yidongnan/grpc-spring-boot-starter/issues)。 + +> **注意** +> +> 如果您正在使用 non-shaded netty(和相关的库),请 **严格** 保持这些版本跟 gRPC [文档](https://github.com/grpc/grpc-java/blob/master/SECURITY.md#netty) 一致。 (grpc-netty-shaded 通过保持这些版本同步来避免这些问题。) + +### 2.x 版本 + +当前版本。 + +| 版本 | spring-boot | gRPC | 日期 | +|:-------:|:-----------:|:------:| --------:| +| 2.6.0 * | 2.2.1 | 1.24.2 | 待定 | +| 2.5.1 | 2.1.6 | 1.22.2 | 2018年8月 | +| 2.5.0 | 2.1.6 | 1.22.1 | 2018年8月 | +| 2.4.0 | 2.1.5 | 1.20.0 | 2019年6月 | +| 2.3.0 | 2.1.4 | 1.18.0 | 2019年4月 | +| 2.2.1 | 2.0.7 | 1.17.1 | 2019年1月 | +| 2.2.0 | 2.0.6 | 1.17.1 | 2018年12月 | +| 2.1.0 | 2.0.? | 1.14.0 | 2018年10月 | +| 2.0.1 | 2.0.? | 1.14.0 | 2018年8月 | +| 2.0.0 | 2.0.? | 1.13.1 | 2018年8月 | + +(* 未来版本) + +### 1.x 版本 + +生命终结——没有计划的更新。 + +| 版本 | spring-boot | gRPC | 日期 | +|:-----:|:-----------:|:------:| -------:| +| 1.4.2 | 1.?.? | 1.12.0 | 2019年6月 | +| 1.4.1 | 1.?.? | 1.12.0 | 2018年6月 | +| ... | 1.?.? | N/A | | + +### 升级依赖关系 + +如果你升级任何版本,我们强烈建议使用 bom 文件进行升级: + +- [spring-boot](https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent) +- [grpc-java](https://mvnrepository.com/artifact/io.grpc/grpc-bom) + +### 发布日志 + +有关每个版本的更改,请参考发行说明。 + +- [grpc-spring-boot-starter](https://github.com/yidongnan/grpc-spring-boot-starter/releases) +- [spring-boot](https://github.com/spring-projects/spring-boot/releases) +- [grpc-java](https://github.com/grpc/grpc-java/releases) + +---------- + +[<- 返回索引](index)