在 TypeScript 类中,用于访问 Redis 的客户端已关闭。

huangapple go评论72阅读模式
英文:

Getting client is closed on TypeScript class that is used to access Redis

问题

Below is the translated code:

我的类

    import * as redis from 'redis';
    
    export class RedisClient {
        private client: any
    
        constructor() {
            this.client = redis.createClient()
        }
    
        public async set(key: string, value: any): Promise<void> {
            return new Promise((resolve, reject) => {
                this.client.set(key, value, (err: any) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve();
                    }
                });
            });
        }
    
        public async get(key: string): Promise<string> {
            return new Promise((resolve, reject) => {
                this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
                    if (err) {
                        reject(err);
                    } else {
                        resolve(value);
                    }
                });
            });
        }
    }

As for your update, using the Singleton pattern to ensure that the RedisClient is only instantiated once is a common approach when working with resources like Redis connections. This helps avoid the overhead of creating a new connection for each operation.

In TypeScript, the implementation you've shown seems reasonable. By using a static method instance() and a cached instance, you ensure that only one RedisClient instance is created and reused throughout the application. This can improve performance and resource usage when dealing with Redis or other similar resources that benefit from connection pooling.

Overall, it's a good pattern to follow when working with resources like Redis, and it aligns with best practices for managing connections efficiently.

英文:

My class

import * as redis from &#39;redis&#39;;

export class RedisClient {
    private client: any

    constructor() {
        this.client = redis.createClient()
    }

    public async set(key: string, value: any): Promise&lt;void&gt; {
        return new Promise((resolve, reject) =&gt; {
            this.client.set(key, value, (err: any) =&gt; {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            });
        });
    }

    public async get(key: string): Promise&lt;string&gt; {
        return new Promise((resolve, reject) =&gt; {
            this.client.get(key, (err: any, value: string | PromiseLike&lt;string&gt;) =&gt; {
                if (err) {
                    reject(err);
                } else {
                    resolve(value);
                }
            });
        });
    }
}

I know I need to add this.client.connect() and I was hoping I could add it to constructor but that doesn't work. I get client is closed. It does work if I add this.client.connect within the set method but is the approach considered good if we are connecting to Redis every single time before calling set/get?

Is there a better approach to this


Update:
Taking answer into suggestion, created with Singleton pattern. Added the following in RedisClient

let cached: Promise&lt;RedisClient&gt;;
static instance(): Promise&lt;RedisClient&gt; {
    if (!cached) cached = RedisClient.create();
    return cached;
}

In caller class

constructor() {
    this.redisClient = RedisClient.instance();
}
public async handle(event: DynamoDBStreamEvent): Promise&lt;void&gt; {
    const client = await this.redisClient;
    ....
}

This works but I am not too familiar with typescript. Is this considered a good pattern. My intention is that RedisClient is only instantiated once so we are not always re-connecting to Redis with every operation

答案1

得分: 1

这只是JavaScript和TypeScript的限制。.connect函数是async的,所以你不能将它放在构造函数中。问题在于,在你的代码其余部分运行之前,.connect的调用没有被解析。你需要等待.connect解析完成,然后才能使用Redis客户端。

如果我发现自己编写这种类型的类,通常会添加一个静态方法,该方法将.createClient.connect一起调用,然后使用该方法代替new。有很多方法可以解决这个问题,但这是其中一种:

import { createClient } from 'redis';

type RedisConnection = ReturnType<typeof createClient>;

export class RedisClient {

  private connection: RedisConnection

  constructor(connection: RedisClientConnection) {
    this.connection = connection
  }

  static async create() {
    const connection = createClient();
    connection.on('error', (err) => console.log('Redis Client Error', err));
    await connection.connect();

    return new RedisClient(connection);
  }

  public async set(key: string, value: any): Promise<void> {
    return this.connection.set(key, value);
  }

  public async get(key: string): Promise<string> {
    return this.connection.get(key);
  }
}

