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

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

config包下面的

ConsumerConfig.java

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

ProviderConfig.java

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

序列化层

Request.java

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

JsonParser.java

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

JsonFormatter.java

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

http容器  httpContainer.java

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

RpcException.java

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

HttpInvoke.java

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

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

ProviderProxyFactory.java

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

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

ConsumerProxyFatory.java

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

简单的RPC框架已经写好

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

pom里面什么依赖都没有

HelloInterface.java

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

People.java

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

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

服务提供者


pom.xml

  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

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

启动类App.java

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

spring-context.xml

  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. <!-- 支持注解配置 -->
  20. <context:annotation-config />
  21. <!-- 构造注入,声明需要暴漏的服务. 还有需要监听的地址 -->
  22. <bean class="com.ibigsea.rpc.proxy.ProviderProxyFactory">
  23. <constructor-arg name="providers">
  24. <map key-type="java.lang.Class" value-type="java.lang.Object">
  25. <!-- 注册服务,类信息,和接口实现 -->
  26. <entry key="com.ibigsea.facade.HelloInterface" value-ref="helloInterface"/>
  27. </map>
  28. </constructor-arg>
  29. <constructor-arg name="config">
  30. <bean id="providerConfig" class="com.ibigsea.rpc.config.ProviderConfig">
  31. <property name="port" value="8888"/>
  32. </bean>
  33. </constructor-arg>
  34. </bean>
  35. </beans>

服务消费者

pom.xml

  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. <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. <dependency>
  18. <groupId>junit</groupId>
  19. <artifactId>junit</artifactId>
  20. <version>4.11</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.springframework</groupId>
  24. <artifactId>spring-test</artifactId>
  25. <version>3.2.8.RELEASE</version>
  26. <scope>test</scope>
  27. </dependency>
  28. </dependencies>
  29. </project>

RefService.java

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

spring-context.xml

  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. <context:component-scan base-package="com.ibigsea" />
  18. <context:annotation-config />
  19. <!--     服务消费者请求的地址 -->
  20. <bean id="consumerConfig" class="com.ibigsea.rpc.config.ConsumerConfig">
  21. <property name="url" value="http://localhost:8888/invoke" />
  22. </bean>
  23. <!-- 设置请求地址,需要生成代理的代理对象 -->
  24. <bean id="helloInterfaceInvoke" class="com.ibigsea.rpc.proxy.ConsumerProxyFactory">
  25. <property name="config" ref="consumerConfig"/>
  26. <property name="clazz" value="com.ibigsea.facade.HelloInterface"/>
  27. </bean>
  28. <!--    产生代理对象,服务消费者可以直接通过@Resource注解引用到该对象,通过http-rpc框架调用到服务消费者 -->
  29. <bean id="helloInterface" factory-bean="helloInterfaceInvoke" factory-method="create"/>
  30. </beans>

测试类HttpRpcTest.java

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

我们可以启动程序看看

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

  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\rpcworkspace\demo-provider\target\classes\spring-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

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

  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\rpcworkspace\demo-comsumer\target\classes\spring-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

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

  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上面

简单Tomcat HTTP RPC框架的更多相关文章

  1. 服务化实战之 dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    转自: http://blog.csdn.net/liubenlong007/article/details/54692241 概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺 ...

  2. dubbo、dubbox、motan、thrift、grpc等RPC框架比较及选型

    概述 前段时间项目要做服务化,所以我比较了现在流行的几大RPC框架的优缺点以及使用场景,最终结合本身项目的实际情况选择了使用dubbox作为rpc基础服务框架.下面就简单介绍一下RPC框架技术选型的过 ...

  3. 手动造轮子——基于.NetCore的RPC框架DotNetCoreRpc

    前言     一直以来对内部服务间使用RPC的方式调用都比较赞同,因为内部间没有这么多限制,最简单明了的方式就是最合适的方式.个人比较喜欢类似Dubbo的那种使用方式,把接口层单独出来,作为服务的契约 ...

  4. 简单RPC框架-基于Consul的服务注册与发现

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  5. 一个简单的基于BIO的RPC框架

    github地址:https://github.com/Luyu05/BioRpcExample PART1:先来整体看下项目的构成 其中bio-rpc-core就是所谓的rpc框架 bio-rpc- ...

  6. 最简单的RPC框架实现

    通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成: 1.服务提供者,运行再服务端,负责提供服务接口定义和服务实现类 2.服务发布者,运行再RPC服 ...

  7. 分布式架构的基石.简单的 RPC 框架实现(JAVA)

    前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...

  8. Java实现简单的RPC框架

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  9. RPC笔记之初探RPC:DIY简单RPC框架

    一.什么是RPC RPC(Remote Procedure Call)即远程过程调用,简单的说就是在A机器上去调用B机器上的某个方法,在分布式系统中极其常用. rpc原理其实很简单,比较容易理解,在r ...

随机推荐

  1. C++实现wc.exe程序

    github项目地址:https://github.com/insomniali/wc 基本功能 wc.exe -c file     统计文件file的字符数  [实现] wc.exe -w fil ...

  2. 在TFS中使用Git Tags(标签或标记),实现代码的版本管理

    一.概述: 与TFVC中标记(Label)一样,Git的标签(Tag)也是TFS系统的代码管理中非常重要的一个版本管理工具.使用标签,我们可以每个时间点的代码注上一个通俗.并且容易记忆的名称(例如标签 ...

  3. DevOps Workshop 研发运维一体化(广州站)

    第一天对软件开发的需求管理.项目计划和源代码管理进行的全面而深入的介绍,并且为到会的所有开发人员提供现场动手实验的机会,大家兴致高涨,按照我们的操作手册完成了所有实验课题. 第二天主要介绍了最新的自动 ...

  4. LeetCode135:Candy

    题目: There are N children standing in a line. Each child is assigned a rating value. You are giving c ...

  5. C#计算机性能参数

    Process proc = Process.GetCurrentProcess(); //string info = "内存:" + (Environment.WorkingSe ...

  6. keil小技能随用随定义

    大家都知道在C语言编程时一般都是先定义再使用这个变量的,不允许在语句的后面再定义,但是有时候我们会在KEIL中发现有些人使用变量就在语句后定义,这时我们自己去尝试却发现总是失败,这是为何呢? 原来是我 ...

  7. [LeetCode] Minimum Number of K Consecutive Bit Flips 连续K位翻转的最小次数

    In an array A containing only 0s and 1s, a K-bit flip consists of choosing a (contiguous) subarray o ...

  8. Java并发工具类之线程间数据交换工具Exchanger

    Exchanger是一个用于线程间协做的工具类,主要用于线程间的数据交换.它提供了一个同步点,在这个同步点,两个线程可以彼此交换数据.两个线程通过exchange方法交换数据,如果一个线程执行exch ...

  9. Swift 里字符串(三)small String

     small string, 只有两个 UInt64 的字,这里面存储了所有的信息. 内存布局如下:  第二个 UInt64 存储了标记位和长度信息,以及部分字符串的值 // Get an int ...

  10. git 忽略某些文件的命令

    正常的,我们在提交项目版本的时候,经常会存在一些需要忽略的文件或者文件夹,那这个时候,我们就可以通过git的一些操作命令来实现! vim .gitignore 通过上面一句话进入编辑忽略文件/文件夹的 ...