为失败而设计
为故障和失败做设计 先来看一个笑话: QA工程师走进酒吧,要了一杯啤酒,要了0杯啤酒,要了999999999杯啤酒,要了一只蜥蜴,要了-1杯啤酒,要了一个sfdeljknesv,酒保从容应对,QA工程师 很满意。接下来,一名顾客来到了同一个酒吧,问厕所在哪,酒吧顿时起了大火,然后整个建筑坍塌了。 事实上,无论我们事先做多么详尽的计划,我们还是会失败。而且大多数时候,失败、故障都会从一个我们无法预期的角度发生,令人猝不及防。 如果没有办法避免失败(事先要考虑到这么多的异常情况不太现实,而且会投入过多的精力),那么就需要设计某种机制,使得当发生这种失败时系统可以将损失降低到最小。 另一方面,系统需要具备从灾难中回复的能力。如果由于某种原因,服务进程意外终止了,那么一个watchdog机制就会非常有用,比如supervisord就可以用来保证进程意外终止之后的自动启动。 [program:jigsaw] command=java -jar /app/jigsaw.jar startsecs=0 stopwaitsecs=0 autostart=true autorestart=true stdout_logfile=/var/log/jigsaw/app.log stderr_logfile=/var/log/jigsaw/app.err 在现实世界中,设计一个无缺陷的系统显然是不可能的,但是通过努力,我们还是有可能设计出具有弹性,能够快速失败,从失败中恢复的系统来。 错误无可避免 令人担心的错误处理 我们先来看两个代码片段,两段代码都是在实现一个典型的Linux下的Socket服务器: int main (int argc, char *argv[]) { int serversock; struct sockaddr_in server; serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) setup_sockaddr(&server); bind(serversock, (struct sockaddr *) &server, sizeof(server)); listen(serversock, MAXPENDING) //... } 如果加上现实中可能出现的各种的处理,代码会变长一些: int main (int argc, char *argv[]) { int serversock; struct sockaddr_in server; if (argc != 2) { fprintf(stderr, "USAGE: server <port>\n"); exit(-1); } if ((serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { fprintf(stderr, "Failed to create socket\n"); exit(-1); } setup_sockaddr(&server); if (bind(serversock, (struct sockaddr *) &server, sizeof(server)) < 0) { fprintf(stderr, "Failed to bind the server socket\n"); exit(-1); } if (listen(serversock, MAXPENDING) < 0) { fprintf(stderr, "Failed to listen on server socket\n"); exit(-1); } //....