天才风云出我辈,一见00岁月枯!
学习之前我对Rpc调用链路的理解
用户A 去调用 用户B的一个服务
对应下面的例子:
首先B先定义接口,以及实现类
B目录结构:
B的接口
所属Module: rpc-api
public interface IHelloService {
String sayHello(String value);
}
B的实现类
所属Module: rpc-provider
public class HelloServiceImpl implements IHelloService{
@Override
public String sayHello(String value) {
System.out.println("我这里是服务提供者,我接收到了请求");
return "小白: " + value;
}
}
之后将服务提供者B中的rpc-api打包就可以了,将接口暴露出去
A的实现
String s = helloService.sayHello("啦啦啦,德玛西亚");// 类似于调用本地方法
下面我们来仔细了解下,具体的调用细节
在此之前,我们先提出几个问题
- 1.服务提供者是怎么样把服务暴露出来,供其它用户调用的
- 2.服务调用者是怎么样寻找到服务提供者的
我的理解
- 1.所谓的服务暴露简单点来说就是让其他人来调用"我的服务",换句话说让别人找到我就可以了
- 2.作为调用者我只需要找到服务提供者就可以了
- 3.当然了,两者之间需要进行数据的交互,比如说参数的传递
那么下面我们就围绕着下面几点来进行论证和讨论
服务提供者对外提供服务
服务对外提供,我们的思路是: 通过Serversocket暴露一个端口,让其他服务来访问我们,代码如下:
-- 对外提供Rpc服务
public class RpcProxyServer {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
public void publisher(Object service, int port) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while (true) {
final Socket socket = serverSocket.accept();
executorService.submit(new ProcessHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
-- 服务处理
public class ProcessHandler implements Runnable {
Socket socket;
Object service;
public ProcessHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
inputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest request = (RpcRequest) inputStream.readObject();
Object result = invoke(request);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private Object invoke(RpcRequest request) {
try {
Class clazz = Class.forName(request.getClassName());
Method method = clazz.getMethod(request.getMethodName(), request.getTypes());
Object[] parameters = request.getParameters();
Object result = method.invoke(service, parameters);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
-- 启动服务
public class App
{
public static void main( String[] args ) {
IHelloService service = new HelloServiceImpl();
RpcProxyServer proxyServer = new RpcProxyServer();
proxyServer.publisher(service,8888);
System.out.println( "Hello World!" );
}
}
我们来简述下上面服务暴露的过程:
简单版本: ServerSocket服务端暴露服务,Server访问服务,没了
复杂版本:
1. 声明我们要对外暴露的服务: IHelloService service = new HelloServiceImpl();
2. 创建一个服务发布对象: RpcProxyServer proxyServer = new RpcProxyServer();
3. 将服务发布出去: proxyServer.publisher(service,8888);
4.
下面我们针对: proxyServer.publisher(service,8888);来仔细说说
public class RpcProxyServer {
private final ExecutorService executorService = Executors.newFixedThreadPool(10);
public void publisher(Object service, int port) {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while (true) {
final Socket socket = serverSocket.accept();
executorService.submit(new ProcessHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
仔细详解:
serverSocket = new ServerSocket(port);指出对外暴露的端口
final Socket socket = serverSocket.accept(); 接收外部的请求,accept()为一个阻塞方法,这里为了接受多个请求,因此用了
线程池
ok,到了这一步,要明白一点,我们的服务一定对外提供完毕,当执行下面的时候,证明服务提供者已经接收到了外部的请求
executorService.submit(new ProcessHandler(socket,service));
下面我们来看看,这个ProcessHandler(socket,service)这个里面的实现
下面我们来分析下这一段代码
public class ProcessHandler implements Runnable {
Socket socket;
Object service;
public ProcessHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
inputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest request = (RpcRequest) inputStream.readObject();
Object result = invoke(request);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
private Object invoke(RpcRequest request) {
try {
Class clazz = Class.forName(request.getClassName());
Method method = clazz.getMethod(request.getMethodName(), request.getTypes());
Object[] parameters = request.getParameters();
Object result = method.invoke(service, parameters);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
}
首先它实现了run()方法
先看这一块代码:
inputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest request = (RpcRequest) inputStream.readObject();
Object result = invoke(request);
这一段代码是: 读取请求过来的数据流
再看invoke()方法
private Object invoke(RpcRequest request) {
try {
Class clazz = Class.forName(request.getClassName());
Method method = clazz.getMethod(request.getMethodName(), request.getTypes());
Object[] parameters = request.getParameters();
Object result = method.invoke(service, parameters);
return result;
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
em... 要是看不懂,我也没办法,这个就是反射调用,根据方法,参数等信息进行反射调用
调用之后返回结果
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(result);
outputStream.flush();
将数据返回给调用者
Ps: 我觉得我已经触摸到了某种感觉,比如说服务发现,服务注册,心跳监听等。。嘤嘤嘤
下面我们再说下服务调用者吧
重点看下面三行代码:
RpcProxyClient client = new RpcProxyClient();
IHelloService helloService = client.clientProxy(IHelloService.class, "localhost", 8888);
String s = helloService.sayHello("啦啦啦,德玛西亚");// 类似于调用本地方法
我们来分析下这三行代码:
1. RpcProxyClient client = new RpcProxyClient(); 创建一个远程访问对象,点进去看看
public class RpcProxyClient {
public <T> T clientProxy(final Class<T> interfaceCls, final String host,int port) {
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(),
new Class<?>[]{interfaceCls},
new RemoteInvocationHandler(host,port));
}
}
ok,注意一下传递的参数
a.IHelloService.class 这是一个class对象
b.一个host: localhost
c.一个端口号port: 8888
可以推断下,localhost + port是用来定位目标主机的,这个IHelloService我们可以用来定位我们要访问的
对象
IHelloService helloService = client.clientProxy(IHelloService.class, "localhost", 8888);
这个方法返回的是一个代理对象,没了
看最后一行代码
String s = helloService.sayHello("啦啦啦,德玛西亚");// 类似于调用本地方法
当我们调用这个方法的时候,他会调用RemoteInvocationHandler.invoke(Object proxy,
Method method, Object[] args)方法,别问我为什么?问就是男人的第七感(以后可以说说)
invoke()方法里面有个: rpcNetTransport.send(request)
这个里面就是调用的具体细节了
public Object send(RpcRequest request) {
Socket socket = null;
ObjectOutputStream outputStream = null;
ObjectInputStream inputStream = null;
try {
socket = new Socket(host, port);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(request);
outputStream.flush();
inputStream = new ObjectInputStream(socket.getInputStream());
Object o = inputStream.readObject();
return o;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
跟我们猜想的一样。。没啥说的,看不懂的可以留言,有什么问题的话,大家一起讨论,下期再见!
。。反正我是会了