前面咱们在开发客户端应用时,所需的服务端地址都是按如下步骤设置的:
名称 | 链接 | 备注 |
---|---|---|
项目主页 | https://github.com/zq2599/blog_demos | 该项目在GitHub上的主页 |
git仓库地址(https) | https://github.com/zq2599/blog_demos.git | 该项目源码的仓库地址,https协议 |
git仓库地址(ssh) | git@github.com:zq2599/blog_demos.git | 该项目源码的仓库地址,ssh协议 |
// 使用springboot插件 plugins { id 'org.springframework.boot' } dependencies { implementation 'org.springframework.boot:spring-boot-starter' // 依赖eureka implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' // 状态暴露需要的依赖 implementation 'org.springframework.boot:spring-boot-starter-actuator' // 依赖自动生成源码的工程 implementation project(':grpc-lib') }
server: port: 8085 spring: application: name: cloud-eureka eureka: instance: hostname: localhost prefer-ip-address: true status-page-url-path: /actuator/info health-check-url-path: /actuator/health lease-expiration-duration-in-seconds: 30 lease-renewal-interval-in-seconds: 30 client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://192.168.50.5:8085/eureka/ server: enable-self-preservation: false endpoints: shutdown: enabled: true
package com.bolingcavalry.grpctutorials; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; @EnableEurekaServer @SpringBootApplication public class CloudEurekaApplication { public static void main(String[] args) { SpringApplication.run(CloudEurekaApplication.class, args); } }
依赖eureka的gRPC服务端,其重点在于:第一,配置使用eureka,第二,不要指定端口;
在父工程grpc-turtorials下面新建名为cloud-server-side的模块,其build.gradle内容如下,注意要引入gRPC服务端相关的starter:
// 使用springboot插件 plugins { id 'org.springframework.boot' } dependencies { implementation 'org.projectlombok:lombok' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter' // 作为gRPC服务提供方,需要用到此库 implementation 'net.devh:grpc-server-spring-boot-starter' // 作为eureka的client implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' // 状态暴露需要的依赖 implementation 'org.springframework.boot:spring-boot-starter-actuator' // 依赖自动生成源码的工程 implementation project(':grpc-lib') // annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor annotationProcessor 'org.projectlombok:lombok' }
spring: application: name: cloud-server-side server: port: 0 grpc: server: port: 0 eureka: instance: prefer-ip-address: true instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}} client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://192.168.50.5:8085/eureka/
package com.bolingcavalry.grpctutorials; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @EnableDiscoveryClient @SpringBootApplication public class CloudServerSideApplication { public static void main(String[] args) { SpringApplication.run(CloudServerSideApplication.class, args); } }
package com.bolingcavalry.grpctutorials; import com.bolingcavalry.grpctutorials.lib.HelloReply; import com.bolingcavalry.grpctutorials.lib.SimpleGrpc; import net.devh.boot.grpc.server.service.GrpcService; import java.util.Date; @GrpcService public class GrpcServerService extends SimpleGrpc.SimpleImplBase { @Override public void sayHello(com.bolingcavalry.grpctutorials.lib.HelloRequest request, io.grpc.stub.StreamObserver<HelloReply> responseObserver) { HelloReply reply = HelloReply.newBuilder().setMessage("1. Hello " + request.getName() + ", " + new Date()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } }
// 使用springboot插件 plugins { id 'org.springframework.boot' } dependencies { implementation 'org.projectlombok:lombok' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter' // 作为gRPC服务使用方,需要用到此库 implementation 'net.devh:grpc-client-spring-boot-starter' // 作为eureka的client implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' // 状态暴露需要的依赖 implementation 'org.springframework.boot:spring-boot-starter-actuator' // 依赖自动生成源码的工程 implementation project(':grpc-lib') // annotationProcessor不会传递,使用了lombok生成代码的模块,需要自己声明annotationProcessor annotationProcessor 'org.projectlombok:lombok' }
server: port: 8086 spring: application: name: cloud-client-side eureka: instance: prefer-ip-address: true status-page-url-path: /actuator/info health-check-url-path: /actuator/health instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}} client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://192.168.50.5:8085/eureka/ grpc: client: # gRPC配置的名字,GrpcClient注解会用到 cloud-server-side: enableKeepAlive: true keepAliveWithoutCalls: true negotiationType: plaintext
package com.bolingcavalry.grpctutorials; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @EnableDiscoveryClient @SpringBootApplication public class CloudClientSideApplication { public static void main(String[] args) { SpringApplication.run(CloudClientSideApplication.class, args); } }
package com.bolingcavalry.grpctutorials; import com.bolingcavalry.grpctutorials.lib.HelloReply; import com.bolingcavalry.grpctutorials.lib.HelloRequest; import com.bolingcavalry.grpctutorials.lib.SimpleGrpc; import io.grpc.StatusRuntimeException; import net.devh.boot.grpc.client.inject.GrpcClient; import org.springframework.stereotype.Service; @Service public class GrpcClientService { @GrpcClient("cloud-server-side") private SimpleGrpc.SimpleBlockingStub simpleStub; public String sendMessage(final String name) { try { final HelloReply response = this.simpleStub.sayHello(HelloRequest.newBuilder().setName(name).build()); return response.getMessage(); } catch (final StatusRuntimeException e) { return "FAILED with " + e.getStatus().getCode().name(); } } }
package com.bolingcavalry.grpctutorials; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class GrpcClientController { @Autowired private GrpcClientService grpcClientService; @RequestMapping("/") public String printMessage(@RequestParam(defaultValue = "will") String name) { return grpcClientService.sendMessage(name); } }
如果您对eureka有所了解,可能会产生一点疑惑:cloud-client-side从eureka取得的cloud-server-side信息,应该是http服务的地址和端口,不应该有gRPC的端口号,因为eureka的注册发现服务并不包含gRPC有关的!
篇幅所限,这里不适合将上述问题展开分析,咱们来关注最核心的地方,相信聪明的您看上一眼就会豁然开朗;
DiscoveryClientNameResolver来自grpc-client-spring-boot-autoconfigure.jar,用来保存从eureka取得的服务端信息,该类的注释已经说得很清楚了,从metadata的gRPC.port配置项中取得gRPC端口号: