Dapr在Java中的實踐 之 服務呼叫

2023-06-06 09:00:37

服務呼叫

通過服務呼叫(Service-to-service Invocation),服務可以使用 gRPC 或 HTTP 這樣的標準協定來發現並可靠地與其他服務通訊。

Dapr採用邊車(Sidecar)、去中心化的架構。 要使用Dapr來呼叫服務,可以在任意Dapr範例上使用invoke這個API。 邊車程式設計模型鼓勵每個服務與自己的Dapr範例對話。 Dapr範例會相互發現並進行通訊。

文章持續更新,微信搜尋「萬貓學社」第一時間閱讀,關注後回覆「電子書」,免費獲取12本Java必讀技術書籍。

建立專案

建立兩個SpringBoot專案,分別命名為:invoke-serverinvoke-clientinvoke-server作為下游服務,被invoke-client呼叫,具體呼叫過程如下圖:

呼叫過程包括:

  1. invoke-client服務對invoke-server服務發起HTTP或gRPC呼叫的時候,存取invoke-client服務的Dapr範例。
  2. invoke-client服務的Dapr範例通過執行在給定託管平臺上服務名解析元件(Name Resolution Component)發現了執行在此Dapr環境中的invoke-server服務。
  3. invoke-client服務的Dapr範例將訊息轉發到服務invoke-server服務的Dapr範例。Dapr範例之間的所有呼叫考慮到效能都優先使用gRPC。 僅服務與Dapr範例之間的呼叫可以是HTTP或gRPC。
  4. invoke-server服務的Dapr範例將請求轉發至invoke-server服務上的特定端點或方法,隨後執行其業務邏輯程式碼。
  5. invoke-server服務返回響應資訊給invoke-client服務時,響應資訊給將轉至invoke-server服務的Dapr範例。
  6. invoke-server服務的Dapr範例訊息轉發至invoke-client服務的Dapr範例。
  7. invoke-client服務接收到其Dapr範例的響應資訊。

文章持續更新,微信搜尋「萬貓學社」第一時間閱讀,關注後回覆「電子書」,免費獲取12本Java必讀技術書籍。

編寫invoke-server的程式碼

呼叫/send介面時,返回對應資訊,主要程式碼如下:

package one.more.society.invoke.server;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class InvokeServerController {

    @RequestMapping(value = "/send", method = RequestMethod.POST)
    public InvokeResponse send(@RequestBody InvokeRequest request) {
        log.info("send - request:{}", request);

        InvokeResponse response = new InvokeResponse();
        response.setCode(1);
        response.setStatus("ok");
        response.setMsgId(System.nanoTime());
        response.setMsgContent("I konw you said: " + request.getMsgContent());

        return response;
    }
}

其中,InvokeRequestInvokeResponse的原始碼如下:

package one.more.society.invoke.server;

import lombok.Data;

@Data
public class InvokeRequest {

    private Long msgId;
    private String msgContent;
}
package one.more.society.invoke.server;

import lombok.Data;

@Data
public class InvokeResponse {

    private int code;
    private String status;
    private Long msgId;
    private String msgContent;
}

application.properties中設定:

server.port=30001

文章持續更新,微信搜尋「萬貓學社」第一時間閱讀,關注後回覆「電子書」,免費獲取12本Java必讀技術書籍。

編寫invoke-client

invoke-client專案的pom.xml檔案中新增如下依賴:

<dependency>
    <groupId>io.dapr</groupId>
    <artifactId>dapr-sdk-springboot</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>com.squareup.okhttp3</groupId>
    <artifactId>okhttp</artifactId>
    <version>4.9.3</version>
</dependency>

注入一個DaprClient的bean:

@Configuration
public class DaprConfig {

    private static final DaprClientBuilder BUILDER = new DaprClientBuilder();

    @Bean
    public DaprClient buildDaprClient() {
        return BUILDER.build();
    }
}

呼叫invoke-server/send介面,主要程式碼如下:

package one.more.society.invoke.client;

import io.dapr.client.DaprClient;
import io.dapr.client.domain.HttpExtension;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@Slf4j
@RestController
public class InvokeClientController {

    @Autowired
    private DaprClient client;

    private static final String SERVICE_APP_ID = "invoke-server";
    private static final String METHOD_NAME = "send";

    @RequestMapping(value = "/say", method = RequestMethod.GET)
    public InvokeResponse say(String message) {
        log.info("send - message:{}", message);

        InvokeRequest request = new InvokeRequest();
        request.setMsgId(System.nanoTime());
        request.setMsgContent(message);

        InvokeResponse response = client.invokeMethod(
                SERVICE_APP_ID,
                METHOD_NAME,
                request,
                HttpExtension.POST,
                InvokeResponse.class).block();

        return response;
    }
}

其中,InvokeRequestInvokeResponse的原始碼與invoke-server中是一樣的。

application.properties中設定:

server.port=30002

文章持續更新,微信搜尋「萬貓學社」第一時間閱讀,關注後回覆「電子書」,免費獲取12本Java必讀技術書籍。

