Dartis는 Dart용 포괄적인 Redis 클라이언트로, 타입 안전한 명령 실행, 파이프라인 처리, 전송 후 잊기 작업, 게시/구독 메시징, 모니터링 모드, 인라인 명령, 트랜잭션, Lua 스크립팅 및 모듈 지원을 제공합니다. Redis 데이터베이스와의 강력하고 효율적인 상호작용을 위해 설계되었으며, Flutter 및 Dart 애플리케이션에서 Redis의 전체 기능 세트를 활용할 수 있게 해줍니다. 컴파일 시 타입 검사와 최적화된 통신 패턴을 통해 안정성과 성능을 중시합니다. 간단한 pub 패키지 설치를 통해 기존 워크플로우에 원활하게 통합됩니다.
사용 사례
발행/구독을 통한 실시간 데이터 스트리밍
파이프라인을 사용한 고성능 배치 작업
트랜잭션 데이터 업데이트
서버 측 Lua 스크립트 실행
실시간으로 Redis 활동 모니터링
Dart 앱에 Redis 모듈 통합하기
주요 기능
유형 안전한 Redis 명령
파이프라인 및 파이어 앤드 포그릿 지원
발행/구독 메시징
Lua 스크립트 통합
거래 지원
디버깅을 위한 모니터 모드
적합한 대상
실시간 Flutter 앱을 개발하는 개발자
고속 Redis 접근이 필요한 백엔드 서비스
복잡한 Redis 워크플로를 사용하는 애플리케이션
데이터베이스 상호작용에서 타입 안전성을 우선시하는 팀
Redis 모듈 또는 고급 기능을 활용하는 프로젝트
고려 사항
실행 중인 Redis 서버가 필요합니다
제한된 플랫폼 지원(특정 플랫폼이 나열되지 않음)
안전한 연결을 위해 추가 구성이 필요할 수 있습니다
Lua 스크립팅은 Redis Lua API에 익숙해야 합니다
비-Redis 데이터베이스용으로 설계되지 않음
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Redis client for Dart
Features
Type-safe commands
Pipelining
Fire and forget
Publish/Subscribe
Monitor mode
Inline commands
Transactions
Lua scripting
Custom serializers/deserializers
Custom commands for building Modules
Usage
Create a connection:
final client = awaitClient.connect('redis://localhost:6379');
Get a type-safe view of the available Redis Commands:
final commands = client.asCommands<String, String>();
Run some commands:
await commands.set('key', 'value');
final value = await commands.get('key');
print(value);
Disconnect:
await client.disconnect();
Connection String
Connection string must follow the following pattern:
redis://{host}:{port}
Example:
redis://localhost:6379
Client Modes
Clients can work in the following modes:
Online
Publish/Subscribe
Inline commands
Monitor
Online
In this mode the client can send any command to the Redis server.
// Connectfinal client = awaitClient.connect('redis://localhost:6379');
// Run some commandsfinal commands = client.asCommands<String, String>();
final result = await commands.ping();
print(result);
// Disconnectawait client.disconnect();
See client.dart in the example folder.
Publish/Subscribe
In this mode the only allowed commands are subscribe, unsubscribe, psubscribe, punsubscribe, ping and quit.
The replies to subscription and unsubscription commands along with the published messages are received in the form of events, so that the client can just read a coherent Stream of events.
final pubsub = awaitPubSub.connect<String, String>('redis://localhost:6379');
// Subscribe to some channels and patterns
pubsub
..subscribe(channel: 'dev.dart')
..psubscribe(pattern: 'dartlang.news.*');
// Listen for server replies
pubsub.stream.listen(print, onError: print);
See pubsub.dart in the example folder.
If the Redis server is protected with a password then a client connection must be created in order to run the Redis AUTH command.
final client = awaitClient.connect('redis://localhost:6379');
final commands = client.asCommands<String, String>();
await commands.auth('password');
// Create the PubSub object using the client connectionfinal broker = PubSub<String, String>(client.connection);
Inline Commands
In this mode the commands are sent to the server using the "inline command" format. Ideal to use in interactive sessions, like a Telnet session.
final terminal = awaitTerminal.connect('redis://localhost:6379');
// Run some commands
terminal.run('PING\r\n'.codeUnits);
// Listen for server replies
terminal.stream.listen(print);
Note that in this mode the commands are just lists of bytes with a trailing \r\n.
See terminal.dart in the example folder.
Monitor
In this mode the client receives all the commands procesed by the Redis server. Useful for debugging.
final monitor = awaitMonitor.connect('redis://localhost:6379');
// Start the monitor mode
monitor.start();
// Listen for server replies
monitor.stream.listen(print);
In this mode the client can not run any command.
See monitor.dart in the example folder.
Commands
The method asCommands<K, V> of the client returns a type-safe view of the available Redis Commands. K is the type to be used for Redis keys and V for values. Most times, using String for keys and values is what you want:
final commands = client.asCommands<String, String>();
However, it's correct to call this method several times in order to get views with different parameterized types:
final strings = client.asCommands<String, String>();
final bytes = client.asCommands<String, List<int>>();
String title = await strings.get('book:24902:title');
List<int> cover = await bytes.get('book:24902:cover');
// ERROR String author = await bytes.get('book:24092:author');
Keep in mind that Redis stores sequences of bytes, not just Strings.
Pipelining
Pipeling is used in order to send multiple commands to the server in only one call, instead of doing one call for each command.
In this mode the client stores locally all the commands without sending them to the server until the flush method is called.
// Start pipeline
client.pipeline();
// Run some commands
commands.incr('product:9238:views').then(print);
commands.incr('product:1725:views').then(print);
commands.incr('product:4560:views').then(print);
// Flush pipeline
client.flush();
The method flush returns a list of Futures that can be used for waiting the completion of all the commands.
// Start pipeline
client.pipeline();
// Run some commands
commands
..incr('product:9238:views')
..incr('product:1725:views')
..incr('product:4560:views');
// Flush pipelinefinal futures = client.flush();
// Wait for all the FuturesawaitFuture.wait<Object>(futures).then(print);
Please note that in this mode await can not be used for waiting the result of the execution of each command because the returned Futures will not be completed until flush was called.
Fire and Forget
In this mode the server doesn't sent replies for the commands, so the client doesn't need to wait for them.
This mode is started running the clientReply command with ReplyMode.off or ReplyMode.skip.
In this mode the Futures are immediately completed with null.
// Discard all the server repliesawait commands.clientReply(ReplyMode.off);
// Run some commandsawait commands.ping().then(print); // nullawait commands.ping().then(print); // nullawait commands.ping().then(print); // null
The following modes are available:
ReplyMode.off: In this mode the server will not reply to client commands.
ReplyMode.skip: In this mode the server will skip the reply of command immediately after it.
ReplyMode.on: In this mode the server will return a reply to every command.
Transactions
Redis allows to group commands together so that they are executed as a single transaction.
A transaction begins running the multi command, ends running the exec command, and can be aborted running the discard command.
// Start transactionawait commands.multi();
// Run some commands
commands.set(key, 1).then(print);
commands.incr(key).then(print);
// End transactionawait commands.exec(); // Or abort: commands.discard()
The watch command can be used for perfoming optimistic lockings over some keys. A transaction will fail if the "watched" keys are modified by another client.
// Watchawait commands.watch(key: key);
// Start transactionawait commands.multi();
// Run some commands
commands.set(key, 1).then(print);
commands.incr(key).then(print);
// End transactionawait commands.exec();
Please note that in this mode await can not be used for waiting the result of the execution of each command because the returned Futures will not be completed until exec or discard were called.
Caveats
Don't run the clientReply command inside a transaction. If the "fire and forget" mode is de/activated inside a transaction then the client could go out of sync with the server.
Redis transactions are deprecated in favor of Lua scripting.
Lua scripting
Redis allows to run Lua scripts in the server.
Scripts can be executed with the eval and evalsha commands.
The result of a script can be anything. It can be ignored, like in the above example, or it can be mapped to a most useful thing.
// Maps a list of server replies to a list of Stringsclass_MapperimplementsMapper<List<String>> {
@overrideList<String> map(Reply reply, RedisCodec codec) =>
codec.decode<List<String>>(reply);
}
...
// Evaluate with a mapperfinal results = await commands.eval<List<String>>(
'return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}',
keys: [key1, key2],
args: ['first', 'second'],
mapper: _Mapper());
print(results); // ['key1', 'key2', 'first', 'second']
Custom Serializers/Deserializers
Encoders are used for serializing all the values sent to Redis. They convert instances of any type to list of bytes. Encoders for types int, double, String and List<int> are registered by default. UTF-8 is used for Strings.
Custom encoders can be written extending from Encoder or Converter.
Example:
An encoder that encodes instances of DateTime to lists of bytes:
Decoders are used for deserializing all the replies received from Redis. They convert list of bytes to instances of any type, and arrays of server replies to lists of instances of any type. Decoders for types int, double, String, List<int>, List<double>, List<Sring> and List<List<int>> are registered by default. UTF-8 is used for Strings.
Custom decoders can be written extending from Decoder or Converter.
Example:
A decoder that decodes lists of bytes to instances of DateTime.
Custom sets of commands can be written extending from ModuleBase. This class exposes the method run that sent to Redis any given line of commands, so it can be used for implementing the API of any Redis module.
final commands = TypedCommands<String>(client);
await commands.set<String>('name', 'Bob');
await commands.set<int>('age', 29);
await commands.set<List<int>>('photo', png);
final name = await commands.get<String>('name');
final age = await commands.get<int>('age');
final photo = await commands.get<List<int>>('photo');
Note that if a module works with a custom structure, like a record with multiple fields, then custom encoders and decoders should be used.
Log
The logging package is used for logging messages through a custom logger named 'dartis'.
Here is a simple logging configuration that logs all messages via print:
Set the log level according your needs. Most times, INFO is what you want. ALL is good for filling issues.
Testing
Dependencies of this packages can installed with pub get and test cases can
be run with pub run test. These test cases requires a redis running on
localhost:6379, for local development this can be created with docker:
docker run --rm -p 127.0.0.1:6379:6379 redis
This starts a container running redis and exposes port 6379 localhost, when
killed using ctrl+c the container will be deleted.