开始食用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(之二)的更多相关文章

  1. 开始食用grpc(之一)

    开始食用grpc(之一) 转载请注明出处:https://www.cnblogs.com/funnyzpc/p/9501353.html ```   记一次和一锅们压马路,路过一咖啡厅(某巴克),随口 ...

  2. Go GRPC 入门(二)

    前言 最近较忙,其实准备一篇搞定的 中途有事,只能隔了一天再写 正文 pb.go 需要注意的是,在本个 demo 中,客户端与服务端都是 Golang,所以在客户端与服务端都公用一个 pb.go 模板 ...

  3. grpc(二)记一次grpc debug--io.grpc.StatusRuntimeException: UNKNOWN

    1.起初是dingding一直报错: instance:服务器名 err:GrpcClient#placeOrder: io.grpc.StatusRuntimeException: UNKNOWN ...

  4. go语言gRPC系列(二) - 为gRPC添加证书

    1. 前言 2. 生成自签证书 2.1 MAC生成自签证书的教程链接: 2.2 Windows生成自签证书的教程 3. 改造服务端使用自签证书 3.1 复制证书至代码下 3.2 改造代码添加证书认证 ...

  5. .Net Core gRPC 实战(二)

    概述 gRPC 客户端必须使用与服务相同的连接级别安全性.  如调用服务时通道和服务的连接级别安全性不一致,gRPC 客户端就会抛出错误. gRPC 配置使用HTTP gRPC 客户端传输层安全性 ( ...

  6. GRPC单向/双向流

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

  7. grpc入门(三)

    grpc入门(三) 一.介绍 本文是关于grpc的第三篇博文,是对前两篇博文的具体代码实现,秉着个人一贯的风格,没有太多抒情和总结,直接就上代码. 文章代码参考:https://github.com/ ...

  8. 【美食技术】家庭自制DIY鸡蛋饼和疙瘩汤早餐视频教程

    鸡蛋饼制作方法 食材准备面粉 150g鸡蛋饼  鸡蛋饼鸡蛋 2个盐 适量水 适量(约300ml)油 20g荵花适量也可根据自己喜好准备一些调味料. 做法 鸡蛋饼是一种家常点心,做法很多,这里提供3种. ...

  9. 初识google多语言通信框架gRPC系列(二)编译gRPC

    目录 一.概述 二.编译gRPC 三.C#中使用gRPC 四.C++中使用gRPC 无论通过哪种语言调用gRPC,都必须要编译gRPC,因为生成proto访问类时,除了产生标准的数据定义类之外,还需要 ...

随机推荐

  1. C#简单继承示例详解——快速入门

    在面向对象当中继承是非常重要的,也是面向对象的三大特性之一(继承.封装.多态),今天我们来揭开他的神秘面纱. 话不多说,我们上菜. using System; using System.Collect ...

  2. 文件进行MD5计算

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  3. QT文件操作

    /* 写文件 */ QFile file(filePath); bool bo = file.open(QIODevice::WriteOnly | QIODevice::Append); //qDe ...

  4. SQL内模糊查询语句拼接时单引号'问题

    下面以存储过程查询所有为例,非存储过程(或不是查询所有将*替换为你想要查询的列即可)更为简单, 语法:select * from 表名 where 列名like'%条件%' 拼接后的set @变量名 ...

  5. 个人整理的 Windows 下 .NET 开发必装的软件

    注: 最后更新时间:2019-03-15 一..NET 开发 1. 必装 软件名称 说明 下载地址 JetBrains Toolbox JetBrins 全家桶管理工具. 下载地址 JetBrains ...

  6. Linux 桌面玩家指南:01. 玩转 Linux 系统的方法论

    特别说明:要在我的随笔后写评论的小伙伴们请注意了,我的博客开启了 MathJax 数学公式支持,MathJax 使用$标记数学公式的开始和结束.如果某条评论中出现了两个$,MathJax 会将两个$之 ...

  7. Oracle AWRSQRPT报告生成和性能分析

    我写的SQL调优专栏:https://blog.csdn.net/u014427391/article/category/8679315 对于局部的,比如某个页面列表sql,我们可以使用Oracle的 ...

  8. Java基础——集合(持续更新中)

    集合框架 Java.util.Collection Collection接口中的共性功能 1,添加 booblean add(Object obj);  往该集合中添加元素,一次添加一个 boolea ...

  9. python --- 插入排序算法

    先上一张图,看看能不能从里面悟出些什么: 问题的解决思路: 就是当插入第i个的时候,前面的[i- 1]个已经排好了,这时候lst[i]就倒过来逐个和前面的关键字顺序进行比较,找到插入位置即将lst[i ...

  10. kafka的设计

    1.动机 设计 kafka 初衷,作为统一平台处理大公司的实时数据.所以 必须具有如下特性: 支持海量数据 高吞吐量 低延迟(实时性) 支持分区,分布式 容错 2.持久化 kafka 高度依赖 文件系 ...