啟動服務

在啟動之前先用mvn命令打包:

mvn clean package

invoke-server專案的目錄中執行以下命令,啟動invoke-server服務:

dapr run --app-id invoke-server --app-port 30001 --dapr-http-port 31001 -- java -jar target/invoke-server-0.0.1-SNAPSHOT.jar

invoke-client專案的目錄中執行以下命令,啟動invoke-client服務:

dapr run --app-id invoke-client --app-port 30002 --dapr-http-port 31002 -- java -jar target/invoke-client-0.0.1-SNAPSHOT.jar

在Dapr Dashboard中看到:

兩個服務都已經啟動成功。

存取http://localhost:30002/say?message=OneMoreSociety驗證整個呼叫流程:

可以看到服務之間的呼叫沒有問題,並返回了預想的結果。

文章持續更新,微信搜尋「萬貓學社」第一時間閱讀,關注後回覆「電子書」,免費獲取12本Java必讀技術書籍。

名稱解析元件

為了啟用服務發現和服務呼叫,Dapr使用可插拔的名稱解析元件。 Kubernetes名稱解析元件使用Kubernetes DNS服務來解析叢集中執行的其他服務的位置;自託管機器可以使用mDNS名稱解析元件。

Consul名稱解析元件可以在任何託管環境中使用,包括Kubernetes或自託管環境。下面讓我們來嘗試一下,使用Consul作為名稱解析元件。

在使用者目錄下的.dapr資料夾中,找到config.yaml檔案。在該檔案中,新增一個nameResolutionspec ,並將component欄位設定為consul,比如:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: daprConfig
spec:
  nameResolution:
    component: "consul"
    configuration:
      client:
        address: "127.0.0.1:8500"
      selfRegister: true

重新啟動服務,可以在紀錄檔中看到註冊到了Consul上:

time="14:28:54.4540593+08:00" level=info msg="service:invoke-client registered on consul agent" app_id=invoke-client instance=OneMoreSociety scope=dapr.contrib type=log ver=1.7.3
time="14:28:54.4550937+08:00" level=info msg="Initialized name resolution to consul" app_id=invoke-client instance=OneMoreSociety scope=dapr.runtime type=log ver=1.7.3

在Consul中也可以看到兩個服務都已經註冊上去了,如下圖:

值得注意的是:Consul名稱解析元件目前還處於Alpha狀態,最好不要在生產環境使用。

更詳細的設定說明見下表:

設定項 是否必填 資料型別 說明 範例
client N Config 設定使用者端與 Consul 代理的連線。 如果留空,將使用預設值,即127.0.0.1:8500 192.168.0.111:8500
queryOptions N QueryOptions 設定用於解決健康服務的查詢,預設為UseCache:true UseCache: false,
Datacenter: "myDC"
checks N AgentServiceCheck陣列 當進行註冊服務時,設定健康檢查。預設到Dapr範例檢測健康端點。
tags N string陣列 在註冊服務服務時包含的額外標籤 - "dapr"
meta N string字典 在註冊服務服務時包含的額外的後設資料 DAPR_METRICS_PORT: "${DAPR_METRICS_PORT}"
daprPortMetaKey N string 用於在服務解析過程中從Consul服務後設資料中獲取Dapr範例埠的 key,它也將用於在註冊時在後設資料中設定Dapr範例埠。 預設為 DAPR_PORT "DAPR_TO_DAPR_PORT"
selfRegister N boolean 控制Dapr範例是否會向Consul註冊服務,預設為 false true
advancedRegistration N AgentServiceRegistration 通過設定完全控制服務註冊結果。 如果設定此項,Checks、 Tags、 Meta 和 SelfRegister的任何設定將被忽略。

設定範例:

apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
  name: appconfig
spec:
  nameResolution:
    component: "consul"
    configuration:
      client:
        address: "127.0.0.1:8500"
      selfRegister: true
      checks:
        - name: "Dapr Health Status"
          checkID: "daprHealth:${APP_ID}"
          interval: "15s",
          http: "http://${HOST_ADDRESS}:${DAPR_HTTP_PORT}/v1.0/healthz"
        - name: "Service Health Status"
          checkID: "serviceHealth:${APP_ID}"
          interval: "15s",
          http: "http://${HOST_ADDRESS}:${APP_PORT}/health"
      tags:
        - "dapr"
        - "v1"
        - "${OTHER_ENV_VARIABLE}"
      meta:
        DAPR_METRICS_PORT: "${DAPR_METRICS_PORT}"
        DAPR_PROFILE_PORT: "${DAPR_PROFILE_PORT}"
      daprPortMetaKey: "DAPR_PORT"        
      queryOptions:
        useCache: true
        filter: "Checks.ServiceTags contains dapr"

最後,感謝你這麼帥,還給我點贊

微信公眾號:萬貓學社

微信掃描二維條碼

關注後回覆「電子書」

獲取12本Java必讀技術書籍