You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ji...@apache.org on 2016/11/04 17:04:28 UTC

svn commit: r1768070 - in /httpd/httpd/trunk/modules/cache: config.m4 mod_socache_redis.c mod_socache_redis.dsp

Author: jim
Date: Fri Nov  4 17:04:28 2016
New Revision: 1768070

URL: http://svn.apache.org/viewvc?rev=1768070&view=rev
Log:
And the socache provider interface for Redis

Added:
    httpd/httpd/trunk/modules/cache/mod_socache_redis.c
    httpd/httpd/trunk/modules/cache/mod_socache_redis.dsp
Modified:
    httpd/httpd/trunk/modules/cache/config.m4

Modified: httpd/httpd/trunk/modules/cache/config.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/config.m4?rev=1768070&r1=1768069&r2=1768070&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/config.m4 (original)
+++ httpd/httpd/trunk/modules/cache/config.m4 Fri Nov  4 17:04:28 2016
@@ -133,6 +133,7 @@ fi
 APACHE_MODULE(socache_shmcb,  shmcb small object cache provider, , , most)
 APACHE_MODULE(socache_dbm, dbm small object cache provider, , , most)
 APACHE_MODULE(socache_memcache, memcache small object cache provider, , , most)
+APACHE_MODULE(socache_redis, redis small object cache provider, , , most)
 APACHE_MODULE(socache_dc, distcache small object cache provider, , , no, [
     APACHE_CHECK_DISTCACHE
 ])

