问题描述

在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)的更多相关文章

  1. Windows Azure -Azure 网站、云服务和虚拟机的对比

    Azure 网站.云服务和虚拟机对比 概述 Azure提供了几种方法来承载网站: Azure网站.云服务和虚拟机.本文帮助您了解选项和为您的Web应用程序做出正确选择. Azure网站是大多数web应 ...

  2. Azure 网站、云服务和虚拟机比较

    最后更新时间(英文版):09/24/2014 最后更新时间(中文版):04/11/2015 Azure 提供几种方式托管 web 应用程序,如 Azure 网站.云服务和虚拟机.查看这些不同的选项后, ...

  3. Windows Azure移动终端云服务管理(公测版)

    概览 云在远方,管理在您手中.在这个移动为先 云为先的世界,服务不再是基于请求才提供,而是主动来到身边方便您的模式了.我们最近将会陆续推出几大移动端利器帮助您随时随地管理您的云服务. 首批利器之中排名 ...

  4. nopCommerce 3.9 大波浪系列 之 使用部署在Docker中的Redis缓存主从服务

    一.概述 nop支持Redis作为缓存,Redis出众的性能在企业中得到了广泛的应用.Redis支持主从复制,HA,集群. 一般来说,只有一台Redis是不可行的,原因如下: 单台Redis服务器会发 ...

  5. 【Azure Redis 缓存】Windows和Linux系统本地安装Redis, 加载dump.rdb中数据以及通过AOF日志文件追加数据

    任务描述 本次集中介绍使用Windows和Linux()搭建本地Redis服务器的步骤,从备份的RDB文件中加载数据,以及如何生成AOF文件和通过AOF文件想已经运行的Redis追加数据. 操作步骤 ...

  6. Windows Azure虚拟机和云服务实例计费方式更新

    在之前的Windows Azure计费账单中,A0,A1,A2,A3,A4系列的虚拟机(云服务实例)都是以A1为基准计费单位的,即: 虚拟机大小 计费单位(小时) A0 A1*0.25 A1 A1*1 ...

  7. Redis总结(二)C#中如何使用redis

    上一篇讲述了安装redis<Redis总结(一)Redis安装>,同时也大致介绍了redis的优势和应用场景.本篇着重讲解.NET中如何使用redis和C#. Redis官网提供了很多开源 ...

  8. Redis总结(二)C#中如何使用redis(转载)

    上一篇讲述了安装redis<Redis总结(一)Redis安装>,同时也大致介绍了redis的优势和应用场景.本篇着重讲解.NET中如何使用redis和C#. Redis官网提供了很多开源 ...

  9. 在.net Core中使用StackExchange.Redis 2.0

    StackExchange.Redis 2.0做了大量的改进包括使用了高性能的IO库System.IO.Pipelines来提升性能以及解决Timeouts问题, 但是在.net Core2.2之前为 ...

随机推荐

  1. NVIDIA空中导航SDK改造5G通信

    NVIDIA空中导航SDK改造5G通信 Transforming Next-Generation Wireless with 5T for 5G and the NVIDIA Aerial SDK N ...

  2. python_request 接口测试线性框架,模块化思想,增加日志打印

    一.大框架 如下为一个简单的线性框架,同时编写common_api 模块,把一个个接口进行封装,案例编写时候只需要直接调用输入参数即可. 二. test_cases 模块具体写法 2.1  commo ...

  3. wrk(2)- Lua 脚本的使用

    背景 要用 wrk 进行压测 看了下其他同事的压测,都用了 Lua 脚本来自定义一些东西 所以这一篇主要讲 Lua 脚本 Lua 介绍 Lua 脚本是一种轻量小巧的脚本语言,用标准 c 语言编写,并以 ...

  4. application/octet-stream二进制流, springboot项目, postman 测试参数设置

    这里使用 原生String接受,之后解析,

  5. 【模拟8.01】big(trie树)

    一道trie树的好题 首先我们发现后手对x的操作就是将x左移一位,溢出位在末尾补全 那么我们也可以理解为现将初值进行该操作,再将前i个元素异或和进行操作,与上等同. 那么我们等于转化了问题:     ...

  6. 冷饭新炒 | 深入Quartz核心运行机制

    目录 Quartz的核心组件 JobDetail Trigger 为什么JobDetail和Trigger是一对多的关系 常见的Tigger类型 怎么排除掉一些日期不触发 Scheduler List ...

  7. Nginx报400 Bad Request

    本地遇到此问题,关闭浏览器,重新运行代码即可

  8. 『无为则无心』Python基础 — 13、Python流程控制语句(条件语句)

    目录 1.流程控制基本概念 2.选择结构(条件语句) (1)条件语句概念 (2)if语句语法 (3)if...else...语句 (4)多重判断 (5)if语句嵌套 3.应用:猜拳游戏 4.三元运算符 ...

  9. Keepalive介绍及工作原理

    注:keepalive和Nginx和高可用没有关联. 1.什么是高可用,为什么要设计高可用? 1.两台业务系统启动着相同的服务,如果有一台故障,另一台自动接管,我们将国称之为高可用.2.系统可用率算法 ...

  10. POJ 3347 Kadj Squares 计算几何

    求出正方形的左右端点,再判断是否覆盖 #include <iostream> #include <cstdio> #include <cstring> #inclu ...