grpc是google在github于2015年开源的一款RPC框架,虽然protobuf很早google就开源了,但是google一直没推出正式的开源框架,导致github上基于protobuf的rpc五花八门,国内比较著名的有百度的sofa-pbrpc,但是遗憾的是soft-pbrpc没有对应的java实现版本。rgpc还有一个独立的官网:http://www.grpc.io/,目前已经支持的语言有 C, C++, Java, Go, Node.js, Python, Ruby, Objective-C, PHP 、 C#. grpc最大的特点是基于protobuf + http2 协议,http2协议虽然还未正式定稿,但从目前得知的内容来看,潜力巨大。下面是grpc基本的hello world的示例:
一、grpc-contract
还是按老套路,把服务涉及的对象定义、接口定义抽象出来,下面是项目结构图:
pom.xml的内容如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>yjmyzz.grpc</groupId> 8 <artifactId>grpc-contract</artifactId> 9 <version>1.0</version> 10 11 12 <dependencies> 13 14 <dependency> 15 <groupId>junit</groupId> 16 <artifactId>junit</artifactId> 17 <version>4.10</version> 18 </dependency> 19 20 <dependency> 21 <groupId>com.google.protobuf</groupId> 22 <artifactId>protobuf-java</artifactId> 23 <version>3.0.0-beta-1</version> 24 </dependency> 25 26 <dependency> 27 <groupId>io.grpc</groupId> 28 <artifactId>grpc-all</artifactId> 29 <version>0.8.0</version> 30 </dependency> 31 32 </dependencies> 33 34 <!--下面这个节点可选--> 35 <pluginRepositories> 36 <pluginRepository> 37 <releases> 38 <updatePolicy>never</updatePolicy> 39 </releases> 40 <snapshots> 41 <enabled>false</enabled> 42 </snapshots> 43 <id>central</id> 44 <name>Central Repository</name> 45 <url>https://repo.maven.apache.org/maven2</url> 46 </pluginRepository> 47 <pluginRepository> 48 <id>protoc-plugin</id> 49 <url>https://dl.bintray.com/sergei-ivanov/maven/</url> 50 </pluginRepository> 51 </pluginRepositories> 52 53 54 <build> 55 <extensions> 56 <extension> 57 <groupId>kr.motd.maven</groupId> 58 <artifactId>os-maven-plugin</artifactId> 59 <version>1.4.0.Final</version> 60 </extension> 61 </extensions> 62 <plugins> 63 <!--用于根据proto文件生成java类的插件--> 64 <plugin> 65 <groupId>com.google.protobuf.tools</groupId> 66 <artifactId>maven-protoc-plugin</artifactId> 67 <version>0.4.2</version> 68 <configuration> 69 <protocArtifact>com.google.protobuf:protoc:3.0.0-alpha-3.1:exe:${os.detected.classifier} 70 </protocArtifact> 71 <pluginId>grpc-java</pluginId> 72 <pluginArtifact>io.grpc:protoc-gen-grpc-java:0.8.0:exe:${os.detected.classifier}</pluginArtifact> 73 </configuration> 74 <executions> 75 <execution> 76 <goals> 77 <goal>compile</goal> 78 <goal>compile-custom</goal> 79 </goals> 80 </execution> 81 </executions> 82 </plugin> 83 84 <!--生成源代码jar包的插件(可选)--> 85 <plugin> 86 <artifactId>maven-source-plugin</artifactId> 87 <version>2.4</version> 88 <executions> 89 <execution> 90 <phase>package</phase> 91 <goals> 92 <goal>jar-no-fork</goal> 93 </goals> 94 </execution> 95 </executions> 96 </plugin> 97 98 </plugins> 99 </build> 100 </project>
demo_service_dto.proto内容如下:
syntax = "proto3"; package yjmyzz.grpc.study.dto; option java_multiple_files = true; option java_outer_classname = "DemoServiceDto"; message PingRequest { string in=1; } message PingResponse { string out=1; } message QueryParameter { int32 ageStart = 1; int32 ageEnd = 2; } message Person { int32 age = 1; string name = 2; bool sex=3; double salary=4; int32 childrenCount=5; } message PersonList{ repeated Person items=1; }
注:grpc要求protobuf必须使用3.0以上版本
demo_service.proto内容如下:
mvn install 后,会自动在target下生成相应的java class类
二、grpc-server
pom.xml文件如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>yjmyzz.grpc</groupId> 8 <artifactId>grpc-server</artifactId> 9 <version>1.0</version> 10 11 12 <dependencies> 13 <dependency> 14 <groupId>com.google.protobuf</groupId> 15 <artifactId>protobuf-java</artifactId> 16 <version>3.0.0-beta-1</version> 17 </dependency> 18 19 <dependency> 20 <groupId>yjmyzz.grpc</groupId> 21 <artifactId>grpc-contract</artifactId> 22 <version>1.0</version> 23 </dependency> 24 25 <dependency> 26 <groupId>io.grpc</groupId> 27 <artifactId>grpc-all</artifactId> 28 <version>0.8.0</version> 29 </dependency> 30 31 <dependency> 32 <groupId>junit</groupId> 33 <artifactId>junit</artifactId> 34 <version>4.10</version> 35 </dependency> 36 37 </dependencies> 38 39 40 </project>
先对服务接口提供实现:
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
|
package yjmyzz.grpc.study.service.impl; import io.grpc.stub.StreamObserver; import yjmyzz.grpc.study.dto.*; import yjmyzz.grpc.study.service.DemoServiceGrpc; import java.util.ArrayList; import java.util.List; public class DemoServiceImpl implements DemoServiceGrpc.DemoService { public void ping(PingRequest pingRequest, StreamObserver<PingResponse> streamObserver) { PingResponse reply = PingResponse.newBuilder().setOut( "pong => " + pingRequest.getIn()).build(); streamObserver.onValue(reply); streamObserver.onCompleted(); } public void getPersonList(QueryParameter queryParameter, StreamObserver<PersonList> streamObserver) { //System.out.println(queryParameter.getAgeStart() + "-" + queryParameter.getAgeEnd()); PersonList.Builder personListBuilder = PersonList.newBuilder(); Person.Builder builder = Person.newBuilder(); List<Person> list = new ArrayList<Person>(); for ( short i = 0 ; i < 10 ; i++) { list.add(builder.setAge(i).setChildrenCount(i).setName( "test" + i).setSex( true ).build()); } personListBuilder.addAllItems(list); streamObserver.onValue(personListBuilder.build()); streamObserver.onCompleted(); } } |
和前面thrift、avro的helloworld一样,这里的实现只是意思一下,方便测试而已。
grpc的server端是基于Netty的(当然还有OKHttp的实现,详情见github项目主页),下面是server端的代码:
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 yjmyzz.grpc.study.server; import io.grpc.ServerImpl; import io.grpc.inprocess.InProcessServerBuilder; import io.grpc.netty.NettyServerBuilder; import yjmyzz.grpc.study.service.DemoServiceGrpc; import yjmyzz.grpc.study.service.impl.DemoServiceImpl; public class DemoServiceServer { private int port = 50051 ; private ServerImpl server; private void start() throws Exception { server = NettyServerBuilder.forPort(port) .addService(DemoServiceGrpc.bindService( new DemoServiceImpl())) .build().start(); server = InProcessServerBuilder.forName( "testServer" ) .addService(DemoServiceGrpc.bindService( new DemoServiceImpl())) .build().start(); System.out.println( "Server started, listening on " + port); Runtime.getRuntime().addShutdownHook( new Thread() { @Override public void run() { System.out.println( "*** shutting down gRPC server since JVM is shutting down" ); DemoServiceServer. this .stop(); System.out.println( "*** server shut down" ); } }); } private void stop() { if (server != null ) { server.shutdown(); } } public static void main(String[] args) throws Exception { final DemoServiceServer server = new DemoServiceServer(); server.start(); } } |
三、grpc-client
pom.xml内容:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <groupId>yjmyzz.grpc</groupId> 8 <artifactId>grpc-client</artifactId> 9 <version>1.0</version> 10 11 <dependencies> 12 13 <dependency> 14 <groupId>com.google.protobuf</groupId> 15 <artifactId>protobuf-java</artifactId> 16 <version>3.0.0-beta-1</version> 17 </dependency> 18 19 <dependency> 20 <groupId>yjmyzz.grpc</groupId> 21 <artifactId>grpc-contract</artifactId> 22 <version>1.0</version> 23 </dependency> 24 25 <dependency> 26 <groupId>io.grpc</groupId> 27 <artifactId>grpc-all</artifactId> 28 <version>0.8.0</version> 29 </dependency> 30 31 <dependency> 32 <groupId>junit</groupId> 33 <artifactId>junit</artifactId> 34 <version>4.10</version> 35 </dependency> 36 37 </dependencies> 38 39 </project>
Client端代码:
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
|
package yjmyzz.grpc.study.client; import io.grpc.ChannelImpl; import io.grpc.netty.NegotiationType; import io.grpc.netty.NettyChannelBuilder; import yjmyzz.grpc.study.dto.PersonList; import yjmyzz.grpc.study.dto.PingRequest; import yjmyzz.grpc.study.dto.PingResponse; import yjmyzz.grpc.study.dto.QueryParameter; import yjmyzz.grpc.study.service.DemoServiceGrpc; import java.util.concurrent.TimeUnit; public class DemoServiceClient { private final ChannelImpl channel; private final DemoServiceGrpc.DemoServiceBlockingStub blockingStub; public DemoServiceClient(String host, int port) { channel = NettyChannelBuilder.forAddress(host, port).negotiationType(NegotiationType.PLAINTEXT) .build(); blockingStub = DemoServiceGrpc.newBlockingStub(channel); } public void shutdown() throws InterruptedException { channel.shutdown().awaitTermination( 5 , TimeUnit.SECONDS); } public void ping(String name) { try { System.out.println( "Will try to ping " + name + " ..." ); PingRequest request = PingRequest.newBuilder().setIn(name).build(); PingResponse response = blockingStub.ping(request); System.out.println( "ping: " + response.getOut()); } catch (RuntimeException e) { System.out.println( "RPC failed:" + e.getMessage()); return ; } } public void getPersonList(QueryParameter parameter) { try { //System.out.println("Will try to getPersonList " + parameter + " ..."); PersonList response = blockingStub.getPersonList(parameter); //System.out.println("items count: " + response.getItemsCount()); // for (Person p : response.getItemsList()) { // System.out.println(p); // } } catch (RuntimeException e) { System.out.println( "RPC failed:" + e.getMessage()); return ; } } public static void main(String[] args) throws Exception { DemoServiceClient client = new DemoServiceClient( "localhost" , 50051 ); try { client.ping( "a" ); int max = 100000 ; Long start = System.currentTimeMillis(); for ( int i = 0 ; i < max; i++) { client.getPersonList(getParameter()); } Long end = System.currentTimeMillis(); Long elapse = end - start; int perform = Double.valueOf(max / (elapse / 1000d)).intValue(); System.out.print( "rgpc " + max + " 次NettyServer调用,耗时:" + elapse + "毫秒,平均" + perform + "次/秒" ); } finally { client.shutdown(); } } private static QueryParameter getParameter() { return QueryParameter.newBuilder().setAgeStart( 5 ).setAgeEnd( 50 ).build(); } } |
在笔记本测试的结果:
Will try to ping a ...
ping: pong => a
rgpc 100000 次NettyServer调用,耗时:36409毫秒,平均2746次/秒
基本上在每秒3k次的数量级,相对thrift(1w+)、avro(5k+)来讲,目前的差距还是很明显的,但是新事物成长总是需要时间,再给google一段时间,相信以后会让大家感到惊艳的。
在序列化方面,也做了一个跟之前thrift、avro类似的测试:
1
2
3
4
5
6
7
8
9
10
11
|
@Test public void test() throws InvalidProtocolBufferException { QueryParameter queryParameter = QueryParameter.newBuilder().setAgeStart( 1 ).setAgeEnd( 5 ).build(); byte [] bytes1 = queryParameter.toByteArray(); System.out.println( "Protobuf 3.0 二进制序列后的byte数组长度:" + bytes1.length); QueryParameter result = QueryParameter.parseFrom(bytes1); System.out.println(queryParameter.getAgeStart() + " - " + result.getAgeStart()); } |
输出:
Protobuf 3.0 二进制序列后的byte数组长度:4
1 - 1
在2进制序列化后的大小方面,protobuf 3大体跟thrift的TCompactProtocal(大小5)接近,比avro(大小2)略差。