thrift 入門(mén)(1/2)

一渺杉、thrift 定義

“什么是 thrift” 這個(gè)問(wèn)題蛇数,我曾問(wèn)過(guò)別人,也有人拿來(lái)問(wèn)過(guò)我是越。
無(wú)論是官網(wǎng)耳舅、百度、谷歌還是博客倚评,得到的答案都差不多浦徊,挨個(gè)補(bǔ)齊定語(yǔ)無(wú)非就是:

  • thrift 是一個(gè)服務(wù)框架。
  • thrift 是一個(gè) rpc 服務(wù)框架天梧。
  • thrift 是一個(gè)跨語(yǔ)言的 rpc 服務(wù)框架盔性。
  • thrift 是一個(gè) Facebook 公布的開(kāi)源跨語(yǔ)言的 rpc 服務(wù)框架。

那 “rpc 又是什么” 呢呢岗?


二冕香、什么是 RPC?

RPC 定義

  • RPC = Remote Procedure Call后豫,遠(yuǎn)程過(guò)程調(diào)用悉尾。是一種進(jìn)程間通信方式,允許像調(diào)用本地服務(wù)一樣調(diào)用遠(yuǎn)程服務(wù)挫酿。
  • 比較另辟蹊徑的解釋是:RPC 是一種編程模式构眯,把對(duì)服務(wù)器的調(diào)用抽象為過(guò)程調(diào)用,通常還伴隨著框架代碼自動(dòng)生成等功能早龟。
  • 為了便于理解惫霸,先把“遠(yuǎn)程過(guò)程調(diào)用”和“本地過(guò)程調(diào)用”做個(gè)對(duì)比猫缭。
rpc與本地過(guò)程調(diào)用對(duì)比

“遠(yuǎn)程過(guò)程調(diào)用(RPC)” 與 “本地過(guò)程調(diào)用” 對(duì)比
由上圖可以看出:

  • 本地過(guò)程調(diào)用,所有的過(guò)程都在同一個(gè)服務(wù)器上它褪,依次調(diào)用即可饵骨。
  • 遠(yuǎn)程過(guò)程調(diào)用(即 RPC),部分過(guò)程在 Client 部分過(guò)程在 Server茫打,Client 想要使用 Server 中的過(guò)程并沒(méi)有本地過(guò)程調(diào)用那樣簡(jiǎn)單居触,需要兩個(gè)端(Client 和 Server)交互。

RPC 與本地過(guò)程調(diào)用的的差異具體體現(xiàn)在兩方面:

  1. RPC 要求雙方具有網(wǎng)絡(luò)通信能力老赤。網(wǎng)絡(luò)傳輸開(kāi)銷增加轮洋。
  2. RPC 要求雙方統(tǒng)一數(shù)據(jù)結(jié)構(gòu)和傳輸協(xié)議,因?yàn)檫h(yuǎn)程過(guò)程與主調(diào)方運(yùn)行在完全不同的地址空間中抬旺,無(wú)法通過(guò)傳遞指針直接調(diào)用弊予。編程復(fù)雜度增高。

這就很容易讓人想到基于 HTTP 的各種 webService 實(shí)現(xiàn)开财,因?yàn)樗耆珴M足前述兩個(gè)要求汉柒。

HTTP 和 RPC
這里單獨(dú)說(shuō)一下我的理解。

  • “基于 HTTP 的 webService 實(shí)現(xiàn)”责鳍,廣義上可以算是一種 RPC 的實(shí)現(xiàn)方式碾褂,因?yàn)椴煌?webService 之間可以通過(guò) HTTP 接口相互通信。
  • 但是 RPC 又不僅限于使用 HTTP 協(xié)議實(shí)現(xiàn)历葛,HTTP 僅僅是 RPC 實(shí)現(xiàn)可選的一種傳輸協(xié)議正塌,除了 HTTP 協(xié)議外,還可以選擇 TCP/MQ 等其他方式恤溶。

回到正題乓诽,接下來(lái)看 thrift 作為一個(gè) “RPC框架” 到底做了什么,或者說(shuō)能做什么咒程。


三鸠天、從 sayHi 開(kāi)始

為了便于理解,我們從一個(gè)非常簡(jiǎn)單的小需求入手:要求是客戶端輸入姓名(例如:小明)帐姻,服務(wù)端輸出打招呼的句子(例如:Hi,小明!)稠集。

PS:請(qǐng)不要把這里的 demo 當(dāng)范本“摘抄”,僅僅是為了說(shuō)明問(wèn)題卖宠,寫(xiě)的略挫 ==

3.1 原生 Socket 裸寫(xiě) sayHi

以 java 語(yǔ)言為例巍杈,用原生 jdk 類庫(kù)提供的能力實(shí)現(xiàn) sayHi 功能。

定義三個(gè)類:

  • V1SayHiService.java扛伍,sayHi 服務(wù)類筷畦。
  • V1SocketDemoServer.java,服務(wù)端。
  • V1SocketDemoClient.java鳖宾,客戶端吼砂。
