【Azure Redis 缓存】云服务Worker Role中调用StackExchange.Redis,遇见莫名异常(RedisConnectionException: UnableToConnect on xxx 或 No connection is available to service this operation: xxx)
问题描述
在Visual Studio 2019中,通过Cloud Service模板创建了一个Worker Role的角色,在角色中使用StackExchange.Redis来连接Redis。遇见了一系列的异常:
- RedisConnectionException: No connection is available to service this operation: PING; It was not possible to connect to the redis server(s); ConnectTimeout; IOCP: (Busy=0,Free=1000,Min=8,Max=1000), WORKER: (Busy=2,Free=32765,Min=8,Max=32767), Local-CPU: n/a
- RedisConnectionException: UnableToConnect on xxxxxx.redis.cache.chinacloudapi.cn:6380/Interactive, origin: ResetNonConnected, input-buffer: 0, outstanding: 0, last-read: 5s ago, last-write: 5s ago, unanswered-write: 524763s ago, keep-alive: 60s, pending: 0, state: Connecting, last-heartbeat: never, last-mbeat: -1s ago, global: 5s ago, mgr: Inactive, err: never
- IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
- SocketException: An existing connection was forcibly closed by the remote host
异常截图:
问题分析
根据异常信息 Socket Exception, 在建立连接的时候被Remote Host关闭,也就是Redis服务端强制关闭了此连接。那么就需要进一步分析,为什么Redis会强制关闭连接呢? 查看Redis的连接字符串:
xxxxxx.redis.cache.chinacloudapi.cn:6380,password=<access key>,ssl=True,abortConnect=False
使用6380端口,建立SSL连接,在连接字符串中已经启用SSL。在创建Azure Redis的资源中,会发现一段提示:TLS1.0,1.1已不被支持。需要使用TLS1.2版本。
而当前的Cloud Service使用的是.NET Framework 4.5。 而恰巧,在 .NET Framework 4.5.2 或更低版本上,Redis .NET 客户端默认使用最低的 TLS 版本;在 .NET Framework 4.6 或更高版本上,则使用最新的 TLS 版本。
所以如果使用的是较旧版本的 .NET Framework,需要手动启用 TLS 1.2: StackExchange.Redis: 在连接字符串中设置 ssl=true
和 sslprotocols=tls12
。
问题解决
在字符串中添加 ssl=True,sslprotocols=tls12, 完整字符串为:
string cacheConnection = "xxxxxx.redis.cache.chinacloudapi.cn:6380,password=xxxxxxxxx+xxx+xxxxxxx=,ssl=True,sslprotocols=tls12, abortConnect=False";
在Visual Studio 2019代码中的效果如:
Could Service 与 Redis 使用的简单代码片段为
WorkerRole:
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks; namespace WorkerRole1
{
public class WorkerRole : RoleEntryPoint
{
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly ManualResetEvent runCompleteEvent = new ManualResetEvent(false); private RedisJob redisjob1 = new RedisJob(); public override void Run()
{
Trace.TraceInformation("WorkerRole1 is running"); try
{
this.RunAsync(this.cancellationTokenSource.Token).Wait();
}
finally
{
this.runCompleteEvent.Set();
}
} public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; // For information on handling configuration changes
// see the MSDN topic at https://go.microsoft.com/fwlink/?LinkId=166357. bool result = base.OnStart(); Trace.TraceInformation("WorkerRole1 has been started"); return result;
} public override void OnStop()
{
Trace.TraceInformation("WorkerRole1 is stopping"); this.cancellationTokenSource.Cancel();
this.runCompleteEvent.WaitOne(); base.OnStop(); Trace.TraceInformation("WorkerRole1 has stopped");
} private async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following with your own logic.
while (!cancellationToken.IsCancellationRequested)
{
Trace.TraceInformation("Working");
redisjob1.RunReidsCommand();
await Task.Delay(10000);
}
}
}
}
RedisJob:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using StackExchange.Redis; namespace WorkerRole1
{
class RedisJob
{
private static Lazy<ConnectionMultiplexer> lazyConnection = CreateConnection(); public static ConnectionMultiplexer Connection
{
get
{
return lazyConnection.Value;
}
} private static Lazy<ConnectionMultiplexer> CreateConnection()
{
return new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = "xxxxxx.redis.cache.chinacloudapi.cn:6380,password=xxxxxx+xxx+xxxx=,ssl=True,sslprotocols=tls12, abortConnect=False";
return ConnectionMultiplexer.Connect(cacheConnection);
});
} public void RunReidsCommand() { IDatabase cache = Connection.GetDatabase(); // Perform cache operations using the cache object... // Simple PING command
string cacheCommand = "PING";
Console.WriteLine("\nCache command : " + cacheCommand);
Console.WriteLine("Cache response : " + cache.Execute(cacheCommand).ToString()); // Simple get and put of integral data types into the cache
cacheCommand = "GET Message";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringGet()");
Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString()); cacheCommand = "SET Message \"Hello! The cache is working from a .NET console app!\"";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringSet()");
Console.WriteLine("Cache response : " + cache.StringSet("Message", "Hello! The cache is working from a .NET console app!").ToString()); // Demonstrate "SET Message" executed as expected...
cacheCommand = "GET Message";
Console.WriteLine("\nCache command : " + cacheCommand + " or StringGet()");
Console.WriteLine("Cache response : " + cache.StringGet("Message").ToString());
}
}
}
参考资料
删除与 Azure Cache for Redis 配合使用的 TLS 1.0 和 1.1: https://docs.microsoft.com/zh-cn/azure/azure-cache-for-redis/cache-remove-tls-10-11
将应用程序配置为使用 TLS 1.2
大多数应用程序使用 Redis 客户端库来处理与缓存的通信。 这里说明了如何将以各种编程语言和框架编写的某些流行客户端库配置为使用 TLS 1.2。
.NET Framework
在 .NET Framework 4.5.2 或更低版本上,Redis .NET 客户端默认使用最低的 TLS 版本;在 .NET Framework 4.6 或更高版本上,则使用最新的 TLS 版本。 如果使用的是较旧版本的 .NET Framework,则可以手动启用 TLS 1.2:
- StackExchange.Redis: 在连接字符串中设置
ssl=true
和sslprotocols=tls12
。- ServiceStack.Redis: 请按照 ServiceStack.Redis 说明操作,并至少需要 ServiceStack.Redis v5.6。
.NET Core
Redis .NET Core 客户端默认为操作系统默认 TLS 版本,此版本明显取决于操作系统本身。
根据操作系统版本和已应用的任何修补程序,有效的默认 TLS 版本可能会有所不同。 有一个关于此内容的信息源,也可以访问此处,阅读适用于 Windows 的相应文章。
但是,如果你使用的是旧操作系统,或者只是想要确保我们建议通过客户端手动配置首选 TLS 版本。
Java
Redis Java 客户端基于 Java 版本 6 或更早版本使用 TLS 1.0。 如果在缓存中禁用了 TLS 1.0,则 Jedis、Lettuce 和 Redisson 无法连接到 Azure Cache for Redis。 升级 Java 框架以使用新的 TLS 版本。
对于 Java 7,Redis 客户端默认不使用 TLS 1.2,但可以配置为使用此版本。 Jedis 允许你使用以下代码片段指定基础 TLS 设置:
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLParameters sslParameters = new SSLParameters();
sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
sslParameters.setProtocols(new String[]{"TLSv1.2"}); URI uri = URI.create("rediss://host:port");
JedisShardInfo shardInfo = new JedisShardInfo(uri, sslSocketFactory, sslParameters, null); shardInfo.setPassword("cachePassword"); Jedis jedis = new Jedis(shardInfo);Lettuce 和 Redisson 客户端尚不支持指定 TLS 版本,因此,如果缓存仅接受 TLS 1.2 连接,这些客户端将无法工作。 我们正在审查这些客户端的修补程序,因此请检查那些包是否有包含此支持的更新版本。
在 Java 8 中,默认情况下会使用 TLS 1.2,并且在大多数情况下都不需要更新客户端配置。 为了安全起见,请测试你的应用程序。
Node.js
Node Redis 和 IORedis 默认使用 TLS 1.2。
PHP
Predis
低于 PHP 7 的版本:Predis 仅支持 TLS 1.0。 这些版本不支持 TLS 1.2;必须升级才能使用 TLS 1.2。
PHP 7.0 到 PHP 7.2.1:默认情况下,Predis 仅使用 TLS 1.0 或 TLS 1.1。 可以通过以下变通办法来使用 TLS 1.2。 在创建客户端实例时指定 TLS 1.2:
$redis=newPredis\Client([
'scheme'=>'tls',
'host'=>'host',
'port'=>6380,
'password'=>'password',
'ssl'=>[
'crypto_type'=>STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT,
],
]);PHP 7.3 及更高版本:Predis 使用最新的 TLS 版本。
PhpRedis
PhpRedis 在任何 PHP 版本上均不支持 TLS。
Python
Redis-py 默认使用 TLS 1.2。
GO
Redigo 默认使用 TLS 1.2。
【完】
【Azure Redis 缓存】云服务Worker Role中调用StackExchange.Redis,遇见莫名异常(RedisConnectionException: UnableToConnect on xxx 或 No connection is available to service this operation: xxx)的更多相关文章
- Windows Azure -Azure 网站、云服务和虚拟机的对比
Azure 网站.云服务和虚拟机对比 概述 Azure提供了几种方法来承载网站: Azure网站.云服务和虚拟机.本文帮助您了解选项和为您的Web应用程序做出正确选择. Azure网站是大多数web应 ...
- Azure 网站、云服务和虚拟机比较
最后更新时间(英文版):09/24/2014 最后更新时间(中文版):04/11/2015 Azure 提供几种方式托管 web 应用程序,如 Azure 网站.云服务和虚拟机.查看这些不同的选项后, ...
- Windows Azure移动终端云服务管理(公测版)
概览 云在远方,管理在您手中.在这个移动为先 云为先的世界,服务不再是基于请求才提供,而是主动来到身边方便您的模式了.我们最近将会陆续推出几大移动端利器帮助您随时随地管理您的云服务. 首批利器之中排名 ...
- nopCommerce 3.9 大波浪系列 之 使用部署在Docker中的Redis缓存主从服务
一.概述 nop支持Redis作为缓存,Redis出众的性能在企业中得到了广泛的应用.Redis支持主从复制,HA,集群. 一般来说,只有一台Redis是不可行的,原因如下: 单台Redis服务器会发 ...
- 【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据
任务描述 本次集中介绍使用Windows和Linux()搭建本地Redis服务器的步骤,从备份的RDB文件中加载数据,以及如何生成AOF文件和通过AOF文件想已经运行的Redis追加数据. 操作步骤 ...
- Windows Azure虚拟机和云服务实例计费方式更新
在之前的Windows Azure计费账单中,A0,A1,A2,A3,A4系列的虚拟机(云服务实例)都是以A1为基准计费单位的,即: 虚拟机大小 计费单位(小时) A0 A1*0.25 A1 A1*1 ...
- Redis总结(二)C#中如何使用redis
上一篇讲述了安装redis<Redis总结(一)Redis安装>,同时也大致介绍了redis的优势和应用场景.本篇着重讲解.NET中如何使用redis和C#. Redis官网提供了很多开源 ...
- Redis总结(二)C#中如何使用redis(转载)
上一篇讲述了安装redis<Redis总结(一)Redis安装>,同时也大致介绍了redis的优势和应用场景.本篇着重讲解.NET中如何使用redis和C#. Redis官网提供了很多开源 ...
- 在.net Core中使用StackExchange.Redis 2.0
StackExchange.Redis 2.0做了大量的改进包括使用了高性能的IO库System.IO.Pipelines来提升性能以及解决Timeouts问题, 但是在.net Core2.2之前为 ...
随机推荐
- Auto ML自动调参
Auto ML自动调参 本文介绍Auto ML自动调参的算法介绍及操作流程. 操作步骤 登录PAI控制台. 单击左侧导航栏的实验并选择某个实验. 本文以雾霾天气预测实验为例. 在实验画布区,单击左上角 ...
- pytorch空间变换网络
pytorch空间变换网络 本文将学习如何使用称为空间变换器网络的视觉注意机制来扩充网络.可以在DeepMind paper 阅读更多有关空间变换器网络的内容. 空间变换器网络是对任何空间变换的差异化 ...
- NVIDIA Turing Architecture架构设计(上)
NVIDIA Turing Architecture架构设计(上) 在游戏市场持续增长和对更好的 3D 图形的永不满足的需求的推动下, NVIDIA 已经将 GPU 发展成为许多计算密集型应用的世界领 ...
- 短波红外(SWIR)相机camera
短波红外(SWIR)相机camera AVs Can't Drive Everywhere. Can TriEye's SWIR Camera Help? TriEye的短波红外(SWIR)摄像机能否 ...
- BlazorCharts 原生图表库的建设历程
作者:陈超超 Ant Design Blazor 项目贡献者,拥有十多年从业经验,长期基于.Net技术栈进行架构与开发产品的工作,现就职于正泰集团. 邮箱:timchen@live.com 欢迎各位读 ...
- Headline 项目总结中
目录 1.项目准备 1.1 rem适配 1.2 通用样式CSS 1.3删除测试代码 1.4Git托管 2.login页面 2.1 页面布局和表单校验 2.2login页的接口抽取 2.5.loadin ...
- 【Android编程】android平台的MITM瑞士军刀_cSploit源码解析及中间人攻击复现
/文章作者:Kali_MG1937 作者博客ID:ALDYS4 QQ:3496925334 未经允许,禁止转载/ 何为MITM欺骗,顾名思义,中间人攻击的含义即为在局域网中充当数据包交换中间人的角色 ...
- 从一条sql报错解决过程学习程序员查bug的思路
从oracle迁移数据到达梦后,发现数据库默认值都丢失了.于是我想从oracle数据库将默认值查出来,在达梦数据库加回去. 于是上网查了一下,看怎么获取oracle数据库字段默认值信息,找到了这个sq ...
- centos7 安装卸载程序rpm使用方法
1.安装 rpm 包: ➢ 基本语法 rpm -ivh RPM 包全路径名称 2.卸载 rpm 包: ➢ 基本语法 rpm -e RPM 包的名称 ➢ 应用案例 删除 firefox 软件包 rpm ...
- 【题解】codeforces 8c Looking for Order 状压dp
题目描述 Lena喜欢秩序井然的生活.一天,她要去上大学了.突然,她发现整个房间乱糟糟的--她的手提包里的物品都散落在了地上.她想把所有的物品都放回她的手提包.但是,这里有一点问题:她一次最多只能拿两 ...