MASTER: 只从 Master 节点读取数据,当客户端要求数据强一致性时需要采用该模式。该模式对 Master 压力较大,在同一个分片内无法采用多个节点对读操作进行负载分担。
PREFER_MASTER: 优先从 Master 节点读取数据,当 Master 节点不可用时,从 Replica 节点读取。
REPLICA: 只从 Replica 节点读取数据,由于 Master 到 Replica 的数据复制过程是异步执行的,采用该方式有可能读取到过期的数据,因此适用于客户端对数据一致性要求不高的场景。该模式下可以采用多个 Replica 节点来分担来自客户端的读负载。
PREFER_REPLICA: 优先从 Replica 节点读取数据,当 Replica 节点不可用时,从 Master 节点读取。
ANY: 从任意节点读取数据。
在前面下发的 EnvoyFilter 中,我们将 Envoy Redis Proxy 的读策略设置为了 "REPLICA", 因此客户端的读操作应该只会被发送到 Replica 节点。让我们使用下面的命令来验证读写分离的策略:
通过客户端发起一系列 key 为 "b" 的 get 和 set 操作:
$ kubectl exec -it `kubectl get pod -l app=redis-client -n redis -o jsonpath="{.items[0].metadata.name}"` -c redis-client -n redis -- redis-cli -h redis-cluster redis-cluster:6379> get b "b" redis-cluster:6379> get b "b" redis-cluster:6379> get b "b" redis-cluster:6379> set b bb OK redis-cluster:6379> get b "bb" redis-cluster:6379>在前面的 Redis Cluster 拓扑中,我们已经得知 key "b" 属于 Shard[0] 这个分片。我们可以通过命令 redis-cli monitor 查看该分片中 Master 和 Replica 节点中收到的命令。
Master 节点:
$ kubectl exec redis-cluster-0 -c redis -n redis -- redis-cli monitorSlave 节点:
$ kubectl exec redis-cluster-4 -c redis -n redis -- redis-cli monitor从下图中可以看到,所有 get 请求都被 Envoy 发送到了 Replica 节点上。
Redis 流量镜像Envoy Redis Proxy 支持流量镜像,即将客户端发送的请求同时发送到一个镜像 Redis 服务器/集群上。流量镜像是一个非常有用的功能,我们可以使用流量镜像将生产环境中的线上数据导入到测试环境中,以使用线上数据对应用进行尽可能真实的模拟测试,同时又不会影响到线上用户的正常使用。
我们创建一个单节点的 Redis 节点,用做镜像服务器:
$ kubectl apply -f k8s/redis-mirror.yaml -n redis deployment.apps/redis-mirror created service/redis-mirror created采用 EnvoFilter 来启用镜像策略:
$ sed -i .bak "s/\${REDIS_VIP}/`kubectl get svc redis-cluster -n redis -o=jsonpath='{.spec.clusterIP}'`/" istio/envoyfilter-redis-proxy-with-mirror.yaml $ kubectl apply -f istio/envoyfilter-redis-proxy-with-mirror.yaml envoyfilter.networking.istio.io/add-redis-proxy configured通过客户端发起一系列 key 为 "b" 的 get 和 set 操作:
$ kubectl exec -it `kubectl get pod -l app=redis-client -n redis -o jsonpath="{.items[0].metadata.name}"` -c redis-client -n redis -- redis-cli -h redis-cluster redis-cluster:6379> get b "b" redis-cluster:6379> get b "b" redis-cluster:6379> get b "b" redis-cluster:6379> set b bb OK redis-cluster:6379> get b "bb" redis-cluster:6379> set b bbb OK redis-cluster:6379> get b "bbb" redis-cluster:6379> get b "bbb"可以通过命令 redis-cli monitor 分别查看 Master、Replica 和镜像节点中收到的命令。
Master 节点:
$ kubectl exec redis-cluster-0 -c redis -n redis -- redis-cli monitorSlave 节点:
$ kubectl exec redis-cluster-4 -c redis -n redis -- redis-cli monitor镜像 节点:
$ kubectl exec -it `kubectl get pod -l app=redis-mirror -n redis -o jsonpath="{.items[0].metadata.name}"` -c redis-mirror -n redis -- redis-cli monitor从下图中可以看到,所有 set 请求都被 Envoy 发送到了一份镜像节点上。
实现原理在上面的步骤中,我们在 Istio 中创建了两个 EnvoyFilter 配置对象。这两个 EnvoyFilter 修改了 Envoy 代理的配置,主要包括两部分内容:Redis Proxy Network Filter 配置和 Redis Cluster 配置。
下面的 EnvoyFilter 替换了 Pilot 为 Redis Service 创建的 Listener 中的 TCP Proxy Network Filter,将其替换为一个 "type.googleapis.com/envoy.config.filter.network.redis_proxy.v2.RedisProxy" 类型的 Network Filter。 该 Redis Proxy 的缺省路由指向 "custom-redis-cluster",并且配置了读写分离策略和流量镜像策略。
apiVersion: networking.istio.io/v1alpha3 kind: EnvoyFilter metadata: name: add-redis-proxy namespace: istio-system spec: configPatches: - applyTo: NETWORK_FILTER match: listener: name: ${REDIS_VIP}_6379 # Replace REDIS_VIP with the cluster IP of "redis-cluster service filterChain: filter: name: "envoy.filters.network.tcp_proxy" patch: operation: REPLACE value: name: envoy.redis_proxy typed_config: "@type": type.googleapis.com/envoy.config.filter.network.redis_proxy.v2.RedisProxy stat_prefix: redis_stats prefix_routes: catch_all_route: request_mirror_policy: # Send requests to the mirror cluster - cluster: outbound|6379||redis-mirror.redis.svc.cluster.local exclude_read_commands: True # Mirror write commands only: cluster: custom-redis-cluster settings: op_timeout: 5s enable_redirection: true enable_command_stats: true read_policy: REPLICA # Send read requests to replica