// V1SayHiService.java
package com.ann.javas.projects.javacores.socket.demo;

public class V1SayHiService {
  public static String sayHi(String name) {
    return "Hi," + name + "!";
  }
}
// V1SocketDemoServer.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by liyanling on 2018/6/27.
 * ************************************
 * <p>
 * 交互方式(短連接)
 * <p>
 * 1、服務(wù)端啟動(dòng)監(jiān)聽(tīng)鼎文。
 * 2渔肩、服務(wù)端【每】獲得一個(gè)客戶端連接,處理完一條消息后即【關(guān)閉連接】拇惋,并【等待下一個(gè)客戶端連接】周偎。
 * 3、服務(wù)端按行讀取消息內(nèi)容撑帖,作為姓名參數(shù)蓉坎,回寫(xiě) sayHi 消息
 */
public class V1SocketDemoServer {

  private static Logger logger = LoggerFactory.getLogger(V1SocketDemoServer.class);

  public static final int PORT = 9091;
  private static int count = 1;

  public static void main(String[] args) throws Exception {
    startServer();
  }

  private static void startServer() throws Exception {
    try {

      ServerSocket serverSocket = new ServerSocket(PORT);
      logger.info("{}:服務(wù)端(端口:{})啟動(dòng)監(jiān)聽(tīng),等待客戶端連接", count++, PORT);

      while (true) {
        Socket socket = serverSocket.accept();
        logger.info("{}:服務(wù)端已與客戶端建立連接胡嘿,開(kāi)始接收客戶端消息...", count++);

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String name = reader.readLine();
        logger.info("{}:服務(wù)端獲取輸入流蛉艾,讀取客戶端信息:{}", count++, name);

        String sayHiResult = V1SayHiService.sayHi(name);
        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        writer.write(sayHiResult + "\n");
        writer.flush();
        logger.info("{}:服務(wù)端獲取輸出流,響應(yīng)客戶端請(qǐng)求衷敌,回寫(xiě):{}", count++, sayHiResult);

        writer.close();
        reader.close();
        socket.close();
//        serverSocket.close();// 這里不要把 serverSocket 關(guān)了勿侯。還得繼續(xù)監(jiān)聽(tīng)呢
        logger.info("{}:服務(wù)端關(guān)閉資源", count++);
        logger.info("{}:等待下一個(gè)客戶端連接", count++);
      }

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

}

// V1SocketDemoClient.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created by liyanling on 2018/6/27.
 */
public class V1SocketDemoClient {

  private static      Logger logger = LoggerFactory.getLogger(V1SocketDemoClient.class);
  public static final String IP     = "127.0.0.1";

  public static Scanner scanner;
  public static int count = 1;


  public static void main(String[] args) throws Exception {
    scanner = new Scanner(System.in);

    logger.info("{}:demo1-----------",count++);
    startClient(IP, V1SocketDemoServer.PORT);
    Thread.sleep(1000);

    logger.info("{}:demo2-----------",count++);
    startClient(IP, V1SocketDemoServer.PORT);

    scanner.close();
  }

  public static void startClient(String IP, int PORT) throws Exception {
    try {
      logger.info("{}:準(zhǔn)備連接服務(wù)端(IP:{},PORT:{})", count++, IP, PORT);

      Socket socket = new Socket(IP, PORT);
      logger.info("{}:服務(wù)端已連接成功(IP:{},PORT:{})", count++, IP, PORT);

      PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
      logger.info("{}:客戶端獲取輸出流,向服務(wù)端發(fā)送信息:", count++);

      logger.info("{}:請(qǐng)輸入姓名:", count++);
      String name = scanner.nextLine();

      writer.write(name + "\n");
      writer.flush();

      BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      logger.info("{}:客戶端獲取輸入流缴罗,讀取服務(wù)端返回信息:{}", count++, reader.readLine());

      reader.close();
      writer.close();
      socket.close();
      logger.info("{}:關(guān)閉資源", count++);

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

}

先啟動(dòng) Server 再啟動(dòng) Client助琐,然后在客戶端終端輸入“小明”并回車,兩端分別輸出的日志如下:

// V1SocketDemoServer.java 日志
00:28:02.023 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 1:服務(wù)端(端口:9091)啟動(dòng)監(jiān)聽(tīng)瞒爬,等待客戶端連接
00:28:06.032 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 2:服務(wù)端已與客戶端建立連接弓柱,開(kāi)始接收客戶端消息...
00:28:09.817 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 3:服務(wù)端獲取輸入流沟堡,讀取客戶端信息:小明
00:28:09.822 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 4:服務(wù)端獲取輸出流侧但,響應(yīng)客戶端請(qǐng)求,回寫(xiě):Hi,小明!
00:28:09.823 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 5:服務(wù)端關(guān)閉資源
00:28:09.823 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 6:等待下一個(gè)客戶端連接
00:28:10.828 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - 7:服務(wù)端已與客戶端建立連接航罗,開(kāi)始接收客戶端消息...

// V1SocketDemoClient.java 日志
00:28:06.028 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 1:demo1-----------
00:28:06.029 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 2:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9091)
00:28:06.032 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 3:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9091)
00:28:06.033 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 4:客戶端獲取輸出流禀横,向服務(wù)端發(fā)送信息:
00:28:06.033 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 5:請(qǐng)輸入姓名:
小明
00:28:09.822 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 6:客戶端獲取輸入流,讀取服務(wù)端返回信息:Hi,小明!
00:28:09.824 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 7:關(guān)閉資源
00:28:10.827 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 8:demo2-----------
00:28:10.828 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 9:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9091)
00:28:10.828 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 10:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9091)
00:28:10.829 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 11:客戶端獲取輸出流粥血,向服務(wù)端發(fā)送信息:
00:28:10.829 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoClient - 12:請(qǐng)輸入姓名:

這是原生 Socket 實(shí)現(xiàn)的 sayHi柏锄,非常簡(jiǎn)單,100多行代碼而已复亏。

雙端通信和服務(wù)規(guī)則如下:

  1. Server 僅提供 sayHi 功能趾娃。
  2. sayHi 功能僅一個(gè)姓名參數(shù)。
  3. 客戶端->服務(wù)端 逐行寫(xiě)入?yún)?shù)缔御,行數(shù)據(jù)即姓名抬闷。
  4. 服務(wù)端->客戶端 逐行回寫(xiě)返回值,行數(shù)據(jù)即 sayHi 消息。

3.2 sayHi 迭代升級(jí)

接下來(lái)笤成,我們對(duì) sayHi 功能升級(jí)一下:

  1. 加“性別”參數(shù)评架,男性稱呼為“xx先生”,女性稱呼為“xx女士”炕泳。
  2. 加“體重”參數(shù)纵诞,超過(guò) 200 斤則提醒:“你該減肥啦”。

到這兒培遵,前面的一部分規(guī)則就要被打破了浙芙,一次請(qǐng)求不能僅僅帶一個(gè)“姓名”參數(shù),還要帶上性別和體重籽腕,重新制定規(guī)則如下:

  1. Server 僅提供 sayHi 功能茁裙。
  2. sayHi 功能三個(gè)參數(shù):姓名、性別节仿、體重晤锥。
  3. 客戶端->服務(wù)端 逐行寫(xiě)入?yún)?shù),行數(shù)據(jù)按姓名廊宪、性別矾瘾、體重順序帶三個(gè)參數(shù),參數(shù)用“,”分隔箭启。
  4. 服務(wù)端->客戶端 逐行回寫(xiě)返回值壕翩,行數(shù)據(jù)即 sayHi 消息。

在原來(lái)的基礎(chǔ)上傅寡,做個(gè) V2 版本:

  • V2SayHiService.java放妈,sayHi 服務(wù)類。
  • V2SayHiParam.java荐操,sayHi 參數(shù)芜抒。
  • V2SocketDemoServer.java,服務(wù)端托启。
  • V2SocketDemoClient.java宅倒,客戶端。
// V2SayHiService.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by liyanling on 2018/9/9.
 */
public class V2SayHiService {

  private static       Logger logger          = LoggerFactory.getLogger(V1SocketDemoServer.class);
  private final static String SAY_HI_TEMPLATE = "Hi,%s%s!%s";

  public static String sayHi(String params) {
    try {
      return sayHi(V2SayHiParam.decode(params));
    } catch (Throwable t) {
      logger.error("caught exception,t=", t);
      return "sayHi參數(shù)格式錯(cuò)誤:" + params;
    }
  }

  private static String sayHi(V2SayHiParam v2SayHiParam) {
    String name = v2SayHiParam.getName();
    String nameSuffix = V2SayHiParam.Gender.BOY == v2SayHiParam.getGender() ? "先生" : "女士";
    String weightNotice = v2SayHiParam.getWeight() >= 200 ? "你該減肥啦~" : "";
    return String.format(SAY_HI_TEMPLATE, name, nameSuffix, weightNotice);
  }

}


// V2SayHiParam.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class V2SayHiParam {

  private static Logger logger = LoggerFactory.getLogger(V2SayHiParam.class);

  public String name;
  public Gender gender;
  public int    weight;

  public enum Gender {
    GIRL, BOY
  }

  public V2SayHiParam() {
  }

  public V2SayHiParam(String name, Gender gender, int weight) {
    this.name = name;
    this.gender = gender;
    this.weight = weight;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Gender getGender() {
    return gender;
  }

  public void setGender(Gender gender) {
    this.gender = gender;
  }

  public int getWeight() {
    return weight;
  }

  public void setWeight(int weight) {
    this.weight = weight;
  }

  @Override
  public String toString() {
    return "V2SayHiParam{" + "name='" + name + '\'' + ", gender=" + gender + ", weight=" + weight + '}';
  }


  // String -> V2SayHiParam
  public static V2SayHiParam decode(String params) {
    int separatorLocate1 = params.indexOf(',');
    int separatorLocate2 = params.indexOf(',', separatorLocate1 + 1);
    int length = params.length();

    String name = params.substring(0, separatorLocate1);
    String gender = params.substring(separatorLocate1 + 1, separatorLocate2);
    int weight = Integer.valueOf(params.substring(separatorLocate2 + 1, length));

    return new V2SayHiParam(name, Gender.valueOf(gender), weight);
  }


  // V2SayHiParam -> String
  public static String encode(V2SayHiParam v2SayHiParam) {
    return v2SayHiParam.getName() + "," + v2SayHiParam.getGender() + "," + v2SayHiParam.getWeight();
  }


}

