开始食用grpc(之一)

转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9501353.html

```

   记一次和一锅们压马路,路过一咖啡厅(某巴克),随口就问随行的锅门:你能从那咖啡厅看到什么?

当时的那家某巴克处于闹市,也正值周末,屋外屋内喝咖啡的人几近乎十分的安静,使用电脑的,刷手机的、做作业的。。。而且大都是年轻人和中年人。

   锅门撂了句:一群屌丝呗 (;¬_¬) 

  。。。白了他一眼(¬_¬)

( ...其实想教唆他进去看看美女,歇歇脚来着 ๑乛◡乛๑ )

  .......

许久之后,也就是最近看到诗人余秀华的一句话后忽有所感,原句是:

    “反正是背负慢慢凋残的孤独,耀眼的孤独,义无反顾的孤独

  这才明白他们是在消费孤独,也为孤独所消费; 他们是,周围的人是,还有 ( ∙̆ .̯ ∙̆ )

  那~ 孤独的结果是什么呢 ?

```

  这次讲下大系统通讯必备的一项组件:rpc,rpc有很多 如 dubbo、thirft、feign、motan、grpc 等~,这其中有字符串方式的也有二进制流方式的;整体来说二进制方式的一般会较字符串方式的快许多,字符形式的的慢,但是简单;而二进制方式的 序列化和跨平台较为麻烦些;我个人选取rpc时一般综合考虑一下几点:

  A>传输方式是否是二进制

  B>是否长期支持

  C>是否支持跨平台,开源组件是否丰富

  C+>是否支持异步调用

  D>有无明显的bug或缺点

  E>维护和开发是否有好

综合下来,个人坚定之选择grpc,这个初出茅庐(2015年发布)的东东,十分强大:

>> http2流方式

  >> 自带异步特性

  >> 跨平台(支持11种语言)

  >> 无需zookeeper这类单独的服务中心

  >> 对开发人员比较友好

>> 服务调用方可设置请求头

当然缺点也是存在的:需要单独写proto文件(生成目标语言的一套语法定义)、变量为null时会赋予默认初始值、链式调用(还好调用接口较为单一,只是语法较为怪异)...

  如果您在意以上缺点,可绕过本文哈~

ok,现在开始我开始讲grpc,内容大致有四:

    A->grpc的简单配置 (本节)

    A>简单grpc编写 (本节)

    B>复杂grpc proto服务文件编写 (本节)

    C>双向流式调用方法及注意事项 (下一节)

    D>grpc安全问题及拦截器 (下一节)

grpc的配置:

这里我的工程是基于springboot,同时为简化开发起见,我使用 grpc-spring-boot-starter ,开始之前先感谢这位开发者为简化grpc的java平台简化了太多的开发,同时也为springcloud融合做了太多共享,非常感谢~!

   这里,首先得准备三个springboot模块,这三个模块包含:grpc proto3文件生成模块、grpc 客户端、grpc 服务端,我的工程结构大致是这样子的(工程是多模块的):

这里面的三个模块一看就懂,就不细讲啦~,准备好这三个模块后,依次配置依赖包及参数:

服务端(preview-grpc-server):

  pom.xml中依赖包配置>

        <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-autoconfigure</artifactId>
<version>RELEASE</version>
</dependency>
    <!-- 由于我的工程是多模块的,若不作为jar包引入,也可以将preview-grpc-lib中的java文件拷贝到当前工程内也可 -->
     <dependency>
     <groupId>com.github.carvechris</groupId>
     <artifactId>preview-grpc-lib</artifactId>
     <version>1.0-SNAPSHOT</version>
     </dependency>
 

  配置文件yml(如果是properties文件也可参照此配置):

 grpc:
server:
port: 2804

spring:
application:
name: preview-grpc-server

(注意:一定要定义应用名称,在调用的时候会用到应用名称的,这里是:preview-grpc-server)

客户端(preview-grpc-client):

  pom.xml文件依赖包配置>

<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-client-spring-boot-starter</artifactId>
<version>1.4.0.RELEASE</version>
</dependency> <!-- 由于我的工程是多模块的,若不作为jar包引入,也可以将preview-grpc-lib中的java文件拷贝到当前工程内也可 -->
<dependency>
 <groupId>com.github.carvechris</groupId>
 <artifactId>preview-grpc-lib</artifactId>
 <version>1.0-SNAPSHOT</version>
</dependency>
 

  yml配置文件参数:

 grpc:
client:
preview-grpc-server:
host:
- 127.0.0.1
port:
- 2804
enableKeepAlive: true
keepAliveWithoutCalls: true

