Thrift框架服務端併發處理模式的java示例

項目因爲需要對外提供不同語言的接口,所以我們採用了高效、跨語言的RPC框架Thrift。因爲用的爽!順理成章繼續沿用,但是這次並不是屢試不爽。項目的Thrift服務端沒辦法併發請求處理,一直都單線程的將非阻塞的客服端請求one by one的處理,如果請求處理時間長的話,就會出現請求高延時的情況。so bad! 不過XXX,終於XXX!以下就是我學習Thrift API之後找到的解決方案示例。

軟件:Thrift-0.9.2

依賴:jdk1.7

開發環境:ideaIU-14.1.4

測試環境:win7

Thrift-0.9.2下載地址:http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.2/thrift-0.9.2.exe

建立maven工程ThriftDemo,在pom.xml配置文件添加必要的依賴:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

 

    <groupId>ThriftDemo</groupId>

    <artifactId>ThriftDemo</artifactId>

    <version>1.0-SNAPSHOT</version>

 

    <dependencies>

 

        <dependency>

            <groupId>org.apache.thrift</groupId>

            <artifactId>libthrift</artifactId>

            <version>0.9.2</version>

        </dependency>

 

        <dependency>

            <groupId>org.slf4j</groupId>

            <artifactId>slf4j-api</artifactId>

            <version>1.7.12</version>

        </dependency>

 

    </dependencies>

 

</project>

根據 Thrift 的語法規範編寫腳本文件 UserService.thrift:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

namespace java com.mangocool.thrift

 

struct User

{

    1:string id

    2:string name

    3:i32 sex

}

 

service UserService

{

    string whatIsName(1:string word)

    User userInfo(1:string id)

}

對於以上腳本和如何生成User.java和UserService.java在這裏就不贅述了,之前的一片文章有,地址:一個簡單的Thrift框架Java語言示例

實現類UserServiceImpl.java:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package com.mangocool.thrift;

 

import org.apache.thrift.TException;

 

/**

 * Created by MANGOCOOL on 2015/9/7.

 */

public class UserServiceImpl implements UserService.Iface

{

 

    @Override

    public String whatIsName(String word) throws TException {

        String name = "what talking about?";

        System.out.println("what your name?");

        if(!word.isEmpty())

        {

            try {

                for(int i=0; i<30; i++)

                {

                    System.out.println("wo..." + i);

                    Thread.sleep(500);

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            name = "my name is mangocool!";

        }

        System.out.println("接收的參數word: " + word);

        return name;

    }

 

    @Override

    public User userInfo(String id) throws TException {

        User user = new User();

        if(!id.isEmpty() && id.equals("1023"))

        {

            user.setId("1023");

            user.setName("mangocool");

            user.setSex(1);

        } else

        {

            user.setName("no user!");

        }

        System.out.println("接收的參數id: " + id);

        return user;

    }

}

爲方便稍後的併發測試,增加了休眠。

客戶端UserServiceClient.java:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

package com.mangocool.thrift;

 

import org.apache.thrift.TException;

import org.apache.thrift.async.TAsyncClientManager;

import org.apache.thrift.protocol.TCompactProtocol;

import org.apache.thrift.protocol.TProtocol;

import org.apache.thrift.protocol.TProtocolFactory;

import org.apache.thrift.transport.*;

 

import java.io.IOException;

 

/**

 * Created by MANGOCOOL on 2015/9/7.

 */

public class UserServiceClient {

 

    String address = "localhost";

    int port = 7911;

    int timeout = 100*1000;

 

    public void start()

    {

        //使用非阻塞方式,按塊的大小進行傳輸,類似於Java中的NIO。記得調用close釋放資源

        TTransport transport =

                new TFramedTransport(new TSocket(address, port, timeout));

        //高效率的、密集的二進制編碼格式進行數據傳輸協議

        TProtocol protocol = new TCompactProtocol(transport);

        UserService.Client client = new UserService.Client(protocol);

        try {

            open(transport);

            System.out.println(client.whatIsName("hello!"));

            close(transport);

        } catch (TException e) {

            e.printStackTrace();

        }

    }

 

    public void open(TTransport transport)

    {

        if(transport != null && !transport.isOpen())

        {

            try {

                transport.open();

            } catch (TTransportException e) {

                e.printStackTrace();

            }

        }

    }

 

    public void close(TTransport transport)

    {

        if(transport != null && transport.isOpen())

        {

            transport.close();

        }

    }

 

    public static void main(String[] args) {

        UserServiceClient usc = new UserServiceClient();

        usc.start();

    }

}

服務端UserServiceServer.java:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

package com.mangocool.thrift;

 

import org.apache.thrift.TProcessor;

import org.apache.thrift.TProcessorFactory;

import org.apache.thrift.protocol.TBinaryProtocol;

import org.apache.thrift.protocol.TCompactProtocol;

import org.apache.thrift.server.*;

import org.apache.thrift.transport.TFramedTransport;

import org.apache.thrift.transport.TNonblockingServerSocket;

import org.apache.thrift.transport.TNonblockingServerTransport;

import org.apache.thrift.transport.TTransportException;

 

import java.util.concurrent.Executor;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

 

/**

 * Created by MANGOCOOL on 2015/9/7.

 */

public class UserServiceServer {

 

    private int servicePort = 7911;

 

    public void invoke()

    {

        try {

            // 非阻塞式的,配合TFramedTransport使用

            TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(servicePort);

            // 關聯處理器與Service服務的實現

            TProcessor processor = new UserService.Processor<UserService.Iface>(new UserServiceImpl());

            // 目前Thrift提供的最高級的模式,可併發處理客戶端請求

            TThreadedSelectorServer.Args args = new TThreadedSelectorServer.Args(serverTransport);

            args.processor(processor);

            // 設置協議工廠,高效率的、密集的二進制編碼格式進行數據傳輸協議

            args.protocolFactory(new TCompactProtocol.Factory());

            // 設置傳輸工廠,使用非阻塞方式,按塊的大小進行傳輸,類似於Java中的NIO

            args.transportFactory(new TFramedTransport.Factory());

            // 設置處理器工廠,只返回一個單例實例

            args.processorFactory(new TProcessorFactory(processor));

            // 多個線程,主要負責客戶端的IO處理

            args.selectorThreads(2);

            // 工作線程池

            ExecutorService pool = Executors.newFixedThreadPool(3);

            args.executorService(pool);

            TThreadedSelectorServer server = new TThreadedSelectorServer(args);

            System.out.println("Starting server on port " + servicePort + "......");

            server.serve();

        } catch (TTransportException e) {

            e.printStackTrace();

        }

    }

 

    public static void main(String[] args) {

        UserServiceServer uss = new UserServiceServer();

        uss.invoke();

    }

}

關於ExecutorService線程池的詳解,也可以參考文章:Executor線程池詳解

測試的時候,先啓動服務端,然後啓動多個客戶端,就可以看到服務端併發處理請求的效果了。

 

學習文章:http://blog.csdn.net/houjixin/article/details/42779915

Github源碼:https://github.com/apache/thrift/blob/master/lib/java/src/org/apache/thrift/server/TThreadedSelectorServer.java

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章