欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

Drupal自定义缓存之共享内存

对于高性能、高并发软件架构中一个重要的优化策略就是,能在内存中处理的决不能放到外存中。这里外存包括:网络I/O,DB,硬盘。
对于一个drupal站点,我们都要使用到缓存,前文《drupal性能优化经验贴》中也谈到,必须使用cache。
一般情况下,我们使用memcache,大型站点,使用分布式memcache,并且memcache常常和Application不在同一台服务器上面。这就会带来一个问题,内部网络IO。对于小站点可能没有什么问题,但是对于一个大型站点,这个问题很严重。
因为drupal的某些缓存比较大,比如theme-registry, content-type-info, locale翻译信息,对于一个较复杂的站点,这几个缓存加起来至少1M以上,大到3-5M。如果一个request需要3M的缓存,那么1000request需要3G的流量,这样即便是内网的IO,也会造成很大的瓶颈。(笔者曾经就遇到这样的问题,所以这里就是对这个问题的解决方案的分享)。

如何解决呢,就是将某些缓存,再缓存到本机,这样就会省去网络IO,降低了网络瓶颈。
缓存到本机,我们可以使用本机文件缓存,比如缓存到tmp下面。但是按照我们的原则: 能在内存中处理的决不能放到外存中,我们可以把这缓存放到内存中(前提是硬件服务器内存比较大,现在一般较好的服务器可以达到8G,以至于20,30G,这样内存完全可以拿出1G去缓存这些内容)。

为了方便,我们可以写一个接口函数,去封装缓存动作,可以缓存到APC(内存共享)、文件。这里我们重点选择文件夹,因为文件夹正常是缓存在磁盘上,但是我们可以把这个文件夹挂载到内存上。如果内存不够可以取消内存挂载,这样操作起来比较灵活,也不需要修改任何代码。

200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
define('V_CACHE_FILE', 1);
 
/**
 *@important Create the cache folder to store the cache data firstly
 
   #create memcache local cache folder
   mkdir memcache ; sudo chown nginx:nginx memcache
 
 */
function v_cache_file_dir() {
  $path = dirname($_SERVER['SCRIPT_FILENAME']);
  #$dir  = rtrim($path, '/') .'/'. trim(file_directory_temp(), '/') .'/memcache';
  $dir  = rtrim($path, '/') .'/memcache';
 
  if($_GET['debug'] == 'test-cache') {
    drupal_set_message($dir. '@DIR');
  }
  if(!is_dir($dir)) {
    #mkdir($dir);
    throw new Exception('Create local directory for cache, ' .$dir);
  }
  return $dir;
}
 
function v_cache_file_set($key, $value) {
  $dir = v_cache_file_dir();
  $file = "$key.ser";
  if($_GET['debug'] == 'test-cache') {
    drupal_set_message($key. '@FILE_CACHE_SET');
  }
  file_put_contents($dir .'/'. $file, serialize($value));
}
 
function v_cache_file_get($key) {
  $dir = v_cache_file_dir();
 
  $file = "$key.ser";
  $file = $dir .'/'. $file;
  if(file_exists($file)) {
     if($_GET['debug'] == 'test-cache') {
      drupal_set_message($key.'@FILE_CACHE_GET');
     }
    return unserialize(file_get_contents($file));
  }
  else {
    return false;
  }
}
 
function v_cache_encode_key($key) {
  $address = $_SERVER['SERVER_ADDR'];
  //$key     = str_ireplace(':', '-', $key);
  $key = "$key:$address";
  return $key;
}
 
function v_cache_set($key, $value, $table = 'cache', $expire = CACHE_PERMANENT) {
  $key_mem = v_cache_encode_key($key);
  if (function_exists('apc_cache_info')) {
    apc_store($key, $value, 60*60);//1 hour
    cache_set($key_mem, time(), $table, $expire);
  }
  else if (V_CACHE_FILE) {
    v_cache_file_set($key, $value);//1 hour
    cache_set($key_mem, time(), $table, $expire);
  }
  else {
    cache_set($key, $value, $table, $expire);
  }
}
 
function v_cache_get($key, $table = 'cache') {
  if (function_exists('apc_cache_info') || VIA_CACHE_FILE) {
    static $static;
    $ret = false;
  if (isset($static[$key])) {
    $ret = $static[$key];
  }
  else {
   $key_mem   = via_cache_encode_key($key);
   if(cache_get($key_mem, $table)) {
     $cache_callback = function_exists('apc_cache_info') ? 'apc_fetch' : 'v_cache_file_get';
     $value = $cache_callback($key);
     if($value) {
       $cache = new stdClass;
       $cache->data = $value;
       $static[$key] = $cache;
       $ret = $cache;
     }
   }
  }
  return $ret;
 }
 else {
   return cache_get($key, $table);
  }
}
 
function v_cache_clear_all($cid = NULL, $table = NULL, $wildcard = FALSE) {
  if ($cid) {
    cache_clear_all($cid, 'cache', TRUE);
    //apc_clear_cache('user');
  }
}

添加了上面到代码之后,我们需要修改一些drupal核心code,去调用我们自己定义的cache方法,重点关注下面几个cache:

  • common.inc 里面的shema,函数: drupal_get_schema里面调用的cache方法。

  • theme.inc 里面的theme-registry,函数:_theme_save_registry。

  • locale.module里面的locale函数,修改里面的cache方法。

  • content.module里面的content-type-info, 函数:_content_type_info。

因为以上信息缓存一次,修改机会比较小,所以缓存到本机是比较理想的做法。
还有一个需要注意的,上面的缓存虽然存到的本机,但是cache的key还是存到了drupal正常的cache里面,所以当清除drupal的cache之后,这些key消失,通过这样的方法,可以实现对cache清除的操作控制。

当然,除了修改缓存的操作,还需要一个清除缓存的操作,我们可以通过实现相关hook来操作。
这个修改是需要修改drupal核心代码的,所以请谨慎使用。

来自  http://www.drupal001.com/2011/10/drupal-custom-cache-tips/

普通分类: