redis的pconnect与connect连接

From : http://www.v2ex.com/t/95635

 

Short Version: 

如果你的应用/服务, 可以有独立的进程, 使用自己的内存, 就可以放心地用 pconnect. 

=============== 

Story Version: 

几个月前, 同事遇到了这么一个 pconnect on php 的坑: 

一台机器上跑了俩 HTTP 服务, 分别连接了同一个 Redis 服务器, 使用了的两个 DB. 

DB 的结构一样, 但内容不一样. 

HTTP 环境用的是 Apache + mod_PHP. 

服务 A: 

``` 
$con = pconnect(...); 

$con->set(BLAH_KEY, ...); 
$con->set(ANOTHER_KEY, ...); 

$resp->send($con->get(SOME_KEY)); 
``` 

服务 B: 

``` 
$con = pconnect(...); 
$con->select(2); 

$con->set(BLAH_KEY, ...); 
$con->set(ANOTHER_KEY, ...); 

$resp->send($con->get(SOME_KEY)); 
``` 

俩服务都返回各自 DB 中 SOME_KEY 的结果. 

---- 

A, B服务启动后. 

1) 连续访问 A, 返回正常 
2) 再连续访问 B, 返回正常 
3) 再访问 A, 返回的结果都是 B 的. 

---- 

由于没空细研究 PHP 的 Redis 驱动是怎么写的, 所以当它是个黑盒子吧. 

所以猜了一下原因: 

驱动模块被 Apache 加载之后, 一直留在内存里. 
当使用了 pconnect 后, 驱动将保留这个连接, 和在这个连接上操作过的状态(比如 select), 以备下次使用. 

这就导致了 A 的代码中, 由于没有使用 select, 在访问 B 之后, A 中的 Redis 连接还在 B 的 DB 上. 

同时, 由于 A, B 代码中对 Redis 的操作不是原子的 (虽然很快), 所以仅仅在 A 上使用 select 也是不安全的. 

---- 

结果就是: 

把 A, B 代码里的 pconnect, 改成 connect. 问题解决. 

---- 

也许还可以用的其他解决方法, 来让 pconnect 工作正常(没试): 

让驱动认为这是两个不同的 Redis 服务: 

-- 再启动一个 Redis 服务, 另外占用一个端口. 
-- 做端口转发, 或者本机做一个 TCP 代理. 
-- 分配给它不同的 hostname (虽然各 hostname 指向同一个IP), 使驱动保存两个 pconnect 产生的连接. 

---- 

或许, 驱动层更应该做好这些东西, 比如: 按 HOST/PORT/DB 来保留 pconnect 得到的连接.

posted on 2016-05-12 22:26  阿卡贝拉  阅读(457)  评论(0)    收藏  举报