// V2SocketDemoServer.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by liyanling on 2018/6/27.
 */
public class V2SocketDemoServer {

  private static Logger logger = LoggerFactory.getLogger(V2SocketDemoServer.class);

  public static final int PORT = 9092;
  private static int count = 1;

  public static void main(String[] args) throws Exception {
    startServer();
  }

  private static void startServer() throws Exception {
    try {

      ServerSocket serverSocket = new ServerSocket(PORT);
      logger.info("{}:服務(wù)端(端口:{})啟動(dòng)監(jiān)聽(tīng)屯耸,等待客戶端連接", count++, PORT);

      while (true) {
        Socket socket = serverSocket.accept();
        logger.info("{}:服務(wù)端已與客戶端建立連接拐迁,開(kāi)始接收客戶端消息...", count++);

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String params = reader.readLine();
        logger.info("{}:服務(wù)端獲取輸入流,讀取客戶端信息:{}", count++, params);

        String sayHiResult = V2SayHiService.sayHi(params);

        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        writer.write(sayHiResult + "\n");
        writer.flush();
        logger.info("{}:服務(wù)端獲取輸出流疗绣,響應(yīng)客戶端請(qǐng)求线召,回寫(xiě):{}", count++, sayHiResult);

        writer.close();
        reader.close();
        socket.close();
//        serverSocket.close();// 這里不要把 serverSocket 關(guān)了。還得繼續(xù)監(jiān)聽(tīng)呢
        logger.info("{}:服務(wù)端關(guān)閉資源", count++);
        logger.info("{}:等待下一個(gè)客戶端連接", count++);
      }

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

}

// V2SocketDemoClient.java
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created by liyanling on 2018/6/27.
 */
public class V2SocketDemoClient {

  private static      Logger logger = LoggerFactory.getLogger(V2SocketDemoClient.class);
  public static final String IP     = "127.0.0.1";

  public static Scanner scanner;
  public static int count = 1;

  public static void main(String[] args) throws Exception {
    scanner = new Scanner(System.in);

    logger.info("{}:demo1-----------",count++);
    startClient(IP, V2SocketDemoServer.PORT);
    Thread.sleep(1000);
    logger.info("{}:demo2-----------",count++);
    startClient(IP, V2SocketDemoServer.PORT);

    scanner.close();
  }

  public static void startClient(String IP, int PORT) throws Exception {
    try {
      logger.info("{}:準(zhǔn)備連接服務(wù)端(IP:{},PORT:{})", count++, IP, PORT);

      Socket socket = new Socket(IP, PORT);
      logger.info("{}:服務(wù)端已連接成功(IP:{},PORT:{})", count++, IP, PORT);

      PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
      logger.info("{}:客戶端獲取輸出流多矮,向服務(wù)端發(fā)送信息:", count++);

      logger.info("{}:請(qǐng)輸入姓名:", count++);
      String name = scanner.nextLine();
      logger.info("{}:請(qǐng)輸入性別(GIRL / BOY):", count++);
      String gender = scanner.nextLine();
      logger.info("{}:請(qǐng)輸入體重(單位:斤):", count++);
      Integer weight = Integer.valueOf(scanner.nextLine());

      V2SayHiParam v2SayHiParam = new V2SayHiParam(name, V2SayHiParam.Gender.valueOf(gender), weight);

      writer.write(V2SayHiParam.encode(v2SayHiParam) + "\n");
      writer.flush();

      BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      logger.info("{}:客戶端獲取輸入流缓淹,讀取服務(wù)端返回信息:{}", count++, reader.readLine());

      reader.close();
      writer.close();
      socket.close();
      logger.info("{}:關(guān)閉資源", count++);

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }
}

仍舊是先啟動(dòng)服務(wù)端,再啟動(dòng)客戶端,在客戶端終端輸入:大熊割卖、BOY前酿、220,分別是姓名鹏溯、性別罢维、體重。輸出日志如下:

// V2SocketDemoServer.java 日志
00:30:02.588 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 1:服務(wù)端(端口:9092)啟動(dòng)監(jiān)聽(tīng)丙挽,等待客戶端連接
00:30:05.646 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 2:服務(wù)端已與客戶端建立連接肺孵,開(kāi)始接收客戶端消息...
00:30:17.821 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 3:服務(wù)端獲取輸入流,讀取客戶端信息:大熊,BOY,220
00:30:17.829 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 4:服務(wù)端獲取輸出流颜阐,響應(yīng)客戶端請(qǐng)求平窘,回寫(xiě):Hi,大熊先生!你該減肥啦~
00:30:17.830 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 5:服務(wù)端關(guān)閉資源
00:30:17.830 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 6:等待下一個(gè)客戶端連接
00:30:18.835 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoServer - 7:服務(wù)端已與客戶端建立連接,開(kāi)始接收客戶端消息...

// V2SocketDemoClient.java 日志
00:30:05.639 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 1:demo1-----------
00:30:05.641 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 2:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9092)
00:30:05.646 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 3:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9092)
00:30:05.648 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 4:客戶端獲取輸出流凳怨,向服務(wù)端發(fā)送信息:
00:30:05.648 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 5:請(qǐng)輸入姓名:
大熊
00:30:12.951 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 6:請(qǐng)輸入性別(GIRL / BOY):
BOY
00:30:15.267 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 7:請(qǐng)輸入體重(單位:斤):
220
00:30:17.829 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 8:客戶端獲取輸入流瑰艘,讀取服務(wù)端返回信息:Hi,大熊先生!你該減肥啦~
00:30:17.830 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 9:關(guān)閉資源
00:30:18.834 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 10:demo2-----------
00:30:18.834 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 11:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9092)
00:30:18.835 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 12:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9092)
00:30:18.835 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 13:客戶端獲取輸出流,向服務(wù)端發(fā)送信息:
00:30:18.835 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V2SocketDemoClient - 14:請(qǐng)輸入姓名:

3.3 能力擴(kuò)展

再來(lái)個(gè)升級(jí)肤舞,除 sayHi 功能外紫新,還需要:

  1. 新增 locate 功能,用于定位用戶位置李剖。
  2. 新增 adsRecommendation 功能芒率,用于推送廣告。

前面的實(shí)現(xiàn)默認(rèn)只提供 sayHi 功能篙顺,要新增 locate 和 adsRecommendation 支持偶芍,再次修改規(guī)則為:

  1. Server 僅提供 sayHi、locate德玫、adsRecommendation功能匪蟀。
  2. sayHi 功能三個(gè)參數(shù):姓名、性別化焕、體重萄窜。
  3. locate 和 adsRecommendation 無(wú)參數(shù)铃剔。
  4. 客戶端->服務(wù)端 逐行寫(xiě)入功能名和參數(shù)撒桨,參數(shù)仍舊按順序“,”分隔,功能名和參數(shù)之間用“|”分隔键兜。
  5. 服務(wù)端->客戶端 逐行回寫(xiě)返回值凤类。

類定義和源碼如下:

  • V3DemoService.java,demo 服務(wù)類普气,提供 sayHi谜疤、locate、adsRecommendation 功能。
  • V3SayHiParam.java夷磕,sayHi 參數(shù)履肃。
  • V3ocketDemoServer.java,服務(wù)端坐桩。
  • V3SocketDemoClient.java尺棋,客戶端。
// V3DemoService.java 
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.List;
import java.util.Random;

/**
 * Created by liyanling on 2018/9/9.
 */
public class V3DemoService {

  private static       Logger logger          = LoggerFactory.getLogger(V1SocketDemoServer.class);

  private final static String SAY_HI_TEMPLATE = "Hi,%s%s!%s";

  private final static List<String> CITIES = Arrays.asList("上海", "北京", "廣州", "成都", "內(nèi)蒙古", "香港", "河北", "云南");

  private final static List<String> ADS = Arrays.asList("坐紅旗車绵跷,走中國(guó)路", "要想皮膚好膘螟,早晚用大寶", "喝匯源果汁,走健康之路", "送禮就送腦白金",
                                                        "飄柔碾局,就是這么自信");

  public static String locate() {
    int citySize = CITIES.size();
    int randomIndex = Math.abs((new Random(System.currentTimeMillis())).nextInt()) % citySize;
    return CITIES.get(randomIndex);
  }

  public static String adsRecommendation() {
    int adsSize = ADS.size();
    int randomIndex = Math.abs((new Random(System.currentTimeMillis())).nextInt()) % adsSize;
    return ADS.get(randomIndex);
  }

  public static String sayHi(String params) {
    try {
      return sayHi(V3SayHiParam.decode(params));
    } catch (Throwable t) {
      logger.error("caught exception,t=", t);
      return "sayHi參數(shù)格式錯(cuò)誤:" + params;
    }
  }

  private static String sayHi(V3SayHiParam v3SayHiParam) {
    String name = v3SayHiParam.getName();
    String nameSuffix = V3SayHiParam.Gender.BOY == v3SayHiParam.getGender() ? "先生" : "女士";
    String weightNotice = v3SayHiParam.getWeight() >= 200 ? "你該減肥啦~" : "";
    return String.format(SAY_HI_TEMPLATE, name, nameSuffix, weightNotice);
  }

