精华内容
下载资源
问答
  • websocket和grpc
    2021-01-22 22:05:26

    在 2020 年生日周期间,Cloudflare 发布了对 gRPC 的支持。大家对 Beta 测试的浓厚兴趣令我们受宠若惊,在此我们向所有申请并** gRPC 的人致以衷心感谢!在本文中,我们将深入探讨有关如何实施这一支持的技术细节。
      
      什么是 gRPC?
      
      gRPC 是通过 HTTP/2 运行的一种开源 RPC 框架。RPC(远程过程调用)是供计算机用于命令另一台计算机执行某项操作,而不是调用库中的本地函数。长久以来,分布式计算发展过程中一直都有 RPC 的身影,出现了针对不同领域的不同实现。以下特征造就了 gRPC 的与众不同:
      
      需要现已广泛使用的现代 HTTP/2 协议进行传输。
      
      以开源形式提供完整的客户端/服务器参考实现、演示和测试套件。
      
      不指定消息格式,但协议缓冲区是首选的序列化机制。
      
      客户端和服务器都可流式传输数据,避免了轮询新数据或创建新连接的麻烦。
      
      在协议方面,gRPC 广泛使用HTTP/2 帧:请求和响应看起来与普通的 HTTP/2 请求非常相似。
      
      不过,与众不同的地方在于 gRPC 使用了 HTTP 尾部(trailer)。HTTP 尾部尽管没有得到广泛使用,自 1999 年于原始 HTTP/1.1 RFC2616中定义以来一直存在。根据定义,HTTP 消息标头位于 HTTP 消息正文之前,但 HTTP 尾部是一组可附加到消息正文后面的 HTTP 标头。不过,由于 HTTP 尾部的用例不多,因此许多服务器和客户端实施没有全面支持它们。虽然 HTTP/1.1 需要为其正文使用分块传输编码来发送 HTTP 尾部,但对于 HTTP/2,尾部位于正文的 DATA 帧之后的 HEADER 帧中。
      
      在一些情形中,HTTP 尾部很有用处。例如,我们使用 HTTP 响应代码指示请求的状态,但响应代码是 HTTP 响应的第一行,因此需要尽早确定响应代码。HTTP 尾部可以在正文之后发送一些元数据。例如,假设您的 Web 服务器发送了一个大数据流(非固定大小),最后您要发送所发送数据的 SHA256 校验和,以便客户端可以验证内容。通常,这无法通过 HTTP 状态代码或应在响应开始时发送的响应标头来实现。使用 HTTP 尾部标头,您可以在发送完所有数据后发送另一个标头(例如 Digest)。
      
      gRPC 将 HTTP 尾部用于两个用途。首先,在发送内容后以尾部标头形式发送最终状态(grpc-status)。其二是为了支持流式用例。这些用例的持续时间远比普通 HTTP 请求长。HTTP 尾部用于提供请求或响应的后处理结果。例如,如果在流数据处理过程中发生错误,则可使用尾部发送错误代码,而这无法通过消息正文之前的标头来实现。
      
      以下是 HTTP/2 帧中 gRPC 请求和响应的简单示例:
      
      在这里插入图片描述

    向 Cloudflare Edge 添加 gRPC 支持
      
      既然 gRPC 使用 HTTP/2,原生支持 gRPC 听起来不难,因为 Cloudflare 已经支持 HTTP/2 了。但是,我们有两个问题:
      
      我们的边缘代理不完全支持 HTTP 请求/响应尾部标头:Cloudflare 使用 NGINX 接受来自用户端的流量,并且对尾部的支持有限。让情况更加复杂的是,穿过 Cloudflare 的请求和响应还需要经过一系列其他代理。
      
      HTTP/2 至源站:我们的边缘代理使用 HTTP/1.1 从源站获取对象(无论是动态还是静态对象)。要代理 gRPC 流量,我们需要支持使用 HTTP/2 与客户 gRPC 源站连接。
      
      gRPC 流式传输需要允许双向请求/响应流:gRPC 有两种类型的协议流;其一是一元的,即简单的请求和响应,其二是流式传输,允许各个方向上不间断的数据流。为了全面支持流式传输,需要在另一端接收到响应标头之后发送 HTTP 消息正文。例如,流式传输客户端将在接收到响应标头之后持续发送请求正文。
      
      出于这些原因,gRPC 请求通过我们的网络代理时会中断。为克服这些限制,我们研究了各种各样的解决方案。例如,NGINX 具有一个 gRPC 上游模块,可支持 HTTP/2 gRPC 源站,但它是一个单独的模块,而且还需要 HTTP/2 下游,不能用于我们的服务,因为请求在某些情况下会穿过多个 HTTP 代理。由于内部负载平衡架构的关系,而且确保所有内部流量都使用 HTTP/2 将花费太多精力,因此在管道的每一处全部使用 HTTP/2 是不现实的。
      在这里插入图片描述

    转换成 HTTP/1.1
      
      最终,我们找到了一个比较好的办法:在内部网络中将 gRPC 消息转换为不含尾部的 HTTP/1.1 消息,然后在请求发送到源站之前将转换回 HTTP/2。对于 Cloudflare 中不支持 HTTP 尾部的大多数 HTTP 代理,这个办法都适用,而且我们需要的更改也极少。
      
      我们不必自己发明格式,gRPC 社区已经开发了一种 HTTP/1.1 兼容版本:gRPC-web。gRPC-web 是对基于 HTTP/2 的原始 gRPC 规范的修改。最初目的是与不能直接访问 HTTP/2 帧的 Web 浏览器搭配。使用 gRPC-web 时,HTTP 尾部将移到正文中,因此我们无需担心代理中对 HTTP 尾部的支持。它还附带了流式传输支持。我们的安全产品(例如 WAF 和 Bot Management)仍可以检查生成的 HTTP/1.1 消息,提供的安全级别与 Cloudflare 为其他 HTTP 流量带来的相同。
      
      在 Cloudflare 的边缘代理接收到 HTTP/2 gRPC 消息时,该消息将“转换”为 HTTP/1.1 gRPC-web 格式。gRPC 消息转换之后,它将穿过我们的管道,并以和普通 HTTP 请求相同的方式应用 WAF、Cache 和 Argo 等服务。
      
      在 gRPC-web 消息即将离开 Cloudflare 网络时,需要再次“转回”成 HTTP/2 gRPC。由我们系统转换的请求会加上标记,因此我们的系统不会意外转换源自客户端的 gRPC-web 流量。
      
      HTTP/2 源站支持
      
      一个工程方面的挑战是支持使用 HTTP/2 连接到源站。在这个项目之前,Cloudflare 无法通过 HTTP/2 连接到源站。
      
      因此,我们决定在内部构建 HTTP/2 源站支持。我们开发了一个独立的源代理,它能通过 HTTP/2 连接到源站。在这个新平台基础上,我们实施了 gRPC 的转换逻辑。gRPC 支持是利用此新平台的第一个功能。路线图中已经规划了对 HTTP/2 连接源服务器的更广泛支持。
      
      gRPC 流式传输支持
      
      如上文所述,gRPC 具有流式传输模式,可以在流中发送请求正文或响应正文。在 gRPC 请求的生命周期内,可以随时发送 gRPC 消息块。流的末尾有一个 HEADER 帧用来指示流末尾。转换成 gRPC-web 后,我们将使用分块编码发送正文并使连接保持开放以接受正文的两面,直到获得用来指示流末尾的 gRPC 消息块为止。这需要我们的代理支持双向传输。
      
      例如,客户端流式传输是一种有趣的模式,其中服务器已经使用响应代码及其标头进行响应,但客户端仍然能够发送请求正文。
      
      互操作性测试
      
      Cloudflare 的每一新功能在发布之前都需要进行妥善测试。在最初开发过程中,我们使用了 envoy 代理及其 gRPC-web 过滤器功能以及 gRPC 的官方示例。我们准备了一个带有 envoy 代理和 gRPC 测试源站的测试环境,以确保边缘代理可以正确处理 gRPC 请求。来自 gRPC 测试客户端的请求发送到边缘代理,并转换为 gRPC-web,然后转发到 envoy 代理。之后,envoy 转换回 gRPC 请求并发送到 gRPC 测试源站。我们通过这种办法成功验证了基本行为。
      
      基本功能准备就绪后,我们还需要确保两端的转换功能正常工作。为此,我们构建了更深层次的互操作性测试。
      
      我们参照现有的 gRPC 互操作性测**例开发了我们的测试套件,并在本地边缘代理和新源站代理之间运行了测试的第一个迭代。
      
      对于测试的第二个迭代,我们使用了不同的 gRPC 实施。例如,某些服务器在出现即时错误时以仅尾部响应发送最终状态(grpc-status)。此响应在单个 HEADERS 帧块中包含 HTTP/2 响应标头和尾部,并且同时设置了 END_STREAM 和 END_HEADERS 标记。其他实施则在单独的 HEADERS 帧中以尾部形式发送最终状态。
      
      在本地验证了互操作性之后,我们针对支持生产环境中所有服务的开发环境运行了测试。这样,我们便可以确保没有意外副作用会影响 gRPC 请求。
      
      我们喜欢开展内部测试!我们成功部署了边缘 gRPC 支持的第一批服务,其中之一是 Cloudflare drand 随机信标。启用不难,而且我们过去几周一直在生产环境中运行该信标,从未出现任何问题。
      
      总结
      
      支持新协议是一项激动人心的工作!在现有系统中实施新技术支持既令人兴奋,又错综复杂,通常涉及在实施速度和整体系统复杂性之间进行权衡。对于 gRPC,我们成功地快速建立了支持,而且无需对 Cloudflare 边缘进行大幅更改。为实现这个目标,我们仔细考虑了各种实施方案,而后敲定了在 HTTP/2 gRPC 和 HTTP/1.1 gRPC-web 格式之间进行转换的方案。选择这种设计不仅加快并简化了服务集成,而且依然与我们用户的期望和限制相符。
      
      如果您对使用 Cloudflare 保护和加速 gRPC 服务感兴趣,可以在此处阅读更多内容。另外,如果您想攻克一些有趣的工程挑战(如本文所述的这个),欢迎您报名申请!

    更多相关内容
  • 导读:虽然说 Knative 默认就支持 WebSocket gRPC,但在使用中会发现,有时想要把自己的 WebSocketgRPC 部署到 Knative 中,还是存在各种不顺利。虽然最后排查发现,大多是自己的程序问题或是配置错误导致的...

    作者 | 冬岛  阿里云容器平台工程师

    导读:虽然说 Knative 默认就支持 WebSocket 和 gRPC,但在使用中会发现,有时想要把自己的 WebSocket 或 gRPC 部署到 Knative 中,还是存在各种不顺利。虽然最后排查发现,大多是自己的程序问题或是配置错误导致的。本文分别给出了一个 WebSocket 和 gRPC 的例子,当需要在生产或者测试环境部署相关服务时,可以使用本文给出的示例进行 Knative 服务的测试。

    WebSocket

    如果自己手动的配置 Istio Gateway 支持 WebSocket 就需要开启 websocketUpgrade 功能。但使用 Knative Serving 部署其实就自带了这个能力。本示例的完整代码放在 https://github.com/knative-sample/websocket-chat ,这是一个基于 WebSocket 实现的群聊的例子。

    使用浏览器连接到部署的服务中就可以看到一个接收信息的窗口和发送信息的窗口。当你发出一条信息以后所有连接进来的用户都能收到你的消息。所以你可以使用两个浏览器窗口分别连接到服务中,一个窗口发送消息一个窗口接收消息,以此来验证 WebSocket 服务是否正常。

    本示例是在 gorilla/websocket  基础之上进行了一些优化:

    • 代码中添加了 vendor 依赖,你下载下来就可以直接使用
    • 添加了 Dockerfile 和 Makefile 可以直接编译二进制和制作镜像
    • 添加了 Knative Sevice 的 yaml 文件(service.yaml),你可以直接提交到 Knative 集群中使用
    • 也可以直接使用编译好的镜像 registry.cn-hangzhou.aliyuncs.com/knative-sample/websocket-chat:2019-10-15

    Knative Service 配置:

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: websocket-chat
    spec:
      template:
        spec:
          containers:
            - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/websocket-chat:2019-10-15
              ports:
                - name: http1
                  containerPort: 8080

    代码 clone 下来以后执行 kubectl apply -f service.yaml 把服务部署到 Knative 中,然后直接访问服务地址即可使用。

    查看 ksvc 列表,并获取访问域名。

    └─# kubectl get ksvc
    NAME             URL                                                    LATESTCREATED          LATESTREADY            READY   REASON
    websocket-chat   http://websocket-chat.default.serverless.kuberun.com   websocket-chat-7ghc9   websocket-chat-7ghc9   True

    现在使用浏览器打开 http://websocket-chat.default.serverless.kuberun.com 即可看到群聊窗口。

    打开两个窗口,在其中一个窗口发送一条消息,另外一个窗口通过 WebSocket 也收到了这条消息。

    gRPC

    gRPC 不能通过浏览器直接访问,需要通过 client 端和 server 端进行交互。本示例的完整代码放在 https://github.com/knative-sample/grpc-ping-go ,本示例会给一个可以直接使用的镜像,测试 gRPC 服务。

    Knative Service 配置:

    apiVersion: serving.knative.dev/v1
    kind: Service
    metadata:
      name: grpc-ping
    spec:
      template:
        spec:
          containers:
          - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/grpc-ping-go:2019-10-15
            ports:
              - name: h2c
                containerPort: 8080

    代码 clone 下来以后执行 kubectl apply -f service.yaml 把服务部署到 Knative 中。

    获取 ksvc 列表和访问域名:

    └─# kubectl get ksvc
    NAME             URL                                                    LATESTCREATED          LATESTREADY            READY     REASON
    grpc-ping        http://grpc-ping.default.serverless.kuberun.com        grpc-ping-p2tft                               Unknown   RevisionMissing
    websocket-chat   http://websocket-chat.default.serverless.kuberun.com   websocket-chat-6hgld   websocket-chat-6hgld   True

    现在我们已经知道 gRPC  server 的地址是 grpc-ping.default.serverless.kuberun.com,端口是 80,那么我们可以发起测试请求:

    └─# docker run --rm registry.cn-hangzhou.aliyuncs.com/knative-sample/grpc-ping-go:2019-10-15 /client -server_addr="grpc-ping.default.serverless.kuberun.com:80" -insecure
    2019/10/16 11:35:07 Ping got hello - pong
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854794231 +0800 CST m=+73.061909052
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854827273 +0800 CST m=+73.061942072
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854835802 +0800 CST m=+73.061950606
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854842843 +0800 CST m=+73.061957643
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854849211 +0800 CST m=+73.061964012
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854855249 +0800 CST m=+73.061970049
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854861659 +0800 CST m=+73.061976460
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854875071 +0800 CST m=+73.061989873
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854905416 +0800 CST m=+73.062020221
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.85491183 +0800 CST m=+73.062026630
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.85492533 +0800 CST m=+73.062040133
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854932285 +0800 CST m=+73.062047083
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854946977 +0800 CST m=+73.062061782
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854953311 +0800 CST m=+73.062068112
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854966639 +0800 CST m=+73.062081440
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854973939 +0800 CST m=+73.062088739
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854985463 +0800 CST m=+73.062100268
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854993275 +0800 CST m=+73.062108073
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854999812 +0800 CST m=+73.062114613
    2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.855012676 +0800 CST m=+73.062127479

    小结

    本文通过两个例子分别展示了 WebSocket 和 gRPC 的部署方法:

    • WebSocket 示例通过一个 chat 的方式展示发送和接受消息
    • gRPC 通过启动一个 client 的方式展示 gRPC 远程调用的过程

    原文链接
    本文为云栖社区原创内容,未经允许不得转载。

    展开全文
  • grpc-websocket-proxy-example

    2021-02-21 07:03:48
    grpc-websocket-proxy-example
  • 有四种支持四种主要通信协议的服务器-MQTT,GRPC,WebRTC和Websocket 该服务器可以在本地使用,以为智能家居,PBX(如freeswitch或星号)提供语音识别。 该服务器还可以作为后端运行,以在网络上进行流语音识别,...
  • 支持tcp,websocket接入2.离线消息同步3.多业务接入4.单用户多设备同时在线5.单聊,群聊,以及超大群聊天场景6.支持服务水平扩展使用技术:数据库:Mysql+Redis通讯框架:Grpc长连接通讯协议:Protocol Buffers日志...

    简要介绍

    gim是一个即时通讯服务器,代码全部使用golang完成。主要功能

    1.支持tcp,websocket接入

    2.离线消息同步

    3.多业务接入

    4.单用户多设备同时在线

    5.单聊,群聊,以及超大群聊天场景

    6.支持服务水平扩展

    使用技术:

    数据库:Mysql+Redis

    通讯框架:Grpc

    长连接通讯协议:Protocol Buffers

    日志框架:Zap

    安装部署

    1.首先安装MySQL,Redis

    2.创建数据库gim,执行sql/create_table.sql,完成初始化表的创建(数据库包含提供测试的一些初始数据)

    3.修改config下配置文件,使之和你本地配置一致

    4.分别切换到cmd的tcp_conn,ws_conn,logic目录下,执行go run main.go,启动TCP连接层服务器,WebSocket连接层服务器,逻辑层服务器

    迅速跑通本地测试

    1.在test目录下,tcp_conn或者ws_conn目录下,执行go run main,启动测试脚本

    2.根据提示,依次填入app_id,user_id,device_id,sync_sequence(中间空格空开),进行长连接登录;数据库device表中已经初始化了一些设备信息,用作测试

    3.执行api/logic/logic_client_ext_test.go下的TestLogicExtServer_SendMessage函数,发送消息

    业务服务器如何接入

    1.首先生成私钥和公钥

    2.在app表里根据你的私钥添加一条app记录

    3.将app_id和公钥保存到业务服务器

    4.将用户通过LogicClientExtServer.AddUser接口添加到IM服务器

    5.通过LogicClientExtServer.RegisterDevice接口注册设备,获取设备id(device_id)

    6.将app_id,user_id,device_id用公钥通过公钥加密,生成token,相应库的代码在pkg/util/aes.go

    7.接下来使用这个token,app就可以和IM服务器交互

    rpc接口简介

    项目所有的proto协议在gim/public/proto/目录下

    1.tcp.proto

    长连接通讯协议

    2.logic_client.ext.proto

    对客户端(Android设备,IOS设备)提供的rpc协议

    3.logic_server.ext.proto

    对业务服务器提供的rpc协议

    4.logic.int.proto

    对conn服务层提供的rpc协议

    5.conn.int.proto

    对logic服务层提供的rpc协议

    项目目录简介

    api: 服务对外提供的grpc接口

    cmd: 服务启动入口

    config: 服务配置

    internal: 每个服务私有代码

    pkg: 服务共有代码

    sql: 项目sql文件

    test: 长连接测试脚本

    服务简介

    1.tcp_conn

    维持与客户端的TCP长连接,心跳,以及TCP拆包粘包,消息编解码

    2.ws_conn

    维持与客户端的WebSocket长连接,心跳,消息编解码

    3.logic

    设备信息,用户信息,群组信息管理,消息转发逻辑

    TCP拆包粘包

    遵循LV的协议格式,一个消息包分为两部分,消息字节长度以及消息内容。

    这里为了减少内存分配,拆出来的包的内存复用读缓存区内存。

    拆包流程:

    1.首先从系统缓存区读取字节流到buffer

    2.根据包头的length字段,检查报的value字段的长度是否大于等于length

    3.如果大于,返回一个完整包(此包内存复用),重复步骤2

    4.如果小于,将buffer的有效字节前移,重复步骤1

    单用户多设备支持,离线消息同步

    每个用户都会维护一个自增的序列号,当用户A给用户B发送消息是,首先会获取A的最大序列号,设置为这条消息的seq,持久化到用户A的消息列表,

    再通过长连接下发到用户A账号登录的所有设备,再获取用户B的最大序列号,设置为这条消息的seq,持久化到用户B的消息列表,再通过长连接下发

    到用户B账号登录的所有设备。

    假如用户的某个设备不在线,在设备长连接登录时,用本地收到消息的最大序列号,到服务器做消息同步,这样就可以保证离线消息不丢失。

    度扩散和写扩散

    首先解释一下,什么是读扩散,什么是写扩散

    读扩散

    简介:群组成员发送消息时,先建立一个会话,都将这个消息写入这个会话中,同步离线消息时,需要同步这个会话的未同步消息

    优点:每个消息只需要写入数据库一次就行,减少数据库访问次数,节省数据库空间

    缺点:一个用户有n个群组,客户端每次同步消息时,要上传n个序列号,服务器要对这n个群组分别做消息同步

    写扩散

    简介:在群组中,每个用户维持一个自己的消息列表,当群组中有人发送消息时,给群组的每个用户的消息列表插入一条消息即可

    优点:每个用户只需要维护一个序列号和消息列表

    缺点:一个群组有多少人,就要插入多少条消息,当群组成员很多时,DB的压力会增大

    消息转发逻辑选型以及特点

    普通群组:

    采用写扩散,群组成员信息持久化到数据库保存。支持消息离线同步。

    超大群组:

    采用读扩散,群组成员信息保存到redis,不支持离线消息同步。

    核心流程时序图

    长连接登录

    7a0069966b77

    eaf3a08af9c64bbd.png

    离线消息同步

    7a0069966b77

    ef9c9452e65be3ced63573164fec7ed5.png

    心跳

    7a0069966b77

    6ea6acf2cd4b956e.png

    消息单发

    c1.d1和c1.d2分别表示c1用户的两个设备d1和d2,c2.d3和c2.d4同理

    7a0069966b77

    e000fda2f18e86f3.png

    小群消息群发

    c1,c2.c3表示一个群组中的三个用户

    7a0069966b77

    749fc468746055a8ecf3fba913b66885.png

    大群消息群发

    7a0069966b77

    e3f92bdbb3eef199d185c28292307497.png

    错误处理,链路追踪,日志打印

    系统中的错误一般可以归类为两种,一种是业务定义的错误,一种就是未知的错误,在业务正式上线的时候,业务定义的错误的属于正常业务逻辑,不需要打印出来,

    但是未知的错误,我们就需要打印出来,我们不仅要知道是什么错误,还要知道错误的调用堆栈,所以这里我对GRPC的错误进行了一些封装,使之包含调用堆栈。

    func WrapError(err error) error {

    if err == nil {

    return nil

    }

    s := &spb.Status{

    Code: int32(codes.Unknown),

    Message: err.Error(),

    Details: []*any.Any{

    {

    TypeUrl: TypeUrlStack,

    Value: util.Str2bytes(stack()),

    },

    },

    }

    return status.FromProto(s).Err()

    }

    // Stack 获取堆栈信息

    func stack() string {

    var pc = make([]uintptr, 20)

    n := runtime.Callers(3, pc)

    var build strings.Builder

    for i := 0; i < n; i++ {

    f := runtime.FuncForPC(pc[i] - 1)

    file, line := f.FileLine(pc[i] - 1)

    n := strings.Index(file, name)

    if n != -1 {

    s := fmt.Sprintf(" %s:%d \n", file[n:], line)

    build.WriteString(s)

    }

    }

    return build.String()

    }

    这样,不仅可以拿到错误的堆栈,错误的堆栈也可以跨RPC传输,但是,但是这样你只能拿到当前服务的堆栈,却不能拿到调用方的堆栈,就比如说,A服务调用

    B服务,当B服务发生错误时,在A服务通过日志打印错误的时候,我们只打印了B服务的调用堆栈,怎样可以把A服务的堆栈打印出来。我们在A服务调用的地方也获取

    一次堆栈。

    func WrapRPCError(err error) error {

    if err == nil {

    return nil

    }

    e, _ := status.FromError(err)

    s := &spb.Status{

    Code: int32(e.Code()),

    Message: e.Message(),

    Details: []*any.Any{

    {

    TypeUrl: TypeUrlStack,

    Value: util.Str2bytes(GetErrorStack(e) + " --grpc-- \n" + stack()),

    },

    },

    }

    return status.FromProto(s).Err()

    }

    func interceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {

    err := invoker(ctx, method, req, reply, cc, opts...)

    return gerrors.WrapRPCError(err)

    }

    var LogicIntClient pb.LogicIntClient

    func InitLogicIntClient(addr string) {

    conn, err := grpc.DialContext(context.TODO(), addr, grpc.WithInsecure(), grpc.WithUnaryInterceptor(interceptor))

    if err != nil {

    logger.Sugar.Error(err)

    panic(err)

    }

    LogicIntClient = pb.NewLogicIntClient(conn)

    }

    像这样,就可以获取完整一次调用堆栈。

    错误打印也没有必要在函数返回错误的时候,每次都去打印。因为错误已经包含了堆栈信息

    // 错误的方式

    if err != nil {

    logger.Sugar.Error(err)

    return err

    }

    // 正确的方式

    if err != nil {

    return err

    }

    然后,我们在上层统一打印就可以

    func startServer {

    extListen, err := net.Listen("tcp", conf.LogicConf.ClientRPCExtListenAddr)

    if err != nil {

    panic(err)

    }

    extServer := grpc.NewServer(grpc.UnaryInterceptor(LogicClientExtInterceptor))

    pb.RegisterLogicClientExtServer(extServer, &LogicClientExtServer{})

    err = extServer.Serve(extListen)

    if err != nil {

    panic(err)

    }

    }

    func LogicClientExtInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {

    defer func() {

    logPanic("logic_client_ext_interceptor", ctx, req, info, &err)

    }()

    resp, err = handler(ctx, req)

    logger.Logger.Debug("logic_client_ext_interceptor", zap.Any("info", info), zap.Any("ctx", ctx), zap.Any("req", req),

    zap.Any("resp", resp), zap.Error(err))

    s, _ := status.FromError(err)

    if s.Code() != 0 && s.Code() < 1000 {

    md, _ := metadata.FromIncomingContext(ctx)

    logger.Logger.Error("logic_client_ext_interceptor", zap.String("method", info.FullMethod), zap.Any("md", md), zap.Any("req", req),

    zap.Any("resp", resp), zap.Error(err), zap.String("stack", gerrors.GetErrorStack(s)))

    }

    return

    }

    这样做的前提就是,在业务代码中透传context,golang不像其他语言,可以在线程本地保存变量,像Java的ThreadLocal,所以只能通过函数参数的形式进行传递,gim中,service层函数的第一个参数

    都是context,但是dao层和cache层就不需要了,不然,显得代码臃肿。

    最后可以在客户端的每次请求添加一个随机的request_id,这样客户端到服务的每次请求都可以串起来了。

    func getCtx() context.Context {

    token, _ := util.GetToken(1, 2, 3, time.Now().Add(1*time.Hour).Unix(), util.PublicKey)

    return metadata.NewOutgoingContext(context.TODO(), metadata.Pairs(

    "app_id", "1",

    "user_id", "2",

    "device_id", "3",

    "token", token,

    "request_id", strconv.FormatInt(time.Now().UnixNano(), 10)))

    }

    github

    展开全文
  • 然后我新来的,让我改成 websocket 双向通信的来做消息推送。 简单的业务场景就是做一个聊天室,mq转发消息,在php bin/hyperf.php start 启动后,老是进程自动断掉: 一直找不到原因,后来看到官方文档 :...

    公司原有的业务消息推送是靠前端 ajax 轮眉请求后端接口完成的。然后我新来的,让我改成 websocket 双向通信的来做消息推送。

    简单的业务场景就是做一个聊天室,mq转发消息,在php bin/hyperf.php start 启动后,老是进程自动断掉:

    一直找不到原因,后来看到官方文档 :https://hyperf.wiki/2.0/#/zh-cn/quick-start/install

    说下面的这些扩展不兼容。

    存在兼容性问题的扩展

    由于 Hyperf 基于 Swoole 协程实现,而 Swoole 4 带来的协程功能是 PHP 前所未有的,所以与不少扩展都仍存在兼容性的问题。
    以下扩展(包括但不限于)都会造成一定的兼容性问题,不能与之共用或共存:

    • xhprof
    • xdebug
    • blackfire
    • trace
    • uopz

    我检查了一下我的扩展,没有这些,那会是啥情况啊,swoole 从4.6.7 到 4.6.0 全部安装了一遍,还是这个问题,连接刚一启动就断掉。

    我再次检查一下安装的扩展。

    注意到有个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 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。于是果断禁掉!

     

    接着重新打开hyperf服务,正常了!

     

     

    grpc底层用的是http2来连接的,通过node.js做服务的,所以可能是这个原因导致hyperf关闭!

     

     

    展开全文
  • spear ... Package Name NuGet Downloads Spear.ProxyGenerator ...Spear.Protocol.WebSocket Spear.Protocol.Grpc Contracts [ServiceRoute("test")] //自定义路由键 public interface ITestContract
  • grpc-websocket-proxy 使用此帮助程序包装grpc-gateway mux,以通过websocket公开流终结点。 在网络上,它使用消息的换行符分隔的json编码。 用法: mux := runtime.NewServeMux() opts := []grpc.DialOption{...
  • RSocket 与 gRPC性能对比

    千次阅读 2020-10-22 16:45:12
    几乎每次我向观众介绍RSocket时,都会有人问这个问题:“ RSocket与gRPC相比如何?” 今天我们要找出答案。 搭建平台 插座 RSocket在应用程序网络上实现反应式语义。它是一种网络协议,可端对端实施反压力其他...
  • GRPC-Bus WebSocket代理客户端该客户端库通过WebSocket代理服务器将浏览器JavaScript上下文连接到标准GRPC服务,并完全支持双向流传输(即,由或由) 安装npm install grpc-bus-websocket-client 您还需要安装并运行...
  • GRPC简单应用(Java+Python),javapython版本的grpc调用实现,附文档
  • 它通过访问gRPC服务公开的反射API读取protobuf服务定义,并将HTTPJSON请求动态转换为gRPC调用。 创建gRPC Mate的目的是提供一种非常轻松地从gRPC服务器提供HTTPJSON的方式,而无需共享protobuf定义文件,没有...
  • rpc是一种编程思想,而http则是基于tcp/ip的应用层协议,两者不是同个层次的东西,在rpc开发中可以通过使用http协议来完成(比如grpc),也可以基于其它协议,比如更底层的传输层的tcp协议   7.Web Service的...
  • 为什么需要grpc双向流?
  • RPC、HTTP和WebSocket协议

    2020-07-25 14:39:59
    1、RPCHttp的区别 性能:rpc通过thrity进行二进制传输,http json序列化耗费性能 传输协议:rpc 基于 tcp 也可基于http,http 只能是http 负载均衡:rpc自己支持,http需要自己搭建,比如nginx 传输效率:rpc...
  • gRPC Web网关代理HTTP和WebSocket请求到gRPC服务。用法const path = require ( 'path' ) ;const gateway = createGrpcGateway ( { apiHost : 'grpc-api:443'} ) ;gateway . listen ( 8080 ) ;
  • RPC 是一种技术思想而非一种规范或协议,常见 RPC 技术框架有: 应用级的服务框架:阿里的 Dubbo/Dubbox、Google gRPC、Spring Boot/Spring Cloud。 远程通信协议:RMI、Socket、SOAP(HTTP XML)、REST(HTTP JSON)...
  • 通过HTTP2 / HTTP1(包括SSE)和WebSocket发送监视流 创建GraphQL查询,自省,突变订阅 为gRPC提供完整的流测试支持 在每个请求的交互式图表中查看响应时间信息历史记录 保存多个请求的工作空间以供以后访问 ...
  • (二)websocket socket区别 1,websocket是应用层的协议,而socket是传输控制层的协议. (三)websocket http区别 相同点: 1,都是应用层的协议 2,都是基于tcp,并且都是可靠的协议 不同点: 1,websocket是持久...
  • grpc-lb这是一个用于go的gRPC负载平衡库。 功能支持随机,RoundRobin一致性哈希策略。 支持etcd,consulzookeeper作为注册表。 示例包main imp grpc-lb这是go的gRPC负载平衡库。 功能支持Random,RoundRobin...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,131
精华内容 852
关键字:

websocket和grpc

友情链接: WineshopManager.rar