简单Tomcat HTTP RPC框架

RPC基础知识

什么是RPC?

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。

RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

RPC的模型

C/S模式 
基于传输层协议(例如TCP/IP)  远程调用不是新的一种数据传输协议
事件响应基本模型(请求、计算、响应)

RPC设计的目的

通过固定的协议调用非本机的方法
提供不同语言程序之间通信
可以在不了解底层通信,像本地方法一样调用

RPC框架完全封装了网络传输以及其他细节,比如Spring 的RPC框架在调用远程对象的方法时就像调用Spring Bean 对象一样使用.

RPC的应用 大量的分布式应用都使用了RPC协议,比如分布式操作系统、分布式计算、分布式软件设计

RPC过程详解

RPC框架封装网络传输和其他细节,消费者和生产者不用去关心底层原理

消费者的代理层控制了整个RPC调用的流程,生成代理对象,封装请求报文,发送请求之类的

服务提供者会有一个监听模块,用来监听请求,并且按照约定,应该是注册了的服务才会被消费者调用到,注册的服务需要被反射调用到,用来计算结果

RPC框架的特点和设计模型

封装网络交互

尽量不要让RPC框架的使用者涉及到过多的网络层的开发

远程调用对象的代理

将接口代理的对象放入到Spring 容器之中,方便服务端开发

支持容器(Spring、Jetty等)

支持Spring容器,还有Jetty这样的web容器

可配置,可扩展

尽量做到可配置,可扩展

设计模型

Proxy代理层

用于对象的代理,对象的反射调用,RPC流程的控制

Serialize序列化层

将请求和结果做序列化和反序列化

Invoke网络模块

网络通信相关的处理

Container容器组件

支持代理层监听网络请求

代码实现

pom.xml

[html] view plain copy
 
  1. <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">  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>com.ibigsea</groupId>  
  4.   <artifactId>http-rpc</artifactId>  
  5.   <version>0.0.1-SNAPSHOT</version>  
  6.     
  7.   <dependencies>  
  8.         <dependency>  
  9.             <groupId>com.alibaba</groupId>  
  10.             <artifactId>fastjson</artifactId>  
  11.             <version>1.2.3</version>  
  12.         </dependency>  
  13.         <dependency>  
  14.             <groupId>ch.qos.logback</groupId>  
  15.             <artifactId>logback-classic</artifactId>  
  16.             <version>1.0.13</version>  
  17.         </dependency>  
  18.         <dependency>  
  19.             <groupId>org.mortbay.jetty</groupId>  
  20.             <artifactId>jetty</artifactId>  
  21.             <version>6.1.26</version>  
  22.         </dependency>  
  23.         <dependency>  
  24.             <groupId>org.apache.httpcomponents</groupId>  
  25.             <artifactId>httpcore</artifactId>  
  26.             <version>4.3.3</version>  
  27.         </dependency>  
  28.         <dependency>  
  29.             <groupId>org.apache.httpcomponents</groupId>  
  30.             <artifactId>httpclient</artifactId>  
  31.             <version>4.3.6</version>  
  32.             <exclusions>  
  33.                 <exclusion>  
  34.                     <artifactId>commons-logging</artifactId>  
  35.                     <groupId>commons-logging</groupId>  
  36.                 </exclusion>  
  37.             </exclusions>  
  38.         </dependency>  
  39.         <dependency>  
  40.             <groupId>org.springframework</groupId>  
  41.             <artifactId>spring-context</artifactId>  
  42.             <version>3.2.8.RELEASE</version>  
  43.             <exclusions>  
  44.                 <exclusion>  
  45.                     <artifactId>commons-logging</artifactId>  
  46.                     <groupId>commons-logging</groupId>  
  47.                 </exclusion>  
  48.             </exclusions>  
  49.         </dependency>  
  50.         <dependency>  
  51.           <groupId>commons-logging</groupId>  
  52.           <artifactId>commons-logging</artifactId>  
  53.           <version>1.2</version>  
  54.         </dependency>  
  55.   </dependencies>  
  56. </project>  

config包下面的

ConsumerConfig.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.config;  
  2.   
  3. /** 
  4.  * 服务消费者配置 
  5.  *  
  6.  * @author bigsea 
  7.  * 
  8.  */  
  9. public class ConsumerConfig {  
  10.   
  11.     /** 
  12.      * 请求地址 服务提供者监听的地址和端口 
  13.      */  
  14.     private String url;  
  15.   
  16.     public String getUrl() {  
  17.         return url;  
  18.     }  
  19.   
  20.     public void setUrl(String url) {  
  21.         this.url = url;  
  22.     }  
  23.   
  24. }   

