Authcache构建响应式高性能网站解决方案

引子

安装了上百个模块之后的Drupal大型网站一般都会遇到性能问题,尤其是当网站流量开始增大,各种性能和稳定性方面的问题也随之出现。另一方面,我们既想充分适配移动端,又想在PC端保持特殊的设计,这时响应式设计主题出现在了我们的视野,但我觉得作为过渡,更好的方案是使用Mobile Switch模块只为移动端使用响应式设计主题。

本文结合Authcache模块和Mobile Switch模块来讨论一下这两个模块结合带来的好处以及实施办法。

为了获得Drupal高性能,我们需要在用户请求的各个环节使用缓存技术,这些缓存技术既有普遍意义上的解决方案,例如APC, Memcache, Varnish等等,也有根据Drupal自身特点,由第三方模块实现的缓存机制,例如Boost模块,Views Content Cache模块,Entity Cache模块等等。

大多数人都知道Boost在匿名用户缓存领域是非常优秀的选择,因为从Drupal启动阶段来讲,Boost通过重写(Rewrite)技术可以不进入Drupal即可识别和响应对应的页面缓存,但对登陆用户来讲就失效了。(当然这一点通过修改Boost核心代码,也可以让登陆用户使用Boost缓存,但不建议这么做,除非你的页面对所有登陆非登陆用户都是一样的,或者实现了对动态内容的Ajax加载),由于本文重点在Authcache和Mobile Switch模块的讨论,因此其他性能优化模块不展开讨论了。

什么是Authcache模块和Mobile Switch模块

Authcache模块用来解决对登陆用户的缓存,支持各种后端缓存媒介,默认建议是使用数据库后端(cache_page),因为比较好调试,也因为官方支持的最好,同时也支持Boost,Varnish,以及File Cache等机制。缓存是基于角色的,因此适用场景是相同URL对相同角色的不同用户内容是完全一样的。如果有所不同,需要利用Ajax或者ESI(Edge Side Includes)技术实现局部动态更新。

Mobile Switch模块可以识别当前浏览设备是桌面浏览器,平板电脑还是智能手机,并且支持针对移动设备使用另外一套Drupal主题。由于URL是一样的,所以我们内容必须继承,只是样式针对移动设备进行优化。

是否可以整合这两个模块

默认的这两个模块是不能一起工作的,同时开启的话,如果桌面浏览器先访问并生成了缓存,移动设备访问同样的地址将使用桌面浏览器生成的缓存,反过来,如果是移动设备先访问,生成的缓存就是响应式的缓存,这时桌面浏览器访问同样地址就会看到移动设备生成的缓存。

好在Authcache提供了HOOK,我们可以通过实现其HOOK,同时获得两个模块的特性。下面我们来介绍一下实施方法。

原理及参考代码

其实核心原理就是对于相同URL,要根据不同的设备生成不同的缓存主键。因此要实现其微调缓存主键的钩子,以下是在自定义模块中需要实现的代码。

<?php
/**
 * hook_authcache_key_properties
 *
 * 根据Mobile switch模块的检测结果添加mobile_detect key
 *
 * @param array $properties
 */
function HOOK_authcache_key_properties() {
  if (
function_exists('mobile_switch_mobile_detect')) {
   
$properties = array();
   
$mobile_detect = '';
   
$browser = mobile_switch_mobile_detect();
   
    if (
is_array($browser)) {
      if (
$browser['istablet']) {
       
$mobile_detect .= ':tablet';
      } elseif (
$browser['ismobiledevice']) {
       
$mobile_detect .= ':mobile';
      } else {
       
$mobile_detect .= ':standard';
      }
    }
   
   
$properties['mobile_detect'] = $mobile_detect;
    return
$properties;
  }
}

/**
 * hook_authcache_enum_key_properties
 *
 * 根据Mobile switch模块的检测结果添加mobile_detect key
 *
 * @param array $properties
 */
