开始食用grpc(之二)
开始食用grpc(之二)
转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9570992.html
```
前段时间有童鞋找我开专栏、搬家、甚至还有人找我写书的。。。这其中有大平台 疼讯、阿里...,也有小平台 :第八基地、云聚、西部数码...,在此再次感谢各位赏识,吾文采拙劣,技术水平较次,实在没时间写书,也没时间给各位解答不熟悉的技术问题...;同时邀请我开专栏、搬家的平台也请不要重复邀请呢。
额,同时对于转载的童鞋,需要说明的是:我的博客是免费公益性质的,若能受到点儿启发或者有些许的帮助就是对比人最大的肯定,基于此,请各位转载的童鞋 能 原文转载(对原作者极大的尊重),若是平台转载请在转载的后的博客页面中切勿投放过多的广告,在此我强调过我的博客是免费性质的,若是拿来做付费或是赚取广告费性质的请和我联系(可以容许少许的广告,广告也不可遮盖博客内容),在此请各位谅解哈~,同时感谢深耕在开源一线的童鞋,是你们改善了一线开发人员的处境,也让整个行业变得快速高效,致敬!
```
此次我就接着上次的话茬把我所了解的grpc将完吧,grpc这两节的内容大致如下:
A->grpc的简单配置 (上一节)
A>简单grpc编写 (上一节)
B>复杂grpc proto服务文件编写 (上一节)
C>双向流式调用方法及注意事项 (本节)
D>grpc安全问题及拦截器 (本节)
这次我是这么安排的,先列举一个双向流的编写过程,然后在讲讲这里面的坑,然后再浅谈一下grpc安全问题,同时编写一个简单的grpc拦截器,若基本配置不是很清楚请仔细阅读 https://www.cnblogs.com/funnyzpc/p/9501353.html
双向流式调用方法及注意事项:
由于双向流的使用方式不用于上期所讲的,这里我从编写一步步讲。
先在preview-grpc-lib工程先的proto文件夹下编写一个包含双向流的是proto文件以生成客户端和服务器相关代码(记得把生成的代码放入工程内)。
(MultiStream.proto)
syntax = "proto3"; option java_multiple_files = true;
option java_package = "com.funnyzpc.XXX.grpc.lib.multiStream";
//定义一个服务
service MultiStreamService{
rpc queryStream (stream MultiStreamReq) returns (stream MultiStreamResp) { } }
//定义一个请求体(用于传参)
message MultiStreamReq{
int32 page_no=;
int32 page_size=;
MultiStreamDataReq data=;
} message MultiStreamDataReq{
string name=;
bool type=;
}
//定义一个响应体(用于回参)
message MultiStreamResp{
string req_str=;
MultiStreamFirstResp first=;
}
message MultiStreamFirstResp{
string f_content=;
int64 idx=; }
这里可能需要对比着上一节所讲的复杂proto文件编写的内容,可以看到:请求体和响应体的定义大致都是一样的,只是在服务定义的时候会有一些些差别>请求体和响应体的前面多了一个关键字"stream” ,就是(请求或响应)只要一方是以流的方式发送就需要声明为 “stream" 。
编写个客户端服务代码:
@Service
public class GrpcMultiStreamClientService {
private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiStreamClientService.class); @GrpcClient("preview-grpc-server")
private Channel rpcChannel; /**
* grpc>双向流方式
* @return
*/
public Object queryByStream()throws Exception{
Map<String,Object> resp=new HashMap<>(); StreamObserver<MultiStreamResp> req= new StreamObserver<MultiStreamResp>() {
@Override
public void onNext(MultiStreamResp multiStreamResp) {
resp.put("req_str",multiStreamResp.getReqStr());
resp.put("f_content",multiStreamResp.getFirst().getFContent());
resp.put("idx",multiStreamResp.getFirst().getIdx());
LOG.info("onNext()");
//return resp;
} @Override
public void onError(Throwable throwable) {
LOG.info("onError()");
} @Override
public void onCompleted() {
LOG.info("onCompleted()");
}
}; MultiStreamServiceGrpc.MultiStreamServiceStub stud=MultiStreamServiceGrpc.newStub(rpcChannel);
StreamObserver<MultiStreamReq> reqStream=stud.queryStream(req); MultiStreamDataReq streamDataReq=MultiStreamDataReq.newBuilder()
.setName("req>name field")
.setType(false)
.build();
MultiStreamReq streamReq= MultiStreamReq.newBuilder()
.setPageNo(1)
.setPageSize(20)
.setData(streamDataReq).build(); reqStream.onNext(streamReq);
reqStream.onCompleted();
return resp;
}
}
可以在上图看到,请求方法内首先是要放入一个构造的内部请求方法,请求体也需要放入到StreamObserver这个对象中,这是与之前编写的grpc客户端(阻塞)所不一样的地方,同时构造stub的时候是newStub而不是newBlockingStub ,当然这两者是有区别的,前者仅适用于http2二进制流的方式 并且是一个异步的(这是重点),而后者是仅适用于http1.1的字符明文方式 并且是阻塞方式(这也是重点),后面我会说说这两者的具体使用区别。
接下来写一个grpc服务端服务类,这是代码:
@GrpcService(value= MultiStreamServiceGrpc.class)
public class GrpcMultiStreamService extends MultiStreamServiceGrpc.MultiStreamServiceImplBase{
private static final Logger LOG= LoggerFactory.getLogger(GrpcMultiStreamService.class); @Override
public StreamObserver<MultiStreamReq> queryStream(StreamObserver<MultiStreamResp> resp) {
return new StreamObserver<MultiStreamReq>() {
@Override
public void onNext(MultiStreamReq multiStreamReq) {
MultiStreamFirstResp firstResp=MultiStreamFirstResp.newBuilder()
.setFContent("f_content")
.setIdx(99).build();
MultiStreamResp req=MultiStreamResp.newBuilder()
.setReqStr("req_str")
.setFirst(firstResp).build();
resp.onNext(req);
resp.onCompleted();
} @Override
public void onError(Throwable throwable) {
LOG.info("onError()");
} @Override
public void onCompleted() {
LOG.info("onCompleted()");
}
};
}
整体的构造方法和逻辑代码和客户端代码相似,同时服务端的逻辑代码基本上全在StreamObserver这个异步对象中处理,同时这个构造方法也提供了错误和完成所对的重载方法,要进行业务处理也必须在重载的onNext方法中编写。
主题的服务已经编写完成,现在添加一个控制器来看看这个服务有没问题。
@Autowired
private GrpcMultiStreamClientService multiStreamClientService; @RequestMapping("/grpc4")
public Object grpc4()throws Exception{
return multiStreamClientService.queryByStream();
}
可能你会咦的一声说:请求是成功的,但为什么取到的服务端的参数是空呢?
其实这个很好理解,因为客户端的请求服务方式是流,此种方式下响应当然是异步的,这里方便调试,需要添加线程阻塞,才可能获取到服务端的响应参数(下图中红色部分)>
@Service
public class GrpcMultiStreamClientService {
private static final Logger LOG=LoggerFactory.getLogger(GrpcMultiStreamClientService.class); @GrpcClient("preview-grpc-server")
private Channel rpcChannel; /**
* grpc>双向流方式
* @return
*/
public Object queryByStream()throws Exception{
Map<String,Object> resp=new HashMap<>(); StreamObserver<MultiStreamResp> req= new StreamObserver<MultiStreamResp>() {
@Override
public void onNext(MultiStreamResp multiStreamResp) {
resp.put("req_str",multiStreamResp.getReqStr());
resp.put("f_content",multiStreamResp.getFirst().getFContent());
resp.put("idx",multiStreamResp.getFirst().getIdx());
LOG.info("onNext()");
//return resp;
} @Override
public void onError(Throwable throwable) {
LOG.info("onError()");
} @Override
public void onCompleted() {
LOG.info("onCompleted()");
}
}; MultiStreamServiceGrpc.MultiStreamServiceStub stud=MultiStreamServiceGrpc.newStub(rpcChannel);
StreamObserver<MultiStreamReq> reqStream=stud.queryStream(req); MultiStreamDataReq streamDataReq=MultiStreamDataReq.newBuilder()
.setName("req>name field")
.setType(false)
.build();
MultiStreamReq streamReq= MultiStreamReq.newBuilder()
.setPageNo(1)
.setPageSize(20)
.setData(streamDataReq).build(); reqStream.onNext(streamReq);
reqStream.onCompleted();
Thread.sleep(10000);
return resp;
}
}
可以看到线程睡眠了10秒,如果打断点可以看到 睡眠的过程中会响应客户端中的onNext方法,再就是把参数放入到resp中,当然客户端服务为流的方式下一般不做线程睡眠的操作,因为服务器有可能超时,如果超时那可就麻烦了。所以说grpc异步是有极好的应用场景,比如业务费阻塞,日志处理等等,同时如果需要直接响应请使用阻塞的方式(上面已经说过了),好了,这个时候,我们看看结果>
ok,可以顺利的看到服务器的响应结果了。
grpc安全问题及拦截器:
对于grpc安全问题,grpc只在服务端提供了 服务端证书验证 的方式,具体就是在在客户端请求的时候验证客户地址是否是有效而已,默认不使用的时候服务端证书的开关是关闭着的,这个验证其实也很简陋,具体的可以看看源码便知:
如若开发的系统要保证极高的安全度,建议使用这两类方式:
A>将客户端应用和服务端应用放置在同一个内往下,服务端关闭外网直接访问
B>可以在服务端添加拦截器,使用token的方式来验证客户端身份是否合法(这种方式可能需要客户端设置请求头)
对于以上两种安全访问方式,也可以以混合的方式使用,对于以上后者,我简单的列举下如何使用拦截器,就一个简单的例子呵~
首先填写一个服务端拦截器>
public class GrpcInterceptor implements ServerInterceptor {
private static final Logger LOG=LoggerFactory.getLogger(GrpcInterceptor.class); @Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
LOG.info(call.getAttributes().toString());
String inetSocketString = call.getAttributes()
.get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR).toString();
LOG.info(inetSocketString);
return next.startCall(call,headers);
}
}
如上,拦截器实现于grpc 的 ServerInterceptor 来编写的,如果需要做拦截处理 可以直接在interceptCall方法中编写相应的逻辑。
然后需要在服务端服务类的注解中声明所使用的拦截器>
@GrpcService(value= MultiStreamServiceGrpc.class,interceptors = GrpcInterceptor.class)
public class GrpcMultiStreamService extends MultiStreamServiceGrpc.MultiStreamServiceImplBase{
//此处略
}
拦截器声明可以见以上代码红色部分,以上代码的具体逻辑部分与以上GrpcMultiStreamService内容相同,同时顺带说下上面注解中的value变量,这个变量只是声明当前服务端服务类所使用的grpc的服务类是什么,当然可以填写其他的grpc的服务类(一定是proto文件生成的类才可以),并且不能为空!,同时这里就不给测试结果囖,读者打个断点就知道了。
现在是: 2018-09-01 19:39:12
各位晚安~
开始食用grpc(之二)的更多相关文章
- 开始食用grpc(之一)
开始食用grpc(之一) 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9501353.html ``` 记一次和一锅们压马路,路过一咖啡厅(某巴克),随口 ...
- Go GRPC 入门(二)
前言 最近较忙,其实准备一篇搞定的 中途有事,只能隔了一天再写 正文 pb.go 需要注意的是,在本个 demo 中,客户端与服务端都是 Golang,所以在客户端与服务端都公用一个 pb.go 模板 ...
- grpc(二)记一次grpc debug--io.grpc.StatusRuntimeException: UNKNOWN
1.起初是dingding一直报错: instance:服务器名 err:GrpcClient#placeOrder: io.grpc.StatusRuntimeException: UNKNOWN ...
- go语言gRPC系列(二) - 为gRPC添加证书
1. 前言 2. 生成自签证书 2.1 MAC生成自签证书的教程链接: 2.2 Windows生成自签证书的教程 3. 改造服务端使用自签证书 3.1 复制证书至代码下 3.2 改造代码添加证书认证 ...
- .Net Core gRPC 实战(二)
概述 gRPC 客户端必须使用与服务相同的连接级别安全性. 如调用服务时通道和服务的连接级别安全性不一致,gRPC 客户端就会抛出错误. gRPC 配置使用HTTP gRPC 客户端传输层安全性 ( ...
- GRPC单向/双向流
开始食用grpc(之二)https://www.cnblogs.com/funnyzpc/p/9570992.html 开始食用grpc(之一)https://www.cnblogs.com/funn ...
- grpc入门(三)
grpc入门(三) 一.介绍 本文是关于grpc的第三篇博文,是对前两篇博文的具体代码实现,秉着个人一贯的风格,没有太多抒情和总结,直接就上代码. 文章代码参考:https://github.com/ ...
- 【美食技术】家庭自制DIY鸡蛋饼和疙瘩汤早餐视频教程
鸡蛋饼制作方法 食材准备面粉 150g鸡蛋饼 鸡蛋饼鸡蛋 2个盐 适量水 适量(约300ml)油 20g荵花适量也可根据自己喜好准备一些调味料. 做法 鸡蛋饼是一种家常点心,做法很多,这里提供3种. ...
- 初识google多语言通信框架gRPC系列(二)编译gRPC
目录 一.概述 二.编译gRPC 三.C#中使用gRPC 四.C++中使用gRPC 无论通过哪种语言调用gRPC,都必须要编译gRPC,因为生成proto访问类时,除了产生标准的数据定义类之外,还需要 ...
随机推荐
- [PHP] debug_backtrace()可以获取到代码的调用路径追踪
查看代码的时候,看到有使用这个函数,测试一下 1.debug_backtrace()可以获取到代码的调用追踪,以数组形式返回 2.debug_print_backtrace() — 打印一条回溯,直接 ...
- 将个人博客从GitHub迁移至阿里云服务器过程总结
让我们先回顾下前两篇博客: 程序员如何从0到1搭建自己的技术博客 在个人博客中优雅的使用Gitalk评论插件 通过前两篇博客,我们了解了如何快速的从0到1搭建一个个人博客并使用了Gitalk评论插件, ...
- crontab常用
--crontab检查是否安装[oracle@rac1 ~]$ rpm -qa | grep crontabcrontabs-1.10-8启动与关闭[oracle@rac1 ~]$ /etc/init ...
- LNMP shell
#!/bin/bash #set -x #date: 2018-12-13 #Description: 一键安装LNMP环境 or LAMP 环境 #Version: 0.4 #Author: sim ...
- echarts在tab切换时容器宽度设置为100%,只展示100px
在 mychart.setOption(option); 后面加上 mychart.resize(); 即可
- F#正则表达式
此词法分析器允许您使用F#计算表达式以非常声明的方式定义基于正则表达式的规则. F# 打开 Lexer 让 定义= lexerDefinitions { 做!addNextlineDefinition ...
- 打开ubantu报错(invalid environment block. Press any key to continue)
今天向往常一样打开ubantu ,却无法正常打开,如下图 意思是无效的环境模块,随意按键继续,按任意键后如下图 折腾了许久问题也没有得到解决,后来在某篇博客中找到了答案 https://blog.cs ...
- Fastadmin安装以及各种问题解决
FastAdmin是一款基于ThinkPHP5+Bootstrap的极速后台开发框架 https://www.fastadmin.net/ 参照官方文档安装,还是有坑的 首先注意:无需下载PHP.Th ...
- [Swift-2019力扣杯春季决赛]2. 按字典序排列最小的等效字符串
给出长度相同的两个字符串:A 和 B,其中 A[i] 和 B[i] 是一组等价字符.举个例子,如果 A = "abc" 且 B = "cde",那么就有 'a' ...
- Kubernetes 笔记 10 Job 机器人加工厂
本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...