本文的范围是提供全面的指南,以在 Kubernetes 集群上启动 Redis 主从集群并实现支持分布式缓存的 Sprinboot 应用程序。对 Kubernetes/Redis/Spring boot 的全面介绍超出了本文的范围。
先决条件
- 启动并运行 Kubernetes 集群
- Node.js v16.15.0 或本地最新版本
- 本地首选 IDE 或文本编辑器
- Java 8 或本地最新版本
在 Kubernetes 上部署 Redis 集群
以下步骤描述了如何在 Kubernetes 上设置 Redis 主从集群。我强烈建议在部署到生产环境之前对 K8S Storage Class/Persistent Volume/ConfigMap 对象进行一些研究。如果您需要全面了解以下步骤,请阅读本教程。
1、创建名称空间
在您的 K8S 集群上运行流动命令以创建命名空间,这将允许更有效地管理您在 K8S 集群上的对象。
kubectl create ns redis
2、定义一个存储类
现在我们将创建一个应用于整个集群的存储类。在您的 K8S 集群上运行流动命令以创建存储类。storage-class.yaml包含配置。
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: WaitForFirstConsumer allowVolumeExpansion: true reclaimPolicy: Delete |
kubectl apply -f storage-class.yaml
3.创建一个持久卷
在这个解决方案中,我们在 Redis 集群上创建了 3 个节点,所以我们需要 3 个持久卷。在您的 K8S 集群上运行流动命令以创建持久卷。persistent-volume.yaml包含配置。
apiVersion: v1 kind: PersistentVolume metadata: name: local-pv1 spec: storageClassName: local-storage capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/storage/data1" --- apiVersion: v1 kind: PersistentVolume metadata: name: local-pv2 spec: storageClassName: local-storage capacity: storage: 1Gi accessModes: - ReadWriteOnce hostPath: path: "/storage/data2" --- apiVersion: v1 kind: PersistentVolume metadata: name: local-pv3 spec: storageClassName: local-storage capacity: storage: 2Gi accessModes: - ReadWriteOnce hostPath: path: "/storage/data3" |
kubectl apply -f persistent-volume.yaml
4.创建配置图
您可以在此处获取 ConfigMap 清单的配置。请确保更改masterauth&requirepass 值。这两个变量是 Redis 集群主从节点的密码。如果您对两者使用相同的值将很容易维护。
kubectl apply -n redis -f redis-config.yaml
5.使用StatefulSet部署Redis
StatefulSet 在需要控制主从行为时管理 pod。在您的 K8S 集群上运行流动命令以创建持久卷。persistent-volume.yaml 包含配置。
redis-statefulset.yaml
apiVersion: apps/v1 kind: StatefulSet metadata: name: redis spec: serviceName: redis replicas: 3 selector: matchLabels: app: redis template: metadata: labels: app: redis spec: initContainers: - name: config image: redis:6.2.3-alpine command: [ "sh", "-c" ] args: - | cp /tmp/redis/redis.conf /etc/redis/redis.conf echo "finding master..." MASTER_FDQN=`hostname -f | sed -e 's/redis-[0-9]\./redis-0./'` if [ "$(redis-cli -h sentinel -p 5000 ping)" != "PONG" ]; then echo "master not found, defaulting to redis-0" if [ "$(hostname)" == "redis-0" ]; then echo "this is redis-0, not updating config..." else echo "updating redis.conf..." echo "slaveof $MASTER_FDQN 6379" >> /etc/redis/redis.conf fi else echo "sentinel found, finding master" MASTER="$(redis-cli -h sentinel -p 5000 sentinel get-master-addr-by-name mymaster | grep -E '(^redis-\d{1,})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})')" echo "master found : $MASTER, updating redis.conf" echo "slaveof $MASTER 6379" >> /etc/redis/redis.conf fi volumeMounts: - name: redis-config mountPath: /etc/redis/ - name: config mountPath: /tmp/redis/ containers: - name: redis image: redis:6.2.3-alpine command: ["redis-server"] args: ["/etc/redis/redis.conf"] ports: - containerPort: 6379 name: redis volumeMounts: - name: data mountPath: /data - name: redis-config mountPath: /etc/redis/ volumes: - name: redis-config emptyDir: {} - name: config configMap: name: redis-config volumeClaimTemplates: - metadata: name: data spec: accessModes: [ "ReadWriteOnce" ] storageClassName: "local-storage" resources: requests: storage: 500Mi |
kubectl apply -n redis -f redis-statefulset.yaml
6.创建负载均衡服务
在 Kubernetes 部署的最后一步,我们将通过公共 IP 公开 Redis 服务器。为此,我们部署了 Loadbalancer 服务。
apiVersion: v1 kind: Service metadata: name: redis-service labels: app: redis spec: selector: app: redis ports: - port: 80 targetPort: 6379 protocol: "TCP" name: redis type: LoadBalancer |
kubectl apply -n redis -f redis-lb.yaml
6.1 检查外部IP
部署负载均衡器后,几分钟后集群将提供公共外部 IP。为了检查在几分钟内运行以下命令。
kubectl get service -n redis
从本地访问 Redis 集群
对于此步骤,您应该必须在本地计算机上安装 Node.js v16.15.0 或最新版本。此步骤是可选的,但为了检查日志/验证连接性,最好有办法通过redis-cli. 为此,您无需在本地计算机上安装 Redis 服务器。
1、安装redis-cli
npm install -g redis-cli
2.访问Redis集群
一旦 npm 安装成功,您可以redis-cli在终端上运行以下命令之后的任何支持的命令。
rdcli -h {host} -a {password} -p {port}
3.使用redis-cli监控Redis集群
我将分享一些有用的命令来对 Redis 集群进行基本级别的维护。您可以使用以下命令刷新所有键并检查集群上存储的值。
3.1检查Redis集群中存储的值
rdcli -h {ip} -a {password} -p {port} xxx.xxx.xxx.xxx:xx> KEYS * |
3.2 在 Redis 集群上刷新缓存
rdcli -h {ip} -a {password} -p {port} FLUSHALL
实现 Springboot 应用程序
现在我们要实现具有分布式缓存功能的 Springboot 应用程序。此应用程序包含一个 GET 服务返回字符串值,但使用Thread.sleep. 让我们看看分布式缓存解决方案如何帮助我们克服这种缓慢。
1. 将以下依赖项添加到 POM
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.pivotal.cfenv</groupId> <artifactId>java-cfenv-boot</artifactId> <version>2.4.0</version> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> |
2.Redis配置
要访问 Redis 远程服务器,我们需要在属性文件中添加一些属性,并在根包上实现 RedisConfiguration 类。
application.properties 文件
spring.redis.host=xxx.xxx.xxx.xxx spring.redis.port=xx spring.redis.password=xxxxxxxx spring.cache.redis.time-to-live=10000 |
package com.booking; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisStandaloneConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration public class RedisConfiguration { @Value("${spring.redis.host:xxx.xxx.xxx.xxx}") private String redisHost; @Value("${spring.redis.port:xx}") private int redisPort; @Value("${spring.redis.password:xxxxxxxx}") private String redisPassword; @Bean public JedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHost, redisPort); redisStandaloneConfiguration.setPassword(redisPassword); return new JedisConnectionFactory(redisStandaloneConfiguration); } @Bean public <T> RedisTemplate<String, T> redisTemplate() { RedisTemplate<String, T> redisTemplate = new RedisTemplate<String, T>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.afterPropertiesSet(); return redisTemplate; } } |
3. 创建 RESTful Web API
至此,我们讨论了属于远程 Redis 集群的配置。现在我们将实现一个典型的 Spring Boot Rest API,唯一的变化是我们在主类和服务类中提供了一些注释来配置缓存启用。
BookingServiceApplication.java(主类)
package com.booking; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableCaching public class BookingServiceApplication { public static void main(String[] args) { SpringApplication.run(BookingServiceApplication.class, args); } } |
BookingController.java(控制器类)
package com.booking.contraller; import com.booking.model.Booking; import com.booking.service.BookingService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("booking") public class BookingController { @Autowired private BookingService bookingService; @GetMapping public String getServiceDetails(@RequestParam(name = "scope", required = false, defaultValue = "N/A") String scope){ return bookingService.getServiceDetails(scope); } } |
BookingService.java(服务类)
package com.booking.service; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class BookingService { @Cacheable("echoCacheWithParam") public String getServiceDetails(String level) { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } if(level.equals("ALL")){ return "Book Service v.1.0 \n Developed By Denuwan"; }else { return "Book Service v.1.0"; } } } |
curl -X GET \ http://localhost:8080/booking
一旦你完成了 spring boot Rest 服务的实现,你就可以启动服务器并尝试上面的 GET 方法。由于Thread sleep. 但是当您尝试第二次响应时,应该需要 < 1Seconds,因为响应过程来自 Redis 缓存服务器,而不是命中服务层。
另外,请注意,我们将缓存时间设置为application.properties文件中的实时属性, 因此当您在 10 秒内再次执行 GET 请求时,您会注意到该请求再次从服务层处理并花费 > 5 秒。
源码:这里