Dapr在Java中的實踐 之 狀態管理

2023-06-07 09:00:21

狀態管理

狀態管理(State Management)使用鍵值對作為儲存機制,可以輕鬆的使長時執行、高可用的有狀態服務和無狀態服務共同執行在我們的服務中。

我們的服務可以利用Dapr的狀態管理API在狀態儲存元件中儲存、讀取和查詢鍵值對。

狀態儲存元件是可插拔的,目前支援使用Azure CosmosDB、 Azure SQL Server、 PostgreSQL,、AWS DynamoDB、Redis 作為狀態儲存媒介。

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

編寫範例程式碼

建立一個SpringBoot專案,命名為:state-management,該專案的狀態管理呼叫過程如下圖:

state-management該專案的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();
    }
}

state-management專案中一共有3個介面:

  • save:儲存狀態
  • get:讀取狀態
  • delete:刪除狀態

具體原始碼如下:

package one.more.society.state.management;

import io.dapr.client.DaprClient;
import io.dapr.client.domain.State;
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 StateManagementController {

    @Autowired
    private DaprClient client;

    private static final String STATE_STORE_NAME = "statestore";
    private static final String STATE_STORE_KEY = "one.more.society";

    /**
     * 儲存狀態
     *
     * @param value value
     * @return
     */
    @RequestMapping(value = "/save", method = RequestMethod.GET)
    public StateResponse save(String value) {
        log.info("save - value:{}", value);
        client.saveState(STATE_STORE_NAME, STATE_STORE_KEY, value).block();

        StateResponse response = new StateResponse();
        response.setCode(1);
        response.setStatus("save");
        response.setValue(value);
        return response;
    }

    /**
     * 讀取狀態
     *
     * @return StateResponse
     */
    @RequestMapping(value = "/get", method = RequestMethod.GET)
    public StateResponse get() {
        log.info("get");
        State<String> value = client.getState(STATE_STORE_NAME, STATE_STORE_KEY, String.class).block();
        log.info("value: {}", value.getValue());

        StateResponse response = new StateResponse();
        response.setCode(1);
        response.setStatus("get");
        response.setValue(value.getValue());
        return response;
    }

    /**
     * 刪除狀態
     *
     * @return
     */
    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public StateResponse delete() {
        log.info("delete");
        client.deleteState(STATE_STORE_NAME, STATE_STORE_KEY).block();

        StateResponse response = new StateResponse();
        response.setCode(1);
        response.setStatus("delete");
        return response;
    }
}

另外,在application.properties中設定:

server.port=30003

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

啟動服務

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

mvn clean package

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

dapr run --app-id state-management --app-port 30003 --dapr-http-port 31003 -- java -jar target/state-management-0.0.1-SNAPSHOT.jar

在Dapr Dashboard中看到:

服務都已經啟動成功。

先存取http://localhost:30003/get,可以看到:

讀取狀態返回為null,接下來存取http://localhost:30003/save?value=萬貓學社,可以看到:

狀態已經儲存了,再存取http://localhost:30003/get驗證一下:

狀態被正確讀取,再存取http://localhost:30003/delete,可以看到:

狀態已經被刪除了,再存取http://localhost:30003/get驗證一下:

讀取狀態返回為null。

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

狀態儲存元件

初始化Dapr後,預設為我們指定的狀態儲存元件是Redis,在使用者目錄下的.dapr資料夾中的components資料夾中,可以找到statestore.yaml檔案:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.redis
  version: v1
  metadata:
  - name: redisHost
    value: localhost:6379
  - name: redisPassword
    value: ""
  - name: actorStateStore
    value: "true"

下面讓我們來嘗試一下,使用MySQL作為狀態儲存元件,把statestore.yaml檔案修改為:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.mysql
  version: v1
  metadata:
  - name: connectionString
    value: "root:one.more.society@tcp(127.0.0.1:3306)/?allowNativePasswords=true"

重新啟動服務,可以看到在紀錄檔中看到使用MySQL作為狀態儲存元件:

time="09:57:35.5632633+08:00" level=info msg="Creating MySql schema 'dapr_state_store'" app_id=state-management instance=JT-243137 scope=dapr.contrib type=log ver=1.7.3
time="09:57:35.5862126+08:00" level=info msg="Creating MySql state table 'state'" app_id=state-management instance=JT-243137 scope=dapr.contrib type=log ver=1.7.3
time="09:57:35.6563599+08:00" level=info msg="component loaded. name: statestore, type: state.mysql/v1" app_id=state-management instance=JT-243137 scope=dapr.runtime type=log ver=1.7.3

如果在MySQL中沒有對應的庫和表,Dapr預設為我們自動建立一個名為dapr_state_store的庫,還有一個名為state的表,如下圖:

其中,state的表結構為:

CREATE TABLE `state` (
  `id` varchar(255) NOT NULL,
  `value` json NOT NULL,
  `isbinary` tinyint(1) NOT NULL,
  `insertDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `updateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `eTag` varchar(36) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

再存取一下http://localhost:30003/save?value=萬貓學社,就可以在資料庫中看到對應的資料:

值得注意的是:MySQL狀態儲存元件目前還處於Alpha狀態,最好不要在生產環境使用。

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

設定項 是否必填 說明 範例
connectionString Y 用於連線到 MySQL 的連線字串。 請不要將schema新增到連線字串中。 非SSL連線:
"<user>:<password>@tcp(<server>:3306)/?allowNativePasswords=true"
Enforced SSL 連線:
"<user>:<password>@tcp(<server>:3306)/?allowNativePasswords=true&tls=custom"
schemaName N 要使用的schema名稱。 如果指定的schema不存在,將會自動建立。預設值為"dapr_state_store" "one_more_state_store"
tableName N 要使用的表名。如果對應的表不存在,將被自動建立。預設值為 "state" "one_more_state"
pemPath N 使用 Enforced SSL 連線 時,指定要使用的 PEM 檔案完整路徑。 "/one/more/society/file.pem"
pemContents N 如果沒有提供pemPath,用於Enforced SSL連線的PEM檔案的內容。可以在K8s環境下使用。 "pem value"

設定範例:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
  name: statestore
spec:
  type: state.mysql
  version: v1
  metadata:
  - name: connectionString
    value: "root:one.more.society@tcp(127.0.0.1:3306)/?allowNativePasswords=true&tls=custom"
  - name: schemaName
    value: "one_more_state_store"
  - name: tableName
    value: "one_more_state"
  - name: pemPath
    value: "/one/more/society/file.pem"

微信公眾號:萬貓學社

微信掃描二維條碼

關注後回覆「電子書」

獲取12本Java必讀技術書籍