开始食用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. Unity的UI究竟为什么可以合批

    1.UI/Default代码研究首先,我想到的是,既然是对图集纹理进行采样,而且又不能统一更改材质的纹理UV值,我们通常写的shader都是直接根据模型UV值对主纹理进行采样,那会不会是shader中 ...

  2. 递归一个List<T>,可自己根据需要改造为通用型。

    /// <summary> /// 递归一个List<ProvinceOrg> /// </summary> /// <returns></ret ...

  3. 模块使用:time、datetime、calendar、sys、os、os.path、normcase和normapath、random、json、pickle

    ## time:时间 时间戳(timestamp):time.time() 延迟线程的运行:time.sleep(secs) (指定时间戳下的)当前时区时间:time.localtime([secs] ...

  4. Mybatis-Plus入门示例

    1.内容: Mybatis-Plus只是在Mybatis的基础上,实现了功能增强,让开发更加简洁高效. Mybatis-Plus并没有修改Mybatis的任何特性. 2.入门示例: 2.1 需求:使用 ...

  5. Android之Retrofit详解(转载)

    说明:该文章转载于https://www.jianshu.com/p/a3e162261ab6 前言 在Andrroid开发中,网络请求十分常用 而在Android网络请求库中,Retrofit是当下 ...

  6. c/c++ 网络编程 陈硕老师视频理解之ttcp

    ttcp 是干啥的:测试2台机器间的网络传输性能 wiki 功能如下图: 对应的视频是: 4.回顾基础的Sockets API.mkv 5.TTCP代码概览.mkv 6.使用TTCP进行网络传输性能测 ...

  7. Visual Studio Code-批量添加或删除注释行

    小技巧一例,批量删除Visual Studio code或notepad++注解信息,便于读取有效代码或文本信息,具体操作如下: Visual Studio Code批量删除注解行信息: 在VS Co ...

  8. July 05th. 2018, Week 27th. Thursday

    Pleasure in the job puts perfection in the work. 乐于工作才能有完美表现. From Aristole. Do you want promotion? ...

  9. Git报错 bad numeric config value '100000' for 'pack.windowmemory': out of range

    Git报错 bad numeric config value '10240M' for 'pack.windowmemory': out of range $ git config --edit -- ...

  10. 页面结构化在 Android 上的尝试

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/M45DM5Ix7a2fmrsE8VPvxg 作者:b ...