Added: httpd/httpd/trunk/modules/cache/mod_socache_redis.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_socache_redis.c?rev=1768070&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/cache/mod_socache_redis.c (added)
+++ httpd/httpd/trunk/modules/cache/mod_socache_redis.c Fri Nov  4 17:04:28 2016
@@ -0,0 +1,408 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+* contributor license agreements.  See the NOTICE file distributed with
+* this work for additional information regarding copyright ownership.
+* The ASF licenses this file to You under the Apache License, Version 2.0
+* (the "License"); you may not use this file except in compliance with
+* the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#include "httpd.h"
+#include "http_config.h"
+
+#include "apr.h"
+#include "apu_version.h"
+
+/* apr_redis support requires >= 1.6 */
+#if APU_MAJOR_VERSION > 1 || \
+    (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION > 5)
+#define HAVE_APU_REDIS 1
+#endif
+
+#ifdef HAVE_APU_REDIS
+
+#include "ap_socache.h"
+#include "ap_mpm.h"
+#include "http_log.h"
+#include "apr_redis.h"
+
+/* The underlying apr_redis system is thread safe.. */
+#define RD_KEY_LEN 254
+
+#ifndef RD_DEFAULT_SERVER_PORT
+#define RD_DEFAULT_SERVER_PORT 6379
+#endif
+
+
+#ifndef RD_DEFAULT_SERVER_MIN
+#define RD_DEFAULT_SERVER_MIN 0
+#endif
+
+#ifndef RD_DEFAULT_SERVER_SMAX
+#define RD_DEFAULT_SERVER_SMAX 1
+#endif
+
+#ifndef RD_DEFAULT_SERVER_TTL
+#define RD_DEFAULT_SERVER_TTL    apr_time_from_sec(15)
+#endif
+
+#ifndef RD_DEFAULT_SERVER_RWTO
+#define RD_DEFAULT_SERVER_RWTO    apr_time_from_sec(5)
+#endif
+
+module AP_MODULE_DECLARE_DATA socache_redis_module;
+
+typedef struct {
+    apr_uint32_t ttl;
+    apr_uint32_t rwto;
+} socache_rd_svr_cfg;
+
+struct ap_socache_instance_t {
+    const char *servers;
+    apr_redis_t *rc;
+    const char *tag;
+    apr_size_t taglen; /* strlen(tag) + 1 */
+};
+
+static const char *socache_rd_create(ap_socache_instance_t **context,
+                                     const char *arg,
+                                     apr_pool_t *tmp, apr_pool_t *p)
+{
+    ap_socache_instance_t *ctx;
+
+    *context = ctx = apr_palloc(p, sizeof *ctx);
+
+    if (!arg || !*arg) {
+        return "List of server names required to create redis socache.";
+    }
+
+    ctx->servers = apr_pstrdup(p, arg);
+
+    return NULL;
+}
+
+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)
+{
+    apr_status_t rv;
+    int thread_limit = 0;
+    apr_uint16_t nservers = 0;
+    char *cache_config;
+    char *split;
+    char *tok;
+
+    socache_rd_svr_cfg *sconf = ap_get_module_config(s->module_config, &socache_redis_module);
+
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &thread_limit);
+
+    /* Find all the servers in the first run to get a total count */
+    cache_config = apr_pstrdup(p, ctx->servers);
+    split = apr_strtok(cache_config, ",", &tok);
+    while (split) {
+        nservers++;
+        split = apr_strtok(NULL,",", &tok);
+    }
+
+    rv = apr_redis_create(p, nservers, 0, &ctx->rc);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                     "Failed to create Redis Object of '%d' size.",
+                     nservers);
+        return rv;
+    }
+
+    /* Now add each server to the redis */
+    cache_config = apr_pstrdup(p, ctx->servers);
+    split = apr_strtok(cache_config, ",", &tok);
+    while (split) {
+        apr_redis_server_t *st;
+        char *host_str;
+        char *scope_id;
+        apr_port_t port;
+
+        rv = apr_parse_addr_port(&host_str, &scope_id, &port, split, p);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                         "Failed to Parse redis Server: '%s'", split);
+            return rv;
+        }
+
+        if (host_str == NULL) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                         "Failed to Parse Server, "
+                         "no hostname specified: '%s'", split);
+            return APR_EINVAL;
+        }
+
+        if (port == 0) {
+            port = RD_DEFAULT_SERVER_PORT;
+        }
+
+        rv = apr_redis_server_create(p,
+                                     host_str, port,
+                                     RD_DEFAULT_SERVER_MIN,
+                                     RD_DEFAULT_SERVER_SMAX,
+                                     thread_limit,
+                                     sconf->ttl,
+                                     sconf->rwto,
+                                     &st);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                         "Failed to Create redis Server: %s:%d",
+                         host_str, port);
+            return rv;
+        }
+
+        rv = apr_redis_add_server(ctx->rc, st);
+        if (rv != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                         "Failed to Add redis Server: %s:%d",
+                         host_str, port);
+            return rv;
+        }
+
+        split = apr_strtok(NULL,",", &tok);
+    }
+
+    ctx->tag = apr_pstrcat(p, namespace, ":", NULL);
+    ctx->taglen = strlen(ctx->tag) + 1;
+
+    /* socache API constraint: */
+    AP_DEBUG_ASSERT(ctx->taglen <= 16);
+
+    return APR_SUCCESS;
+}
+
+static void socache_rd_destroy(ap_socache_instance_t *context, server_rec *s)
+{
+    /* noop. */
+}
+
+/* Converts (binary) id into a key prefixed by the predetermined
+ * namespace tag; writes output to key buffer.  Returns non-zero if
+ * the id won't fit in the key buffer. */
+static int socache_rd_id2key(ap_socache_instance_t *ctx,
+                             const unsigned char *id, unsigned int idlen,
+                             char *key, apr_size_t keylen)
+{
+    char *cp;
+
+    if (idlen * 2 + ctx->taglen >= keylen)
+        return 1;
+
+    cp = apr_cpystrn(key, ctx->tag, ctx->taglen);
+    ap_bin2hex(id, idlen, cp);
+
+    return 0;
+}
+
+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)
+{
+    char buf[RD_KEY_LEN];
+    apr_status_t rv;
+    apr_uint32_t timeout;
+
+    if (socache_rd_id2key(ctx, id, idlen, buf, sizeof(buf))) {
+        return APR_EINVAL;
+    }
+    timeout = apr_time_sec(expiry - apr_time_now());
+    if (timeout <= 0) {
+        return APR_EINVAL;
+    }
+
+    rv = apr_redis_setex(ctx->rc, buf, (char*)ucaData, nData, timeout, 0);
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO()
+                     "scache_rd: error setting key '%s' "
+                     "with %d bytes of data", buf, nData);
+        return rv;
+    }
+
+    return APR_SUCCESS;
+}
+
+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)
+{
+    apr_size_t data_len;
+    char buf[RD_KEY_LEN], *data;
+    apr_status_t rv;
+
+    if (socache_rd_id2key(ctx, id, idlen, buf, sizeof buf)) {
+        return APR_EINVAL;
+    }
+
+    /* ### this could do with a subpool, but _getp looks like it will
+     * eat memory like it's going out of fashion anyway. */
+
+    rv = apr_redis_getp(ctx->rc, p, buf, &data, &data_len, NULL);
+    if (rv) {
+        if (rv != APR_NOTFOUND) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
+                         "scache_rd: 'retrieve' FAIL");
+        }
+        return rv;
+    }
+    else if (data_len > *destlen) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s, APLOGNO()
+                     "scache_rd: 'retrieve' OVERFLOW");
+        return APR_ENOMEM;
+    }
+
+    memcpy(dest, data, data_len);
+    *destlen = data_len;
+
+    return APR_SUCCESS;
+}
+
+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)
+{
+    char buf[RD_KEY_LEN];
+    apr_status_t rv;
+
+    if (socache_rd_id2key(ctx, id, idlen, buf, sizeof buf)) {
+        return APR_EINVAL;
+    }
+
+    rv = apr_redis_delete(ctx->rc, buf, 0);
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, s, APLOGNO()
+                     "scache_rd: error deleting key '%s' ",
+                     buf);
+    }
+
+    return rv;
+}
+
+static void socache_rd_status(ap_socache_instance_t *ctx, request_rec *r, int flags)
+{
+    /* TODO: Make a mod_status handler. meh. */
+}
+
+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)
+{
+    return APR_ENOTIMPL;
+}
+
+static const ap_socache_provider_t socache_mc = {
+    "redis",
+    0,
+    socache_rd_create,
+    socache_rd_init,
+    socache_rd_destroy,
+    socache_rd_store,
+    socache_rd_retrieve,
+    socache_rd_remove,
+    socache_rd_status,
+    socache_rd_iterate,
+};
+
+#endif /* HAVE_APU_REDIS */
+
+static void* create_server_config(apr_pool_t* p, server_rec* s)
+{
+    socache_rd_svr_cfg *sconf = apr_palloc(p, sizeof(socache_rd_svr_cfg));
+
+    sconf->ttl = RD_DEFAULT_SERVER_TTL;
+    sconf->rwto = RD_DEFAULT_SERVER_RWTO;
+
+    return sconf;
+}
+
+static const char *socache_rd_set_ttl(cmd_parms *cmd, void *dummy,
+                                      const char *arg)
+{
+    apr_interval_time_t ttl;
+    socache_rd_svr_cfg *sconf = ap_get_module_config(cmd->server->module_config,
+                                                     &socache_redis_module);
+
+    if (ap_timeout_parameter_parse(arg, &ttl, "s") != APR_SUCCESS) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           " has wrong format", NULL);
+    }
+
+    if ((ttl < apr_time_from_sec(0)) || (ttl > apr_time_from_sec(3600))) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           " can only be 0 or up to one hour.", NULL);
+    }
+
+    /* apr_redis_server_create needs a ttl in usec. */
+    sconf->ttl = ttl;
+
+    return NULL;
+}
+
+static const char *socache_rd_set_rwto(cmd_parms *cmd, void *dummy,
+                                      const char *arg)
+{
+    apr_interval_time_t rwto;
+    socache_rd_svr_cfg *sconf = ap_get_module_config(cmd->server->module_config,
+                                                     &socache_redis_module);
+
+    if (ap_timeout_parameter_parse(arg, &rwto, "s") != APR_SUCCESS) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           " has wrong format", NULL);
+    }
+
+    if ((rwto < apr_time_from_sec(0)) || (rwto > apr_time_from_sec(3600))) {
+        return apr_pstrcat(cmd->pool, cmd->cmd->name,
+                           " can only be 0 or up to one hour.", NULL);
+    }
+
+    /* apr_redis_server_create needs a ttl in usec. */
+    sconf->rwto = rwto;
+
+    return NULL;
+}
+
+static void register_hooks(apr_pool_t *p)
+{
+#ifdef HAVE_APU_REDIS
+
+    ap_register_provider(p, AP_SOCACHE_PROVIDER_GROUP, "redis",
+                         AP_SOCACHE_PROVIDER_VERSION,
+                         &socache_mc);
+#endif
+}
+
+static const command_rec socache_redis_cmds[] =
+{
+    AP_INIT_TAKE1("RedisConnPoolTTL", socache_rd_set_ttl, NULL, RSRC_CONF,
+                      "TTL used for the connection pool with the Redis server(s)"),
+    AP_INIT_TAKE1("RedisTimeout", socache_rd_set_rwto, NULL, RSRC_CONF,
+                  "R/W timeout used for the connection with the Redis server(s)"),
+    {NULL}
+};
+
+AP_DECLARE_MODULE(socache_redis) = {
+    STANDARD20_MODULE_STUFF, 
+    NULL,                        /* create per-dir    config structures */
+    NULL,                        /* merge  per-dir    config structures */
+    create_server_config,        /* create per-server config structures */
+    NULL,                        /* merge  per-server config structures */
+    socache_redis_cmds,          /* table of config file commands       */
+    register_hooks               /* register hooks                      */
+};
+

