RPC實現原理(HSF、dubbo) 從頭開始(一)

前言java

 

闊別了好久博客園,雖然看了之前寫的不少東西感受好幼稚,可是仍是以爲應該把一些本身以爲有用的東西和你們分享。廢話很少說,如今開始進入正題。web

以前的六年工做經驗,呆過了一些大公司,每一個在大公司呆過的人應該知道,在一個大型應用中不斷的增長業務和功能,還有基於性能的考慮,使得不少基礎服務必須進行模塊化,從而讓各子系統方便使用而不是每一個系統從新再實現一套,也可使可能成爲瓶頸的基礎功能能夠單獨進行擴展,好比(以電商系統舉例)用戶信息管理、交易管理中心、商品管理中心等等。  在rpc發展最初,服務進行模塊塊之後,各個子系統、模塊實現的技術五花八門,如:hessian、WebService、Socket、http等進行互相調用,各個子系統之間的交互方式和方法不統一,使得各系統間很難很好的整合。而且這些方式還涉及超時、加密解密、參數的傳遞等各類問題。  在這種狀況下,hsf、dubbo這種高性能rpc中間件出現了。  如今我就已最簡單的方式從頭開始講起其中的原理。編程

我將分爲一個系列爲你們進行解剖服務器

1、RPC實現原理(HSF、dubbo) 從頭開始(一)網絡

2、RPC實現原理(HSF、dubbo)發佈一個服務與訂閱一個服務(三)框架

3、RPC實現原理(HSF、dubbo)zookeeper進行集羣配置管理(二)socket

4、RPC實現原理(HSF、dubbo)netty替換java socket(四)tcp

5、待補充模塊化

NO.1  TCP傳輸協議性能

爲何選擇TCP做爲傳輸協議?HTTP在TCP的上一層,位於應用層,TCP位於網絡層,以越往底層越快的原理,我就不過多解釋爲何選擇tcp做爲傳輸協議了。 那麼在項目中咱們怎麼使用tcp進行調用呢?直接上個例子代碼:

socket服務端:

 

import java.net.*;
import java.io.*;

/**
 * socket編程之:簡單socket server
 * 
 * @author chengwei.lcw 2016-11-27
 */
public class SocketServer {
    private ServerSocket serverSocket;
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;

    public SocketServer() {
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                // 此處會阻塞,後面會講到nio的做用 
                socket = serverSocket.accept();
                in = new BufferedReader(new InputStreamReader(
                        socket.getInputStream()));
                out = new PrintWriter(socket.getOutputStream(), true);
                String line = in.readLine();
                // 打印出來看看結果
                System.out.println("line:" + line);
                
                // 返回給client端,通知我已收到數據
                out.println("you input is :" + line);
                out.close();
                in.close();
                socket.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new SocketServer();
    }
}

 

scoket客戶端:

 

import java.io.*;
import java.net.*;

/**
 * socket編程之:簡單socket client
 * 
 * @author chengwei.lcw 2016-11-27
 */
public class SocketClient {
    private Socket socket;
    private BufferedReader in;
    private PrintWriter out;

    public SocketClient() {
        try {
            socket = new Socket("127.0.0.1", 9999);
            in = new BufferedReader(new InputStreamReader(
                    socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
            // 向服務端寫數據
            BufferedReader line = new BufferedReader(new InputStreamReader(
                    System.in));

            out.println(line.readLine());
            line.close();
            // 打印出來服務端發回來的回執
            System.out.println(in.readLine());
            
            in.close();
            out.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new SocketClient();
    }
}

先啓動server,再啓動client,輸入參數,回車,二者第一次會話完成。

小結總結:

目前例子中咱們使用了標準io socket,這裏的不少時候會阻塞,如accept()、read()時都會阻塞。測試的時候可讓客戶端睡眠幾秒,在這期間啓動第二個客戶端,這個時候第一個客戶端未完成前,第二個客戶端是被阻塞在accept()中的。  這種狀況能夠給每一個客戶端都單獨分配一個線程,可是這樣建立過多的線程,可能會嚴重影響服務器的性能。 第二種解決方案就是使用NIO 非阻塞的通訊方式,jdk1.4以後已經引入了這個功能,這樣可使得服務器只要啓動一個線程就能處理全部的客戶端socket請求。netty就是基於NIO的高性能框架,相比jdk nio作了不少改進,修復了一些缺陷。  (這裏不對netty與jdk nio作過多贅述,這不在咱們討論原理細節裏,若是你們對這方面有興趣,我會單獨寫篇隨筆進行深度講解)

 

NO.2 序列化方式

在真正的項目中,不少時候咱們傳的都是本身定義的類。在遠程通信中,類的傳輸咱們須要對類進行序列化和反序列化。序列化的方式有多種,如二進制、xml、soap。咱們就以用的最多的二進制進行舉例:

socket服務端:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * socket編程之:傳輸對象server
 * 
 * @author chengwei.lcw 2016-11-27
 */
public class SocketObjectSever {

    private ServerSocket serverSocket;
    private ObjectInputStream in;
    private ObjectOutputStream out;

    public SocketObjectSever() {
        try {
            serverSocket = new ServerSocket(9999);

            while (true) {
                // 此處會阻塞,後面會講到nio的做用
                Socket socket = serverSocket.accept();

                in = new ObjectInputStream(socket.getInputStream());
                out = new ObjectOutputStream(socket.getOutputStream());

                // 接收server端傳來的數據,並轉爲Student
                Student student = (Student) in.readObject();
                // 重寫了toString()方法,打印出來看看
                System.out.println("Server: " + student);

                // 返回給client端,通知我已收到數據
                out.writeObject("yes client, I receive");
                out.flush();

            }

        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new SocketObjectSever();
    }

}

 

socket客戶端:

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

 
/**
 * socket編程之:傳輸對象client
 * 
 * @author chengwei.lcw 2016-11-27
 */
public class SocketObjectClient {
    private Socket socket;
    private ObjectInputStream in;
    private ObjectOutputStream out;
     
    public SocketObjectClient() {
        try {
            socket = new Socket("127.0.0.1",9999);
            out = new ObjectOutputStream(socket.getOutputStream());
            in = new ObjectInputStream(socket.getInputStream());
             
            /*
             * 建一個student對象,用於傳輸
             */
            Student s = new Student("chengwei.lcw", 28);
            
            // 把對象寫到管道中,client端進行接收
            out.writeObject(s);
            out.flush();
             
            String receive = (String) in.readObject();
            System.out.println("Client Receive :"+receive);
             
            in.close();
            out.close();
            socket.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
     
    public static void main(String[] args) {
        new SocketObjectClient();
    }
 
}

 

另外定義一個要傳輸的類:

import java.io.Serializable;

/**
 * socket編程之:要進行傳輸的類,須要繼承Serializable接口
 * 
 * @author chengwei.lcw 2016-11-27
 * 
 */
public class Student implements Serializable {
    
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String toString() {
        return "name=" + this.name + ", age=" + this.age; 
    }
}

依然先啓動server,再啓動client,server端控制檯輸出:

Server: name=chengwei.lcw, age=28

這樣爲止,咱們的socket能夠傳輸對象了。

這裏咱們使用的序列化方式爲java直接進行序列化,而hessian序列化比Java序列化高效不少,生成的字節流也要短不少,由於hessian在序列化時會把字節流進行壓縮。在後面的升級版中我會使用hessian序列化的方式進行序列化。

 

公司裏還有事,並且我不知道這些是否是各位朋友想看到的內容,忙完今天我會繼續進行補充。 哪裏有講的不對的但願你們來矯正。

相關文章
相關標籤/搜索