ProviderConfig.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.config;  
  2.   
  3. /** 
  4.  * 服务提供者配置 
  5.  *  
  6.  * @author bigsea 
  7.  * 
  8.  */  
  9. public class ProviderConfig {  
  10.   
  11.     /** 
  12.      * 监听端口 服务提供者监听请求端口 
  13.      */  
  14.     private int port;  
  15.   
  16.     public ProviderConfig() {  
  17.     }  
  18.   
  19.     public ProviderConfig(int port) {  
  20.         this.port = port;  
  21.     }  
  22.   
  23.     public int getPort() {  
  24.         return port;  
  25.     }  
  26.   
  27.     public void setPort(int port) {  
  28.         this.port = port;  
  29.     }  
  30.   
  31. }   

序列化层

Request.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.serizlize;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. import com.alibaba.fastjson.annotation.JSONType;  
  6.   
  7. /** 
  8.  * 请求信息 
  9.  *  
  10.  * @author bigsea 
  11.  * 
  12.  */  
  13. public class Request implements Serializable {  
  14.   
  15.     private static final long serialVersionUID = -4363326153251862952L;  
  16.   
  17.     private Class clazz;  
  18.   
  19.     private String method;  
  20.   
  21.     private Object param;  
  22.   
  23.     public Request() {  
  24.     }  
  25.   
  26.     public Request(Class clazz, String method, Object param) {  
  27.         this.clazz = clazz;  
  28.         this.method = method;  
  29.         this.param = param;  
  30.     }  
  31.   
  32.     public Class getClazz() {  
  33.         return clazz;  
  34.     }  
  35.   
  36.     public void setClazz(Class clazz) {  
  37.         this.clazz = clazz;  
  38.     }  
  39.   
  40.     public String getMethod() {  
  41.         return method;  
  42.     }  
  43.   
  44.     public void setMethod(String method) {  
  45.         this.method = method;  
  46.     }  
  47.   
  48.     public Object getParam() {  
  49.         return param;  
  50.     }  
  51.   
  52.     public void setParam(Object param) {  
  53.         this.param = param;  
  54.     }  
  55.   
  56.     /** 
  57.      * 通过反射执行对应的方法 
  58.      *  
  59.      * @param bean 
  60.      * @return 
  61.      * @throws Exception 
  62.      */  
  63.     public Object invoke(Object bean) throws Exception {  
  64.         return clazz.getMethod(method, param.getClass()).invoke(bean, param);  
  65.     }  
  66.   
  67. }  

JsonParser.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.serizlize;  
  2.   
  3. import com.alibaba.fastjson.JSON;  
  4. import com.alibaba.fastjson.parser.ParserConfig;  
  5. import com.alibaba.fastjson.serializer.SerializerFeature;  
  6.   
  7. /** 
  8.  * 反序列化 
  9.  *  
  10.  * @author bigsea 
  11.  * 
  12.  */  
  13. public class JsonParser {  
  14.     /** 
  15.      * 反序列化请求 将请求反序列化成一个请求报文 
  16.      *  
  17.      * @param param 
  18.      * @return 
  19.      */  
  20.     public static Request reqParse(String param) {  
  21.         return JSON.parseObject(param, Request.class);  
  22.     }  
  23.   
  24.     /** 
  25.      * 反序列化响应 将响应反序列化成一个响应报文 
  26.      *  
  27.      * @param result 
  28.      * @return 
  29.      */  
  30.     public static <T> T resbParse(String result) {  
  31.         return (T) JSON.parse(result);  
  32.     }  
  33.   
  34. }  

JsonFormatter.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.serizlize;  
  2.   
  3. import com.alibaba.fastjson.JSON;  
  4. import com.alibaba.fastjson.parser.ParserConfig;  
  5. import com.alibaba.fastjson.serializer.SerializerFeature;  
  6.   
  7. /** 
  8.  * 序列化 
  9.  *  
  10.  * @author bigsea 
  11.  * 
  12.  */  
  13. public class JsonFormatter {  
  14.   
  15.     /** 
  16.      * 将请求序列化成字符串 
  17.      *  
  18.      * @param clazz 
  19.      * @param method 
  20.      * @param param 
  21.      * @return 
  22.      */  
  23.     public static String reqFormatter(Class clazz, String method, Object param) {  
  24.         Request request = new Request(clazz, method, param);  
  25.         return JSON.toJSONString(request, SerializerFeature.WriteClassName);  
  26.     }  
  27.   
  28.     /** 
  29.      * 将响应序列化成字符串 
  30.      *  
  31.      * @param param 
  32.      * @return 
  33.      */  
  34.     public static String resbFormatter(Object param) {  
  35.         return JSON.toJSONString(param, SerializerFeature.WriteClassName);  
  36.     }  
  37.   
  38. }  


