错误使用.Net Redis客户端CSRedisCore,自己挖坑自己填

本文2019年中原创首发于博客园,当时使用CSRedisCore的排障思路引起很大反响,当时被张队公众号翻牌,本次转回公号。

背景

    上次Redis MQ分布式改造之后,编排的容器稳定运行一个多月,昨天突然收到ETL端同事通知,没有采集到解析日志。

赶紧进服务器 docker ps查看容器:

用于数据接收的ReceiverApp容器挂掉了;

尝试docker container start [containerid],几分钟后该容器再次崩溃。 

Redis连接超限

docker log [containerid]  查看容器日志: 显示连接Redis服务的客户端数量超限。

CSRedis.RedisException: ERR max number of clients reached.

Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]Executed action EqidManager.Controllers.EqidController.BatchPutEqidAndProfileIds (EqidReceiver) in 7.1767ms
fail: Microsoft.AspNetCore.Server.Kestrel[13]Connection id "0HLPR3AP8ODKH", Request id "0HLPR3AP8ODKH:00000001": An unhandled exception was thrown by the application.
CSRedis.RedisException: ERR max number of clients reachedat CSRedis.CSRedisClient.GetAndExecute[T](RedisClientPool pool, Func`2 handler, Int32 jump, Int32 errtimes)at CSRedis.CSRedisClient.ExecuteScalar[T](String key, Func`3 hander)at CSRedis.CSRedisClient.LPush[T](String key, T[] value)at RedisHelper.LPush[T](String key, T[] value)at EqidManager.Controllers.EqidController.BatchPutEqidAndProfileIds(List`1 eqidPairs) in /home/gitlab-runner/builds/haD2h5xC/0/webdissector/datasource/eqid-manager/src/EqidReceiver/Controllers/EqidController.cs:line 31at lambda_method(Closure , Object )at Microsoft.Extensions.Internal.ObjectMethodExecutorAwaitable.Awaiter.GetResult()at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)at System.Threading.Tasks.ValueTask`1.get_Result()at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync()at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync()at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextResourceFilter()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Rethrow(ResourceExecutedContext context)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()at Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http.HttpProtocol.ProcessRequests[TContext](IHttpApplication`1 application)
info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]Request finished in 8.9549ms 500 
【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)
【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)
【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)
【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)
【dockerhost:6379/0】仍然不可用,下一次恢复检查时间:09/17/2019 03:11:25,错误:(ERR max number of clients reached)

快速思考:目前编排的某容器使用CSRedisCore对16个Redis DB实例化了16个客户端,但Redis服务也不至于这么不经折腾吧。

赶紧进redis.io官网搜集资料。

After the client is initialized, Redis checks if we are already at the limit of the number of clients that it is possible to handle simultaneously (this is configured using the maxclients configuration directive, see the next p of this document for further information).

In case it can't accept the current client because the maximum number of clients was already accepted, Redis tries to send an error to the client in order to make it aware of this condition, and closes the connection immediately. The error message will be able to reach the client even if the connection is closed immediately by Redis because the new socket output buffer is usually big enough to contain the error, so the kernel will handle the transmission of the error.

大致意思是:maxclients配置了Redis服务允许的客户端最大连接数, 如果当前连接的客户端数超限,Redis服务会回发一个错误消息给客户端,并迅速关闭客户端连接。

立刻进入Redis宿主机查看默认配置,确认当前Redis服务的maxclients=10000(这是一个动态值,由maxclients和最大进程文件句柄决定)。

# Set the max number of connected clients at the same time. By default
# this limit is set to 10000 clients, however if the Redis server is not
# able to configure the process file limit to allow for the specified limit
# the max number of allowed clients is set to the current file limit
# minus 32 (as Redis reserves a few file descriptors for internal uses).
#
# Once the limit is reached Redis will close all the new connections sending
# an error 'max number of clients reached'.
# maxclients 10000

通过Redis-Cli登录Redis服务器, 立刻被踢下线。

基本可认定Redis客户端使用方式有问题。

CSRedisCore使用方式

查看Redis官方资料,可利用redis-cli命令info clients、client list 分析客户端连接。

info clients 命令显示现场确实有10000的连接数;

client list命令输出字段的官方解释:

  • addr: The client address, that is, the client IP and the remote port number it used to connect with the Redis server.

  • fd: The client socket file descriptor number.

  • name: The client name as set by CLIENT SETNAME.

  • age: The number of seconds the connection existed for.

  • idle: The number of seconds the connection is idle.

  • flags: The kind of client (N means normal client, check the full list of flags).

  • omem: The amount of memory used by the client for the output buffer.

  • cmd: The last executed command.

以上解释表明Redis服务器收到很多ip=172.16.1.3(故障容器在网桥内的Ip 地址)的客户端连接,这些连接最后发出的是ping命令(这是一个测试命令)

故障容器使用的Redis客户端是CSRedisCore,该客户端只是单纯将msg写入Redis list数据结构,CSRedisCore上相关github issue给了一些启发。

发现自己将CSRedisClient实例化代码写在 .NETCore api Controller构造函数,这样每次请求构造Controller时都实例化一次Redis客户端,最终Redis客户端连接数达到最大允许连接值。

依赖注入三种模式: 单例(系统内单一实例,一次性注入);瞬态(每次请求产生实例并注入);自定义范围。

有关dotnet apiController 以瞬态模式注入,请查阅文末链接。

还有一个疑问?

为什么Redis服务器没有释放空闲的客户端连接,如果空闲连接被释放了,即使我写了low代码也不至于如此?

查询官方:

By default recent versions of Redis don't close the connection with the client if the client is idle for many seconds: the connection will remain open forever.

However if you don't like this behavior, you can configure a timeout, so that if the client is idle for more than the specified number of seconds, the client connection will be closed.

You can configure this limit via redis.conf or simply using CONFIG SET timeout <value>.

大致意思是最新的Redis服务默认不会释放空闲的客户端连接。

# Close the connection after a client is idle for N seconds (0 to disable)
timeout 0

修改以上Redis服务配置可释放空闲客户端连接。

我们最佳实践当然不是修改Redis idle timeout 配置,问题本质还是因为我实例化了多客户端,赶紧将CSRedisCore实例化代码移到startup.cs并注册为单例。

大胆求证

info clients命令显示稳定在53个Redis连接。

client list命令显示:172.16.1.3(故障容器)建立了50个客户端连接,编排的另一个容器webapp建立了2个连接,redis-cli命令登录到服务器建立了1个连接。

那么问题来了,修改之后,ReceiverApp容器为什么还稳定建立了50个redis连接?

进一步与CSRedisCore原作者沟通,确认CSRedisCore有预热机制,默认在连接池中预热了 50 个连接。

bingo,故障和困惑全部排查清楚。

总结

经此一役,在使用CSRedisCore客户端时,要深入理解

①  Stackexchange.Redis 使用的多路复用连接机制(使用时很容易想到注册为单例),CSRedisCore开源库采用连接池机制,在高并发场景下强烈建议注册为单例, 否则在生产使用中可能会误用在瞬态请求中实例化,导致redis连接数几天之后消耗完。

②   CSRedisCore会默认建立连接池,预热50个连接,开发者要心中有数。

额外的方法论: 尽量不要从某度找答案,要学会问问题,并尝试从官方、stackoverflow、github社区寻求解答,你挖过的坑也许别人早就挖过并踏平过。

Update

    很多博友说问题在于我没有细看CSRedisCore官方readme(readme推荐使用单例),使用方式上我确实没有做成单例:

③ 一般连接池都会有空闲释放回收机制 (CSRedisCore也是连接池机制),所以当时并没有把单例放在心上

④ 本次重要知识点:Redis默认并不会释放空闲客户端连接(但是又设置了最大连接数),这也直接促成了本次容器崩溃事故。

嗯,坑是自己挖的。

+ https://stackoverflow.com/questions/57553401/net-core-are-mvc-controllers-default-singleton

+ https://redis.io/topics/clients

+ https://github.com/2881099/csredis/issues/115

往期精彩回顾

AspNetCore结合Redis实践消息队列

TPL Dataflow组件应对高并发,低延迟要求

基于docker-compose的Gitlab CI/CD实践&排坑指南

转载是一种动力,分享是一种美德    ~~..~~

如果你觉得文章还不赖,您的鼓励是原创干货作者的最大动力,让我们一起激浊扬清。

扫码

关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

最佳牛围栏(二分)

2020.12.30开始学习AcWing算法《算法竞赛进阶指南》&#xff1b; 上传博客方便复习。 #include <iostream> using namespace std; #include <algorithm> const int N 100001; int n, m; int cow[N]; double sum[N];bool check (double ave) {for (int i 1; i <…

IHostingEnvironment VS IHostEnvironment - .NET Core 3.0中的废弃类型

原文&#xff1a;https://andrewlock.net/ihostingenvironment-vs-ihost-environment-obsolete-types-in-net-core-3/作者&#xff1a;Andrew Lock译者&#xff1a;Lamond Lu本篇是如何升级到ASP.NET Core 3.0系列文章的第二篇。Part 1 - 将.NET Standard 2.0 类库转换为.NET C…

【敏捷案例】老板太外行,朝令夕改!要不要拿了年终奖就撤?

快到春节了&#xff0c;不知道有多少人在等着春节后跳槽&#xff0c;想跳槽的原因也很多&#xff0c;其中一个比较普遍的原因是和老板聊不到一块儿去。前两天&#xff0c;有个学员咨询了我们一个问题&#xff0c;因为这个问题比较普遍&#xff0c;拿出来和大家一起讨论一下~提出…

【2019总结篇】谈谈数字化时代,ERP如何坐稳数字化底座

源宝导读&#xff1a;面向未来&#xff0c;信息化、在线化、智能化&#xff0c;开放应该是数字化转型的核心要素&#xff01;本文将重点介绍2019年明源云ERP开放平台在推进数字化转型中的核心技术层面的实践成果。一、前言面向未来&#xff0c;信息化、在线化、智能化&#xff…

dotNetCore操作Redis(含CentOS7哨兵模式部署)

现在说到使用缓存中间件基本就是 Redis 了&#xff0c;通常开发环境或测试环境部署一个单机版就可以运行了&#xff0c;但要上生产环境还需要进行高可用的方式来部署&#xff0c;本文说说在 CentOS7 中 Redis 高可用的部署以及在 dotNetCore 中怎样调用。环境CentOS&#xff1a…

php和mysql一键安装包_iis+php+mysql一键安装教程和安装包

导读&#xff1a;iis上mysqlphp一键安装 很多用vps的朋友&#xff0c;在windons系统iis上配置mysqlphp环境的时候都非常的头痛&#xff0c;过程非常复杂和麻烦&#xff0c;所以我们推荐大家用mysqlphp一键安装包&#xff1a; 1、下载mysqlphp一键安装包&#xff0c;安装包下载地…

程序员过关斩将--自定义线程池来实现文档转码

背景我司在很久之前&#xff0c;一位很久之前的同事写过一个文档转图片的服务&#xff0c;具体业务如下&#xff1a;1. 用户在客户端上传文档&#xff0c;可以是ppt&#xff0c;word&#xff0c;pdf 等格式&#xff0c;用户上传完成可以在客户端预览上传的文档&#xff0c;预览…

UnitTest in .NET(Part 4)

Photo &#xff1a;Unit Test in Visual Studio文 | Edison Zhou上一篇我们学习了如何使用模拟对象进行交互测试。这一篇我们则会进一步使用隔离框架支持适应未来和可用性的功能。为何使用模拟框架&#xff1f; 对于复杂的交互场景&#xff0c;可能手工编写模拟对象和存根就会变…

Xamarin.Forms弹出对话框插件

微信公众号&#xff1a;Dotnet9&#xff0c;网站&#xff1a;Dotnet9&#xff0c;问题或建议&#xff0c;请网站留言&#xff1b;如果您觉得Dotnet9对您有帮助&#xff0c;欢迎赞赏。Dotnet9.com内容目录实现效果业务场景编码实现本文参考源码下载1.实现效果弹出动画 2.业务场景…

Pycharm安装第三方库

转载地址&#xff1a; https://www.cnblogs.com/bwjblogs/p/12839463.html 今晚想安装一些第三方库但是pip版本低&#xff0c;安装一直报错&#xff0c;输入升级的命令也一直不行。于是在pycharm上安装&#xff0c;但是还是一直失败&#xff0c;下面提出解决办法。 然后在搜索…

CAP 3.0 版本正式发布

前言大家好&#xff0c;我们很高兴宣布 CAP 发布了 3.0 版本正式版。自从上次 CAP 2.6 版本发布 以来&#xff0c;已经过去了几个月的时间&#xff0c;关注的朋友可能知道&#xff0c;在这几个月的时间里&#xff0c;也发布了几个预览版的 3.0 版本的NuGet包。3.0 是一个主要版…

mysql字符集变为gbk_MYSQL数据库默认latin1字符集转换为GBK或UTF8

可以采用下面的方法latin1字符集转换为gbk字符集或utf8字符集。具体的转换步骤如下&#xff1a;一、latin1转gbk1、导出数据库mysqldump --default-character-setlatin1 -h 数据库连接ip -u root -P 3306 -p数据库密码 db_name table_name > /usr/home/test/table_name.sql2…

微服务统计,分析,图表,监控一体化的HttpReports项目在.Net Core 中的使用

简单介绍HttpReports 是 .Net Core 下的一个Web项目, 适用于WebAPI&#xff0c;Ocelot网关应用&#xff0c;MVC项目&#xff0c;非常适合针对微服务应用使用&#xff0c;通过中间件的形式集成到您的项目中&#xff0c;可以让开发人员快速的搭建出一个 数据统计&#xff0c;分析…

查看node的位置_升级Node版本RN项目运行报错cb.apply is not a function

今日打算安装一下ReactNative官方推荐的脚手架工具Ignite。infinitered/ignite​github.comIgnite是一套整合了 Redux 以及一些常见 UI 组件的脚手架。它带有一个命令行可以生成 app、组件或是容器。在安装的过程中&#xff0c;提示当前系统安装的node版本过低&#xff0c;无法…

C++构造函数调用规则

1.拷贝函数的值拷贝&#xff1a; #include <iostream> using namespace std;//构造函数的调用规则&#xff1a; //1,创建一个类&#xff0c;C编译器会给每个类都添加至少3个函数 //默认函数(空实现)&#xff0c;析构函数(空实现)&#xff0c;拷贝函数(值拷贝)class Pers…

HTTP Strict Transport Security (HSTS) in ASP.NET Core

本文是《2020年了&#xff0c;再不会HTTPS就老了》的后篇&#xff0c;本文着重聊一聊HTTP Strict Transport Security协议的概念和应用。启用 HTTPS 还不够安全现在很多站点通过HTTPS对外提供服务&#xff0c;用户在访问某站点&#xff0c;往往会直接输入站点域名&#xff08;b…

mysql支持的平台和操作系统_MySQL 数据库所支持的操作系统_MySQL

MySQL数据库所支持的操作系统&#xff1a;我们使用GNU Autoconf&#xff0c;因此将MySQL移植到所有使用Posix线程和C编译器的现代系统是可能的。(要求服务器支持线程。如果只是编译客户端代码&#xff0c;则只需要C编译器)。我们主要在Linux(SuSE和Red Hat)、FreeBSD和Sun Sola…

C++深拷贝与浅拷贝

浅拷贝&#xff1a; 简单的赋值拷贝操作。 深拷贝&#xff1a; 在堆区重新申请空间&#xff0c;进行拷贝操作。 首先我们先写这样的一段代码&#xff1a; #include <iostream> using namespace std; //深拷贝与浅拷贝class Person {public:Person() {cout << &qu…

BeetleX轻松搭建HTTP和Weboskcet网关

在新版本的BeetleX.Bumblebee中实现了对Weboskcet代理的支持&#xff0c;因此使用BeetleX搭建同时支持HTTP和Weboskcet的网关只需要几行代码的工作就能完成&#xff1b;接下来构建一个简单的网关程序并测试一下对asp.net core SignalR进行代理的应用。引用组件使用BeetleX构建网…

[功能发布]Excel与PowerBI互通互联升级版连接SSAS和AzureAS

Excel催化剂发自内心地热爱着PowerBI社区&#xff0c;从最开始提供了PowerBIDeskTop的互通互联功能&#xff0c;到无偿奉献所有此功能的核心原代码&#xff0c;再到今天的高潮&#xff0c;献上最具商业价值的高级功能&#xff0c;让企业级商业智能BI项目插上翅膀&#xff0c;最…