一、开发工具
1.jdk1.6 64位
百度网盘地址:https://pan.baidu.com/s/1Zwqfmi20X4ANNswZzPMzXQ 提取码:k50r
2.apache-maven-3.2.5
百度网盘地址:https://pan.baidu.com/s/1b9ZEnVclXhllmiCoVc3vyQ 提取码:x8jx
3.Eclipse IDE 4.11.0
百度网盘地址:https://pan.baidu.com/s/14_aDA2-xJpQBpDDtDZ_Sag 提取码:5abt
4.apache-tomcat-7.0.68
百度网盘地址:https://pan.baidu.com/s/1SFxj-l8rHpV4e091cT4vGw 提取码:w83x
二、远程通讯协议的基本原理
网络通信需要做的就是将流从一台计算机传输到另外一台计算机,基于传输协议和网络 IO 来实现,其中传输协议比较出名的有 http 、 tcp 、 udp 等等, http 、 tcp 、 udp 都是在基于 Socket 概念上为某类应用场景而扩展出的传输协议,网络 IO ,主要有 bio 、 nio 、 aio 三种方式,所有的分布式应用通讯都基于这个原理而实现,只是为了应用的易用,各种语言通常都会提供一些更为贴近应用易用的应用层协议。
三、应用级协议Binary-RPC
Binary-RPC(Remote Procedure Call Protocol,远程过程调用协议)是一种和RMI(Remote Method Invocation,远程方法调用)类似的远程调用的协议,它和RMI 的不同之处在于它以标准的二进制格式来定义请求的信息 ( 请求的对象、方法、参数等 ) ,这样的好处是什么呢,就是在跨语言通讯的时候也可以使用。
Binary -RPC 协议的一次远程通信过程:
1 、客户端发起请求,按照 Binary -RPC 协议将请求信息进行填充;
2 、填充完毕后将二进制格式文件转化为流,通过传输协议进行传输;
3 、接收到在接收到流后转换为二进制格式文件,按照 Binary -RPC 协议获取请求的信息并进行处理;
4 、处理完毕后将结果按照 Binary -RPC 协议写入二进制格式文件中并返回。
四、Hessian介绍
Hessian是一个轻量级的remoting on http工具,采用的是Binary RPC协议,所以它很适合于发送二进制数据,同时又具有防火墙穿透能力。
它基于HTTP协议传输,使用Hessian二进制序列化,对于数据包比较大的情况比较友好。但是它的参数和返回值都需要实现Serializable接口。
五、示例
1、创建服务端WebServer(Dynamic Web project)转成maven
1)服务注解
/** * @Title: Service.java * @Package com.kamfu.annotation * @Description: TODO(用一句话描述该文件做什么) * @author: liandy * @date: 2019年7月26日 下午11:20:28 * @version V1.0 */ package com.kamfu.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @ClassName: Service * @Description:服务注解类 * @author: liandy * @date: 2019年7月26日 下午11:20:28 * */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface Service { String name() default ""; }
2)Class辅助类
/** * @Title: ClassUtil.java * @Package kamfu.util * @Description: TODO(用一句话描述该文件做什么) * @author: liandy * @date: 2019年7月26日 下午10:39:29 * @version V1.0 */ package com.kamfu.util; import java.io.File; import java.io.IOException; import java.lang.annotation.Annotation; import java.net.URL; import java.util.ArrayList; import java.util.List; import com.kamfu.annotation.Service; /** * @ClassName: ClassUtil * @Description:Class工具类 * @author: liandy * @date: 2019年7月27日 上午1:32:22 * */ public class ClassUtil { /** * @Title: getAnnotationClassList * @Description:获取指定注解的类 * @param: @param an * @param: @param packageName * @param: @return * @param: @throws IOException * @param: @throws ClassNotFoundException * @return: List<Class<?>> * @throws */ public static List<Class<?>> getAnnotationClassList(String packageName,Class annotationClass) throws IOException, ClassNotFoundException { List<Class<?>> result=new ArrayList<Class<?>>(); List<Class<?>> classes=scanPackage(packageName); for(Class<?> item :classes) { @SuppressWarnings("unchecked") Object ann=item.getAnnotation(annotationClass); if(ann!=null) { result.add(item); } } return result; } /** * 获取同一路径下所有子类或接口实现类 * * @param intf * @return * @throws IOException * @throws ClassNotFoundException */ public static List<Class<?>> getAllAssignedClass(Class<?> cls) throws IOException, ClassNotFoundException { List<Class<?>> classes = new ArrayList<Class<?>>(); for (Class<?> c : getClasses(cls)) { if (cls.isAssignableFrom(c) && !cls.equals(c)) { classes.add(c); } } return classes; } public static List<Class<?>> getAllAssignedClass(Class<?> cls,String packageName) throws IOException, ClassNotFoundException { List<Class<?>> classes = new ArrayList<Class<?>>(); for (Class<?> c : scanPackage(packageName)) { if (cls.isAssignableFrom(c) && !cls.equals(c)) { classes.add(c); } } return classes; } public static List<Class<?>> scanPackage(String packageName) throws IOException, ClassNotFoundException { String path = packageName.replace('.', '/'); ClassLoader classloader = Thread.currentThread().getContextClassLoader(); URL url = classloader.getResource(path); return getClasses(new File(url.getFile()), packageName); } /** * 取得当前类路径下的所有类 * * @param cls * @return * @throws IOException * @throws ClassNotFoundException */ public static List<Class<?>> getClasses(Class<?> cls) throws IOException, ClassNotFoundException { String pk = cls.getPackage().getName(); String path = pk.replace('.', '/'); ClassLoader classloader = Thread.currentThread().getContextClassLoader(); URL url = classloader.getResource(path); return getClasses(new File(url.getFile()), pk); } /** * 迭代查找类 * * @param dir * @param pk * @return * @throws ClassNotFoundException */ private static List<Class<?>> getClasses(File dir, String pk) throws ClassNotFoundException { List<Class<?>> classes = new ArrayList<Class<?>>(); if (!dir.exists()) { return classes; } for (File f : dir.listFiles()) { if (f.isDirectory()) { classes.addAll(getClasses(f, pk + "." + f.getName())); } String name = f.getName(); if (name.endsWith(".class")) { classes.add(Class.forName(pk + "." + name.substring(0, name.length() - 6))); } } return classes; } }
3)服务接口
package com.kamfu.service; /** * @Title: IBaseService.java * @Package * @Description: TODO(用一句话描述该文件做什么) * @author: liandy * @date: 2019年7月26日 下午8:35:09 * @version V1.0 */ /** * @ClassName: IBaseService * @Description:TODO(这里用一句话描述这个类的作用) * @author: liandy * @date: 2019年7月26日 下午8:35:09 * */ public interface IBaseService { String test(); }
4)服务实现类
/** * @Title: BaseService.java * @Package com.kamfu.service * @Description: TODO(用一句话描述该文件做什么) * @author: liandy * @date: 2019年7月26日 下午8:36:11 * @version V1.0 */ package com.kamfu.service; import com.kamfu.annotation.Service; /** * @ClassName: BaseService * @Description:TODO(这里用一句话描述这个类的作用) * @author: liandy * @date: 2019年7月26日 下午8:36:11 * */ @Service public class BaseService implements IBaseService{ /** * <p>Title: test</p> * <p>Description: </p> * @return * @see com.kamfu.service.IBaseService#test() */ @Override public String test() { // TODO Auto-generated method stub return "{"a":"1","b":"2"}"; } }
5)自定义HessianServlet(可选)
/** * @Title: MyHessianServlet.java * @Package com.kamfu.service * @Description: TODO(用一句话描述该文件做什么) * @author: liandy * @date: 2019年7月26日 下午10:27:15 * @version V1.0 */ package com.kamfu.servlet; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.Collections; import java.util.HashMap; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.caucho.hessian.io.SerializerFactory; import com.caucho.hessian.server.HessianSkeleton; import com.caucho.services.server.ServiceContext; import com.kamfu.annotation.Service; import com.kamfu.util.ClassUtil; /** * @ClassName: HessianServlet * @Description:Servlet for serving Hessian services. * @author: liandy * @date: 2019年7月26日 下午10:27:15 * */ @SuppressWarnings("serial") public class HessianServlet extends HttpServlet { private Map<String, Object> serviceImplCache = Collections.synchronizedMap(new HashMap<String, Object>()); private Map<String, Class<?>> serviceAPICache = Collections.synchronizedMap(new HashMap<String, Class<?>>()); private SerializerFactory _serializerFactory; public void service(ServletRequest request, ServletResponse response) throws IOException, ServletException { // logger.debug("Hessian服务调用开始"); 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>Hessian Requires POST</h1>"); out.close(); return; } String serviceId = req.getPathInfo(); HessianSkeleton _homeSkeleton = getHomeSkeleton(serviceId); String objectId = req.getParameter("id"); if (objectId == null) objectId = req.getParameter("ejbid"); ServiceContext.begin(req, res, serviceId, objectId); try { InputStream is = request.getInputStream(); OutputStream os = response.getOutputStream(); response.setContentType("x-application/hessian"); SerializerFactory serializerFactory = getSerializerFactory(); invoke(_homeSkeleton, is, os, objectId, serializerFactory); } catch (Throwable e) { throw new ServletException(e); } finally { ServiceContext.end(); } // logger.debug("Hessian服务调用结束"); } /** * Sets the serializer factory. */ public void setSerializerFactory(SerializerFactory factory) { _serializerFactory = factory; } /** * Gets the serializer factory. */ public SerializerFactory getSerializerFactory() { if (_serializerFactory == null) _serializerFactory = new SerializerFactory(); return _serializerFactory; } /** * Sets the serializer send collection java type. */ public void setSendCollectionType(boolean sendType) { getSerializerFactory().setSendCollectionType(sendType); } /** * Sets the debugging flag. */ public void setDebug(boolean isDebug) { } /** * Sets the debugging log name. */ public void setLogName(String name) { // _log = Logger.getLogger(name); } /** * <p>Title: init</p> * <p>Description: 初始化</p> * @param config * @throws ServletException * @see javax.servlet.GenericServlet#init(javax.servlet.ServletConfig) */ public void init(ServletConfig config) throws ServletException { super.init(config); try { this.registerRemoteService(); if ("true".equals(getInitParameter("debug"))) { } if ("false".equals(getInitParameter("send-collection-type"))) setSendCollectionType(false); } catch (Throwable e) { // TODO PAO: 此处考虑如何处理Serverlet异常 throw new ServletException(e); } } /** * @Title: registerRemoteService * @Description: 注册远端服务 * @param: @throws IOException * @param: @throws ClassNotFoundException * @param: @throws InstantiationException * @param: @throws IllegalAccessException * @return: void * @throws */ private void registerRemoteService() throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { for (Class<?> c : ClassUtil.getAnnotationClassList("com.kamfu.service", Service.class)) { Class<?>[] interfaces = c.getInterfaces(); if (interfaces != null && interfaces.length > 0) { this.serviceImplCache.put(c.getSimpleName(), c.newInstance()); this.serviceAPICache.put(c.getSimpleName(), interfaces[0]); } } } /** * Invoke the object with the request from the input stream. * * @param in the Hessian input stream * @param out the Hessian output stream */ protected void invoke(HessianSkeleton skeleton, InputStream is, OutputStream os, String objectId, SerializerFactory serializerFactory) throws Exception { skeleton.invoke(is, os, serializerFactory); } private HessianSkeleton getHomeSkeleton(String serviceId) throws ServletException { String sId = (serviceId != null && serviceId.startsWith("/")) ? serviceId.substring(1) : serviceId; Class<?> _homeAPI = this.getHomeAPI(sId); Object _homeImpl = this.getHomeImpl(sId); HessianSkeleton _homeSkeleton = new HessianSkeleton(_homeImpl, _homeAPI); return _homeSkeleton; } private Class<?> getHomeAPI(String sId) { return this.serviceAPICache.get(sId); } private Object getHomeImpl(String sId) { return this.serviceImplCache.get(sId); } }
核心代码:通过服务的实例化对象及服务的接口实例化 HessianSkeleton对象。
6)maven配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>WebServer</groupId> <artifactId>WebServer</artifactId> <version>0.0.1</version> <packaging>war</packaging> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.1</version> <configuration> <warSourceDirectory>WebContent</warSourceDirectory> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>hessian</groupId> <artifactId>hessian</artifactId> <version>4.0.37</version> </dependency> <!-- <dependency> --> <!-- <groupId>com.kamfu.lib</groupId> --> <!-- <artifactId>Library</artifactId> --> <!-- <version>0.0.1</version> --> <!-- </dependency> --> </dependencies> </project>
7)web应用配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>WebServer</display-name> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <servlet> <!-- 配置 HessianServlet,Servlet的名字随便配置,例如这里配置成ServiceServlet--> <servlet-name>ServiceServlet</servlet-name> <servlet-class>com.kamfu.servlet.HessianServlet</servlet-class> <!-- 配置接口的具体实现类 --> <!-- <init-param> --> <!-- <param-name>service-class</param-name> --> <!-- <param-value>com.kamfu.service.BaseService</param-value> --> <!-- </init-param> --> </servlet> <!-- 映射 HessianServlet的访问URL地址--> <servlet-mapping> <servlet-name>ServiceServlet</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
2、创建客户端Client(Java project)转成maven
1)客户端调用
package com.kamfu.client; import com.caucho.hessian.client.HessianProxyFactory; import com.kamfu.service.IBaseService; /** * Hello world! * */ public class App { public static void main( String[] args ) { try { String url = "http://localhost:8080/WebServer/BaseService"; HessianProxyFactory factory = new HessianProxyFactory(); factory.setOverloadEnabled(true); IBaseService basic = (IBaseService) factory.create(IBaseService.class, url); System.out.println(basic.test()); }catch (Exception e){ e.printStackTrace(); } } }
2)maven配置
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.kamfu.client</groupId> <artifactId>Client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>Client</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>WebServer</groupId> <artifactId>WebServer</artifactId> <version>0.0.1</version> </dependency> </dependencies> </project>
3)客户端从服务端接收到的数据
六、原理图