http容器  httpContainer.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.container;  
  2.   
  3. import org.mortbay.jetty.Connector;  
  4. import org.mortbay.jetty.Server;  
  5. import org.mortbay.jetty.handler.AbstractHandler;  
  6. import org.mortbay.jetty.nio.SelectChannelConnector;  
  7. import org.slf4j.Logger;  
  8. import org.slf4j.LoggerFactory;  
  9.   
  10. import com.ibigsea.rpc.config.ProviderConfig;  
  11.   
  12. /** 
  13.  * 利用Jetty实现简单的嵌入式Httpserver 
  14.  *  
  15.  * @author bigsea 
  16.  * 
  17.  */  
  18. public class HttpContainer {  
  19.   
  20.     private Logger LOG = LoggerFactory.getLogger(HttpContainer.class);  
  21.   
  22.     private AbstractHandler httpHandler;  
  23.     private ProviderConfig providerConfig;  
  24.   
  25.     /** 
  26.      * 构造方法 
  27.      *  
  28.      * @param httpHandler 
  29.      */  
  30.     public HttpContainer(AbstractHandler httpHandler) {  
  31.         this(httpHandler, new ProviderConfig(8080));  
  32.     }  
  33.   
  34.     /** 
  35.      * 构造方法 
  36.      *  
  37.      * @param httpHandler 
  38.      * @param providerConfig 
  39.      */  
  40.     public HttpContainer(AbstractHandler httpHandler, ProviderConfig providerConfig) {  
  41.         this.httpHandler = httpHandler;  
  42.         this.providerConfig = providerConfig;  
  43.     }  
  44.   
  45.     public void start() {  
  46.         // 进行服务器配置  
  47.         Server server = new Server();  
  48.         try {  
  49.             SelectChannelConnector connector = new SelectChannelConnector();  
  50.             // 设置监听端口  
  51.             connector.setPort(providerConfig.getPort());  
  52.             // 设置handler,请求过来之后通过该handler来处理请求  
  53.             server.setHandler(httpHandler);  
  54.             server.setConnectors(new Connector[] { connector });  
  55.             server.start();  
  56.             LOG.info("容器启动~");  
  57.         } catch (Exception e) {  
  58.             LOG.error("容器启动异常~", e);  
  59.         }  
  60.   
  61.     }  
  62.   
  63. }   

RpcException.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.exception;  
  2.   
  3. /** 
  4.  * 异常 
  5.  *  
  6.  * @author bigsea 
  7.  * 
  8.  */  
  9. public class RpcException extends Throwable {  
  10.     private Object data;  
  11.   
  12.     public RpcException(String message, Throwable cause, Object data) {  
  13.         super(message, cause);  
  14.         this.data = data;  
  15.     }  
  16.   
  17.     public RpcException(Object data) {  
  18.         super();  
  19.         this.data = data;  
  20.     }  
  21.   
  22.     public Object getData() {  
  23.         return data;  
  24.     }  
  25. }  

HttpInvoke.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.invoke;  
  2.   
  3. import java.io.OutputStream;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. import org.apache.http.HttpHost;  
  8. import org.apache.http.HttpResponse;  
  9. import org.apache.http.NameValuePair;  
  10. import org.apache.http.client.HttpClient;  
  11. import org.apache.http.client.entity.UrlEncodedFormEntity;  
  12. import org.apache.http.client.methods.HttpPost;  
  13. import org.apache.http.conn.routing.HttpRoute;  
  14. import org.apache.http.impl.client.HttpClients;  
  15. import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;  
  16. import org.apache.http.message.BasicNameValuePair;  
  17. import org.apache.http.util.EntityUtils;  
  18.   
  19. import com.ibigsea.rpc.config.ConsumerConfig;  
  20. import com.ibigsea.rpc.exception.RpcException;  
  21.   
  22. /** 
  23.  * http请求和响应处理 
  24.  *  
  25.  * @author bigsea 
  26.  * 
  27.  */  
  28. public class HttpInvoke {  
  29.   
  30.     private static final HttpClient httpClient = getHttpClient();  
  31.   
  32.     /** 
  33.      * 单例 
  34.      */  
  35.     private static HttpInvoke httpInvoke;  
  36.   
  37.     private HttpInvoke() {  
  38.   
  39.     }  
  40.   
  41.     public static synchronized HttpInvoke getInstance() {  
  42.         if (httpInvoke == null) {  
  43.             httpInvoke = new HttpInvoke();  
  44.         }  
  45.         return httpInvoke;  
  46.     }  
  47.   
  48.     /** 
  49.      * 发送请求 
  50.      *  
  51.      * @param request 
  52.      *            服务消费者将 (类信息、方法、参数)封装成请求报文,序列化后的字符串 
  53.      * @param consumerConfig 
  54.      *            服务消费者请求的地址 
  55.      * @return 请求结果 
  56.      * @throws RpcException 
  57.      */  
  58.     public String request(String request, ConsumerConfig consumerConfig) throws RpcException {  
  59.         HttpPost post = new HttpPost(consumerConfig.getUrl());  
  60.         // 使用长连接  
  61.         post.setHeader("Connection", "Keep-Alive");  
  62.         List<NameValuePair> params = new ArrayList<NameValuePair>();  
  63.         params.add(new BasicNameValuePair("data", request));  
  64.   
  65.         try {  
  66.             post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));  
  67.             HttpResponse response = httpClient.execute(post);  
  68.             if (response.getStatusLine().getStatusCode() == 200) {  
  69.                 return EntityUtils.toString(response.getEntity(), "UTF-8");  
  70.             }  
  71.             throw new RpcException(request);  
  72.         } catch (Exception e) {  
  73.             throw new RpcException("http调用异常", e, request);  
  74.         }  
  75.     }  
  76.   
  77.     /** 
  78.      * 响应结果 服务提供者根据服务消费者的请求报文执行后返回结果信息 
  79.      *  
  80.      * @param response 
  81.      * @param outputStream 
  82.      * @throws RpcException 
  83.      */  
  84.     public void response(String response, OutputStream outputStream) throws RpcException {  
  85.         try {  
  86.             outputStream.write(response.getBytes("UTF-8"));  
  87.             outputStream.flush();  
  88.         } catch (Exception e) {  
  89.             e.printStackTrace();  
  90.         }  
  91.     }  
  92.   
  93.     private static HttpClient getHttpClient() {  
  94.         PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();  
  95.         // 连接池最大生成连接数200  
  96.         cm.setMaxTotal(200);  
  97.         // 默认设置route最大连接数为20  
  98.         cm.setDefaultMaxPerRoute(20);  
  99.         // 指定专门的route,设置最大连接数为80  
  100.         HttpHost localhost = new HttpHost("localhost", 8080);  
  101.         cm.setMaxPerRoute(new HttpRoute(localhost), 50);  
  102.         // 创建httpClient  
  103.         return HttpClients.custom().setConnectionManager(cm).build();  
  104.     }  
  105.   
  106. }  