Added: httpd/httpd/trunk/modules/cache/mod_socache_redis.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_socache_redis.dsp?rev=1768070&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/cache/mod_socache_redis.dsp (added)
+++ httpd/httpd/trunk/modules/cache/mod_socache_redis.dsp Fri Nov  4 17:04:28 2016
@@ -0,0 +1,111 @@
+# Microsoft Developer Studio Project File - Name="mod_socache_redis" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=mod_socache_redis - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_socache_redis.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "mod_socache_redis.mak" CFG="mod_socache_redis - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "mod_socache_redis - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "mod_socache_redis - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "mod_socache_redis - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MD /W3 /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "mod_socache_redis_EXPORTS" /FD /c
+# ADD CPP /nologo /MD /W3 /O2 /Oy- /Zi /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Release\mod_socache_redis_src" /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /fo"Release/mod_socache_redis.res" /i "../../include" /i "../../srclib/apr/include" /d "NDEBUG" /d BIN_NAME="mod_socache_redis.so" /d LONG_NAME="socache_redis_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Release\mod_socache_redis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_redis.so /opt:ref
+# Begin Special Build Tool
+TargetPath=.\Release\mod_socache_redis.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "mod_socache_redis - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MDd /W3 /EHsc /Zi /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /FD /c
+# ADD CPP /nologo /MDd /W3 /EHsc /Zi /Od /I "../../srclib/apr-util/include" /I "../../srclib/apr/include" /I "../../include" /D "_DEBUG" /D "WIN32" /D "_WINDOWS" /Fd"Debug\mod_socache_redis_src" /FD /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /fo"Debug/mod_socache_redis.res" /i "../../include" /i "../../srclib/apr/include" /d "_DEBUG" /d BIN_NAME="mod_socache_redis.so" /d LONG_NAME="socache_redis_module for Apache"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug
+# ADD LINK32 kernel32.lib /nologo /subsystem:windows /dll /incremental:no /debug /out:".\Debug\mod_socache_redis.so" /base:@..\..\os\win32\BaseAddr.ref,mod_socache_redis.so
+# Begin Special Build Tool
+TargetPath=.\Debug\mod_socache_redis.so
+SOURCE="$(InputPath)"
+PostBuild_Desc=Embed .manifest
+PostBuild_Cmds=if exist $(TargetPath).manifest mt.exe -manifest $(TargetPath).manifest -outputresource:$(TargetPath);2
+# End Special Build Tool
+
+!ENDIF 
+
+# Begin Target
+
+# Name "mod_socache_redis - Win32 Release"
+# Name "mod_socache_redis - Win32 Debug"
+# Begin Source File
+
+SOURCE=.\mod_socache_redis.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\build\win32\httpd.rc
+# End Source File
+# End Target
+# End Project