一、Hessian

hessian是一种高效简洁的远程调用框架,它采用的是二进制Binary-RPC协议,基于Http协议进行传输,具有轻量、传输量小、平台无关的特点,Hessian通常通过Web应用来提供服务,通过接口暴露,特别适合于目前网络带宽比较小的手机网络应用项目。采用Binary RPC协议,相比webservice而言更简单、快捷,可与spring集成,配置简单,使用HessianServiceExporter提供bean服务。Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说, Hessian 具有更好的性能和易用性,而且支持多种不同的语言,实际上Dubbo采用的就是 Hessian 序列化来实现,只不过Dubbo对Hessian进行了重构,性能更高。

通过Hessian本身提供的API来发起请求和接收请求,Hessian通过其自定义的串行化机制将请求信息进行序列化,产生二进制流,在通过其私有的串行化机制来将请求信息进行反序列化,复杂对象序列化速度仅次于RMI,简单对象序列化优于RMI。另外还能跨语言使用,精简高效,可以透过防火墙。但是缺乏安全机制,传输没有加密处理,事务处理欠缺。一般都是与WEB服务器结合,借助WEB服务器的成熟功能,对于异常处理等一些不足方面都可以由成熟的WEB服务器保证。

Hessian处理过程示意图:客户端——>序列化写到输出流——>远程方法(服务器端)——>序列化写到输出流 ——>客户端读取输入流——>输出结果

简单来说:客户端拥有与服务器相同(兼容)的接口,客户端通过Hession请求就可以调用服务器的方法

 

二、Hessian与Servlet结合

通过服务端与客户端两个WEB工程项目通信,实现客户端发送请求上传文件。需要导入hessian.jar包。

1、服务端

创建一个web服务端工程。

1.接口类

通过后面的配置需要把接口暴露出去。

public interface FileUploadService {
 
     public void upload(String filename, InputStream data);
 }

2.接口实现类

public class FileUploadServiceImpl implements FileUploadService {

