提問:什麼是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 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 serviceList=null;
public ServerThread(Socket client,List 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 staticT getService(Classclazz,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);
}鄭州人流醫院那家好:http://www.zztj120.com/
}
運行consumer中的主函數:
上述可以看出是consumer遠程調用了provider中的sellBatterCake()方法!!!