Hessian是一个轻量级的remoting onhttp工具,使用简单的方法提供了RMI的功能。 相比WebService,Hessian更简单、快捷。采用的是二进制RPC协议,由于采用的是二进制协议,因此它很适合于发送二进制数据。——百度百科html
学习hessian,必须知道什么是RPC。git
实现RPC,必须解决以下几个问题:github
一、通信问题。web
二、寻址问题。spring
三、序列化与反序列化。apache
带着这三个问题咱们一块儿来探究一下hessian;api
首先,你们去下载源码,并导入到idea中打开;网络
https://github.com/zhaojiatao/learn_hessianmvc
项目结构:app
--learn_hessian
----api 客户端和服务端均共同引用的接口api
----client 客户端调用demo,包括本地直接经过代理对象调用以及使用spring集成方式调用
----webserver 经过传统servlet方式提供服务
----webserver_spring 与spring集成 提供服务
在web.xml中配置HessianServlet,
指明服务地址:/hessian
及其实现类:com.zjt.learn.hessian.api.impl.GetUserInfoImpl
<servlet> <servlet-name>HessianServlet</servlet-name> <servlet-class>com.caucho.hessian.server.HessianServlet</servlet-class> <init-param> <param-name>service-class</param-name> <param-value>com.zjt.learn.hessian.api.impl.GetUserInfoImpl</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>HessianServlet</servlet-name> <url-pattern>/hessian</url-pattern> </servlet-mapping>
package com.zjt.learn.hessian.api.impl; import com.zjt.learn.hessian.api.GetUserInfo; import com.zjt.learn.hessian.dto.User; import org.apache.commons.lang3.StringUtils; /** * Created by zhaojiatao@souche.com on 2018/4/17 */ public class GetUserInfoImpl implements GetUserInfo { @Override public String getuserinfo(String id) { if(StringUtils.isNotBlank(id)){ User user=new User(); user.setId("1"); user.setName("zhaojiatao"); user.setAddress("hangzhou"); user.setAge(18); user.setGender(1); return user.toString(); } return null; } }
首先在web.xml中配置springmvc:
<servlet> <servlet-name>SpringMVC</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:applicationContext.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>SpringMVC</servlet-name> <url-pattern>/remote/*</url-pattern> </servlet-mapping>
配置applicationContext.xml:
使用HessianServiceExporter来处理请求;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd" default-lazy-init="true"> <bean id = "getuserService" class="com.zjt.learn.hessian.api.impl.GetUserInfoImpl"/> <bean name="/getuserHessianService" class="org.springframework.remoting.caucho.HessianServiceExporter"> <property name="service" ref="getuserService"/> <property name="serviceInterface" value="com.zjt.learn.hessian.api.GetUserInfo"/> </bean> </beans>
package test; import com.caucho.hessian.client.HessianProxyFactory; import com.zjt.learn.hessian.api.GetUserInfo; /** * Created by zhaojiatao@souche.com on 2018/4/17 */ public class BasicClient { public static void main(String[] args) { try { String url = "http://localhost:8080/hessian"; HessianProxyFactory factory = new HessianProxyFactory(); factory.setOverloadEnabled(true); GetUserInfo getUserInfo = (GetUserInfo) factory.create(GetUserInfo.class, url); System.out.println(getUserInfo.getuserinfo("1")); System.out.println("over"); }catch (Exception e){ e.printStackTrace(); } } }
新建spring.xml配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd" default-lazy-init="true"> <bean id="getuserService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> <property name="serviceUrl" value="http://localhost:8080/remote/getuserHessianService"/> <property name="serviceInterface" value="com.zjt.learn.hessian.api.GetUserInfo"/> </bean> </beans>
package test; import com.zjt.learn.hessian.api.GetUserInfo; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * Created by zhaojiatao@souche.com on 2018/4/18 */ public class BasicSpringClient { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"classpath:spring.xml"}); GetUserInfo getUserInfo = (GetUserInfo)context.getBean("getuserService"); System.out.println(getUserInfo.getuserinfo("1")); } }
hessian的使用是很简单的。你们本身照着代码敲一下就能够。
回到文章开始提出的问题,即rpc框架须要解决的问题,看看hessian如何解决的。
咱们先看看客户端在发起远程请求前都经历了什么:
首先,客户端经过代理工厂对象HessianProxyFactory的create方法建立代理对象;
在create方法里能够看到,该代理对象在执行时的handler是经过HessianProxy代理对象的invoke方法来执行;典型的动态代理;
在invoke方法中:
经过String methodName = method.getName();获得方法名
经过sendRequest方法取得和服务端的链接HessianConnection对象;
跟踪sendRequest方法,咱们发现:
发现,hessian是使用http协议进行网络通讯;
在is = getInputStream(conn);处等待服务端返回的响应;
咱们跟踪这里:
咱们得出结论:hessian使用lookup方法来寻找远程服务;
咱们继续看刚刚跟踪的客户端调用时执行的HessianProxy对象的invoke方法,
进入其中的
conn = sendRequest(mangleName, args);
再进入
out.call(methodName, args);
再进入
writeObject(args[i]);
进入其Hessian2Output实现中
最终看到了hessian如何进行序列化:
serializer.writeObject(object, this);
至此,咱们也能够梳理一下hessian客户端动态代理的执行流程:
咱们再来看看服务端的执行细节:
经过后台的代码,可见咱们全部的工做都围绕在HessianServlet在展开。该Servlet中有两个比较重要的方法:init()、service();
init方法初始化服务和服务对象,主要分为3步:
经过home-class或者service-class建立服务端的实现类实例;
init方法还会建立HessianSkeleton对象,这是Hessian服务端的核心功能部分。
HessianSkeleton继承自AbstractSkeleton,其构造方法,将会从实现类中抽取方法和方法的Method对象,而且存储到_methodMap中。
对于一个Servlet来讲其service方法是对外提供服务的方法:
最主要的是调用HessianSkeleton对象的invoke方法。注意,Servlet实例中有两个HessianSkeleton变量,分别是:_objectSkeleton和 _homeSkeleton,
invoke方法:
首先从HessianInput对象中获取到Method信息,获取到真正的service对象。
根据反射机制,调用service对象的invoke方法,获取到返回值。
最后调用HessianOutput对象将结果写回到调用方。
本文参考:
https://blog.csdn.net/sunwei_pyw/article/details/74002351
https://www.cnblogs.com/happyday56/p/4268249.html