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上运行该应用程序。
从那以后,我们在deployment.yaml启动的三个pod中声明了我们的应用程序的三个实例。如果成功完成Hazelcast发现,您应该会看到Skaffold打印的Pod日志片段。
详细调试图片点击标题见原文。