3月23日(周日)下午16:30左右,博客园主站负载均衡中的2台Web服务器CPU玩起了爬楼梯的游戏(见上图),一直爬到了接近100%。发现这个状况后,我们立即将这2台阿里云临时磁盘云服务器从负载均衡中摘下来,挂上1台云盘云服务器,恢复了正常。

由于曾经多次遇到过阿里云云服务器CPU问题,现在对阿里云云服务器产生了一种偏见,只要出现CPU问题,就会首先怀疑云服务器的问题。而这次出现问题时,换上云盘云服务器立即恢复正常,我们就坚定地认为临时磁盘云服务器存在某种问题。于是,我们提交了工单,向阿里云客服抱怨这个问题。

。。。

接着突然发生的状况让我们的“坚定”产生了动摇,刚加上去的那台云盘云服务器也出现了CPU跑高的问题。

阿里云云服务器连续出问题的可能性很小,也许是其他原因引起的。这个突发情况让我们冷静下来去回想出问题之前进行过什么操作。

想起来了——

出现问题之前,我们进行过清空OCS实例缓存的操作(注:OCS是阿里云提供的Memcached缓存服务)。

缓存不仅能缓解数据库的压力,而且能缓解CPU的压力。比如有些数据从数据库中读取出来后需要进行一些正则表达式的处理(耗CPU的大户),如果缓存中存在,直接读取就行;如果缓存中不存在,需要先从数据库中读取,接着进行正则处理,然后放入缓存。清空缓存后会引发大量这样的操作,从而给CPU带来压力。

但是以前我们多次在周末访问低峰的时候进行过同样的清空OCS缓存的操作,增加的这点压力对Web服务器的CPU来说是小菜一碟。

为什么这次却有天壤之别?

  • 这个周末的访问量的确比之前的周末要高一些,但不致于影响这么大。
  • 在CPU跑高时,日志中记录了很多OCS缓存客户端读取数据慢的情况。难道是OCS的问题?是OCS读取缓存慢引发CPU高,还是CPU高引发OCS缓存读取速度慢?分析之后,还是觉得后者的可能性大一些。
  • 现在与之前相比,哪些变化可能引发在缓存失效的情况下需要更多的CPU消耗?

想起来了——Markdown!

1月份的时候我们发布了简陋的Markdown功能,现在比以前有了更多Markdown写的博文,而这些博文转换成HTML用了复杂的正则表达式。当访问一篇使用Mardown写的博文时,如果缓存中没有,会从数据库读取原始的Markdown内容,用正则表达式转换成HTML后放入缓存,后续的访问就直接从缓存中读取HMTL内容。当清空OCS缓存后,大量的Markdown内容需要重建缓存,进行大量的复杂的正则表达式处理,这会给CPU带来很大的压力!

这是就是问题的真相?难道是我们自己导演的缓存雪崩?。。。没这么简单!在访问低峰,共16个核的CPU竟然都没有撑住,不可思议!凭我们的经验,这16个核没这么弱不禁风!

继续回想。。。

又想起来了!我们曾经实际在另外一个ASP.NET应用程序中遇到过类似的情况——

在C#中用正则表达式处理大文本时,某种条件会触发CPU高上去,而且会一直高居不下,只有回收应用程序池才能让CPU下去。当时怎么优化正则表达式也没有用,后来没办法,使用磁盘文件进行大量缓存,减少了触发这个问题的几率。

难道.NET在正则表达式处理上隐藏着不为人所知的坑?微软从.NET Framework 4.5开始给正则表达式增加了超时设置(matchTimeout),似乎验证了这一点。

//   matchTimeout:
// A time-out interval, or System.Text.RegularExpressions.Regex.InfiniteMatchTimeout
// to indicate that the method should not time out.
public Regex(string pattern, RegexOptions options, TimeSpan matchTimeout);

虽然我们的应用程序已经升级到了.NET  Framework 4.5,但是还没有去使用这个特性,现在实际遇到的问题将之呼唤出来。

解决方法一:给Markdow转换所用的所有正则表达式加上超时设置——TimeSpan.FromSeconds(1),如果某个正则表达式处理超过1秒就会引发异常,从而不让任何一只老鼠坏了一碗汤。

