在Kubernetes上使用Spring Boot實現Hazelcast分散式快取 – Piotr

banq發表於2020-02-22

Hazelcast是領先的記憶體資料網格(IMDG)解決方案。IMDG的主要思想是在群集內的許多節點之間分佈資料。因此,它似乎是在Kubernetes等雲平臺上執行的理想解決方案,在該平臺上,您可以輕鬆擴充套件或縮減多個正在執行的例項。由於Hazelcast是用Java編寫的,因此您可以使用標準庫輕鬆地將其與Java應用程式整合。Spring Boot可以簡化Hazelcast的入門。您也可以使用非官方的庫來為Hazelcast實現Spring Repositories模式-Spring Data Hazelcast。本文的主要目的是演示如何將Hazelcast嵌入到Spring Boot應用程式中以及如何在Kubernetes上將其作為多例項叢集執行。多虧了Spring Data Hazelcast,我們不必再去研究Hazelcast資料型別的細節了。儘管Spring Data Hazelcast並沒有提供許多高階功能,但對於入門來說還是非常好的。

帶有示例應用程式的原始碼通常可以在GitHub上獲得。它可以在這裡https://github.com/piomin/sample-hazelcast-spring-datagrid.git。您應該訪問模組employee-kubernetes-service。

架構

我們正在Kubernetes上執行單個Spring Boot應用程式的多個例項。每個應用程式公開用於HTTP API訪問的埠8080和用於Hazelcast群整合員發現的埠5701。Hazelcast例項被嵌入到Spring Boot應用程式中。我們正在Kubernetes上建立兩個服務。它們中的第一個專用於HTTP API訪問,而第二個則負責啟用Hazelcast例項之間的發現。HTTP API將用於發出一些測試請求,這些請求會將資料新增到群集並在其中查詢資料。讓我們繼續執行。

依賴關係

hazelcast-spring庫提供了Spring和Hazelcast之間的整合。Hazelcast庫的版本通過依賴管理與Spring Boot相關,因此我們只需要將Spring Boot的版本定義為最新的stable即可2.2.4.RELEASE。與該版本的Spring Boot相關的Hazelcast的當前版本為3.12.5。為了在Kubernetes上啟用Hazelcast成員發現,我們還需要包括hazelcast-kubernetes依賴項。其版本與核心庫無關。最新的版本2.0是因為我們仍然在使用Hazelcast 3我們宣佈版本專用於Hazelcast 4 1.5.2中hazelcast-kubernetes。為了簡化,我們還包括Spring Data Hazelcast和可選的Lombok。

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.4.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.hazelcast</groupId>
        <artifactId>spring-data-hazelcast</artifactId>
        <version>2.2.2</version>
    </dependency>
    <dependency>
        <groupId>com.hazelcast</groupId>
        <artifactId>hazelcast-spring</artifactId>
    </dependency>
    <dependency>
        <groupId>com.hazelcast</groupId>
        <artifactId>hazelcast-client</artifactId>
    </dependency>
    <dependency>
        <groupId>com.hazelcast</groupId>
        <artifactId>hazelcast-kubernetes</artifactId>
        <version>1.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

啟用KUBERNETES發現

包括必需的依賴項後,已為我們的應用程式啟用了Hazelcast。我們唯一需要做的就是通過Kubernetes進行發現。該HazelcastInstancebean在上下文中已經可用,因此我們可以通過定義com.hazelcast.config.Configbean 來更改其配置。我們需要禁用預設情況下啟用的多播發現,並在網路配置中啟用Kubernetes發現,如下所示。Kubernetes配置需要設定Hazelcast部署的目標名稱空間及其服務名稱。

@Bean
Config config() {
    Config config = new Config();
    config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(false);
    config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
    config.getNetworkConfig().getJoin().getKubernetesConfig().setEnabled(true)
            .setProperty("namespace", "default")
            .setProperty("service-name", "hazelcast-service");
    return config;
}

我們還必須hazelcast-service在port上定義Kubernetes服務5701。它指的是employee-service部署。

apiVersion: v1
kind: Service
metadata:
  name: hazelcast-service
spec:
  selector:
    app: employee-service
  ports:
    - name: hazelcast
      port: 5701
  type: LoadBalancer

這是我們的示例應用程式的Kubernetes部署和服務定義。我們為部署設定了三個副本。我們還將在容器外部公開兩個埠。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: employee-service
  labels:
    app: employee-service
spec:
  replicas: 3
  selector:
    matchLabels:
      app: employee-service
  template:
    metadata:
      labels:
        app: employee-service
    spec:
      containers:
        - name: employee-service
          image: piomin/employee-service
          ports:
            - name: http
              containerPort: 8080
            - name: multicast
              containerPort: 5701