    public void upload(String filename, InputStream data) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            bis = new BufferedInputStream(data);
            bos = new BufferedOutputStream(new FileOutputStream("E:/" + filename));
            byte[] buffer = new byte[8192];
            int r = bis.read(buffer, 0, buffer.length);
            while (r > 0) {
                bos.write(buffer, 0, r);
                r = bis.read(buffer, 0, buffer.length);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

3.配置xml

在web.xml配置servlet,HessianServlet是Hessian内的,并不要我们自己实现。home-api或者api-class来配置接口,而home-class或者service-class配置该接口的实现类。然后配置URL映射拦截请求。

<servlet>
		<servlet-name>FileUploadService</servlet-name>
		<servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class>
		<init-param>
			<param-name>home-class</param-name>
			<param-value>service.FileUploadServiceImpl</param-value>
		</init-param>
		<init-param>
			<param-name>home-api</param-name>
			<param-value>service.FileUploadService</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>FileUploadService</servlet-name>
		<url-pattern>/FileUploadService</url-pattern>
	</servlet-mapping>

2、客户端

需要把服务端接口放到客户端,也就是上面的FileUploadService接口,因为客户端通过hessian获取该接口的代理对象,接口调用方法来实现真正的方法调用。

在这里通过java应用来模拟发送http请求。

public class FileUploaderClient {
    public static void main(String[] args) throws Exception {
        HessianProxyFactory factory = new HessianProxyFactory();
        FileUploadService uploader = (FileUploadService) factory.create(FileUploadService.class,
"http://localhost:8080/HessianServer/FileUploadService");
        InputStream data = new BufferedInputStream(new FileInputStream("E:/text.txt"));
        uploader.upload("text.txt", data);
    }
}

 

3、实现过程

客户端用Hessian通过HessianProxyFactory的create方法来获取该接口实例,create方法返回的是一个动态代理对象,当调用接口方法的时候运行的是代理对象的invoke方法,使用HessianProxy对象作为代理的Handler,handler的invoke方法,在进行一些方法名和参数的确认之后,创建HttpURLConnection对象,调用sendRequest方法,将方法名和参数用HessianOutput对象(设置序列化的方式)的call方法,写入到服务端。

服务端通过HessianServlet来接收请求,当第一次请求来时会调用init方法初始化,根据web.xml配置的service接口创建HessianSkeleton,在service方法中调用invoke方法具体去调用HessianSkeleton中的invoke方法,在方法内创建序列化流,从HessianInput对象中获取到Method信息,获取到真正的service对象,根据反射机制,调用service对象的invoke方法,获取到返回值,最后调用HessianOutput对象将结果写回到调用方,在这里面通过序列化获取发来的方法和参数实现真正的方法调用,最后把结果序列化流返回出去。

Hessian协议用于集成Hessian的服务,Hessian底层采用Http通讯,采用Servlet暴露服务。适用场景:传入传出参数数据包较大,提供者比消费者个数多,提供者压力较大,可传文件。因此比较高效的做法是带上传下载文件的服务使用hessian协议,去普通的服务使用dubbo

 

三、Hessian与Spring结合

跟spring结合需要加入spring包,spring-beans、spring-context、spring-core、spring-expression4个spring依赖包,spring内部底层用了日志,所以还得加上commons-logging,还要spring-aop因为hessian底层用了aop代理对象。另外需要用springmvc作为拦截请求,所以得加上spring-webmvc,而与hessian结合的jar包,在spring中已经拥有,导入spring-web。不要去添加spring-remoting来完成hessian和spring的结合,从2.5开始,spring-remoting的内容被拆分到spring-webmvc或spring-web中,所以要匹配上spring-webmvc那部分需要用spring-web中的,自己额外导入的会引起不匹配接收不到请求报错。

1、服务端

1.实体类

定义user实体类并实现Serializable接口用于序列化传输

public class User implements Serializable{

	private static final long serialVersionUID = 1692800630322115854L;
	private String name;
	public User() {
		
	}
	public User(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	
}

 2.创建服务层接口

Hessian的这个远程过程调用,完全使用动态代理来实现的,采用面向接口编程,因此,Hessian服务建议通过接口暴露。

public interface IService {
    public User getUser();
    public String setUser(User user);
}

3.服务层实现

public class ServiceImpl implements IService {
	private User vo = new User();
	@Override
	public User getUser() {
		return vo;
	}
	@Override
	public String setUser(User user) {
		vo.setName(user.getName());
		return "添加成功";
	}
}

 4.配置springmvc.xml

HessianServiceExporter是一个Spring MVC控制器,它接收Hessian请求(HTTP协议的请求),并将这些请求转换成对被导出POJO的方法调用。既然是HTTP请求,那我们就必须配置Spring 的DispatcherServlet ,并配置HandlerMapping,将相应的URL映射给HessianServiceExporter

<beans>
<bean id="ServiceImpl" class="service.ServiceImpl" />
	<bean name="/user"
		class="org.springframework.remoting.caucho.HessianServiceExporter">
		<property name="service" ref="ServiceImpl" />
		<property name="serviceInterface" value="service.IService" />
	</bean>
</beans>

5.配置web.xml

Hessian通过Servlet提供远程服务。需要将匹配某个模式的请求映射到Hessian服务。Spring的DispatcherServlet可以完成该功能,DispatcherServlet可将匹配模式的请求转发到Hessian服务。

<servlet>
        <servlet-name>hessianServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>        
    </servlet>

    <servlet-mapping>
        <servlet-name>hessianServlet</servlet-name>
        <url-pattern>/hessian/*</url-pattern>
    </servlet-mapping>

2、客户端

首先需要把model层的user和service层的接口拷贝一份过来。

1.配置客户端的springmvc.xml

<bean id="user"
        class="org.springframework.remoting.caucho.HessianProxyFactoryBean">
        <property name="serviceUrl">
            <value>http://localhost:8080/HessianServer/hessian/user</value>
        </property>
        <property name="serviceInterface">
            <value>service.IService</value>
        </property>
    </bean>

2.配置客户端的web.xml

Spring的DispatcherServlet用来接收发送到前端发来的请求

<servlet>
		<servlet-name>dispatcher</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>classpath:springmvc.xml</param-value>
		</init-param>
	</servlet>
	<servlet-mapping>
		<servlet-name>dispatcher</servlet-name>
		<url-pattern>*.do</url-pattern>
	</servlet-mapping>

3.创建控制层 

通过@Autowired依赖注入把service的代理对象注入进去,接收前端发来的数据并返回到前端。

@Controller
public class HessianController {
	@Autowired
	@Qualifier("user")
	private IService service;
	
	@RequestMapping("/user.do")
	public String add(User user,Model m) {
		service.setUser(user);
		m.addAttribute(service.getUser());
		return "success.jsp";
	}
}

4.创建jsp页面

index.jsp用于表单提交上传数据到控制层

<form action="user.do">
<input type="text" name="name">
<input type="submit" value="提交"> 
</form>

 success.jsp用于提交数据成功后展示数据界面

<body>
${user.name}
</body>

 

3、运行

同时运行两个项目,在index.jsp中填写数据"张三",并提交就能在success.jsp看到

rpc框架实现有哪些 rpc框架性能对比_spring

在这里可以看到完成了控制层和服务层的分离,另外我没有引入数据库,你可以自行加入数据库保存数据。

4、实现过程

客户端配置的HessianProxyFactoryBean实例化的时候会调用父类的afterPropertiesSet方法来创建hessianProxy代理对象,而该类通过afterPropertiesSet方法来创建AopProxy代理对象serviceProxy,HessianProxyFactoryBean实现了FactoryBean接口,而接口的方法getObject()正是我们在调用context.getBean(“x”)的时候被调用,所以获取的bean其实是serviceProxy,而默认使用的是JDK动态代理。当在客户端调用服务层方法时就会触发serviceProxy的invoke方法,JdkDynamicAopProxy实现了InvocationHandler接口,在invoke方法内会把方法名参数封装到了ReflectiveMethodInvocation中,再调用它的proceed方法,然后又调用了HessianProxyFactoryBean中的invoke()方法,而这里就执行了hessianProxy对象的方法调用,hessianProxy对象在初始化HessianProxyFactoryBean的时候就初始化好了,也就是我们配置的service和url,后面的实现跟和servlet结合发送请求一样,也就是这个代理对象调用invoke与服务端通信,从而实现真正的方法调用。

而服务端DispatcherServlet只是启动一个映射的作用,真正的处理在HessianServiceExporter类中,容器初始化的时候会调用InitializingBean的afterPropertiesSet()方法,所以HessianServiceExporter在实例化的同时会调用prepare方法来检查配置文件的正确性和调用getProxyForService方法创建ProxyFactory,通过ProxyFactory来实例化HessianSkeleton对象,又进一步的将Inputstream封装入HessianInput中,将Outputstream封装入Hessian2Output中,接下来把HessianInput和Hessian2Output传入HessianSkeleton中,当请求来时先到DispatcherServlet,然后根据路径获取handler,并调用HessianServiceExporter类的handleRequest方法,里面调用invoke方法交给HessianSkeleton来处理,而后面接收请求跟前面于servlet一样通过HessianSkeleton来实现。