示例代码如下:

private static Regex _newlinesLeadingTrailing =
new Regex(@"^\n+|\n+\z",
RegexOptions.Compiled,
TimeSpan.FromSeconds());

但是,这样一个一个正则表达式进行修改,好麻烦!

于是有了“解决方法一”的改进版:

在Global.asax.cs中Application_Start添加如下的代码:

protected void Application_Start(object sender, EventArgs e)
{
AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT", TimeSpan.FromSeconds());
}

这样就可以全局设置所有正则表达式的默认超时时间。

采用了解决方法一之后,我们又仔细考虑了一下,学得这不是最终解决方案。解决方法一虽然解决了一只老鼠坏一锅汤的问题,但是假如一百只、一千只老鼠接连出现呢?也会给CPU带来压力,这种压力会影响主站对其他请求的响应速度。

更好的解决方法应该是——不管Markdown的正则表达式处理消耗多少CPU,即使把CPU跑爆了,也不要影响主站。所以,将这部分处理分出去,隔离开来,才是最终解决方法。

最终解决方法

将Markdown的正则表达式处理放在独立的站点、独立的服务器,然后在博客程序中需要处理Markdown的时候,将文本内容post给这个独立站点进行处理。

之前在博客程序中是这样处理Markdown的:

if (entry.IsMarkdown)
{
body = new MarkdownSharp.Markdown().Transform(body);
}

现在用了一台单独的云服务器跑ASP.NET MVC程序进行Markdown处理,MVC代码如下:

public class MarkdownController : Controller
{
[HttpPost]
public ActionResult Transform()
{
using (var reader = new StreamReader(Request.InputStream))
{
var bodyText = reader.ReadToEnd();
return Content(new MarkdownSharp.Markdown().Transform(bodyText));
}
}
}

上面的代码中,为了减少MVC的处理工作,直接从http post body中获取Markdown文本。

然后博客程序中用HttpClient将Markdown文本post给这个独立MVC站点进行处理。示例代码如下:

if (entry.IsMarkdown)
{
var httpClient = new HttpClient();
var httpContent = new StringContent(body);
var response = httpClient.PostAsync("http://markdown.s.cnblogs.com/markdown/transform", httpContent).Result;
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
body = response.Content.ReadAsStringAsync().Result;
}
else
{
body = new MarkdownSharp.Markdown().Transform(body);
}
}

上面的代码也考虑了一定的容错,假如处理Markdown的站点down掉了,博客程序会暂时辛苦一下,自己进行Markdown的正则处理。

这个最终解决方案已经实际部署到我们的主站(www.cnblogs.com)中。

C#正则表达式引发的CPU跑高问题以及解决方法的更多相关文章

  1. 实际遭遇GC回收造成的Web服务器CPU跑高

    今天下午有段时间访问园子感觉不如以前那么快的流畅,上Web服务器一看,果然,负载均衡中的1台云服务器CPU跑高. 上图中红色曲线表示的是CPU占用率.正常情况下,CPU占用率一般在40%以下. 这台云 ...

  2. ADF控件ID变化引发JS无法定位控件的解决方法

    原文地址:ADF控件ID变化引发JS无法定位控件的解决方法作者:Nicholas JSFF定义的控件ID到了客户端时往往会改变.例如在JSFF中的一个的ID为"ot1",但是当这个 ...

  3. IntelliJ IDEA编辑文件的时候CPU飙高问题的解决

    原文地址:https://www.javatang.com/archives/2018/04/26/25582403.html 上篇文章中说明了解决IntelliJ IDEA中文输入法无提示的问题,最 ...

  4. C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析与解决方法

    对于C#中异常:“The type initializer to throw an exception(类型初始值设定项引发异常)”的简单分析,目前本人分析两种情况,如下: 情况一: 借鉴麒麟.NET ...

  5. 正则表达式回溯-导致CPU偏高

    最近了解了下有关正则表达式回溯的内容,想想就写下来,方便自己. 正则表达式匹配算法是建立在正则表达式引擎的基础上的,目前有两种引擎:DFA(确定型有穷自动机)和NFA(不确定型有穷自动机).这两种引擎 ...

  6. PHP 网站大流量与高并发的解决方法

    php 网站如何应对大流量与高并发呢? 首先,确认服务器硬件是否足够支持当前的流量. 普通的P4服务器一般最多能支持每天10万地理IP,如果访问量比这个还要大,则请配置一台更高性能的专用服务器. 否则 ...

  7. IOS中input光标跑偏问题的解决方法

    ios端兼容input光标高度处理 在最近的项目中遇到一个问题,input输入框光标,在安卓手机上显示没有问题,但是在苹果手机上 当点击输入的时候,光标的高度和父盒子的高度一样.造成的原因就是给父盒子 ...

  8. 使用BasicDataSource引发的数据库连接中断的问题和解决方法

    http://blog.csdn.net/itbasketplayer/article/details/44198963 http://blog.sina.com.cn/s/blog_9e3e5499 ...

  9. Java进程占用内存过高,排查解决方法

    最近收到邮件报警,说内存使作率达到84%.如下图: 解决方法: A:可能是代码原因导致的问题: 1.使用命令:top 查看当前进程的状态 2.从上图可以看到PID:916的java进程占用内存较大.定 ...