---
apiVersion: v1
kind: Service
metadata:
  name: employee-service
  labels:
    app: employee-service
spec:
  ports:
    - port: 8080
      protocol: TCP
  selector:
    app: employee-service
  type: NodePort

實際上,這是在Kubernetes上成功執行Hazelcast叢集所需要做的全部工作。在進行部署之前,讓我們看一下應用程式實現的詳細資訊。

實體

我們的應用非常簡單。它定義了一個模型物件,該物件儲存在Hazelcast群集中。這樣的類需要具有id –一個用Spring Data註釋的欄位@Id,並且應該實現Seriazable介面。

@Getter
@Setter
@EqualsAndHashCode
@ToString
public class Employee implements Serializable {
 
    @Id
    private Long id;
    @EqualsAndHashCode.Exclude
    private Integer personId;
    @EqualsAndHashCode.Exclude
    private String company;
    @EqualsAndHashCode.Exclude
    private String position;
    @EqualsAndHashCode.Exclude
    private int salary;
 
}

使用Spring Data Hazelcast,我們可以定義儲存庫,而無需使用任何查詢或特定於Hazelcast的API進行查詢。我們使用Spring Data定義的眾所周知的方法命名模式來構建find方法,如下所示。我們的儲存庫介面應該擴充套件HazelcastRepository。

public interface EmployeeRepository extends HazelcastRepository<Employee, Long> {
 
    Employee findByPersonId(Integer personId);
    List<Employee> findByCompany(String company);
    List<Employee> findByCompanyAndPosition(String company, String position);
    List<Employee> findBySalaryGreaterThan(int salary);
 
}

要啟用Spring Data Hazelcast儲存庫,我們應該使用註釋主類或配置類@EnableHazelcastRepositories。

@SpringBootApplication
@EnableHazelcastRepositories
public class EmployeeApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(EmployeeApplication.class, args);
    }
     
}

最後,這是Spring控制器的實現。它允許我們呼叫儲存庫中定義的所有find方法,將新Employee物件新增到Hazelcast中並刪除現有物件。

@RestController
@RequestMapping("/employees")
public class EmployeeController {
 
    private static final Logger logger = LoggerFactory.getLogger(EmployeeController.class);
 
    private EmployeeRepository repository;
 
    EmployeeController(EmployeeRepository repository) {
        this.repository = repository;
    }
 
    @GetMapping("/person/{id}")
    public Employee findByPersonId(@PathVariable("id") Integer personId) {
        logger.info("findByPersonId({})", personId);
        return repository.findByPersonId(personId);
    }
     
    @GetMapping("/company/{company}")
    public List<Employee> findByCompany(@PathVariable("company") String company) {
        logger.info(String.format("findByCompany({})", company));
        return repository.findByCompany(company);
    }
 
    @GetMapping("/company/{company}/position/{position}")
    public List<Employee> findByCompanyAndPosition(@PathVariable("company") String company, @PathVariable("position") String position) {
        logger.info(String.format("findByCompany({}, {})", company, position));
        return repository.findByCompanyAndPosition(company, position);
    }
     
    @GetMapping("/{id}")
    public Employee findById(@PathVariable("id") Long id) {
        logger.info("findById({})", id);
        return repository.findById(id).get();
    }
 
    @GetMapping("/salary/{salary}")
    public List<Employee> findBySalaryGreaterThan(@PathVariable("salary") int salary) {
        logger.info(String.format("findBySalaryGreaterThan({})", salary));
        return repository.findBySalaryGreaterThan(salary);
    }
     
    @PostMapping
    public Employee add(@RequestBody Employee emp) {
        logger.info("add({})", emp);
        return repository.save(emp);
    }
 
    @DeleteMapping("/{id}")
    public void delete(@PathVariable("id") Long id) {
        logger.info("delete({})", id);
        repository.deleteById(id);
    }
 
}

在MINIKUBE上執行

我們將在Minikube上測試示例應用程式。

$ minikube start --vm-driver=virtualbox

該應用程式配置為可以與Skaffold和Jib Maven外掛一起執行。它們簡化了Minikube上的構建和部署過程。假設我們位於應用程式的根目錄中,我們只需要執行以下命令。Skaffold使用Maven自動構建我們的應用程式,基於Maven設定建立Docker映像,從k8s目錄中應用部署檔案,最後在Kubernetes上執行該應用程式。

$ skaffold dev

從那以後,我們在deployment.yaml啟動的三個pod中宣告瞭我們的應用程式的三個例項。如果成功完成Hazelcast發現,您應該會看到Skaffold列印的Pod日誌片段。

詳細除錯圖片點選標題見原文。

 

相關文章