function HOOK_authcache_enum_key_properties() {
  if (
function_exists('mobile_switch_mobile_detect')) {
   
   
$properties['mobile_detect'] = array(
     
'name' => t('Mobile Detect'),
     
'choices' => array(':tablet', ':standard', ':mobile'),
    );

    return
$properties;
  }
}

/**
 * hook_authcache_enum_anonymous_keys_alter
 *
 * 枚举匿名用户缓存的几种可能的Key
 *
 * @global type $base_root
 * @param type $anonymous_keys
 */
function HOOK_authcache_enum_anonymous_keys_alter(&$anonymous_keys) {
  global
$base_root;
 
 
$anonymous_keys[] = $base_root . '/tablet';
 
$anonymous_keys[] = $base_root . '/mobile';
 
$anonymous_keys[] = $base_root . '/standard';
 
}
?>

仅仅实现了上面的几个钩子是不够的,还有一个匿名用户的缓存主键是在Drupal启动到第二步(DRUPAL_BOOTSTRAP_PAGE_CACHE)就已经完成了的,因此需要在前一步(DRUPAL_BOOTSTRAP_CONFIGURATION阶段)就要定义自定义主键生成器函数,以下示例代码需要写在settings.php里:

<?php
/**
 * define authcache_key_generator callback
 *
 * 至少要在DRUPAL_BOOTSTRAP_PAGE_CACHE及以后触发
 *
 */
function drupal_authcache_key_generator() {
  global
$base_root;
 
 
drupal_load('module', 'mobile_switch');
 
  if (
function_exists('mobile_switch_mobile_detect')) {
   
$browser = mobile_switch_mobile_detect();
   
$mobile_detect = '';
   
    if (
is_array($browser)) {
      if (
$browser['istablet']) {
       
$mobile_detect .= 'tablet';
      } elseif (
$browser['ismobiledevice']) {
       
$mobile_detect .= 'mobile';
      } else {
       
$mobile_detect .= 'standard';
      }
    }
   
    if (
$mobile_detect) {
      return
$base_root . '/' . $mobile_detect;
    }
   
  }
 
  return
$base_root;
}

# 启用Authcache自定义主键生成器
$conf['authcache_key_generator'] = 'drupal_authcache_key_generator';
?>

除此之外,我们如果想让Authcache机制生效,最重要的一点还是在settings.php里声明启用Authcache,这和Drupal7其他缓存机制是类似的,参考代码如下,以数据库后端为例,如果模块目录存放与本文不一致,需要修改路径。

<?php
$conf
['cache_backends'][] = 'sites/all/modules/contrib/authcache/authcache.cache.inc';
$conf['cache_backends'][] = 'sites/all/modules/contrib/authcache/modules/authcache_builtin/authcache_builtin.cache.inc';

// 默认推荐使用数据库缓存,如果使用了Memcache,并且内存非常大的话,也可以考虑放到Memcache里
$conf['cache_class_cache_page'] = 'DrupalDatabaseCache';
?>

Authcache模块自带了25个子模块,为了应用Authcache技术,我们要开启其中的10多个甚至更多。这里是需要按需启用的,至少要开启一个后端,为了实现局部动态显示需要用到Authcache Ajax或者Authcache ESI还有核心的Authcache Personalization API等子模块,为了调试你可能需要临时开启Authcache Debug子模块,为了在一些事件发生时(例如Flag,或者Vote)相应的页面的缓存能够失效,我们需要开启Cache Expiration和Authcache Builtin Cache Expiration v1/v2模块,为了让一些Drupal常用元素支持Authcache还可以按需开启自带的其他子模块。

总结

Authcache默认缓存主键生成机制是基于域名和角色的,本文为其加入了设备这一变量,如果有必要还可以考虑语言等其他变量,但如果把用户UID这一变量加入进去,就变成了为同一个URL的每个用户单独生成一份缓存,这种方法虽然也是可以尝试的,但比较不划算,可能会耗费大量的磁盘或者内存空间。

在两个模块的作用下,即实现了登陆用户的缓存,又实现了桌面浏览器和移动端分别使用不同的主题的目的,进而我们获得了性能和体验上的提升。