如果你使用ZeroRPC;
如果你買不起服務器或者拿不到服務器用;
如果你決定把好多服務都放在一個物理主機上跑;
那你很有可能會遇到這個問題,當你的server進程因為某些意外掛了,而client還在不斷有請求過來時,你會發(fā)現(xiàn)有一定可能server無法重新啟動,理由是端口已被占用,看一下就會發(fā)覺被client給占了。這很氣人,如果不把client全殺了server就起不來,而server起不來client就會一直卡著端口,重啟client又不一定那么容易。
真實環(huán)境下這個問題真的坑到我一次,于是我決定花點時間解決一下這個問題,所以就做了個workaround。
原理其實很簡單,如果client發(fā)現(xiàn)心跳包發(fā)不出去或者remote超時了,就把自己的連接給關了,下次再調用的時候重新連接。
多數(shù)情況下并不需要這套機制,重新建立tcp連接帶來的開銷是不小的,如果在線上環(huán)境請謹慎使用。
[python client]
import zerorpc
class RPCProxy(object):
def __init__(self, connect_to=None, context=None, timeout=30, heartbeat=5,
passive_heartbeat=False):
self._client = zerorpc.Client(connect_to, context,
timeout, heartbeat, passive_heartbeat)
self.endpoint = connect_to
self.context = context
self.timeout = timeout
self.heartbeat = heartbeat
self.passive_heartbeat = passive_heartbeat
def connect(self, endpoint, resolve=True):
self.endpoint = endpoint
return self._client.connect(endpoint, resolve)
def bind(self, endpoint, resolve=True):
self.endpoint = endpoint
return self._client.bind(endpoint, resolve)
def __call__(self, method, *args, **kwargs):
if self._client._events._socket.closed:
self._client = zerorpc.Client(self.endpoint, self.context, self.timeout,
self.heartbeat, self.passive_heartbeat)
try:
result = self._client(method, *args, **kwargs)
return result
except zerorpc.exceptions.LostRemote, e:
self._client.close()
raise e
except zerorpc.exceptions.TimeoutExpired, e:
self._client.close()
raise e
def __getattr__(self, method):
return lambda *args, **kargs: self(method, *args, **kargs)
夸一句,python里 __getattr__和__call__配合使用真是精妙,很簡單就模仿了local代碼的調用方式。
[node.js client]
var zerorpc = require('zerorpc');
var rpcproxy = function _rpcproxy (endpoint){
this.client = new zerorpc.Client();
this.client.connect(endpoint);
this.endpoint = endpoint;
return this;
};
rpcproxy.prototype.invoke = function() {
if (this.client.closed()){
this.client = new zerorpc.Client();
this.client.connect(this.endpoint);
}
var hasCallabck = arguments.length && typeof(arguments[arguments.length-1]) == 'function';
if (hasCallabck){
this.callerCallback = arguments[arguments.length-1];
}
var argList = [];
for (var i = 0; i < hasCallabck?arguments.length-1:arguments.length; i++){
argList.push(arguments[i]);
}
eval('this.client.invoke('+this.makeArgListString(argList)+',this._callback.bind(this));');
};
rpcproxy.prototype.makeArgListString = function(argList){
var argString = []
for (var i in argList){
argString.push('argList['+i+']');
}
return argString.join(',');
};
rpcproxy.prototype._callback = function(err, res, more){
if (err && err.name == 'HeartbeatError' || err.name == 'TimeoutExpired') {
this.client.close();
}
if (this.callerCallabck){
this.callerCallback(err, res, more);
}
};
js的實現(xiàn)使用了比較邪惡的eval方法,不過也不用擔心不安全,因為組織后的字符串是由argList[0], argList[1]...組成的,并不包含實際的值,不會被執(zhí)行一些奇怪的東西。
如有錯誤,歡迎指正。以上。