前言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序列化的方式進行序列化。
公司裏還有事,並且我不知道這些是否是各位朋友想看到的內容,忙完今天我會繼續進行補充。 哪裏有講的不對的但願你們來矯正。