关于grpc:gRPCGateWay-Swagger-实战

上一次咱们分享了对于 gRPC-Gateway 疾速实战 ,能够查看地址来进行回顾 : 也能够查看对于 gRPC 的历史文章: gRPC介绍<!----> gRPC 客户端调用服务端须要连接池吗?<!----> gRPC的拦截器<!----> gRPC的认证<!----> 分享一下 gRPC- HTTP网关 I明天次要是分享对于 gRPC-Gateway Swagger 的实战局部,文章大体分为如下几个局部: 根本环境补充<!----> gRPC-GateWay Swagger 实战根本环境补充首先,咱们来看一下环境搭建结束和实现实战之后的目录构造 通过目录构造咱们能够看到: my_grpcgateway 目录下多了 pkg 目录这个目录是通过工具生成的,次要是解决 swagger 的 go 文件 my_grpcgateway/protoc/order/order.swagger.json工具生成的 swagger.json 文件,外面记录了对于 swagger 的接口 和 配置相干信息 my_grpcgateway/protoc/order/protoc-gen-swaggerprotoc-gen-swagger 工具目录 my_grpcgateway/third_partyswagger 的资源目录 那么咱们就动起手来,一个一个的将上述的内容填充起来吧: 装置 Protoc Plugingo get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger执行结束之后,咱们能够在咱们的 GOPATH 目录下找到 protoc-gen-swagger 目录 此时,咱们将 protoc-gen-swagger 目录拷贝到咱们的 my_grpcgateway/order/protoc 下 cd my_grpcgateway/order/protoc cp /root/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.16.0/protoc-gen-swagger ./ -rf下载 swagger-ui 的动态资源进入地址 :https://github.com/swagger-api/swagger-ui 将我的项目代码 download 下来 ...

September 1, 2023 · 2 min · jiezi

关于grpc:gRPCGateway-快速实战

明天来分享一波 gRPC-Gateway , 之前咱们有分享过什么是 gRPC 及其应用形式,能够看看这些对于 gRPC 的历史文章: gRPC介绍<!----> gRPC 客户端调用服务端须要连接池吗?<!----> gRPC的拦截器<!----> gRPC的认证<!----> 分享一下 gRPC- HTTP网关 I明天次要是分享对于 gRPC-Gateway 的实战局部,文章大体分为如下几个局部: gRPC-GateWay 简略原理介绍<!----> 根本环境创立<!----> gRPC-GateWay 实战gRPC-GateWay 简略原理介绍如下是官网的一张基本原理图 gRPC-Gateway 是一个协定插件,它读取一个 gRPC 服务定义,并生成一个反向代理服务,它将一个 RESTful JSON API 转换为 gRPC ,这个服务器是依据gRPC定义中的自定义选项生成的。 简略来说,咱们的 gRPC-Gateway 能做到的事件就是: 可能提供 RESTful JSON API 接口,并且申请通过 http 接口打进来,gRPC-Gateway 可能将其申请转给 grpc 服务,最终 grpc 服务进行解决并响应 接下来,咱们就来看看 gRPC-Gateway 是如何同时提供 http 接口和 grpc 服务的 根本环境创立应用 gRPC-Gateway ,咱们须要搭建根本的环境,正如之前咱们实战 grpc 的时候,须要搭建一个根本的 grpc 环境,总的来说须要做如下事项: 开始创立本人的我的项目目录mkdir my_grpcgatewaycd my_grpcgatewaymdkir protoc/ordercd protoc/order下载 gRPC-Gateway 的依赖$ go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway$ go get google.golang.org/protobuf/cmd/protoc-gen-go$ go get google.golang.org/grpc/cmd/protoc-gen-go-grpc查看工具,执行完第二步之后,咱们能够看到咱们将 gRPC-Gateway 的依赖库和工具都下载下来了,咱们能够看到在咱们的 GOPATH 目录下有如下工具 ...

August 31, 2023 · 2 min · jiezi

关于grpc:工作中你会使用到-grpcurl-吗

在平时的开发过程中,咱们个别是 http 接口对外, grpc 接口对外部微服务 置信对于如何去申请 http 接口,大家都很相熟了 如果是 inux 外面应用 curl 命令<!----> 在 windows 外面咱们能够应用 postman 来申请接口<!----> 如果对于一个云上开发的接口的话,咱们可能会应用 apifox 来进行申请那么对于 grpc 服务端提供的接口,咱们个别会如何去申请这些接口来自测呢 Windows 外面咱们能够应用 bloomRPC 工具<!----> Linux 外面咱们能够应用 grpcurl 工具DEMO对于 grpc 的基本知识,感兴趣的能够查看历史文章: gRPC介绍<!----> gRPC 客户端调用服务端须要连接池吗?<!----> gRPC的拦截器<!----> gRPC的认证<!----> 分享一下 gRPC- HTTP网关 I那么咱们写一个 demo,一个 grpc 的服务端,提供如下接口 查问租户的详情<!----> 查问租户的列表Demo 目录构造如下: 咱们的 proto 文件能够是这样的: protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative tenant.proto根本简略的代码实现如下: rpc_services.go 代码如下 这个时候启动咱们的服务端,如何运行 golang 程序就不赘述了 bloomRPC 工具开始来下载 bloomRPC 工具 ...

August 29, 2023 · 1 min · jiezi

关于grpc:gRPC的理解与使用

协定介绍gRPC 是谷歌开源的一套 RPC 协定框架,底层应用HTTP/2协定,次要有两局部,数据编码以及申请映射 数据编码是将内存对象编码为可传输的字节流,也包含把字节流转化为内存对象,常见的蕴含json, msgpack, xml, protobuf,其中该编码效率比json高一些,grpc抉择应用protobuf gRPC为什么基于HTTP2HTTP1.1遇到的问题 协定繁琐,蕴含很多的细节设计,也预留了很多将来扩大选项,所以没有软件实现了协定中提及的全副细节协定规定是一发一收这种模式,相当于一个先进先出的串行队列,HTTP Pipelining 把多个 HTTP 申请放到一个 TCP 连贯中来发送,发送过程中不须要服务器对前一个申请的响应,然而在客户端,还是会依照发送的程序来接管响应申请,导致 HTTP 头阻塞(Head-of-line blocking)HTTP2的个性与组成 HEAD 头数据压缩: 对 HTTP 头字段进行数据压缩,因为 HTTP 头蕴含了大量冗余数据,HTTP2对这些数据进行了压缩,压缩后对于申请大小的影响显著,能够将多个申请压缩到一个包中,减小传输负载多路复用: 每个 HTTP 申请/应答在各自的流(stream,每个流都是互相独立,有一个整数ID 标识,是存在于TCP连贯中的一个虚构连贯通道,能够承载双向音讯)中实现数据交换,如果一个申请/应答阻塞或者速度很慢,也不会影响其它流中的申请/应答解决,在一个 TCP 连贯中就能够传输多个流数据而无需建设多个连贯流量管制和优先级机制: 能够无效利用流的多路复用机制,流量管制能够确保只有接收者应用的数据会被传输,优先级机制能够确保重要的资源被优先传输服务端推送: 即服务端能够推送应答给客户端消息报文二进制编码最小传输单元帧(frame):HTTP2 定义了很多类型的帧,每个帧服务于不同的目标,数据帧中有 1 个要害数据,这个帧属于哪个资源,音讯由一个或多个帧形成json全称JavaScript Object Notation,一种轻量级的数据交换格局,具备良好的可读和便于疾速编写的个性。可在不同平台之间进行数据交换,在json呈现以前,罕用的是xml(Extensiable Markup Language)进行文件传输 xml和json的独特长处 可读性好,构造清晰分层存储(档次嵌套)都可作为Ajaxs传输数据都跨平台,可作为数据传输格局json的长处 数据格式简略,易读易写,且数据都是压缩的,文件较小,便于传输json解析难度较低,而xml须要循环遍历DOM进行解析,效率较低服务端和客户端能够间接应用json,便于保护,而不同客户端解析xml可能应用不同办法json 已成为以后服务器与 web 利用之间数据传输的公认规范xml的应用领域 xml格局较为谨严,可读性更强,更易于拓展,能够良好的做配置文件呈现较早,在各个领域有宽泛的利用,具备广泛的流行性json语法规定json语法是JavaScript语法的子集,而json个别也是用来传输对象和数组。也就是json语法是JavaScript语法的一部分(满足特定语法的JavaScript语法) 数据保留在名称、值对中,数据由逗号分隔花括号示意对象中括号示意数组json名称/值json 数据的书写格局为:"名称":"值"。对应JavaScript的概念就是:名称="值"但json的格局和JavaScript对象格局还是有所区别: JavaScript对象的名称能够不加引号,也能够单引号,也能够双引号,但json字符串的名称只能加双引号的字符示意。JavaScript对象的键值能够是除json值之外还能够是函数等其余类型数据,而json字符串的值对只能是数字、字符串(要双引号)、逻辑值、对象(加大括号)、数组(中括号)、null。json对象json有两种示意构造—对象和数组,通过这两种示意构造能够示意更简单的构造。比照java的话json数组和json对象就好比java的列表/数组(Object类型)和对象(Map)一样的关系。并且很多状况其对象值可能互相嵌套多层,对对象中存在对象,对象中存在数组,数组中存在对象 JavaScript对象 / json对象 / json字符串//JavaScript对象, 除了字符串、数字、true、false、null和undefined之外,JavaScript中的值都是对象var a1={ name:"pky" , sex:"man", value: 12345 };var a2={'name':'pky' , 'sex':'man', 'value': 12345};//满足json格局的JavaScript对象, json对象var a3={"name":"pky" , "sex":"man", "value": 12345};//json字符串var a4='{"name":"pky" , "sex":"man", "value": 12345}';json次要毛病是非字符串的编码效率比拟低,下面的数据比方value字段的值,在内存中是12345,占用2字节,json编码转变为json字符串之后占用5字节 ...

August 24, 2023 · 3 min · jiezi

关于grpc:gRPC-客户端调用服务端需要连接池吗

发现的问题在微服务开发中,gRPC 的利用相对少不了,个别状况下,外部微服务交互,通常是应用 RPC 进行通信,如果是内部通信的话,会提供 https 接口文档 对于 gRPC 的根本应用能够查看文章 gRPC介绍 对于 gRPC ,咱们须要根本晓得如下的一些知识点: gRPC 的根本四种模式的利用场景 申请响应模式客户端数据流模式服务端数据流模式双向流模式<!----> Proto 文件的定义和应用<!----> gRPC 拦截器的利用 , 根本的能够查看这篇 gRPC 拦截器 实际上有客户端拦截器 和 服务端拦截器,具体具体的能够自行学习<!----> gRPC 的设计原理细节<!----> Go-Kit 的应用当然明天并不是要聊 gRPC 的利用或者原理,而是想聊咱们在开发过程中很容易遇到的问题: 未复用 gRPC 客户端连贯,影响性能最近审查各个服务代码中,发现整个部门应用 gRPC 客户端申请服务端接口的时候,都是会新建一个连贯,而后调用服务端接口,应用结束之后就 close 掉, 例如这样 这会有什么问题呢? 失常简略的应用不会有啥问题,但如果是面临高并发的状况,性能问题很容易就会呈现,例如咱们在做性能测试的时候,就会发现,打一会性能测试,客户端申请服务端的时候就会报错: rpc error: code = Unavailable desc = all SubConns are in TransientFailure, latest connection error: connection error: desc = "transport: Error while dialing dial tcp xxx:xxx: connect: connection refused ...

August 21, 2023 · 5 min · jiezi

关于grpc:K8sgRPC云原生微服务开发与治理实战完结未成曲调先有情

download:K8s+gRPC云原生微服务开发与治理实战完结从源码学习gRPC设计的概述gRPC是一款高性能、开源的RPC框架,由Google公司开发并保护。它基于Protocol Buffers协定进行通信,反对多种编程语言和平台间的通信。在gRPC中,客户端能够像调用本地办法一样调用近程服务,并且主动生成客户端和服务器端的代码,简化了开发者的工作。 在本文中,咱们将从源码层面来学习gRPC的设计,深刻探索其实现原理,包含gRPC的外围组件、音讯传输、序列化和反序列化等方面。 gRPC的外围组件gRPC的外围组件次要包含Channel、Server、Call、Service、Message和Interceptor。其中,Channel用于建设连贯,Server监听并解决申请,Call解决申请和响应,Service提供服务定义,Message示意发送和接管的音讯,Interceptor提供拦截器链。 ChannelChannel是gRPC客户端和服务端之间通信的通道,负责连贯治理、压缩解决、流量管制等。当客户端和服务端建设连贯后,会创立一个对应的Channel对象来治理该连贯,Channel对象外部保护着多个Subchannel对象来实现负载平衡和故障转移。 ServerServer是gRPC服务端的外围组件,负责监听网络申请和解决网络申请。当服务端启动时,会创立一个对应的Server对象来管理网络申请,Server对象外部保护着多个Subserver对象来解决网络申请和响应。 CallCall是gRPC客户端和服务端之间进行申请和响应的外围组件,负责序列化和反序列化、压缩和解压缩、流量管制等。当客户端调用近程办法时,会创立一个对应的Call对象来发送申请和接管响应,Call对象外部保护着多个Stream对象来实现音讯流的传输。 ServiceService是gRPC服务定义的外围组件,负责定义服务的接口和办法。在gRPC中,服务端须要实现服务定义中的办法,客户端则能够通过主动生成的代码来调用服务定义中的办法。 MessageMessage是gRPC中音讯传输的外围组件,负责序列化和反序列化数据。在gRPC中,客户端和服务端之间替换的数据必须是通过序列化解决后的二进制数据,Message对象封装了这些二进制数据及其元信息。 InterceptorInterceptor是gRPC提供的拦截器链,用于在申请和响应之间插入拦截器。拦截器能够在申请和响应前后进行一些解决,比方日志记录、鉴权、限流等。 gRPC的音讯传输gRPC的音讯传输分为两种形式:一种是基于HTTP/2协定的一般模式,另一种是基于TCP协定的流模式。在一般模式下,客户端和服务端之间通过HTTP/2进行通信,能够实现多路复用和头部压缩等优化。而在流模式下,客户端和服务端之间通过TCP进行通信,能够实现更高效的数据传输。 在gRPC的实现中,音讯传输采纳了流式解决的形式,行将申请和响应分成多个音讯进行解决。这样能够防止大量数据的阻塞和提早,进步了通信效率和吞吐量。 gRPC的序列化和反序列化在gRPC中,序列化和反序列化是十分重要的组件,它们负责将音讯转换为二进制格局,并在接管方将其还原为音讯格局。gRPC采纳Protocol Buffers作为序列化和反序列化的规范,比起JSON、XML等格局,Protocol Buffers具备更小的体积和更快的解析速度。 在gRPC的实现中,序列化和反序列化由protobuf库实现。当发送方调用办法时,序列化器将参数对象转换为二进制格局,并将其封装为Message对象;当接管方收到音讯后,反序列化器将其转换为音讯格局,并将其封装为Message对象。 总结通过对gRPC的源码学习,咱们深刻理解了其设计和实现原理。gRPC的外围组件包含Channel、Server、Call、Service、Message和Interceptor,它们别离负责连贯治理、网络申请解决、音讯传输、服务定义、序列化和反序列化、拦截器链等性能。同时,gRPC采纳基于HTTP/2协定的一般模式和基于TCP协定的流模式进行音讯传输,并采纳Protocol Buffers作为序列化和反序列化的规范,具备更高的效率和性能。

May 25, 2023 · 1 min · jiezi

关于grpc:gRPC请求超时和异常处理

1. 申请超时在 HTTP 申请中,咱们发送申请的时候,能够设置一个申请超时工夫-connectTimeout,即在指定的工夫内,如果申请没有达到服务端,为了防止客户端始终进行不必要的期待,就会抛出一个申请超时异样。 然而在微服务零碎中,咱们却很少设置申请超时工夫,个别都是用另外一个概念代替,那就是申请截止工夫。 这是什么起因呢?明天咱们就来简略聊一聊这个话题。 在微服务中咱们客户端的申请在服务端往往会有比较复杂的链条,我想起来 Spring Cloud Sleuth 官网给的一个申请链路追踪的图,咱们间接拿来看下: 这张图中,申请从客户端发动之后,在服务端一共经验了四个 SERVICE,对于这样的申请,如果咱们还是依照之前发送一般 HTTP 申请的形式,设置一个 connectTimeout 显然是不够的。 我举个例子: 假如咱们发送一个申请,为该申请设置 connectTimeout 为 5s,那么这个工夫只对第一个服务 SERVICE1 无效,也就是申请在 5s 之内没有达到 SERVICE1,那么就会抛出连贯超时异样;申请如果在 5s 之内达到 SERVICE1,那么就不会抛出异样,然而!!!,申请达到 SERVICE1 并不意味着申请完结,前面从 SERVICE1 到 SERVICE2,从 SERVICE2 到 SERVICE3,从 SERVICE3 到 SERVICE4,还有四个 HTTP 申请待处理,这些申请超时了怎么办?很显著,connectTimeout 属性对于前面几个申请就遥相呼应了。 所以,对于这种场景,咱们个别应用截止工夫来解决。 截止工夫相当于设置整个申请生命周期的工夫,也就是这个申请,我要多久拿到后果。很显著,这个工夫应该在客户端发动申请的时候设置。 gRPC 中提供了对应的办法,咱们能够十分不便的设置申请的截止工夫 DeadLineTime,如下: public class LoginClient { public static void main(String[] args) throws InterruptedException { ManagedChannel channel = ManagedChannelBuilder.forAddress("localhost", 50051) .usePlaintext() .build(); LoginServiceGrpc.LoginServiceStub stub = LoginServiceGrpc.newStub(channel).withDeadline(Deadline.after(3, TimeUnit.SECONDS)); login(stub); } private static void login(LoginServiceGrpc.LoginServiceStub stub) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(1); stub.login(LoginBody.newBuilder().setUsername("javaboy").setPassword("123").build(), new StreamObserver<LoginResponse>() { @Override public void onNext(LoginResponse loginResponse) { System.out.println("loginResponse.getToken() = " + loginResponse.getToken()); } @Override public void onError(Throwable throwable) { System.out.println("throwable = " + throwable); } @Override public void onCompleted() { countDownLatch.countDown(); } }); countDownLatch.await(); }}服务端通过 Thread.sleep 做个简略的休眠就行了,超时之后,客户端的 onError 办法会被触发,抛出如下异样: ...

March 9, 2023 · 2 min · jiezi

关于grpc:手把手教大家在-gRPC-中使用-JWT-完成身份校验

@[toc]上篇文章松哥和小伙伴们聊了在 gRPC 中如何应用拦截器,这些拦截器有服务端拦截器也有客户端拦截器,这些拦截器的一个重要应用场景,就是能够进行身份的校验。当客户端发动申请的时候,服务端通过拦截器进行身份校验,就晓得这个申请是谁发动的了。明天松哥就来通过一个具体的案例,来和小伙伴们演示一下 gRPC 如何联合 JWT 进行身份校验。 1. JWT 介绍1.1 无状态登录1.1.1 什么是有状态有状态服务,即服务端须要记录每次会话的客户端信息,从而辨认客户端身份,依据用户身份进行申请的解决,典型的设计如 Tomcat 中的 Session。例如登录:用户登录后,咱们把用户的信息保留在服务端 session 中,并且给用户一个 cookie 值,记录对应的 session,而后下次申请,用户携带 cookie 值来(这一步有浏览器主动实现),咱们就能辨认到对应 session,从而找到用户的信息。这种形式目前来看最不便,然而也有一些缺点,如下: 服务端保留大量数据,减少服务端压力服务端保留用户状态,不反对集群化部署1.1.2 什么是无状态微服务集群中的每个服务,对外提供的都应用 RESTful 格调的接口。而 RESTful 格调的一个最重要的标准就是:服务的无状态性,即: 服务端不保留任何客户端请求者信息客户端的每次申请必须具备自描述信息,通过这些信息辨认客户端身份那么这种无状态性有哪些益处呢? 客户端申请不依赖服务端的信息,屡次申请不须要必须拜访到同一台服务器服务端的集群和状态对客户端通明服务端能够任意的迁徙和伸缩(能够不便的进行集群化部署)减小服务端存储压力1.2 如何实现无状态无状态登录的流程: 首先客户端发送账户名/明码到服务端进行认证认证通过后,服务端将用户信息加密并且编码成一个 token,返回给客户端当前客户端每次发送申请,都须要携带认证的 token服务端对客户端发送来的 token 进行解密,判断是否无效,并且获取用户登录信息1.3 JWT1.3.1 简介JWT,全称是 Json Web Token, 是一种 JSON 格调的轻量级的受权和身份认证标准,可实现无状态、分布式的 Web 利用受权: JWT 作为一种标准,并没有和某一种语言绑定在一起,罕用的 Java 实现是 GitHub 上的开源我的项目 jjwt,地址如下:https://github.com/jwtk/jjwt 1.3.2 JWT数据格式JWT 蕴含三局部数据: Header:头部,通常头部有两局部信息: 申明类型,这里是JWT加密算法,自定义咱们会对头部进行 Base64Url 编码(可解码),失去第一局部数据。 Payload:载荷,就是无效数据,在官网文档中(RFC7519),这里给了7个示例信息: iss (issuer):示意签发人exp (expiration time):示意token过期工夫sub (subject):主题aud (audience):受众nbf (Not Before):失效工夫iat (Issued At):签发工夫jti (JWT ID):编号这部分也会采纳 Base64Url 编码,失去第二局部数据。 ...

February 22, 2023 · 5 min · jiezi

关于grpc:聊一聊-gRPC-中的拦截器

明天咱们持续 gRPC 系列。 后面松哥跟大家聊了 gRPC 的简略案例,也说了四种不同的通信模式,感兴趣的小伙伴能够戳这里: 一个简略的案例入门 gRPC聊一聊 gRPC 的四种通信模式明天咱们来持续聊一聊 gRPC 中的拦截器。 有申请的发送、解决,当然就会有拦截器的需要,例如在服务端通过拦截器对立进行申请认证等操作,这些就须要拦截器来实现,明天松哥先和小伙伴们来聊一聊 gRPC 中拦截器的根本用法,前面我再整一篇文章和小伙伴们做一个基于拦截器实现的 JWT 认证的 gRPC。 gRPC 中的拦截器整体上来说能够分为两大类: 服务端拦截器客户端拦截器咱们别离来看。 1. 服务端拦截器服务端拦截器的作用有点像咱们 Java 中的 Filter,服务端拦截器又能够持续细分为一元拦截器和流拦截器。 一元拦截器对应咱们上篇文章中所讲的一元 RPC,也就是一次申请,一次响应这种状况。 流拦截器则对应咱们上篇文章中所讲的服务端流 RPC、客户端流 RPC 以及双向流 RPC。 不过,在 Java 代码中,无论是一元拦截器还是流拦截器,代码其实都是一样的。不过如果你是用 Go 实现的 gRPC,那么这块是不一样的。 所以接下来的内容我就不去辨别一元拦截器和流拦截器了,咱们间接来看一个服务端拦截器的例子。 这里我就不从头开始写了,咱们间接在上篇文章的根底之上持续增加拦截器即可。 服务端拦截器工作地位大抵如下: 从这张图中小伙伴们能够看到,咱们能够在服务端解决申请之前将申请拦挡下来,对立进行权限校验等操作,也能够在服务端将申请处理完毕之后,筹备响应的时候将响应拦挡下来,能够对响应进行二次解决。 首先咱们来看申请拦截器,实际上是一个监听器: public class BookServiceCallListener<R> extends ForwardingServerCallListener<R> { private final ServerCall.Listener<R> delegate; public BookServiceCallListener(ServerCall.Listener<R> delegate) { this.delegate = delegate; } @Override protected ServerCall.Listener<R> delegate() { return delegate; } @Override public void onMessage(R message) { System.out.println("这是客户端发来的音讯,能够在这里进行预处理:"+message); super.onMessage(message); }}这里咱们自定义一个类,继承自 ForwardingServerCallListener 类,在这里重写 onMessage 办法,当有申请达到的时候,就会通过这里的 onMessage 办法。如果咱们须要对传入的参数进行验证等操作,就能够在这里实现。 ...

February 15, 2023 · 2 min · jiezi

关于grpc:写给go开发者的gRPC教程错误处理

本篇为【写给go开发者的gRPC教程】系列第四篇 第一篇:protobuf根底 第二篇:通信模式 第三篇:拦截器 第四篇:错误处理 本系列将继续更新,欢送关注获取实时告诉 根本错误处理首先回顾下pb文件和生成进去的client与server端的接口 service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order);}type OrderManagementClient interface { GetOrder(ctx context.Context, in *wrapperspb.StringValue, opts ...grpc.CallOption) (*Order, error)}type OrderManagementServer interface { GetOrder(context.Context, *wrapperspb.StringValue) (*Order, error) mustEmbedUnimplementedOrderManagementServer()}能够看到,尽管咱们没有在pb文件中的接口定义设置error返回值,但生成进去的go代码是蕴含error返回值的 这十分合乎Go语言的应用习惯:通常状况下咱们定义多个error变量,并且在函数内返回,调用方能够应用errors.Is()或者errors.As()对函数的error进行判断 var ( ParamsErr = errors.New("params err") BizErr = errors.New("biz err"))func Invoke(i bool) error { if i { return ParamsErr } else { return BizErr }}func main() { err := Invoke(true) if err != nil { switch { case errors.Is(err, ParamsErr): log.Println("params error") case errors.Is(err, BizErr): log.Println("biz error") } }} 但,在RPC场景下,咱们还能进行error的值判断么? ...

February 13, 2023 · 4 min · jiezi

关于grpc:聊一聊-gRPC-的四种通信模式

舒适提醒:本文须要联合上一篇 gRPC 文章一起食用,否则可能看不懂。后面一篇文章松哥和大家聊了 gRPC 的根本用法,明天咱们再来略微深刻一点点,来看下 gRPC 中四种不同的通信模式。 gRPC 中四种不同的通信模式别离是: 一元 RPC服务端流 RPC客户端流 RPC双向流 RPC接下来松哥就通过四个残缺的案例,来别离和向搭档们演示这四种不同的通信模式。 1. 筹备工作对于 gRPC 的基础知识咱们就不啰嗦了,咱们间接来看我明天的 proto 文件,如下: 这次我新建了一个名为 book.proto 的文件,这里次要定义了一些图书相干的办法,如下: syntax = "proto3";option java_multiple_files = true;option java_package = "org.javaboy.grpc.demo";option java_outer_classname = "BookServiceProto";import "google/protobuf/wrappers.proto";package book;service BookService { rpc addBook(Book) returns (google.protobuf.StringValue); rpc getBook(google.protobuf.StringValue) returns (Book); rpc searchBooks(google.protobuf.StringValue) returns (stream Book); rpc updateBooks(stream Book) returns (google.protobuf.StringValue); rpc processBooks(stream google.protobuf.StringValue) returns (stream BookSet);}message Book { string id = 1; repeated string tags = 2; string name = 3; float price = 4; string author = 5;}message BookSet { string id = 1; repeated Book bookList = 3;}这个文件中,有一些内容咱们在上篇文章中都讲过了,讲过的我就不再反复了,我说一些上篇文章没有波及到的货色: ...

February 9, 2023 · 5 min · jiezi

关于grpc:写给go开发者的gRPC教程通信模式

