Hiredis
Cache Library
Hiredis
Overview
Hiredis is a lightweight and minimal C client library for Redis servers. Developed and maintained as part of the official Redis project, it achieves high performance and low memory footprint. It provides both synchronous and asynchronous APIs and enables efficient utilization of all Redis features through complete implementation of the Redis protocol.
Details
Hiredis is a library that enables applications written in C to communicate efficiently with Redis servers. Designed with minimal dependencies, it can be used in a wide range of environments from embedded systems to high-performance servers. It supports both synchronous blocking API and asynchronous API, allowing developers to choose appropriate communication methods according to their needs.
Key Features
- Synchronous API: Simple Redis operations using
redisConnect,redisCommand, andfreeReplyObject - Asynchronous API: High-performance non-blocking communication through integration with event loops
- Pipelining: Send multiple commands at once to minimize network round trips
- SSL/TLS Support: Encrypted communication support through OpenSSL integration
- Custom Memory Allocators: Customizable memory management
- Reply Parser API: Direct Redis protocol parsing functionality
- PUSH Reply Support: Support for PUSH replies in Redis 6.0 and later
Architecture
- redisContext: Manages synchronous connection and command execution
- redisAsyncContext: Manages asynchronous connection and callback-based processing
- redisReader: Responsible for Redis protocol parsing
- redisReply: Structure representing replies from Redis server
Pros and Cons
Pros
- High Performance: Optimized performance through C language implementation
- Lightweight: Minimal dependencies and memory footprint
- Official Support: Continuous development and maintenance as official Redis project
- Flexibility: Choice between synchronous/asynchronous APIs, custom memory allocator support
- Stability: Long-term track record and widespread adoption
- Completeness: Complete Redis protocol implementation enables use of all Redis features
Cons
- C Language Only: Direct use in other languages requires bindings
- Low-Level API: No high-level abstractions provided, requires direct Redis operations
- Error Handling: Manual error checking and proper memory management required
- Learning Curve: Requires knowledge of C language and Redis protocol
Reference Links
Code Examples
Basic Synchronous Connection and Command Execution
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
int main() {
// Connect to Redis server
redisContext *c = redisConnect("127.0.0.1", 6379);
if (c == NULL || c->err) {
if (c) {
printf("Error: %s\n", c->errstr);
redisFree(c);
} else {
printf("Can't allocate redis context\n");
}
return 1;
}
// Execute Redis command
redisReply *reply = redisCommand(c, "SET foo bar");
if (reply == NULL) {
printf("Error in redis command\n");
redisFree(c);
return 1;
}
freeReplyObject(reply);
// Retrieve data
reply = redisCommand(c, "GET foo");
if (reply->type == REDIS_REPLY_STRING) {
printf("Value: %s\n", reply->str);
}
freeReplyObject(reply);
// Clean up connection
redisFree(c);
return 0;
}
String Interpolation and Binary-Safe Data Processing
// Using string interpolation in commands
const char *key = "user:1001";
const char *value = "John Doe";
reply = redisCommand(c, "SET %s %s", key, value);
freeReplyObject(reply);
// Processing binary-safe data
const char *binary_data = "\x00\x01\x02\x03";
size_t data_len = 4;
reply = redisCommand(c, "SET binary_key %b", binary_data, data_len);
freeReplyObject(reply);
// Using multiple interpolations
const char *hash_key = "user:profile";
const char *field = "name";
reply = redisCommand(c, "HSET %s %s %s", hash_key, field, value);
freeReplyObject(reply);
Efficient Command Execution with Pipelining
#include <hiredis/hiredis.h>
void pipeline_example(redisContext *c) {
redisReply *reply;
// Send multiple commands in pipeline
redisAppendCommand(c, "SET key1 value1");
redisAppendCommand(c, "SET key2 value2");
redisAppendCommand(c, "GET key1");
redisAppendCommand(c, "GET key2");
// Retrieve responses sequentially
redisGetReply(c, (void**)&reply); // Result of SET key1
printf("SET key1: %s\n", reply->str);
freeReplyObject(reply);
redisGetReply(c, (void**)&reply); // Result of SET key2
printf("SET key2: %s\n", reply->str);
freeReplyObject(reply);
redisGetReply(c, (void**)&reply); // Result of GET key1
printf("GET key1: %s\n", reply->str);
freeReplyObject(reply);
redisGetReply(c, (void**)&reply); // Result of GET key2
printf("GET key2: %s\n", reply->str);
freeReplyObject(reply);
}
Asynchronous API Usage Example
#include <hiredis/hiredis.h>
#include <hiredis/async.h>
// Callback for command completion
void commandCallback(redisAsyncContext *c, void *reply, void *privdata) {
redisReply *r = reply;
if (reply == NULL) return;
printf("Command completed: %s\n", r->str);
// Note: Don't call freeReplyObject in async mode
}
// Callback for connection completion
void connectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Connection error: %s\n", c->errstr);
return;
}
printf("Connected\n");
}
// Callback for disconnection
void disconnectCallback(const redisAsyncContext *c, int status) {
if (status != REDIS_OK) {
printf("Disconnection error: %s\n", c->errstr);
return;
}
printf("Disconnected\n");
}
int async_example() {
redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);
if (c->err) {
printf("Error: %s\n", c->errstr);
redisAsyncFree(c);
return 1;
}
// Set callbacks
redisAsyncSetConnectCallback(c, connectCallback);
redisAsyncSetDisconnectCallback(c, disconnectCallback);
// Execute asynchronous commands
redisAsyncCommand(c, commandCallback, NULL, "SET foo bar");
redisAsyncCommand(c, commandCallback, NULL, "GET foo");
// Event loop execution required (libevent, libuv, etc.)
// Simplified in this example
return 0;
}
SSL/TLS Connection Example
#include <hiredis/hiredis.h>
#include <hiredis/hiredis_ssl.h>
int ssl_example() {
redisSSLContext *ssl_context;
redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE;
// Initialize OpenSSL
redisInitOpenSSL();
// Create SSL context
ssl_context = redisCreateSSLContext(
"ca-bundle.crt", // CA certificate file
"/path/to/certs", // Path to trusted certificates
"client-cert.pem", // Client certificate
"client-key.pem", // Client private key
"redis.example.com", // Server name (SNI)
&ssl_error
);
if (ssl_context == NULL || ssl_error != REDIS_SSL_CTX_NONE) {
printf("SSL context creation error\n");
return 1;
}
// Establish Redis connection
redisContext *c = redisConnect("localhost", 6443);
if (c == NULL || c->err) {
printf("Connection error\n");
return 1;
}
// SSL/TLS handshake
if (redisInitiateSSLWithContext(c, ssl_context) != REDIS_OK) {
printf("SSL handshake error: %s\n", c->errstr);
redisFree(c);
return 1;
}
// Execute commands over SSL connection
redisReply *reply = redisCommand(c, "PING");
printf("PING: %s\n", reply->str);
freeReplyObject(reply);
redisFree(c);
return 0;
}
Custom Memory Allocator Configuration
#include <hiredis/hiredis.h>
// Custom memory functions
void* my_malloc(size_t size) {
printf("Custom malloc: %zu bytes\n", size);
return malloc(size);
}
void my_free(void* ptr) {
printf("Custom free\n");
free(ptr);
}
void setup_custom_allocators() {
hiredisAllocFuncs custom_funcs = {
.mallocFn = my_malloc,
.callocFn = calloc, // Use standard calloc
.reallocFn = realloc, // Use standard realloc
.strdupFn = strdup, // Use standard strdup
.freeFn = my_free,
};
// Set custom allocators
hiredisAllocFuncs original = hiredisSetAllocators(&custom_funcs);
// Reset to original if needed
// hiredisSetAllocators(&original);
// Or reset to default
// hiredisResetAllocators();
}
Error Handling and Resource Management
int safe_redis_operation() {
redisContext *c = NULL;
redisReply *reply = NULL;
int result = 0;
// Establish connection
c = redisConnect("127.0.0.1", 6379);
if (c == NULL) {
printf("Out of memory\n");
result = -1;
goto cleanup;
}
if (c->err) {
printf("Connection error: %s\n", c->errstr);
result = -1;
goto cleanup;
}
// Execute command
reply = redisCommand(c, "SET test_key test_value");
if (reply == NULL) {
printf("Command execution error\n");
result = -1;
goto cleanup;
}
if (reply->type == REDIS_REPLY_ERROR) {
printf("Redis error: %s\n", reply->str);
result = -1;
goto cleanup;
}
printf("Operation successful\n");
cleanup:
if (reply) freeReplyObject(reply);
if (c) redisFree(c);
return result;
}