使用Node.JS连接Kubernetes中Redis Pod的正确方法

时间:2019-04-04 12:56:17

标签: node.js redis kubernetes node-redis

我正在建立一个新的k8s环境,其中包含运行以node.js编写的微服务的多个Pod。其中一些服务连接到Redis缓存。

大多数情况下这都是正常的,但是访问redis时会出现间歇性错误,这使我相信我没有正确连接,最常见的情况是:

RedisServerException: READONLY You can't write against a read only slave.

如果我再试一次,经过两次或三次尝试,我通常都会成功。

这是我的Redis部署:

RESOURCES:

==> v1/Service
NAME                       TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)             AGE
cache-redis-ha-announce-0  ClusterIP  100.xxx.xxx.xxx <none>       6379/TCP,26379/TCP  163m
cache-redis-ha-announce-1  ClusterIP  100.xxx.xxx.xxx <none>       6379/TCP,26379/TCP  163m
cache-redis-ha-announce-2  ClusterIP  100.xxx.xxx.xxx <none>       6379/TCP,26379/TCP  163m
cache-redis-ha             ClusterIP  None            <none>       6379/TCP,26379/TCP  163m

==> v1/StatefulSet
NAME                   DESIRED  CURRENT  AGE
cache-redis-ha-server  3        3        94s

==> v1/Pod(related)
NAME                     READY  STATUS   RESTARTS  AGE
cache-redis-ha-server-0  2/2    Running  0         94s
cache-redis-ha-server-1  2/2    Running  0         64s
cache-redis-ha-server-2  2/2    Running  0         36s

==> v1/ConfigMap
NAME                      DATA  AGE
cache-redis-ha-configmap  3     163m
cache-redis-ha-probes     2     163m


NOTES:
Redis can be accessed via port 6379 and Sentinel can be accessed via port 26379 on the following DNS name from within your cluster:
cache-redis-ha.devtest.svc.cluster.local

在我的服务中,我这样连接到Redis:

this.client = redis.createClient(6379, "cache-redis-ha.devtest.svc.cluster.local");


this.delAsync = promisify(this.client.del).bind(this.client);


async flush(matchPattern: string): Promise<CacheResult> {
    let result: CacheResult = { matchPattern: matchPattern, reply: true };
    return await this.keysAsync(matchPattern).then(async (keys) => {
        result.matchedKeys = keys;
        if (keys.length) {
            return await this.delAsync(keys).then((reply) => {
                result.reply = reply;
                return result;
            });
        }
        return result;
    });
}

我试图连接到createClient中的Sentinel端口,但这没有用。

我的实现中是否存在明显的错误?

1 个答案:

答案 0 :(得分:1)

在Redis集群中,您有一个主控器(即R / W)和多个从属器(即RO)。

当您对所有Redis Pod使用单一服务时,您的连接将循环到所有可用Pod,而K8不知道其中哪个是主Pod,这就是为什么有时会遇到该错误的原因。当您与服务的连接在RO从属设备而不是RW主设备上终止时,就会发生这种情况。

您需要其他服务,以及诸如控制器或其他自动化设备之类的东西,它将使该服务指向现在是主服务器的正确吊舱。

此外,您可以使用discovery从Sentel获取该信息:

  

哨兵与其他哨兵保持联系,以相互检查彼此的可用性并交换消息。但是,您无需在运行的每个Sentinel实例中配置其他Sentinel地址的列表,因为Sentinel使用Redis实例的发布/订阅功能来发现监视相同主服务器和从服务器的其他Sentinel。

     

此功能是通过将问候消息发送到名为 sentinel :hello的频道中来实现的。

     

类似地,您不需要配置连接到主服务器的从服务器的列表是什么,因为Sentinel会通过查询Redis自动发现此列表。

     

每个Sentinel都会向每一个受监视的主/从属发布/订阅通道 sentinel :hello每两秒钟发布一条消息,并通过ip,port,runid宣布其存在。

     

每个哨兵都订阅了发布/订阅频道哨兵:每个主从节点的hello,以查找未知的哨兵。当检测到新的标记时,会将它们添加为该主标记的标记。

     

Hello消息还包括主服务器的完整当前配置。如果接收方Sentinel的给定主控配置早于接收方的配置,它将立即更新为新配置。

     

在将新的哨兵添加到主机之前,Sentinel始终检查是否已经存在具有相同runid或相同地址(ip和端口对)的哨兵。在这种情况下,将删除所有匹配的标记,并添加新的标记。

此外,您可以在任何节点上call sentinel master mymaster来获取当前的主服务器。

因此,最后,您需要获取Redis主服务器地址(或ID)并使用其服务(在您的安装中为cache-redis-ha-announce-*)连接到当前主服务器。