面向服務的體系架構(SOA)
1、基於TCP協議的RPC
1.1、RPC名詞解釋:
RPC(Remote Process Call):遠端過程呼叫。它應用廣泛,實現方式也很多,擁有RMI(remote method invocation)、WebService等諸多成熟的方案,在業界得到廣泛的應用。
RPC的實現包括客戶端和服務端,即服務的呼叫方和服務的服務方。服務呼叫方傳送RPC請求到服務提供方,服務提供方根據呼叫方提供的引數執行請求方法,將執行結果返回給呼叫方,一次RPC呼叫完成。
1.2、物件序列化:
無論是何種型別的資料,最終都需要轉換成二進位制流在網路上傳輸。在面向物件的程式設計中,如何將一個定義好的物件傳輸到遠端呢?資料的傳送方需要將物件轉換為二進位制流,才能在網路上進行傳輸,而資料的接收方則需要把二進位制流轉換為物件。
- 將物件轉換為二進位制流的過程稱為物件的序列化。
- 將二進位制流恢復為物件的過程稱為物件的反序列化。
物件的序列化與反序列化有多種成熟的解決方案,較為常用的有Google的ProtocalBuffers、Java本身內建的序列化方式、Hessian以及JSON和XML等。
這裡重點介紹Java內建的序列化方式和基於Java的Hessian序列化方式:
1、Java內建的序列化方式所實現物件序列化和反序列化的關鍵程式碼:
//定義一個位元組陣列輸出流 ByteArrayOutputStream os = new ByteArrayOutputStream(); //物件輸出流 ObjectOutputStream out = new ObectOutputStream(os); //將物件寫入到位元組陣列輸出,進行序列化 out.writeObject(zhangsan); byte[] zhangsanByte = os.toByteArray(); //位元組陣列輸入流 ByteArrayInputStream is = new ByteArrayInputStream(zhangsanByte); //進行反序列化,從流中讀取物件 ObjectInputStream in = new ObjectInputStream(is); Person person = (Person) in.readObject();
通過java.io包下的ObjectOutputStream的writeObject方法,將Person類的例項zhangsan序列化為位元組陣列,然後再通過ObjectInputStream的readObject方法將位元組陣列反序列化為person物件。
2、使用Hessian進行序列化,需要引入其提供的三方包Hessian-4.0.7.jar,針對基於java的Hessian的序列化和反序列化的實現:
ByteArrayOutputStream os = new ByteArrayOutputStream(); //Hessian的序列化輸出 HessianOutput ho = new HessianOutput(os); ho.writeObject(zhangsan); byte[] zhangsanByte = os.toByteArray(); ByteArrayInputStream is = new ByteArrayInputStream(zhangsanByte); //Hessian的反序列化讀取物件 HessianInput hi = new HessianInput(is); Person person = (Person) hi.readObject();
1.3、基於TCP協議實現RPC:
基於Java的SocketAPI,我們能夠實現一個簡單的RPC呼叫,這個例子中包含了服務的介面及介面的遠端實現、服務的消費者與遠端的提供方。如下圖所示:
public interface SayHelloService() {
public String sayHello(String helloArg);
}
服務的實現:
public class SayHelloServiceImpl implements SayHelloService {
@Override
pulbic String sayHello(String helloArg) {
if(helloArg.equals("hello")) {
return "hello";
}else{
return "bye bye";
}
}
}
服務消費者Consumer類的部分關鍵程式碼:
//介面名稱
String interfacename = SayHelloService.class.getName();
//需要遠端執行的方法
Method method = SayHelloService.class.getMethod("sayHello",java.lang.String.class);
//需要傳遞到遠端的引數
Object[] arguments = = {"hello"};
Socket socket = new Socket("127.0.0.1","1234");
//將方法名稱和引數傳遞到遠端
ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
os.writeUTF(interfacename); //介面名稱
os.writeUTF(method.getName()); //方法名稱
os.writeObject(method.getParameterTypes());
os.writeObject(arguments);
//從遠端讀取方法執行結果
ObjectInputStream is = new ObjectInputStream(socket.getInputStream);
Object result = is.readObject();
服務提供者Provider類的部分關鍵程式碼:
ServerSocket server = new ServerSocket(1234);
while(true) {
Socket socket = server.accept();
//讀取服務資訊
ObjectInputStream is = new ObjectInputStream(socket.getInputStream());
String interfacename = is.readUTF(); //介面名稱
String methodName = is.readUTF(); //方法名稱
Class<?>[] parameterTypes = (Class<?>[]) is.readObject(); //引數型別
Object[] arguments = (Object[]) is.readObject(); //引數物件
//執行呼叫
Class interfacenameclass = Class.forName(interfacename); //得到介面的class
Object service = services.get(interfacename); //取得服務實現的物件
Method method = interfacenameclass.getMethod(methodName,parameterTypes); //獲得要呼叫 的方法
Object result = method.invoke(service,arguments);
ObjectOutputStream os = new ObjectOutputStream(socket.getOutputStream());
os.writeObject(result);
}
服務提供端事先將服務例項化好後放在services這個Map中,通過一個while迴圈,不斷的接收新到來的請求,得到所需要的引數,包括介面名稱、方法名稱、引數型別和引數,通過java的反射取得介面中需要呼叫的方法,執行後將結果返回給服務的消費者。
2、基於HTTP協議的RPC
2.1、HTTP協議棧:
2.2、HTTP請求與相應:
2.3、通過HttpClient傳送HTTP請求:
2.4、使用HTTP協議的優勢:
2.5、JSON和XML:
2.6、RESTful和RPC:
2.7、基於HTTP協議的RPC的實現: