开始食用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. 针对缓存在Redis中的聊天消息的持久化方案分析

    选型依据 数据库的选型主要考虑一下几个方面: 数据库本身是否收费 数据库后期维护成本 是否支持水平及垂直扩展,及扩展的容易程度 业务数据本身特性 使用此数据库的开发成本 由于此数据库主要用来存储缓存在 ...

  2. docker学习------registry可视化的实现

    1.Harbor的发现 之前一直在使用registry,但docker  pull下来的基础镜像并没用携带UI界面,所以查找了一波开源工具,发现了Harbor这一工具,下面对工具进行一个学习. 2.部 ...

  3. 在Ubuntu/CentOS/Debian系统下,使用CPU挖Monero (XMR)币

    CentOS7(增加源) yum repolist # 查看yum源列表 yum localinstall http://dl.fedoraproject.org/pub/epel/7/x86_64/ ...

  4. Python的re模块中search与match的区别

    1.search和match: search:在整个字符中匹配,如果找不到匹配的就返回None match:在字符串开始位置匹配如果不匹配就返回None 2.效率对比: search: match:

  5. 移动端,input、textarea滚动至可视区域

    1.一般情况下 在移动端,点击input框之后,会弹出输入键盘.而内容input的内容也会自动滚动到可视区域内. 2.当父元素设置了overflow属性之后 在设置了overflow属性之后,点击in ...

  6. 用mongo和redis查询排行榜、统计活跃用户

    nosql数据库能解决关系型数据库遇到的性能和扩展性的问题,本博客将以mongodb和redis两种nosql数据库为基础,简单的介绍下面两个业务场景的解决方案: 1.查询排行榜(以当日总步数排名为例 ...

  7. 题解 P4705 【玩游戏】

    这题是真的神仙啊...居然用的 stl 来卡常? 话说 998244353 真的可以一眼 NTT ? noteskey 所以说只要推柿子就好了但是有的地方的推导根本就想不到... 我们令第 t 个答案 ...

  8. 手把手详解持续集成之GitLab CI/CD

    一.环境准备 首先需要有一台 GitLab 服务器,然后需要有个项目:这里示例项目以 Spring Boot 项目为例,然后最好有一台专门用来 Build 的机器,实际生产中如果 Build 任务不频 ...

  9. 一丶HTML介绍

    import socket def main(): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(('local ...

  10. YARN配置

    环境搭建 mapred-site.xml <configuration> <property> <name>mapreduce.framework.name< ...