请注意以下几点:

  1. 我将client重命名为connection,因为你正在创建一个名为RedisClient的类,我认为有两个东西都叫“Client”会令人困惑。
  2. 我添加了一些错误处理,以便在出现问题时知道发生了什么。这是可选的,但建议使用。您可能还希望进行更健壮的处理。
  3. 我使用ReturnType为你消除了所有的any在 TypeScript 类中,用于访问 Redis 的客户端已关闭。
  4. 我删除了所有的回调/承诺逻辑。Node Redis 4.x 现在支持承诺,因此不再需要它。

你甚至可以进一步采用单例模式,在这里使用,因为对于Node.js应用程序来说,Redis很少需要多个连接。

英文:

This is just a limitation of JavaScript and thus TypeScript. The .connect function is async and so you can't put it in the constructor. What's happening is that the resolution of the call to .connect is not happening before the rest of your code runs. You need to wait for .connect to resolve before you can use the Redis client.

If I find myself writing this sort of class, I usually add a static method that calls .createClient and .connect together and then use that method instead of new. There's a lot of ways to skin this cat, but here's one:

import { createClient } from &#39;redis&#39;

type RedisConnection = ReturnType&lt;typeof createClient&gt;

export class RedisClient {

  private connection: RedisConnection

  constructor(connection: RedisClientConnection) {
    this.connection = connection
  }

  async static create() {
    const connection = redis.createClient()
    connection.on(&#39;error&#39;, (err) =&gt; console.log(&#39;Redis Client Error&#39;, err))
    await connection.connect()

    return new RedisClient(connection)
  }

  public async set(key: string, value: any): Promise&lt;void&gt; {
    return this.connection.set(key, value)
  }

  public async get(key: string): Promise&lt;string&gt; {
    return this.connection.get(key)
  }
}

Note a few things here:

  1. I renamed client to connection since you are creating a class called RedisClient as I thought it would be confusing to have two things called "Client".
  2. I added some error handling so you know what's going on when stuff breaks. Optional, but recommended. And you might want to do something a bit more robust.
  3. I got rid of the pesky any for you using ReturnType! 在 TypeScript 类中,用于访问 Redis 的客户端已关闭。
  4. I removed all the callback/promise logic. Node Redis 4.x supports promises now so there's no need for it.

You might even take this a step further and use a Singleton pattern here as Redis rarely needs more than one connection for a Node.js application.

答案2

得分: 0

在你的情况下,我会将 Redis 连接包装在 IIF 中,并将结果赋值给类的 client 属性。

import * as redis from 'redis';

export class RedisClient {
    private client: any;

    constructor() {
        (async () => {
            this.client = redis.createClient();
            await this.client.connect();
        })();
    }

    public async set(key: string, value: any): Promise<void> {
        return new Promise((resolve, reject) => {
            this.client.set(key, value, (err: any) => {
                if (err) {
                    reject(err);
                } else {
                    resolve();
                }
            });
        });
    }

    public async get(key: string): Promise<string> {
        return new Promise((resolve, reject) => {
            this.client.get(key, (err: any, value: string | PromiseLike<string>) => {
                if (err) {
                    reject(err);
                } else {
                    resolve(value);
                }
            });
        });
    }
}

请注意,我只翻译了代码部分,没有包括问题或其他内容。

英文:

What i would do in your situation is the wrap the redis connection in a IIF and assign the result to the client property on the class

import * as redis from &#39;redis&#39;;

export class RedisClient {
private client: any

constructor() {
   (async()=&gt;{
    this.client = redis.createClient();
    await this.client.connect()
   })()
}

public async set(key: string, value: any): Promise&lt;void&gt; {
    return new Promise((resolve, reject) =&gt; {
        this.client.set(key, value, (err: any) =&gt; {
            if (err) {
                reject(err);
            } else {
                resolve();
            }
        });
    });
}

public async get(key: string): Promise&lt;string&gt; {
    return new Promise((resolve, reject) =&gt; {
        this.client.get(key, (err: any, value: string | PromiseLike&lt;string&gt;) =&gt; {
            if (err) {
                reject(err);
            } else {
                resolve(value);
            }
        });
    });
}

}

huangapple
  • 本文由 发表于 2023年1月6日 12:23:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75026900.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定