1. 程式人生 > >面向服務的體系架構(SOA)

面向服務的體系架構(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的實現: