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
 相关代码
相关代码
				 最近下载
最近下载
				 最近浏览
最近浏览
				




 
                 
     
                 
                