一: 问题提出

  现如今大家写的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服务程序暴力退出导致的业务数据不一致的一种解决方案(优雅退出)的更多相关文章

  1. Phoenix数据覆盖的一种解决方案

    最近在做实时数仓,需要兼顾离线和实时两种查询方式,大致的方案是数据通过binlog抽取,经Phoenix插入,hive映射hbase表:Phoenix创建索引,实时查询Phoenix:离线查询hive ...

  2. 大型DELETE(删除大量数据)的一种解决方案

    通过执行单条DELETE语句来删除一个大型的数据集会有以下的缺点: 1.DELETE语句的操作要被完整地记录到日志中,这要求在事务日志中要有足够的空间以完成整个事务: 2.在删除操作期间(可能会花费很 ...

  3. 解决Redis中数据不一致问题

    redis系列之数据库与缓存数据一致性解决方案 数据库与缓存读写模式策略写完数据库后是否需要马上更新缓存还是直接删除缓存? (1).如果写数据库的值与更新到缓存值是一样的,不需要经过任何的计算,可以马 ...

  4. 演示stop暴力停止线程导致数据不一致的问题,但是有些有趣的发现 (2017-07-03 21:25)

    如注释所言 /** * Created by weiwei22 on 17/7/3. * * 这里主要是为了演示stop导致的数据不一致的问题.stop会暴力的结束线程并释放锁,所以有可能在恰好写了一 ...

  5. pt-osc改表导致数据不一致案例分析

    2016-06-10 李丹 dba流浪猫 我们平时除了解决自己问题外,有时候也会协助圈内人士,进行一些故障排查,此案例就是帮某公司DBA进行的故障分析,因为比较典型,特分享一下,但仅仅是分享发生的过程 ...

  6. Redis面试题记录--缓存双写情况下导致数据不一致问题

    转载自:https://blog.csdn.net/lzhcoder/article/details/79469123 https://blog.csdn.net/u013374645/article ...

  7. 由数据迁移至MongoDB导致的数据不一致问题及解决方案

    故事背景 企业现状 2019年年初,我接到了一个神秘电话,电话那头竟然准确的说出了我的昵称:上海小胖. 我想这事情不简单,就回了句:您好,我是小胖,请问您是? "我就是刚刚加了你微信的 xx ...

  8. P2P小贷网站业务数据流程分享

    P2P小贷网站业务数据流程分享 引言 这是去年年底开发的一个项目,完成后和用户的衔接没有很好的做起来,所以项目就搁浅了.9月以来,看各路P2P风声水起,很是热闹:这里分享下我的设计文档,算是抛砖引玉, ...

  9. 基于netcore实现mongodb和ElasticSearch之间的数据实时同步的工具(Mongo2Es)

    基于netcore实现mongodb和ElasticSearch之间的数据实时同步的工具 支持一对一,一对多,多对一和多对多的数据传输方式. 一对一 - 一个mongodb的collection对应一 ...

随机推荐

  1. vim编辑器常见命令归纳大全

    Esc:命令行模式 i:插入命令 a:附加命令 o:打开命令 c:修改命令 r:取代命令 s:替换命令 以上进入文本输入模式   : 进入末行模式 末行模式: w:保存 q:退出,没保存则无法退出 w ...

  2. 微信小程序 bug及解决方案

    1.小程序遮罩滚动穿透问题 解决方案: <view class="mask" wx:if="{{showVipRights}}" catchtap='hi ...

  3. 微信二维码支付-模式一(PC端,解决中文乱码问题)

    近期公司调完银联,调支付宝,调完支付宝调微信.说实话微信的帮助文档确实是烂,而且有没有技术支持,害的我头发都掉了一桌.不说废话了,看代码. 首先登陆微信的公众平台(微信的服务号不是订阅号),然后选择微 ...

  4. spring的7个模块

    Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的.框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架. Spring ...

  5. backbone的一些认识

    body,td { font-family: 微软雅黑; font-size: 10pt } 官网:http://backbonejs.org/ 作者:Jeremy Ashkenas 杰里米·阿什肯纳 ...

  6. windows server 2012 R2汉化 -- 玩转Microsoft Azure

    Microsoft Azure 试用版小试牛刀 首先需要申请一个账号获得试用权 我这里是1元免费试用, 进入后就可以创建自己的虚拟机及数据库 在这里先说创建的windows server 2012 R ...

  7. Python字符编码与转码

    字符编码与转码 注: 该图转自 http://www.cnblogs.com/luotianshuai/p/5735051.html. Python2 解码,编码 #Python2 #-*- codi ...

  8. TestNG exception

    以下内容引自: https://howtodoinjava.com/testng/testng-expected-exception-and-expected-message-tutorial/ Ho ...

  9. java编程思想-第六章-某些练习题

    参考https://blog.csdn.net/caroline_wendy/article/details/47271037 3 package debug; import java.util.Ar ...

  10. 如何在苹果笔记本上装win7系统

    有一哥们,他说他boss给他配了台苹果,可是很不习惯,让我给装一个win系统.以下是我从百度借鉴的: 步骤一 先使用Boot Camp 分割磁盘   1 在Finder工具条中点选"前往&q ...