接下来就是代理成了,因为我们使用了jetty容器,所以这里对服务提供者的代理我们通过jetty的AbstractHandler来实现请求的处理

ProviderProxyFactory.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.proxy;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.Map;  
  5. import java.util.concurrent.ConcurrentHashMap;  
  6.   
  7. import javax.servlet.ServletException;  
  8. import javax.servlet.http.HttpServletRequest;  
  9. import javax.servlet.http.HttpServletResponse;  
  10.   
  11. import org.mortbay.jetty.handler.AbstractHandler;  
  12. import org.mortbay.log.Log;  
  13. import org.slf4j.Logger;  
  14. import org.slf4j.LoggerFactory;  
  15.   
  16. import com.ibigsea.rpc.config.ProviderConfig;  
  17. import com.ibigsea.rpc.container.HttpContainer;  
  18. import com.ibigsea.rpc.exception.RpcException;  
  19. import com.ibigsea.rpc.invoke.HttpInvoke;  
  20. import com.ibigsea.rpc.serizlize.JsonFormatter;  
  21. import com.ibigsea.rpc.serizlize.JsonParser;  
  22. import com.ibigsea.rpc.serizlize.Request;  
  23.   
  24. /** 
  25.  * 服务提供者代理 
  26.  *  
  27.  * @author bigsea 
  28.  * 
  29.  */  
  30. public class ProviderProxyFactory extends AbstractHandler {  
  31.   
  32.     private Logger LOG = LoggerFactory.getLogger(ProviderProxyFactory.class);  
  33.   
  34.     /** 
  35.      * 提供服务需要注册,这里使用map类实现简单的注册 约定俗成的,暴漏服务是需要注册的 
  36.      */  
  37.     private Map<Class, Object> providers = new ConcurrentHashMap<Class, Object>();  
  38.   
  39.     /** 
  40.      * 这里用来获取暴露的服务 
  41.      */  
  42.     private static ProviderProxyFactory factory;  
  43.   
  44.     private static HttpInvoke invoke = HttpInvoke.getInstance();  
  45.   
  46.     /** 
  47.      * 构造方法 注册服务 创建http容器,并启动 
  48.      *  
  49.      * @param providers 
  50.      * @param config 
  51.      */  
  52.     public ProviderProxyFactory(Map<Class, Object> providers, ProviderConfig config) {  
  53.         this.providers = providers;  
  54.         HttpContainer container = new HttpContainer(this, config);  
  55.         container.start();  
  56.         factory = this;  
  57.         for (Map.Entry<Class, Object> entry : providers.entrySet()) {  
  58.             Log.info(entry.getKey().getSimpleName() + " register");  
  59.         }  
  60.     }  
  61.   
  62.     /** 
  63.      * 处理请求 服务消费者发送请求报文过来,服务提供者解析请求报文,通过反射执行方法 
  64.      */  
  65.     public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)  
  66.             throws IOException, ServletException {  
  67.         // 获取请求报文  
  68.         String data = request.getParameter("data");  
  69.   
  70.         try {  
  71.             // 反序列化  
  72.             Request req = JsonParser.reqParse(data);  
  73.             // 获取到注册的服务,并通过反射执行方法  
  74.             Object res = req.invoke(ProviderProxyFactory.getInstance().getBeanByClass(req.getClazz()));  
  75.             // 返回结果  
  76.             invoke.response(JsonFormatter.resbFormatter(res), response.getOutputStream());  
  77.         } catch (Exception e) {  
  78.             e.printStackTrace();  
  79.         } catch (RpcException e) {  
  80.             e.printStackTrace();  
  81.         }  
  82.   
  83.     }  
  84.   
  85.     public Object getBeanByClass(Class clazz) throws RpcException {  
  86.         Object bean = providers.get(clazz);  
  87.         if (bean != null) {  
  88.             return bean;  
  89.         }  
  90.         throw new RpcException("service no register", new NullPointerException(), clazz);  
  91.     }  
  92.   
  93.     public static ProviderProxyFactory getInstance() {  
  94.         return factory;  
  95.     }  
  96.   
  97. }  