随机推荐

  1. 谁动了我的I/O?

    首先,是信用卡账单欠款0.13美刀~~~然后上亚马逊云查了一下账单. 3M次I/O...(1215133次超额的,2000000次免费的.) 于是监控了一下数据:每秒至少写5次,每秒写300KB,平均 ...

  2. 运用Hadoop能否搭建完整的云计算平台?

    Apache Hadoop 是一个用java语言实现的软件框架,在由大量计算机组成的集群中运行海量数据的分布式计算,它可以让应用程序支持上千个节点和PB级别的数据. Hadoop并不完全代表云计算,所 ...

  3. stixel-world代码解读

    下边缘的求法应该是使用的第二篇论文的方法 上边缘的求法应该是使用的第一篇论文的方法 这是求上边缘的代码: std::vector<float> integralMembership(vma ...

  4. Intel MKL 多线程设置

    对于多核程序,多线程对于程序的性能至关重要. 下面,我们将对Intel MKL 有关多线程方面的设置做一些介绍: 我们提到MKL 支持多线程,它包括的两个概念:1>MKL 是线程安全的: MKL ...

  5. java重定向与请求转发

    重定向是不能直接访问WEB-INF下的资源的,因为重定向是浏览器二次请求,众所周知,客户端是不能直接访问WEB-INF下的资源的. 而请求转发却可以直接访问. 然而重定向却可以间接访问WEN-INF下 ...

  6. iPad游戏 Calcculator: The Game 程序自动计算求解方法

    今天在iPad上下了个小游戏,主要是一个计算器的界面,有开始值,目标值,限定步数,以及一些加减乘除,还有作者脑洞想出来的功能键,主要有左移,直接把一个数加到末尾,将其中的某个数改为另一个数等等..玩到 ...

  7. css英文长文字会自动换行的解决办法

    iPhone5中显示的效果如下图: 代码修改: 修改后效果如图:

  8. Ajax (Asynchronous javascript xml) 搜索框核心代码(JQuery) Ajax判断用户名存在核心代码 附:原生js的Ajax代码 其中有json的一句话解释

    前端 <script type="text/javascript"> $(function(){ $("#tid").keyup(function( ...

  9. C++使用GDI+实现图片格式转换

    主要是我在设置壁纸时遇到的个小问题,因为设置壁纸只能是bmp格式的图片,不可能我喜欢的壁纸就都是bmp格式的,就想怎么转换一下图片的格式,于是就在百度搜怎么弄,搜到了可行方法,却没有实现代码,有些看起 ...

  10. C#进阶学习笔记(个人整理)

    学习笔记 第一章: 一.回顾数组 1.变量 : 只能存储一个数据 2.数组 :存储固定大小的相同类型的数据 3.对象 : 存储多个相同/不同类型的数据 4.集合 : 特殊的容器,存储N个相同/不同类型 ...