Apache HTTPD
mod_socache_redis.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2* contributor license agreements. See the NOTICE file distributed with
3* this work for additional information regarding copyright ownership.
4* The ASF licenses this file to You under the Apache License, Version 2.0
5* (the "License"); you may not use this file except in compliance with
6* the License. You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* Unless required by applicable law or agreed to in writing, software
11* distributed under the License is distributed on an "AS IS" BASIS,
12* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13* See the License for the specific language governing permissions and
14* limitations under the License.
15*/
16
17#include "httpd.h"
18#include "http_config.h"
19#include "http_protocol.h"
20
21#include "apr.h"
22#include "apu_version.h"
23
24#include "ap_socache.h"
25#include "ap_mpm.h"
26#include "http_log.h"
27#include "apr_strings.h"
28#include "mod_status.h"
29
34
35/* apr_redis support requires >= 1.6 */
36#if APU_MAJOR_VERSION > 1 || \
37 (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION > 5)
38#define HAVE_APU_REDIS 1
39#endif
40
41/* The underlying apr_redis system is thread safe.. */
42#define RD_KEY_LEN 254
43
44#ifndef RD_DEFAULT_SERVER_PORT
45#define RD_DEFAULT_SERVER_PORT 6379
46#endif
47
48
49#ifndef RD_DEFAULT_SERVER_MIN
50#define RD_DEFAULT_SERVER_MIN 0
51#endif
52
53#ifndef RD_DEFAULT_SERVER_SMAX
54#define RD_DEFAULT_SERVER_SMAX 1
55#endif
56
57#ifndef RD_DEFAULT_SERVER_TTL
58#define RD_DEFAULT_SERVER_TTL apr_time_from_sec(15)
59#endif
60
61#ifndef RD_DEFAULT_SERVER_RWTO
62#define RD_DEFAULT_SERVER_RWTO apr_time_from_sec(5)
63#endif
64
65module AP_MODULE_DECLARE_DATA socache_redis_module;
66
67#ifdef HAVE_APU_REDIS
68#include "apr_redis.h"
70 const char *servers;
72 const char *tag;
73 apr_size_t taglen; /* strlen(tag) + 1 */
74};
75
77 const char *arg,
78 apr_pool_t *tmp, apr_pool_t *p)
79{
81
82 *context = ctx = apr_pcalloc(p, sizeof *ctx);
83
84 if (!arg || !*arg) {
85 return "List of server names required to create redis socache.";
86 }
87
88 ctx->servers = apr_pstrdup(p, arg);
89
90 return NULL;
91}
92
94 const char *namespace,
95 const struct ap_socache_hints *hints,
97{
98 apr_status_t rv;
99 int thread_limit = 0;
101 char *cache_config;
102 char *split;
103 char *tok;
104
106 &socache_redis_module);
107
109
110 /* Find all the servers in the first run to get a total count */
111 cache_config = apr_pstrdup(p, ctx->servers);
112 split = apr_strtok(cache_config, ",", &tok);
113 while (split) {
114 nservers++;
115 split = apr_strtok(NULL,",", &tok);
116 }
117
118 rv = apr_redis_create(p, nservers, 0, &ctx->rc);
119 if (rv != APR_SUCCESS) {
121 "Failed to create Redis Object of '%d' size.",
122 nservers);
123 return rv;
124 }
125
126 /* Now add each server to the redis */
127 cache_config = apr_pstrdup(p, ctx->servers);
128 split = apr_strtok(cache_config, ",", &tok);
129 while (split) {
131 char *host_str;
132 char *scope_id;
134
135 rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
136 if (rv != APR_SUCCESS) {
138 "Failed to Parse redis Server: '%s'", split);
139 return rv;
140 }
141
142 if (host_str == NULL) {
144 "Failed to Parse Server, "
145 "no hostname specified: '%s'", split);
146 return APR_EINVAL;
147 }
148
149 if (port == 0) {
151 }
152
154 host_str, port,
158 sconf->ttl,
159 sconf->rwto,
160 &st);
161 if (rv != APR_SUCCESS) {
163 "Failed to Create redis Server: %s:%d",
164 host_str, port);
165 return rv;
166 }
167
168 rv = apr_redis_add_server(ctx->rc, st);
169 if (rv != APR_SUCCESS) {
171 "Failed to Add redis Server: %s:%d",
172 host_str, port);
173 return rv;
174 }
175
176 split = apr_strtok(NULL,",", &tok);
177 }
178
179 ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
180 ctx->taglen = strlen(ctx->tag) + 1;
181
182 /* socache API constraint: */
183 AP_DEBUG_ASSERT(ctx->taglen <= 16);
184
185 return APR_SUCCESS;
186}
187
189{
190 /* noop. */
191}
192
193/* Converts (binary) id into a key prefixed by the predetermined
194 * namespace tag; writes output to key buffer. Returns non-zero if
195 * the id won't fit in the key buffer. */
197 const unsigned char *id, unsigned int idlen,
198 char *key, apr_size_t keylen)
199{
200 char *cp;
201
202 if (idlen * 2 + ctx->taglen >= keylen)
203 return 1;
204
205 cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
206 ap_bin2hex(id, idlen, cp);
207
208 return 0;
209}
210
212 const unsigned char *id, unsigned int idlen,
213 apr_time_t expiry,
214 unsigned char *ucaData, unsigned int nData,
215 apr_pool_t *p)
216{
217 char buf[RD_KEY_LEN];
218 apr_status_t rv;
220
221 if (socache_rd_id2key(ctx, id, idlen, buf, sizeof(buf))) {
222 return APR_EINVAL;
223 }
224 timeout = apr_time_sec(expiry - apr_time_now());
225 if (timeout <= 0) {
226 return APR_EINVAL;
227 }
228
229 rv = apr_redis_setex(ctx->rc, buf, (char*)ucaData, nData, timeout, 0);
230
231 if (rv != APR_SUCCESS) {
233 "scache_rd: error setting key '%s' "
234 "with %d bytes of data", buf, nData);
235 return rv;
236 }
237
238 return APR_SUCCESS;
239}
240
242 const unsigned char *id, unsigned int idlen,
243 unsigned char *dest, unsigned int *destlen,
244 apr_pool_t *p)
245{
247 char buf[RD_KEY_LEN], *data;
248 apr_status_t rv;
249
250 if (socache_rd_id2key(ctx, id, idlen, buf, sizeof buf)) {
251 return APR_EINVAL;
252 }
253
254 /* ### this could do with a subpool, but _getp looks like it will
255 * eat memory like it's going out of fashion anyway. */
256
257 rv = apr_redis_getp(ctx->rc, p, buf, &data, &data_len, NULL);
258 if (rv) {
259 if (rv != APR_NOTFOUND) {
261 "scache_rd: 'retrieve' FAIL");
262 }
263 return rv;
264 }
265 else if (data_len > *destlen) {
267 "scache_rd: 'retrieve' OVERFLOW");
268 return APR_ENOMEM;
269 }
270
272 *destlen = data_len;
273
274 return APR_SUCCESS;
275}
276
278 const unsigned char *id,
279 unsigned int idlen, apr_pool_t *p)
280{
281 char buf[RD_KEY_LEN];
282 apr_status_t rv;
283
284 if (socache_rd_id2key(ctx, id, idlen, buf, sizeof buf)) {
285 return APR_EINVAL;
286 }
287
288 rv = apr_redis_delete(ctx->rc, buf, 0);
289
290 if (rv != APR_SUCCESS) {
292 "scache_rd: error deleting key '%s' ",
293 buf);
294 }
295
296 return rv;
297}
298
300{
301 apr_redis_t *rc = ctx->rc;
302 int i;
303
304 for (i = 0; i < rc->ntotal; i++) {
307 char *role;
308 apr_status_t rv;
309 char *br = (!(flags & AP_STATUS_SHORT) ? "<br />" : "");
310
311 rs = rc->live_servers[i];
312
313 ap_rprintf(r, "Redis server: %s:%d [%s]%s\n", rs->host, (int)rs->port,
314 (rs->status == APR_RC_SERVER_LIVE) ? "Up" : "Down",
315 br);
316 rv = apr_redis_stats(rs, r->pool, &stats);
317 if (rv != APR_SUCCESS)
318 continue;
319 if (!(flags & AP_STATUS_SHORT)) {
320 ap_rprintf(r, "<b>General::</b> Version: <i>%u.%u.%u</i> [%u bits], PID: <i>%u</i>, Uptime: <i>%u hrs</i> <br />\n",
321 stats->major, stats->minor, stats->patch, stats->arch_bits,
322 stats->process_id, stats->uptime_in_seconds/3600);
323 ap_rprintf(r, "<b>Clients::</b> Connected: <i>%d</i>, Blocked: <i>%d</i> <br />\n",
324 stats->connected_clients, stats->blocked_clients);
325 ap_rprintf(r, "<b>Memory::</b> Total: <i>%" APR_UINT64_T_FMT "</i>, Max: <i>%" APR_UINT64_T_FMT "</i>, Used: <i>%" APR_UINT64_T_FMT "</i> <br />\n",
326 stats->total_system_memory, stats->maxmemory, stats->used_memory);
327 ap_rprintf(r, "<b>CPU::</b> System: <i>%u</i>, User: <i>%u</i><br />\n",
328 stats->used_cpu_sys, stats->used_cpu_user );
329 ap_rprintf(r, "<b>Connections::</b> Recd: <i>%" APR_UINT64_T_FMT "</i>, Processed: <i>%" APR_UINT64_T_FMT "</i>, Rejected: <i>%" APR_UINT64_T_FMT "</i> <br />\n",
330 stats->total_connections_received, stats->total_commands_processed,
331 stats->rejected_connections);
332 ap_rprintf(r, "<b>Cache::</b> Hits: <i>%" APR_UINT64_T_FMT "</i>, Misses: <i>%" APR_UINT64_T_FMT "</i> <br />\n",
333 stats->keyspace_hits, stats->keyspace_misses);
334 ap_rprintf(r, "<b>Net::</b> Input bytes: <i>%" APR_UINT64_T_FMT "</i>, Output bytes: <i>%" APR_UINT64_T_FMT "</i> <br />\n",
335 stats->total_net_input_bytes, stats->total_net_output_bytes);
336 if (stats->role == APR_RS_SERVER_MASTER)
337 role = "master";
338 else if (stats->role == APR_RS_SERVER_SLAVE)
339 role = "slave";
340 else
341 role = "unknown";
342 ap_rprintf(r, "<b>Misc::</b> Role: <i>%s</i>, Connected Slaves: <i>%u</i>, Is Cluster?: <i>%s</i> \n",
343 role, stats->connected_clients,
344 (stats->cluster_enabled ? "yes" : "no"));
345 ap_rputs("<hr><br />\n", r);
346 }
347 else {
348 ap_rprintf(r, "Version: %u.%u.%u [%u bits], PID: %u, Uptime: %u hrs %s\n",
349 stats->major, stats->minor, stats->patch, stats->arch_bits,
350 stats->process_id, stats->uptime_in_seconds/3600, br);
351 ap_rprintf(r, "Clients:: Connected: %d, Blocked: %d %s\n",
352 stats->connected_clients, stats->blocked_clients, br);
353 ap_rprintf(r, "Memory:: Total: %" APR_UINT64_T_FMT ", Max: %" APR_UINT64_T_FMT ", Used: %" APR_UINT64_T_FMT " %s\n",
354 stats->total_system_memory, stats->maxmemory, stats->used_memory,
355 br);
356 ap_rprintf(r, "CPU:: System: %u, User: %u %s\n",
357 stats->used_cpu_sys, stats->used_cpu_user , br);
358 ap_rprintf(r, "Connections:: Recd: %" APR_UINT64_T_FMT ", Processed: %" APR_UINT64_T_FMT ", Rejected: %" APR_UINT64_T_FMT " %s\n",
359 stats->total_connections_received, stats->total_commands_processed,
360 stats->rejected_connections, br);
361 ap_rprintf(r, "Cache:: Hits: %" APR_UINT64_T_FMT ", Misses: %" APR_UINT64_T_FMT " %s\n",
362 stats->keyspace_hits, stats->keyspace_misses, br);
363 ap_rprintf(r, "Net:: Input bytes: %" APR_UINT64_T_FMT ", Output bytes: %" APR_UINT64_T_FMT " %s\n",
364 stats->total_net_input_bytes, stats->total_net_output_bytes, br);
365 if (stats->role == APR_RS_SERVER_MASTER)
366 role = "master";
367 else if (stats->role == APR_RS_SERVER_SLAVE)
368 role = "slave";
369 else
370 role = "unknown";
371 ap_rprintf(r, "Misc:: Role: %s, Connected Slaves: %u, Is Cluster?: %s %s\n",
372 role, stats->connected_clients,
373 (stats->cluster_enabled ? "yes" : "no"), br);
374 }
375 }
376
377}
378
386
399
400#endif /* HAVE_APU_REDIS */
401
403{
405
408
409 return sconf;
410}
411
412static const char *socache_rd_set_ttl(cmd_parms *cmd, void *dummy,
413 const char *arg)
414{
416 socache_rd_svr_cfg *sconf = ap_get_module_config(cmd->server->module_config,
417 &socache_redis_module);
418
420 return apr_pstrcat(cmd->pool, cmd->cmd->name,
421 " has wrong format", NULL);
422 }
423
424 if ((ttl < apr_time_from_sec(0)) || (ttl > apr_time_from_sec(3600))) {
425 return apr_pstrcat(cmd->pool, cmd->cmd->name,
426 " can only be 0 or up to one hour.", NULL);
427 }
428
429 /* apr_redis_server_create needs a ttl in usec. */
430 sconf->ttl = ttl;
431
432 return NULL;
433}
434
435static const char *socache_rd_set_rwto(cmd_parms *cmd, void *dummy,
436 const char *arg)
437{
439 socache_rd_svr_cfg *sconf = ap_get_module_config(cmd->server->module_config,
440 &socache_redis_module);
441
443 return apr_pstrcat(cmd->pool, cmd->cmd->name,
444 " has wrong format", NULL);
445 }
446
447 if ((rwto < apr_time_from_sec(0)) || (rwto > apr_time_from_sec(3600))) {
448 return apr_pstrcat(cmd->pool, cmd->cmd->name,
449 " can only be 0 or up to one hour.", NULL);
450 }
451
452 /* apr_redis_server_create needs a ttl in usec. */
453 sconf->rwto = rwto;
454
455 return NULL;
456}
457
459{
460#ifdef HAVE_APU_REDIS
461
464 &socache_mc);
465#endif
466}
467
469{
470 AP_INIT_TAKE1("RedisConnPoolTTL", socache_rd_set_ttl, NULL, RSRC_CONF,
471 "TTL used for the connection pool with the Redis server(s)"),
473 "R/W timeout used for the connection with the Redis server(s)"),
474 {NULL}
475};
476
479 NULL, /* create per-dir config structures */
480 NULL, /* merge per-dir config structures */
481 create_server_config, /* create per-server config structures */
482 NULL, /* merge per-server config structures */
483 socache_redis_cmds, /* table of config file commands */
484 register_hooks /* register hooks */
485};
486
Apache Multi-Processing Module library.
Small object cache provider interface.
apr_redis_delete(apr_redis_t *rc, const char *key, apr_uint32_t timeout)
Definition apr_redis.c:1005
apr_redis_stats(apr_redis_server_t *rs, apr_pool_t *p, apr_redis_stats_t **stats)
Definition apr_redis.c:1488
Client interface for redis.
APR Strings library.
APR-util Versioning Interface.
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define ap_get_module_config(v, m)
#define AP_DECLARE_MODULE(foo)
request_rec * r
#define APLOGNO(n)
Definition http_log.h:117
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_CRIT
Definition http_log.h:66
#define APLOG_DEBUG
Definition http_log.h:71
const unsigned char * buf
Definition util_md5.h:50
apr_md5_ctx_t * context
Definition util_md5.h:58
int ap_rprintf(request_rec *r, const char *fmt,...) __attribute__((format(printf
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
apr_status_t ap_register_provider(apr_pool_t *pool, const char *provider_group, const char *provider_name, const char *provider_version, const void *provider)
Definition provider.c:35
const char apr_port_t port
Definition http_vhost.h:125
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
#define APR_ENOMEM
Definition apr_errno.h:683
#define APR_ENOTIMPL
Definition apr_errno.h:476
#define APR_NOTFOUND
Definition apr_errno.h:463
#define APR_EINVAL
Definition apr_errno.h:711
apr_brigade_flush void * ctx
const char apr_ssize_t int flags
Definition apr_encode.h:168
const char const apr_size_t data_len
apr_pool_t apr_memcache_stats_t ** stats
apr_redis_t * rc
Definition apr_redis.h:173
apr_redis_server_t * rs
Definition apr_redis.h:205
const char apr_port_t apr_uint32_t apr_uint32_t apr_uint32_t apr_uint32_t apr_uint32_t rwto
Definition apr_redis.h:237
@ APR_RS_SERVER_MASTER
Definition apr_redis.h:375
@ APR_RS_SERVER_SLAVE
Definition apr_redis.h:376
@ APR_RC_SERVER_LIVE
Definition apr_redis.h:66
apr_status_t() ap_socache_iterator_t(ap_socache_instance_t *instance, server_rec *s, void *userctx, const unsigned char *id, unsigned int idlen, const unsigned char *data, unsigned int datalen, apr_pool_t *pool)
Definition ap_socache.h:77
#define AP_SOCACHE_PROVIDER_GROUP
Definition ap_socache.h:218
#define AP_SOCACHE_PROVIDER_VERSION
Definition ap_socache.h:220
#define RSRC_CONF
#define AP_STATUS_SHORT
Definition mod_status.h:32
#define STANDARD20_MODULE_STUFF
void ap_bin2hex(const void *src, apr_size_t srclen, char *dest)
Definition util.c:2314
#define AP_DEBUG_ASSERT(exp)
Definition httpd.h:2283
apr_status_t ap_timeout_parameter_parse(const char *timeout_parameter, apr_interval_time_t *timeout, const char *default_time_unit)
Definition util.c:2622
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
apr_byte_t ttl
apr_uint16_t apr_port_t
char ** scope_id
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
const char * s
Definition apr_strings.h:95
apr_cmdtype_e cmd
apr_int64_t apr_interval_time_t
Definition apr_time.h:55
apr_int64_t apr_time_t
Definition apr_time.h:45
#define apr_time_sec(time)
Definition apr_time.h:63
#define apr_time_from_sec(sec)
Definition apr_time.h:78
apr_status_t ap_mpm_query(int query_code, int *result)
Definition mpm_common.c:421
#define AP_MPMQ_HARD_LIMIT_THREADS
Definition ap_mpm.h:158
Apache Configuration.
Apache Logging library.
HTTP protocol handling.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
sconf
Definition mod_so.c:349
static void socache_rd_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
static void * create_server_config(apr_pool_t *p, server_rec *s)
static apr_status_t socache_rd_init(ap_socache_instance_t *ctx, const char *namespace, const struct ap_socache_hints *hints, server_rec *s, apr_pool_t *p)
static apr_status_t socache_rd_remove(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, apr_pool_t *p)
static int socache_rd_id2key(ap_socache_instance_t *ctx, const unsigned char *id, unsigned int idlen, char *key, apr_size_t keylen)
#define RD_DEFAULT_SERVER_SMAX
#define RD_DEFAULT_SERVER_TTL
static apr_status_t socache_rd_retrieve(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, unsigned char *dest, unsigned int *destlen, apr_pool_t *p)
#define RD_DEFAULT_SERVER_MIN
static const ap_socache_provider_t socache_mc
static void socache_rd_destroy(ap_socache_instance_t *context, server_rec *s)
static void register_hooks(apr_pool_t *p)
static const char * socache_rd_set_rwto(cmd_parms *cmd, void *dummy, const char *arg)
#define RD_DEFAULT_SERVER_RWTO
#define RD_DEFAULT_SERVER_PORT
static const command_rec socache_redis_cmds[]
static const char * socache_rd_set_ttl(cmd_parms *cmd, void *dummy, const char *arg)
#define RD_KEY_LEN
static apr_status_t socache_rd_iterate(ap_socache_instance_t *instance, server_rec *s, void *userctx, ap_socache_iterator_t *iterator, apr_pool_t *pool)
static apr_status_t socache_rd_store(ap_socache_instance_t *ctx, server_rec *s, const unsigned char *id, unsigned int idlen, apr_time_t expiry, unsigned char *ucaData, unsigned int nData, apr_pool_t *p)
static const char * socache_rd_create(ap_socache_instance_t **context, const char *arg, apr_pool_t *tmp, apr_pool_t *p)
static int thread_limit
Definition mod_status.c:89
Status Report Extension Module to Apache.
apr_redis_server_status_t status
Definition apr_redis.h:79
const char * host
Definition apr_redis.h:77
apr_port_t port
Definition apr_redis.h:78
apr_uint16_t ntotal
Definition apr_redis.h:124
apr_redis_server_t ** live_servers
Definition apr_redis.h:125
A structure that represents the current request.
Definition httpd.h:845
apr_pool_t * pool
Definition httpd.h:847
A structure to store information for each virtual server.
Definition httpd.h:1322
IN ULONG IN INT timeout
static size_t keylen(KEY s)
Definition xmlparse.c:7166