对于服务消费者,我们通过jdk的invocationHandler来生成代理对象,对于生成的代理对象都会去执行invoke方法

ConsumerProxyFatory.java

[java] view plain copy
 
  1. package com.ibigsea.rpc.proxy;  
  2.   
  3. import java.lang.reflect.InvocationHandler;  
  4. import java.lang.reflect.Method;  
  5. import java.lang.reflect.Proxy;  
  6.   
  7. import com.ibigsea.rpc.config.ConsumerConfig;  
  8. import com.ibigsea.rpc.invoke.HttpInvoke;  
  9. import com.ibigsea.rpc.serizlize.JsonFormatter;  
  10. import com.ibigsea.rpc.serizlize.JsonParser;  
  11.   
  12. /** 
  13.  * 服务消费者代理 
  14.  *  
  15.  * @author bigsea 
  16.  * 
  17.  */  
  18. public class ConsumerProxyFactory implements InvocationHandler {  
  19.   
  20.     /** 
  21.      * 消费者配置 
  22.      */  
  23.     private ConsumerConfig config;  
  24.   
  25.     /** 
  26.      * 需要通过远程调用的服务 
  27.      */  
  28.     private String clazz;  
  29.   
  30.     private static HttpInvoke invoke = HttpInvoke.getInstance();  
  31.   
  32.     /** 
  33.      * 创建一个动态代理对象,创建出来的动态代理对象会执行invoke方法 
  34.      *  
  35.      * @return 
  36.      * @throws ClassNotFoundException 
  37.      */  
  38.     public Object create() throws ClassNotFoundException {  
  39.         Class interfaceClass = Class.forName(clazz);  
  40.         return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass }, this);  
  41.     }  
  42.   
  43.     /** 
  44.      * 动态代理对象执行该方法 获取(类信息,方法,参数)通过序列化封装成请求报文,通过http请求发送报文到服务提供者 
  45.      */  
  46.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  47.         // 获取类信息  
  48.         Class interfaceClass = proxy.getClass().getInterfaces()[0];  
  49.         // 封装成请求报文  
  50.         String req = JsonFormatter.reqFormatter(interfaceClass, method.getName(), args[0]);  
  51.         // 发送请求报文  
  52.         String resb = invoke.request(req, config);  
  53.         // 解析响应报文  
  54.         return JsonParser.resbParse(resb);  
  55.     }  
  56.   
  57.     public ConsumerConfig getConfig() {  
  58.         return config;  
  59.     }  
  60.   
  61.     public void setConfig(ConsumerConfig config) {  
  62.         this.config = config;  
  63.     }  
  64.   
  65.     public String getClazz() {  
  66.         return clazz;  
  67.     }  
  68.   
  69.     public void setClazz(String clazz) {  
  70.         this.clazz = clazz;  
  71.     }  
  72.   
  73. }  


简单的RPC框架已经写好

然后我们准备一个公共的接口jar

pom里面什么依赖都没有

HelloInterface.java

[java] view plain copy
 
  1. package com.ibigsea.facade;  
  2.   
  3. import com.ibigsea.vo.People;  
  4.   
  5. /** 
  6.  * 定义一个接口,如此而已 
  7.  * @author bigsea 
  8.  * 
  9.  */  
  10. public interface HelloInterface {  
  11.   
  12.     public String speak(People people);  
  13.       
  14. }  


People.java

