注意!PHP memcached扩展默认配置下无法自动failover

简介:
最近项目中用到PHP连接Memcache的场景。这个场景对于PHP开发者很常见。但就是这么常见的一个场景,带来了意想不到的一个问题。

我这边的php memcached扩展版本是:

memcached


MEMCACHED SUPPORT	 ENABLED
Version	                 2.1.0
libmemcached version	 1.0.8
Session support	         yes
igbinary support	 no
json support	         no

案例如下:
在本地服务器启动两个memcache实例,端口分别为1122,1123。PHP我使用的是MemcacheD扩展(注意,是D)。测试代码如下:


$memcache = new Memcached;
 
$memcache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcache->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memcache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
 
$memcache->setOption(Memcached::OPT_TCP_NODELAY, true); 
$memcache->setOption(Memcached::OPT_NO_BLOCK, true); 
$memcache->setOption(Memcached::OPT_CONNECT_TIMEOUT, 50);
$memcache->setOption(Memcached::OPT_SEND_TIMEOUT, 50);
$memcache->setOption(Memcached::OPT_RECV_TIMEOUT, 50);
$memcache->setOption(Memcached::OPT_POLL_TIMEOUT, 50);
$memcache->setOption(Memcached::OPT_HASH, Memcached::HASH_MD5);
 
$memcache->addServers(array(array("HOST" => "127.0.0.1", "PORT" => 1122),array("HOST" => "127.0.0.1", "PORT" => 1123));
 
$mKey = "test_bocheng11";
$status = 2;
$r = $memcache->set($mKey, $status,60*60*24);
$status1 =$memcache->get($mKey);
var_dump($status1);
$memcache->delete($mKey);

然后运行这个脚本。正常情况下可以返回
int(2)

这时候关掉实例1123,再次运行上面的代码,会返回
bool(false)

大家看到这里应该明白出了什么问题,我们上面这段代码无法自动的将fail的server踢出去。这将是一个非常大的隐患。
更麻烦的是,下面这段代码依然无法自动将fail的server去除(使用memcached扩展的默认配置)


$memcache = new Memcached;
$memcache->addServers(array(array("HOST" => "127.0.0.1", "PORT" => 1122),array("HOST" => "127.0.0.1", "PORT" => 1123));
 
$mKey = "test_bocheng11";
$status = 2;
$r = $memcache->set($mKey, $status,60*60*24);
$status1 =$memcache->get($mKey);
var_dump($status1);
$memcache->delete($mKey);

这段代码中我的key="test_bocheng11"是可以被hash到1122的实例上的,因此我们打开1123实例,关闭1122实例后,会发现返回的是
bool(false)

这个问题最终是看了看libmemcached的源码找到了一个解决方案(我当时的libmemcached的版本是1.0.8,也许高版本的libmemcached已经解决了这个问题)

如下的代码可以解决自动failover的问题?


$memcache = new Memcached;
$memcache->setOption(Memcached::OPT_DISTRIBUTION, Memcached::DISTRIBUTION_CONSISTENT);
$memcache->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
$memcache->setOption(Memcached::OPT_LIBKETAMA_COMPATIBLE, true);
 
//自动failover配置
/*$memcache->setOption(Memcached::OPT_SERVER_FAILURE_LIMIT, 1);
$memcache->setOption(Memcached::OPT_RETRY_TIMEOUT, 30);
$memcache->setOption(Memcached::OPT_AUTO_EJECT_HOSTS, true);
*/
//最新的libmemcached的文档中可以看到更建议使用如下的选项
$memcache->setOption(Memcached::OPT_REMOVE_FAILED_SERVERS,true);
 
 
$memcache->addServers(array(array("HOST" => "127.0.0.1", "PORT" => 1122),array("HOST" => "127.0.0.1", "PORT" => 1123));
 
$mKey = "test_bocheng11";
$status = 2;
$r = $memcache->set($mKey, $status,60*60*24);
$status1 =$memcache->get($mKey);
var_dump($status1);
$memcache->delete($mKey);

这时只要1122,1123有一个实例正常运行,就可以保证memcache的数据能够存取。
重点是第7--9行的参数设置,但要注意的是,这套参数配置必须配合DISTRIBUTION_CONSISTENT生效,对于memcached扩展默认的DISTRIBUTION_MODULA依然是无法实现failover的。从libmemcached源码中依稀也可以看到一些踪影。


memcached_return_t run_distribution(memcached_st *ptr)
{
  if (ptr->flags.use_sort_hosts)
  {
    sort_hosts(ptr);
  }
 
  switch (ptr->distribution)
  {
  case MEMCACHED_DISTRIBUTION_CONSISTENT:
  case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA:
  case MEMCACHED_DISTRIBUTION_CONSISTENT_KETAMA_SPY:
  case MEMCACHED_DISTRIBUTION_CONSISTENT_WEIGHTED:
    return update_continuum(ptr);
 
  case MEMCACHED_DISTRIBUTION_VIRTUAL_BUCKET:
  case MEMCACHED_DISTRIBUTION_MODULA:
    break;
 
  case MEMCACHED_DISTRIBUTION_RANDOM:
    srandom((uint32_t) time(NULL));
    break;
 
  case MEMCACHED_DISTRIBUTION_CONSISTENT_MAX:
  default:
    assert_msg(0, "Invalid distribution type passed to run_distribution()");
  }
 
  return MEMCACHED_SUCCESS;
}

这段代码中可以看到 默认的 MEMCACHED_DISTRIBUTION_MODULA 选项,不会触发 update_continuum 操作,而这个操作会影响到下面这个方法中是否重新选择host的判断


static inline void _regen_for_auto_eject(memcached_st *ptr)
{
  if (_is_auto_eject_host(ptr) && ptr->ketama.next_distribution_rebuild) //这个next_distribution_rebuild值会在update_continuum方法调用时被改变。
  {
    struct timeval now;
 
    if (gettimeofday(&now, NULL) == 0 and
        now.tv_sec -> ptr->ketama.next_distribution_rebuild)
    {
      run_distribution(ptr);
    }
  }
}
 
void memcached_autoeject(memcached_st *ptr)
{
  _regen_for_auto_eject(ptr);
}
 
uint32_t memcached_generate_hash_with_redistribution(memcached_st *ptr, const char *key, size_t key_length)
{
  uint32_t hash= _generate_hash_wrapper(ptr, key, key_length);
 
  _regen_for_auto_eject(ptr);
 
  return dispatch_host(ptr, hash);
}

update_continuum 部分代码摘要如下,可以看到判断server是否fail以及修改next_distribution_rebuild的部分。


if (is_auto_ejecting)
  {
    live_servers= 0;
    ptr->next_distribution_rebuild= 0;
    for (host_index= 0; host_index < memcached_server_count(ptr); ++host_index)
    {
      if (list[host_index].next_retry <= now.tv_sec)         live_servers++;       else       {         if (ptr->next_distribution_rebuild == 0 || list[host_index].next_retry < ptr->next_distribution_rebuild)
          ptr->next_distribution_rebuild= list[host_index].next_retry;
      }
    }
  }
  else
  {
    live_servers= memcached_server_count(ptr);
  }


目录
相关文章
|
3月前
|
运维 Unix Linux
Linux系统 PHP安装expect扩展详解
Linux系统 PHP安装expect扩展详解
41 5
|
4月前
|
关系型数据库 MySQL API
|
11天前
|
应用服务中间件 PHP nginx
php如何实现检测nginx配置的正确性
请确保在执行此操作时,PHP有足够的权限来执行Nginx命令和访问Nginx配置文件。另外,将上述代码嵌入到您的应用程序中时,要注意安全性,以防止潜在的命令注入攻击。
50 3
|
12天前
|
编译器 API PHP
深入PHP扩展开发:打造高效自定义模块
【4月更文挑战第30天】 在追求性能优化和特定功能实现的道路上,PHP提供了一种强大机制——扩展。本文将引导读者通过编写一个简单的PHP扩展来探索扩展开发的世界。我们将涉及从环境搭建到代码实现,再到扩展的编译与加载的完整流程,确保读者能够理解并实践如何创建高效的自定义PHP模块。
|
13天前
|
缓存 监控 PHP
【PHP开发专栏】Memcached在PHP中的缓存应用
【4月更文挑战第29天】Memcached是高性能分布式内存缓存系统,常用于加速动态Web应用,减轻数据库负担。在PHP中,通过官方扩展模块与Memcached服务器交互,涉及安装扩展、创建实例、设置/获取缓存、删除缓存及其它操作。使用Memcached可减少数据库负载、缓存查询结果、实现页面缓存,支持分布式缓存,并需注意避免缓存击穿、穿透和雪崩。监控和调优缓存策略能优化性能。了解和掌握Memcached有助于提升PHP应用的效率和扩展性。
|
14天前
|
运维 JavaScript Serverless
Serverless 应用引擎产品使用之在阿里函数计算中,Php环境,配置取消禁止函数exec如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
32 4
|
1月前
|
NoSQL Linux PHP
php添加redis扩展 linux和windos图文详解 l
php添加redis扩展 linux和windos图文详解 l
5 0
|
2月前
|
应用服务中间件 Linux PHP
Linux下安装php环境并且配置Nginx支持php-fpm模块
Linux下安装php环境并且配置Nginx支持php-fpm模块
34 0
|
4月前
|
PHP Windows
php扩展com_dndnet(PHP与.NET框架进行交互)
php扩展com_dndnet(PHP与.NET框架进行交互)
php扩展com_dndnet(PHP与.NET框架进行交互)