proto文件生成模块(preview-grpc-lib)配置:

  pom.xml文件依赖包配置:

 <!--依赖配置-->
<dependencies>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-netty</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-protobuf</artifactId>
<version>${grpc.version}</version>
</dependency>
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-stub</artifactId>
<version>${grpc.version}</version>
</dependency>
</dependencies> <!--proto3文件生成java代码插件配置-->
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>${os.plugin.version}</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>${protobuf.plugin.version}</version>
<configuration>
<protocArtifact>com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}</pluginArtifact>
</configuration>
</plugin>
</plugins>
</build>

  配置完成,这里需要特别说明一下:server模块和client模块的web服务各占一个端口,另外,server模块还会给grpc单独分配一个端口,就我的preview-grpc-server来说:

    服务名称(name)是:preview-grpc-server

    服务占用的端口是:

  切记,不论是web服务还是grpc服务的端口都不能重复,同时一定要理清楚web服务和grpc服务所占用的端口和ip。

简单grpc服务(helloworld.proto)编写

  这里我先展示下我的生成模块的大致样子:

  需要说明的是:编写的proto文件均在proto目录下,java目录下是proto文件生成的java代码,这里的java文件是从target目录总复制到java目录下的,包名一定要与proto里面声明的包名一致!

  java代码生成模块proto3服务文件(helloworld.proto)的编写:

 syntax = "proto3";

 // 是否拆分类文件
option java_multiple_files = true;
// 生成的文件所在的包
option java_package = "com.funnyzpc.xxx.grpc.lib.hello";
// 输出类主文件(此配置可选)
option java_outer_classname = "HelloWorldProto"; // 定义一个服务
service Simple {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {
}
} // 请求体定义
message HelloRequest {
string name = 1;
} // 响应体定义
message HelloReply {
string message = 1;
}

  现在开始使用idea提供的快捷功能生成客户端和服务端java文件(当然也可以使用mvn命令手动生成):

  将生成的java文件复制到应用目录:

  (注意:复制后一定要清理target目录,不然文件重复会报错!)

  在客户端(preview-grpc-client)编写一个grpc服务请求类(GrpcSimpleService.java):

 @Service
public class GrpcSimpleService { @GrpcClient("preview-grpc-server")
private Channel serverChannel; public String sendMessage(String name) {
SimpleGrpc.SimpleBlockingStub stub = SimpleGrpc.newBlockingStub(serverChannel);
HelloReply response = stub.sayHello(HelloRequest.newBuilder().setName(name).build());
return response.getMessage();
}
}

  在服务端(preview-grpc-server)编写对应的grpc的服务类:

 /**
* 简单grpc服务类
*/
@GrpcService(SimpleGrpc.class)
public class GrpcServerService extends SimpleGrpc.SimpleImplBase {
private static final Logger LOG=LoggerFactory.getLogger(GrpcServerService.class); @Override
public void sayHello(HelloRequest req, StreamObserver<HelloReply> responseObserver) {
HelloReply reply = HelloReply.newBuilder().setMessage("Hello ===> " + req.getName()).build();
responseObserver.onNext(reply);
responseObserver.onCompleted();
} }

  上面的@GrpcService是grpc组件的注解,注解中必须声明所使用的grpc(生成的类中的)服务类,同时还可以声明所使用的拦截器(可选) 

 OK,现在添加一个控制器(在preview-grpc-client中编写一个控制器),试试看:

  完美,。。。可能有人proto文件一知半解,接下来进入下一节。

复杂grpc proto服务文件编写:

   首先,我先推荐两个官方网站,若能理解官网内容,可绕过本节

     grpc java平台api及样例>

      https://grpc.io/docs/quickstart/java.html

protocol buffers,proto3文件语法>

      https://developers.google.com/protocol-buffers/docs/proto3?hl=zh-cn

  一般在跨应用调用时,所传递的参数有时候为复杂对象,比如这样{page_no:1,page_size:20,data:{name:XXX,type:2}},这里就写好的复杂层级对象讲解下:

(MultiObject.proto)

 syntax = "proto3";

 // option java_multiple_files = true;
option java_package = "com.github.carvechris.security.grpc.lib.multi"; service MultiObjService{
rpc queryObj (MultiObjReq) returns (MultiObjResp) { } } message MultiObjReq{
int32 page_no=1;
int32 page_size=2;
MultiObjDataReq data=3;
} message MultiObjDataReq{
string name=1;
int32 type=2;
} message MultiObjResp{
string req_str=1;
MultiObjFirstResp first=2;
}
message MultiObjFirstResp{
string f_content=1;
MultiObjNextResp next=2; }
message MultiObjNextResp{
string n_content =1;
}