[java] view plain copy
 
  1. package com.ibigsea.vo;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. /** 
  6.  * 实体 
  7.  * @author bigsea 
  8.  * 
  9.  */  
  10. public class People implements Serializable {  
  11.   
  12.     private static final long serialVersionUID = 1L;  
  13.       
  14.     private String name;  
  15.   
  16.     public People() {  
  17.     }  
  18.   
  19.     public People(String name) {  
  20.         this.name = name;  
  21.     }  
  22.   
  23.     public String getName() {  
  24.         return name;  
  25.     }  
  26.   
  27.     public void setName(String name) {  
  28.         this.name = name;  
  29.     }  
  30.       
  31. }  


接口定义好了 ,我们可以开始弄服务提供者和服务消费者了

服务提供者


pom.xml

[java] view plain copy
 
  1. <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">  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>com.ibigsea</groupId>  
  4.   <artifactId>demo-provider</artifactId>  
  5.   <version>0.0.1-SNAPSHOT</version>  
  6.   <dependencies>  
  7.     <dependency>    
  8.         <groupId>com.ibigsea</groupId>  
  9.         <artifactId>http-rpc</artifactId>  
  10.         <version>0.0.1-SNAPSHOT</version>  
  11.     </dependency>  
  12.     <dependency>    
  13.           <groupId>com.ibigsea</groupId>  
  14.           <artifactId>demo-facade</artifactId>  
  15.           <version>0.0.1-SNAPSHOT</version>  
  16.     </dependency>  
  17.   </dependencies>  
  18. </project>  

HelloService.java

[java] view plain copy
 
  1. package com.ibigsea.service;  
  2.   
  3. import org.springframework.stereotype.Service;  
  4.   
  5. import com.ibigsea.facade.HelloInterface;  
  6. import com.ibigsea.vo.People;  
  7.   
  8. /** 
  9.  * 实现接口,通过spring配置文件,暴漏出一个服务 
  10.  * @author bigsea 
  11.  * 
  12.  */  
  13. @Service("helloInterface")  
  14. public class HelloService implements HelloInterface {  
  15.   
  16.     /** 
  17.      * 方法实现,服务消费者最终执行到该方法 
  18.      */  
  19.     public String speak(People people) {  
  20.         return "Hello " + people.getName();  
  21.     }  
  22.   
  23. }   

启动类App.java

[java] view plain copy
 
  1. package com.ibigsea;  
  2.   
  3. import java.util.concurrent.CountDownLatch;  
  4.   
  5. import org.springframework.context.support.ClassPathXmlApplicationContext;  
  6.   
  7. /** 
  8.  * 启动类 
  9.  * @author bigsea 
  10.  * 
  11.  */  
  12. public class App {  
  13.       
  14.      public static void main(String[] args) throws Exception {  
  15.          ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring-*.xml");  
  16.          context.start();  
  17.          CountDownLatch countDownLatch = new CountDownLatch(1);  
  18.          countDownLatch.await();  
  19.      }  
  20.       
  21. }   

spring-context.xml

[html] view plain copy
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"  
  4.     xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"  
  5.     xmlns:util="http://www.springframework.org/schema/util"  
  6.     xsi:schemaLocation="  
  7.           http://www.springframework.org/schema/beans  
  8.           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  9.        http://www.springframework.org/schema/tx  
  10.        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  11.        http://www.springframework.org/schema/aop  
  12.        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  13.        http://www.springframework.org/schema/util  
  14.        http://www.springframework.org/schema/util/spring-util-3.1.xsd  
  15.        http://www.springframework.org/schema/context  
  16.        http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  17.   
  18.   
  19.     <!-- 扫描包 -->  
  20.     <context:component-scan base-package="com.ibigsea" />  
  21.     <!-- 支持注解配置 -->  
  22.     <context:annotation-config />  
  23.   
  24.     <!-- 构造注入,声明需要暴漏的服务. 还有需要监听的地址 -->  
  25.     <bean class="com.ibigsea.rpc.proxy.ProviderProxyFactory">  
  26.         <constructor-arg name="providers">  
  27.             <map key-type="java.lang.Class" value-type="java.lang.Object">  
  28.                 <!-- 注册服务,类信息,和接口实现 -->  
  29.                 <entry key="com.ibigsea.facade.HelloInterface" value-ref="helloInterface"/>  
  30.             </map>  
  31.         </constructor-arg>  
  32.         <constructor-arg name="config">  
  33.             <bean id="providerConfig" class="com.ibigsea.rpc.config.ProviderConfig">  
  34.                 <property name="port" value="8888"/>  
  35.             </bean>  
  36.         </constructor-arg>  
  37.     </bean>  
  38.   
  39. </beans>  

服务消费者



pom.xml

