全部博文(2065)
分类: Python/Ruby
2010-11-23 22:34:51
rpc-json
时间:2010-11-23
一、相关介绍
1. 请求Request 带过来的参数
method - A String containing the name of the method to be invoked.
params - An Array of objects to pass as arguments to the method.
id - The request id. This can be of any type. It is used to match the response with the request that it is replying to.
2. 处理完毕之后回应Response
result - The Object that was returned by the invoked method. This must be null in case there was an error invoking the method. (如果有错误这就为null)
error - An Error object if there was an error invoking the method. It must be null if there was no error. (没错误它为null)
id - This must be the same id as the request it is responding to.
如果一个请求没有一个回应的话那这个id就应当为null!
它们之间的通讯流格式为TCP/IP格式的流通讯的!
调用示例:
--> { "method": "echo", "params": ["Hello JSON-RPC"], "id": 1}
<-- { "result": "Hello JSON-RPC", "error": null, "id": 1}
--> {"method": "postMessage", "params": ["Hello all!"], "id": 99}
<-- {"result": 1, "error": null, "id": 99} 表示这个方法是有回应的
<-- {"method": "handleMessage", "params": ["user1", "we were just talking"], "id": null}表示这个方法没有回应
PS:参数如果有多个情况使用数组!可以参考下面的ajax调用示例!
二、实现代码
以python实现rpc-server为例
安装步骤:
#svn checkout
#python setup.py install
示例代码如下:
#! /usr/bin/env python """Simple JSON-RPC Server. This module can be used to create simple JSON-RPC servers by creating a server and either installing functions, a class instance, or by extending the SimpleJSONRPCServer class. It can also be used to handle JSON-RPC requests in a CGI environment using CGIJSONRPCRequestHandler. A list of possible usage patterns follows: 1. Install functions: server = SimpleJSONRPCServer(("localhost", 8000)) server.register_function(pow) server.register_function(lambda x,y: x+y, 'add') server.serve_forever() 2. Install an instance: class MyFuncs: def __init__(self): # make all of the string functions available through # string.func_name import string self.string = string def _listMethods(self): # implement this method so that system.listMethods # knows to advertise the strings methods return list_public_methods(self) + \ ['string.' + method for method in list_public_methods(self.string)] def pow(self, x, y): return pow(x, y) def add(self, x, y) : return x + y server = SimpleJSONRPCServer(("localhost", 8000)) server.register_introspection_functions() server.register_instance(MyFuncs()) server.serve_forever() 3. Install an instance with custom dispatch method: class Math: def _listMethods(self): # this method must be present for system.listMethods # to work return ['add', 'pow'] def _methodHelp(self, method): # this method must be present for system.methodHelp # to work if method == 'add': return "add(2,3) => 5" elif method == 'pow': return "pow(x, y[, z]) => number" else: # By convention, return empty # string if no help is available return "" def _dispatch(self, method, params): if method == 'pow': return pow(*params) elif method == 'add': return params[0] + params[1] else: raise 'bad method' server = SimpleXMLRPCServer(("localhost", 8000)) server.register_introspection_functions() server.register_instance(Math()) server.serve_forever() 4. Subclass SimpleJSONRPCServer: class MathServer(SimpleJSONRPCServer): def _dispatch(self, method, params): try: # We are forcing the 'export_' prefix on methods that are # callable through JSON-RPC to prevent potential security # problems func = getattr(self, 'export_' + method) except AttributeError: raise Exception('method "%s" is not supported' % method) else: return func(*params) def export_add(self, x, y): return x + y server = MathServer(("localhost", 8000)) server.serve_forever() 5. CGI script: server = CGIJSONRPCRequestHandler() server.register_function(pow) server.handle_request() """ # This implementation was converted from SimpleXMLRPCServer by # David McNab (david@rebirthing.co.nz) # Original SimpleXMLRPCServer module was written by Brian # Quinlan (brian@sweetapp.com), Based on code written by Fredrik Lundh. import xmlrpclib from xmlrpclib import Fault import SocketServer import BaseHTTPServer import sys import os import SimpleXMLRPCServer import json import traceback class SimpleJSONRPCDispatcher(SimpleXMLRPCServer.SimpleXMLRPCDispatcher): """Mix-in class that dispatches JSON-RPC requests. Based on SimpleXMLRPCDispatcher, but overrides _marshaled_dispatch for JSON-RPC This class is used to register JSON-RPC method handlers and then to dispatch them. There should never be any reason to instantiate this class directly. """ def _marshaled_dispatch(self, data, dispatch_method = None): """Dispatches a JSON-RPC method from marshalled (JSON) data. JSON-RPC methods are dispatched from the marshalled (JSON) data using the _dispatch method and the result is returned as marshalled data. For backwards compatibility, a dispatch function can be provided as an argument (see comment in SimpleJSONRPCRequestHandler.do_POST) but overriding the existing method through subclassing is the prefered means of changing method dispatch behavior. """ rawreq = json.read(data) #params, method = xmlrpclib.loads(data) id = rawreq.get('id', 0) method = rawreq['method'] params = rawreq.get('params', []) responseDict = {'id':id} # generate response try: if dispatch_method is not None: response = dispatch_method(method, params) else: response = self._dispatch(method, params) ## wrap response in a singleton tuple #response = (response,) #response = xmlrpclib.dumps(response, methodresponse=1) responseDict['result'] = response except Fault, fault: #response = xmlrpclib.dumps(fault) responseDict['error'] = repr(response) except: # report exception back to server #response = xmlrpclib.dumps( # xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) # ) responseDict['error'] = "%s:%s" % (sys.exc_type, sys.exc_value) return json.write(responseDict) #class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): class SimpleJSONRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): """Simple JSON-RPC request handler class. Handles all HTTP POST requests and attempts to decode them as XML-RPC requests. """ def do_POST(self): """Handles the HTTP POST request. Attempts to interpret all HTTP POST requests as JSON-RPC calls, which are forwarded to the server's _dispatch method for handling. """ try: # get arguments data = self.rfile.read(int(self.headers["content-length"])) # In previous versions of SimpleXMLRPCServer, _dispatch # could be overridden in this class, instead of in # SimpleXMLRPCDispatcher. To maintain backwards compatibility, # check to see if a subclass implements _dispatch and dispatch # using that method if present. response = self.server._marshaled_dispatch( data, getattr(self, '_dispatch', None) ) except: # This should only happen if the module is buggy # internal error, report as HTTP server error self.send_response(500) self.end_headers() else: # got a valid XML RPC response self.send_response(200) self.send_header("Content-type", "text/json") self.send_header("Content-length", str(len(response))) self.end_headers() self.wfile.write(response) # shut down the connection self.wfile.flush() self.connection.shutdown(1) class SimpleJSONRPCServer(SocketServer.TCPServer, SimpleJSONRPCDispatcher): """Simple JSON-RPC server. Simple JSON-RPC server that allows functions and a single instance to be installed to handle requests. The default implementation attempts to dispatch JSON-RPC calls to the functions or instance installed in the server. Override the _dispatch method inhereted from SimpleJSONRPCDispatcher to change this behavior. """ def __init__(self, addr, requestHandler=SimpleJSONRPCRequestHandler, logRequests=1): self.logRequests = logRequests SimpleJSONRPCDispatcher.__init__(self) SocketServer.TCPServer.__init__(self, addr, requestHandler) class CGIJSONRPCRequestHandler(SimpleJSONRPCDispatcher): """Simple handler for JSON-RPC data passed through CGI.""" def __init__(self): SimpleJSONRPCDispatcher.__init__(self) def handle_get(self): """Handle a single HTTP GET request. Default implementation indicates an error because XML-RPC uses the POST method. """ code = 400 message, explain = \ BaseHTTPServer.BaseHTTPRequestHandler.responses[code] response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \ { 'code' : code, 'message' : message, 'explain' : explain } print 'Status: %d %s' % (code, message) print 'Content-Type: text/html' print 'Content-Length: %d' % len(response) print sys.stdout.write(response) def handle_request(self, request_text = None): """Handle a single JSON-RPC request passed through a CGI post method. If no JSON data is given then it is read from stdin. The resulting JSON-RPC response is printed to stdout along with the correct HTTP headers. """ if request_text is None and \ os.environ.get('REQUEST_METHOD', None) == 'GET': self.handle_get() else: # POST data is normally available through stdin if request_text is None: request_text = sys.stdin.read() self.handle_jsonrpc(request_text) def handle_jsonrpc(self, request_text): """Handle a single JSON-RPC request""" response = self._marshaled_dispatch(request_text) print 'Content-Type: text/json' print 'Content-Length: %d' % len(response) print sys.stdout.write(response) if __name__ == '__main__': server = SimpleJSONRPCServer(("localhost", 8000)) server.register_function(pow) server.register_function(lambda x,y: x+y, 'add') server.serve_forever()
|
PS:其时这里面的调用跟Python里面的rpyc调用非常类似的。可以参照学习下~
三、现在有了rpc-server。来写客户端调用
我们以JS调用为例子说明。其中JS布置在内网一台服务器上面,为了解决内网调用外网接口存在跨域的问题特别提出使用nginx来作代理转发。其中配置nginx如下:
在web端配置nginx代理(其中147这台WEB服务器里面配置了nginx代理转发)
if ( $request_uri ~ /rpcserver/$ ) {
proxy_pass
break;
}
调用示例:
function echo(input){
var service = new rpc.ServiceProxy("", {asynchronous:true,methods: ['add'],protocol: "JSON-RPC"});
service.add({params:[1],
onSuccess:function(sum){
alert("The sum is: " + sum);
},
onException:function(errorObj){
alert("Unable to add numbers because: " + errorObj);
},
onComplete:function(responseObj){
//any 'final' logic
}
});
}
说明:RPC调用的时候要注意JS的跨域调用问题。解决办法是通过nginx中的代理转发机制直接转到指定的一台服务器即可!
四、简单的一个结构图说明
五、遗留的一个问题
在ajax获取到的RCP-JSON回应数据没法正常作json解析。从httpwatch看到的回应数据居然与php-json客户端看到的数据不一致。这个问题不清楚是什么原因导致的。由于JSON没法解析正常,在处理那种有echo 输出的情况时就比较麻烦。这个需要想办法解决?
chinaunix网友2010-11-29 10:41:18
http://www.phpweblog.net/fuyongjie/archive/2008/09/09/5735.html
chinaunix网友2010-11-29 08:23:41
list($ip1,$ip2,$ip3,$ip4)=explode(".",$ip); 将DB里面的IP值进行划分到一个数组里面去。然后进行比较一下找到下一个可用的IP值
chinaunix网友2010-11-29 08:20:17
printf("%u\n", ip2long("121.204.65.26")); // 2043429146 运行显示 2043429146 echo long2ip(ip2long("2043429146")); 运行显示 121.204.65.26