  public static String doSomething(String params) {
    try {
      int separatorLocate = params.indexOf('|');
      int length = params.length();

      String function = params.substring(0, separatorLocate);

      String functionParams = params.substring(separatorLocate + 1, length);

      logger.info("demoService function:{} functionParams:{}",function,functionParams);
      if ("sayHi".equals(function)) {
        return sayHi(functionParams);
      } else if ("locate".equals(function)) {
        return locate();
      } else if ("adsRecommendation".equals(function)) {
        return adsRecommendation();
      } else {
        return "不支持" + function + "功能";
      }

    } catch (Throwable t) {
      logger.error("caught error,t=", t);
      return "參數(shù)格式錯(cuò)誤:" + params;
    }

  }
}


// V3SayHiParam.java 
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class V3SayHiParam {

  private static Logger logger = LoggerFactory.getLogger(V3SayHiParam.class);

  public String name;
  public Gender gender;
  public int    weight;

  public enum Gender {
    GIRL, BOY
  }

  public V3SayHiParam() {
  }

  public V3SayHiParam(String name, Gender gender, int weight) {
    this.name = name;
    this.gender = gender;
    this.weight = weight;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Gender getGender() {
    return gender;
  }

  public void setGender(Gender gender) {
    this.gender = gender;
  }

  public int getWeight() {
    return weight;
  }

  public void setWeight(int weight) {
    this.weight = weight;
  }

  @Override
  public String toString() {
    return "V3SayHiParam{" + "name='" + name + '\'' + ", gender=" + gender + ", weight=" + weight + '}';
  }


  // String -> V3SayHiParam
  public static V3SayHiParam decode(String params) {
    int separatorLocate1 = params.indexOf(',');
    int separatorLocate2 = params.indexOf(',', separatorLocate1 + 1);
    int length = params.length();

    String name = params.substring(0, separatorLocate1);
    String gender = params.substring(separatorLocate1 + 1, separatorLocate2);
    int weight = Integer.valueOf(params.substring(separatorLocate2 + 1, length));

    return new V3SayHiParam(name, Gender.valueOf(gender), weight);
  }


  // V3SayHiParam -> String
  public static String encode(V3SayHiParam v3SayHiParam) {
    return v3SayHiParam.getName() + "," + v3SayHiParam.getGender() + "," + v3SayHiParam.getWeight();
  }


}

// V3ocketDemoServer.java 
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by liyanling on 2018/6/27.
 */
public class V3SocketDemoServer {

  private static Logger logger = LoggerFactory.getLogger(V3SocketDemoServer.class);

  public static final int PORT = 9093;
  private static int count = 1;

  public static void main(String[] args) throws Exception {
    startServer();
  }

  private static void startServer() throws Exception {
    try {

      ServerSocket serverSocket = new ServerSocket(PORT);
      logger.info("{}:服務(wù)端(端口:{})啟動(dòng)監(jiān)聽(tīng)荆残,等待客戶端連接", count++, PORT);

      while (true) {
        Socket socket = serverSocket.accept();
        logger.info("{}:服務(wù)端已與客戶端建立連接,開(kāi)始接收客戶端消息...", count++);

        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        String params = reader.readLine();
        logger.info("{}:服務(wù)端獲取輸入流净当,讀取客戶端信息:{}", count++, params);

        String sayHiResult = V3DemoService.doSomething(params);

        PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
        writer.write(sayHiResult + "\n");
        writer.flush();
        logger.info("{}:服務(wù)端獲取輸出流内斯,響應(yīng)客戶端請(qǐng)求,回寫(xiě):{}", count++, sayHiResult);

        writer.close();
        reader.close();
        socket.close();
//        serverSocket.close();// 這里不要把 serverSocket 關(guān)了像啼。還得繼續(xù)監(jiān)聽(tīng)呢
        logger.info("{}:服務(wù)端關(guān)閉資源", count++);
        logger.info("{}:等待下一個(gè)客戶端連接", count++);
      }

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

}

// V3SocketDemoClient.java 
package com.ann.javas.projects.javacores.socket.demo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * Created by liyanling on 2018/6/27.
 */
public class V3SocketDemoClient {

  private static      Logger logger = LoggerFactory.getLogger(V3SocketDemoClient.class);
  public static final String IP     = "127.0.0.1";

  public static Scanner scanner;
  public static int count = 1;

  public static void main(String[] args) throws Exception {
    scanner = new Scanner(System.in);

    logger.info("{}:demo1-----------", count++);
    startClient(IP, V3SocketDemoServer.PORT);
    Thread.sleep(1000);
    logger.info("{}:demo2-----------", count++);
    startClient(IP, V3SocketDemoServer.PORT);

    Thread.sleep(1000);
    logger.info("{}:demo3-----------", count++);
    startClient(IP, V3SocketDemoServer.PORT);

    scanner.close();
  }

  public static void startClient(String IP, int PORT) throws Exception {
    try {
      logger.info("{}:準(zhǔn)備連接服務(wù)端(IP:{},PORT:{})", count++, IP, PORT);

      Socket socket = new Socket(IP, PORT);
      logger.info("{}:服務(wù)端已連接成功(IP:{},PORT:{})", count++, IP, PORT);

      PrintWriter writer = new PrintWriter(socket.getOutputStream(), true);
      logger.info("{}:客戶端獲取輸出流嘿期,向服務(wù)端發(fā)送信息:", count++);

      logger.info("{}:請(qǐng)輸入功能名(sayHi / locate / adsRecommendation):", count++);
      String function = scanner.nextLine();
      String line = "";



      if ("sayHi".equals(function)) {
        logger.info("{}:請(qǐng)輸入姓名:", count++);
        String name = scanner.nextLine();
        logger.info("{}:請(qǐng)輸入性別(GIRL / BOY):", count++);
        String gender = scanner.nextLine();
        logger.info("{}:請(qǐng)輸入體重(單位:斤):", count++);
        Integer weight = Integer.valueOf(scanner.nextLine());

        V3SayHiParam v3SayHiParam = new V3SayHiParam(name, V3SayHiParam.Gender.valueOf(gender), weight);
        line = function + "|" + V3SayHiParam.encode(v3SayHiParam);
      } else {
        line = function + "|";
      }

      writer.write(line + "\n");
      writer.flush();
      BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
      logger.info("{}:客戶端獲取輸入流,讀取服務(wù)端返回信息:{}", count++, reader.readLine());

      reader.close();
      writer.close();
      socket.close();
      logger.info("{}:關(guān)閉資源", count++);

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }
}

分別測(cè)試 locate埋合、adsRecommendation备徐、sayHi 功能,輸出日志如下:

// V3SocketDemoServer.java 日志
00:31:37.510 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 1:服務(wù)端(端口:9093)啟動(dòng)監(jiān)聽(tīng)甚颂,等待客戶端連接
00:31:51.403 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 2:服務(wù)端已與客戶端建立連接蜜猾,開(kāi)始接收客戶端消息...
00:32:00.525 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 3:服務(wù)端獲取輸入流,讀取客戶端信息:locate|
00:32:00.532 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - demoService function:locate functionParams:
00:32:00.534 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 4:服務(wù)端獲取輸出流振诬,響應(yīng)客戶端請(qǐng)求蹭睡,回寫(xiě):上海
00:32:00.535 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 5:服務(wù)端關(guān)閉資源
00:32:00.535 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 6:等待下一個(gè)客戶端連接
00:32:01.539 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 7:服務(wù)端已與客戶端建立連接,開(kāi)始接收客戶端消息...
00:32:08.982 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 8:服務(wù)端獲取輸入流赶么,讀取客戶端信息:adsRecommendation|
00:32:08.982 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - demoService function:adsRecommendation functionParams:
00:32:08.983 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 9:服務(wù)端獲取輸出流肩豁,響應(yīng)客戶端請(qǐng)求,回寫(xiě):飄柔辫呻,就是這么自信
00:32:08.983 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 10:服務(wù)端關(guān)閉資源
00:32:08.983 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 11:等待下一個(gè)客戶端連接
00:32:09.989 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 12:服務(wù)端已與客戶端建立連接清钥,開(kāi)始接收客戶端消息...
00:32:25.251 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 13:服務(wù)端獲取輸入流,讀取客戶端信息:sayHi|小明,GIRL,210
00:32:25.252 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V1SocketDemoServer - demoService function:sayHi functionParams:小明,GIRL,210
00:32:25.254 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 14:服務(wù)端獲取輸出流放闺,響應(yīng)客戶端請(qǐng)求祟昭,回寫(xiě):Hi,小明女士!你該減肥啦~
00:32:25.254 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 15:服務(wù)端關(guān)閉資源
00:32:25.255 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoServer - 16:等待下一個(gè)客戶端連接

// V3SocketDemoClient.java 日志
00:31:51.398 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 1:demo1-----------
00:31:51.399 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 2:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9093)
00:31:51.404 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 3:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9093)
00:31:51.406 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 4:客戶端獲取輸出流,向服務(wù)端發(fā)送信息:
00:31:51.407 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 5:請(qǐng)輸入功能名(sayHi / locate / adsRecommendation):
locate
00:32:00.535 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 6:客戶端獲取輸入流怖侦,讀取服務(wù)端返回信息:上海
00:32:00.535 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 7:關(guān)閉資源
00:32:01.538 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 8:demo2-----------
00:32:01.538 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 9:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9093)
00:32:01.539 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 10:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9093)
00:32:01.539 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 11:客戶端獲取輸出流篡悟,向服務(wù)端發(fā)送信息:
00:32:01.539 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 12:請(qǐng)輸入功能名(sayHi / locate / adsRecommendation):
adsRecommendation
00:32:08.983 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 13:客戶端獲取輸入流谜叹,讀取服務(wù)端返回信息:飄柔,就是這么自信
00:32:08.983 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 14:關(guān)閉資源
00:32:09.988 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 15:demo3-----------
00:32:09.988 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 16:準(zhǔn)備連接服務(wù)端(IP:127.0.0.1,PORT:9093)
00:32:09.991 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 17:服務(wù)端已連接成功(IP:127.0.0.1,PORT:9093)
00:32:09.991 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 18:客戶端獲取輸出流搬葬,向服務(wù)端發(fā)送信息:
00:32:09.991 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 19:請(qǐng)輸入功能名(sayHi / locate / adsRecommendation):
sayHi
00:32:12.661 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 20:請(qǐng)輸入姓名:
小明
00:32:16.647 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 21:請(qǐng)輸入性別(GIRL / BOY):
GIRL
00:32:22.979 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 22:請(qǐng)輸入體重(單位:斤):
210
00:32:25.254 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 23:客戶端獲取輸入流荷腊,讀取服務(wù)端返回信息:Hi,小明女士!你該減肥啦~
00:32:25.254 [main] INFO  com.ann.javas.projects.javacores.socket.demo.V3SocketDemoClient - 24:關(guān)閉資源