[html] view plain copy
 
  1. <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">  
  2.   <modelVersion>4.0.0</modelVersion>  
  3.   <groupId>com.ibigsea</groupId>  
  4.   <artifactId>demo-comsumer</artifactId>  
  5.   <version>0.0.1-SNAPSHOT</version>  
  6.     
  7.   <dependencies>  
  8.     <dependency>    
  9.         <groupId>com.ibigsea</groupId>  
  10.         <artifactId>http-rpc</artifactId>  
  11.         <version>0.0.1-SNAPSHOT</version>  
  12.     </dependency>  
  13.     <dependency>    
  14.           <groupId>com.ibigsea</groupId>  
  15.           <artifactId>demo-facade</artifactId>  
  16.           <version>0.0.1-SNAPSHOT</version>  
  17.     </dependency>  
  18.         <dependency>  
  19.             <groupId>junit</groupId>  
  20.             <artifactId>junit</artifactId>  
  21.             <version>4.11</version>  
  22.         </dependency>  
  23.         <dependency>  
  24.             <groupId>org.springframework</groupId>  
  25.             <artifactId>spring-test</artifactId>  
  26.             <version>3.2.8.RELEASE</version>  
  27.             <scope>test</scope>  
  28.         </dependency>  
  29.   </dependencies>  
  30.     
  31. </project>  

RefService.java

[java] view plain copy
 
  1. package com.ibigsea.comsumer;  
  2.   
  3. import javax.annotation.Resource;  
  4.   
  5. import org.springframework.stereotype.Service;  
  6.   
  7. import com.ibigsea.facade.HelloInterface;  
  8. import com.ibigsea.vo.People;  
  9.   
  10. @Service("refService")  
  11. public class RefService {  
  12.       
  13.     //这里引用到的是java生成的代理对象  
  14.     @Resource  
  15.     private HelloInterface helloInterface;  
  16.       
  17.     public void sayHello(String name) {  
  18.         System.out.println(helloInterface.speak(new People(name)));  
  19.     }  
  20.       
  21. }   

spring-context.xml

[html] view plain copy
 
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"  
  4.        xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"  
  5.        xmlns:util="http://www.springframework.org/schema/util"  
  6.        xsi:schemaLocation="  
  7.           http://www.springframework.org/schema/beans  
  8.           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
  9.        http://www.springframework.org/schema/tx  
  10.        http://www.springframework.org/schema/tx/spring-tx-3.0.xsd  
  11.        http://www.springframework.org/schema/aop  
  12.        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd  
  13.        http://www.springframework.org/schema/util  
  14.        http://www.springframework.org/schema/util/spring-util-3.1.xsd  
  15.        http://www.springframework.org/schema/context  
  16.        http://www.springframework.org/schema/context/spring-context-3.0.xsd">  
  17.   
  18.     <context:component-scan base-package="com.ibigsea" />  
  19.     <context:annotation-config />  
  20.   
  21. <!--     服务消费者请求的地址 -->  
  22.     <bean id="consumerConfig" class="com.ibigsea.rpc.config.ConsumerConfig">  
  23.         <property name="url" value="http://localhost:8888/invoke" />  
  24.     </bean>  
  25.   
  26. <!-- 设置请求地址,需要生成代理的代理对象 -->  
  27.     <bean id="helloInterfaceInvoke" class="com.ibigsea.rpc.proxy.ConsumerProxyFactory">  
  28.         <property name="config" ref="consumerConfig"/>  
  29.         <property name="clazz" value="com.ibigsea.facade.HelloInterface"/>  
  30.     </bean>  
  31. <!--    产生代理对象,服务消费者可以直接通过@Resource注解引用到该对象,通过http-rpc框架调用到服务消费者 -->  
  32.     <bean id="helloInterface" factory-bean="helloInterfaceInvoke" factory-method="create"/>  
  33. </beans>  

测试类HttpRpcTest.java

[java] view plain copy
 
  1. package com.zto.test;  
  2.   
  3. import org.junit.Test;  
  4. import org.junit.runner.RunWith;  
  5. import org.slf4j.Logger;  
  6. import org.slf4j.LoggerFactory;  
  7. import org.springframework.beans.factory.annotation.Autowired;  
  8. import org.springframework.test.context.ContextConfiguration;  
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  10.   
  11. import com.ibigsea.comsumer.RefService;  
  12. import com.ibigsea.facade.HelloInterface;  
  13. import com.ibigsea.rpc.serizlize.JsonFormatter;  
  14.   
  15. /** 
  16.  * 测试类 
  17.  * @author bigsea 
  18.  * 
  19.  */  
  20. @RunWith(SpringJUnit4ClassRunner.class)  
  21. @ContextConfiguration({"classpath*:spring-*.xml"})  
  22. public class HttpRpcTest  
  23. {  
  24.     private static final Logger logger = LoggerFactory.getLogger(HttpRpcTest.class);  
  25.   
  26.     @Autowired  
  27.     private RefService service;  
  28.   
  29.     @Test  
  30.     public void test() throws InterruptedException {  
  31.         service.sayHello("张三");  
  32.     }  
  33. }  


我们可以启动程序看看

先启动服务提供者,服务提供者控制台

