该楼层疑似违规已被系统折叠 隐藏此楼查看此楼
如果进程本身是无状态的,或者重启了也没有关系,那么用 ip:port 来标识一个“服务”是没问题的,比如常见的 httpd 和 memcached 都可以用它们的惯用 port (80 和 11211)来标识。我们可以在其他程序里安全地引用(refer to)“运行在 10.0.0.5:80 的那个 http 服务器”,或者“10.0.0.6:11211 的 memcached”,就算这两个 service 重启了,也不会有太恶劣的后果,大不了客户端重试一下,或者自动切换到备用地址。
如果服务是有状态的,那么 ip:port 这种标识方法就有大问题,因为客户端无法区分从头到尾和自己打交道的是一个进程还是先后多个进程。在开发服务端程序的时候,为了能快速重启,我们一般都会设置 SO_REUSEADDR,这样的结果是前一秒钟站在 10.0.0.7:8888 后面的进程和后一秒钟占据 10.0.0.7:8888 的进程可能不相同——服务端程序快速重启了。
比方说,考虑一个类似 GFS 的分布式文件系统的 master,如果它仅以 ip:port 来标识自己,然后它向 shadows (不是 chunk server)下达同步指令,那么 shadows 如何得知 master 是不是已经重启呢?发指令的是 master 的“前世”还是“今生”?是不是应该拒绝“前世”的遗命?
如果考虑改成 host:pid 这种标识方式会不会好一点?我认为换汤不换药,因为 pid 的状态空间很小,重复的概率比较大。比如 Linux 的 pid 的最大值是 32768 (/proc/sys/kernel/pid_max),一个程序重启之后,获得与“前世”相同 pid 的概率是 1/32768。或许有读者不相信重启之后 pid 会重复,因为 pid 是递增的,遇到上限再回到目前空闲的最小 pid。考虑一个服务端程序 A,它的 pid 是 1234,它已经稳定运行了好几天,这期间,pid 已经增长了几个轮回(因为这台机器时常会启动一些 scripts 执行一些辅助工作)。在 A 崩溃的前一刻,最近被使用的 pid 已经回到了 1232,当 A 崩溃之后,某个守护进程启动一个脚本(pid = 1233)来清理 A 的 log,然后再重启 A 程序;这样一来,重启之后的 A 程序的 pid 碰巧和它的前世相同,都是 1234。也就是说,用 host:pid 不能唯一标识进程。
那么合在一起,用 ip:port:pid 呢?也不能做到唯一。它和 host:pid 面临的问题是一样的,因为 ip:port 这部分在重启之后不会变,pid 可能轮回。