.Net 8.0 下的新RPC,IceRPC之试试的新玩法"打洞"
作者引言
很高兴啊,我们来到了IceRPC之试试的新玩法"打洞",让防火墙哭去吧
试试RPCs的新玩法"打洞"
比较典型的玩法:RPC数据流从客户端流向服务端,现在来尝试用
IceRPC来玩一个新的花样"打洞"。
概述
对于 IceRPC,客户端是发起连接的实体, 而服务器是接受连接的实体。
建立连接后,通常会从客户端到服务端生成RPCs通道:
- 客户端创建请求,并将该请求发送到服务器
- 服务端接受此请求,并将此请求发送到已提供的"服务实现"
- 服务端返回响应,IceRPC 将此响应带回客户端
IceRPC 提供的几乎所有示例,都是这个客户端到服务器的模式。尽管如此,我们可以使用 IceRPC 试试另一种发送方式。
获取调用器invoker
使用IceRPC,需要一个调用器
invoker来发送请求,并接收相应的响应。 IceRPC(C#)提供了 ClientConnection 和 ConnectionCache 类, 用来建立网络连接的两个"终端"调用器invoker。 当使用这些调用器之一发送请求时, 请求会从底层连接的客户端,传输到服务器端。
"终端"调用器是实际发送请求,并接收响应的调用器invoker。 相比之下,管道Pipeline和拦截器interceptors是非终端调用器:他们处理请求和响应,但需要实际的调用者,来完成这项工作。
服务端到客户端调用所需的调用器invoker是 IConnectionContext.Invoker。 从传入请求中检索连接上下文。 如下所示:
// In a dispatcher implementation
public ValueTask<OutgoingResponse> DispatchAsync(
IncomingRequest request,
CancellationToken cancellationToken)
{
// The invoker represents the connection over which we received this request.
IInvoker invoker = request.ConnectionContext.Invoker;
...
}
如果正在使用 Slice 实施 IceRPC 服务, 需要在调度管道中,安装调度信息中间件UseDispatchInformation(),以将此连接上下文公开为 IDispatchInformationFeature 的一部分。 如下所示:
// Router setup in composition root / main Program
Router router = new Router()
.UseDispatchInformation()
.Map<IGreeterService>(new Chatbot());
// Slice Service implementation
public ValueTask<string> GreetAsync(
string name,
IFeatureCollection features,
CancellationToken cancellationToken)
{
IDispatchInformationFeature? dispatchInfo = features.Get<IDispatchInformationFeature>();
Debug.Assert(dispatchInfo is not null); // installed by the DispatchInformation middleware
// The invoker represents the connection over which we received this Greet request.
IInvoker invoker = dispatchInfo.ConnectionContext.Invoker;
...
}
然后,一旦有了终端调用器,就可以使用该调用器发送请求。 如果使用 Slice,将使用此调用器构建 Slice 代理,然后使用此代理调用操作。 如下所示:
IInvoker invoker = ...; // some invoker
var alarm = new AlarmProxy(invoker); // use Alarm's default path.
await alarm.SomeOpAsync();
推送通知用例
使用IceRPC开发推送通知功能: 当服务端中发生某些事件时(从服务端"推送
push"),客户端希望收到来自服务端的通知。 它不想定期发送请求,来检查是否发生此事件("拉取pull")。
服务器无法打开与客户端的连接(由于防火墙或其他网络限制), 因此,我们希望使用现有的客户端到服务器连接来执行这些通知。
我们可以使用以下 Slice 接口对这种交互进行模拟:
// Implemented by a service in the client
interface Alarm {
abnormalTemperature(value: float32) -> string
}
// Implemented by a service in the server
interface Sensor {
getCurrentTemperature() -> float32
// Monitors the current temperature and calls `abnormalTemperature` on Alarm when the
// current temperature moves outside [low..high].
monitorTemperature(low: float32, high: float32)
}
并实现传感器Sensor服务如下:
// Implementation of Sensor service
public ValueTask MonitorTemperatureAsync(
float low,
float high,
IFeatureCollection features,
CancellationToken cancellationToken)
{
IDispatchInformationFeature? dispatchInfo = features.Get<IDispatchInformationFeature>();
Debug.Assert(dispatchInfo is not null); // installed by DispatchInformation middleware
// We use Alarm's default path for this proxy.
var alarm = new AlarmProxy(dispatchInfo.ConnectionContext.Invoker);
// We enqueue the information and monitor the temperature in a separate task.
_monitor.Add(low, high, alarm);
}
在客户端中,我们实现警报Alarm服务,将其映射到路由器Router内,然后在客户端连接的选项中设置调度程序ClientConnection:
// Client side
Router router = new Router.Map<IAlarmService>(new PopupAlarm()); // use Alarm's default path.
await using var connection = new ClientConnection(
new ClientConnectionOptions
{
Dispatcher = router, // client-side dispatcher
ServerAddress = new ServerAddress(new Uri("icerpc://..."))
});
// Use connection as usual to create a SensorProxy and call MonitorTemperatureAsync.
[SliceService]
internal partial class PopupAlarm : IAlarmService
{
public ValueTask<string> AbnormalTemperatureAsync(
float value,
IFeatureCollection features,
CancellationToken cancellationToken)
{
// Show a popup with the abnormal temperature.
...
return new("Roger"); // acknowledge alarm
}
}
底层调用器invoker
连接上下文提供的调用器是绑定到特定网络连接的"原始"调用器。 如果网络连接因任何原因关闭,则该调用器将不再可用。 当使用此类调用器时,需要自己处理此类连接故障。ClientConnection和ConnectionCache更易于使用,因为它们可以根据需要,重建底层连接。
替代方案:字节流拉动Stream pulls
基于 HTTP 的 RPC 框架,比如 gRPC,不能让 RPC 反过来推送,这里还有一个替代方案!
如果无法将通知推送到客户端,可以从客户端拉取这些通知。 它在网络上使用大约相同数量的字节,但要不优雅。 如下所示:
// Implemented by a service in the server
interface Sensor {
getCurrentTemperature() -> float32
// Monitors the current temperature and streams back any value outside
// the acceptable range.
monitorTemperature(low: float32, high: float32) -> stream float32
}
通过这种方法,客户端会迭代 monitorTemperature 返回的流:每个新值都是一个新通知。
独特优势
RPC可以得到另一方式的回应。 该回应告诉调用者
caller,请求已由客户端中的服务,成功发送和处理。
如果只是将回应流stream responses回您的客户端, 服务器不用任何确认: 它就知道产生了回应流,也可能知道该流已成功通过了网络,但它不知道客户端是否成功接收并处理了该流。
本质上,流响应类似于从服务器到客户端的单向请: 语法不同,但没有功能差异。另一方面,流响应无法模拟从服务器到客户端的双向 RPC。
云路由用例(分布式)
从服务器到客户端制作 RPC 的另一个用例是通过云服务路由, 如图所示 Thermostat【git源码】例子。
这个例子是现实世界中,常见难题的一个简化版本: 比如你有一个客户端应用 (像手机应用app) 需要连接一个设备 (比如恒温器thermostat)。 该设备位于防火墙后面,不接受传入连接。 如何建立彼此通信呢?
解决方案:引入客户端和设备都连接的中介服务器。 该服务器通常部署在"云中",并将请求从客户端路由到设备(反之亦然,如果需要)。 从客户端到设备的请求,通过客户端到服务器的连接,然后通过服务器到设备的连接。 这种方式对 IceRPC 来说,非常好处理:
通过此示例,客户端可以更改恒温器thermostat上的设定点,并等待恒温器的确认:例如"确定"或故障—,因为指定的设定点太低:

恒温器Thermostat示例在服务器 DeviceConnection 类中实现自己的终端调用器
/// <summary>Represents the server-side of the connection from the device to this server. This
/// connection remains valid across re-connections from the device.</summary>
internal class DeviceConnection : IInvoker
{
private volatile IInvoker? _invoker;
public async Task<IncomingResponse> InvokeAsync(
OutgoingRequest request,
CancellationToken cancellationToken = default)
{
if (_invoker is IInvoker invoker)
{
try
{
return await invoker.InvokeAsync(request, cancellationToken);
}
catch (ObjectDisposedException)
{
// throw NotFound below
}
}
throw new DispatchException(StatusCode.NotFound, "The device is not connected.");
}
/// <summary>Sets the invoker that represents the latest connection from the device.</summary>
internal void SetInvoker(IInvoker invoker) => _invoker = invoker;
}
设备连接:从设备到服务器的最新连接。 拥有一个,在连接中留下来的终端调用器非常有用: 它允许恒温器服务器创建管道和代理,无需每次设备重新连接时,重新创建。
结论
反向调用生成 RPC 是一个强大的功能,是 IceRPC 与其他 RPC 框架区分的主要功能。 可以利用此功能构建,具有有意义的语义的网络应用程序,而且这些应用程序可以在防火墙上开心地正常工作"打洞"。
作者结语
- 一直做,不停做,才能提升速度
- 翻译的不好,请手下留情,谢谢
- 如果对我有点小兴趣,如可加我哦,一起探讨人生,探讨道的世界。
- 觉得还不错的话,点个赞哦

.Net 8.0 下的新RPC,IceRPC之试试的新玩法"打洞"的更多相关文章
- Ubuntu18.04和OpenWrt 18.06.0 下使用aria2和BaiduExport处理百度盘下载
Ubuntu下没有可用的百度盘客户端, 下载大文件如果通过浏览器就会特别不可靠. 可以使用Chrome插件 BaiduExport 得到下载链接和参数后, 通过aria2进行下载. Aria2安装 直 ...
- Ubuntu14.0下安装Zend Framework 2
Ubuntu14.0下安装Zend Framework 2为了安装这个东西,忙活了快一天了,参考中文博客一直没有安装成功,有些博客的时间也是已经很早了,后来google看英文版的才安装成功,这里记录一 ...
- .NET Framework4.0 下的多线程
一.简介 在4.0之前,多线程只能用Thread或者ThreadPool,而4.0下提供了功能强大的Task处理方式,这样免去了程序员自己维护线程池,而且可以申请取消线程等...所以本文主要描述Tas ...
- NodeJS - Express 3.0下ejs模板使用 partial展现 片段视图
如果你也在看Node.js开发指南,如果你也在一步一步实现 microBlog 项目!也许你会遇到本文提到的问题,如果你用的是Express 3.0 本书实例背景是 Express 2.0 而如今升级 ...
- 【CNMP系列】CentOS7.0下安装PHP5.6.30服务
上一节我们讲过了如何在CentOS7.0下安装MySql服务,如果没有看到欢迎页面的朋友,可以加我的个人微信详聊:litao514148204 附上一节地址:http://www.cnblogs.co ...
- 【CNMP系列】CentOS7.0下安装Nginx服务
话步前言,CNMP之路,系统起步:http://www.cnblogs.com/riverdubu/p/6425028.html 这回我来讲解下CentOS7.0下如何安装和配置Nginx服务 Ngi ...
- 【Linux】CentOS7.0下安装JDK环境
写在前面:此次试验是在CentOS7上面安装的,亲测成功. 所需工具:JDK1.8安装包,xftp 具体步骤: 1,首先使用xftp连接到自己的虚拟机,然后查看是否有"/usr/java/j ...
- Centos 7.0 下安装 Zabbix server 3.0服务器的安装及 监控主机的加入(1)
一.本系列分为6部分 1.Centos 7.0 下安装 Zabbix server 3.0服务器的安装及 监控主机的加入 2.Centos 6.5 下安装 Zabbix server 3.0服务器的安 ...
- odoo8.0下selection_add的使用
在odoo中有selection类型的字段,用于限定字段的值在某些范围之内,在view上面显示此字段时,会显示一个下拉的列表. 如果是自己新定义的字段,这个列表的内容可以自己定义,但如果是继承自某个对 ...
- .NET2.0引用.NET3.5的System.Core.dll&Dapper在.NET2.0下的配置
微软MSDN对.NET2.0,3.0,3.5的描述: .NET Framework 版本 2.0.3.0 和 3.5 是使用同一 CLR 版本 (CLR 2.0) 生成的. 这些版本表示单个安装的连续 ...
随机推荐
- verilog之function
verilog之function 1.基本作用 function,就是声明一个函数.与task的区别就是有参数.function的返回值就是函数名(可以设置位宽),输入值任意,均作为输入参数.代码块需 ...
- KingbaseES V8R6集群运维案例-- 备库数据库服务意外down分析
案例说明: KingbaseES V8R6一主二备架构的集群,两个备库节点sys_log日志分别不同时间点收到'fast shutdown request'的日志信息,导致备库数据库服务down,需要 ...
- C#中base关键字的几种用法 (base可以对派生类(子类)实例中调用基类(父类)的构造函数方法或者基类上已经被重写的虚方法)
base最大的使用就是"面向对象"开发的多态中.base可以对派生类(子类)实例中调用基类(父类)的构造函数方法或者基类上已经被重写的虚方法. 首先声明两个类 A B public ...
- HarmonyOS应用开发—资源分类与访问
应用开发过程中,经常需要用到颜色.字体.间距.图片等资源,在不同的设备或配置中,这些资源的值可能不同. ● 应用资源:借助资源文件能力,开发者在应用中自定义资源,自行管理这些资源在不同的设备或配置中 ...
- 94个JS/eTS开源组件首发上新,肯定有你要用的一款!
原文:https://mp.weixin.qq.com/s/6RdxNisTQoyPds811PNZKA,点击链接查看更多技术内容. 2021年的华为开发者大会(HDC2021)上,我们发布了新一代的 ...
- 实验1 c语言开发环境使用和数据类型 运算符 表达式
#include<stdio.h> #include<stdlib.h> int main() { printf(" O\n"); printf(" ...
- k8s 深入篇———— docker 是什么[一]
前言 简单的整理一下一些基本概念. 正文 简单运行一个容器: 创建一个容器: docker run -it busybox /bin/bash 然后看下进程: ps -ef 做了一个障眼法,使用的是p ...
- 重新点亮shell————特殊符号[五]
前言 简单整理一下特殊符号. 正文 特殊符号大全: 引号 ' 完成引用 "" 不完全引用 ` 执行命令 括号 () (()) $() 圆括号 单独使用圆括号会产生一个子shell ...
- jenkins 持续集成和交付——gogs安装(外篇)
前言 因为在jenkins 过程中一般需要去处理一些git的东西,为了完整性,填补一下git管理安装,这里使用gogs,因为gogs比较小,我运行的小机器能够承受,当然只适合个人,这里用来做实验,网上 ...
- Pytorch Dataset入门
Dataset入门 Pytorch Dataset code:torch/utils/data/dataset.py#L17 Pytorch Dataset tutorial: tutorials ...