[html] view plain copy
 
  1. 五月 01, 2017 2:43:43 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh  
  2. 信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@685f4c2e: startup date [Mon May 01 14:43:43 CST 2017]; root of context hierarchy  
  3. 五月 01, 2017 2:43:43 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions  
  4. 信息: Loading XML bean definitions from file [E:workspace pcworkspacedemo-provider argetclassesspring-context.xml]  
  5. 五月 01, 2017 2:43:44 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons  
  6. 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@26be92ad: defining beans [helloInterface,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,com.ibigsea.rpc.proxy.ProviderProxyFactory#0,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy  
  7. 14:43:45.608 [main] INFO  org.mortbay.log - Logging to Logger[org.mortbay.log] via org.mortbay.log.Slf4jLog  
  8. 14:43:45.622 [main] DEBUG org.mortbay.log - Container Server@4562e04d + ProviderProxyFactory@2a65fe7c as handler  
  9. 14:43:45.622 [main] DEBUG org.mortbay.log - Container Server@4562e04d + SelectChannelConnector@0.0.0.0:8888 as connector  
  10. 14:43:45.622 [main] INFO  org.mortbay.log - jetty-6.1.26  
  11. 14:43:45.638 [main] DEBUG org.mortbay.log - Container Server@4562e04d + org.mortbay.thread.QueuedThreadPool@235834f2 as threadpool  
  12. 14:43:45.639 [main] DEBUG org.mortbay.log - started org.mortbay.thread.QueuedThreadPool@235834f2  
  13. 14:43:45.640 [main] DEBUG org.mortbay.log - starting ProviderProxyFactory@2a65fe7c  
  14. 14:43:45.640 [main] DEBUG org.mortbay.log - started ProviderProxyFactory@2a65fe7c  
  15. 14:43:45.640 [main] DEBUG org.mortbay.log - starting Server@4562e04d  
  16. 14:43:45.691 [main] DEBUG org.mortbay.log - started org.mortbay.jetty.nio.SelectChannelConnector$1@2b71e916  
  17. 14:43:45.692 [main] INFO  org.mortbay.log - Started SelectChannelConnector@0.0.0.0:8888  
  18. 14:43:45.693 [main] DEBUG org.mortbay.log - started SelectChannelConnector@0.0.0.0:8888  
  19. 14:43:45.693 [main] DEBUG org.mortbay.log - started Server@4562e04d  
  20. 14:43:45.693 [main] INFO  c.i.rpc.container.HttpContainer - 容器启动~  
  21. 14:43:45.693 [main] INFO  org.mortbay.log - HelloInterface register  


然后执行服务消费者的测试方法,服务消费者控制台:

[html] view plain copy
 
  1. 五月 01, 2017 2:44:31 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners  
  2. 信息: Could not instantiate TestExecutionListener class [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.  
  3. 五月 01, 2017 2:44:31 下午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners  
  4. 信息: Could not instantiate TestExecutionListener class [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their dependencies) available.  
  5. 五月 01, 2017 2:44:31 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions  
  6. 信息: Loading XML bean definitions from file [E:workspace pcworkspacedemo-comsumer argetclassesspring-context.xml]  
  7. 五月 01, 2017 2:44:32 下午 org.springframework.context.support.GenericApplicationContext prepareRefresh  
  8. 信息: Refreshing org.springframework.context.support.GenericApplicationContext@7946e1f4: startup date [Mon May 01 14:44:32 CST 2017]; root of context hierarchy  
  9. 五月 01, 2017 2:44:32 下午 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons  
  10. 信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4f51b3e0: defining beans [refService,org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,consumerConfig,helloInterfaceInvoke,helloInterface,org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor]; root of factory hierarchy  
  11. Hello 张三  
  12. 五月 01, 2017 2:44:33 下午 org.springframework.context.support.GenericApplicationContext doClose  
  13. 信息: Closing org.springframework.context.support.GenericApplicationContext@7946e1f4: startup date [Mon May 01 14:44:32 CST 2017]; root of context hierarchy  


服务提供者这里输出了消费者请求的日志

[html] view plain copy
 
  1. 14:44:33.117 [348984985@qtp-592983282-1 - /invoke] DEBUG org.mortbay.log - REQUEST /invoke on org.mortbay.jetty.HttpConnection@6e01b90  
  2. 14:44:33.213 [348984985@qtp-592983282-1 - /invoke] DEBUG org.mortbay.log - RESPONSE /invoke  200  


演示成功

注意


因为这里使用了fastjson,  而我们的Request里面有类信息,进行序列化和反序列的时候我们要在启动类增加参数

 -Dfastjson.parser.autoTypeSupport=true

其他解决方案看这里

https://github.com/alibaba/fastjson/wiki/enable_autotype

好了,这里的代码我会上传到我的github上面

原文地址:https://www.cnblogs.com/csguo/p/7572984.html