0

我在 ElasticCache 上使用ioredis作为适配器我有 2 个节点,一个主节点和一个副本,

有了这个配置

  • 数据分层:禁用
  • 引擎版本兼容性:5.0.6
  • 多可用区:启用
  • 自动故障转移:启用

在此处输入图像描述

在上个月,我们开始遇到很多 ECONNRESET 和 ETIMEDOUT 错误,通常是随着新实例的扩展而出现的,其中一些连接了这些错误,而另一些则因这些错误而下降

Error: read ECONNRESET
    at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
    at TCP.callbackTrampoline (internal/async_hooks.js:131:14)

Error: read ETIMEDOUT
     at TCP.onStreamRead (internal/stream_base_commons.js:209:20)
     at TCP.callbackTrampoline (internal/async_hooks.js:131:14)

这是我们的包装代码


     import IORedis, { KeyType, Ok, ValueType } from 'ioredis';

    import { logger, monitor } from '@utils/logging';

    const { host, port, maxRetriesPerRequest, connectTimeout } = config.get('redis');

    class RedisUtil {
        private redis: IORedis.Redis;

        private static handleError(type: string, key: string, error?: any): void {
            let errorMessage = '';
            switch (type) {
                case 'increment':
                    errorMessage = `Error thrown when trying to increment ${key}`;
                    break;
                case 'ttl':
                    errorMessage = `Error thrown when trying to get ttl for ${key}`;
                    break;
                case 'decrement':
                    errorMessage = `Error thrown when trying to decrement ${key}`;
                    break;
                case 'reset':
                    errorMessage = `Error thrown when trying to reset  ${key}`;
                    break;
            }
            logger.error(error, errorMessage);
            throw new Error(errorMessage);
        }

        constructor() {
            this.initRedis();
        }

        public async set(key: KeyType, value: ValueType, ttl?: number | string): Promise<Ok> {
            try {
                this.notifyDbSet(key, value);
                if (ttl) {
                    return await this.redis.set(key, value, 'EX', ttl);
                }
                return await this.redis.set(key, value);
            } catch (error) {
                logger.error(error, `Redis failed setting key ${key}`);
            }
        }

        public async get(key: KeyType): Promise<any> {
            try {
                this.notifyDbGet(key);
                return await this.redis.get(key);
            } catch (error) {
                logger.error(error, `Redis failed getting value for key ${key}`);
            }
        }

        public async expire(key, expiry): Promise<void> {
            await this.redis.pexpire(key, expiry);
        }

        public increment(key, cb): void {
            this.redis
                .multi()
                .incr(key)
                .pttl(key)
                .exec(async (err, replies) => {
                    if (!Array.isArray(replies)) throw err;
                    const [[incrError, counter], [ttlError, ttl]] = replies;
                    if (incrError) RedisUtil.handleError('increment', key, incrError);
                    if (ttlError) RedisUtil.handleError('ttl', key, ttlError);
                    cb({ counter, ttl });
                });
        }

        public decrement(key, cb): void {
            this.redis
                .multi()
                .decr(key)
                .pttl(key)
                .exec(async (err, replies) => {
                    if (!Array.isArray(replies)) throw err;
                    const [[decrError, _], [ttlError, ttl]] = replies;
                    if (decrError) RedisUtil.handleError('decrement', key, decrError);
                    if (ttlError) RedisUtil.handleError('ttl', key, ttlError);
                    cb({ ttl });
                });
        }

        public async resetKey(key): Promise<void> {
            this.redis.del(key).catch(resetError => {
                if (resetError) RedisUtil.handleError('reset', key, resetError);
            });
        }

        private initRedis(): void {
            this.redis = new IORedis({
                host,
                port,
                enableReadyCheck: true,
                showFriendlyErrorStack: true,
                maxRetriesPerRequest: maxRetriesPerRequest | 3,
                connectTimeout: connectTimeout | 2000
            });
            this.setEventListeners();
        }

        private setEventListeners(): void {
            this.redis.on('connect', () => {
                logger.info(`Redis connection is established. Host: ${host}`);
            });

            this.redis.on('ready', () => {
                logger.info(`Redis is ready`);
            });

            this.redis.on(
                'error',
                (error => {
                    logger.error(error, 'Redis error occurred while trying to connect Redis server');
                    monitor.errorEvent(
                        'Redis error occurred while trying to connect Redis server',
                        `Error: ${error}. Host: ${host}`,
                        ['redis_connection_failure'],
                        null
                    );
                    this.redis.disconnect();
                }).bind(this)
            );

            this.redis.on('close', () => {
                logger.info(`Redis server connection has closed`);
            });

            this.redis.on('end', ms => {
                logger.info(`Redis connection end - connection is failed to establish`);
            });
        }

        private notifyDbGet(key: KeyType): void {
            try {
                monitor.increment('redis_db.get', 1, [`db_host:${host}`]);
            } catch (error) {
                logger.error(error, `failed to notify datadog on redis DB get for key: ${key}`);
            }
        }

        private notifyDbSet(key: KeyType, value: ValueType): void {
            try {
                monitor.increment('redis_db.set', 1, [`db_host:${host}`]);
            } catch (error) {
                logger.error(error, `failed to notify datadog on redis DB set for key: ${key}, value: ${value}`);
            }
        }
    }

    export const redis = new RedisUtil();

this.redis.on('error',(error => {
                logger.error(error, 'Redis error occurred while trying to connect Redis server');
                this.redis.disconnect();
            }).bind(this)
        );
  1. 有人遇到过这个问题吗?
  2. 错误处理的最佳实践是什么,在这种情况下,我应该在 [ECONNRESET,ETIMEDOUT] 错误状态代码返回时调用断开连接吗?
4

0 回答 0