apache hessian原理学习和自己实现rpc远程调用实例代码
因为最近有点时间,所以就想读一些技术的源码,最近选择hessian。废话不多说,进入正题:
hessian客户端时序图:
因为hessian源码相对于spring算很简单了。所以不浪费大家宝贵的时间来分析源码。想说说怎么实现远程调用(rpc)。
远程调用,顾名思义就是通过远程调用别的机器(JVM or ...)中的方法。要想实现远程调用,思路是怎样呢?
常用的模式代理,工厂。为啥怎么说呢?
远程调用通过网络,肯定需要工厂产出不同的类,不然一个类写,会死人的。
代理模式,因为调用前你是不知道实际的调用,肯定是通过代理调用到远程方法。
下面给大家说说远程调用的原理(PS:不懂代理的同学,先上百度搜索:java代理)。
实现自定义的远程调用,就是序列化成字节流后,发送到服务端,服务端根据调用的api、方法、参数去具体调用一个方法,调用方法后把结果输入到输出流里面,最后结果展示在代理的return中。
ok,开始贴代码:
客户端(HttpRpcClient):
String url = "http://localhost:8080/hessian-study-server/helloworld1"; HttpProxyFactory factory = new HttpProxyFactory(); Class<?> clazz = Class.forName("com.jzx.hessian.server.SayHelloService"); SayHelloService sayHelloService = (SayHelloService) factory.create(clazz, url); System.out.println(sayHelloService.sayHello("小明", "男")); UserVo vo = sayHelloService.sayHello1("小明", "男"); System.out.println("姓名:" + vo.getName() + " 性别:" + vo.getSex());
从代码中分析,首先创建了一个工厂类HttpProxyFactory,其次创建了一个类的描述。
HttpProxyFactory代码(主要目的是得到一个代理):
if (api == null) { throw new NullPointerException("api must not be null for HttpProxyFactory.create()"); } InvocationHandler handler = null; handler = new HttpProxy(url, this, api); return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[] { api }, handler);
HttpProxy代理干了啥 (得到调用接口类、方法名、参数值):
HttpTransportation transportation = new HttpTransportation(); transportation.setClazz(api); transportation.setMethod(method.getName()); transportation.setParams(args); byte[] bytes = ObjectUtils.objectToByte(transportation); Object result = sendPost(url, bytes); return result;
序列化对象发送POST请求:
PrintWriter out = null; BufferedReader in = null; Object result = null; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // 获取URLConnection对象对应的输出流 OutputStream outputStream = conn.getOutputStream(); outputStream.write(param); outputStream.close(); // 定义BufferedReader输入流来读取URL的响应 // in = new BufferedReader(new InputStreamReader(conn.getInputStream())); byte[] bytes = toByteArray(conn.getInputStream()); Object object = ObjectUtils.byteToObject(bytes); if (object instanceof String) { byte[] dest = new byte[bytes.length - 7]; System.arraycopy(bytes, 7, dest, 0, dest.length); result = new String(dest, "UTF-8"); // result = new String(bytes, "UTF-8"); } if (object instanceof UserVo) { result = object; } } catch (Exception e) { System.out.println("发送 POST 请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null) { out.close(); } if (in != null) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result;
以上是客户端的分割线------------------------服务端:
配置一个sevlet,初始化配置文件:
@Override public void init(ServletConfig config) throws ServletException { super.init(config); if (getInitParameter("home-api") != null) { String className = getInitParameter("home-api"); try { _homeAPI = Class.forName(className); } catch (Exception e) { } } if (getInitParameter("service-class") != null) { String className = getInitParameter("service-class"); Class<?> clazz; try { clazz = Class.forName(className); _homeImpl = clazz.newInstance(); } catch (Exception e) { } } httpRpcSkeleton = new HttpRpcSkeleton(_homeAPI, _homeImpl); }
@Override public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; if (!req.getMethod().equals("POST")) { res.setStatus(500); // , "Hessian Requires POST"); PrintWriter out = res.getWriter(); res.setContentType("text/html"); out.println("<h1>httrpc Requires POST</h1>"); return; } OutputStream out = res.getOutputStream(); try { httpRpcSkeleton.invoke(req, out); } catch (Exception e) { e.printStackTrace(); } finally { out.flush(); out.close(); } }
看到请求最终被HttpRpcSkeleton的invoke调用,相关代码:
public void invoke(HttpServletRequest request, OutputStream out) throws Exception { int len = request.getContentLength(); ServletInputStream input = request.getInputStream(); byte[] buffer = new byte[len]; input.read(buffer, 0, len); HttpTransportation transportation = (HttpTransportation) ObjectUtils.byteToObject(buffer); Method method = methodMap.get(transportation.getMethod()); Object result = method.invoke(_homeImpl, transportation.getParams()); out.write(ObjectUtils.objectToByte(result)); // out.write(result.getBytes()); }
以上就是实现远程,调用的原理。
另外对项目的说明,有兴趣的童鞋,可以试下不用java本身的序列化,试试protobuf。
另外有一个疑问困扰了一个晚上,当返回为字符串的时候,ObjectInputStream会多7个字节,不解,本人的java io很low,求解。
源码为maven结构:
运行时截图:
猜你喜欢
- /
- /hessian-study
- /hessian-study/modules
- /hessian-study/modules/hessian-study-server
- /hessian-study/modules/hessian-study-server/pom.xml
- /hessian-study/modules/hessian-study-server/src
- /hessian-study/modules/hessian-study-server/src/lib
- /hessian-study/modules/hessian-study-server/src/lib/hessian-4.0.37-src.jar
- /hessian-study/modules/hessian-study-server/src/lib/hessian-4.0.37.jar
- /hessian-study/modules/hessian-study-server/src/main
- /hessian-study/modules/hessian-study-server/src/main/java
- /hessian-study/modules/hessian-study-server/src/main/java/com
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/hessian
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/hessian/client
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/hessian/server
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/hessian/vo
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/httprpc
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx/hessian
- /hessian-study/modules/hessian-study-server/src/main/java/com/jzx
- /hessian-study/modules/hessian-study-server/src/main/java/com
- /hessian-study/modules/hessian-study-server/src/main/java
- /hessian-study/modules/hessian-study-server/src/lib
- /hessian-study/modules/hessian-study-server
- /hessian-study/modules
- /hessian-study