Pat is a streamlined, high-performance Java library built on top of Lettuce, designed to provide an effortless way to integrate Redis Pub/Sub functionality into your projects. With Pat, you can incorporate Redis listeners and efficiently send messages without the hassle of complex setup and implementation.
- Fluent API: Use
PatBuilderfor clean and readable configuration - Easy Pub/Sub: Simple methods for synchronous and asynchronous message publishing
- Annotation-based Subscriptions: Register listeners using the
@PatSubscribeannotation - Functional Subscriptions: Dynamically subscribe to channels using
Consumer<PatEvent> - Built-in Parsing: Integrated support for Gson and Protobuf to easily deserialize messages
- Compression Support: Built-in support for GZIP and DEFLATE compression
- Client Reuse: Support for both managed and external Redis clients
- Lightweight: Minimal overhead over Lettuce
dependencies {
implementation("com.fabiodm:pat:1.1.3")
}<dependency>
<groupId>com.fabiodm</groupId>
<artifactId>pat</artifactId>
<version>1.1.3</version>
</dependency>Use PatBuilder to let Pat create and manage the Redis connection:
import com.fabiodm.pat.PatBuilder;
import com.fabiodm.pat.api.PatClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.codec.CompressionCodec;
PatClient pat = PatBuilder.create(RedisURI.create("redis://localhost:6379"))
.withCompression(CompressionCodec.CompressionType.GZIP) // Optional
.build();
// Connect to the Redis server
pat.connect();If you already have a RedisClient instance, you can pass it to Pat:
import io.lettuce.core.RedisClient;
RedisClient existingClient = RedisClient.create("redis://localhost:6379");
PatClient pat = PatBuilder.fromClient(existingClient)
.withCompression(CompressionCodec.CompressionType.GZIP) // Optional
.build();
pat.connect();
// Note: When using an external client, shutdown() will NOT close the RedisClientPat supports both synchronous and asynchronous message publishing:
// Synchronous publishing
pat.send("my-channel", "Hello, Redis!");
pat.send("my-channel", new byte[]{0x01, 0x02, 0x03});
// Asynchronous publishing
pat.sendAsync("my-channel", "Async message");There are two ways to receive messages:
Annotate methods with @PatSubscribe and register the class instance:
public class MyListener {
@PatSubscribe("my-channel")
public void onMessage(PatEvent event) {
System.out.println("Received from " + event.channel() + ": " + event.messageAsString());
}
@PatSubscribe("other-channel")
public void onOtherMessage(PatEvent event) {
System.out.println("Another channel: " + event.messageAsString());
}
}
// Register the listener
MyListener listener = new MyListener();
pat.register(listener);Subscribe dynamically using a Consumer:
pat.subscribeToChannel(this, "dynamic-channel", event -> {
System.out.println("Dynamic message: " + event.messageAsString());
});Pat includes built-in parsers for common formats:
@PatSubscribe("json-channel")
public void onJsonEvent(PatEvent event) {
event.asGsonParser().asObject().ifPresent(jsonElement -> {
System.out.println("JSON: " + jsonElement);
});
}@PatSubscribe("proto-channel")
public void onProtoEvent(PatEvent event) {
event.asProtobufParser(MyProtoMessage.parser()).asObject().ifPresent(proto -> {
System.out.println("Proto name: " + proto.getName());
});
}@PatSubscribe("raw-channel")
public void onRawEvent(PatEvent event) {
String message = event.messageAsString();
byte[] rawBytes = event.message();
String channel = event.channel();
}Customize the underlying Lettuce ClientOptions:
import io.lettuce.core.ClientOptions;
import io.lettuce.core.TimeoutOptions;
import java.time.Duration;
ClientOptions options = ClientOptions.builder()
.autoReconnect(true)
.pingBeforeActivateConnection(true)
.timeoutOptions(TimeoutOptions.builder()
.fixedTimeout(Duration.ofSeconds(5))
.build())
.build();
PatClient pat = PatBuilder.create(RedisURI.create("redis://localhost:6379"))
.withClientOptions(options)
.build();Note: withClientOptions() cannot be used when passing an external RedisClient via fromClient().
Pat supports GZIP and DEFLATE compression for message payloads:
PatClient pat = PatBuilder.create(RedisURI.create("redis://localhost:6379"))
.withCompression(CompressionCodec.CompressionType.GZIP)
.build();Close the Pub/Sub connection while keeping the Redis client alive:
pat.disconnect();Fully shut down Pat and release all resources:
pat.shutdown();Important:
- If Pat created the Redis client (via
PatBuilder.create()),shutdown()will close the Redis client - If you passed an external Redis client (via
PatBuilder.fromClient()),shutdown()will only close Pat's connection, leaving your Redis client open for you to manage
import com.fabiodm.pat.PatBuilder;
import com.fabiodm.pat.api.PatClient;
import com.fabiodm.pat.annotation.PatSubscribe;
import com.fabiodm.pat.event.PatEvent;
import io.lettuce.core.RedisURI;
import io.lettuce.core.codec.CompressionCodec;
public class PatExample {
static void main(String[] args) {
// Create and connect
PatClient pat = PatBuilder.create(RedisURI.create("redis://localhost:6379"))
.withCompression(CompressionCodec.CompressionType.GZIP)
.build();
pat.connect();
// Register listener
pat.register(new MessageListener());
// Send messages
pat.send("notifications", "Hello from Pat!");
pat.sendAsync("events", "Async event");
// Cleanup
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
pat.disconnect();
pat.shutdown();
}));
}
static class MessageListener {
@PatSubscribe("notifications")
public void onNotification(PatEvent event) {
System.out.println("Notification: " + event.messageAsString());
}
@PatSubscribe("events")
public void onEvent(PatEvent event) {
event.asGsonParser().asObject().ifPresent(json -> {
System.out.println("Event data: " + json);
});
}
}
}- Java: 17 or higher
- Redis (Lettuce): 5.0 or higher
This project is licensed under the MIT License – see the LICENSE file for details.
Contributions are welcome! If you'd like to contribute:
- Fork the repository
- Create your feature branch (
git checkout -b feature/your-feature) - Commit your changes (
git commit -m 'Add your feature') - Push to the branch (
git push origin feature/your-feature) - Open a Pull Request
All contributions are appreciated!
Thanks to these awesome contributors:
- @StarlessDev – Library design, performance improvements and bug fixes
If you encounter any issues or have questions, please open an issue on the GitHub repository