阿里云服务器ECS    
弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新 [咨询更多]
阿里云存储OSS
简单易用、多重冗余、数据备份高可靠、多层次安全防护安全性更强、低成本 [咨询更多]
阿里云数据库RDS
稳定可靠、可弹性伸缩、更拥有容灾、备份、恢复、监控、迁移等方面的全套解决方案 [咨询更多]
阿里云安全产品
DDoS高防IP、web应用防火墙、安骑士、sll证书、态势感知众多阿里云安全产品热销中 [咨询更多]
阿里云折扣优惠    
云服务器ECS、数据库、负载均衡等产品新购、续费、升级联系客服获取更多专属折扣 [咨询更多]
grpc服务
2020-6-28    点击量:
  grpc协议相比http而言,既具备http跨操作系统和编程语言的好处,又提供了基于流的通信优势。而且,grpc逐渐成为工业界的标准,一旦我们的grpc服务可以mesh化,那么更多的非标准协议就可以通过转为grpc协议的方式,低成本地接入服务网格,实现跨技术栈的服务通信。

  grpc服务的示例部分使用最普遍的编程语言Java及最高效的编程框架SpringBoot。示例的拓扑示意如下:

grpc服务1

  1.1 springboot
  
  common——proto2java
  
  示例工程包含三个模块,分别是common、provider、consumer。其中,common负责将定义grpc服务的protobuf转换为java的rpc模板代码;后两者对其依赖,分别实现grpc的服务端和客户端。
  
  示例工程的protobuf定义如下,实现了两个方法SayHello和SayBye。SayHello的入参是一个字符串,返回一个字符串;SayBye只有一个字符串类型的出参。

grpc服务2

  common构建过程使用protobuf-maven-plugin自动生成rpc模板代码。
  
  provider——grpc-spring-boot-starter
  
  provider依赖grpc-spring-boot-starter包以最小化编码,实现grpc服务端逻辑。示例实现了两套grpc方法,以在后文演示不同流量的返回结果不同。
  
  第一套方法示意如下:

grpc服务3

  第二套方法示意如下:

grpc服务4

  consumer——RESTful
  
  consumer的作用有两个,一个是对外暴露RESTful服务,一个是作为grpc的客户端调用grpc服务端provider。示意代码如下:

@RestController
public class GreeterController {
    private static String GRPC_PROVIDER_HOST;
    static {
        GRPC_PROVIDER_HOST = System.getenv("GRPC_PROVIDER_HOST");
        if (GRPC_PROVIDER_HOST == null || GRPC_PROVIDER_HOST.isEmpty()) {
            GRPC_PROVIDER_HOST = "provider";
        }
        LOGGER.info("GRPC_PROVIDER_HOST={}", GRPC_PROVIDER_HOST);
    }
    @GetMapping(path = "/hello/{msg}")
    public String sayHello(@PathVariable String msg) {
        final ManagedChannel channel = ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)
                .usePlaintext()
                .build();
        final GreeterGrpc.GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
        ListenableFuture future = stub.sayHello(HelloRequest.newBuilder().setName(msg).build());
        try {
            return future.get().getReply();
        } catch (InterruptedException | ExecutionException e) {
            LOGGER.error("", e);
            return "ERROR";
        }
    }
    @GetMapping("bye")
    public String sayBye() {
        final ManagedChannel channel = ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)
                .usePlaintext()
                .build();
        final GreeterGrpc.GreeterFutureStub stub = GreeterGrpc.newFutureStub(channel);
        ListenableFuture future = stub.sayBye(Empty.newBuilder().build());
        try {
            return future.get().getReply();
        } catch (InterruptedException | ExecutionException e) {
            LOGGER.error("", e);
            return "ERROR";
        }
    }
}

  这里需要注意的是GRPC_PROVIDER_HOST变量,我们在ManagedChannelBuilder.forAddress(GRPC_PROVIDER_HOST, 6565)中使用到这个变量,以获得provider服务的地址。相信你已经发现,服务开发过程中,我们没有进行任何服务发现能力的开发,而是从系统环境变量里获取这个值。而且,在该值为空时,我们使用了一个hardcode值provider。没错,这个值将是后文配置在isito中的provider服务的约定值。
  
  1.2 curl&grpcurl
  
  本节将讲述示例工程的本地启动和验证。首先我们通过如下脚本构建和启动provider和consumer服务:



# terminal 1

mvn clean install -DskipTests -U

java -jar provider/target/provider-1.0.0.jar

# terminal 2

export GRPC_PROVIDER_HOST=localhost

java -jar consumer/target/consumer-1.0.0.jar

export GRPC_PROVIDER_HOST=localhost

java -jar consumer/target/consumer-1.0.0.jar

  我们使用curl以http的方式请求consumer:



# terminal 3

$ curl localhost:9001/hello/feuyeux

Hello feuyeux!

$ curl localhost:9001/bye

Bye bye!





  1.2 docker
  
  服务验证通过后,我们制作三个docker镜像,以作为deployment部署到kubernetes上。这里以provider的dockerfile为例:



FROM openjdk:8-jdk-alpine

ARG JAR_FILE=provider-1.0.0.jar

COPY ${JAR_FILE} provider.jar

COPY grpcurl /usr/bin/grpcurl

ENTRYPOINT ["java","-jar","/provider.jar"]




  构建镜像和推送到远端仓库的脚本示意如下:
docker build -f grpc.provider.dockerfile -t feuyeux/grpc_provider_v1:1.0.0 .
docker build -f grpc.provider.dockerfile -t feuyeux/grpc_provider_v2:1.0.0 .
docker build -f grpc.consumer.dockerfile -t feuyeux/grpc_consumer:1.0.0 .
docker push feuyeux/grpc_provider_v1:1.0.0
docker push feuyeux/grpc_provider_v2:1.0.0
docker push feuyeux/grpc_consumer:1.0.0
  本地启动服务验证,示意如下:
# terminal 1
docker run --name provider2 -p 6565:6565 feuyeux/grpc_provider_v2:1.0.0
# terminal 2
docker exec -it provider2 sh
grpcurl -v -plaintext localhost:6565 org.feuyeux.grpc.Greeter/SayBye
exit
# terminal 3
export LOCAL=$(ipconfig getifaddr en0)
docker run --name consumer -e GRPC_PROVIDER_HOST=${LOCAL} -p 9001:9001 feuyeux/grpc_consumer
# terminal 4
curl -i localhost:9001/bye

  1.3 istio
  
  验证完镜像后,我们进入重点。本节将完整讲述如下拓扑的服务治理配置:
grpc服务5


  Deployment
  
  consumer的deployment声明示意如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: consumer
    version: v1
...
      containers:
        - name: consumer
          image: feuyeux/grpc_consumer:1.0.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 9001

  provider1的deployment声明示意如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: provider-v1
  labels:
    app: provider
    version: v1
...
      containers:
        - name: provider
          image: feuyeux/grpc_provider_v1:1.0.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 6565

  provider2的deployment声明示意如下:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: provider-v2
  labels:
    app: provider
    version: v2
...
      containers:
        - name: provider
          image: feuyeux/grpc_provider_v2:1.0.0
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 6565

  Deployment中使用到了前文构建的三个镜像。在容器服务中不存在时(IfNotPresent)即会拉取。
  
  这里需要注意的是,provider1和provider2定义的labels.app都是provider,这个标签是provider的唯一标识,只有相同才能被Service的Selector找到并认为是一个服务的两个版本。


联系客服免费领取更多阿里云产品新购、续费升级折扣,叠加官网活动折上折更优惠