netcore服务程序暴力退出导致的业务数据不一致的一种解决方案(优雅退出)
一: 问题提出
现如今大家写的netcore程序大多部署在linux平台上,而且服务程序里面可能会做各种复杂的操作,涉及到多数据源(mysql,redis,kafka)。成功部署成后台
进程之后,你以为这样就万事大吉了? 却不知当你更新代码时,暴力的kill掉这个进程导致你的业务出现数据不一致,业务流程被破坏等等问题。比如下面这段代码:
1. TestService
public class TestService
{
public static void Run()
{
while (true)
{
Console.WriteLine($"{DateTime.Now}: 1. 获取mysql");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 2. 获取redis");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 3. 更新monogdb");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 4. 通知kafka");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 5. 所有业务处理完毕");
System.Threading.Thread.Sleep();
}
}
}
2. Main程序
public static void Main(string[] args)
{
var bgtask = Task.Run(() => { TestService.Run(); }); bgtask.Wait();
}
这里不考虑程序的健壮性,只表达这里可能出现的问题,当程序退出后,这里必然会遇到TestService.Run方法出现未执行完的情况,导致数据不一致,比如下
面我简单的部署了一下,可以看到程序到了 5:03:24s之后就结束了,显然破坏了业务逻辑。程序没有完整的执行结束,那问题该怎么解决呢?
[root@localhost netcore]# nohup dotnet ConsoleApp4.dll &
[]
[root@localhost netcore]# nohup: ignoring input and appending output to ‘nohup.out’ [root@localhost netcore]# ps -ef | grep dotnet
root : pts/ :: dotnet ConsoleApp4.dll
root : pts/ :: grep --color=auto dotnet
[root@localhost netcore]# kill
[root@localhost netcore]# tail nohup.out
// :: PM: . 获取redis
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM: . 获取mysql
// :: PM: . 获取redis
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM: . 获取mysql
[]+ Done nohup dotnet ConsoleApp4.dll
[root@localhost netcore]#
二:思考 kill 命令
要解决这个问题,大家一定要从kill命令入手, 在centos上进行kill -x pid 的时候,不知道有多少人了解了这个命令,除了常见的 kill -9 pid ,其实还有很多其
他的数字,则代表其他的意思,可以通过kill -l 看一下。
[root@localhost ~]# kill -l
) SIGHUP ) SIGINT ) SIGQUIT ) SIGILL ) SIGTRAP
) SIGABRT ) SIGBUS ) SIGFPE ) SIGKILL ) SIGUSR1
) SIGSEGV ) SIGUSR2 ) SIGPIPE ) SIGALRM ) SIGTERM
) SIGSTKFLT ) SIGCHLD ) SIGCONT ) SIGSTOP ) SIGTSTP
) SIGTTIN ) SIGTTOU ) SIGURG ) SIGXCPU ) SIGXFSZ
) SIGVTALRM ) SIGPROF ) SIGWINCH ) SIGIO ) SIGPWR
) SIGSYS ) SIGRTMIN ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX-
) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX-
) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX- ) SIGRTMAX-
) SIGRTMAX- ) SIGRTMAX
其中里面的
2. SIGNIT (Ctrl+C)
3. SIGQUIT (退出)
9. SIGKILL (强制终止)
15. SIGTERM (终止)
都可以让程序退出,好了,线索出来了,那我能不能让程序捕获到kill命令发出的这Sigxxx信号呢??? 通过寻找资料之后的一阵浑身痉挛,你明白了原来只有
-9是不能让程序捕获到,其他的程序都能捕获,那么既然能捕获,我就可以在捕获的事件中做程序的安全退出。 大概的脑图就像下面这样:
三:研究如何捕获
在core 2.0之后,获取sigterm就非常简单了,可以在当前应用程序域中挂载一个ProcessExit 事件,在ProcessExit中让应用程序安全的退出。
然后还有一个问题就是,如何在ProcessExit中通知TestService结束执行呢? 这里就用到了CancellationTokenSource 这种线程安全的取消协调机制,思考之后
画出来的脑图大概是这个样子,不一定对,但是逻辑大概出来了。。。
四: 问题解决
有了上面的脑图,写起代码就快啦~~~
1. Main函数
public static void Main(string[] args)
{
var cts = new CancellationTokenSource(); var bgtask = Task.Run(() => { TestService.Run(cts.Token); }); AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
Console.WriteLine($"{DateTime.Now} 后台测试服务,准备进行资源清理!"); cts.Cancel(); //设置IsCancellationRequested=true,让TestService今早结束
bgtask.Wait(); //等待 testService 结束执行 Console.WriteLine($"{DateTime.Now} 恭喜,Test服务程序已正常退出!"); Environment.Exit();
}; Console.WriteLine($"{DateTime.Now} 后端服务程序正常启动!"); bgtask.Wait();
}
Main函数中做了如上的变更,将CancellationToken传递给 Run方法,这样当我执行Cancel的时候,Run方法就能感知到Token的变化,然后就是调用Wait等待
TestService执行结束。
2. TestService
public class TestService
{
public static void Run(CancellationToken token)
{
while (true)
{
if (token.IsCancellationRequested) break; Console.WriteLine($"{DateTime.Now}: 1. 获取mysql");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 2. 获取redis");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 3. 更新monogdb");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 4. 通知kafka");
System.Threading.Thread.Sleep();
Console.WriteLine($"{DateTime.Now}: 5. 所有业务处理完毕");
System.Threading.Thread.Sleep();
}
}
}
TestService的while循环里面,在周期轮训的开头,加上一个IsCancellationRequested的判断,如果Cancel()方法被调用,IsCancellationRequested就会变
成true,从而让本方法感知到外界让我结束,所以本逻辑就不再进行下一个周期了,从而保证业务逻辑的完整。
五:部署
为了更好的表达效果,我加了很多的日志,还是采用nohup的模式来观察一下程序的流转过程。
[root@localhost netcore]# nohup dotnet ConsoleApp1.dll &
[]
[root@localhost netcore]# nohup: ignoring input and appending output to ‘nohup.out’ [root@localhost netcore]# ps -ef | grep dotnet
root : pts/ :: dotnet ConsoleApp1.dll
root : pts/ :: grep --color=auto dotnet
[]- Done nohup dotnet ConsoleApp1.dll
[root@localhost netcore]# kill
[root@localhost netcore]# tail - nohup.out
// :: PM: . 获取mysql
// :: PM 后端服务程序正常启动!
// :: PM: . 获取redis
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM: . 获取mysql
// :: PM: . 获取redis
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM: . 获取mysql
// :: PM: . 获取redis
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM: . 获取mysql
// :: PM: . 获取redis
// :: PM 后台测试服务,准备进行资源清理!
// :: PM: . 更新monogdb
// :: PM: . 通知kafka
// :: PM: . 所有业务处理完毕
// :: PM 恭喜,Test服务程序已正常退出!
大家可以清楚的看到,5:11:49 收到了system给过来的kill通知,但是程序还是等到了5:11:57才真正的结束自己,这样是不是就保证了业务流程免遭破坏呢?
好了,本篇就说到这里,希望对你有帮助。
netcore服务程序暴力退出导致的业务数据不一致的一种解决方案(优雅退出)的更多相关文章
- Phoenix数据覆盖的一种解决方案
最近在做实时数仓,需要兼顾离线和实时两种查询方式,大致的方案是数据通过binlog抽取,经Phoenix插入,hive映射hbase表:Phoenix创建索引,实时查询Phoenix:离线查询hive ...
- 大型DELETE(删除大量数据)的一种解决方案
通过执行单条DELETE语句来删除一个大型的数据集会有以下的缺点: 1.DELETE语句的操作要被完整地记录到日志中,这要求在事务日志中要有足够的空间以完成整个事务: 2.在删除操作期间(可能会花费很 ...
- 解决Redis中数据不一致问题
redis系列之数据库与缓存数据一致性解决方案 数据库与缓存读写模式策略写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以马 ...
- 演示stop暴力停止线程导致数据不一致的问题,但是有些有趣的发现 (2017-07-03 21:25)
如注释所言 /** * Created by weiwei22 on 17/7/3. * * 这里主要是为了演示stop导致的数据不一致的问题.stop会暴力的结束线程并释放锁,所以有可能在恰好写了一 ...
- pt-osc改表导致数据不一致案例分析
2016-06-10 李丹 dba流浪猫 我们平时除了解决自己问题外,有时候也会协助圈内人士,进行一些故障排查,此案例就是帮某公司DBA进行的故障分析,因为比较典型,特分享一下,但仅仅是分享发生的过程 ...
- Redis面试题记录--缓存双写情况下导致数据不一致问题
转载自:https://blog.csdn.net/lzhcoder/article/details/79469123 https://blog.csdn.net/u013374645/article ...
- 由数据迁移至MongoDB导致的数据不一致问题及解决方案
故事背景 企业现状 2019年年初,我接到了一个神秘电话,电话那头竟然准确的说出了我的昵称:上海小胖. 我想这事情不简单,就回了句:您好,我是小胖,请问您是? "我就是刚刚加了你微信的 xx ...
- P2P小贷网站业务数据流程分享
P2P小贷网站业务数据流程分享 引言 这是去年年底开发的一个项目,完成后和用户的衔接没有很好的做起来,所以项目就搁浅了.9月以来,看各路P2P风声水起,很是热闹:这里分享下我的设计文档,算是抛砖引玉, ...
- 基于netcore实现mongodb和ElasticSearch之间的数据实时同步的工具(Mongo2Es)
基于netcore实现mongodb和ElasticSearch之间的数据实时同步的工具 支持一对一,一对多,多对一和多对多的数据传输方式. 一对一 - 一个mongodb的collection对应一 ...
随机推荐
- python_code list_3
>>> seq=['foo','x41','?','***']>>> def func(x): return x.isalnum() >>> li ...
- IBM x3850 RAID5数据恢复过程
[raid数据恢复故障描述] 需要进行数据恢复的是北京一家公司的IBM X3850服务器,服务器挂载了5块73G SAS硬盘组成raid5磁盘阵列,4号盘为热备盘(Hot-Spare),由于未知 ...
- Spring Boot实战笔记(六)-- Spring高级话题(多线程)
一.多线程 Springt通过任务执行器(TaskExecutor)来实现多线程和并发编程.使用ThreadPoolTaskExecutor可实现一个基于线程池的TaskExecutor.而实际开发中 ...
- git merge 撤销与git 回滚
git merge提交后恢复 http://blog.psjay.com/posts/git-revert-merge-commit/ git回滚 https://www.jianshu.com/p/ ...
- 基于Kafka Connect框架DataPipeline可以更好地解决哪些企业数据集成难题?
DataPipeline已经完成了很多优化和提升工作,可以很好地解决当前企业数据集成面临的很多核心难题. 1. 任务的独立性与全局性. 从Kafka设计之初,就遵从从源端到目的的解耦性.下游可以有很多 ...
- Linux内核调试方法
内核配置选项中要使能CONFIG_MAGIC_SYSRQ选项,这样系统启动之后,会生成/proc/sysrq-trigger节点用于调试. 其次,可以在/etc/sysctl.conf中设置kerne ...
- Mysql服务启动与关闭
启动: 在cmd中输入 net start mysql 关闭: 在cmd中输入 net stop mysql
- SSM-MyBatis-17:Mybatis中一级缓存(主要是一级缓存存在性的证明,增删改对一级缓存会造成什么影响)
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 缓存------------------------------------------> 很熟悉的一个 ...
- 关于maven的配置使用 这一篇还比较全 2017.12.13
https://www.cnblogs.com/tangshengwei/p/6341462.html
- Spider与OpenPyXL的结合
OpenPyXL的API文档 1.OpenPyXL基础操作 引入Workbook这个类,然后调用 from openpyxl import Workbook wb = Workbook() 通过ope ...