java版gRPC實戰之七:基於eureka的注冊發現


歡迎訪問我的GitHub

https://github.com/zq2599/blog_demos

內容:所有原創文章分類匯總及配套源碼,涉及Java、Docker、Kubernetes、DevOPS等;

《java版gRPC實戰》全系列鏈接

  1. 用proto生成代碼
  2. 服務發布和調用
  3. 服務端流
  4. 客戶端流
  5. 雙向流
  6. 客戶端動態獲取服務端地址
  7. 基於eureka的注冊發現

關於eureka

前面咱們在開發客戶端應用時,所需的服務端地址都是按如下步驟設置的:

  • 在application.yml中配置,如下圖:

在這里插入圖片描述

  • 在用到gRPC的bean中,使用注解GrpcClient即可將Stub類注入到成員變量中:

在這里插入圖片描述

  • 上述操作方式的優點是簡單易用好配置,缺點也很明顯:服務端的IP地址或者端口一旦有變化,就必須修改application.yml並重啟客戶端應用;
  • 聰明的您一定想到了應對之道:注冊中心!沒錯,有了注冊中心,咱們的客戶端只要能從注冊中心取得最新的服務端地址,就不再需要手動配置了,以下是常規的eureka作用說明:

在這里插入圖片描述

本篇概覽

  • 如果您有Spring Cloud的開發經驗,對resttemplate和feign等應該很熟悉,但是Spring Cloud環境下的gRPC調用卻沒有那么常用,本篇的目標是通過實戰與大家一起掌握Spring Cloud環境下的gRPC調用,分為以下章節:
  1. eureka應用開發
  2. gRPC服務端開發
  3. gRPC客戶端開發
  4. 驗證
  5. 一點疑惑

源碼下載

名稱 鏈接 備注
項目主頁 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協議
  • 這個git項目中有多個文件夾,《java版gRPC實戰》系列的源碼在grpc-tutorials文件夾下,如下圖紅框所示:

在這里插入圖片描述

  • grpc-tutorials文件夾下有多個目錄,本篇文章對應的eureka代碼在cloud-eureka目錄,服務端代碼在cloud-server-side目錄,客戶端代碼在cloud-client-side目錄,如下圖:

在這里插入圖片描述

eureka應用開發

  • 在父工程grpc-turtorials下面新建名為cloud-eureka的模塊,其build.gradle內容如下:
// 使用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')
}
  • 配置文件bootstrap.yml,設置自己的web端口號和應用名,另外eureka.client.serviceUrl.defaultZone的配置請改成自己的IP:
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
  • 這個模塊只有一個類CloudEurekaApplication.java:
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服務端,其重點在於:第一,配置使用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'
}
  • 配置文件application.yml,設置自己的應用名,另外值得注意的是server.portgrpc.server.port這兩個配置的值都是0,這樣兩個端口就會被自動分配未被占用的值:
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/
  • 啟動類CloudServerSideApplication.java:
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);
    }
}
  • 提供gRPC服務的類GrpcServerService,和local-server模塊中的一樣:
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();
    }
}
  • 以上就是服務端代碼了,可見除了將gRPC端口設置為0,以及常規使用eureka的配置,其他部分和local-server模塊是一樣的;

gRPC客戶端開發

  • 依賴eureka的gRPC客戶端,其重點在於:第一,配置使用eureka,第二,配置中的gRPC配置項的名字要等於gRPC服務端在eureka注冊的名字,如下圖紅框所示:

在這里插入圖片描述

  • 在父工程grpc-turtorials下面新建名為cloud-client-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-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'
}
  • 配置文件application.yml,設置自己的web端口號,另外值得注意的是gRPC配置項cloud-server-side的名字要等於gRPC服務端在eureka注冊的名字,並且不需要address配置項
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
  • 啟動類CloudClientSideApplication.java,使用了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 CloudClientSideApplication {

    public static void main(String[] args) {
        SpringApplication.run(CloudClientSideApplication.class, args);
    }
}
  • 封裝gRPC調用的服務類GrpcServerService,和local-server模塊中的一樣,GrpcClient注解對應配置中的gRPC配置項:
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();
        }
    }
}
  • 再做一個web接口類,這樣我們就能通過web調用驗證gRPC服務了:
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);
    }
}
  • 客戶端開發完畢,接下來可以驗證了;

驗證

  • 啟動cloud-eureka:

在這里插入圖片描述

  • 啟動cloud-server-side,可見gRPC服務端口自動分配了65141,不過我們無需關心這個值,因為客戶端可以從eureka獲取到:

在這里插入圖片描述

  • 接下來啟動cloud-client-side,啟動成功后eureka上可見兩個服務的注冊信息:

在這里插入圖片描述

  • 瀏覽器訪問cloud-client-side提供的web接口,響應如下,可見cloud-client-side成功調用了cloud-server-side的gRPC服務:

在這里插入圖片描述

一點疑惑

  • 如果您對eureka有所了解,可能會產生一點疑惑:cloud-client-side從eureka取得的cloud-server-side信息,應該是http服務的地址和端口,不應該有gRPC的端口號,因為eureka的注冊發現服務並不包含gRPC有關的!

  • 篇幅所限,這里不適合將上述問題展開分析,咱們來關注最核心的地方,相信聰明的您看上一眼就會豁然開朗;

  • DiscoveryClientNameResolver來自grpc-client-spring-boot-autoconfigure.jar,用來保存從eureka取得的服務端信息,該類的注釋已經說得很清楚了,從metadata的gRPC.port配置項中取得gRPC端口號:

在這里插入圖片描述

  • 在DiscoveryClientNameResolver的代碼中打上斷點,查看成員變量instanceList,可見metadata中確實有gRPC端口的信息:

在這里插入圖片描述

  • 至於cloud-server-side如何將端口號提交到eureka,以及cloud-client-side為何會使用DiscoveryClientNameResolver來處理eureka的服務列表信息,就不在本文中討論了,您要是有興趣深入研究eureka,可以參考《程序員欣宸文章匯總(Spring篇)》中的Eureka源碼分析專題,如下圖:

在這里插入圖片描述

  • 至此,基於eureka的gRPC服務注冊發現的開發和驗證就完成了,希望本文可以給您帶來一些參考,讓您的服務在注冊中心的加持下更加靈活和可靠;

你不孤單,欣宸原創一路相伴

  1. Java系列
  2. Spring系列
  3. Docker系列
  4. kubernetes系列
  5. 數據庫+中間件系列
  6. DevOps系列

歡迎關注公眾號:程序員欣宸

微信搜索「程序員欣宸」,我是欣宸,期待與您一同暢游Java世界...
https://github.com/zq2599/blog_demos


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM