简单概念
RPC全程是(Remote Procedure Call)远程过程调用,使得分布式应用更加容易构建。一个应用调用一个远程功能时,像是在本地调用一样。因此RPC需要提供一中透明调度机制,让本地应用不必显示的区分本地调用和远程调用。
开源RPC框架
- Dubbo 阿里巴巴于2011年开源,仅支持Java
- Motan 微博内部使用,仅支持java
- springCloud,spring全家桶里面的组建,提供了一个丰富的spring生态
- gRPC Google 2015年开源,支持跨语言,多种语言
- Thrift Facebook开源,贡献给了 Apache基金,支持多种语言
特点
- RPC框架一般使用长连接,不必每次通信都3次握手,减少开销
- RPC一般都有注册中心、监控管理。服务提供者发布、下线接口、动态扩展,调用方无感知的
代码
主要包含四部分
- 把服务调用者称为Consumer
- 把提供服务方称为Provider
- 把服务注册中心称为RPC
- 把透明接口称为Provider-Consumer
透明接口
这里消费者要调用的方法是HelloService
public interface HelloService {
public String sayHello(String name) ;
}
Consumer
public class Consumer {
public static void main(String[] args) {
Invocation invocation = new Invocation(HelloService.class.getName(),"sayHello",
new Class[]{String.class}, new Object[]{"程小青"});
HttpClient httpClient = new HttpClient();
String result = httpClient.send("localhost",8080, invocation);
System.out.println(result);
}
}
Provider
Provider启动,向注册中心注册服务,然后启动后等待消费者的连接
public class Provider {
public static void main(String[] args) {
// 向注册中心注册服务
LocalRegister.regist(HelloService.class.getName(), HelloServiceImpl.class);
// Netty, Tomcat
HttpServer httpServer =new HttpServer();
httpServer.start("localhost", 8080);
}
}
Provider所提供的服务,要实现透明接口
public class HelloServiceImpl implements HelloService{
@Override
public String sayHello(String name) {
return "hello: " + name;
}
}
本地注册中心
- Invocation规定了请求的格式
- DispatcherServlet负责将收到的请求转交给相应的Handler进行处理
- LocalRegister 是服务注册中心
Invocation
请求的格式,在调用远程服务时,我们要指定所调用的服务的接口名、方法名、方法的参数类型列表(便于同名方法的重载)、方法的参数列表。
// 因为是网络请求的参数,所以要支持序列化,这里用的是JDK的序列化,也要实现其他的比如用JSON的序列化方式等
public class Invocation implements Serializable {
//调用的接口名
private String interfaceName;
//调用的方法名
private String methodName;
//调用的参数类型,判断同名方法的重载
private Class[] parameterTypes;
//调用的参数值
private Object[] parameters;
public Invocation(String interfaceName, String methodName, Class[] parameterTypes, Object[] parameters) {
this.interfaceName = interfaceName;
this.methodName = methodName;
this.parameterTypes = parameterTypes;
this.parameters = parameters;
}
... 省略Get Set方法
DispatcherServlet
相当于一个请求中转站,会根据受到的请求,分发给相应的处理器Handler进行处理。
/**
* DispatcherServlet相当于一个中转站
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 拿到请求之后,交给相应的处理器去处理,(在一些框架里,handler通常是过滤器和责任链的模式)
new HttpServerHandler().handler(req, resp);
}
}
HttpServerHandler
处理请求的组件。
/**
* 专门处理请求的
*/
public class HttpServerHandler {
public void handler(HttpServletRequest req, HttpServletResponse resp){
// 处理请求 --->调用哪个接口的哪个方法,传的什么参数(接口、方法、方法参数,可以把这三者抽象成一个对象)
//首先要判断序列化的方式然后进行反序列化,这里直接使用了JDK的反序列化
try {
Invocation invocation = (Invocation) new ObjectInputStream(req.getInputStream()).readObject();
//获取到接口名字
String interfaceName = invocation.getInterfaceName();
//从注册中心找到对应的接口实现类
Class classImpl = LocalRegister.get(interfaceName);
// 根据实现类、方法名字、参数类型找到对应的方法
Method method = classImpl.getMethod(invocation.getMethodName(), invocation.getParameterTypes());
//执行方法
Object result = method.invoke(classImpl.newInstance(), invocation.getParameters());
// 把结果写入Response中
IOUtils.write((String)result,resp.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
HttpServer,HttpClient
这两个组件分别是为Provider和Consumer发起网络请求准备的。
LocalRegister
本地服务注册
/**
* 本地注册,当然还可以扩展为注册中心注册
*/
public class LocalRegister {
// 接口名字对应的类
private static Map<String, Class> map = new HashMap<>();
/**
* 注册接口,接口名字对应哪个实现类
* @param interfaceName
* @param implClass
*/
public static void regist(String interfaceName, Class implClass) {
map.put(interfaceName, implClass);
}
/**
* 根据接口名字返回接口
* @param interfaceName
* @return
*/
public static Class get(String interfaceName){
return map.get(interfaceName);
}
}
详细的RPC介绍可参考:zhuanlan.zhihu.com/p/374901408 本Demo地址:github.com/zljya/JAVAR…