文件中定义一个服务 必须以关键字 service 开始,上面的 MultiObjReq 与 MultiObjResp 分别为服务的请求和响应对象,这两个对象在生成java文件后,每个请求对象都是一个单独的类(可在一个java中也可不在,若不在需要定义:option java_multiple_files = true;),不管是请求对象还是响应对象,都需要单独声明这个对象以及对象中的变量类型及所处的位置,就像这样:

 message MultiObjReq{
int32 page_no=1;
int32 page_size=2;
MultiObjDataReq data=3;
}

自定义类型需要在单独定义,比如"MultiObjDataReq";在上面这个例子中,定义的请求对象MultiObjReq的第一个字段为 page_no,第一个为page_size,第三个为定义的一个对象,每个参数开始需标明当前字段类型,如果这个字段是自定义类型时无需定义数据类型(但是一定要有参数序号,当然也可以定义一个map类型);另外,通用字段类型同go语言的数据类型(参照以上链接);注意,请求或响应对象定义时必须以关键字message开始。

另外,请注意,如果某个字段是个列表(java中的List),需要在字段或者对象前添加关键字 repeated ,这样:

//返回体数据定义
message GrpcResp { string sign=3; string msg=4; repeated datas detail=5;
} message datas{ uint64 id=1; string address=2; double isadmin=3;
}

  现在展示上面的MultiObject.proto文件 所编写的客户端和服务端类:

  客户端(preview-grpc-client):

  (GrpcMultiObjClientService.java)

 @Service
public class GrpcMultiObjClientService { @GrpcClient("preview-grpc-service")
private Channel serverChannel; public Object testMultiObj() { MultiObject.MultiObjDataReq reqData=MultiObject.MultiObjDataReq.newBuilder()
.setName("queryName")
.setType(33)
.build(); MultiObject.MultiObjReq req=MultiObject.MultiObjReq.newBuilder()
.setPageNo(11)
.setPageSize(22)
.setData(reqData)
.build(); MultiObjServiceGrpc.MultiObjServiceBlockingStub stb=MultiObjServiceGrpc.newBlockingStub(serverChannel);
MultiObject.MultiObjResp resp=stb.queryObj(req); Map<String,Object> reMap=new HashMap<>();
reMap.put("getFContent",resp.getFirst().getFContent());
reMap.put("getNContent",resp.getFirst().getNext().getNContent());
reMap.put("getReqStr",resp.getReqStr());
return reMap;
} }

  服务端(preview-grpc-server):

  (GrpcMultiObjService.java)

 @GrpcService(MultiObjServiceGrpc.class)
public class GrpcMultiObjService extends MultiObjServiceGrpc.MultiObjServiceImplBase{
private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiObjService.class); @Override
public void queryObj(MultiObject.MultiObjReq request,StreamObserver<MultiObject.MultiObjResp> responseObserver) { LOG.info("MultiObjServiceGrpc>start");
Map<String,Object> reqData=new HashMap<String,Object>();
reqData.put("getPageNo",request.getPageNo());
reqData.put("getPageSize",request.getPageSize());
reqData.put("getName",request.getData().getName());
reqData.put("getType",request.getData().getType()); MultiObject.MultiObjNextResp next=MultiObject.MultiObjNextResp.newBuilder().setNContent("n_content").build();
MultiObject.MultiObjFirstResp first=MultiObject.MultiObjFirstResp.newBuilder().setFContent("f_content").setNext(next).build();
MultiObject.MultiObjResp resp=MultiObject.MultiObjResp.newBuilder().setReqStr(JSON.toJSONString(reqData)).setFirst(first).build(); responseObserver.onNext(resp);
responseObserver.onCompleted();
LOG.info("MultiObjServiceGrpc>end");
} }

现在是 2018-08-26 02:13:42

  由于在双向流编写及测试环节碰到些问题,耽搁了许久,此次会将双向流和安全及拦截器放在下一篇讲,各位,晚安 (=。=)  

