最近没事,翻了翻rpc,由于在工作中接触到关于hessian和需要对hessian进行一下小小的改动,于是对其通信原理和方式进行了一下大致的研究;
什么是rpc?
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。RMI是J2EE中标准的RPC服务,RPC的完美封装是的将业务逻辑可以进行分布式部署,将运算密集型的工作进行分流(但请求还是同步的),在使用过程中间封装起实现可以实现在客户端调用的时候完全透明(需要在RMI客户端调用方式进行封装)。
什么是hessian
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,因为采用的是二进制协议,所以它很适合于发送二进制数据。
二进制?http!
在hessian的介绍过程中间,最大的亮点是二进制和http,hessian分为客户端为服务端两部分,客户端根据根据借口按照invocationHandler进行代理,拦截方法请求,将URL和方法名字和参数进行序列化,采用http协议封装发送至服务端,服务端的servlet解析请求流,根据协议,凡序列化得到调用的方法和参数,生成结果,同理反馈至客户端。
下面是URLConncetion与自定义server的通信具体过程
public class UrlClient {
public static void main(String[] args) throws Exception {
URL url=new URL("http://127.0.0.1:8080/hwt/he");
URLConnection conn=url.openConnection();
conn.setAllowUserInteraction(true);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
((HttpURLConnection)conn).setRequestMethod("POST");
conn.setRequestProperty("Content-type", "application/x-java-serialized-object");
OutputStream os=conn.getOutputStream();
os.write("hello world".getBytes());
conn.connect();
InputStream is=conn.getInputStream();
while(true){
;
}
}
}
服务器
public class Server2 {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(6061);
while (true) {
Socket request = server.accept();
System.out.println("接受请求:");
byte[] bs=new byte[255];
int index=0;
InputStream is=request.getInputStream();
while((index=is.read(bs))>-1){
System.out.println(new String(bs,0,index));
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在客户端调用getInputStream()的时候,将outputStream.write的body加上配置的http header按照http协议进行了封装,发送至服务器,等待服务器回写。
Console:
接受请求:
POST / HTTP/1.1
Content-type: application/x-java-serialized-object
Cache-Control: no-cache
Pragma: no-cache
User-Agent: Java/1.6.0_10
Host: 127.0.0.1:6061
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive
Content-
Length: 11
hello world
hello world被作为了http body,二进制的写入了服务器的inputStream。
servletInputStreamRequest
我们在编写servlet的时候,能够从
servletInputStreamRequest sis=Request.getInputStream();
中获取到,但是往往却打印不出任何数据,其实在web server(如tomcat,jetty)中,并非是将监听的
Socket socket=serverSocket.accpet();
inputStream is=socket.getInputStream();
直接包装成为了servletInputStream,而是根据http协议进行了封装和解析。于是,我们能够成servletInputStream中获取到http body的字节数据;
客户端代码:
URL url=new URL("http://127.0.0.1:8080/hwt/he");
服务器servlet代码:
System.out.println("post");
byte[] bs=new byte[255];
int index=0;
InputStream is=request.getInputStream();
while((index=is.read(bs))>-1){
System.out.println(new String(bs,0,index));
}
Console:
Hello world!
http是船,hessian自定义协议是人
在客户端中,根据接口进行了代理拦截,
public Object invoke(Object proxy, Method method, Object []args)
throws Throwable
{
String mangleName;
synchronized (_mangleMap) {
mangleName = _mangleMap.get(method);
}
if (mangleName == null) {
String methodName = method.getName();
Class<?> []params = method.getParameterTypes();
// equals and hashCode are special cased
if (methodName.equals("equals")
&& params.length == 1 && params[0].equals(Object.class)) {
Object value = args[0];
if (value == null || ! Proxy.isProxyClass(value.getClass()))
return Boolean.FALSE;
Object proxyHandler = Proxy.getInvocationHandler(value);
if (! (proxyHandler instanceof HessianProxy))
return Boolean.FALSE;
HessianProxy handler = (HessianProxy) proxyHandler;
return new Boolean(_url.equals(handler.getURL()));
}
else if (methodName.equals("hashCode") && params.length == 0)
return new Integer(_url.hashCode());
else if (methodName.equals("getHessianType"))
return proxy.getClass().getInterfaces()[0].getName();
else if (methodName.equals("getHessianURL"))
return _url.toString();
else if (methodName.equals("toString") && params.length == 0)
return "HessianProxy[" + _url + "]";
if (! _factory.isOverloadEnabled())
mangleName = method.getName();
else
mangleName = mangleName(method);
synchronized (_mangleMap) {
_mangleMap.put(method, mangleName);
}
}
InputStream is = null;
HessianConnection conn = null;
try {
if (log.isLoggable(Level.FINER))
log.finer("Hessian[" + _url + "] calling " + mangleName);
conn = sendRequest(mangleName, args);
is = conn.getInputStream();
if (log.isLoggable(Level.FINEST)) {
PrintWriter dbg = new PrintWriter(new LogWriter(log));
HessianDebugInputStream dIs
= new HessianDebugInputStream(is, dbg);
dIs.startTop2();
is = dIs;
}
AbstractHessianInput in;
int code = is.read();
if (code == 'H') {
int major = is.read();
int minor = is.read();
in = _factory.getHessian2Input(is);
Object value = in.readReply(method.getReturnType());
return value;
}
else if (code == 'r') {
int major = is.read();
int minor = is.read();
in = _factory.getHessianInput(is);
in.startReplyBody();
Object value = in.readObject(method.getReturnType());
if (value instanceof InputStream) {
value = new ResultInputStream(conn, is, in, (InputStream) value);
is = null;
conn = null;
}
else
in.completeReply();
return value;
}
else
throw new HessianProtocolException("'" + (char) code + "' is an unknown code");
} catch (HessianProtocolException e) {
throw new HessianRuntimeException(e);
} finally {
try {
if (is != null)
is.close();
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
try {
if (conn != null)
conn.destroy();
} catch (Exception e) {
log.log(Level.FINE, e.toString(), e);
}
}
}
将方法名以及参数进行序列化,根据URL地址发送至服务器,这时候http body中的byte[]是hessian自定义的二进制协议,这和普通的c/s二进制协议无本质区别。
在servlet端,解析了servletInputStream中的http body byte[]数据,根据定制的二进制协议进行了凡序列号,根据java 发射进行了方法调用以及返回值的序列号,同理借助servletOutputStream发送至客户端(进行了response的封装,response header装饰)。