Hessian源码分析--HessianSkeleton
HessianSkeleton是Hessian的服务端的核心,简单总结来说:HessianSkeleton根据客户端请求的链接,获取到需要执行的接口及实现类,对客户端发送过来的二进制数据进行反序列化,获得需要执行的函数及参数值,然后根据函数和参数值执行具体的函数,接下来对执行的结果进行序列化然后通过连接返回给客户端。
接下来我们按照源码一步一步进行分析,在上一篇博客Hessian源码分析--HessianServlet我们了解到它是调用HessianSkeleton的invoke方法,接下来我们对invoke方法进行分析。
首先获取头类型,这个头类型应该是决定到底是使用那种序列化和反序列函数的,这里对序列化和反序列化不多过多分析。
然后接下来的操作就是调用invoke(_service, in, out)函数了,也没有其他重要的操作了。
public void invoke(InputStream is, OutputStream os,
SerializerFactory serializerFactory)
throws Exception
{
HessianInputFactory.HeaderType header = _inputFactory.readHeader(is);
AbstractHessianInput in;
AbstractHessianOutput out;
switch (header) {
case CALL_1_REPLY_1:
in = _hessianFactory.createHessianInput(is);
out = _hessianFactory.createHessianOutput(os);
break;
case CALL_1_REPLY_2:
in = _hessianFactory.createHessianInput(is);
out = _hessianFactory.createHessian2Output(os);
break;
case HESSIAN_2:
in = _hessianFactory.createHessian2Input(is);
in.readCall();
out = _hessianFactory.createHessian2Output(os);
break;
default:
throw new IllegalStateException(header + " is an unknown Hessian call");
}
if (serializerFactory != null) {
in.setSerializerFactory(serializerFactory);
out.setSerializerFactory(serializerFactory);
}
try {
invoke(_service, in, out);
} finally {
in.close();
out.close();
if (isDebug)
os.close();
}
}
调用invoke(_service, in, out)函数实际是调用如下操作,也是没有具体操作
public void invoke(AbstractHessianInput in, AbstractHessianOutput out)
throws Exception
{
invoke(_service, in, out);
}
接下来调用invoke(_service, in, out)函数是整个服务端的核心了,
首先是获取函数名mehtodName,通过methodName = in.readMethod()反序列化来获得函数名
定义函数对象Method method;
然后根据函数名获得函数对象 method = getMethod(methodName);,这里的函数对象是以函数名为key以对象为value存放到map中去的,这样有利于提高性能
ServiceContext context = ServiceContext.getContext();
// backward compatibility for some frameworks that don't read
// the call type first
in.skipOptionalCall();
// Hessian 1.0 backward compatibility
String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();
context.addHeader(header, value);
}
String methodName = in.readMethod();
int argLength = in.readMethodArgLength();
Method method;
method = getMethod(methodName + "__" + argLength);
if (method == null)
method = getMethod(methodName);
既然都已经拿到函数对象了,当然接下来的操作就是执行客户端要调用的函数了,这也是服务端的关键
method.getParameterTypes获得参数类型
Object []values = new Object[args.length];创建参数对象数组
values[i] = in.readObject(args[i]);反序列化客户端发送过来的参数值
result = method.invoke(service, values); 通过反射调用客户端想要调用的函数,并返回结果。
out.writeReply(result);对结果值进行序列化并发送给客户端,这样整个的RPC的调用过程就结束了。
<span style="white-space:pre"> </span>Class<?> []args = method.getParameterTypes();
if (argLength != args.length && argLength >= 0) {
out.writeFault("NoSuchMethod",
escapeMessage("method " + method + " argument length mismatch, received length=" + argLength),
null);
out.close();
return;
}
Object []values = new Object[args.length];
for (int i = 0; i < args.length; i++) {
// XXX: needs Marshal object
values[i] = in.readObject(args[i]);
}
Object result = null;
try {
result = method.invoke(service, values);
} catch (Exception e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException)
e1 = ((InvocationTargetException) e).getTargetException();
log.log(Level.FINE, this + " " + e1.toString(), e1);
out.writeFault("ServiceException",
escapeMessage(e1.getMessage()),
e1);
out.close();
return;
}
// The complete call needs to be after the invoke to handle a
// trailing InputStream
in.completeCall();
out.writeReply(result);
out.close();
总结:RPC机制就是客户端发送给服务端想要调用的函数及参数值,服务端通过客户端发送过来的函数和参数值通过反射机制进行函数调用执行,然后将执行结果返回给客户端,这样一个RPC的调用过程结束了。
HessianSkeleton完整源码:
public class HessianSkeleton extends AbstractSkeleton {
private static final Logger log
= Logger.getLogger(HessianSkeleton.class.getName());
private boolean _isDebug;
private HessianInputFactory _inputFactory = new HessianInputFactory();
private HessianFactory _hessianFactory = new HessianFactory();
private Object _service;
/**
* Create a new hessian skeleton.
*
* @param service the underlying service object.
* @param apiClass the API interface
*/
public HessianSkeleton(Object service, Class<?> apiClass)
{
super(apiClass);
if (service == null)
service = this;
_service = service;
if (! apiClass.isAssignableFrom(service.getClass()))
throw new IllegalArgumentException("Service " + service + " must be an instance of " + apiClass.getName());
}
/**
* Create a new hessian skeleton.
*
* @param service the underlying service object.
* @param apiClass the API interface
*/
public HessianSkeleton(Class<?> apiClass)
{
super(apiClass);
}
public void setDebug(boolean isDebug)
{
_isDebug = isDebug;
}
public boolean isDebug()
{
return _isDebug;
}
public void setHessianFactory(HessianFactory factory)
{
_hessianFactory = factory;
}
/**
* Invoke the object with the request from the input stream.
*
* @param in the Hessian input stream
* @param out the Hessian output stream
*/
public void invoke(InputStream is, OutputStream os)
throws Exception
{
invoke(is, os, null);
}
/**
* Invoke the object with the request from the input stream.
*
* @param in the Hessian input stream
* @param out the Hessian output stream
*/
public void invoke(InputStream is, OutputStream os,
SerializerFactory serializerFactory)
throws Exception
{
boolean isDebug = false;
if (isDebugInvoke()) {
isDebug = true;
PrintWriter dbg = createDebugPrintWriter();
HessianDebugInputStream dIs = new HessianDebugInputStream(is, dbg);
dIs.startTop2();
is = dIs;
HessianDebugOutputStream dOs = new HessianDebugOutputStream(os, dbg);
dOs.startTop2();
os = dOs;
}
HessianInputFactory.HeaderType header = _inputFactory.readHeader(is);
AbstractHessianInput in;
AbstractHessianOutput out;
//通过头部信息来选择序列化和反序列化方式
switch (header) {
case CALL_1_REPLY_1:
in = _hessianFactory.createHessianInput(is);
out = _hessianFactory.createHessianOutput(os);
break;
case CALL_1_REPLY_2:
in = _hessianFactory.createHessianInput(is);
out = _hessianFactory.createHessian2Output(os);
break;
case HESSIAN_2:
in = _hessianFactory.createHessian2Input(is);
in.readCall();
out = _hessianFactory.createHessian2Output(os);
break;
default:
throw new IllegalStateException(header + " is an unknown Hessian call");
}
if (serializerFactory != null) {
in.setSerializerFactory(serializerFactory);
out.setSerializerFactory(serializerFactory);
}
try {
invoke(_service, in, out);
} finally {
in.close();
out.close();
if (isDebug)
os.close();
}
}
/**
* Invoke the object with the request from the input stream.
*
* @param in the Hessian input stream
* @param out the Hessian output stream
*/
public void invoke(AbstractHessianInput in, AbstractHessianOutput out)
throws Exception
{
invoke(_service, in, out);
}
/**
* Invoke the object with the request from the input stream.
*
* @param in the Hessian input stream
* @param out the Hessian output stream
*/
public void invoke(Object service,
AbstractHessianInput in,
AbstractHessianOutput out)
throws Exception
{
ServiceContext context = ServiceContext.getContext();
// backward compatibility for some frameworks that don't read
// the call type first
in.skipOptionalCall();
// Hessian 1.0 backward compatibility
String header;
while ((header = in.readHeader()) != null) {
Object value = in.readObject();
context.addHeader(header, value);
}
//反序列化获得客户端发送过来的函数名
String methodName = in.readMethod();
int argLength = in.readMethodArgLength();
Method method;
//从map中获取Method对象
method = getMethod(methodName + "__" + argLength);
if (method == null)
method = getMethod(methodName);
if (method != null) {
}
else if ("_hessian_getAttribute".equals(methodName)) {
String attrName = in.readString();
in.completeCall();
String value = null;
if ("java.api.class".equals(attrName))
value = getAPIClassName();
else if ("java.home.class".equals(attrName))
value = getHomeClassName();
else if ("java.object.class".equals(attrName))
value = getObjectClassName();
out.writeReply(value);
out.close();
return;
}
else if (method == null) {
out.writeFault("NoSuchMethodException",
escapeMessage("The service has no method named: " + in.getMethod()),
null);
out.close();
return;
}
//获得参数值类型
Class<?> []args = method.getParameterTypes();
if (argLength != args.length && argLength >= 0) {
out.writeFault("NoSuchMethod",
escapeMessage("method " + method + " argument length mismatch, received length=" + argLength),
null);
out.close();
return;
}
Object []values = new Object[args.length];
for (int i = 0; i < args.length; i++) {
// XXX: needs Marshal object
//函数的参数值
values[i] = in.readObject(args[i]);
}
Object result = null;
try {
//反射执行客户端想要执行的函数
result = method.invoke(service, values);
} catch (Exception e) {
Throwable e1 = e;
if (e1 instanceof InvocationTargetException)
e1 = ((InvocationTargetException) e).getTargetException();
log.log(Level.FINE, this + " " + e1.toString(), e1);
out.writeFault("ServiceException",
escapeMessage(e1.getMessage()),
e1);
out.close();
return;
}
// The complete call needs to be after the invoke to handle a
// trailing InputStream
//调用结束
in.completeCall();
//对结果值进行序列化,并发送给客户端
out.writeReply(result);
out.close();
}
//以下是和日志相关的,不会影响主流程
private String escapeMessage(String msg)
{
if (msg == null)
return null;
StringBuilder sb = new StringBuilder();
int length = msg.length();
for (int i = 0; i < length; i++) {
char ch = msg.charAt(i);
switch (ch) {
case '<':
sb.append("<");
break;
case '>':
sb.append(">");
break;
case 0x0:
sb.append("�");
break;
case '&':
sb.append("&");
break;
default:
sb.append(ch);
break;
}
}
return sb.toString();
}
protected boolean isDebugInvoke()
{
return (log.isLoggable(Level.FINEST)
|| isDebug() && log.isLoggable(Level.FINE));
}
/**
* Creates the PrintWriter for debug output. The default is to
* write to java.util.Logging.
*/
protected PrintWriter createDebugPrintWriter()
throws IOException
{
return new PrintWriter(new LogWriter(log));
}
static class LogWriter extends Writer {
private Logger _log;
private StringBuilder _sb = new StringBuilder();
LogWriter(Logger log)
{
_log = log;
}
public void write(char ch)
{
if (ch == '\n' && _sb.length() > 0) {
_log.fine(_sb.toString());
_sb.setLength(0);
}
else
_sb.append((char) ch);
}
public void write(char []buffer, int offset, int length)
{
for (int i = 0; i < length; i++) {
char ch = buffer[offset + i];
if (ch == '\n' && _sb.length() > 0) {
_log.fine(_sb.toString());
_sb.setLength(0);
}
else
_sb.append((char) ch);
}
}
public void flush()
{
}
public void close()
{
}
}
}
Hessian源码分析--HessianSkeleton的更多相关文章
- Hessian源码分析--总体架构
Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能. 相比WebService,Hessian更简单.快捷.采用的是二进制RPC协议,因为采用的是二进制协 ...
- Hessian源码分析--HessianProxy
在上一篇博客 Hessian源码分析--HessianProxyFactory 中我们了解到,客户端获得的对象其实是HessianProxy生成的目标对象,当调用目标对象的方法时,会调用Hessian ...
- (转)hessian源码分析(一)------架构
在计费中心的对外交互这块采用了hessian,有必要对hessian的运行机理和源码做一定的解析. 大致翻了翻源码后,发现hessian的主要结构分客户端与服务端,中间基于http传输.客户端主要做的 ...
- Hessian源码分析--HessianServlet
Hessian可以通过Servlet来对外暴露服务,HessianServlet继承于HttpServlet,但这仅仅是一个外壳,使用web服务器来提供对外的Http请求,在web.xml中我们会进行 ...
- Hessian源码分析--HessianProxyFactory
HessianProxyFactory是HessianProxy的工厂类,其通过HessianProxy来生成代理类. 如下面代码: HessianProxyFactory factory = new ...
- SURF算法与源码分析、下
上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SU ...
- Dubbo 源码分析 - 服务引用
1. 简介 在上一篇文章中,我详细的分析了服务导出的原理.本篇文章我们趁热打铁,继续分析服务引用的原理.在 Dubbo 中,我们可以通过两种方式引用远程服务.第一种是使用服务直联的方式引用服务,第二种 ...
- 【OpenCV】SIFT原理与源码分析:关键点搜索与定位
<SIFT原理与源码分析>系列文章索引:http://www.cnblogs.com/tianyalu/p/5467813.html 由前一步<DoG尺度空间构造>,我们得到了 ...
- 12.源码分析—如何为SOFARPC写一个序列化?
SOFARPC源码解析系列: 1. 源码分析---SOFARPC可扩展的机制SPI 2. 源码分析---SOFARPC客户端服务引用 3. 源码分析---SOFARPC客户端服务调用 4. 源码分析- ...
随机推荐
- 开源小工具 - swagger API访问代码生成器(js/typescript)
现在流行前后端分离,后端通常会使用swagger生成api文档,提供给前端的同事来调用,一般前端是查看这个文档,根据文档说明编写调用代码.其实,swagger已经提供了足够多的描述信息,我们可以基于s ...
- 索引法则--LIKE以%开头会导致索引失效进而转向全表扫描(使用覆盖索引解决)
Mysql 系列文章主页 =============== 1 准备数据 1.1 建表 DROP TABLE IF EXISTS staff; CREATE TABLE IF NOT EXISTS st ...
- map内置函数、lambda表达式、快捷生成想要的列表、filter内置函数
map函数 语法 map(function, iterable, ...) 参数 function -- 函数,有两个参数 iterable ...
- python 循环和file操作实现用户密码输错三次将用户锁定
一.需求编写登录接口1.输入用户名密码2.认证成功后显示欢迎信息3.输错三次后锁定 二.简单思路登录,三次密码输入错误锁定用户1.用户信息文件:存放用户名和密码2.黑名单文件:将输入三次错误的用户加入 ...
- Angular4.0入门
angular与其他的差别 angular cli安装 cnpm install -g @angular/cli 最新版本 cnpm uninstall -g @angular/cli 卸载全局版本 ...
- sshpass笔记
sshpass简介 ssh登录的时候使用的是交互式输入,不能预先在命令行使用参数指定密码,sshpass就是为了解决这个问题的.sshpass提供非交互式输入密码的方式,可以用在shell脚本中自动输 ...
- C++后台实践:古老的CGI与Web开发
本文写给C/C++程序猿,也适合其他对历史感兴趣的程序猿 ============================================= 谈到web开发,大家首先想到的PHP.JavaEE ...
- shell编程--基本格式,基本语法,运算符,expr,(()),$[]
02/shell编程 Shell是用户与内核进行交互操作的一种接口,目前最流行的Shell称为bash Shell Shell也是一门编程语言."."号执行脚本时,会让脚本在调用者 ...
- 27 自定义View 和案例
有时安卓提供的View不满足我们的需求时可以创建一个类继承View或其子类重写方法 如 package com.qf.sxy.day28_customview.view; import android ...
- HTML简单使用
HTML简单使用 标签 : 前端技术 HTML HTML(Hypertext Marked Language), 即超文本标记语言,能够独立于各种操作系统平台(如UNIX/Linux/Windows等 ...