RPC(Remote Procedure Call Protocol) 远程过程调用协议,在Java中.即本地程序可以调用远程的对象里面的方法进行操作.
一个简单的RPC框架,实现的功能就是让不同的机器之间的程序可以相互调用其内部的方法.这里可以理解为spring的bean.
RPC:远程端 发送调用请求RPC,RPC返回请求的对象给远程端.
Spring : 类 通过注解/XML配置文件 向Spring容器发送请求的类名/id/类型,spring容器返回请求的对象.
功能上极其相似,只是一个是本地,一个是远程.那么RPC框架也需要这几步.
抽象出来,可以分为以下几层:
- 容器层:负责监听消费者的请求/生产者监听请求
- 代理层:负责数据的远端调用.
- 通信层:负责数据的传输
- 序列化层:负责数据的转换
OK,下面开始写框架.
我用的是maven来搭建,下面是maven的pom.xml
<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.pan</groupId>
<artifactId>http-rpc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>http-rpc</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<fastjson.version>1.2.17</fastjson.version>
<logback.version>1.0.13</logback.version>
<jetty.vesion>6.1.26</jetty.vesion>
<httpcompent.core.version>4.3.3</httpcompent.core.version>
<httpcompent.client.version>4.3.6</httpcompent.client.version>
<spring.context.version>4.2.8.RELEASE</spring.context.version>
<junit.version>4.12</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${fastjson.version}</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
</dependency>
<dependency>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty</artifactId>
<version>${jetty.vesion}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>${httpcompent.core.version}</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>${httpcompent.core.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.context.version}</version>
<exclusions>
<exclusion>
<artifactId>commons-logging</artifactId>
<groupId>commons-logging</groupId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
OK,废话不说了,代码开始.首先是结构:
异常处理层
1.异常处理类,写的很简单,有个样子就成哈~,理解原理就可以.实际使用还是别人的框架.
package com.pan.http_rpc.exception;
public class RpcException extends Throwable {
private static final long serialVersionUID = 1L;
// 异常消息
private String msg;
// 异常代码
private String code;
public RpcException() {
msg = getMessage();
code = getCode();
}
public RpcException(String msg) {
}
public RpcException(String msg, String code) {
}
/**
*
* @return 异常消息
*/
public String getMsg() {
return msg;
}
/**
*
* @return 异常代码
*/
public String getCode() {
return code;
}
}
数据序列化层:
1.序列化接口:
package com.pan.http_rpc.serialize;
public interface Formater {
/**
* 将请求封装成String类型
*
* @param clazz
* 请求的接口
* @param method
* 请求的方法
* @param param
* 请求的参数
* @return 格式化之后的请求
*/
String reqFormat(Class<?> clazz, String method, Object param);
/**
* 将响应封装成String类型
*
* @param respone
* 响应的结果
* @return 格式化之后的响应
*/
String repFormat(Object respone);
}
2.解析接口:
package com.pan.http_rpc.serialize;
public interface Parser {
/**
*
* @param reqFormat
* 请求的字符串类型
* @return 解析之后的请求类型
*/
Request reqParser(String reqFormat);
/**
*
* @param repFormat
* 响应的字符串类型
* @return 解析之后的响应类型
*/
<T> T repParser(String repFormat);
}
3.请求对象,用于封装数据
package com.pan.http_rpc.serialize;
import java.io.Serializable;
public class Request implements Serializable {
private static final long serialVersionUID = 1L;
// 请求的接口
private Class<?> clazz;
// 请求的方法
private String method;
// 请求的参数
private Object param;
/**
* 调用本地的方法,并返回结果
*
* @param bean
* 被调用方法的对象
* @return 结果
* @throws Exception
*/
public Object invoke(Object bean) throws Exception {
return clazz.getMethod(method, param.getClass()).invoke(bean, param);
}
public Class<?> getClazz() {
return clazz;
}
public void setClazz(Class<?> clazz) {
this.clazz = clazz;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public Object getParam() {
return param;
}
public void setParam(Object param) {
this.param = param;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
}
4.序列化实现类:使用阿里的fastjson序列化
package com.pan.http_rpc.serialize.json;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.pan.http_rpc.serialize.Formater;
import com.pan.http_rpc.serialize.Request;
public class JsonFormater implements Formater {
// 静态单例获取该对象
public static final JsonFormater formater = new JsonFormater();
// 构造方法私有化
private JsonFormater() {
}
public String reqFormat(Class<?> clazz, String method, Object param) {
Request request = new Request();
request.setClazz(clazz);
request.setMethod(method);
request.setParam(param);
return JSON.toJSONString(request, SerializerFeature.WriteClassName);
}
public String repFormat(Object respone) {
return JSON.toJSONString(respone, SerializerFeature.WriteClassName);
}
}
5.解析实现类:
package com.pan.http_rpc.serialize.json;
import com.alibaba.fastjson.JSON;
import com.pan.http_rpc.serialize.Parser;
import com.pan.http_rpc.serialize.Request;
public class JsonParser implements Parser {
// 静态单例获取该对象
public static final Parser parser = new JsonParser();
public Request reqParser(String reqFormat) {
return JSON.parseObject(reqFormat, Request.class);
}
@SuppressWarnings("unchecked")
public <T> T repParser(String repFormat) {
return (T) JSON.parseObject(repFormat);
}
}
调用层:
1.消费者配置类
package com.pan.http_rpc.invoke;
public class ConsumerConfig {
//生产者提供的url
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
2.生产者配置类
package com.pan.http_rpc.invoke;
public class ProviderConfig {
// 生产者位置/名字 eg:localhost:8580/provider中的provider
private String target;
// 生产者端口
private Integer port;
public ProviderConfig() {
}
public ProviderConfig(String target, Integer port) {
}
public String getTarget() {
return target;
}
public void setTarget(String target) {
this.target = target;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}
3.调用接口
package com.pan.http_rpc.invoke;
import java.io.OutputStream;
import com.pan.http_rpc.exception.RpcException;
public interface Invoker {
/**
* 调用请求
*
* @param request
* 请求报文
* @param consumerConfig
* 消费者配置
* @return
* @throws RpcException
*/
String request(String request, ConsumerConfig consumerConfig) throws RpcException;
/**
* 请求应答
*
* @param response
* 响应报文
* @param outputStream
* 输出流
* @throws RpcException
*/
void response(String response, OutputStream outputStream) throws RpcException;
}
4.调用接口实现类:
package com.pan.http_rpc.invoke;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import com.pan.http_rpc.exception.RpcException;
public class HttpInvoker implements Invoker {
// 单例获取该类
public static final HttpInvoker invoker = new HttpInvoker();
private static final HttpClient httpClient = getHttpClient();
// 构造私有
private HttpInvoker() {
}
public String request(String request, ConsumerConfig consumerConfig) throws RpcException {
// 获取客户端连接
HttpPost post = new HttpPost(consumerConfig.getUrl());
// 设置连接模式为长连接
post.setHeader("Connection", "Keep-Alive");
// 传值
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("data", request));
try {
// 编码
post.setEntity(new UrlEncodedFormEntity(params, "UTF-8"));
// 执行请求
HttpResponse response = httpClient.execute(post);
// 判断请求成功与否
if (response.getStatusLine().getStatusCode() == 200) {
return EntityUtils.toString(response.getEntity(), "UTF-8");
} else {
throw new RpcException("请求异常");
}
} catch (ParseException e) {
e.printStackTrace();
return null;
} catch (IOException e) {
e.printStackTrace();
return null;
} catch (RpcException e) {
System.out.println(e.getMsg());
return null;
}
}
public void response(String response, OutputStream outputStream) throws RpcException {
try {
// 输出响应信息
outputStream.write(response.getBytes("UTF-8"));
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*
* @return httpClient
*/
private static HttpClient getHttpClient() {
PoolingHttpClientConnectionManager pccm = new PoolingHttpClientConnectionManager();
// 连接池最大生成连接数200
pccm.setMaxTotal(200);
// 默认设置route最大连接数为20
pccm.setDefaultMaxPerRoute(20);
// host与port设置
HttpHost localhost = new HttpHost("localhost", 8580);
// 指定专门的route,设置最大连接数为80
pccm.setMaxPerRoute(new HttpRoute(localhost), 80);
// 创建httpClient
return HttpClients.custom().setConnectionManager(pccm).build();
}
}
容器层
1.容器接口
package com.pan.http_rpc.container;
public abstract class Container {
// 容器是否启动
public static volatile boolean isStart = false;
// 启动方法
public abstract void start();
// 容器对象
public static volatile Container container = null;
}
2.容器接口实现类
package com.pan.http_rpc.container;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.AbstractHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import com.pan.http_rpc.invoke.ProviderConfig;
public class HttpContainer extends Container {
//jetty处理
private AbstractHandler httpHandler;
//生产者配置
private ProviderConfig providerConfig;
// 初始化容器,未指定配置是加载默认配置
public HttpContainer(AbstractHandler httpHandler) {
this(httpHandler, new ProviderConfig("/invoke", 8080));
}
// 初始化容器,
public HttpContainer(AbstractHandler httpHandler, ProviderConfig providerConfig) {
this.httpHandler = httpHandler;
this.providerConfig = providerConfig;
Container.container = this;
}
// 启动框架
public void start() {
Server server = new Server();
try {
//使用jetty实例化连接
SelectChannelConnector connector = new SelectChannelConnector();
//设置端口
connector.setPort(providerConfig.getPort());
//设置连接
server.setConnectors(new Connector[] { connector });
//加载处理
server.setHandler(httpHandler);
//启动
server.start();
isStart = true ;
} catch (Throwable e) {
System.out.println(e.getMessage());
}
}
}
3.启动类
package com.pan.http_rpc.container;
import java.util.concurrent.CountDownLatch;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main {
/**
* 启动方法
*/
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath*:spring-*.xml");
context.start();
CountDownLatch countDownLatch = new CountDownLatch(1);
countDownLatch.await();
}
}
代理层(核心代码)
1.生产者代理工厂类
package com.pan.http_rpc.proxy;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.jetty.handler.AbstractHandler;
import com.pan.http_rpc.container.Container;
import com.pan.http_rpc.container.HttpContainer;
import com.pan.http_rpc.invoke.HttpInvoker;
import com.pan.http_rpc.invoke.Invoker;
import com.pan.http_rpc.invoke.ProviderConfig;
import com.pan.http_rpc.serialize.Formater;
import com.pan.http_rpc.serialize.Parser;
import com.pan.http_rpc.serialize.Request;
import com.pan.http_rpc.serialize.json.JsonFormater;
import com.pan.http_rpc.serialize.json.JsonParser;
public class ProviderProxyFactory extends AbstractHandler {
// 服务map
private Map<Class<?>, Object> providers = new ConcurrentHashMap<Class<?>, Object>();
// 服务代理类工厂
private static ProviderProxyFactory factory;
// RPC解析对象
private Parser parser = JsonParser.parser;
// RPC格式化对象
private Formater formater = JsonFormater.formater;
// RPC传输对象
private Invoker invoker = HttpInvoker.invoker;
/**
* 默认模式下初始化服务注册代理工厂
*
* @param providers
* 服务map
*/
public ProviderProxyFactory(Map<Class<?>, Object> providers) {
// 容器判断
if (Container.container == null) {
new HttpContainer(this).start();
}
// 服务注册
for (Map.Entry<Class<?>, Object> entry : providers.entrySet()) {
register(entry.getKey(), entry.getValue());
}
factory = this;
}
/**
* 初始化服务注册代理工厂
*
* @param providers
* 服务map
* @param providerConfig
* 服务配置
*/
public ProviderProxyFactory(Map<Class<?>, Object> providers, ProviderConfig providerConfig) {
// 容器判断
if (Container.container == null) {
new HttpContainer(this, providerConfig).start();
}
// 提取需要注册的map
for (Map.Entry<Class<?>, Object> entry : providers.entrySet()) {
register(entry.getKey(), entry.getValue());
}
factory = this;
}
/**
* 注册
*
* @param clazz
* 生产者接口
* @param object
* 对应的对象
*/
private void register(Class<?> clazz, Object object) {
// 注册到服务列表map中
this.providers.put(clazz, object);
System.out.println("生产者添加成功!");
}
/**
*
* @param clazz
* 接口
* @return 需求的对象
*/
public Object getBeanByClass(Class<?> clazz) {
Object bean = providers.get(clazz);
if (bean != null) {
return bean;
}
return null;
}
/**
* 获取该对象
*
* @return
*/
public static ProviderProxyFactory getInstance() {
return factory;
}
/**
* 获取一个空的服务注册map,用于服务注册
*
* @return 服务列表map
*/
public static Map<Class<?>, Object> getProviderMap() {
return new HashMap<Class<?>, Object>();
}
/**
* 响应请求
*
* @param target
* 路径/位置
* @param request
* 请求
* @param response
* 响应
* @param dispatch
* 调度模式
*/
public void handle(String target, HttpServletRequest request, HttpServletResponse response, int dispatch)
throws IOException, ServletException {
// 取出报文数据
String reqStr = request.getParameter("data");
try {
// 请求解析
Request rpcRequest = parser.reqParser(reqStr);
// 反射请求
Object result = rpcRequest.invoke(ProviderProxyFactory.getInstance().getBeanByClass(rpcRequest.getClazz()));
// 响应请求报文
invoker.response(formater.repFormat(result), response.getOutputStream());
} catch (Throwable e) {
e.printStackTrace();
}
}
}
2.消费者代理工厂类
package com.pan.http_rpc.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.pan.http_rpc.invoke.ConsumerConfig;
import com.pan.http_rpc.invoke.HttpInvoker;
import com.pan.http_rpc.invoke.Invoker;
import com.pan.http_rpc.serialize.Formater;
import com.pan.http_rpc.serialize.Parser;
import com.pan.http_rpc.serialize.json.JsonFormater;
import com.pan.http_rpc.serialize.json.JsonParser;
public class ConsumerProxyFactory implements InvocationHandler {
// 消费者配置
private ConsumerConfig consumerConfig;
// RPC解析
private Parser parser = JsonParser.parser;
// RPC格式化
private Formater formater = JsonFormater.formater;
// RPC调用
private Invoker invoker = HttpInvoker.invoker;
/**
* 添加消费者
*
* @param clazzPath
* 接口路径
* @return
* @throws Exception
* 异常
*/
public Object create(String clazzPath) throws Exception {
Class<?> interfaceClass = Class.forName(clazzPath);
return Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[] { interfaceClass }, this);
}
/**
* @param proxy
* 请求的类
* @param method
* 请求的方法
* @param args
* 请求的参数
* @return 响应解析之后的对象
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 获取调用的接口
Class<?> interfaceClass = proxy.getClass().getInterfaces()[0];
// 格式化请求
String req = formater.reqFormat(interfaceClass, method.getName(), args[0]);
// 调用生产者的方法
String rep = invoker.request(req, consumerConfig);
// 解析报文并返回结果对象
return parser.repParser(rep);
}
public ConsumerConfig getConsumerConfig() {
return consumerConfig;
}
public void setConsumerConfig(ConsumerConfig consumerConfig) {
this.consumerConfig = consumerConfig;
}
public Invoker getInvoker() {
return invoker;
}
public void setInvoker(Invoker invoker) {
this.invoker = invoker;
}
public Formater getFormater() {
return formater;
}
public void setFormater(Formater formater) {
this.formater = formater;
}
public Parser getParser() {
return parser;
}
public void setParser(Parser parser) {
this.parser = parser;
}
}
整个框架流程:
生产者与消费者统一实现同一接口类,所以要么依赖需要调用的项目,实现他的接口.要么,写一个公共接口来一起依赖,然后分调用.
简单的RPC没实际意义,除了对RPC框架有一个大概认知以及阅读阿里的dubbo.