本篇为【写给go开发者的gRPC教程系列】第二篇 第一篇:protobuf根底第二篇:通信模式上一篇介绍了如何编写 protobuf 的 idl,并应用 idl 生成了 gRPC 的代码,当初来看看如何编写客户端和服务端的代码 Simple RPC (Unary RPC)syntax = "proto3";package ecommerce;import "google/protobuf/wrappers.proto";option go_package = "ecommerce/";message Order { string id = 1; repeated string items = 2; string description = 3; float price = 4; string destination = 5;}service OrderManagement { rpc getOrder(google.protobuf.StringValue) returns (Order);}定义如上的 idl,须要关注几个事项 应用protobuf最新版本syntax = "proto3";protoc-gen-go要求 pb 文件必须指定 go 包的门路。即option go_package = "ecommerce/";定义的method仅能有一个入参和出参数。如果须要传递多个参数须要定义成message应用import援用另外一个文件的 pb。google/protobuf/wrappers.proto是 google 内置的类型生成 go 和 grpc 的代码 $ protoc -I ./pb \ --go_out ./ecommerce --go_opt paths=source_relative \ --go-grpc_out ./ecommerce --go-grpc_opt paths=source_relative \ ./pb/product.protoecommerce├── product.pb.go└── product_grpc.pb.gopb└── product.protoserver 实现1、由 pb 文件生成的 gRPC 代码中蕴含了 service 的接口定义,它和咱们定义的 idl 是吻合的 ...

January 26, 2023 · 5 min · jiezi

关于grpc:写给go开发者的gRPC教程protobuf基础

gRPC是谷歌开源的一款高性能、反对多种开发语言的服务框架,对于一个rpc咱们关注如下几方面: 序列化协定。gRPC应用protobuf,首先应用protobuf定义服务,而后应用这个文件来生成客户端和服务端的代码。因为pb是跨语言的,因而即便服务端和客户端语言并不统一也是能够相互序列化和反序列化的 网络传输层。gRPC应用http2.0协定,http2.0相比于HTTP 1.x ,大幅度的晋升了 web 性能。 Protobuf IDL所谓序列化艰深来说就是把内存的一段数据转化成二进制并存储或者通过网络传输,而读取磁盘或另一端收到后能够在内存中重建这段数据 1、protobuf协定是跨语言跨平台的序列化协定。 2、protobuf自身并不是和gRPC绑定的。它也能够被用于非RPC场景,如存储等 json、 xml都是一种序列化的形式,只是他们不须要提前预约义idl,且具备可读性,当然他们传输的体积也因而较大,能够说是各有优劣 所以先来介绍下protobuf的idl怎么写。protobuf最新版本为proto3,在这里你能够看到具体的文档阐明:https://protobuf.dev/programm... 定义音讯类型protobuf里最根本的类型就是message,每一个messgae都会有一个或者多个字段(field),其中字段蕴含如下元素 类型:类型不仅能够是标量类型(int、string等),也能够是复合类型(enum等),也能够是其余message字段名:字段名比拟举荐的是应用下划线/分隔名称字段编号:一个messgae内每一个字段编号都必须惟一的,在编码后其实传递的是这个编号而不是字段名字段规定:音讯字段能够是以下字段之一 singular:格局正确的音讯能够有零个或一个字段(但不能超过一个)。应用 proto3 语法时,如果未为给定字段指定其余字段规定,则这是默认字段规定optional:与 singular 雷同,不过您能够查看该值是否明确设置repeated:在格局正确的音讯中,此字段类型能够反复零次或屡次。零碎会保留反复值的程序map:这是一个成对的键值对字段保留字段:为了防止再次应用到已移除的字段能够设定保留字段。如果任何将来用户尝试应用这些字段标识符,编译器就会报错标量值类标量类型会波及到不同语言和编码方式,后续有机会深刻讲 .proto TypeGo TypeNotesdoublefloat64 floatfloat32 int32int32应用可变长度的编码。对正数的编码效率低下 - 如果您的字段可能蕴含负值,请改用 sint32。int64int64应用可变长度的编码。对正数的编码效率低下 - 如果字段可能有负值,请改用 sint64。uint32uint32应用可变长度的编码。uint64uint64应用可变长度的编码。sint32int32应用可变长度的编码。有符号整数值。与惯例 int32 相比,这些函数能够更高效地对正数进行编码。sint64int64应用可变长度的编码。有符号整数值。与惯例 int64 相比,这些函数能够更高效地对正数进行编码。fixed32uint32始终为 4 个字节。如果值通常大于 2^28,则比 uint32 更高效。fixed64uint64始终为 8 个字节。如果值通常大于 2^56,则比 uint64 更高效。sfixed32int32始终为 4 个字节。sfixed64int64始终为 8 个字节。boolbool stringstring字符串必须始终蕴含 UTF-8 编码或 7 位 ASCII 文本,并且长度不得超过 232。bytes[]byte能够蕴含任意长度的 2^32 字节。复合类型数组message SearchResponse { repeated Result results = 1;}message Result { string url = 1; string title = 2; repeated string snippets = 3;}枚举message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus corpus = 4;}服务定义的method仅能有一个入参和出参数。如果须要传递多个参数须要定义成message ...

January 24, 2023 · 3 min · jiezi

关于grpc:Grpc使用bufbuild-快速编译

本文通过实例来解说应用buf来疾速的编译proto文件,不须要再用protoc命令加各种参数来编译proto文件。 当时须要装置buf, 装置办法请参考官网installation咱们先建设目录构造 auth.protosyntax = "proto3";option go_package ="trade/auth/pb/proto/go/auth/v1;authV1";package auth.v1;message LoginRequest { string username = 1; string password = 2;}message LoginResponse { string access_token = 1; int32 expires_in = 2;}service AuthService { rpc Login (LoginRequest) returns (LoginResponse);}进入proto文件夹,执行buf mod initcd trade/apps/auth/pb/protobuf mod init进入下级pb文件夹 增加buf.gen.yaml文件buf.gen.yaml的配置,此处用到的plugin是本地模式的,请提前装好红框里的插件怎么装,请自行百度,如果有疑难能够私信我。 # buf.gen.yamlversion: v1plugins: - plugin: go out: gen/proto/go opt: paths=source_relative - plugin: go-grpc out: gen/proto/go opt: paths=source_relative输出命令 buf generate 进行编译,生成grpc的代码buf generate如果须要生成grpc-gateway的代码,请持续向下看在pb目录下创立一个auth.yamltype: google.api.Serviceconfig_version: 3http: rules: - selector: auth.v1.AuthService.Login post: "/v1/login" body: "*"批改buf.gen.yaml,增加grpc-gateway插件,并指定http接口规定的文件为auth.yaml - plugin: grpc-gateway out: gen/proto/go opt: - paths=source_relative - grpc_api_configuration=auth.yaml从新执行 buf generate 进行编译,生成proto文件对应的gw文件 ...

December 20, 2022 · 1 min · jiezi

关于grpc:grpc-备忘

grpcui 绑定调整grpcui 默认是绑定在127.0.0.1上,如果须要外网拜访 能够通过-bind设置 例如 grpcui -bind '0.0.0.0' -plaintext 127.0.0.1:8104 grpc 状态码状态码形容阐明0Ok返回胜利1Canceled操作已勾销2Unknown未知谬误。如果从另一个地址空间接管到的状态值属于在该地址空间中未知的谬误空间,则能够返回此谬误的示例。 没有返回足够的错误信息的API引发的谬误也可能会转换为此谬误3InvalidArgument示意客户端指定了有效的参数。 请留神,这与FailedPrecondition不同。 它示意无论零碎状态如何(例如格局谬误的文件名)都有问题的参数4DeadlineExceeded意味着操作在实现之前过期。 对于更改零碎状态的操作,即便操作胜利实现,也可能会返回此谬误。 例如,服务器的胜利响应可能会提早足够的工夫以使截止日期到期5NotFound示意找不到某个申请的实体(例如文件或目录)6AlreadyExists示意尝试创立实体失败,因为曾经存在7PermissionDenied示意调用者没有执行指定操作的权限。它不能用于因耗尽某些资源而引起的回绝(应用ResourceExhausted代替这些谬误)。如果调用者无奈辨认,则不能应用它(应用Unauthenticated代替这些谬误)8ResourceExhausted示意某些资源已耗尽,可能是每个用户的配额,或者整个文件系统空间有余9FailedPrecondition示意操作被回绝,因为零碎不处于操作执行所需的状态10Aborted示意操作被停止,通常是因为并发问题(如序列器查看失败,事务异样终止等)造成的11OutOfRange示意操作尝试超过无效范畴12Unimplemented该办法未实现13Internal意味着底层零碎预期的一些不变量已被突破。 如果你看到其中的一个谬误,那么事件就会十分蹩脚14Unavailable外部Grpc服务不可用,申请不到15DataLoss批示不可复原的数据失落或损坏16Unauthenticated示意申请没有无效的操作认证凭证常见状态码 4 超时过期14 不可用(rpc服务没有开启 端口异样)参考这里

November 29, 2022 · 1 min · jiezi

关于grpc:解压proto文件

以php clinet为例: 解压proto文件装置相应扩大,进入以后文件夹下执行如下命令即可protoc --php_out=../ centerorder.protoprotoc --php_out=../ centerpay.proto Passport接入的是第3版 这样会缺失客户端文件/usr/local/protobuf/bin/protoc --php_out=./ User.proto 在proto以后文件下运行脚本,将解压后的文件放在上一级当时建好的Settlement文件夹下protoc --proto_path=./ --php_out=../Settlement --grpc_out=../Settlement --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin ./settlement.proto或者:protoc --proto_path=./ --php_out=../ --grpc_out=../ --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin ./settlement.proto

October 24, 2022 · 1 min · jiezi

关于grpc:grpc中的拦截器

0.1、索引https://waterflow.link/articles/1665853719750 当咱们编写 HTTP 应用程序时,您能够应用 HTTP 中间件包装特定于路由的应用程序处理程序,能够在执行应用程序处理程序之前和之后执行一些常见的逻辑。 咱们通常应用中间件来编写跨畛域组件,例如受权、日志记录、缓存等。在 gRPC 中能够应用称为拦截器的概念来实现雷同的性能。 通过应用拦截器,咱们能够在客户端和服务器上拦挡 RPC 办法的执行。 在客户端和服务器上,都有两种类型的拦截器: UnaryInterceptor(一元拦截器)StreamInterceptor(流式拦截器)UnaryInterceptor 拦挡一元 RPC,而 StreamInterceptor 拦挡流式 RPC。 在一元 RPC 中,客户端向服务器发送单个申请并返回单个响应。 在流式 RPC 中,客户端或服务器,或单方(双向流式传输),获取一个流读取一系列音讯返回,而后客户端或服务器从返回的流中读音讯,直到没有更多音讯为止。 1、在 gRPC 客户端中编写拦截器咱们能够在 gRPC 客户端应用程序中编写两种类型的拦截器: UnaryClientInterceptor:UnaryClientInterceptor 拦挡客户端上一元 RPC 的执行。StreamClientInterceptor:StreamClientInterceptor拦挡ClientStream的创立。 它可能会返回一个自定义的 ClientStream 来拦挡所有 I/O 操作。1、UnaryClientInterceptor为了创立 UnaryClientInterceptor,能够通过提供 UnaryClientInterceptor 函数值调用 WithUnaryInterceptor 函数,该函数返回一个 grpc.DialOption 指定一元 RPC 的拦截器: func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption而后将返回的 grpc.DialOption 值用作调用 grpc.Dial 函数以将拦截器利用于一元 RPC 的参数。 UnaryClientInterceptor func 类型的定义如下: type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts …CallOption) error参数调用者是实现 RPC 的处理程序,调用它是拦截器的责任。 UnaryClientInterceptor 函数值提供拦截器逻辑。 这是一个实现 UnaryClientInterceptor 的示例拦截器: ...

October 16, 2022 · 6 min · jiezi

关于grpc:golang开发一个简单的grpc

0.1、索引https://waterflow.link/articles/1665674508275 1、什么是grpc在 gRPC 中,客户端应用程序能够间接调用不同机器上的服务器应用程序上的办法,就像它是本地对象一样,使您更容易创立分布式应用程序和服务。 与许多 RPC 零碎一样,gRPC 基于定义服务的思维,指定能够近程调用的办法及其参数和返回类型。 在服务端,服务端实现这个接口并运行一个 gRPC 服务器来解决客户端调用。 在客户端,客户端有一个stub(在某些语言中仅称为客户端),它提供与服务器雷同的办法。 所以grpc是跨语言的。 2、什么是Protocol BuffersProtocol Buffers提供了一种语言中立、平台中立、可扩大的机制,用于以向前兼容和向后兼容的形式序列化结构化数据。 它相似于 JSON,只是它更小更快,并且生成本地语言绑定。 能够通过 .proto定义数据结构,而后就能够应用Protocol Buffers编译器 protoc 从. proto 定义中生成咱们喜爱的语言的数据拜访类。 它们为每个字段提供简略的拜访器,如 name() 和 set_name(),以及将整个构造序列化/解析到原始字节/从原始字节中提取的办法。 3、grpc服务端1、首先咱们须要下载go对应的protoc插件go install google.golang.org/protobuf/cmd/[email protected]go install google.golang.org/grpc/cmd/[email protected]而后把GOPATH放到PATH export PATH="$PATH:$(go env GOPATH)/bin"接着打印下看看有没有进去 echo $PATH/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$GOPATH/bin:/usr/local/go/bin接着开新窗口,执行上面命令看下protoc是否装置胜利 protoc --versionlibprotoc 3.19.12、接着咱们创立一个hello.proto内容如下(不懂构造的可自行百度) syntax = "proto3";package helloservice;option go_package = ".;helloservice"; // 指定包名message String { string value = 1;}service HelloService { rpc Hello(String) returns (String); // 一元办法 rpc Channel (stream String) returns (stream String); // 流式办法}目录构造如下 ...

October 13, 2022 · 2 min · jiezi

关于grpc:从零开始学gRPC一