开始食用grpc(之一)的更多相关文章

  1. 开始食用grpc(之二)

    开始食用grpc(之二) 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9570992.html ``` 前段时间有童鞋找我开专栏.搬家.甚至还有人找我写书的. ...

  2. GRPC单向/双向流

    开始食用grpc(之二)https://www.cnblogs.com/funnyzpc/p/9570992.html 开始食用grpc(之一)https://www.cnblogs.com/funn ...

  3. gRPC源码分析1-SSL/TLS

    引子 前几天看到微信后台团队分享了TLS相关文章,正好gRPC里TLS数据加密是很重要的一块,于是整理出了这篇文章. 在gRPC里,如果仅仅是用来做后端微服务,可以考虑不加密.本文太长,先给个大纲. ...

  4. gRPC源码分析2-Server的建立

    gRPC中,Server.Client共享的Class不是很多,所以我们可以单独的分别讲解Server和Client的源码. 通过第一篇,我们知道对于gRPC来说,建立Server是非常简单的,还记得 ...

  5. gRPC源码分析0-导读

    gRPC是Google开源的新一代RPC框架,官网是http://www.grpc.io.正式发布于2016年8月,技术栈非常的新,基于HTTP/2,netty4.1,proto3.虽然目前在工程化方 ...

  6. 谷歌发布的首款基于HTTP/2和protobuf的RPC框架:GRPC

    Google 刚刚开源了grpc,  一个基于HTTP2 和 Protobuf 的高性能.开源.通用的RPC框架.Protobuf 本身虽然提供了RPC  的定义语法,但是一直以来,Google 只开 ...

  7. gRPC .NET Core跨平台学习

    前些天发布gRPC C# 学习,在.NET Framework 中使用gRPC ,今天来学习 .NET Core gRPC. gRPC 的.NET Core 包在NuGet 上发布了,结合.NET C ...

  8. gRPC C#学习

    前些天gRPC 发布1.0 版本,代表着gRPC 已经正式进入稳定阶段. 今天我们就来学习gRPC C# .而且目前也已经支持.NET Core 可以实现完美跨平台. 传统的.NET 可以通过Mono ...

  9. .net core 用grpc实现微服务

    GRPC 是Google发布的一个开源.高性能.通用RPC(Remote Procedure Call)框架.提供跨语言.跨平台支持.以下以.NET Core 使用控制台.docker中演示如何使用G ...

随机推荐

  1. JS代码风格自动规整工具Prettier

    问题背景 通常使用 ESLint做代码风格检查检查, 和部分代码质量检查. 但是使用ESLint在入库时候, 会产生很多的代码修正工作, 需要开发者一个一个的修改. 如果很多,并且时间紧迫,甚是尴尬. ...

  2. 【转】关于Tomcat下项目线程启动两次的问题

    最近遇见了一个很搞得事情,在tomcat下启动项目时自己写的定时程序被执行了两次,导致程序启动了两个线程,使定时任务在几秒间隔内执行了两次,后来通过日志查到,原来是tomcat将项目启动了两次,为什么 ...

  3. 【bzoj 4449】[Neerc2015]Distance on Triangulation

    Description 给定一个凸n边形,以及它的三角剖分.再给定q个询问,每个询问是一对凸多边行上的顶点(a,b),问点a最少经过多少条边(可以是多边形上的边,也可以是剖分上的边)可以到达点b. I ...

  4. mysql—增删改查语句总结

    关于MySQL数据库——增删改查语句集锦 一.基本的sql语句 CRUD操作: create 创建(添加) read 读取 update 修改 delete 删除 .添加数据 ,'n001','201 ...

  5. git错误记录及解决

    一.git每次提交.拉取都要输用户名和密码 问题描述:每次提交.拉取文件时都要输用户名和密码,特别麻烦 原因:在git上面注册了用户名a,然后本机安装了TortoiseGit工具,登录时会在本机C:\ ...

  6. SqlServer如何获取存储过程的返回值

    1.Output参数返回值 1 CREATE PROCEDURE [dbo].[upInformation]( 2 @age int , 3 @id bigint OUTPUT 4 ) 5 AS 6 ...

  7. Python3 指定文件夹下所有文件(包括子目录下的文件)拷贝到目标文件夹下

    #!/usr/bin/env python3 # -*- coding:utf8 -*- # @TIME :2018/9/17 9:02 # @Author:dazhan # @File :copyf ...

  8. 415 DOM 查找列表框、下拉菜单控件、对表格元素/表单控件进行增删改操作、创建元素并且复制节点与删除、 对表格操作、通用性和标准的事件监听方法(点击后弹窗效果以及去掉效果)

    DOM访问列表框.下拉菜单的常用属性: form.length.options.selectedindex.type       使用options[index]返回具体选项所对应的常用属性:defa ...

  9. POI操作excle

    将根目录下的poi-3.6-20091214.jar和Lib目录下三个通用包 commons-logging-1.1.jar junit-3.8.1.jar log4j-1.2.13.jar拷贝到项目 ...

  10. 软件测试面试必问--bug交互流程

    目前市场主要用的bug管理工具:禅道.jira.QC.bugfree等,当然也有自己公司开发的. 不过不管哪一种工具,核心交互流程都是差不多的,只是字段的名称不一样而已,参考如下两张示意图: 这是前几 ...