3.4 小結(jié)

從 V1 到 V2 再到 V3,你會(huì)發(fā)現(xiàn)一個(gè)很痛的點(diǎn)是:隨便來(lái)個(gè)新需求急凰,都會(huì)導(dǎo)致客戶端與服務(wù)端的“大換血”停局,可擴(kuò)展性非常差,說(shuō)到底香府,還是“序列化與反序列化規(guī)則”做的不夠好董栽,單單行數(shù)據(jù)、","分隔企孩、"|"分隔 這種“口頭”約定怔蚌,是遠(yuǎn)遠(yuǎn)不夠的爹土。

從這個(gè)角度入手,我們來(lái)看 thrift 是怎么做的。


下接:thrift 入門(mén)(2/2)
PS:我也不想拆饱溢,但是放一起文章太長(zhǎng)無(wú)法發(fā)布蝶俱。瘦馍。碳褒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市莲组,隨后出現(xiàn)的幾起案子诊胞,更是在濱河造成了極大的恐慌,老刑警劉巖锹杈,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撵孤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡竭望,警方通過(guò)查閱死者的電腦和手機(jī)邪码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)咬清,“玉大人闭专,你說(shuō)我怎么就攤上這事【缮眨” “怎么了影钉?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)粪滤。 經(jīng)常有香客問(wèn)我斧拍,道長(zhǎng),這世上最難降的妖魔是什么杖小? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任肆汹,我火速辦了婚禮,結(jié)果婚禮上予权,老公的妹妹穿的比我還像新娘昂勉。我一直安慰自己,他們只是感情好扫腺,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布岗照。 她就那樣靜靜地躺著,像睡著了一般笆环。 火紅的嫁衣襯著肌膚如雪攒至。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天躁劣,我揣著相機(jī)與錄音迫吐,去河邊找鬼。 笑死账忘,一個(gè)胖子當(dāng)著我的面吹牛志膀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鳖擒,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼溉浙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了蒋荚?” 一聲冷哼從身側(cè)響起戳稽,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎期升,沒(méi)想到半個(gè)月后广鳍,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吓妆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年赊时,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片行拢。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祖秒,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舟奠,到底是詐尸還是另有隱情竭缝,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布沼瘫,位于F島的核電站抬纸,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏耿戚。R本人自食惡果不足惜湿故,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一阿趁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坛猪,春花似錦脖阵、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至就斤,卻和暖如春悍募,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背洋机。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工坠宴, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人槐秧。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓啄踊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親刁标。 傳聞我的和親對(duì)象是個(gè)殘疾皇子颠通,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)自:http://blog.csdn.net/kesonyk/article/details/50924489 ...
    晴天哥_王志閱讀 24,784評(píng)論 2 38
  • 上接:thrift 入門(mén)(1/2)PS:我也不想拆,但是放一起文章太長(zhǎng)無(wú)法發(fā)布膀懈。顿锰。。 四启搂、thrift 入門(mén) 4....
    李眼鏡閱讀 1,060評(píng)論 0 1
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理硼控,服務(wù)發(fā)現(xiàn),斷路器胳赌,智...
    卡卡羅2017閱讀 134,628評(píng)論 18 139
  • Thrift是什么牢撼? Thrift是Facebook于2007年開(kāi)發(fā)的跨語(yǔ)言的rpc服框架,提供多語(yǔ)言的編譯功能疑苫,...
    jiangmo閱讀 9,403評(píng)論 0 6
  • 現(xiàn)在有很多家長(zhǎng),花很多很多的時(shí)間陪孩子挺勿,但一般都在做兩件事:1.下指令曲横,糾偏。指導(dǎo)孩子做作業(yè)不瓶,然后挑出錯(cuò)誤的禾嫉,要求...
    高宇_Betty閱讀 411評(píng)論 1 5