RPC简述
RPC简述
提问:什么是RPC?
PRC是一种远程过程调用框架,相比远程调用,我们可以先理解本地调用,本地调用就是方法A和方法B都写在同一个工程中,然后A可以调用B。但是如果A方法和B方法是属于不同工程中,则需要进行远程调用。
误解:如A工程中需要调用B工程中的方法,一般是将B工程打一个包,然后A中会引入这个包,然后就可以调用到B中的方法了,这种两个工程间没有网络通信则只能称为远程方法的引用,只有两个工程存在网络通信,A调用B的方法并且是B处理后返回给A,才能称为远程过程调用!!
RPC远程调用原理
一、RPC是怎么做到远程调用的,其原理是什么?
追根究底,R客户端与服务端建立TCP链接,相比于HTTP通信协议少去应用层的许多东西。数据的传输就是通过这个TCP的链接。
Client:服务消费方
Server:服务提供方
(1).Client以调用本地服务方式调用远程API
(2).Client Stub负责接收参数,方法等,将其封装(编码)成能够进行网络传输的消息体
(3).Client Stub负责网络寻址,将消息体发送对应的服务端
(4).Server Stub负责接收消息,并解码成服务端能够识别的信息,调用对应的服务端方法
(5).Server本地服务将调用结果发送给Server Stub
(6).Server Stub将返回结果包装成消息体返回给Client Stub
(7).Client Stub接收消息并进行解码
(8).Client获取到最终调用结果
二、实例运行
举例:实现两个本地工程之间的远程调用,如下是RpcProvider工程的代码
public interface BatterCakeService {
public String sellBatterCake(String name);
}
public class BatterCakeServiceImpl implements BatterCakeService{
public String sellBatterCake(String name) {
return name+"味道很赞";
}
}
public class RpcProvider {
//存储注册的服务列表
private static List<Object> serviceList;
/**
* 发布rpc服务
* @param
* @param port
* @throws Exception
*/
public static void export(int port,Object... services) throws Exception {
serviceList= Arrays.asList(services);
//创建本地服务,端口20006
ServerSocket server = new ServerSocket(port);
Socket client = null;
while (true) {
//阻塞等待输入,一直监听,如果有服务请求,则返回,否则一直等待
client = server.accept();
//每一个请求,启动一个线程处理
new Thread(new ServerThread(client,serviceList)).start();
}
}
}
public class ServerThread implements Runnable {
private Socket client=null;
private List<Object> serviceList=null;
public ServerThread(Socket client,List<Object> service)
{
this.client=client;
this.serviceList=service;
}
@Override
public void run() {
ObjectInputStream input=null;
ObjectOutputStream output=null;
try {
//创建请求服务的输入输出流
input=new ObjectInputStream(client.getInputStream());
output=new ObjectOutputStream(client.getOutputStream());
// 读取客户端要访问那个service
Class serviceClass = (Class)input.readObject();
// 找到该服务类
Object obj = findService(serviceClass);
if(obj==null)
{
output.writeObject(serviceClass.getName() + "服务未发现");
}else {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Method method = obj.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(obj, arguments);
output.writeObject(result);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
client.close();
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private Object findService(Class serviceClass) {
// TODO Auto-generated method stub
for (Object obj : serviceList) {
boolean isFather = serviceClass.isAssignableFrom(obj.getClass());
if (isFather) {
return obj;
}
}
return null;
}
}
主函数:
public class RpcBootStrap {
public static void main(String[] args) throws Exception {
BatterCakeService batterCakeService =new BatterCakeServiceImpl();
//发布卖煎饼的服务,注册在20006端口
RpcProvider.export(20006,batterCakeService);
}
}
运行主函数,保持服务提供方的监听
服务消费方consumer代码:consumer中会调用provider中的sellBatterCake(String name)方法,可以从如下看出,consumer中并没有sellBatterCake(String name)方法的实现
public interface BatterCakeService {
public String sellBatterCake(String name);
}
public class ProxyHandler implements InvocationHandler {
private String ip;
private int port;
public ProxyHandler(String ip, int port) {
// TODO Auto-generated constructor stub
this.ip = ip;
this.port = port;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket(this.ip, this.port);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
output.writeObject(proxy.getClass().getInterfaces()[0]);
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(args);
output.flush();
Object result = input.readObject();
if(result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
socket.shutdownOutput();
}
}
}
public class RpcConsumer {
public static <T> T getService(Class<T> clazz,String ip,int port) {
ProxyHandler proxyHandler =new ProxyHandler(ip,port);
return (T) Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[] {clazz}, proxyHandler);
}
}
public class RpcTest {
public static void main(String[] args) {
BatterCakeService batterCakeService=RpcConsumer.getService(BatterCakeService.class, "127.0.0.1", 20006);
String result=batterCakeService.sellBatterCake("双蛋");
System.out.println(result);
}
}
运行consumer中的主函数:
上述可以看出是consumer远程调用了provider中的sellBatterCake()方法!!!