最近看到一個(gè)遠(yuǎn)程調(diào)用的簡(jiǎn)單實(shí)現(xiàn),于是加上自己的理解分享給大家。
遠(yuǎn)程調(diào)用是典型的CS模型,Server端提供服務(wù),客戶端調(diào)用得到結(jié)果
先看服務(wù)端提供服務(wù)的方法
1 /**
2 * 提供服務(wù)
3 *
4 * @param service 服務(wù)實(shí)現(xiàn)
5 * @param port 端口(可以雙發(fā)約定)
6 * @throws Exception
7 */
8 public static void provide(final Object service, final int port) throws Exception {
9 //參數(shù)檢查
10 if (service == null) {
11 throw new IllegalArgumentException("The service can't be null!");
12 }
13 if (port > 65535) {
14 throw new IllegalArgumentException("The host can't greater than 65535!");
15 }
16 //開(kāi)啟一個(gè)ServerSocket接收請(qǐng)求
17 ServerSocket serverSocket = new ServerSocket(port);
18 //死循環(huán)等待請(qǐng)求
19 while (true) {
20 //接受到請(qǐng)求,獲取socket
21 final Socket socket = serverSocket.accept();
22 try {
23 //開(kāi)啟一個(gè)線程處理
24 new Thread(new Runnable() {
25 @Override
26 public void run() {
27 try {
28 try {
29 //重socket中獲取輸入流
30 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
31 try {
32 //獲取方法名
33 String methodName = ois.readUTF();
34 //獲取方法參數(shù)數(shù)組
35 Class[] methodParameterTypes = (Class[]) ois.readObject();
36 //獲取參數(shù)值數(shù)組
37 Object[] arguments = (Object[]) ois.readObject();
38 //根據(jù)方法名和方法參數(shù)獲取方法(根據(jù)方法名和方法參數(shù)可以唯一定位到一個(gè)方法)
39 Method method = service.getClass().getMethod(methodName, methodParameterTypes);
40 if (method == null) {
41 throw new NoSuchMethodException();
42 }
43 //執(zhí)行方法
44 Object result = method.invoke(service, arguments);
45 System.out.println("Method:" + methodName + ";Arguments:" + arguments + " invoke!");
46 //獲取socket輸出流
47 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
48 try {
49 //輸出結(jié)果
50 oos.writeObject(result);
51 } finally {
52 oos.close();
53 }
54 } finally {
55 ois.close();
56 }
57 } finally {
58 socket.close();
59 }
60 } catch (Exception e) {
61 //記個(gè)日志啥的
62 e.printStackTrace();
63 }
64 }
65 }).start();
66 } catch (Exception e) {
67 //記個(gè)日志啥的
68 e.printStackTrace();
69 }
70 }
71 }
然后是消費(fèi)的方法
**
* 消費(fèi)服務(wù)
*
* @param clazz 接口類
* @param host 發(fā)布服務(wù)機(jī)器的host
* @param port 發(fā)布服務(wù)機(jī)器的port
* @return
*/
public static Object consume(final Class clazz, final String host, final int port) {
//參數(shù)檢查
if (clazz == null) {
throw new IllegalArgumentException("The clazz can't be null!");
}
if (host == null || host.isEmpty()) {
throw new IllegalArgumentException("The host can't be null or empty!");
}
if (port > 65535) {
throw new IllegalArgumentException("The host can't greater than 65535!");
}
//生成代理,每次調(diào)用方法其實(shí)是調(diào)用遠(yuǎn)程的服務(wù)
Object proxy = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
//建立socket鏈接
Socket socket = new Socket(host, port);
try {
//獲取輸出流
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
try {
String methodName = method.getName();
Class[] methodParameterTypes = method.getParameterTypes();
//輸出要調(diào)用的方法名
oos.writeUTF(methodName);
//輸出要調(diào)用的方法參數(shù)列表
oos.writeObject(methodParameterTypes);
//輸出要調(diào)用的方法參數(shù)
oos.writeObject(arguments);
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
try {
//獲取結(jié)果
Object result = ois.readObject();
//可能返回的對(duì)象是異常
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
ois.close();
}
} finally {
oos.close();
}
} finally {
socket.close();
}
}
});
return proxy;
}
一般端口可以雙方約定,而host可以采用configServer的方法解決,也就是開(kāi)啟一個(gè)服務(wù),當(dāng)啟動(dòng)一個(gè)服務(wù)的時(shí)候到configServer注冊(cè)一下(服務(wù)名+host),如果是多臺(tái)服務(wù)器提供服務(wù),host就是一個(gè)list,調(diào)用方發(fā)起調(diào)用的時(shí)候首先到configServer根據(jù)服務(wù)名獲取host列表,然后選一個(gè)host發(fā)起調(diào)用!configServer的優(yōu)點(diǎn)是可以做到很多控制,比如流量控制,權(quán)重控制,調(diào)用host列表維護(hù)(死掉就剔除,重試機(jī)制)等等,這樣調(diào)用方不用關(guān)心我調(diào)用的是哪臺(tái)機(jī)器,只用關(guān)心我調(diào)用哪個(gè)方法。但也有壞處,一旦configServer掛掉了.......(其實(shí)也可以通過(guò)MS或調(diào)用方本地緩存調(diào)用列表解決)。
一般由調(diào)用方提供一個(gè)接口包(算是一個(gè)雙方的約定),接口類中定義了提供發(fā)提供的方法
如我們發(fā)布一個(gè)簡(jiǎn)單的服務(wù)
1 public interface Girl {
2 //提供服務(wù)
3 String server(String name);
4 }
實(shí)現(xiàn)
1 public class GirlImpl implements Girl{
2 @Override
3 public String server(String name) {
4 return name+"亞美爹";
5 }
6 }
發(fā)布服務(wù)
這里你可以寫在一個(gè)main方法中,也可以配置一個(gè)Spring的bean,并配置init方法,然后在init方法中開(kāi)啟
1 Girl beautifulGirl=new GirlImpl();
2 try {
//在本機(jī)的1111端口上開(kāi)啟Girl的服務(wù)
3 Utils.provide(beautifulGirl, 1111);
4 } catch (Exception e) {
5 e.printStackTrace();
6 }
消費(fèi)
1 try {
2 //從此你就獲得了一個(gè)漂亮妹子,她可以給你提供各種服務(wù)
3 Girl beautifulGirl= (Girl)Utils.consume(Girl.
class, "127.0.0.1", 3333);
4 //你可以來(lái)一個(gè)循環(huán),或者來(lái)一個(gè)死循環(huán),一直
哈哈
5 beautifulGirl.server("yourName");
6 }
catch (Exception e) {
7 e.printStackTrace();
8 }
總結(jié):
其實(shí)遠(yuǎn)程調(diào)用也就是獲取服務(wù)的一個(gè)代理,每當(dāng)你調(diào)用服務(wù)的方法事,他都會(huì)想服務(wù)方傳去方法,方法參數(shù)列表,參數(shù),前兩個(gè)用于唯一確定一個(gè)方法,后一個(gè)用于方法調(diào)用。
這里實(shí)現(xiàn)的很簡(jiǎn)答,當(dāng)然還有很復(fù)雜的,比如Spring的實(shí)現(xiàn),淘寶的HSF等等
以上為個(gè)人理解,如果有錯(cuò)的地方,歡迎指正。