前言gRPC作为以后最热门的RPC框架之一,以其独特的跨语言、跨平台个性,博得许多公司的青眼。诚实说,之前我只是一人传虚;万人传实并没有认真去钻研,明天我会依据官网的demo开展介绍整个gRPC的性能,前面一篇会介绍gRPC如何整合到SpringCloud。 我这里只提供了搭建demo工程的材料,倡议本人入手来操作。没有截图我的项目也是因为官网的材料相当齐全,没必要反复造轮子。 gRPC总览在间接应用gRPC之前,咱们先理解下它的所有个性。官网形容 我就不开展讲了,gRPC有以下几点次要性能: 应用Protocol Buffer 定义服务。语言和平台的中立性双向流式通信基于HTTP2.0身份认证、SLB、tracing、health check组件可扩大。疾速搭建环境筹备操作系统:WindownsJDK版本:1.8编辑工具:IntelliJ IDEA Community Edition 2021.1.2 x64 创立我的项目本人创立一个maven我的项目就好,没有别的要求(All in one,临时不须要分模块)。想要偷懒的同学也能够借鉴我的我的项目,这里曾经给你筹备好了。然而我倡议本人入手尝试搭建比拟好,不然印象不太粗浅,等于没学(有个词儿叫口头废人,尽管不好听但目标是心愿能让你入手实际)。 阐明: 这里之所以没用官网的demo是因为我本地切实编译不进去,而且官网给的我的项目太大,对于学习来说没必要全副下载。(我只想要examples模块,然而必须强制全副下载,就很烦)当然,要下载的敌人看这里 。官网的中文文档地址是这个 ,能够依照官网提供的文档做。但如果只是玩具,那我这种All on one的形式我感觉更简略。 Helloworld咱们间接拿官网的例子来解说,官网代码地址 。外面有很多例子,我这里只讲局部。 proto文件咱们到官网地址 抄作业时要留神,整个目录的构造。proto文件只能放在模块的src/main上面,留神地位还有名字是否弄错,不然生成不了代码。 代码服务端:代码地址 客户端:代码地址 个性解说咱们晓得gRPC不仅仅是一个helloworld就能形容分明的,我上面将官网的代码例子做个分类,顺便总结下。 Stream例子proto: 代码地址代码目录: 代码地址总结上述代码想表白的意思是,服务端在收到全副的客户端数据之后再响应回客户端处理结果。理论状况能够是服务端先解决一部分,而后返回局部。也能够是任意程序, 重要的是理解如何应用Stream的相干API来做交互。 TLS例子proto: 代码地址代码目录: 代码地址(还是helloworld的例子) 总结这个就没啥好说的,用了Http2.0的TLS个性,默认就是开启的。如果客户端要传明文则必须在channel中配置,如下所示。 SLB 和 Health Check例子proto: 代码地址代码目录: 代码地址官网并没提供,上面都是本人的尝试 Client端在创立ManagedChannel时指定SLB策略。默认实现只有 pick_first 和 round_robin,这里以round_robin为例子解说。target字符串做调整新增两个类,代码如下。 public class MyNameResolverProvider extends NameResolverProvider { /** * The constant containing the scheme that will be used by this factory. */ public static final String STATIC_SCHEME = "static"; private static final Pattern PATTERN_COMMA = Pattern.compile(","); @Nullable @Override public NameResolver newNameResolver(final URI targetUri, final NameResolver.Args args) { if (STATIC_SCHEME.equals(targetUri.getScheme())) { return of(targetUri.getAuthority(), args.getDefaultPort()); } return null; } /** * Creates a new {@link NameResolver} for the given authority and attributes. * * @param targetAuthority The authority to connect to. * @param defaultPort The default port to use, if none is specified. * @return The newly created name resolver for the given target. */ private NameResolver of(final String targetAuthority, int defaultPort) { requireNonNull(targetAuthority, "targetAuthority"); // Determine target ips final String[] hosts = PATTERN_COMMA.split(targetAuthority); final List<EquivalentAddressGroup> targets = new ArrayList<>(hosts.length); for (final String host : hosts) { final URI uri = URI.create("//" + host); int port = uri.getPort(); if (port == -1) { port = defaultPort; } targets.add(new EquivalentAddressGroup(new InetSocketAddress(uri.getHost(), port))); } if (targets.isEmpty()) { throw new IllegalArgumentException("Must have at least one target, but was: " + targetAuthority); } return new MyStaticNameResolver(targetAuthority, targets); } @Override public String getDefaultScheme() { return STATIC_SCHEME; } @Override protected boolean isAvailable() { return true; } @Override protected int priority() { return 4; // Less important than DNS } @Override public String toString() { return "StaticNameResolverProvider [scheme=" + getDefaultScheme() + "]"; }}public class MyStaticNameResolver extends NameResolver { private final String authority; private final ResolutionResult result; /** * Creates a static name resolver with only a single target server. * * @param authority The authority this name resolver was created for. * @param target The target address of the server to use. */ public MyStaticNameResolver(final String authority, final EquivalentAddressGroup target) { this(authority, ImmutableList.of(requireNonNull(target, "target"))); } /** * Creates a static name resolver with multiple target servers. * * @param authority The authority this name resolver was created for. * @param targets The target addresses of the servers to use. */ public MyStaticNameResolver(final String authority, final Collection<EquivalentAddressGroup> targets) { this.authority = requireNonNull(authority, "authority"); if (requireNonNull(targets, "targets").isEmpty()) { throw new IllegalArgumentException("Must have at least one target"); } this.result = ResolutionResult.newBuilder() .setAddresses(ImmutableList.copyOf(targets)) .build(); } /** * Creates a static name resolver with multiple target servers. * * @param authority The authority this name resolver was created for. * @param result The resolution result to use.. */ public MyStaticNameResolver(final String authority, final ResolutionResult result) { this.authority = requireNonNull(authority, "authority"); this.result = requireNonNull(result, "result"); } @Override public String getServiceAuthority() { return this.authority; } @Override public void start(final Listener2 listener) { listener.onResult(this.result); } @Override public void refresh() { // Does nothing } @Override public void shutdown() { // Does nothing } @Override public String toString() { return "StaticNameResolver [authority=" + this.authority + ", result=" + this.result + "]"; }}新增文件io.grpc.NameResolverProviderServer端启动多个实现,记得要改端口(能够在server端加些日志来验证)阐明:下面的代码是从grpc-spring-boot-start中抄过来的,前面会细讲。原生的grpc只能用dns,本地没法做,所以采纳这种形式验证SLB。其实在这个过程中咱们也能看到,如果要基于grpc性能组件做扩大也是极不便的。 ...

June 23, 2022 · 3 min · jiezi

关于grpc:理解-GRPC-中的长连接

有些技术知识点能在这么短的工夫里搞清楚弄明确,和本人接触的技术深度以及广度,工作教训密不可分。再次强调一下,千万不要试图去钻研你钻研了很久都整不明确的货色,或者是你的档次不到,也或者是你从未在理论的利用场景接触过,这种状况下你去钻研,只会事倍功半,徒劳一番罢了。(在不该了解的时候了解不该了解的知识点,后果只会事倍功半)。 GRPC 包含=》 传输协定(http2.0) + 序列化协定(pb) HTTP协定是基于申请/响应模式的,因而只有服务端给了响应,本次HTTP连贯就完结了,或者更精确的说,是本次HTTP申请就完结了,基本没有长连贯这一说。那么天然也就没有短连贯这一说了。 网络上说HTTP分为长连贯和短连贯,其实实质上是说的TCP连贯。TCP连贯是一个双向的通道,它是能够放弃一段时间不敞开的,因而TCP连贯才有真正的长连贯和短连贯这一说。 HTTP协定说到底是应用层的协定,而TCP才是真正的传输层协定,只有负责传输的这一层才须要建设连贯。 举例:一个形象的例子就是,拿你在网上购物来说,HTTP协定是指的那个快递单,你寄件的时候填的单子就像是发了一个HTTP申请,等货物运到中央了,快递员会依据你发的申请把货物送给相应的收货人。而TCP协定就是两头运货的那个大货车,也可能是火车或者飞机,但不论是什么,它是负责运输的,因而必须要有路,不论是地上还是天上。那么这个路就是所谓的TCP连贯,也就是一个双向的数据通道。 HTTP申请和HTTP响应,都是通过TCP连贯这个通道来回传输的。 肯定要务必记住,长连贯是指的TCP连贯,而不是HTTP连贯。 问题:1.第一个问题是,是不是只有设置Connection为keep-alive就算是长连贯了?当然是的,但要服务器和客户端都设置。 2、第二个问题是,咱们平时用的是不是长连贯?当初用的基本上都是HTTP1.1协定,基本上Connection都是keep-alive。而且HTTP协定文档上也提到了,HTTP1.1默认是长连贯,也就是默认Connection的值就是keep-alive 3、长连贯有啥益处?长连贯是为了复用,那既然长连贯是指的TCP连贯,也就是说复用的是TCP连贯。那这就很好解释了,也就是说,长连贯状况下,多个HTTP申请能够复用同一个TCP连贯,这就节俭了很多TCP连贯建设和断开的耗费。 比方你申请了淘宝的一个网页,这个网页里必定还蕴含了CSS、JS等等一系列资源,如果你是短连贯(也就是每次都要从新建设TCP连贯)的话,那你每关上一个网页,根本要建设几个甚至几十个TCP连贯,这节约了多少资源就不必LZ去说了吧。 但如果是长连贯的话,那么这么屡次HTTP申请(这些申请包含申请网页内容,CSS文件,JS文件,图片等等),其实应用的都是一个TCP连贯,很显然是能够节俭很多耗费的,只须要一次TCP三次握手就行了。 长连贯还要多提一句,那就是,长连贯并不是永恒连贯的。如果一段时间内(具体的工夫长短,是能够在header当中进行设置的,也就是所谓的超时工夫),这个连贯没有HTTP申请收回的话,那么这个长连贯就会被断掉。 参考: https://juejin.cn/post/692388...

June 5, 2022 · 1 min · jiezi

关于grpc:预览目录gozero必知必会

go-zero必知必会服务注册发现一、服务注册发现分类【阐明】:其余服务发现形式看https://github.com/zeromicro/...官网demo,其中yaml中,etcd、IP直连、k8s三种配置文件也不是对立的 默认形式etcdapi配置文件#gateway-api.yaml#api服务发现配置模块AdminRpc: Timeout: 10000 Etcd: Hosts: - 127.0.0.1:2379 Key: admin.rpcrpc配置文件#admin.yamlName: admin.rpcListenOn: 127.0.0.1:8080#rpc服务发现配置模块Etcd: Hosts: - 127.0.0.1:2379 Key: admin.rpckubernetes【阐明】:举荐应用k8s形式部署对应利用,这样能够缩小保护一套etcd集群。 1.api配置文件 #gateway-api.yaml#api服务发现配置模块AdminRpc: Timeout: 10000 Endpoints: - 127.0.0.1:395112.rpc配置文件 #admin.yamlName: admin.rpcListenOn: 127.0.0.1:8080#rpc服务发现配置模块,不须要配置IP直连1.api配置文件 #gateway-api.yaml#api服务发现配置模块AdminRpc: Timeout: 10000 Endpoints: - 127.0.0.1:395112.rpc配置文件 #admin.yamlName: admin.rpcListenOn: 127.0.0.1:8080#rpc服务发现配置模块,不须要配置consulnacospolaris二、框架组件(一)、Api定义(二)、我的项目配置rest.RestConf配置配置构造体 RestConf struct { service.ServiceConf Host string `json:",default=0.0.0.0"` Port int CertFile string `json:",optional"` KeyFile string `json:",optional"` Verbose bool `json:",optional"` MaxConns int `json:",default=10000"` MaxBytes int64 `json:",default=1048576"` // milliseconds Timeout int64 `json:",default=3000"` CpuThreshold int64 `json:",default=900,range=[0:1000]"` Signature SignatureConf `json:",optional"`}//成员service.ServiceConftype ServiceConf struct { Name string Log logx.LogConf Mode string `json:",default=pro,options=dev|test|rt|pre|pro"` MetricsUrl string `json:",optional"` Prometheus prometheus.Config `json:",optional"` Telemetry trace.Config `json:",optional"`}//成员SignatureSignatureConf struct { Strict bool `json:",default=false"` Expiry time.Duration `json:",default=1h"` PrivateKeys []PrivateKeyConf}第1步:在internal/config/config.go新增配置文件 ...

April 29, 2022 · 1 min · jiezi

关于grpc:gRPC-简介实践

前言古代的软件服务大多数是分布式应用程序,通过裸露本人的 API 对内或对外提供了一系列的性能点。服务与服务之间有时是跨语言、跨平台通信的。 为了解决这些简单场景,市面上也涌现了有很多解决方案。比方构建 RESTful 服务,将服务能力转化为资源汇合;也有面向函数调用的客户端-服务器模式:近程过程调用(Remote Procedure Calls)。明天要介绍的 gRPC 就是后者的演变,一个十分受欢迎分布式过程间通信技术。 意识 gRPCgRPC 是 Google 在 2015 年推出的 RPC 框架。在意识 gRPC 之前,咱们先来理解下 RPC 的相干常识。RPC 次要使用于分布式程序中,它构建了客户端-服务器模型,相似于申请-响应通信形式,只不过这种申请被咱们形象为了函数办法 + 入参信息,底层的网络通信则被屏蔽了起来,到最初就像本地办法调用一样。 Protocol Buffers下面提到了函数的入参信息,有入参就有对象类型,而端到端的对象类型就势必波及到网络通信过程中的字节序列化和反序列化过程。在 gRPC 中,采纳了 Protobuf(Protocol Buffers)作为序列化和反序列化协定。它是一种轻便高效的结构化数据存储格局,基于二进制编码构建,可能缩小 CPU 的简单解析,保障了 RPC 调用的高性能。 另外 Protobuf 反对多种编程语言,咱们只须要对其进行接口定义形容,便能够依据形容文件主动生成客户端和服务端须要应用到的构造体和办法函数,就像是代码主动生成一样,大大提高了咱们的编程效率。 HTTP/2gRPC 是基于 HTTP/2 设计的,HTTP/2 也是 2015 年公布的,它是下一代的 HTTP 协定,具备很多高级性能,如: 基于二进制格局传输,传输速度更快,更紧凑,不易出错。多路复用,单个连贯可发送多个申请。对报头压缩,能升高传输开销。容许服务器被动推送。正是这些 HTTP/2 的个性,使得 gRPC 可能应用较少的资源,取得较快的响应,在挪动端设施上更加省电省流量。 gRPC 的应用接口定义当咱们开发一个 gRPC 应用程序时,要做的第一件事件就是定义一个接口形容文件。在这个文件里,咱们会将函数、参数信息都形容进去,就像上面这个 ProductInfo.proto 文件一样: // ProductInfo.protosyntax = "proto3";package ecommerce;// 服务里的接口列表service ProductInfo { rpc addProduct(Product) returns (ProductID); rpc getProduct(ProductID) returns (Product);}// 参数信息message Product { string id = 1; string name = 2; string description = 3;}message ProductID { string value = 1;}gRPC 服务端当咱们拿到定义好的接口形容文件 ProductInfo.proto 后,就能够应用 Protobuf 编译器:protoc 来生成咱们的服务端代码了。假如咱们的服务端采纳的是 Go 语言,则在通过一系列插件的装置后,咱们就能够应用上面的命令来编译生成代码了: ...

March 8, 2022 · 2 min · jiezi

关于grpc:postman如何调试grpc

在刚刚接触 grpc 的时候,调试十分不不便,之后应用过 Grpc Swagger 等一系列工具,应用都还蛮不便的,然而作为一个 postman 的忠诚粉丝,还是心愿能够应用 postman 来调试 grpc 服务。 前段时间,我老大对我说,postman 反对 grpc 了,我哈哈,情绪异样冲动,毕竟在工作中又能够省出一部分工夫去摸鱼了。 明天钻研下 postman 如何反对 grpc 服务调试。 前置条件postman 反对 grpc 是在 v9.7.1 及以上版本,低于此版本的须要降级到此版本以上版本。 如何应用 如上是官网的演示图,如下我写下我是如何看图钻研的。 其实有两种形式能够调用,第一是通过 postman 反射能力调用服务,第二是创立或导入 proto 后调用服务。 1. 反射第一步: 第二步: 第三步: 第四步:抉择Using server reflection,第三个下拉框抉择须要调用的接口,Invoke点击调用 通过以上四步,实现 grpc 服务调用,很简略吧,只须要输出ip和端口,其余的只是下拉框选一选,然而我点了几次 Save 都提醒 Coming soon 意思是马上就来 ,那就等吧。我预计是 Save 性能还未公布上线,还用不了,这个是比拟大的悲观,毕竟每次都得须要录入IP、端口等等。 2. 创立、导入proto这种形式其实更加不便,能够将 proto 文件导入到 postman 中,并且能够分版本,最终调用的时候应用导入的模版来进行调用。 第一步: 第二步:咱们导入下 demo.proto 文件第三步:导入导入实现: 第四步:可选,批改名称 第五步:这步骤也就是最终将文件加载进来,咱们能够看到具体的 proto 文件内容,也能够批改,然而不会同步本地磁盘文件。 ...

February 19, 2022 · 1 min · jiezi

关于grpc:gRPC-源码阅读及实践之-resolver

ResolvergRPC 插件式编程之Resolver 随着微服务越来越流行,服务间的通信也是绕不开的话题,gRPC 在泛滥 RPC 框架中算得上佼佼者,不仅其有一个好爸爸,grpc 在扩大方面也给开发者留有足够的空间,明天咱们将走进grpc 扩大之 Resolver,gRPC Resolver 提供了用户自行解析主机的扩大能力,咱们在应用 gRPC 时,大家有没有想过,为什么 gRPC 为什么反对以下几种格局的 target: 直连, 链接 target 为指标服务的endpointdns 服务发现unix其中在进入连贯之前,gRPC 会依据用户是否提供了 Resolver 来进行指标服务的 endpoint 解析,明天咱们来尝试写一个最简略的 etcd 做服务发现的例子 阐明源码浏览的 gRPC 版本为 3.5.1 环境etcd 装置go思路咱们将为 server 服务,假如名称为 grpc-server 启动多个实例以 grpc-server 为 key 向 etcd put 每个实例的 endpoint 真正进入 etcd 的 key 为以 grpc-server + / + 随机值实现 resolver.Builder, 获取 target从 etcd 读取以 grpc-server 为 prefix 的 endpoints告诉负载均衡器从新 pick 实例实现实现 resolver.Builder ...

January 14, 2022 · 3 min · jiezi

关于grpc:gRPC系列二-异步服务使用

gRPC系列(二) 异步服务应用相干文章:gRPC系列(一)装置和入门 异步的实现次要围绕的是grpc提供的队列:grpc::CompletionQueue。 客户端代码异步客户端代码绝对于同步客户端来说并没有简单多少,简略来说,就是同步rpc调用是调用完不会立即返回,而是能够异步从队列中取得返回后果,实现调用的解耦,咱们来看代码。 #include <iostream>#include <grpcpp/completion_queue.h>#include <grpcpp/security/credentials.h>#include <grpcpp/support/async_unary_call.h>#include <grpcpp/grpcpp.h>#include "../protos/simple/simple.grpc.pb.h"using grpc::Status;using grpc::Channel;using grpc::CompletionQueue;using grpc::ClientContext;using grpc::ClientAsyncResponseReader;using Simple::EchoRequest;using Simple::EchoResponse;int main(){ std::shared_ptr<Channel> chan = grpc::CreateChannel("localhost:12345",grpc::InsecureChannelCredentials()); std::unique_ptr<Simple::Server::Stub> stub(Simple::Server::NewStub(chan)); ClientContext context; EchoRequest req; req.set_msg("hello world!"); EchoResponse resp; CompletionQueue cq; // 实现rpc调用会将tag增加到cq队列中 std::unique_ptr<ClientAsyncResponseReader<EchoResponse>> rpc(stub->AsyncEcho(&context, req, &cq)); Status status; // 第三个参数是一个上下文标签,用于帮咱们标识这个申请 // grpc框架只会将其保存起来 rpc->Finish(&resp, &status, (void*)1); void* got_tag; bool ok = false; // 从队列中获取,申请的标签以及状态 cq.Next(&got_tag, &ok); if(ok && got_tag == (void*)1){ // check一下后果 std::cout << resp.msg() << std::endl; } return 0;}服务端代码异步服务端不不便了解,能够参考:grpc应用记录(三)简略异步服务实例这里次要波及到的类包含grpc::ServerCompletionQueue、grpc::ServerAsyncResponseWriter、grpc::ServerAsyncResponseWriter、Simple::Server::AsyncService。次要的解决流程是, ...

December 18, 2021 · 2 min · jiezi

关于grpc:gRPC系列一-安装和入门

gRPC系列(一) 装置和入门gRPC是由谷歌公司开发的一款rpc框架,反对多种语言,包含C++、Java、Golang、python等等。这个系列将会次要记录gRPC的学习过程,本文次要包含装置和简略的应用,语言为C++。 装置我的操作系统是ubuntu20.04。 装置依赖 sudo apt-get install pkg-configsudo apt-get install autoconf automake libtool make g++ unzipsudo apt-get install libgfalgs-dev libgtest-devsudo apt-get install clang libc++-dev下载gRPC git clone https://github.com/grpc/grpc.gitcd grpcgit submodule update --init // 更新第三方源码装置protobuf源码 cd third_party/protobuf/git submodule update --init --recursive./autogen.sh // 生成配置脚本./configure // 生成Makefile文件,为下一步的编译做筹备,能够加上装置门路:--prefix=pathmakemake checksudo make installsudo ldconfig // 更新共享库缓存which protoc // 查看软件是否装置胜利protoc --version // 查看是否装置胜利装置gRPC cd ../..makesudo make install简略应用应用grpc包含几个步骤:定义服务、生成代码、编写服务端代码、编写客户端代码、运行。 定义服务服务定义文件是proto文件,中文文档能够参考Protobuf语法指南这里先写一个简略的proto文件 syntax="proto3";// 语法类型package Simple;// 这是生成代码应用的namespace,所有生成的代码都会在这个namespace中。// 指定服务的名称,生成的代码外面的二级namespaceservice Server { rpc Echo(EchoRequest) returns (EchoReponse){}}message EchoRequest { string msg = 1; // }message EchoResponse { string msg = 1;}下面的接口中,必须有参数和返回值,如果不须要参数或者返回值,也必须定义一个空的(没有成员)message,否则无奈通过编译。 ...

December 18, 2021 · 2 min · jiezi

关于grpc:Golang-gRPC实践-在gRPC中使用FlatBuffers编码

介绍gRPC默认应用Protocol Buffers编码,同时也反对其余编码如:JSON、FlatBuffers等。 FlatBuffers是一个跨平台的序列化库,旨在实现最大的内存效率。它容许您间接拜访序列化数据,而无需首先对其进行解析/解包,同时仍具备良好的向前/向后兼容性。 我的项目地址:https://github.com/google/flatbuffers FlatBuffers在解编码性能上要比Protocol Buffers快很多,这里有两篇具体介绍Protocol Buffers和FlatBuffers比照的文章: https://blog.csdn.net/chosen0ne/article/details/43033575https://juzii.gitee.io/2020/03/02/protobuf-vs-flatbuffer/ 这里有一篇文章具体介绍了FlatBuffers以及schema的编写: https://halfrost.com/flatbuffers_schema/ 这里次要来演示一下如何在gRPC中应用FlatBuffers. 编码编写fbs接口定义文件api/fbs/greeter.fbs namespace models;table HelloReply { message:string;}table HelloRequest { name:string;}table ManyHellosRequest { name:string; num_greetings:int;}rpc_service Greeter { SayHello(HelloRequest):HelloReply; SayManyHellos(ManyHellosRequest):HelloReply (streaming: "server");}这里的定义和protobuf差不多,应用table定义构造体,应用rcp_service定义接口。 这里定义了三个构造体用于数据发送和接管,定义了两个接口用于演示。 生成gRPC代码先装置flatc 从上面地址中下载对应版本的flatc即可 https://github.com/google/flatbuffers/releases/tag/v2.0.0 flatc --go --grpc -o api/ api/fbs/greeter.fbs参数阐明: --go 指定生成的语言是go--grpc 指定生成grpc代码-o 可选,指定要生成的指标文件目录前缀--go-namespace 可选,指定生成的包名,笼罩 fbs 文件中的定义会在指定目录下生成一个models目录,外面即是生成的代码,这个目录名就是fbs文件中定义的namespace,也能够通过参数'--go-namespace来笼罩这个值,以指定新的目录,如: flatc --go --go-namespace newmodels --grpc -o api/ api/fbs/greeter.fbs倡议通过fbs定义namespace,这个namespace也是Go文件的package名称。 生成的文件目录是这样的: ├── api│   ├── fbs│   │   └── greeter.fbs│   └── models│   ├── Greeter_grpc.go│   ├── HelloReply.go│   ├── HelloRequest.go│   └── ManyHellosRequest.go当初咱们能够编写gRPC的代码了。 ...

December 15, 2021 · 3 min · jiezi

关于grpc:基于内存通信的gRPC调用

Apache Dubbo 有injvm形式的通信,可能防止网络带来的提早,同时也不占用本地端口,对测试、本地验证而言,是一种比拟不便的RPC通信形式。 最近看到 containerd 的代码,发现它也有相似的需要。但应用ip端口通信,有可能会有端口抵触;应用unix socket,可能会有门路抵触。考查了下gRPC有没有和injvm相似的,基于内存的通信形式。起初发现pipe十分好用,所以记录了下。 Golang/gRPC对网络的形象首先,咱们先看一下gRPC一次调用的架构图。当然,这个架构图目前只关注了网络形象散布。 咱们重点关注网络局部。 操作系统零碎形象首先,在网络包之上,零碎形象进去了socket,代表一条虚构连贯,对于UDP,这个虚构连贯是不牢靠的,对于TCP,这个链接是尽力牢靠的。 对于网络编程而言,仅仅有连贯是不够的,还须要通知开发者如何创立、敞开连贯。对于服务端,零碎提供了accept办法,用来接管连贯。对于客户端,零碎提供了connect办法,用于和服务端建设连贯。 Golang形象在Golang中,socket对等的概念叫net.Conn,代表了一条虚构连贯。 接下来,对于服务端,accept这个行为被包装成了net.Listener接口;对于客户端,Golang则基于connect提供了net.Dial办法。 type Listener interface { // 接管来自客户端的网络连接 Accept() (Conn, error) Close() error Addr() Addr}gRPC应用那么gRPC是怎么应用Listener和Dial的呢? 对于gRPC服务端,Serve办法接管一个Listener,示意在这个Listener上提供服务。 对于gRPC客户端,网络实质上就是一个可能连贯到某个中央的货色就能够,所以只须要一个dialer func(context.Context, string) (net.Conn, error)函数就行了。 什么是pipe在操作系统层面,pipe示意一个数据管道,而这个管道两端都在本程序中,能够很好的满足咱们的要求:基于内存的网络通信。 Golang也基于pipe提供了net.Pipe()函数创立了一个双向的、基于内存通信的管道,在能力上,可能很好的满足gRPC对底层通信的要求。 然而net.Pipe仅仅产生了两个net.Conn,即只产生两个网络连接,没有之前提到的Listner,也没有Dial办法。 于是联合Golang的channel,把net.Pipe包装成了Listner,也提供了Dial办法: Listener.Accept(),只须要监听一个channel,客户端连贯过去的时候,把连贯通过channel传递过去即可Dial办法,调用Pipe,将一端通过channel给服务端(作为服务端连贯),另一端作为客户端连贯代码如下: package mainimport ( "context" "errors" "net" "sync" "sync/atomic")var ErrPipeListenerClosed = errors.New(`pipe listener already closed`)type PipeListener struct { ch chan net.Conn close chan struct{} done uint32 m sync.Mutex}func ListenPipe() *PipeListener { return &PipeListener{ ch: make(chan net.Conn), close: make(chan struct{}), }}// Accept 期待客户端连贯func (l *PipeListener) Accept() (c net.Conn, e error) { select { case c = <-l.ch: case <-l.close: e = ErrPipeListenerClosed } return}// Close 敞开 listener.func (l *PipeListener) Close() (e error) { if atomic.LoadUint32(&l.done) == 0 { l.m.Lock() defer l.m.Unlock() if l.done == 0 { defer atomic.StoreUint32(&l.done, 1) close(l.close) return } } e = ErrPipeListenerClosed return}// Addr 返回 listener 的地址func (l *PipeListener) Addr() net.Addr { return pipeAddr(0)}func (l *PipeListener) Dial(network, addr string) (net.Conn, error) { return l.DialContext(context.Background(), network, addr)}func (l *PipeListener) DialContext(ctx context.Context, network, addr string) (conn net.Conn, e error) { // PipeListener是否曾经敞开 if atomic.LoadUint32(&l.done) != 0 { e = ErrPipeListenerClosed return } // 创立pipe c0, c1 := net.Pipe() // 期待连贯传递到服务端接管 select { case <-ctx.Done(): e = ctx.Err() case l.ch <- c0: conn = c1 case <-l.close: c0.Close() c1.Close() e = ErrPipeListenerClosed } return}type pipeAddr intfunc (pipeAddr) Network() string { return `pipe`}func (pipeAddr) String() string { return `pipe`}如何用pipe作为gRPC的connection有了下面的包装,咱们就能够基于此创立一个gRPC的服务器端和客户端,来进行基于内存的RPC通信了。 ...

December 5, 2021 · 4 min · jiezi

关于grpc:gRPC-PHP与GO-数据增长性能测试与分析

前言大家好,我是CrazyCodes,最近调研了下对于PHP通过gRPC申请go,与PHP通过HTTP形式申请,依据数据量一直增长的状况下,均匀响应工夫会有多大差距。 一致性申明测试报告全副由开发机测试得出,未进行任何配置更改。 本机配置 3 GHz 六核Intel Core i516 GB 2400 MHz DDR4MacOsGo启动HTTP http.ListenAndServePHP启动HTTP php -S 127.0.0.1:8088Ab ab -c 200 -n 200 -k测试数据阐明Go作为服务端,是通过for间接迭代出的测试数据,没有任何其余多余操作 // 伪代码for i := 0; i < 2,20,200,2000,5000; i++ {}其余阐明 下方GPH[数字],代表gRPC + PHP + HTTP + 数据量 GPH 2返回数据集大小统一 gRPC Document Length≈87bytesHttp Document Length≈315bytesGo Grpc -> Go Grpc 均匀响应工夫为66.215ms Php Grpc -> Go Grpc 均匀响应工夫为190.551ms Php Http -> Go Http 均匀响应工夫为120.692ms GPH 20返回数据集大小统一 gRPC Document Length≈556bytesHttp Document Length≈2198bytesGo Grpc -> Go Grpc 均匀响应工夫为71.525ms ...

November 7, 2021 · 1 min · jiezi

关于grpc:GRPC-如何实现分布式日志跟踪

简介: 本文将介绍如何在 gRPC 分布式场景中,实现 API 的日志跟踪。 介绍本文将介绍如何在 gRPC 分布式场景中,实现 API 的日志追踪。 什么是 API 日志追踪?一个 API 申请会跨多个微服务,咱们心愿通过一个惟一的 ID 检索到整个链路的日志。 咱们将会应用 rk-boot 来启动 gRPC 服务。 请拜访如下地址获取残缺教程: https://rkdev.info/cnhttps://rkdocs.netlify.app/cn (备用) 装置go get github.com/rookie-ninja/rk-boot 疾速开始rk-boot 默认集成了 grpc-gateway,并且会默认启动。 咱们会创立 /api/v1/greeter API 进行验证,同时开启 logging, meta 和 tracing 拦截器以达到目标。 1. 创立 api/v1/greeter.protosyntax = "proto3";package api.v1;option go_package = "api/v1/greeter";service Greeter { rpc Greeter (GreeterRequest) returns (GreeterResponse) {}}message GreeterRequest { string name = 1;}message GreeterResponse { string message = 1;}2. 创立 api/v1/gw_mapping.yamltype: google.api.Serviceconfig_version: 3# Please refer google.api.Http in https://github.com/googleapis/googleapis/blob/master/google/api/http.proto file for details.http: rules: - selector: api.v1.Greeter.Greeter get: /api/v1/greeter3. 创立 buf.yamlversion: v1beta1name: github.com/rk-dev/rk-demobuild: roots: - api4. 创立 buf.gen.yamlversion: v1beta1plugins: # protoc-gen-go needs to be installed, generate go files based on proto files - name: go out: api/gen opt: - paths=source_relative # protoc-gen-go-grpc needs to be installed, generate grpc go files based on proto files - name: go-grpc out: api/gen opt: - paths=source_relative - require_unimplemented_servers=false # protoc-gen-grpc-gateway needs to be installed, generate grpc-gateway go files based on proto files - name: grpc-gateway out: api/gen opt: - paths=source_relative - grpc_api_configuration=api/v1/gw_mapping.yaml # protoc-gen-openapiv2 needs to be installed, generate swagger config files based on proto files - name: openapiv2 out: api/gen opt: - grpc_api_configuration=api/v1/gw_mapping.yaml5. 编译 proto file$ buf generate如下的文件会被创立。$ tree api/gen api/gen└── v1 ├── greeter.pb.go ├── greeter.pb.gw.go ├── greeter.swagger.json └── greeter_grpc.pb.go 1 directory, 4 files6. 创立 bootA.yaml & serverA.goServer-A 监听 1949 端口,并且发送申请给 Server-B。 ...

October 21, 2021 · 4 min · jiezi

关于grpc:GRPC-如何优雅关闭进程graceful-shutdown

简介: 本文将介绍优雅敞开 gRPC 微服务。在过程收到敞开信号时,咱们须要敞开后盾运行的逻辑,比方,MySQL 连贯等等。 介绍本文将介绍优雅敞开 gRPC 微服务。 什么是优雅敞开? 在过程收到敞开信号时,咱们须要敞开后盾运行的逻辑,比方,MySQL 连贯等等。 咱们将会应用 rk-boot 来启动 gRPC 服务。 请拜访如下地址获取残缺教程: https://rkdev.info/cnhttps://rkdocs.netlify.app/cn (备用)装置go get github.com/rookie-ninja/rk-boot 疾速开始1.创立 boot.yaml---grpc: - name: greeter # Name of grpc entry port: 8080 # Port of grpc entry enabled: true # Enable grpc entry2.创立 main.go通过 AddShutdownHookFunc() 来增加 shutdownhook 函数。 package mainimport ( "context" "github.com/rookie-ninja/rk-boot" "github.com/rookie-ninja/rk-gin/interceptor/context")// Application entrance.func main() { // Create a new boot instance. boot := rkboot.NewBoot() // Add shutdown hook function boot.AddShutdownHookFunc("shutdown-hook", func() { fmt.Println("shutting down") }) // Bootstrap boot.Bootstrap(context.Background()) // Wait for shutdown sig boot.WaitForShutdownSig(context.Background())}3.启动 main.go$ go run main.go ...

October 21, 2021 · 1 min · jiezi

关于grpc:Grpc对象转proto代码工具

Grpc对象转proto代码工具尽管Grpc.Tools能够将proto文件主动生成代理类,然而proto文件得手敲,还容易出错,如果接口比较复杂,定义比拟多,这就很头疼了 为了解决这个问题Class2Proto诞生了,应用规范C#对象转换成proto文件,不论是新写的接口,还是老的API接口转Grpc,都没问题 <u>装置nuget包:CRL.Class2Proto</u><u>using CRL.Class2Proto;</u>定义标准接口代码 [ProtoServiceAttribute("protoTest", "ClassTestAction")]public interface ClassTestAction{ ClassTest getObj(TestObj a); //ClassTest getObj2(TestObj a); Request getObj3(TestObj2<Request> a); TestObj2<List<Request>> getObj4(TestObj2<List<Request>> a);}运行转换方法生成proto文件 var convertInfo = ClassConvert.Convert(System.Reflection.Assembly.GetAssembly(typeof(ClassTest)));convertInfo.ForEach(b => b.CreateCode());程序目录Protos成生了protoTest.proto文件 syntax = "proto3";option csharp_namespace = "gRPC.gRpcClient.protoTest";package protoTest;service ClassTestAction { rpc getObj(TestObjDTO) returns (ClassTestDTO); rpc getObj3(TestObj2_RequestDTO) returns (RequestDTO); rpc getObj4(TestObj2_l_RequestDTO) returns (TestObj2_l_RequestDTO);}message StatusDTO { ok = 0; fail = 1;}message TestObjDTO { string Id = 1;}message ClassTestDTO { string Name = 1; int32 nullableValue = 2; StatusDTO Status = 3; TestObjDTO Data = 4; repeated string Name2 = 5; repeated TestObjDTO Data2 = 6; map<string, TestObjDTO> Data3 = 7; string time = 8; double decimalValue = 9; string Id = 10;}message RequestDTO {}message TestObj2_RequestDTO { string Id = 1; RequestDTO data = 2;}message TestObj2_l_RequestDTO { string Id = 1; repeated RequestDTO data = 2;}

July 2, 2021 · 1 min · jiezi

关于grpc:gRPCgo源码剖析与实战专栏介绍

1、背景介绍无论是分布式架构,还是微服务架构,服务之间如何高效、可靠性的通信是具备肯定的挑战性的;那么,解决方案之一就是应用RPC通信;而由google开发的grpc-go是一个高性能、开源和通用的RPC框架,面向挪动和HTTP2设计。在波及到网络通信的畛域内,常常能够看到grpc-go的身影;目前,曾经在Kubernetes、Docker、Istio等优良开源框架中广泛应用;因而,有必要对grpc-go的外围原理进行深刻理解,以便可能更好的为微服务架构,分布式架构提供平安、高效、牢靠的网络通信服务。 2、专栏解读:本专栏十分具体的介绍了grpc-go的源码,编写了近100篇文章来帮忙大家理解每个性能的外围原理; 浏览本专栏不仅能够理解到grpc-go的外围原理;如,如何建设rpc连贯,rpc申请,滑动窗口(流量管制)原理,如何自定义平衡器,解析器,重试机制,加密,认证,如何让grpc-go反对lz4算法等等;如何基于wireshark进行抓包剖析; 还能够从grpc-go源码中学到很多技巧:如,grpc-go框架是如何应用事件机制的,如何应用上下文的?如何比拟两个数的大小?当某个步骤失败时如何对本步骤以及后面的步骤实现重试机制等等,对go语言的进步有很大的帮忙; 本专栏最大的目标,心愿您学完之后,该技术可能成为您的技术亮点;比方,在求职简历上能够表明熟读grpc-go源码,具备二次开发grpc-go源码的能力;通过本人的致力后,甚至能够称为grpc-go框架的源码贡献者;也是证实你go语言程度的一种能力;心愿这一项技术可能称为您简历上的亮点之一。 3、专栏次要分为以下几个模块3.1.模块1:grpc客户端是如何向grpc服务器端建设起rpc连贯的本模块次要介绍rpc链接建设阶段所波及到的内容;次要波及到的内容: grpc客户端跟grpc服务器端整个交互过程次要经验过那些阶段;如何建设起的tcp链接?如果rpc链接失败后,grpc客户端是否尝试重试链接,每次连贯的间隔时间是如何设置的;等等 3.2.模块2:解析器原理以及实际介绍本模块介绍解析器相干原理;次要波及到的内容: 解析器是用来做什么的?如何实现一个解析器,如何注册一个解析器?对grpc框架内置的解析器介绍?如何应用consul来自定义解析器; 3.3.模块3:平衡器原理以及实际介绍本模块介绍平衡器相干原理;次要波及到的内容: 平衡器次要实现什么性能?客户端如何指定应用哪个平衡器呢?如何实现、注册、创立平衡器?pickFirst平衡器介绍、round_robin平衡器介绍、grpc+LoadBalancer平衡器介绍、grpc+nginx平衡器介绍、grpc+consul自定义解析器介绍等等; 3.4.模块4:grpc客户端是如何向grpc服务器端发动rpc申请的以及解决流程本模块介绍的原理以及流程:是在rpc建设连贯根底之上的;也就是rpc连贯建设后,客户端如何调用本地办法,服务器端如何接管申请,执行申请,反馈执行后果给客户端。次要波及到的内容: 在rpc申请阶段,次要经验了哪些过程;在客户端一侧,什么场景下,会触发流的创立?客户端是如何将申请服务的名称,办法名称等信息告诉服务器端的?以及如何将申请办法的参数值发送给服务器端的?客户端一侧,接管服务器端反馈执行后果的流程?grpc服务器端一侧,解决客户端的整体流程?grpc服务器端对客户端的每次tcp申请,是单协程解决,还是多协程解决?等等; 3.5.模块5:帧接管原理介绍本模块介绍帧接收器相干原理;每次客户端跟服务器端建设rpc连贯后,就会创立各自的帧接收器,专门用来接管对方发送的帧;次要内容波及到: grpc客户端帧接收器的原理介绍;grpc客户端帧接收器是如何解决不同的帧的?grpc服务器端帧接收器的原理介绍;服务器端接管到客户端的头帧后,如何解决?等等 3.6.模块6:帧发送器原理介绍本模块介绍帧发送器相干原理;每次客户端跟服务器端建设rpc连贯后,就会创立各自的帧发送器,专门用来将各种类型的帧发送给对方;客户端跟服务器端的帧发送器原理是一样的。次要内容波及到: 帧发送器的整体流程介绍;如何将帧数据再内存里进行存储和读取?在同一个过程里如何应用告诉的形式生产数据帧?帧发送器解决帧时有什么特点?帧发送器的根本流程介绍?为什么源码作者将帧发送器的代码如何设计?客户端一侧,头帧的整体解决流程介绍?等等 3.7.模块7:滑动窗口(流量管制)本模块次要介绍grpc服务器端在接管客户端发送的数据帧时,是如何动态控制客户端发送的数据帧大小的;也能够称之为流量管制。波及到的内容: 滑动窗口的整体流程介绍;帧发送器是如何将数据帧发送给服务器端的;在服务器端一侧,帧接收器是如何将接管到的数据帧存储到本地的?数据帧缓存到recvBuffer前的流控?从recvBuffer里读取数据时,如何调整流控指标?服务器端在真正执行客户端的申请办法时,是如何残缺的读取到申请参数值的?如何计算本次发送的数据帧的大小?服务器端发送的设置帧、窗口更新帧是如何影响客户端的帧大小的?基于wireshark对grpc进行抓包剖析:如何配置wireshark可能抓取grpc包,多场景抓包测试用例剖析;等等 3.8.模块8:勾销性能cancellation本模块对grpc框架中的勾销性能cancellation进行介绍;波及到的内容: 勾销cancellation性能的基本原理介绍;勾销性能实现什么成果?客户端一侧,是如何解决勾销性能的?服务器端一侧,是如何解决勾销性能的?等等 3.9.模块9:截止工夫deadline本模块对grpc框架中的截止工夫Deadline进行介绍;波及到的内容: 截止工夫deadline跟勾销性能cancellation的区别?截止工夫能够产生在哪些阶段?客户端一侧,如何配置启动截止工夫?客户端一侧,当截止工夫产生在不同的阶段,每个阶段是如何解决的?服务器端一侧,是如何解决截止工夫的?等等 3.10.模块10:衰弱检测health本模块对grpc框架中的衰弱检测HealthChecking进行介绍;波及到的内容: 客户端是如何断定服务器端的服务的衰弱状态的?衰弱检测原理的整体流程图介绍;客户端的链接状态时如何更新为Ready状态的?客户端如何判断服务器端的服务可能失常提供服务呢?服务器端衰弱检测Watcher的核心思想?衰弱检测运行时场景的异样场景解决?等等 3.11.模块11:拦截器interceptor本模块对grpc框架中的拦截器interceptor原理进行介绍;波及到的内容: 拦截器的分类;客户端如何应用拦截器;服务器端如何应用拦截器;服务器端何时触发拦截器流程?客户端何时触发拦截器流程?拦截器的调度策略,即拦截器的执行过程?等等 3.12.模块12:放弃链接keepalive本模块对grpc框架中的放弃链接keepalive原理进行介绍;波及到的内容: 放弃链接跟衰弱检测的区别?服务器端keepalive的原理图?服务器端何时触发keepalive性能的启动?当链接处于不同状态时,服务器端如何解决链接?客户端一侧keepalive的原理图?服务器端跟客户端交互解决goAway帧的流程图;等等 3.13.模块13:多路复用multiplex本模块对grpc框架中多路复用multiplex原理进行介绍;波及到的内容: grpc采矿机是如何实现多路复用的;实现多路复用的基本思路?grpc框架中,服务器端是如何辨别不同的服务申请的?一个服务器申请,可能存在多个http2数据帧,服务器端是如何存储不同服务申请的http2数据帧的?如何按顺序存储?如何按程序读取数据帧呢?等等 3.14.模块14:压缩个性compression本模块对grpc框架中压缩个性compression原理进行介绍;波及到的内容: grpc框架是如何反对不同的压缩算法的?如何启动压缩个性?如何注册一个压缩算法?客户端一侧,压缩解决数据的原理?服务器端一侧,压缩解决数据的原理?在客户端一侧,发送数据阶段,压缩器压缩数据的解决流程?在服务器端一侧,如何获取客户端采纳的压缩算法呢?如何解压数据?如何让grpc框架反对lz4压缩算法呢?等等 3.15.模块15:重试机制retry本模块对grpc框架中的重试机制retry原理进行介绍;波及到的内容: 在grpc框架中什么场景下应用了重试机制?客户端一侧,如何启动、禁止重试机制性能?如何定义、应用重试策略呢?grpc框架中,实现重试机制的主体思路?重试机制withRetry实现形式的特点?客户端是如何判断是不是容许重试呢?如果某个阶段失败了,客户端如何重试前几步的操作呢?等等 3.16.模块16:元数据metadata本模块对grpc框架中的元数据metadata原理进行介绍;波及到的内容: 客户端一侧,如何创立元数据?如何应用创立的元数据?如何将元数据信息存储到上下文中的呢?服务器端一侧,是如何接管元数据的?如何让服务端提供的服务中有能力应用元数据;等等 3.17.模块17:加密encryption本模块对grpc框架中加密encryption原理进行介绍;波及到的内容: grpc框架反对哪几种加密?基于tls证书加密的测试用例介绍?基于ca的tls证书加密测试用例介绍?链路建设阶段时的tls加密原理介绍:客户端一侧,加密原理介绍?服务器端加密原理介绍,介绍时配合wireshark抓包剖析;如何配置wireshark可能抓取tls包?整体握手过程原理介绍;数据帧发送阶段的tls加密介绍;如何应用在tls链路建设阶段单方协商好的数据作为加密数据的?应用协商好的加密数据开始对数据帧进行加密?等等 3.18.模块18:认证authentication本模块对grpc框架中的认证authentication原理进行介绍;波及到的内容: 如何了解认证?grpc+oauth2.Token认证形式介绍:如何配置认证?认证性能属于链路级别性能,还是属于流级别性能?服务器端一侧是如何获取认证信息的?如何来验证认证信息的?grpc+自定义认证形式介绍;grpc+Basic认证形式介绍:basic认证形式介绍;模仿Basic认证测试用例介绍;grpc+jwt认证形式介绍:jwt认证介绍;jwt-grpc-go认证测试用例介绍;客户端向服务器端发动受权码申请?如何具体校验jwt?如何生成jwt?等等 4、合适人群go语言研发工程师、go语言爱好者微服务研发工程师kubernetes开发工程师docker开发工程师中间件研发工程师计算机相关业余的大学生、研究生对grpc-go感兴趣的爱好者等

May 13, 2021 · 1 min · jiezi

关于grpc:grpc通过-etcd-实现服务发现与注册源码分析

介绍上面介绍 jupiter-0.2.7 版本中 grpc 通过 etcd 实现服务发现与注册。 服务发现与注册的实现解析服务注册服务注册的流程图: etcd的服务注册代码模块在 jupiter/pkg/registry/etcdv3 中。 上面让咱们来看看理论的代码 // Registry register/unregister service// registry impl should control rpc timeouttype Registry interface { RegisterService(context.Context, *server.ServiceInfo) error UnregisterService(context.Context, *server.ServiceInfo) error ListServices(context.Context, string, string) ([]*server.ServiceInfo, error) WatchServices(context.Context, string, string) (chan Endpoints, error) io.Closer}在 pkg/registry/registry.go 中定义了注册服务对象的接口。不同的服务只有实现了这些接口,jupiter 就能应用。 首先咱们来看看注册办法 // RegisterService register service to registryfunc (reg *etcdv3Registry) RegisterService(ctx context.Context, info *server.ServiceInfo) error { err := reg.registerBiz(ctx, info) ...}// 业务信息注册func (reg *etcdv3Registry) registerBiz(ctx context.Context, info *server.ServiceInfo) error { ... // 提交信息到 etcd _, err := reg.client.Put(readCtx, key, val, opOptions...) ...}这里次要的局部是 reg.client.Put()  将服务信息提交到 etcd 中。其中的租约机制我会在之后独自写一篇文章介绍。这里次要还是关注如何注册。源码中还有个 registerMetric() 办法,这个办法的目标是将服务信息在提交到etcd的 prometheus 前缀目录下,用于服务监控,用的也是 client.Put() 办法。这里具体就不展现代码了,感兴趣的同学能够去源码库中查看。 ...

December 5, 2020 · 3 min · jiezi

关于grpc:grpcnode-源码阅读笔记0

简略介绍 gRPC贴一张挂在官网的图片:https://grpc.io/docs/what-is-... 能够了解 gRPC 是 RPC(近程过程调用)框架的一种实现,对于 RPC 的介绍因为并不是本次的主题,所以放个链接来帮忙大家了解:https://www.zhihu.com/questio... 我所了解 RPC 整个执行的过程就是 Client 调用办法 -> 序列化申请参数 -> 传输数据 -> 反序列化申请参数 -> Server 解决申请 -> 序列化返回数据 -> 传输数据 -> Client 接管到办法返回值: 其次要逻辑会集中在 数据的序列化/反序列化 以及 数据的传输上,而这两项 gRPC 别离选用了 Protocol Buffers 和 HTTP2 来作为默认选项。 gRPC 在 Node.js 的实现gRPC 在 Node.js 的实现上一共有两个官网版本,一个是基于 c++ addon 的版本,另一个是纯 JS 实现的版本。 gRPC 在 Node.js 中相干的模块除了上边提到的两个 gRPC 的实现,在 Node.js 中还存在一些其余的模块用来辅助应用 gRPC。 grpc-tools 这个是每个语言都会用的,用来依据 proto 文件生成对应,插件提供了 Node.js 语言的实现proto-loader 用来动静加载 proto 文件,不须要应用 grpc_tools 提前生成代码(性能比上边的形式稍差)这次笔记次要是针对 grpc-node 形式的实现,在 c++ addon 模块的实现下,并不是一个 gRPC 的残缺实现,做的事件更多的是一个连接的工作,通过 JS、c++ 两层封装将 c++ 版本的 gRPC 能力裸露进去供用户应用。 ...

December 1, 2020 · 7 min · jiezi

用Golang构建gRPC服务

本教程提供了Go使用gRPC的基础教程 在教程中你将会学到如何: 在.proto文件中定义一个服务。使用protocol buffer编译器生成客户端和服务端代码。使用gRPC的Go API为你的服务写一个客户端和服务器。继续之前,请确保你已经对gRPC概念有所了解,并且熟悉protocol buffer。需要注意的是教程中的示例使用的是proto3版本的protocol buffer:你可以在Protobuf语言指南与Protobuf生成Go代码指南中了解到更多相关知识。 为什么使用gRPC我们的示例是一个简单的路线图应用,客户端可以获取路线特征信息、创建他们的路线摘要,还可以与服务器或者其他客户端交换比如交通状态更新这样的路线信息。 借助gRPC,我们可以在.proto文件中定义我们的服务,并以gRPC支持的任何语言来实现客户端和服务器,客户端和服务器又可以在从服务器到你自己的平板电脑的各种环境中运行-gRPC还会为你解决所有不同语言和环境之间通信的复杂性。我们还获得了使用protocol buffer的所有优点,包括有效的序列化(速度和体积两方面都比JSON更有效率),简单的IDL(接口定义语言)和轻松的接口更新。 安装安装grpc包首先需要安装gRPC golang版本的软件包,同时官方软件包的examples目录里就包含了教程中示例路线图应用的代码。 $ go get google.golang.org/grpc然后切换到`grpc-go/examples/route_guide:`目录: $ cd $GOPATH/src/google.golang.org/grpc/examples/route_guide安装相关工具和插件安装protocol buffer编译器安装编译器最简单的方式是去https://github.com/protocolbu... 下载预编译好的protoc二进制文件,仓库中可以找到每个平台对应的编译器二进制文件。这里我们以Mac Os为例,从https://github.com/protocolbu... 下载并解压文件。 更新PATH系统变量,或者确保protoc放在了PATH包含的目录中了。 安装protoc编译器插件$ go get -u github.com/golang/protobuf/protoc-gen-go编译器插件protoc-gen-go将安装在$GOBIN中,默认位于$GOPATH/bin。编译器protoc必须在$PATH中能找到它: $ export PATH=$PATH:$GOPATH/bin定义服务首先第一步是使用protocol buffer定义gRPC服务还有方法的请求和响应类型,你可以在下载的示例代码examples/route_guide/routeguide/route_guide.proto中看到完整的.proto文件。 要定义服务,你需要在.proto文件中指定一个具名的service service RouteGuide { ...}然后在服务定义中再来定义rpc方法,指定他们的请求和响应类型。gRPC允许定义四种类型的服务方法,这四种服务方法都会应用到我们的RouteGuide服务中。 一个简单的RPC,客户端使用存根将请求发送到服务器,然后等待响应返回,就像普通的函数调用一样。// 获得给定位置的特征rpc GetFeature(Point) returns (Feature) {}服务器端流式RPC,客户端向服务器发送请求,并获取流以读取回一系列消息。客户端从返回的流中读取,直到没有更多消息为止。如我们的示例所示,可以通过将<font color="red">stream</font>关键字放在响应类型之前来指定服务器端流方法。//获得给定Rectangle中可用的特征。结果是//流式传输而不是立即返回//因为矩形可能会覆盖较大的区域并包含大量特征。rpc ListFeatures(Rectangle) returns (stream Feature) {}客户端流式RPC,其中客户端使用gRPC提供的流写入一系列消息并将其发送到服务器。客户端写完消息后,它将等待服务器读取所有消息并返回其响应。通过将<font color="red">stream</font>关键字放在请求类型之前,可以指定客户端流方法。// 接收路线上被穿过的一系列点位, 当行程结束时// 服务端会返回一个RouteSummary类型的消息.rpc RecordRoute(stream Point) returns (RouteSummary) {}双向流式RPC,双方都使用读写流发送一系列消息。这两个流是独立运行的,因此客户端和服务器可以按照自己喜欢的顺序进行读写:例如,服务器可以在写响应之前等待接收所有客户端消息,或者可以先读取消息再写入消息,或其他一些读写组合。每个流中的消息顺序都会保留。您可以通过在请求和响应之前都放置<font color="red">stream</font>关键字来指定这种类型的方法。//接收路线行进中发送过来的一系列RouteNotes类型的消息,同时也接收其他RouteNotes(例如:来自其他用户)rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}我们的.proto文件中也需要所有请求和响应类型的protocol buffer消息类型定义。比如说下面的Point消息类型: // Points被表示为E7表示形式中的经度-纬度对。//(度数乘以10 ** 7并四舍五入为最接近的整数)。// 纬度应在+/- 90度范围内,而经度应在// 范围+/- 180度(含)message Point { int32 latitude = 1; int32 longitude = 2;}生成客户端和服务端代码接下来要从我们的.proto服务定义生成gRPC客户端和服务端的接口。我们使用protoc编译器和上面安装的编译器插件来完成这些工作: ...

October 6, 2019 · 4 min · jiezi

分布式服务框架gRPC

什么是gRPCgRPC是Google开发的高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于Protobuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。在gRPC中一个客户端可以像使用本地对象那样直接调用位于不同机器上的服务端应用的方法(methods)。这让你能够更容易的构建分布式的应用和服务。和其他RPC系统类似,gRPC也是基于定义一个服务,指定服务可以被远程调用的方法以及他们的参数和返回类型。在服务端,实现服务的接口然后运行一个gRPC服务来处理可出端的请求。在客户端,客户端拥有一个存根(stub在某些语言中仅称为客户端),提供与服务器相同的方法。 ·gRPC客户端和服务器可以在各种环境中运行并相互通信,并且可以使用gRPC支持的任何语言编写。因此,例如,您可以使用Go,Python或Ruby的客户端轻松地用Java创建gRPC服务器。此外,最新的Google API的接口将拥有gRPC版本,可让您轻松地在应用程序中内置Google功能。 使用protocol buffer默认情况下,gRPC使用protocol buffer,用于序列化结构化数据(尽管它可以与其他数据格式(例如JSON)一起使用)。使用协议缓冲区的第一步是在proto文件中为要序列化的数据定义结构:proto文件扩展名为.proto的普通文本文件。protocol buffer数据被构造为消息,其中每个消息都是信息的逻辑记录,其中包含一系列称为字段的名称/值对。这是一个简单的示例: message Person { string name = 1; int32 id = 2; bool has_ponycopter = 3;}定义了数据结构后,就可以使用protocol buffer编译器protoc生成你所选语言的数据访问类。访问类为每个字段提供了简单的访问器(例如name())和set_name()),以及将整个结构序列化为原始字节或从原始字节中解析出整个结构的方法-例如,如果您选择的语言是C ++,则在上面的示例将生成一个名为Person的类。然后,您可以在应用程序中使用此类来填充,序列化和检索Person的protocol buffer消息。 除此之外你还要在.proto件中定义gRPC服务,并将RPC方法参数和返回类型指定为protocol buffer消息: // The greeter service definition.service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {}}// The request message containing the user's name.message HelloRequest { string name = 1;}// The response message containing the greetingsmessage HelloReply { string message = 1;}gRPC使用也是使用编译器protoc从proto文件生成代码,不过编译器要首先安装一个gRPC插件。使用gRPC插件,你可以获得生成的gRPC客户端和服务器代码,以及用于填充,序列化和检索消息类型的常规protocol buffer访问类代码。 ...

October 4, 2019 · 1 min · jiezi

gRPC入坑记

概要由于gRPC主要是谷歌开发的,由于一些已知的原因,gRPC跑demo还是不那么顺利的。单独写这一篇,主要是gRPC安装过程中的坑太多了,记录下来让大家少走弯路。 主要的坑: 如果使用PHP、Python开发gRPC的客户端,需要编译gRPC命令行工具,生成proto的代码生成插件,否则proto里定义的service无法编译出来。编译需要使用GCC4.8级以上版本,否则报不支持C++11。然后需要龟速下周grpc源码,并下载一大堆第三方依赖。这个过程非常痛苦。使用golang、java的可以忽略。PHP还需要按照grpc的c扩展。编译需要使用GCC4.8级以上版本。如果使用golang开发服务,依赖的第三方服务基本是下载不下来的,需要使用go mod增加映射规则到github仓库,github下载也是龟速。本文讲解gRPC demo的同时,会介绍如何解决这些坑。本文对应的Github地址:https://github.com/52fhy/grpc... 。该仓库存储了demo示例,以及部分系统编译好的二进制包,大家觉得有些步骤里耗时实在太长了,可以直接clone该仓库,复制二进制包到对应目录(仅限测试开发,生产环境还是老老实实自己编译吧)。 升级GCCgRPC命令行工具编译需要使用 GCC4.8及以上版本。CentOS6系列的内置版本是GCC4.7。 使用gcc --version可以查看版本。如果你的系统GCC版本>=4.8,可以忽略本节。如果仅使用golang、java,请忽略本节。 注:不建议大家下载GCC源码包或者使用yum下载GCC4.8及以上版本,原因:1) 源码包安装真的是非常非常的慢 2) yum 源下载速度慢的像蜗牛。下面的SCL安装方法是推荐大家用的,安装好后原来的版本还能用。如果需要升级gcc至4.8或更高版本,建议直接采用安装SCL源之后安装devtoolset-6(devtoolset-6目前gcc版本为6.3),因为devtoolset-4及之前的版本都已经结束支持,只能通过其他方法安装。 升级到gcc 6.3: yum -y install centos-release-sclyum -y install devtoolset-6-gcc devtoolset-6-gcc-c++ devtoolset-6-binutilsscl enable devtoolset-6 bash需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。如果要长期使用gcc 6.3的话: echo "source /opt/rh/devtoolset-6/enable" >>/etc/profile这样退出shell重新打开就是新版的gcc了。其它版本同理。 升级到gcc 7.3: yum -y install centos-release-sclyum -y install devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-binutilsscl enable devtoolset-7 bash已经停止支持的devtoolset4(gcc 5.2)及之前版本的安装方法,可能比较慢,大家感兴趣的话可以尝试。 升级到gcc 4.8:wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtoolset-2.repoyum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutilsscl enable devtoolset-2 bash升级到gcc4.9:wget https://copr.fedoraproject.org/coprs/rhscl/devtoolset-3/repo/epel-6/rhscl-devtoolset-3-epel-6.repo -O /etc/yum.repos.d/devtoolset-3.repoyum -y install devtoolset-3-gcc devtoolset-3-gcc-c++ devtoolset-3-binutilsscl enable devtoolset-3 bash升级到gcc 5.2:wget https://copr.fedoraproject.org/coprs/hhorak/devtoolset-4-rebuild-bootstrap/repo/epel-6/hhorak-devtoolset-4-rebuild-bootstrap-epel-6.repo -O /etc/yum.repos.d/devtoolset-4.repoyum install devtoolset-4-gcc devtoolset-4-gcc-c++ devtoolset-4-binutils -yscl enable devtoolset-4 bash编译gRPC命令行工具如果仅使用golang、java,请忽略本节。gRPC分C、JAVA、GO、NodeJS版本,C版本包括C++, Python, Ruby, Objective-C, PHP, C#,这些语言都是基于C版本开发的,共用代码库一个代码库。 ...

July 7, 2019 · 3 min · jiezi

记一次技术调研二-Android-应用实现-gRPC-调用

问题在手机应用的开发中,通常会将复杂的业务逻辑层实现放在服务端,客户端仅负责表现层。但是对于某些手机应用而言,业务逻辑的实现位于服务端反而是不安全的或是不合理的,而是需要将其逻辑直接在手机端实现。 目的 面对不同系统的手机客户端,单独重复实现相同的业务逻辑,并非最佳实践。如何通过第三方语言 Go 语言将业务逻辑封装成库的形式,并以静态打包的方式提供给不同系统的手机客户端使用,是本次调研的目的。 理想目标图: 具体调研内容包括: [x] iOS 应用实现 gRPC 调用[x] Android 应用实现 gRPC 调用[x] GoMobile SDK 在 iOS & Android 上的集成[x] GoMobile SDK 在 iOS & Android 上的边界[ ] C/S 架构 or 静态库其中关于 gRPC 在 iOS 与 Android 的实现,本身官方就已经提供了样例。本次调研会用到相关内容,所以将其作为调研的一部分记录下来,方便后来者阅读。调研中所有涉及的项目代码均存放于: liujianping/grpc-apps 仓库中, 需要的朋友可以直接下载测试。 更多最新文章请关注个人站点:GitDiG.com, 原文地址:Android 应用实现 gRPC 调用。1. 环境安装保证整个过程在"全球通"的环境下操作。 1.1 JDK 安装没什么好说的,下载安装。设置好相应的环境变量。完成后,通过 java -version 命令确认安装成功。 1.2 Android Studio 安装官网下载安装包,按步骤安装即可。 2. 开启服务端请参考iOS 应用实现 gRPC 调用服务端部分, 确认服务端服务启动。 ...

July 6, 2019 · 3 min · jiezi

记一次技术调研一-iOS-应用实现-gRPC-调用

问题在手机应用的开发中,通常会将复杂的业务逻辑层实现放在服务端,客户端仅负责表现层。但是对于某些手机应用而言,业务逻辑的实现位于服务端反而是不安全的或是不合理的,而是需要将其逻辑直接在手机端实现。 目的 面对不同系统的手机客户端,单独重复实现相同的业务逻辑,并非最佳实践。如何通过第三方语言 Go 语言将业务逻辑封装成库的形式,并以静态打包的方式提供给不同系统的手机客户端使用,是本次调研的目的。 理想目标图: 具体调研内容包括: [x] iOS 应用实现 gRPC 调用[x] Android 应用实现 gRPC 调用[ ] GoMobile SDK 在 iOS & Android 上的集成[ ] GoMobile SDK 在 iOS & Android 上的边界[ ] C/S 架构 or 静态库其中关于 gRPC 在 iOS 与 Android 的实现,本身官方就已经提供了样例。本次调研会用到相关内容,所以将其作为调研的一部分记录下来,方便后来者阅读。调研中所有涉及的项目代码均存放于: liujianping/grpc-apps 仓库中, 需要的朋友可以直接下载测试。 原文发布在我的个人站点: GitDiG.com. 原文链接:iOS 应用实现 gRPC 调用 .1. 环境安装作为一名非专职 iOS 的程序员,经常需要调研陌生的技术或者语言。首先是要克服对于未知的畏惧心理。其实很多东西没那么难,只是需要开始而已。 为了完成目标调研,开始第一部分的调研工作。以文字形式记录下来,方便后来者。 1.1 XCode 安装没什么好说的,直接 AppStore 下载安装。有点慢,一边下载一边准备其它环境。 1.2 Cocoapod 安装类似与其它语言的第三方库管理工具。也没什么好说的,登录官网,按说明安装。 ...

July 4, 2019 · 3 min · jiezi

grpc的Go服务端和PHP客户端实现

前言gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持. gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。本例系统为 CentOS Linux release 7.5.1804 (Core) ,具体实现如下: 安装GO(已安装跳过)1、安装yum 源 yum install epel -y2、然后使用 yum 安装 Golang: yum install go -y查看版本 go version#go version go1.9.4 linux/amd643、配置环境变量在 /etc/profile 添加: export GOPATH=/home/goexport PATH=$PATH:$GOPATH/bin然后执行 source /etc/profile 使之生效,创建GOPATH目录 mkdir /home/go安装protobuf1、安装相关软件协议编译器是用C ++编写的。如果您使用的是C ++,请按照C ++安装说明在C ++运行时安装protoc。 ...

July 4, 2019 · 5 min · jiezi

浅谈-gRPC

原文地址:浅谈 gRPC gRPC 在 Go 语言中大放异彩,越来越多的小伙伴在使用,最近也在公司安利了一波,希望能通过这篇文章能带你一览 gRPC 的爱与恨。本文篇幅较长,希望你做好阅读准备,目录如下: 简述gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持。 gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特性。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。 调用模型 1、客户端(gRPC Stub)调用 A 方法,发起 RPC 调用。 2、对请求信息使用 Protobuf 进行对象序列化压缩(IDL)。 3、服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回。 4、对响应结果使用 Protobuf 进行对象序列化压缩(IDL)。 5、客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果。 调用方式一、Unary RPC:一元 RPC Servertype SearchService struct{}func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil}const PORT = "9001"func main() { server := grpc.NewServer() pb.RegisterSearchServiceServer(server, &SearchService{}) lis, err := net.Listen("tcp", ":"+PORT) ... server.Serve(lis)}创建 gRPC Server 对象,你可以理解为它是 Server 端的抽象对象。将 SearchService(其包含需要被调用的服务端接口)注册到 gRPC Server。 的内部注册中心。这样可以在接受到请求时,通过内部的 “服务发现”,发现该服务端接口并转接进行逻辑处理。创建 Listen,监听 TCP 端口。gRPC Server 开始 lis.Accept,直到 Stop 或 GracefulStop。Clientfunc main() { conn, err := grpc.Dial(":"+PORT, grpc.WithInsecure()) ... defer conn.Close() client := pb.NewSearchServiceClient(conn) resp, err := client.Search(context.Background(), &pb.SearchRequest{ Request: "gRPC", }) ...}创建与给定目标(服务端)的连接句柄。创建 SearchService 的客户端对象。发送 RPC 请求,等待同步响应,得到回调后返回响应结果。二、Server-side streaming RPC:服务端流式 RPC ...

June 29, 2019 · 8 min · jiezi

gRPCgRPC-Gateway-能不能不用证书

如果你以前有涉猎过 gRPC+gRPC Gateway 这两个组件,你肯定会遇到这个问题,就是 “为什么非得开 TLS,才能够实现同端口双流量,能不能不开?” 又或是 “我不想用证书就实现这些功能,行不行?”。我被无数的人问过无数次这些问题,也说服过很多人,但说服归说服,不代表放弃。前年不行,不代表今年不行,在今天我希望分享来龙去脉和具体的实现方式给你。 原文地址:gRPC+gRPC Gateway 能不能不用证书? 过去为什么 h2 不行因为 net/http2 仅支持 "h2" 标识,而 "h2" 标识 HTTP/2 必须使用传输层安全性(TLS)的协议,此标识符用于 TLS 应用层协议协商字段以及识别 HTTP/2 over TLS。 简单来讲,也就 net/http2 必须使用 TLS 来交互。通俗来讲就要用证书,那么理所当然,也就无法支持非 TLS 的情况了。 寻找 h2c那这条路不行,我们再想想别的路?那就是 HTTP/2 规范中的 "h2c" 标识了,"h2c" 标识允许通过明文 TCP 运行 HTTP/2 的协议,此标识符用于 HTTP/1.1 升级标头字段以及标识 HTTP/2 over TCP。 但是这条路,早在 2015 年就已经有在 issue 中进行讨论,当时 @bradfitz 明确表示 “不打算支持 h2c,对仅支持 TLS 的情况非常满意,一年后再问我一次”,原文回复如下: We do not plan to support h2c. I don't want to receive bug reports from users who get bitten by transparent proxies messing with h2c. Also, until there's widespread browser support, it's not interesting. I am also not interested in being the chicken or the egg to get browser support going. I'm very happy with the TLS-only situation, and things like https://LetsEncrypt.org/ will make TLS much easier (and automatic) soon.Ask me again in one year. ...

June 22, 2019 · 2 min · jiezi

gRPC的PHP客户端

工作中需要使用gRPC,服务端采用的python,客户端采用PHP。这里主要讲述PHP客户端。 分为以下几个部分: 安装protoc生成protobuf安装PHP扩展定义客户端安装protoc这里是mac环境 下载地址:https://github.com/protocolbu... 解压之后进入目录,执行./autogen.sh如果报错的话需要安装插件brew install automake 再次执行./autogen.sh./configure --prefix=/usr/local/protobufmake && make install最后不要忘记配置环境变量 vim ~/.bash_profileexport PROTOBUF=/usr/local/protobufexport PATH=$PROTOBUF/bin:$PATHsource ~/.bash_profile验证 protoc --version生成protobuf文件使用服务端的.proto文件,执行protoc --php_out=. lottery.proto syntax = "proto3";package lotteryservice;service Greeter { rpc lottery(lotteryReq) returns (lotteryRes){}}message lotteryReq { string param = 1;}message lotteryRes { string data = 1;}会生成如下目录: 安装PHP扩展gRPC扩展: http://pecl.php.net/package/gRPCprotobuf扩展: http://pecl.php.net/package/p... 自定义客户端在项目目录下编写composer.json { "name": "grpc-go-php", "require": { "grpc/grpc": "^v1.3.0", "google/protobuf": "^v3.3.0" }, "autoload":{ "psr-4":{ "GPBMetadata\\":"GPBMetadata/", "Lotteryservice\\":"Lotteryservice/" } }}composer install 之后会生成如下目录: ...

June 3, 2019 · 1 min · jiezi

gPRC实现跨语言的微服务间通信-精通外语的电报员与煲电报粥的小怪兽

作者:亚瑟、文远 1. 微服务框架 -- 从系统怪物到服务小怪兽一个小巧的单体应用会随着公司业务的扩张而慢慢成长,逐渐演化成一个庞大且复杂的系统怪物,系统任何一处的问题都将影响整个怪物的表现,很少有单独的开发者能理清系统怪物所有的肌理脉络,导致bug的定位和新功能的扩展都变得越来越困难,对系统的任一改动都要求整个怪物一起回归测试并重新部署,效率必然不高。所以公司发展到了一定阶段,总会需要从架构上寻找解决系统怪物之道,而微服务就是目前最流行的架构方案之一,它将系统怪物拆分成多个独立自治的服务小怪兽,让我们有能力分而治之。[插画:无数小怪兽组成一座大怪兽形状的山,小怪兽正一个个从山上滚下来] 2. RPC框架 -- 小怪兽的电报员一旦系统怪物被拆分成了多个服务小怪兽,小怪兽们如何沟通协作就成了我们最关心的问题。服务小怪兽间的通信就好像发电报一样,涉及到数据序列化、反序列化、连接管理、收发线程、超时处理等多个问题,RPC框架的出现解决了这些问题,就好像通过电报员发电报一样,使用RPC框架让小怪兽们不必关心通信的底层细节。[插画:小怪兽在请电报员发电报] RPC调用细节服务消费方(小怪兽A)以本地调用方式调用服务client stub(小怪兽A的电报员)接受到调用后负责将方法、参数等编码成能够进行网络传输的消息体(电报)client stub(小怪兽A的电报员)找到服务地址,并将消息发送到服务端server stub(小怪兽B的电报员)收到消息(电报)后进行解码server stub(小怪兽B的电报员)根据解码结果调用本地的服务(小怪兽B)本地服务(小怪兽B)执行并将结果返回给server stub(小怪兽B的电报员)server stub(小怪兽B的电报员)将结果编码成消息(电报)并发送至客户端client stub(小怪兽A的电报员)接受到消息(电报)并进行解码服务消费方(小怪兽A)得到最终的结果3. gRPC -- 这位电报员是语言天才如果通信的小怪兽们语言不通,那么我们需要对电报员(亦即RPC框架)的人选提出更高的要求,无论小怪兽们用的是什么语言,协助通信的两位电报员都必须把它们的话翻译成电报员彼此能理解的同一种语言,亦即IDL(Interface Description Language),是的,电报员在这种情况下还必须承担翻译的角色,而gRPC就是一位如此优秀的电报员。[插画:小怪兽说"今晚的月色真美",电报员写”I Love you“] 4. gPRC Demo实现Node客户端小怪兽发送"今晚的月色真美",Java服务端小怪兽收到电报内容,并回复"I love you too"。 通过Spring Boot创建Java项目,pom.xml中加入如下依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-netty-shaded</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-protobuf</artifactId> <version>1.21.0</version> </dependency> <dependency> <groupId>io.grpc</groupId> <artifactId>grpc-stub</artifactId> <version>1.21.0</version> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.5.0.Final</version> </extension> </extensions> <plugins> <plugin> <groupId>org.xolstice.maven.plugins</groupId> <artifactId>protobuf-maven-plugin</artifactId> <version>0.5.1</version> <configuration> <protocArtifact>com.google.protobuf:protoc:3.7.1:exe:${os.detected.classifier}</protocArtifact> <pluginId>grpc-java</pluginId> <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.21.0:exe:${os.detected.classifier}</pluginArtifact> <!--指定生成文件目录--> <outputDirectory>src/main/java</outputDirectory> <!--重新生成文件时不清除 原有src/main/java下的内容--> <clearOutputDirectory>false</clearOutputDirectory> </configuration> <executions> <execution> <goals> <goal>compile</goal> <goal>compile-custom</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>定义IDL文件 ...

May 30, 2019 · 2 min · jiezi

fong-纯typescript的node-gRPC微服务框架

简介fong: A service framework of node gRPC. github: https://github.com/xiaozhongliu/fong fong是一个完全用typescript编写的node gRPC框架, 可以基于它很方便地编写gRPC微服务应用. 一般是用来编写service层应用, 以供bff层或前端层等调用. 优点1.纯typescript编写, typescript的好处不用多说了. 并且用户使用这个框架框架时, 查看定义都是ts源码, 用户使用框架感受不到type definition文件. 2.效仿egg.js的『约定优于配置』原则, 按照统一的约定进行应用开发, 项目风格一致, 开发模式简单, 上手速度极快. 如果用过egg, 就会发现一切都是那么熟悉. 对比目前能找到的开源node gRPC框架很少, 跟其中star稍微多点的mali简单对比一下: 对比方面malifong项目风格约定 √定义查看跳转definition源代码编写语言javascripttypescriptproto文件加载仅能加载一个按目录加载多个代码生成 √中间件√√配置 √日志 √controller加载 √service加载 即将支持, 目前可以自己import即可util加载 即将支持, 目前可以自己import即可入参校验 即将支持插件机制 打算支持更多功能 TBD示例示例项目github: https://github.com/xiaozhongliu/ts-rpc-seed 运行服务使用vscode的话直接进F5调试typescript. 或者: npm start测试请求ts-node tester# 或者:npm run tscnode dist/tester.js使用目录约定不同类型文件只要按以下目录放到相应的文件夹即可自动加载. root├── proto| └── greeter.proto├── config| ├── config.default.ts| ├── config.dev.ts| ├── config.test.ts| ├── config.stage.ts| └── config.prod.ts├── midware| └── logger.ts├── controller| └── greeter.ts├── service| └── sample.ts├── util| └── sample.ts└── typings| ├── enum.ts| └── indexed.d.ts├── log| ├── common.20190512.log| ├── common.20190513.log| ├── request.20190512.log| └── request.20190513.log├── app├── packagen├── tsconfign└── tslintn入口文件import App from 'fong'new App().start()配置示例默认配置config.default.ts与环境配置config.<NODE_ENV>.ts是必须的, 运行时会合并. 配置可从ctx.config和app.config获取. ...

May 13, 2019 · 2 min · jiezi

使用grpc开发RPC服务(一)

前言笔者最近换了一份工作从一家新零售公司去到一家做电子商务的公司,主要的编程语言也从NodeJs转为了Go,因为新公司使用的是grpc做的微服务,所以要重新开始学习新的东西了,正好把这周学习的东西做个总结。整个系列主要涉及Golang、gRPC、go-micros、Docker、Docker-compose、consul,通过本系列,你可以了解到如何1、使用grpc/go-mircos构建微服务,2、使用docker进行服务的部署,3、使用consul进行服务发现grpc简介gRPC是谷歌开源的一款跨平台、高性能的RPC框架,笔者目前主要使用它来进行后端微服务的开发。可能会有的同学对RPC不太熟悉,其实在笔者看来,RPC和HTTP并无多大的区别都是一种调用方式,区别则是在于RPC会限制传输协议、传输的参数等,以此换取高效的传输流程,比如grpc就使用的是google开源的protobuf协议,使用TCP的方式进行传输,使得请求比起普通的JSON+HTPP更加快捷。关于更多protobuf的信息,可以查看这里必要的准备了解Golang及其生态安装gRPc及protobuf,教程安装golang安装protobuf编译器本期目标本期目标是使用gRPC实现一个非常小的微服务user-service,服务的功能非常简单,只提供一个获取用户信息的接口一、首先我们需要定义好整个服务的protobuf文件user.protosyntax = “proto3”; // 指定语法格式,注意 proto3 不再支持 proto2 的 required 和 optionalpackage proto; // 指定生成的 user.pb.go 的包名,防止命名冲突// service 定义开放调用的服务,即 UserInfoService 微服务service UserInfoService {// rpc 定义服务内的 GetUserInfo 远程调用rpc GetUserInfo (UserRequest) returns (UserResponse) {}} // message 对应生成代码的 struct// 定义客户端请求的数据格式message UserRequest {// [修饰符] 类型 字段名 = 标识符;string name = 1;}// 定义服务端响应的数据格式message UserResponse {int32 id = 1;string name = 2;int32 age = 3;repeated string title = 4; // repeated 修饰符表示字段是可变数组,即 slice 类型}然后我们通过protoc命令编译proto文件,生成对应的go文件protoc -I . –go_out=plugins=grpc:. ./user.proto具体文件太长就不放出来展示了二、我们来实现server.go首先我们应该明确实现的步骤:1、实现GetUserInfo接口2、使用gRPC建立服务,监听端口3、将我们实现的服务注册到gRPC中去话不多说,代码如下package mainimport (“fmt"“log"“net”// Import the generated protobuf codepb “go_mirco_service/proto"“golang.org/x/net/context"“google.golang.org/grpc”)type UserInfoService struct{}var u = UserInfoService{}func (u *UserInfoService) GetUserInfo(ctx context.Context, req *pb.UserRequest) (resp *pb.UserResponse, err error) {name := req.Nameif name == “leoython” {resp = &pb.UserResponse{Id: 233, Name: name,Age: 20, Title: []string{“Gopher”}, } }err = nilreturn} func main() {port := “:2333"l, err := net.Listen(“tcp”, port)if err != nil { log.Fatalf(“listen error: %v\n”, err) } fmt.Printf(“listen %s\n”, port)s := grpc.NewServer() // 将 UserInfoService 注册到 gRPC// 注意第二个参数 UserInfoServiceServer 是接口类型的变量// 需要取地址传参 pb.RegisterUserInfoServiceServer(s, &u) s.Serve(l)}到此我们就实现了利用gRPC实现了一个非常简单但是五脏俱全的RPC服务,但是却出现了一个问题,我们无法直接调用,所以我们还需要实现一个调用server的客户端,代码如下package main import (“fmt"“log” pb “go_mirco_service/proto” “golang.org/x/net/context"“google.golang.org/grpc”) func main() {conn, err := grpc.Dial(":2333”, grpc.WithInsecure())if err != nil { log.Fatalf(“dial error: %v\n”, err) }defer conn.Close() // 实例化 UserInfoService 微服务的客户端client := pb.NewUserInfoServiceClient(conn) // 调用服务req := new(pb.UserRequest)req.Name = “leoython"resp, err := client.GetUserInfo(context.Background(), req)if err != nil { log.Fatalf(“resp error: %v\n”, err) } fmt.Printf(“Recevied: %v\n”, resp)}结语至此,我们已经学会使用gRPC进行开发,采用protobuf进行参数的定义,下一篇笔者将会使用grpc-gateway将RPC接口转换为rest接口供客户端调用,而不需要客户端实现RPC,这也是现在主流微服务的一种服务提供方式,对外使用REST,对内使用RPC,关于更多微服务的内容,推荐查看nginx的关于微服务的文章关于作者Leoython,擅长Javascript, Python, Go,最近在研究Rust和k8sE-Mail: leoython@gmail.com文章编写于: 2019/01/31转载请注明出处:http://www.leoython.club/arti… ...

April 7, 2019 · 1 min · jiezi

CNCF案例研究:网易

网易如何利用Kubernetes支持全球互联网业务公司:网易地点:中国杭州行业:互联网技术挑战它的游戏业务是世界上最大的游戏业务之一,但这并不是网易为中国消费者提供的唯一服务。该公司还经营电子商务、广告、音乐流媒体、在线教育和电子邮件平台;最后一个通过163.com等网站为近十亿用户提供免费电子邮件服务。2015年,为所有这些系统提供基础设施的网易云团队,意识到他们的研发流程正在减缓开发者的速度。“我们的用户需要自己准备所有基础设施。”网易云和容器服务架构师Feng Changjian说。“我们渴望通过无服务器的容器服务,自动为我们的用户提供基础设施和工具。”解决方法在考虑建立自己的业务流程解决方案后,网易决定将其私有云平台建立在Kubernetes上。这项技术来自Google的事实让团队相信它可以跟上网易的规模。“经过2到3个月的评估,我们相信它可以满足我们的需求。”Changjian说。该团队在2015年开始使用Kubernetes,甚至在1.0之前。今天,网易内部云平台还利用了CNCF项目Prometheus、Envoy、Harbor、gRPC和Helm,在生产集群中运行10,000个节点,并且可以在一个集群中支持多达30,000个节点。基于其内部平台的经验,该公司向外部客户推出了基于Kubernetes的云和面向微服务的PaaS产品,网易轻舟微服务。影响网易团队报告说,Kubernetes使研发效率提高了100%以上。部署效率提高了280%。“在过去,如果我们想进行升级,我们需要与其他团队合作,甚至在其他部门工作。”Changjian说。“我们需要特殊的工作人员来准备一切,所以花了大约半个小时。现在我们可以在5分钟内完成。”新平台还允许使用GPU和CPU资源进行混合部署。“之前,如果我们将所有资源都用于GPU,我们就不会为CPU提供备用资源。但是现在我们通过混合部署得到了改进。”他说。这些改进也提高了资源的利用率。“该系统可以在一个集群中支持30,000个节点。在生产中,我们在单个集群中获得了10,000个节点的数据。整个内部系统正在使用该系统进行开发、测试和生产。” - Zeng Yuxing,网易架构师它的游戏业务是全球第五大,但这并非网易为消费者提供的唯一服务。该公司还在中国经营电子商务、广告、音乐流媒体、在线教育和电子邮件平台;其中最后一个通过163.com和126.com等热门网站为近十亿用户提供免费电子邮件服务。凭借这种规模,网易云团队为所有这些系统提供基础设施,在2015年发现他们的研发流程难以使开发者满足需求。“我们的用户需要自己准备所有基础设施。”网易云和容器服务架构师Feng Changjian说。“我们渴望通过无服务器的容器服务,自动为我们的用户提供基础设施和工具。”在考虑建立自己的业务流程解决方案后,网易决定将其私有云平台建立在Kubernetes上。这项技术来自Google的事实让团队相信它可以跟上网易的规模。“经过2到3个月的评估,我们相信它可以满足我们的需求。”Changjian说。“我们利用Kubernetes的可编程性,以便我们可以构建一个平台来满足内部客户的升级和部署需求。” - Feng Changjian,网易云和容器服务架构师该团队在2015年开始采用Kubernetes,甚至在1.0之前,因为它相对容易使用,并且让公司启用了DevOps。“我们放弃了Kubernetes的一些概念;我们只想使用标准化框架。”Changjian说。“我们利用Kubernetes的可编程性,以便我们可以构建一个平台来满足内部客户的升级和部署需求。”该团队首先专注于构建容器平台,以更好地管理资源,然后通过添加监控等内部系统,将注意力转向改进其对微服务的支持。这意味着整合CNCF项目Prometheus、Envoy、Harbor、gRPC和Helm。“我们努力提供简化和标准化的流程,因此我们的用户和客户可以利用我们的最佳实践。”Changjian说。团队正在继续改进。例如,企业的电子商务部分需要利用混合部署,这在过去需要使用两个独立的平台:基础架构即服务平台和Kubernetes平台。最近,网易创建了一个跨平台的应用程序,可以同时使用单命令部署。“只要公司拥有一支成熟的团队和足够的开发者,我认为Kubernetes是一种非常好的技术,可以帮助他们。” - Li Lanqing,网易Kubernetes开发者今天,网易内部云平台“可以在一个集群中支持30,000个节点。”架构师Zeng Yuxing说。“在生产中,我们在单个集群中获得了10,000个节点的数据。整个内部系统正在使用该系统进行开发、测试和生产。”网易团队报告说,Kubernetes使研发效率提高了100%以上。部署效率提高了280%。“在过去,如果我们想进行升级,我们需要与其他团队合作,甚至在其他部门工作。”Changjian说。“我们需要特殊的工作人员来准备一切,所以花了大约半个小时。现在我们可以在5分钟内完成。”新平台还允许使用GPU和CPU资源进行混合部署。“之前,如果我们将所有资源都用于GPU,我们就不会为CPU提供备用资源。但是现在我们通过混合部署得到了改进。”他说。这些改进也提高了资源的利用率。“通过与这个社区合作,我们可以从中获得一些经验,我们也可以从中受益。我们可以看到社区面临的问题和挑战,并参与其中。” - Li Lanqing,网易Kubernetes开发者基于使用其内部平台的结果和经验,该公司向外部客户推出基于Kubernetes的云和面向微服务的PaaS产品,网易轻舟微服务。“我们的想法是,我们可以找到我们的游戏和电子商务以及云音乐供应商遇到的问题,因此我们可以整合他们的经验,并提供一个平台来满足用户的需求。”Changjian说。无论是否使用网易产品,该团队都鼓励其他公司尝试Kubernetes。“只要公司拥有一支成熟的团队和足够的开发者,我认为Kubernetes是一种非常好的技术,可以帮助他们。”Kubernetes开发者Li Lanqing说。作为最终用户和供应商,网易已经更多地参与社区,向其他公司学习并分享他们所做的事情。该团队一直在为Harbor和Envoy项目做出贡献,并在网易规模测试技术时提供反馈。“我们是一个专注于解决微服务架构挑战的团队。”Changjian说。“通过与这个社区合作,我们可以从中获得一些经验,我们也可以从中受益。我们可以看到社区面临的问题和挑战,并参与其中。”KubeCon + CloudNativeCon + Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon + Open Source Summit赞助方案KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon + Open Source Summit购票窗口,立即购票!CNCF邀请你加入最终用户社区

March 15, 2019 · 1 min · jiezi

NestJS 集成graphql grpc 分布式实践

前言: 为了学习nestjs graphql grpc 微服务方面的知识,具体grpc和graphql的语法再之后在做详细分析1 创建项目npm i -g @nestjs/clinest new project-name2 添加graphql创建graphql-config.service.ts文件,用于graphql的配置及编写过滤器的逻辑@Injectable()export class GraphQLConfigService implements GqlOptionsFactory { constructor() {} createGqlOptions(): GqlModuleOptions { return { typePaths: [join(process.cwd(), “./graphqls/*.graphql”)], // 配置的graphql文件地址 installSubscriptionHandlers: true, definitions: { path: join(process.cwd(), “src/graphql.schema.ts”), // 解析之后的文件地址 outputAs: “class” }, context: async ({ req }) => { // 过滤器 let user = Jwt.verifyToken(req.headers.authorization); // 业务逻辑 return { user }; } }; }}添加进app.module.ts里@Module({ imports: [ GraphQLModule.forRootAsync({ imports: [ApplicationModule], useClass: GraphQLConfigService }), ],})export class ApplicationModule {}创建文件xxx.resolvers.ts @Query(‘getUser’) async getUser() { return {}; }3 添加grpc首先,创建一个子项目xxx子项目创建grpc.options.ts文件,用于init连接的配置export const grpcClientOptions: ClientOptions = { transport: Transport.GRPC, options: { url: “localhost:50051”, // 服务地址 package: “xxx”, protoPath: join(__dirname, ‘./xxx.proto’), },};创建接口, 注意这里的首字母会被自动装为大写 @GrpcMethod(“UserService”) async addUser(data: User): Promise<any> { return data }在main.ts引入import { grpcClientOptions } from ‘./grpc.options’;async function bootstrap() { const app = await NestFactory.create(AppModule); app.connectMicroservice(grpcClientOptions); await app.startAllMicroservicesAsync();}bootstrap();其他微服务或是apigateway调用是创建一个 const grpcClientOptions: ClientOptions = { transport: Transport.GRPC, options: { url: grpcServe.user.url, package: grpcServe.user.package, protoPath: join(__dirname, ‘../../common/proto/user.proto’), },};@Injectable()export class ClentServe { constructor() {} @Client(grpcClientOptions) public readonly client: ClientGrpc;} ...

March 13, 2019 · 1 min · jiezi

用consul做grpc的服务发现

用consul做grpc的服务发现与健康检查consul服务发现与负载均衡当server端是集群部署时,client调用server就需要用到服务发现与负载均衡。通常有两总方式:一种方式是在client与server之间加代理,由代理来做负载均衡一种方式是将服务注册到一个数据中心,client通过数据中心查询到所有服务的节点信息,然后自己选择负载均衡的策略。第一种方式常见的就是用nginx给http服务做负载均衡,client端不直接与server交互,而是把请求并给nginx,nginx再转给后端的服务。这种方式的优点是:client和server无需做改造,client看不到server的集群,就像单点一样调用就可以这种方式有几个缺点:所有的请求都必须经过代理,代理侧容易出现性能瓶颈代理不能出故障,一旦代理挂了服务就没法访问了。第二种方式可以参考dubbo的rpc方式,所有的服务都注册在zookeeper上,client端从zookeeper订阅server的列表,然后自己选择把请求发送到哪个server上。对于上面提到的两个缺点,这种方式都很好的避免了:client与server端是直接交互的,server可以做任意的水平扩展,不会出现性能瓶颈注册中心(zookeeper)通过raft算法实现分布式高可用,不用担心注册中心挂了服务信息丢失的情况。这种方式的缺点就是实现起来比较复杂。用第一种方式做grpc的负载均衡时可以有以下的选择:nginx grpctraefik grpc用第二种方式时,可以选择的数据中心中间件有:zookeeperetcd Etcd是Kubernetes集群中的一个十分重要的组件consul他们都实现了raft算法,都可以用来做注册中心,本篇文章选择consul是因为consul的特点就是做服务发现,有现成的api可以用。用consul给golang的grpc做服务注册与发现grpc的resolvergrpc的Dial()和DialContent()方法中都可以添加Load-Balance的选项,Dial方法已经被废弃了,本篇文章介绍使用DialContext的方法。grpc官方实现了dns_resolver用来做dns的负载均衡。我们通过例子看看grpc client端的代码是怎么写的,然后再理解dns_resolver的源码,最后参照dns_resolver来写自己的consul_resovler。dns的负载均衡的例子:package mainimport ( “context” “log” “google.golang.org/grpc” “google.golang.org/grpc/balancer/roundrobin” pb “google.golang.org/grpc/examples/helloworld/helloworld” “google.golang.org/grpc/resolver”)const ( address = “dns:///dns-record-name:443” defaultName = “world”)func main() { // The secret sauce resolver.SetDefaultScheme(“dns”) // Set up a connection to the server. ctx, _ := context.WithTimeout(context.Background(), 5*time.Second) conn, err := grpc.DialContext(ctx, address, grpc.WithInsecure(), grpc.WithBalancerName(roundrobin.Name)) if err != nil { log.Fatalf(“did not connect: %v”, err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the servers in round-robin manner. for i := 0; i < 3; i++ { ctx := context.Background() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: defaultName}) if err != nil { log.Fatalf(“could not greet: %v”, err) } log.Printf(“Greeting: %s”, r.Message) }}DialContext的定义如下:func DialContext(ctx context.Context, target string, opts …DialOption) (conn *ClientConn, err error)下面这行代码指明了用dns_resolver,实际上也可以不写,grpc会根据DialContext的第二个参数target来判断选用哪个resolver,例子中传给DialContext的target是 dns:///dns-record-name:443,grpc会自动选择dns_resolverresolver.SetDefaultScheme(“dns”)下面的这个选项,指明了grpc用轮询做为负载均衡的策略grpc.WithBalancerName(roundrobin.Name)调用grpc.DialContext之后,grpc会找到对应的resovler,拿到服务的地址列表,然后在调用服务提供的接口时,根据指定的轮询策略选择一个服务。gRPC Name Resolution里面说了,可以实现自定义的resolver作为插件。先看看resolver.go的源码,源码路径是$GOPATH/src/google.golang.org/grpc/resolver/resolver.gom = make(map[string]Builder) //scheme到Builder的mapfunc Register(b Builder) { //用于resolver注册的接口,dns_resolver.go的init方中调用了这个方法,实际就是更新了map m[b.Scheme()] = b}type Resolver interface { ResolveNow(ResolveNowOption) //立即resolve,重新查询服务信息 Close() //关闭这个Resolver}type Target struct {//uri解析之后的对象, uri的格式详见RFC3986 Scheme string Authority string Endpoint string}type Address struct {//描述一个服务的地址信息 Addr string //格式是 host:port Type AddressType ServerName string Metadata interface{}}type ClientConn interface {//定义了两个callback函数,用于通知服务信息的更新 NewAddress(addresses []Address) NewServiceConfig(serviceConfig string)}type Builder interface { Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error) //返回一个Resolver Scheme() string //返回scheme如 “dns”, “passthrough”, “consul”}func Get(scheme string) Builder { //grpc.ClientConn会高用这个方法获取指定的Builder接口的实例 if b, ok := m[scheme]; ok { return b } return nil}即使加了注释,估计也很难马上理解这个其中的具体含意,博主也是结合dns_resolver.go,反复读了好几遍才理解resolver.go。其大致的意思是,grpc.DialContext方法调用之后:解析target(例如dns:///dns-record-name:443)获取scheme调用resolver.Get方法根据scheme拿到对应的Builder调用Builder.Build方法解析target获取服务地址的信息调用ClientConn.NewAddress和NewServiceConfig这两个callback把服务信息传递给上层的调用方返回Resolver接口实例给上层上层可以通过Resolver.ResolveNow方法主动刷新服务信息了解了resolver源码的意思之后,再看一下dns_resolver.go就比较清晰了//注册一个Builder到resolver的map里面//这个方法会被默认调用,了解go的init可以自行百度func init() { resolver.Register(NewBuilder())}func NewBuilder() resolver.Builder {//创建一个resolver.Builder的实例 return &dnsBuilder{minFreq: defaultFreq}}func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { //解析target拿到ip和端口 host, port, err := parseTarget(target.Endpoint, defaultPort) if err != nil { return nil, err } // IP address. if net.ParseIP(host) != nil { host, _ = formatIP(host) addr := []resolver.Address{{Addr: host + “:” + port}} i := &ipResolver{ cc: cc, ip: addr, rn: make(chan struct{}, 1), q: make(chan struct{}), } cc.NewAddress(addr) go i.watcher() return i, nil } // DNS address (non-IP). ctx, cancel := context.WithCancel(context.Background()) d := &dnsResolver{ freq: b.minFreq, backoff: backoff.Exponential{MaxDelay: b.minFreq}, host: host, port: port, ctx: ctx, cancel: cancel, cc: cc, t: time.NewTimer(0), rn: make(chan struct{}, 1), disableServiceConfig: opts.DisableServiceConfig, } if target.Authority == "" { d.resolver = defaultResolver } else { d.resolver, err = customAuthorityResolver(target.Authority) if err != nil { return nil, err } } d.wg.Add(1) go d.watcher()//起一个goroutine,因为watcher这个方法是个死循环,当定时器 return d, nil}func (d *dnsResolver) watcher() { defer d.wg.Done() for { //这个select没有default,当没有case满足时会一直阻塞 //结束阻塞的条件是定时器超时d.t.C,或者d.rn这个channel中有数据可读 select { case <-d.ctx.Done(): return case <-d.t.C: case <-d.rn: } result, sc := d.lookup() // Next lookup should happen within an interval defined by d.freq. It may be // more often due to exponential retry on empty address list. if len(result) == 0 { d.retryCount++ d.t.Reset(d.backoff.Backoff(d.retryCount)) } else { d.retryCount = 0 d.t.Reset(d.freq) } //resolver.ClientConn的两个callback的调用,实现服务信息传入上层 d.cc.NewServiceConfig(sc) d.cc.NewAddress(result) }}//向channel中写入,用于结束watcher中那个select的阻塞状态,后面的代码就是重新查询服务信息的逻辑func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOption) { select { case i.rn <- struct{}{}: default: }}实现consul_resovler上面我们了解了grpc的resolver的机制,接下来实现consul_resolver, 我们先把代码的架子搭起来init() //返回一个resolver.Builder的实例//实现resolver.Builder的接口中的所有方法就是一个resolver.Buildertype consulBuidler strcut {}func (cb *consulBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { //TODO 解析target, 拿到consul的ip和端口 //TODO 用consul的go api连接consul,查询服务结点信息,并且调用resolver.ClientConn的两个callback}func (cb *consulBuilder) Scheme() string { return “consul”}//ResolverNow方法什么也不做,因为和consul保持了发布订阅的关系//不需要像dns_resolver那个定时的去刷新func (cr *consulResolver) ResolveNow(opt resolver.ResolveNowOption) {}//暂时先什么也不做吧func (cr *consulResolver) Close() {}现在来看,实现consul_resolver.go最大的问题就是怎么用consul提供的go api了,参考这篇文章就可以了,然后consul_resolver.go的代码就出来了package consulimport ( “errors” “fmt” “github.com/hashicorp/consul/api” “google.golang.org/grpc/resolver” “regexp” “sync”)const ( defaultPort = “8500”)var ( errMissingAddr = errors.New(“consul resolver: missing address”) errAddrMisMatch = errors.New(“consul resolver: invalied uri”) errEndsWithColon = errors.New(“consul resolver: missing port after port-separator colon”) regexConsul, _ = regexp.Compile("^([A-z0-9.]+)(:[0-9]{1,5})?/([A-z_]+)$"))func Init() { fmt.Printf(“calling consul init\n”) resolver.Register(NewBuilder())}type consulBuilder struct {}type consulResolver struct { address string wg sync.WaitGroup cc resolver.ClientConn name string disableServiceConfig bool lastIndex uint64}func NewBuilder() resolver.Builder { return &consulBuilder{}}func (cb *consulBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { fmt.Printf(“calling consul build\n”) fmt.Printf(“target: %v\n”, target) host, port, name, err := parseTarget(fmt.Sprintf("%s/%s", target.Authority, target.Endpoint)) if err != nil { return nil, err } cr := &consulResolver{ address: fmt.Sprintf("%s%s", host, port), name: name, cc: cc, disableServiceConfig: opts.DisableServiceConfig, lastIndex: 0, } cr.wg.Add(1) go cr.watcher() return cr, nil}func (cr *consulResolver) watcher() { fmt.Printf(“calling consul watcher\n”) config := api.DefaultConfig() config.Address = cr.address client, err := api.NewClient(config) if err != nil { fmt.Printf(“error create consul client: %v\n”, err) return } for { services, metainfo, err := client.Health().Service(cr.name, cr.name, true, &api.QueryOptions{WaitIndex: cr.lastIndex}) if err != nil { fmt.Printf(“error retrieving instances from Consul: %v”, err) } cr.lastIndex = metainfo.LastIndex var newAddrs []resolver.Address for _, service := range services { addr := fmt.Sprintf("%v:%v", service.Service.Address, service.Service.Port) newAddrs = append(newAddrs, resolver.Address{Addr: addr}) } fmt.Printf(“adding service addrs\n”) fmt.Printf(“newAddrs: %v\n”, newAddrs) cr.cc.NewAddress(newAddrs) cr.cc.NewServiceConfig(cr.name) }}func (cb *consulBuilder) Scheme() string { return “consul”}func (cr *consulResolver) ResolveNow(opt resolver.ResolveNowOption) {}func (cr *consulResolver) Close() {}func parseTarget(target string) (host, port, name string, err error) { fmt.Printf(“target uri: %v\n”, target) if target == "" { return “”, “”, “”, errMissingAddr } if !regexConsul.MatchString(target) { return “”, “”, “”, errAddrMisMatch } groups := regexConsul.FindStringSubmatch(target) host = groups[1] port = groups[2] name = groups[3] if port == "" { port = defaultPort } return host, port, name, nil}到此,grpc客户端服务发现就搞定了。consul的服务注册服务注册直接用consul的go api就可以了,也是参考前一篇文章,简单的封装一下,consul_register.go的代码如下:package consulimport ( “fmt” “github.com/hashicorp/consul/api” “time”)type ConsulService struct { IP string Port int Tag []string Name string}func RegitserService(ca string, cs *ConsulService) { //register consul consulConfig := api.DefaultConfig() consulConfig.Address = ca client, err := api.NewClient(consulConfig) if err != nil { fmt.Printf(“NewClient error\n%v”, err) return } agent := client.Agent() interval := time.Duration(10) * time.Second deregister := time.Duration(1) * time.Minute reg := &api.AgentServiceRegistration{ ID: fmt.Sprintf("%v-%v-%v", cs.Name, cs.IP, cs.Port), // 服务节点的名称 Name: cs.Name, // 服务名称 Tags: cs.Tag, // tag,可以为空 Port: cs.Port, // 服务端口 Address: cs.IP, // 服务 IP Check: &api.AgentServiceCheck{ // 健康检查 Interval: interval.String(), // 健康检查间隔 GRPC: fmt.Sprintf("%v:%v/%v", cs.IP, cs.Port, cs.Name), // grpc 支持,执行健康检查的地址,service 会传到 Health.Check 函数中 DeregisterCriticalServiceAfter: deregister.String(), // 注销时间,相当于过期时间 }, } fmt.Printf(“registing to %v\n”, ca) if err := agent.ServiceRegister(reg); err != nil { fmt.Printf(“Service Register error\n%v”, err) return }}改造一下grpc的helloworld把grpc的helloworld的demo改一下,用consul来做服务注册和发现。server端代码:package mainimport ( “context” “fmt” “google.golang.org/grpc” “google.golang.org/grpc/health/grpc_health_v1” “log” “net” “server/internal/consul” pb “server/proto/helloworld”)const ( port = “:50051”)// server is used to implement helloworld.GreeterServer.type server struct{}// SayHello implements helloworld.GreeterServerfunc (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf(“Received: %v”, in.Name) return &pb.HelloReply{Message: “Hello " + in.Name}, nil}func RegisterToConsul() { consul.RegitserService(“127.0.0.1:8500”, &consul.ConsulService{ Name: “helloworld”, Tag: []string{“helloworld”}, IP: “127.0.0.1”, Port: 50051, })}//healthtype HealthImpl struct{}// Check 实现健康检查接口,这里直接返回健康状态,这里也可以有更复杂的健康检查策略,比如根据服务器负载来返回func (h *HealthImpl) Check(ctx context.Context, req *grpc_health_v1.HealthCheckRequest) (*grpc_health_v1.HealthCheckResponse, error) { fmt.Print(“health checking\n”) return &grpc_health_v1.HealthCheckResponse{ Status: grpc_health_v1.HealthCheckResponse_SERVING, }, nil}func (h *HealthImpl) Watch(req grpc_health_v1.HealthCheckRequest, w grpc_health_v1.Health_WatchServer) error { return nil}func main() { lis, err := net.Listen(“tcp”, port) if err != nil { log.Fatalf(“failed to listen: %v”, err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) grpc_health_v1.RegisterHealthServer(s, &HealthImpl{}) RegisterToConsul() if err := s.Serve(lis); err != nil { log.Fatalf(“failed to serve: %v”, err) }}client端代码:package mainimport ( “client/internal/consul” pb “client/proto/helloworld” “context” “google.golang.org/grpc” “log” “os” “time”)const ( target = “consul://127.0.0.1:8500/helloworld” defaultName = “world”)func main() { consul.Init() // Set up a connection to the server. ctx, _ := context.WithTimeout(context.Background(), 5time.Second) conn, err := grpc.DialContext(ctx, target, grpc.WithBlock(), grpc.WithInsecure(), grpc.WithBalancerName(“round_robin”)) if err != nil { log.Fatalf(“did not connect: %v”, err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the server and print out its response. name := defaultName if len(os.Args) > 1 { name = os.Args[1] } for { ctx, _ := context.WithTimeout(context.Background(), time.Second) r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf(“could not greet: %v”, err) } log.Printf(“Greeting: %s”, r.Message) time.Sleep(time.Second * 2) }}运行一把启动consulconsul agent -dev启动hello servercd servergo run cmd/main.go启动hello clientcd clientgo run cmd/main.go运行结果://client2019/03/07 17:22:04 Greeting: Hello world2019/03/07 17:22:06 Greeting: Hello world//server2019/03/07 17:22:04 Received: world2019/03/07 17:22:06 Received: world完整工程的git地址工程使用方法:cd servergo mod tidygo run cmd/main.gocd clientgo mod tidygo run cmd/main.go请自行解决防火墙的问题参考文章grpc 名称发现与负载均衡golang consul-grpc 服务注册与发现gRPC Client-Side Load Balancing in Gogodoc grpcconsul go api ...

March 7, 2019 · 6 min · jiezi

CNCF案例研究:奇虎360

公司:奇虎360地点:中国北京行业:计算机软件挑战中国软件巨头奇虎360科技的搜索部门,so.com是中国第二大搜索引擎,市场份额超过35%。该公司一直在使用传统的手动操作来部署环境,随着项目数量的不断增加,管理层希望提高服务器资源的利用率。“我们希望解决为大量项目构建运营环境的效率问题。”搜索云平台项目发起人之一郭少巍说。解决方法大约三年前,360开始规划容器云服务。该公司现在拥有完整的PaaS解决方案,该解决方案围绕Kubernetes编排、Prometheus监控、gRPC、CoreDNS和Harbor仓库,此外还使用Wayne(开源)部署项目。“我们主要在搜索部门的开发和交付过程中推广容器技术,以提供业务效率和服务质量。”郭说。影响自从切换到由Kubernetes管理容器,开发者开始编写新项目的业务需求代码的设置时间从2-6小时减少到10-30分钟。将应用程序部署到容器云环境后,服务器成本是最初在360使用的物理机解决方案的55%,以及之前传统虚拟化解决方案(VPS)的82%,同时确保相同的性能和可用性。此外,最近在一个小时内完成了大规模的计算机房迁移,这个迁移以前需要一周才能完成。“Kubernetes提供了完整和高度集成的功能组件,我们只需少量的开发工作即可满足业务需求。”郭少巍,奇虎360搜索云平台项目发起人中国软件巨头奇虎360科技的搜索部门,so.com是中国第二大搜索引擎,市场份额超过35%。 该公司一直在使用传统的手动操作来部署环境,随着项目数量的不断增加,管理层希望提高服务器资源的利用率。“我们希望解决为大量项目构建运营环境的效率问题。”搜索云平台项目发起人之一郭少巍说。大约三年前,360开始规划容器云系统。“我们评估了三种编排技术,最终选择了Kubernetes,因为它提供了完整和高度集成的功能组件,”郭说,“我们只需要少量的开发工作就能满足业务需求。”“将服务容器化并投入我们的云平台之后,完成两个计算机房的迁移只需一个小时左右,这是效率的极大提升。”郭少巍,奇虎360搜索云平台项目发起人该公司现在拥有完整的PaaS解决方案,该解决方案围绕Kubernetes编排、Prometheus监控、Harbor仓库和Wayne(开源)部署项目构建。该平台在很大程度上依赖于开源组件,包括CNCF项目gRPC和CoreDNS,以及InfluxDB、Kafka和Ceph。“我们主要在搜索部门的开发和交付过程中推广容器技术,以提供业务效率和服务质量。”郭说。事实上,这些云原生技术带来了更高的效率。自从切换到由Kubernetes管理容器,开发者开始编写新项目的业务需求代码的设置时间已从2-6小时减少到10-30分钟。应用程序部署到容器云环境后,服务器成本是最初在360使用的物理机解决方案的55%,以及之前传统虚拟化解决方案(VPS)的82%,同时确保相同的性能和可用性。“过去两年,我们的部门经历了两次大规模的计算机房迁移。过去,对于运营和开发而言,这是非常痛苦的,因为他们必须密切合作一周左右才能完成整个在线服务的迁移。”郭少巍,奇虎360搜索云平台项目发起人此外,最近在一个小时内完成了大规模的计算机房迁移,这个迁移以前需要一周才能完成。“我们的部门在过去两年里经历了两次大规模的计算机房迁移,”郭说。“过去,对于运营和开发而言,这是非常痛苦的,因为他们必须密切合作一周左右才能完成整个在线服务的迁移。但是,在将服务容器化并投入云平台之后,完成两个房间的迁移只需要一个小时左右,这是效率的极大提升。”云平台仍处于早期阶段。“我们仍在推动FaaS的实施和业务应用,预计将有10-20%的业务迁移到平台。”郭说。“对于初创公司或技术实力较弱的公司,我认为选择成熟的开源解决方案将使公司能够专注于业务需求,避免在基本平台开发的早期投入太多资源。”郭少巍,奇虎360搜索云平台项目发起人最后,该公司开源其平台项目Wayne。Wayne基于Kubernetes,以满足企业管理需求。该团队也非常乐意与其他想要沿着云原生路径前进的组织分享其经验和建议。“对于创业公司或技术实力较弱的公司,我认为选择成熟的开源解决方案将使公司能够专注于业务需求,避免在基本平台开发的早期投入太多资源,”郭说。最终,努力是值得的。对于360,“业务效率大大提高。”KubeCon + CloudNativeCon中国论坛提案征集(CFP)2月22日截止KubeCon + CloudNativeCon 论坛让用户、开发人员、从业人员汇聚一堂,面对面进行交流合作。与会人员有 Kubernetes、Prometheus 及其他云原生计算基金会 (CNCF) 主办项目的领导,和我们一同探讨云原生生态系统发展方向。中国开源峰会提案征集(CFP)2月22日截止在中国开源峰会上,与会者将共同合作及共享信息,了解最新和最有趣的开源技术,包括Linux、IoT、区块链、AI、网络等;并获得如何在开源社区中导向和引领的信息。大会日期:提案征集截止日期:太平洋标准时间 2 月 22 日,星期五,晚上 11:59提案征集通知日期:2019 年 4 月 8 日会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日提醒:这是一场社区会议。因此,让我们尽量避开公然推销产品和/或供应商销售宣传。KubeCon + CloudNativeCon + Open Source Summit赞助方案出炉啦KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请

February 11, 2019 · 1 min · jiezi

grpc和consul结合实现分布式rpc调用

GRPC主要介绍了grpc在使用示例和原理,以及如何与consul结合gRPC 是什么?gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得我们能够更容易地创建分布式应用和服务。参考文档:gRPC Python Quickstart开始前确保已经安装grpcio-tools和grpcio这两个包定义一个GRPC有如下三个步骤:定义一个消息类型编译该proto文件编写服务端代码编写客户端代码我们以实现一个echo的grpc为例。定义一个消息类型首先定义通信双方(即客户端和服务端)交互的消息格式(protobuf消息的格式),然后定义该echo服务如下:syntax = “proto3”; // 声明使用 proto3 语法// 定义客户端请求的protobuf格式,如下所示,包含一个字符串字段qmessage Req { string q = 1;}// 定义服务端相应的protobuf格式,如下所示,包含一个字符串字段amessage Resp { string a = 1;}// 定义echo服务,如下所示,该服务包含一个名称为"echo"的rpcservice Echoer{ rpc echo (Req) returns (Resp) {}}使用以下命令编译:python -m grpc_tools.protoc -I./ –python_out=. –grpc_python_out=. ./Echoer.proto生成两个py文件Echoer_pb2.py 此文件包含生成的 request(Req) 和 response(Resp) 类。Echoer_pb2_grpc.py 此文件包含生成的 客户端(EchoerStub)和服务端(EchoerServicer)的类创建服务端代码创建和运行 Echoer 服务可以分为两个部分:实现我们服务定义的生成的服务接口:做我们的服务的实际的“工作”的函数。运行一个 gRPC 服务器,监听来自客户端的请求并传输服务的响应。在当前目录,创建文件 Echoer_server.py,实现一个新的函数:from concurrent import futuresimport timeimport grpcimport Echoer_pb2import Echoer_pb2_grpc_ONE_DAY_IN_SECONDS = 60 * 60 * 24class Echoer(Echoer_pb2_grpc.EchoerServicer): # 工作函数 def SayHello(self, request, context): return Echoer_pb2.Resp(a=“echo”)def serve(): # gRPC 服务器 server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) Echoer_pb2_grpc.add_EchoerServicer_to_server(Echoer(), server) server.add_insecure_port(’[::]:50051’) server.start() # start() 不会阻塞,如果运行时你的代码没有其它的事情可做,你可能需要循环等待。 try: while True: time.sleep(_ONE_DAY_IN_SECONDS) except KeyboardInterrupt: server.stop(0)if name == ‘main’: serve()创建客户端代码在当前目录,打开文件 Echoer_client.py,实现一个新的函数:from future import print_functionimport grpcimport Echoer_pb2import Echoer_pb2_grpcdef run(): channel = grpc.insecure_channel(’localhost:50051’) # 创建信道 stub = Echoer_pb2_grpc.EchoerStub(channel) # 通过信道获取凭据,即Stub response = stub.echo(Echoer_pb2.Req(q=‘echo’)) # 调用rpc,获取响应 print(“Echoer client received: " + response.a)if name == ‘main’: run()运行代码首先运行服务端代码python Echoer_server.py复制代码然后运行客户端代码python Echoer_client.py# outputEchoer client received: echo进阶点击查看参考博客为了通信安全起见,GRPC提供了TSlSSL的支持。首先利用openssl创建一个自签名证书$ openssl genrsa -out server.key 2048Generating RSA private key, 2048 bit long modulus (2 primes)……………………………………………………+++++………………………………………………………………………………………………………………..+++++e is 65537 (0x010001)$ openssl req -new -x509 -sha256 -key server.key -out server.crt -days 3650You are about to be asked to enter information that will be incorporatedinto your certificate request.What you are about to enter is what is called a Distinguished Name or a DN.There are quite a few fields but you can leave some blankFor some fields there will be a default value,If you enter ‘.’, the field will be left blank.—–Country Name (2 letter code) [AU]:State or Province Name (full name) [Some-State]:Locality Name (eg, city) []:Organization Name (eg, company) [Internet Widgits Pty Ltd]:Organizational Unit Name (eg, section) []:Common Name (e.g. server FQDN or YOUR name) []:EchoerEmail Address []:生成了server.key和server.crt两个文件,服务端两个文件都需要,客户端只需要crt文件修改服务端代码server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))Echoer_pb2_grpc.add_EchoerServicer_to_server(Echoer(), server)# 读取 key and certificatewith open(os.path.join(os.path.split(file)[0], ‘server.key’)) as f: private_key = f.read().encode()with open(os.path.join(os.path.split(file)[0], ‘server.crt’)) as f: certificate_chain = f.read().encode()# 创建 server credentialsserver_creds = grpc.ssl_server_credentials(((private_key, certificate_chain,),))# 调用add_secure_port方法,而不是add_insesure_port方法server.add_secure_port(’localhost:50051’, server_creds)修改客户端代码# 读取证书with open(‘server.crt’) as f: trusted_certs = f.read().encode()# 创建 credentialscredentials = grpc.ssl_channel_credentials(root_certificates=trusted_certs)# 调用secure_channel方法,而不是insecure_channel方法channel = grpc.secure_channel(’localhost:50051’, credentials)启动服务端后,启动客户端,会出现以下错误:grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with: status = StatusCode.UNAVAILABLE details = “Connect Failed” debug_error_string = “{“created”:"@1547552759.642000000”,“description”:“Failed to create subchannel”,“file”:“src/core/ext/filters/client_channel/client_channel.cc”,“file_line”:2721,“referenced_errors”:[{“created”:"@1547552759.642000000”,“description”:“Pick Cancelled”,“file”:“src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc”,“file_line”:241,“referenced_errors”:[{“created”:"@1547552759.642000000",“description”:“Connect Failed”,“file”:“src/core/ext/filters/client_channel/subchannel.cc”,“file_line”:689,“grpc_status”:14,“referenced_errors”:[{“created”:"@1547552759.642000000",“description”:“Peer name localhost is not in peer certificate”,“file”:“src/core/lib/security/security_connector/security_connector.cc”,“file_line”:880}]}]}]}">!!! 警告:这是因为TSLSSL模式下,客户端是通过服务名称:port来获取服务的凭据,而不是ip:port, 所以对客户端做如下修改:# 修改前channel = grpc.secure_channel(’localhost:50051’, credentials)# 修改后channel = grpc.secure_channel(‘Echoer:50051’, credentials)!!! 警告:其次,在TSLSSL模式下,客户端对服务名称:port解析时候需要dns支持,目前不知道如何解决,只能够采取以下措施解决,通过修改windows的host文件,利用host将服务名称解析为IP地址,打开windows的host文件,地址:C:\Windows\System32\drivers\etc\hosts备份后修改如下,添加:# 服务的IP地址 服务名称127.0.0.1 Echoer保存即可修改后,再次运行,即可运行成功注意事项:CA证书和私钥key都是配套的,不配套的CA证书和key是无法校验成功的结合consul注意事项:确保consul已经正确启动,查看http://ip:port:8500/, 可查看consul的状态,确保已经安装python-consul这个库,否则无法操作consul首先想象我们以上的grpc示例程序之所以成功的有限制条件,我们知道服务端已经正常启动我们知道了服务端的ip和端口但在实际过程中,一般是不可能确切知道服务的ip和端口的,所以consul就起了个中间桥梁的作用,具体如下:服务注册服务注册,顾名思义,服务在启动之前,必须现在consul中注册。服务端:当服务端启动之后,consul会利用服务注册时获得的ip和port同服务建立联系,其中最重要的就是health check即心跳检测。consul通过心跳检测来判定该服务是否正常。客户端:客户端通过consul来查询所需服务的ip和port,若对应服务已经注册且心跳检测正常,则会返回给客户端对应的ip和port信息,然后客户端就可以利用这个来连接服务端了服务注册示例代码如下:def register(self, server_name, ip, port, consul_host=CONSUL_HOST): """ server_name: 服务名称 ip: 服务IP地址 port: 服务监听的端口 consul_host: 所连接的consul服务器的IP地址 """ c = consul.Consul(host=consul_host) # 获取与consul的连接 print(f"开始注册服务{server_name}") check = consul.Check.tcp(ip, port, “10s”) # 设置心跳检测的超时时间和对应的ip和port端口 c.agent.service.register(server_name, f"{server_name}-{ip}-{port}", address=ip, port=port, check=check) # 注册既然有服务注册,当然会有服务注销,示例代码如下:def unregister(self, server_name, ip, port, consul_host=CONSUL_HOST): c = consul.Consul(host=consul_host) print(f"开始退出服务{server_name}") c.agent.service.deregister(f"{server_name}-{ip}-{port}")服务查询客户端则需要在consul中查询对应服务的IP和port,但由于在TSL/SSL模式下,所需的只是服务名称和port,故而只需要查询port端口即可。客户端服务查询采用的是DNS的查询方式,必须确保安装dnspython库,用于创建DNS查询服务查询示例代码如下:# 创建一个consul dns查询的 resolverconsul_resolver = resolver.Resolver()consul_resolver.port = 8600consul_resolver.nameservers = [consul_host]def get_host_port(self, server_name): try: dns_answer_srv = consul_resolver.query(f"{server_name}.service.consul", “SRV”) # 查询对应服务的port, except DNSException as e: return None, None return server_name, dns_answer_srv[0].port # 返回服务名和端口grpc流模式grpc总共提供了四种数据交互模式:simpe 简单模式 RPC:即上述的所有的grpcserver-side streaming 服务端流式 RPCclient-side streaming 客户端流式 RPCBidirectional streaming 双向数据流模式的 gRPC由于grpc对于消息有大小限制,diff数据过大会导致无法接收数据,我们在使用过程中,使用了流模式来解决了此类问题,在此模式下,客户端传入的参数由具体的protobuf变为了protobuf的迭代器,客户端接收的响应也变为了迭代器,获取完整的响应则需要迭代获取。服务端响应也变为了一个迭代器。修改服务定义文件:# 修改前service Echoer{ rpc echo (Req) returns (Resp) {}}# 修改后service Echoer{ rpc echo (stream Req) returns (stream Resp) {}}重新编译修改服务端将工作函数修改为如下所示, 即工作函数变成了一个迭代器:def echo(self, request_iterator, context): for i in range(10): yield Echoer_pb2.Resp(a=“echo”)修改客户端将echo的传入参数修改为迭代器:def qq(): for i in range(10): yield Echoer_pb2.Req(q=“echo”)response = stub.echo(qq())for resp in response: print(“Echoer client received: " + response.a)重新运行,接收结果如下:$ python Echoer_client.pyEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echoEchoer client received: echo ...

January 19, 2019 · 3 min · jiezi

浏览器引入gRPC的现况

作者:Johan BrandhorstgRPC 1.0于2016年8月发布,现已发展成为应用通信的首选技术解决方案之一。它已被全球的初创公司、企业公司和开源项目采用。它对多语言环境的支持、关注性能、类型安全性和开发者生产力已经改变了开发者设计架构的方式。到目前为止,基本上只有移动应用程序和后端开发者获得这些好处,而前端开发者不得不继续依赖JSON REST接口作为其主要的信息交换方式。然而,随着gRPC-Web的发布,gRPC有望成为前端开发者工具箱中的有价值补充。在这篇文章中,我将描述gRPC在浏览器中的一些历史,探索当前的状态,并分享对未来的一些看法。初期在2016年夏天,Google和Improbable(1)的团队独立地开始实施可以称为“浏览器的gRPC”的东西。他们很快发现了彼此的存在,并聚在一起为新协议定义了规范(2)。gRPC-Web规范目前无法在浏览器中实现HTTP/2 gRPC规范(3),因为没有浏览器API对请求提供足够的细粒度控制。例如:没有办法强制使用HTTP/2,即使有,也无法在浏览器中访问原始HTTP/2帧。gRPC-Web规范从HTTP/2规范的角度出发,然后定义差异。这些特别包括:支持HTTP/1.1和HTTP/2。在请求/响应主体的最末端发送gRPC跟踪程序,如gRPC消息头(4)中的新位所示。用于在gRPC-Web请求和gRPC HTTP/2响应之间进行转换的强制代理。技术部分基本思想是让浏览器发送正常的HTTP请求(使用Fetch或XHR),并在gRPC服务器前面有一个小代理,将请求和响应转换为浏览器可以使用的内容。两个实现方式Google和Improbable的团队在两个不同的存储库中实现了规范(5,6),并且采用了稍微不同的实现,它们都不完全符合规范,在很长一段时间内都不兼容另一个代理(7,8)。Improbable的gRPC-Web客户端(9)以TypeScript实现,可以在npm上以grpc-web-client(10)获得。还有一个Go代理可用,既可作为导入现有Go gRPC服务器的软件包(11),也可作为独立代理,将任意gRPC服务器暴露给gRPC-Web前端(12)。Google的gRPC-Web客户端(13)使用Google Closure库(14)以JavaScript实现,可以在npm上以grpc-web(15)获得。它最初附带作为NGINX扩展实现的代理(16),但后来在Envoy代理HTTP过滤器(17)上提供,该过滤器自v1.4.0以来在所有版本中都可获得。功能集gRPC HTTP/2的实现都支持四种方法类型:一元(unary)、服务器端、客户端和双向流。但是,gRPC-Web规范并未强制要求任何客户端或双向流支持,只是在浏览器中实现WHATWG Streams(18)后才会实现。Google客户端支持一元和服务器端流,但仅在与grpcwebtext模式一起使用时才支持。grpcweb模式只完全支持一元请求。这两种模式指定了在请求和响应中编码protobuf有效负载的不同方法。Improbable客户端支持一元和服务器端流,并且实现根据浏览器功能在XHR和Fetch之间自动选择。这表格总结了支持的不同功能:有关此表格的更多信息,请参阅我在github上的兼容性测试repo。兼容性测试可能演变为一些自动化测试框架,以便在将来强制执行和记录各种兼容性。兼容性问题当然,有两个不同的代理也会出现兼容性问题。幸运的是,最近已经解决了这些问题,因此你可以期望将任一客户端与任一代理一起使用。未来Google的实施在2018年10月(21)公布了版本1.0和一般可用性,并公布了未来目标的路线图(22),包括:类似JSON的有效消息编码Node、Python、Java等的进程内代理与流行框架集成(React、Angular、Vue)Fetch API传输以实现内存高效的流式传输双向流支持Google正在寻求有关哪些功能对社区很重要的反馈,如果你认为其中任何一项对您特别有价值,请填写他们的调查(23)。两个项目最近的对话已经同意将Google客户端和Envoy代理作为新用户的首选解决方案。Improbable的客户端和代理将作为规范的替代实现,而不依赖于Google Closure,但应被视为实验性的。将为现有用户生成迁移指南,以便迁移到Google客户端,团队也正在共同协作所生成的API。结论Google客户端将继续以稳定的速度实施新的功能和修复,其团队致力于成功,并且它是官方的gRPC客户。它没有像Improbable客户端那样的Fetch API支持,但如果这是社区所需的一个重要功能,它将被添加。Google团队和更大的社区正在为官方客户端进行合作,以使gRPC社区受益。自GA宣布以来,社区对Google gRPC-Web存储库的贡献大幅增加。在两个代理之间进行选择时,功能没有区别,所以它成为你部署模型的问题。Envoy将适合某些场景,而进程中的Go代理有其自身的优势。如果你今天开始使用gRPC-Web,请先试用Google客户端。它具有严格的API兼容性保证,并建立在Gmail和Google Maps使用的坚如磐石的Google Closure库基础之上。如果你需要Fetch API的内存效率,或实验性的websocket客户端和双向流,Improbable客户端是一个不错的选择,并且在可预见的未来继续由Improbable使用和维护。无论哪种方式,gRPC-Web都是Web开发者的绝佳选择。它将复杂协议的可移植性、性能和工程设计引入浏览器,并为前端开发者带来激动人心的时刻!参考文献https://improbable.io/games/b…https://github.com/grpc/grpc/...https://github.com/grpc/grpc/...https://github.com/grpc/grpc/...https://github.com/improbable...https://github.com/grpc/grpc-webhttps://github.com/improbable...https://github.com/grpc/grpc-...https://github.com/improbable...https://www.npmjs.com/package...https://github.com/improbable...https://github.com/improbable...https://github.com/grpc/grpc-...https://developers.google.com...https://www.npmjs.com/package...https://github.com/grpc/grpc-...https://www.envoyproxy.io/doc...https://streams.spec.whatwg.org/The Improbable client supports client-side and bi-directional streaming with an experimental websocket transport. This is not part of the gRPC-Web spec, and is not recommended for production use.grpcweb allows server streaming methods to be called, but it doesn’t return data until the stream has closed.https://grpc.io/blog/grpc-web-gahttps://github.com/grpc/grpc-…https://docs.google.com/forms…

January 9, 2019 · 1 min · jiezi

Hyperledger Fabric Node.js 智能合约即链码开发

Hyperledger Fabric是一种联盟区块链,Fabric区块链也支持智能合约,被称为链码(Chaincode)。Fabric链码就是一个标准的(运行在docker容器中的)操作系统进程,通过gRPC协议与Fabric节点通信。因此理论上可以使用任何语言开发Fabric链码。目前官方提供了三种开发语言的Fabric链码开发工具包:Go、Java和Node.js,本文将介绍如何使用node.js开发Fabric链码。上汇智网,用互动方式学习以太坊、比特币、EOS、tendermint等更多区块链开发教程。Fabric官方提供了两种开发node.js链码的途径:fabric-shim和fabric-contract-api。使用fabric-shim开发Fabric链码fabric-shim是较底层的链码开发包,它封装了与节点通信的grpc协议。安装方法如下:/fabric-shim-chaincode-demo$ npm install fabric-shimfabric-shim要求链码开发者定义一个实现两个预定义方法的类。Init(stub):初始化链码时节点将调用该方法Invoke(stub):节点将应用对链码的调用转化为对该方法的调用参数stub由节点传入,它提供了访问链上账本的方法,以便读取或更新账本状态。例如,下面的代码实现了一个最小化的node.js链码,每次调用链码都会更新acc0的状态(例如:可以使用这个状态代表账户余额):const shim = require(‘fabric-shim’);class EzChaincode { async Init(stub) { return shim.success(Buffer.from(‘init done!’));//返回success对象 } async Invoke(stub) { let key = ‘acc0’; let oldValue = await stub.getState(key); //读取账本中acc0的状态 let newValue = oldValue + 100; await stub.putState(key, Buffer.from(newValue)); //更新acc0的状态 return shim.success(Buffer.from(‘update done!’));//返回success对象 }};一旦定义好链码,就可以使用shim.start()方法启动链码实例了。例如:const shim = require(‘fabric-shim’);class EzChainCode {…}shim.start(new EzChaincode());这就是一个完整的Fabric链码了!将上面代码保存为demo.js,可以直接用node.js启动:/fabric-shim-chaincode-demo$ node demo.js使用fabric-contract-api开发Fabric链码fabric-shim是一种相对底层的fabric grpc协议封装,它直接把链码接口暴露给开发者,虽然简单直白,但如果要实现相对复杂一点的链码,开发者需要自己在Invoke实现中进行方法路由。fabric-contract-api则是更高层级的封装,开发者直接继承开发包提供的Contract类,就不用费心合约方法路由的问题了。fabric-contrac-api开发方法如下:/fabric-contract-api-demo$ npm install fabric-contract-api使用fabric-contract-api的链码示例代码如下,除了构造函数之外的每个方法都自动称为链码的方法,可供外部应用调用 ://demo.jsconst { Contract } = require(‘fabric-contract-api’);class EzContract extends Contract constructor(){ super(‘EzContract’); } async update(ctx, newValue) { await ctx.stub.putState(‘acc0’, Buffer.from(newValue)); return Buffer.from(‘update done!’); } async remove(ctx) { //….. }};module.exports.contracts = [‘EzContract’];与fabric-shim不同,fabric-contract-api只需要链码导出contracts数组,因此不能直接使用node.js启动链码,而需要使用fabric-chaincode-node程序。例如:/fabric-contract-api-demo$ fabric-chaincode-node demo.js汇智网原创,转载请标明出处。 ...

December 30, 2018 · 1 min · jiezi

gRPC遇见.NET SDK和Visual Studio:构建时自动生成编码

作者:Kirill’kkm’Katsnelson作为微软向其跨平台.NET产品发展的一部分,他们大大简化了项目文件格式,并允许第三方代码生成器与.NET项目的紧密集成。我们一直倾听,现在很自豪地介绍从Grpc.Tools NuGet包的1.17版本开始,.NET C#项目中的Protocol Buffer和gRPC服务.proto文件的集成编译。1.17版本现在可以从Nuget.org获得。你不再需要使用手写脚本从.proto文件生成代码:.NET构建神奇地为你处理此问题。集成工具在调用代码生成器之前,定位proto编译器和gRPC插件,标准Protocol Buffer导入和跟踪依赖关系,以便生成的C#源文件永远不会过时,同时将重新生成保持在最低要求。实质上,.proto文件被视为.NET C#项目中的第一类源。演练在这篇博文中,我们将介绍最简单,且可能是最常见的方案,使用跨平台dotnet命令从.proto文件创建库。我们将基本实现Greeter库的克隆,由C#Helloworld示例目录中的客户端和服务器项目共享。创建新项目让我们从创建新的库项目开始。/work$ dotnet new classlib -o MyGreeterThe template “Class library” was created successfully./work$ cd MyGreeter~/work/MyGreeter$ ls -lFtotal 12-rw-rw-r– 1 kkm kkm 86 Nov 9 16:10 Class1.cs-rw-rw-r– 1 kkm kkm 145 Nov 9 16:10 MyGreeter.csprojdrwxrwxr-x 2 kkm kkm 4096 Nov 9 16:10 obj/观察到dotnet new命令创建了我们不需要的文件Class1.cs,因此将其删除。另外,我们需要一些.proto文件来编译。在本练习中,我们将从gRPC发行版中复制示例文件examples/protos/helloworld.proto。/work/MyGreeter$ rm Class1.cs/work/MyGreeter$ wget -q https://raw.githubusercontent.com/grpc/grpc/master/examples/protos/helloworld.proto(在Windows上,使用del Class1.cs,如果你没有wget命令,只需打开上面的URL,并使用Web浏览器中的“另存为…”命令)。接下来,将必需的NuGet包添加到项目中:/work/MyGreeter$ dotnet add package Grpcinfo : PackageReference for package ‘Grpc’ version ‘1.17.0’ added to file ‘/home/kkm/work/MyGreeter/MyGreeter.csproj’./work/MyGreeter$ dotnet add package Grpc.Toolsinfo : PackageReference for package ‘Grpc.Tools’ version ‘1.17.0’ added to file ‘/home/kkm/work/MyGreeter/MyGreeter.csproj’./work/MyGreeter$ dotnet add package Google.Protobufinfo : PackageReference for package ‘Google.Protobuf’ version ‘3.6.1’ added to file ‘/home/kkm/work/MyGreeter/MyGreeter.csproj’.将.proto文件添加到项目中接下来是一个重要的部分。首先,默认情况下,.csproj项目文件会自动在其目录中找到所有.cs文件,尽管Microsoft现在建议禁止这种通配行为,所以我们也决定不通配.proto文件。因此,必须明确地将.proto文件添加到项目中。其次,将属性PrivateAssets=“All”添加到Grpc.Tools包参考中是非常重要,这样新库的使用者就不会不必要地获取它。这是有道理的,因为程序包只包含编译器、代码生成器和导入文件,这些在.proto文件编译的项目之外是不需要的。虽然,在这个简单的演练中并非严格要求,但始终应该是你的标准做法。因此,编辑文件MyGreeter.csproj以添加helloworld.proto以便将其编译,并将PrivateAssets属性添加到Grpc.Tools包参考中。你生成的项目文件现在应如下所示:<Project Sdk=“Microsoft.NET.Sdk”> <PropertyGroup> <TargetFramework>netstandard2.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include=“Google.Protobuf” Version=“3.6.1” /> <PackageReference Include=“Grpc” Version=“1.17.0” /> <!– The Grpc.Tools package generates C# sources from .proto files during project build, but is not needed by projects using the built library. It’s IMPORTANT to add the ‘PrivateAssets=“All”’ to this reference: –> <PackageReference Include=“Grpc.Tools” Version=“1.17.0” PrivateAssets=“All” /> <!– Explicitly include our helloworld.proto file by adding this line: –> <Protobuf Include=“helloworld.proto” /> </ItemGroup></Project>构建它!此时,你可以使用dotnet build命令构建项目,以编译.proto文件和库程序集。在本演练中,我们将在命令中添加日志切换开关-v:n,所以我们可以看到编译helloworld.proto文件的命令是在运行。你可能会发现,在第一次编译项目时,总是这样做是个好主意!请注意,下面省略了许多输出行,因为构建输出非常详细。/work/MyGreeter$ dotnet build -v:nBuild started 11/9/18 5:33:44 PM. 1:7>Project “/home/kkm/work/MyGreeter/MyGreeter.csproj” on node 1 (Build target(s)). 1>_Protobuf_CoreCompile: /home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/protoc –csharp_out=obj/Debug/netstandard2.0 –plugin=protoc-gen-grpc=/home/kkm/.nuget/packages/grpc.tools/1.17.0/tools/linux_x64/grpc_csharp_plugin –grpc_out=obj/Debug/netstandard2.0 –proto_path=/home/kkm/.nuget/packages/grpc.tools/1.17.0/build/native/include –proto_path=. –dependency_out=obj/Debug/netstandard2.0/da39a3ee5e6b4b0d_helloworld.protodep helloworld.proto CoreCompile: [ … skipping long output … ] MyGreeter -> /home/kkm/work/MyGreeter/bin/Debug/netstandard2.0/MyGreeter.dllBuild succeeded.如果此时再次调用dotnet build -v:n命令,则不会调用protoc,也不会编译C#源。但是,如果你更改了helloworld.proto源代码,那么在构建期间它的输出将被重新生成,然后由C#编译器重新编译。这是你期望修改任何源文件的常规依赖关系跟踪行为。当然,你也可以将.cs文件添加到同一个项目中:毕竟,它是构建.NET库的常规C#项目。我们在RouteGuide示例中是这样做的。生成的文件在哪里?你可能想知道原型编译器和gRPC插件输出C#文件的位置。默认情况下,它们与其他生成的文件,放在同一目录中,例如对象(在.NET构建用语中称为“中间输出”目录),在obj/目录下。这是.NET构建的常规做法,因此自动生成的文件,不会使工作目录混乱,或意外地置于源代码控制之下。否则,调试器等工具可以访问它们。你也可以在该目录中看到其他自动生成的源:~/work/MyGreeter$ find obj -name ‘.cs’obj/Debug/netstandard2.0/MyGreeter.AssemblyInfo.csobj/Debug/netstandard2.0/Helloworld.csobj/Debug/netstandard2.0/HelloworldGrpc.cs(如果你从Windows命令提示符下执行此演练,请使用dir /s obj .cs)还有更多虽然,在许多情况下最简单的默认行为是足够的,但是有很多方法可以在大型项目中,微调.proto编译过程。如果你发现默认安排不适合你的工作流程,我们建议你阅读文档文件BUILD-INTEGRATION.md,以获取可用选项。该软件包还扩展了Visual Studio的“属性”窗口,因此你可以在Visual Studio界面中为每个文件设置一些选项。“经典”.csproj项目和Mono也有支持。分享你的经验与任何复杂功能的初始版本一样,我们很高兴收到你的反馈。有什么不符合预期的工作?你有不容易用新工具覆盖的场景吗?你是否知道如何改善工作流程?请仔细阅读文档,然后在GitHub上的gRPC代码存储库中提交问题。你的反馈,对于确定构建集成工作的未来发展方向,非常重要! ...

December 20, 2018 · 1 min · jiezi

带入gRPC:分布式链路追踪 gRPC-Opentracing-Zipkin

带入gRPC:分布式链路追踪 gRPC + Opentracing + Zipkin原文地址:带入gRPC:分布式链路追踪 gRPC + Opentracing + Zipkin项目地址:https://github.com/EDDYCJY/go…前言在实际应用中,你做了那么多 Server 端,写了 N 个 RPC 方法。想看看方法的指标,却无处下手?本文将通过 gRPC + Opentracing + Zipkin 搭建一个分布式链路追踪系统来实现查看整个系统的链路、性能等指标 ????Opentracing是什么OpenTracing 通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现不过 OpenTracing 并不是标准。因为 CNCF 不是官方标准机构,但是它的目标是致力为分布式追踪创建更标准的 API 和工具名词解释Trace一个 trace 代表了一个事务或者流程在(分布式)系统中的执行过程Span一个 span 代表在分布式系统中完成的单个工作单元。也包含其他 span 的 “引用”,这允许将多个 spans 组合成一个完整的 Trace每个 span 根据 OpenTracing 规范封装以下内容:操作名称开始时间和结束时间key:value span Tagskey:value span LogsSpanContextTagsSpan tags(跨度标签)可以理解为用户自定义的 Span 注释。便于查询、过滤和理解跟踪数据LogsSpan logs(跨度日志)可以记录 Span 内特定时间或事件的日志信息。主要用于捕获特定 Span 的日志信息以及应用程序本身的其他调试或信息输出SpanContextSpanContext 代表跨越进程边界,传递到子级 Span 的状态。常在追踪示意图中创建上下文时使用Baggage ItemsBaggage Items 可以理解为 trace 全局运行中额外传输的数据集合一个案例图中可以看到以下内容:执行时间的上下文服务间的层次关系服务间串行或并行调用链结合以上信息,在实际场景中我们可以通过整个系统的调用链的上下文、性能等指标信息,一下子就能够发现系统的痛点在哪儿Zipkin是什么Zipkin 是分布式追踪系统。它的作用是收集解决微服务架构中的延迟问题所需的时序数据。它管理这些数据的收集和查找Zipkin 的设计基于 Google Dapper 论文。运行docker run -d -p 9411:9411 openzipkin/zipkin其他方法安装参见:https://github.com/openzipkin…验证访问 http://127.0.0.1:9411/zipkin/ 检查 Zipkin 是否运行正常gRPC + Opentracing + Zipkin在前面的小节中,我们做了以下准备工作:了解 Opentracing 是什么搭建 Zipkin 提供分布式追踪系统的功能接下来实现 gRPC 通过 Opentracing 标准 API 对接 Zipkin,再通过 Zipkin 去查看数据目录结构新建 simple_zipkin_client、simple_zipkin_server 目录,目录结构如下:go-grpc-example├── LICENSE├── README.md├── client│ ├── …│ ├── simple_zipkin_client├── conf├── pkg├── proto├── server│ ├── …│ ├── simple_zipkin_server└── vendor安装$ go get -u github.com/openzipkin/zipkin-go-opentracing$ go get -u github.com/grpc-ecosystem/grpc-opentracing/go/otgrpcgRPCServerpackage mainimport ( “context” “log” “net” “github.com/grpc-ecosystem/go-grpc-middleware” “github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc” zipkin “github.com/openzipkin/zipkin-go-opentracing” “google.golang.org/grpc” “github.com/EDDYCJY/go-grpc-example/pkg/gtls” pb “github.com/EDDYCJY/go-grpc-example/proto”)type SearchService struct{}func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil}const ( PORT = “9005” SERVICE_NAME = “simple_zipkin_server” ZIPKIN_HTTP_ENDPOINT = “http://127.0.0.1:9411/api/v1/spans” ZIPKIN_RECORDER_HOST_PORT = “127.0.0.1:9000”)func main() { collector, err := zipkin.NewHTTPCollector(ZIPKIN_HTTP_ENDPOINT) if err != nil { log.Fatalf(“zipkin.NewHTTPCollector err: %v”, err) } recorder := zipkin.NewRecorder(collector, true, ZIPKIN_RECORDER_HOST_PORT, SERVICE_NAME) tracer, err := zipkin.NewTracer( recorder, zipkin.ClientServerSameSpan(false), ) if err != nil { log.Fatalf(“zipkin.NewTracer err: %v”, err) } tlsServer := gtls.Server{ CaFile: “../../conf/ca.pem”, CertFile: “../../conf/server/server.pem”, KeyFile: “../../conf/server/server.key”, } c, err := tlsServer.GetCredentialsByCA() if err != nil { log.Fatalf(“GetTLSCredentialsByCA err: %v”, err) } opts := []grpc.ServerOption{ grpc.Creds(c), grpc_middleware.WithUnaryServerChain( otgrpc.OpenTracingServerInterceptor(tracer, otgrpc.LogPayloads()), ), } …}zipkin.NewHTTPCollector:创建一个 Zipkin HTTP 后端收集器zipkin.NewRecorder:创建一个基于 Zipkin 收集器的记录器zipkin.NewTracer:创建一个 OpenTracing 跟踪器(兼容 Zipkin Tracer)otgrpc.OpenTracingClientInterceptor:返回 grpc.UnaryServerInterceptor,不同点在于该拦截器会在 gRPC Metadata 中查找 OpenTracing SpanContext。如果找到则为该服务的 Span Context 的子节点otgrpc.LogPayloads:设置并返回 Option。作用是让 OpenTracing 在双向方向上记录应用程序的有效载荷(payload)总的来讲,就是初始化 Zipkin,其又包含收集器、记录器、跟踪器。再利用拦截器在 Server 端实现 SpanContext、Payload 的双向读取和管理Clientfunc main() { // the same as zipkin server // … conn, err := grpc.Dial(":"+PORT, grpc.WithTransportCredentials(c), grpc.WithUnaryInterceptor( otgrpc.OpenTracingClientInterceptor(tracer, otgrpc.LogPayloads()), )) …}otgrpc.OpenTracingClientInterceptor:返回 grpc.UnaryClientInterceptor。该拦截器的核心功能在于:(1)OpenTracing SpanContext 注入 gRPC Metadata (2)查看 context.Context 中的上下文关系,若存在父级 Span 则创建一个 ChildOf 引用,得到一个子 Span其他方面,与 Server 端是一致的,先初始化 Zipkin,再增加 Client 端特需的拦截器。就可以完成基础工作啦验证启动 Server.go,执行 Client.go。查看 http://127.0.0.1:9411/zipkin/ 的示意图:复杂点来,自己实践一下总结在多服务下的架构下,串行、并行、服务套服务是一个非常常见的情况,用常规的方案往往很难发现问题在哪里(成本太大)。而这种情况就是分布式追踪系统大展拳脚的机会了希望你通过本章节的介绍和学习,能够了解其概念和搭建且应用一个追踪系统 ????参考本系列示例代码go-grpc-example系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server带入gRPC:gRPC Streaming, Client and Server带入gRPC:TLS 证书认证带入gRPC:基于 CA 的 TLS 证书认证带入gRPC:Unary and Stream interceptor带入gRPC:让你的服务同时提供 HTTP 接口带入gRPC:对 RPC 方法做自定义认证带入gRPC:gRPC Deadlines带入gRPC:分布式链路追踪 gRPC+Opentracing+Zipkin资料opentracingzipkin ...

October 21, 2018 · 2 min · jiezi

带入gRPC:让你的服务同时提供 HTTP 接口

带入gRPC:让你的服务同时提供 HTTP 接口原文地址:带入gRPC:让你的服务同时提供 HTTP 接口项目地址:https://github.com/EDDYCJY/go…前言接口需要提供给其他业务组访问,但是 RPC 协议不同无法内调,对方问能否走 HTTP 接口,怎么办?微信(公众号、小程序)等第三方回调接口只支持 HTTP 接口,怎么办我相信你在实际工作中都会遇到如上问题,在 gRPC 中都是有解决方案的,本章节将会进行介绍 ????为什么可以同时提供 HTTP 接口关键一点,gRPC 的协议是基于 HTTP/2 的,因此应用程序能够在单个 TCP 端口上提供 HTTP/1.1 和 gRPC 接口服务(两种不同的流量)怎么同时提供 HTTP 接口检测协议if r.ProtoMajor == 2 && strings.Contains(r.Header.Get(“Content-Type”), “application/grpc”) { server.ServeHTTP(w, r)} else { mux.ServeHTTP(w, r)}流程检测请求协议是否为 HTTP/2判断 Content-Type 是否为 application/grpc(gRPC 的默认标识位)根据协议的不同转发到不同的服务处理gRPCTLS在前面的章节,为了便于展示因此没有简单封装在本节需复用代码,重新封装了,可详见:go-grpc-example目录结构新建 simple_http_client、simple_http_server 目录,目录结构如下:go-grpc-example├── client│ ├── simple_client│ ├── simple_http_client│ └── stream_client├── conf├── pkg│ └── gtls├── proto├── server│ ├── simple_http_server│ ├── simple_server│ └── stream_serverServer在 simple_http_server 目录下新建 server.go,写入文件内容:package mainimport ( “context” “log” “net/http” “strings” “github.com/EDDYCJY/go-grpc-example/pkg/gtls” pb “github.com/EDDYCJY/go-grpc-example/proto” “google.golang.org/grpc”)type SearchService struct{}func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " HTTP Server"}, nil}const PORT = “9003"func main() { certFile := “../../conf/server/server.pem” keyFile := “../../conf/server/server.key” tlsServer := gtls.Server{ CertFile: certFile, KeyFile: keyFile, } c, err := tlsServer.GetTLSCredentials() if err != nil { log.Fatalf(“tlsServer.GetTLSCredentials err: %v”, err) } mux := GetHTTPServeMux() server := grpc.NewServer(grpc.Creds(c)) pb.RegisterSearchServiceServer(server, &SearchService{}) http.ListenAndServeTLS(”:"+PORT, certFile, keyFile, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.ProtoMajor == 2 && strings.Contains(r.Header.Get(“Content-Type”), “application/grpc”) { server.ServeHTTP(w, r) } else { mux.ServeHTTP(w, r) } return }), )}func GetHTTPServeMux() *http.ServeMux { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { w.Write([]byte(“eddycjy: go-grpc-example”)) }) return mux}http.NewServeMux:创建一个新的 ServeMux,ServeMux 本质上是一个路由表。它默认实现了 ServeHTTP,因此返回 Handler 后可直接通过 HandleFunc 注册 pattern 和处理逻辑的方法http.ListenAndServeTLS:可简单的理解为提供监听 HTTPS 服务的方法,重点的协议判断转发,也在这里面其实,你理解后就会觉得很简单,核心步骤:判断 -> 转发 -> 响应。我们改变了前两步的默认逻辑,仅此而已Client在 simple_http_server 目录下新建 client.go,写入文件内容:package mainimport ( “context” “log” “google.golang.org/grpc” “github.com/EDDYCJY/go-grpc-example/pkg/gtls” pb “github.com/EDDYCJY/go-grpc-example/proto”)const PORT = “9003"func main() { tlsClient := gtls.Client{ ServerName: “go-grpc-example”, CertFile: “../../conf/server/server.pem”, } c, err := tlsClient.GetTLSCredentials() if err != nil { log.Fatalf(“tlsClient.GetTLSCredentials err: %v”, err) } conn, err := grpc.Dial(”:"+PORT, grpc.WithTransportCredentials(c)) if err != nil { log.Fatalf(“grpc.Dial err: %v”, err) } defer conn.Close() client := pb.NewSearchServiceClient(conn) resp, err := client.Search(context.Background(), &pb.SearchRequest{ Request: “gRPC”, }) if err != nil { log.Fatalf(“client.Search err: %v”, err) } log.Printf(“resp: %s”, resp.GetResponse())}验证gRPC Client$ go run client.go 2018/10/04 14:56:56 resp: gRPC HTTP ServerHTTP/1.1 访问总结通过本章节,表面上完成了同端口提供双服务的功能,但实际上,应该是加深了 HTTP/2 的理解和使用,这才是本质拓展如果你有一个需求,是要同时提供 RPC 和 RESTful JSON API 两种接口的,不要犹豫,点进去:gRPC + gRPC Gateway 实践问题你以为这个方案就万能了吗,不。Envoy Proxy 的支持就不完美,无法同时监听一个端口的两种流量 ????参考本系列示例代码go-grpc-example系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server带入gRPC:gRPC Streaming, Client and Server带入gRPC:TLS 证书认证带入gRPC:基于 CA 的 TLS 证书认证带入gRPC:Unary and Stream interceptor带入gRPC:让你的服务同时提供 HTTP 接口带入gRPC:对 RPC 方法做自定义认证带入gRPC:gRPC Deadlines ...

October 13, 2018 · 2 min · jiezi

带入gRPC:gRPC Streaming, Client and Server

带入gRPC:gRPC Streaming, Client and Server原文地址:带入gRPC:gRPC Streaming, Client and Server前言本章节将介绍 gRPC 的流式,分为三种类型:Server-side streaming RPC:服务器端流式 RPCClient-side streaming RPC:客户端流式 RPCBidirectional streaming RPC:双向流式 RPC流任何技术,因为有痛点,所以才有了存在的必要性。如果您想要了解 gRPC 的流式调用,请继续图gRPC Streaming 是基于 HTTP/2 的,后续章节再进行详细讲解为什么不用 Simple RPC流式为什么要存在呢,是 Simple RPC 有什么问题吗?通过模拟业务场景,可得知在使用 Simple RPC 时,有如下问题:数据包过大造成的瞬时压力接收数据包时,需要所有数据包都接受成功且正确后,才能够回调响应,进行业务处理(无法客户端边发送,服务端边处理)为什么用 Streaming RPC大规模数据包实时场景模拟场景每天早上 6 点,都有一批百万级别的数据集要同从 A 同步到 B,在同步的时候,会做一系列操作(归档、数据分析、画像、日志等)。这一次性涉及的数据量确实大在同步完成后,也有人马上会去查阅数据,为了新的一天筹备。也符合实时性。两者相较下,这个场景下更适合使用 Streaming RPCgRPC在讲解具体的 gRPC 流式代码时,会着重在第一节讲解,因为三种模式其实是不同的组合。希望你能够注重理解,举一反三,其实都是一样的知识点 ????目录结构$ tree go-grpc-example go-grpc-example├── client│ ├── simple_client│ │ └── client.go│ └── stream_client│ └── client.go├── proto│ ├── search.proto│ └── stream.proto└── server ├── simple_server │ └── server.go └── stream_server └── server.go增加 stream_server、stream_client 存放服务端和客户端文件,proto/stream.proto 用于编写 IDLIDL在 proto 文件夹下的 stream.proto 文件中,写入如下内容:syntax = “proto3”;package proto;service StreamService { rpc List(StreamRequest) returns (stream StreamResponse) {}; rpc Record(stream StreamRequest) returns (StreamResponse) {}; rpc Route(stream StreamRequest) returns (stream StreamResponse) {};}message StreamPoint { string name = 1; int32 value = 2;}message StreamRequest { StreamPoint pt = 1;}message StreamResponse { StreamPoint pt = 1;}注意关键字 stream,声明其为一个流方法。这里共涉及三个方法,对应关系为List:服务器端流式 RPCRecord:客户端流式 RPCRoute:双向流式 RPC基础模板 + 空定义Serverpackage mainimport ( “log” “net” “google.golang.org/grpc” pb “github.com/EDDYCJY/go-grpc-example/proto” )type StreamService struct{}const ( PORT = “9002”)func main() { server := grpc.NewServer() pb.RegisterStreamServiceServer(server, &StreamService{}) lis, err := net.Listen(“tcp”, “:"+PORT) if err != nil { log.Fatalf(“net.Listen err: %v”, err) } server.Serve(lis)}func (s *StreamService) List(r *pb.StreamRequest, stream pb.StreamService_ListServer) error { return nil}func (s *StreamService) Record(stream pb.StreamService_RecordServer) error { return nil}func (s *StreamService) Route(stream pb.StreamService_RouteServer) error { return nil}写代码前,建议先将 gRPC Server 的基础模板和接口给空定义出来。若有不清楚可参见上一章节的知识点Clientpackage mainimport ( “log” “google.golang.org/grpc” pb “github.com/EDDYCJY/go-grpc-example/proto”)const ( PORT = “9002”)func main() { conn, err := grpc.Dial(”:"+PORT, grpc.WithInsecure()) if err != nil { log.Fatalf(“grpc.Dial err: %v”, err) } defer conn.Close() client := pb.NewStreamServiceClient(conn) err = printLists(client, &pb.StreamRequest{Pt: &pb.StreamPoint{Name: “gRPC Stream Client: List”, Value: 2018}}) if err != nil { log.Fatalf(“printLists.err: %v”, err) } err = printRecord(client, &pb.StreamRequest{Pt: &pb.StreamPoint{Name: “gRPC Stream Client: Record”, Value: 2018}}) if err != nil { log.Fatalf(“printRecord.err: %v”, err) } err = printRoute(client, &pb.StreamRequest{Pt: &pb.StreamPoint{Name: “gRPC Stream Client: Route”, Value: 2018}}) if err != nil { log.Fatalf(“printRoute.err: %v”, err) }}func printLists(client pb.StreamServiceClient, r *pb.StreamRequest) error { return nil}func printRecord(client pb.StreamServiceClient, r *pb.StreamRequest) error { return nil}func printRoute(client pb.StreamServiceClient, r *pb.StreamRequest) error { return nil}一、Server-side streaming RPC:服务器端流式 RPC服务器端流式 RPC,显然是单向流,并代指 Server 为 Stream 而 Client 为普通 RPC 请求简单来讲就是客户端发起一次普通的 RPC 请求,服务端通过流式响应多次发送数据集,客户端 Recv 接收数据集。大致如图:Serverfunc (s *StreamService) List(r *pb.StreamRequest, stream pb.StreamService_ListServer) error { for n := 0; n <= 6; n++ { err := stream.Send(&pb.StreamResponse{ Pt: &pb.StreamPoint{ Name: r.Pt.Name, Value: r.Pt.Value + int32(n), }, }) if err != nil { return err } } return nil}在 Server,主要留意 stream.Send 方法。它看上去能发送 N 次?有没有大小限制?type StreamService_ListServer interface { Send(*StreamResponse) error grpc.ServerStream}func (x *streamServiceListServer) Send(m *StreamResponse) error { return x.ServerStream.SendMsg(m)}通过阅读源码,可得知是 protoc 在生成时,根据定义生成了各式各样符合标准的接口方法。最终再统一调度内部的 SendMsg 方法,该方法涉及以下过程:消息体(对象)序列化压缩序列化后的消息体对正在传输的消息体增加 5 个字节的 header判断压缩+序列化后的消息体总字节长度是否大于预设的 maxSendMessageSize(预设值为 math.MaxInt32),若超出则提示错误写入给流的数据集Clientfunc printLists(client pb.StreamServiceClient, r *pb.StreamRequest) error { stream, err := client.List(context.Background(), r) if err != nil { return err } for { resp, err := stream.Recv() if err == io.EOF { break } if err != nil { return err } log.Printf(“resp: pj.name: %s, pt.value: %d”, resp.Pt.Name, resp.Pt.Value) } return nil}在 Client,主要留意 stream.Recv() 方法。什么情况下 io.EOF ?什么情况下存在错误信息呢?type StreamService_ListClient interface { Recv() (*StreamResponse, error) grpc.ClientStream}func (x *streamServiceListClient) Recv() (*StreamResponse, error) { m := new(StreamResponse) if err := x.ClientStream.RecvMsg(m); err != nil { return nil, err } return m, nil}通过阅读源码,可得知:当流结束(调用了 Close)时,会出现 io.EOF。而错误信息(err)基本都由另一侧反馈过来,因此进行日常处理和标记即可验证运行 stream_server/server.go:$ go run server.go运行 stream_client/client.go:$ go run client.go 2018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20182018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20192018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20202018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20212018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20222018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 20232018/09/24 16:18:25 resp: pj.name: gRPC Stream Client: List, pt.value: 2024二、Client-side streaming RPC:客户端流式 RPC客户端流式 RPC,单向流,客户端通过流式发起多次 RPC 请求给服务端,服务端发起一次响应给客户端,大致如图:Serverfunc (s *StreamService) Record(stream pb.StreamService_RecordServer) error { for { r, err := stream.Recv() if err == io.EOF { return stream.SendAndClose(&pb.StreamResponse{Pt: &pb.StreamPoint{Name: “gRPC Stream Server: Record”, Value: 1}}) } if err != nil { return err } log.Printf(“stream.Recv pt.name: %s, pt.value: %d”, r.Pt.Name, r.Pt.Value) } return nil}多了一个从未见过的方法 stream.SendAndClose,它是做什么用的呢?在这段程序中,我们对每一个 Recv 都进行了处理,当发现 io.EOF (流关闭) 后,需要将最终的响应结果发送给客户端,同时关闭正在另外一侧等待的 RecvClientfunc printRecord(client pb.StreamServiceClient, r *pb.StreamRequest) error { stream, err := client.Record(context.Background()) if err != nil { return err } for n := 0; n < 6; n++ { err := stream.Send(r) if err != nil { return err } } resp, err := stream.CloseAndRecv() if err != nil { return err } log.Printf(“resp: pj.name: %s, pt.value: %d”, resp.Pt.Name, resp.Pt.Value) return nil}stream.CloseAndRecv 和 stream.SendAndClose 是配套使用的流方法,相信聪明的你已经秒懂它的作用了验证重启 stream_server/server.go,再次运行 stream_client/client.go:stream_client:$ go run client.go2018/09/24 16:23:03 resp: pj.name: gRPC Stream Server: Record, pt.value: 1stream_server:$ go run server.go2018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 20182018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 20182018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 20182018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 20182018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 20182018/09/24 16:23:03 stream.Recv pt.name: gRPC Stream Client: Record, pt.value: 2018三、Bidirectional streaming RPC:双向流式 RPC双向流式 RPC,顾名思义是双向流。由客户端以流式的方式发起请求,服务端同样以流式的方式响应请求首个请求一定是 Client 发起,但具体交互方式(谁先谁后、一次发多少、响应多少、什么时候关闭)根据程序编写的方式来确定(可以结合协程)因此图示也千变万化,这里就不放出来了Serverfunc (s *StreamService) Route(stream pb.StreamService_RouteServer) error { n := 0 for { err := stream.Send(&pb.StreamResponse{ Pt: &pb.StreamPoint{ Name: “gPRC Stream Client: Route”, Value: int32(n), }, }) if err != nil { return err } r, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } n++ log.Printf(“stream.Recv pt.name: %s, pt.value: %d”, r.Pt.Name, r.Pt.Value) } return nil}Clientfunc printRoute(client pb.StreamServiceClient, r *pb.StreamRequest) error { stream, err := client.Route(context.Background()) if err != nil { return err } for n := 0; n <= 6; n++ { err = stream.Send(r) if err != nil { return err } resp, err := stream.Recv() if err == io.EOF { break } if err != nil { return err } log.Printf(“resp: pj.name: %s, pt.value: %d”, resp.Pt.Name, resp.Pt.Value) } stream.CloseSend() return nil}验证重启 stream_server/server.go,再次运行 stream_client/client.go:stream_server$ go run server.go2018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 20182018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 20182018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 20182018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 20182018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 20182018/09/24 16:29:43 stream.Recv pt.name: gRPC Stream Client: Route, pt.value: 2018stream_client$ go run client.go2018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 02018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 12018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 22018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 32018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 42018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 52018/09/24 16:29:43 resp: pj.name: gPRC Stream Client: Route, pt.value: 6总结在本文共介绍了三类流的交互方式,可以根据实际的业务场景去选择合适的方式。会事半功倍哦 ????系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server带入gRPC:gRPC Streaming, Client and Server ...

September 25, 2018 · 5 min · jiezi

带入gRPC:gRPC Client and Server

带入gRPC:gRPC Client and Server原文地址:带入gRPC:gRPC Client and Server前言本章节将使用 Go 来编写 gRPC Server 和 Client,让其互相通讯。在此之上会使用到如下库:google.golang.org/grpcgithub.com/golang/protobuf/protoc-gen-go安装gRPCgo get -u google.golang.org/grpcProtocol Buffers v3wget https://github.com/google/protobuf/releases/download/v3.5.1/protobuf-all-3.5.1.zipunzip protobuf-all-3.5.1.zipcd protobuf-3.5.1/./configuremakemake install检查是否安装成功protoc –version若出现以下错误,执行 ldconfig 命名就能解决这问题protoc: error while loading shared libraries: libprotobuf.so.15: cannot open shared object file: No such file or directoryProtoc Plugingo get -u github.com/golang/protobuf/protoc-gen-go安装环境若有问题,可参考我先前的文章 《介绍与环境安装》 内有详细介绍,不再赘述gRPC本小节开始正式编写 gRPC 相关的程序,一起上车吧 ????目录结构$ tree go-grpc-example go-grpc-example├── client├── proto│ └── search.proto└── server.goIDL编写在 proto 文件夹下的 search.proto 文件中,写入如下内容:syntax = “proto3”;package proto;service SearchService { rpc Search(SearchRequest) returns (SearchResponse) {}}message SearchRequest { string request = 1;}message SearchResponse { string response = 1;}生成在 proto 文件夹下执行如下命令:$ protoc –go_out=plugins=grpc:. *.protoplugins=plugin1+plugin2:指定要加载的子插件列表我们定义的 proto 文件是涉及了 RPC 服务的,而默认是不会生成 RPC 代码的,因此需要给出 plugins 参数传递给 protoc-gen-go,告诉它,请支持 RPC(这里指定了 gRPC)–go_out=.:设置 Go 代码输出的目录该指令会加载 protoc-gen-go 插件达到生成 Go 代码的目的,生成的文件以 .pb.go 为文件后缀: (冒号)冒号充当分隔符的作用,后跟所需要的参数集。如果这处不涉及 RPC,命令可简化为:$ protoc –go_out=. *.proto注:建议你看看两条命令生成的 .pb.go 文件,分别有什么区别生成后执行完毕命令后,将得到一个 .pb.go 文件,文件内容如下:type SearchRequest struct { Request string protobuf:"bytes,1,opt,name=request" json:"request,omitempty" XXX_NoUnkeyedLiteral struct{} json:"-" XXX_unrecognized []byte json:"-" XXX_sizecache int32 json:"-"}func (m *SearchRequest) Reset() { *m = SearchRequest{} }func (m *SearchRequest) String() string { return proto.CompactTextString(m) }func (*SearchRequest) ProtoMessage() {}func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}}func (m *SearchRequest) GetRequest() string { if m != nil { return m.Request } return “"}通过阅读这一部分代码,可以知道主要涉及如下方面:字段名称从小写下划线转换为大写驼峰模式(字段导出)生成一组 Getters 方法,能便于处理一些空指针取值的情况ProtoMessage 方法实现 proto.Message 的接口生成 Rest 方法,便于将 Protobuf 结构体恢复为零值Repeated 转换为切片type SearchRequest struct { Request string protobuf:"bytes,1,opt,name=request" json:"request,omitempty"}func (*SearchRequest) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{0}}type SearchResponse struct { Response string protobuf:"bytes,1,opt,name=response" json:"response,omitempty"}func (*SearchResponse) Descriptor() ([]byte, []int) { return fileDescriptor_search_8b45f79ee13ff6a3, []int{1}}…func init() { proto.RegisterFile(“search.proto”, fileDescriptor_search_8b45f79ee13ff6a3) }var fileDescriptor_search_8b45f79ee13ff6a3 = []byte{ // 131 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x29, 0x4e, 0x4d, 0x2c, 0x4a, 0xce, 0xd0, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x53, 0x4a, 0x9a, 0x5c, 0xbc, 0xc1, 0x60, 0xe1, 0xa0, 0xd4, 0xc2, 0xd2, 0xd4, 0xe2, 0x12, 0x21, 0x09, 0x2e, 0xf6, 0x22, 0x08, 0x53, 0x82, 0x51, 0x81, 0x51, 0x83, 0x33, 0x08, 0xc6, 0x55, 0xd2, 0xe1, 0xe2, 0x83, 0x29, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0x15, 0x92, 0xe2, 0xe2, 0x28, 0x82, 0xb2, 0xa1, 0x8a, 0xe1, 0x7c, 0x23, 0x0f, 0x98, 0xc1, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xe6, 0x5c, 0x6c, 0x10, 0x01, 0x21, 0x11, 0x88, 0x13, 0xf4, 0x50, 0x2c, 0x96, 0x12, 0x45, 0x13, 0x85, 0x98, 0xa3, 0xc4, 0x90, 0xc4, 0x06, 0x16, 0x37, 0x06, 0x04, 0x00, 0x00, 0xff, 0xff, 0xf3, 0xba, 0x74, 0x95, 0xc0, 0x00, 0x00, 0x00,}而这一部分代码主要是围绕 fileDescriptor 进行,在这里 fileDescriptor_search_8b45f79ee13ff6a3 表示一个编译后的 proto 文件,而每一个方法都包含 Descriptor 方法,代表着这一个方法在 fileDescriptor 中具体的 Message FieldServer这一小节将编写 gRPC Server 的基础模板,完成一个方法的调用。对 server.go 写入如下内容:package mainimport ( “context” “log” “net” “google.golang.org/grpc” pb “github.com/EDDYCJY/go-grpc-example/proto”)type SearchService struct{}func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { return &pb.SearchResponse{Response: r.GetRequest() + " Server”}, nil}const PORT = “9001"func main() { server := grpc.NewServer() pb.RegisterSearchServiceServer(server, &SearchService{}) lis, err := net.Listen(“tcp”, “:"+PORT) if err != nil { log.Fatalf(“net.Listen err: %v”, err) } server.Serve(lis)}创建 gRPC Server 对象,你可以理解为它是 Server 端的抽象对象将 SearchService(其包含需要被调用的服务端接口)注册到 gRPC Server 的内部注册中心。这样可以在接受到请求时,通过内部的服务发现,发现该服务端接口并转接进行逻辑处理创建 Listen,监听 TCP 端口gRPC Server 开始 lis.Accept,直到 Stop 或 GracefulStopClient接下来编写 gRPC Go Client 的基础模板,打开 client/client.go 文件,写入以下内容:package mainimport ( “context” “log” “google.golang.org/grpc” pb “github.com/EDDYCJY/go-grpc-example/proto”)const PORT = “9001"func main() { conn, err := grpc.Dial(”:"+PORT, grpc.WithInsecure()) if err != nil { log.Fatalf(“grpc.Dial err: %v”, err) } defer conn.Close() client := pb.NewSearchServiceClient(conn) resp, err := client.Search(context.Background(), &pb.SearchRequest{ Request: “gRPC”, }) if err != nil { log.Fatalf(“client.Search err: %v”, err) } log.Printf(“resp: %s”, resp.GetResponse())}创建与给定目标(服务端)的连接交互创建 SearchService 的客户端对象发送 RPC 请求,等待同步响应,得到回调后返回响应结果输出响应结果验证启动 Server$ pwd$GOPATH/github.com/EDDYCJY/go-grpc-example$ go run server.go启动 Client$ pwd $GOPATH/github.com/EDDYCJY/go-grpc-example/client$ go run client.go 2018/09/23 11:06:23 resp: gRPC Server系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server总结在本章节,我们对 Protobuf、gRPC Client/Server 分别都进行了介绍。希望你结合文中讲述内容再写一个 Demo 进行深入了解,肯定会更棒 ???? ...

September 24, 2018 · 3 min · jiezi

带入gRPC:gRPC及相关介绍

带入gRPC:gRPC及相关介绍原文地址:带入gRPC:gRPC及相关介绍作为开篇章,将会介绍 gRPC 相关的一些知识。简单来讲 gRPC 是一个 基于 HTTP/2 协议设计的 RPC 框架,它采用了 Protobuf 作为 IDL你是否有过疑惑,它们都是些什么?本文将会介绍一些常用的知识和概念,更详细的会给出手册地址去深入一、RPC什么是 RPCRPC 代指远程过程调用(Remote Procedure Call),它的调用包含了传输协议和编码(对象序列号)协议等等。允许运行于一台计算机的程序调用另一台计算机的子程序,而开发人员无需额外地为这个交互作用编程实际场景:有两台服务器,分别是A、B。在 A 上的应用 C 想要调用 B 服务器上的应用 D,它们可以直接本地调用吗? 答案是不能的,但走 RPC 的话,十分方便。因此常有人称使用 RPC,就跟本地调用一个函数一样简单RPC 框架我认为,一个完整的 RPC 框架,应包含负载均衡、服务注册和发现、服务治理等功能,并具有可拓展性便于流量监控系统等接入 那么它才算完整的,当然了。有些较单一的 RPC 框架,通过组合多组件也能达到这个标准你认为呢?常见 RPC 框架gRPCThriftRpcxDubbo比较一下\跨语言多 IDL服务治理注册中心服务管理gRPC√××××Thrift√××××Rpcx×√√√√Dubbo×√√√√为什么要 RPC简单、通用、安全、效率RPC 可以基于 HTTP 吗RPC 是代指远程过程调用,是可以基于 HTTP 协议的肯定会有人说效率优势,我可以告诉你,那是基于 HTTP/1.1 来讲的,HTTP/2 优化了许多问题(当然也存在新的问题),所以你看到了本文的主题 gRPC二、Protobuf介绍Protocol Buffers 是一种与语言、平台无关,可扩展的序列化结构化数据的方法,常用于通信协议,数据存储等等。相较于 JSON、XML,它更小、更快、更简单,因此也更受开发人员的青眯语法syntax = “proto3”;service SearchService { rpc Search (SearchRequest) returns (SearchResponse);}message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3;}message SearchResponse { …}1、第一行(非空的非注释行)声明使用 proto3 语法。如果不声明,将默认使用 proto2 语法。同时我建议用 v2 还是 v3,都应当声明其使用的版本2、定义 SearchService RPC 服务,其包含 RPC 方法 Search,入参为 SearchRequest 消息,出参为 SearchResponse 消息3、定义 SearchRequest、SearchResponse 消息,前者定义了三个字段,每一个字段包含三个属性:类型、字段名称、字段编号4、Protobuf 编译器会根据选择的语言不同,生成相应语言的 Service Interface Code 和 Stubs最后,这里只是简单的语法介绍,详细的请右拐 Language Guide (proto3)数据类型.proto TypeC++ TypeJava TypeGo TypePHP Typedoubledoubledoublefloat64floatfloatfloatfloatfloat32floatint32int32intint32integerint64int64longint64integer/stringuint32uint32intuint32integeruint64uint64longuint64integer/stringsint32int32intint32integersint64int64longint64integer/stringfixed32uint32intuint32integerfixed64uint64longuint64integer/stringsfixed32int32intint32integersfixed64int64longint64integer/stringboolboolbooleanboolbooleanstringstringStringstringstringbytesstringByteString[]bytestringv2 和 v3 主要区别删除原始值字段的字段存在逻辑删除 required 字段删除 optional 字段,默认就是删除 default 字段删除扩展特性,新增 Any 类型来替代它删除 unknown 字段的支持新增 JSON Mapping新增 Map 类型的支持修复 enum 的 unknown 类型repeated 默认使用 packed 编码引入了新的语言实现(C#,JavaScript,Ruby,Objective-C)以上是日常涉及的常见功能,如果还想详细了解可阅读 Protobuf Version 3.0.0相较 Protobuf,为什么不使用XML?更简单数据描述文件只需原来的1/10至1/3解析速度是原来的20倍至100倍减少了二义性生成了更易使用的数据访问类三、gRPC介绍gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计多语言C++C#DartGoJavaNode.jsObjective-CPHPPythonRuby特点1、HTTP/22、Protobuf3、客户端、服务端基于同一份 IDL 4、移动网络的良好支持5、支持多语言概览讲解1、客户端(gRPC Sub)调用 A 方法,发起 RPC 调用 2、对请求信息使用 Protobuf 进行对象序列化压缩(IDL)3、服务端(gRPC Server)接收到请求后,解码请求体,进行业务逻辑处理并返回4、对响应结果使用 Protobuf 进行对象序列化压缩(IDL)5、客户端接受到服务端响应,解码请求体。回调被调用的 A 方法,唤醒正在等待响应(阻塞)的客户端调用并返回响应结果示例在这一小节,将简单的给大家展示 gRPC 的客户端和服务端的示例代码,希望大家先有一个基础的印象,将会在下一章节详细介绍 ????构建和启动服务端lis, err := net.Listen(“tcp”, fmt.Sprintf(":%d", *port))if err != nil { log.Fatalf(“failed to listen: %v”, err)}grpcServer := grpc.NewServer()…pb.RegisterSearchServer(grpcServer, &SearchServer{})grpcServer.Serve(lis)1、监听指定 TCP 端口,用于接受客户端请求2、创建 gRPC Server 的实例对象3、gRPC Server 内部服务和路由的注册4、Serve() 调用服务器以执行阻塞等待,直到进程被终止或被 Stop() 调用创建客户端var opts []grpc.DialOption…conn, err := grpc.Dial(*serverAddr, opts…)if err != nil { log.Fatalf(“fail to dial: %v”, err)}defer conn.Close()client := pb.NewSearchClient(conn)…1、创建 gRPC Channel 与 gRPC Server 进行通信(需服务器地址和端口作为参数)2、设置 DialOptions 凭证(例如,TLS,GCE凭据,JWT凭证)3、创建 Search Client Stub4、调用对应的服务方法思考题1、什么场景下不适合使用 Protobuf,而适合使用 JSON、XML?2、Protobuf 一节中提到的 packed 编码,是什么?总结在开篇内容中,我利用了尽量简短的描述给你介绍了接下来所必须、必要的知识点希望你能够有所收获,建议能到我给的参考资料处进行深入学习,是最好的了系列目录带入gRPC:gRPC及相关介绍带入gRPC:gRPC Client and Server参考资料Protocol BuffersgRPC ...

September 24, 2018 · 2 min · jiezi