转载自1:https://www.cnblogs.com/atree/p/Concurrency_Async.html
转载自2:https://blog.csdn.net/mss359681091/article/details/52056105
并发(英文Concurrency),其实是一个很泛的概念,字面意思就是“同时做多件事”,不过方式有所不同。在.NET的世界里面,处理高并发大致有以下几种方法:
异步编程就是使用future模式(又称promise)或者回调机制来实现(Non-blocking on waiting)。如果使用回调或事件来实现(容易callback hell),不仅编写这样的代码不直观,很快就容易把代码搞得一团糟。
不过在.NET 4.5 及以上框架中引入的async/await关键字(在.NET 4.0中通过添加Microsoft.Bcl.Async包也可以使用),让编写异步代码变得容易和优雅。通过使用async/await关键字,可以像写同步代码那样编写异步代码,所有的回调和事件处理都交给编译器和运行时帮你处理了,简单好用。
使用异步编程有两个好处:不阻塞主线程(比如UI线程),提高服务端应用的吞吐量。所以微软推荐ASP.NET中默认使用异步来处理请求。
例如:我用异步做微信模板消息推送。
///
/// 使用异步Action测试异步模板消息接口
///
///
///
public async Task TemplateMessageAsync(string openId, string first, string keyword1, string keyword2, string keyword3, string keyword4, string remark, string url)
{
if (openId == null)
{
return ReturnString(7771, "OPENID不能为空");
}
else
{
var testData = new //TestTemplateData()
{
first = new TemplateDataItem(first),
keyword1 = new TemplateDataItem(keyword1),
keyword2 = new TemplateDataItem(keyword2),
keyword3 = new TemplateDataItem(keyword3),
keyword4 = new TemplateDataItem(keyword4),
remark = new TemplateDataItem(remark)
};
var result = await TemplateApi.SendTemplateMessageAsync(_wechat.APPID, openId, "m6td4jp_heMA5rhopbUaHApOlp2DD5x18BMXWKj3M5U", url, testData);
return ReturnString(0, "成功");
}
}
并行编程的出现实际上是随着CPU有多核而兴起的,目的是充分利用多核CPU的计算能力。并行编程由于会提高CPU的利用率,更适合客户端的一些应用,对于服务端的应用可能会造成负面影响(因为服务器本身就具有并行处理的特点,比如IIS会并行的处理多个请求)。我自己使用并行编程最多的场景是之前分析环境数据不确定度的时候,使用并行的方式计算蒙特卡洛模拟(计算上千次之后拟合),当然后来我使用泰勒级数展开来计算不确定度,没有这么多的计算量就无需并行了。当然在计算多方案结果比较的情况下,还是继续使用了并发计算。
在.NET中,并行的支持主要靠.NET 4.0引入的任务并行库和并行LINQ。通过这些库可以实现数据并行处理(处理方式相同,输入数据不同,比如我上面提到的应用场景)或者任务并行处理(处理方式不同,且数据隔离)。通过使用并行处理库,你不用关心Task的创建和管理(当然更不用说底层的线程了),只需要关注处理任务本身就行了。
具体的用法还是参考官方文档:https://msdn.microsoft.com/en-us/library/dd460693(v=vs.110).aspx
响应式编程最近成为了一个Buzzword,其实微软6年前就开始给.NET提供一个Reactive Extensions了。一开始要理解响应式编程有点困难,但是一旦理解了,你就会对它的强大功能爱不释手。简单来说,响应式编程把事件流看作数据流,不过数据流是从IEnumable中拉取的,而事件流是从IObservable推送给你的。为什么响应式编程可以实现并发呢?这是因为Rx做到线程不可知,每次事件触发,后续的处理会从线程池中任意取出一个线程来处理。且可以对事件设置窗口期和限流。举个例子,你可以用Rx来让搜索文本框进行延迟处理(而不用类似我很早的时候用个定时器来延迟了)。
要详细了解Rx最好的方式就是浏览 IntroToRx.com 这个网站,当然还有官方文档:https://msdn.microsoft.com/en-us/data/gg577609。
数据流(DataFlow)编程可能大家就更陌生了,不过还是有些常用场景可以使用数据流来解决。数据流其实是在任务并行库(TPL)上衍生出来的一套处理数据的扩展(也结合了异步的特性),TPL也是处理并行编程中任务并行和数据并行的基础库。
望文生义,TPL DataFlow就是对数据进行一连串处理,首先为这样的处理定义一套网格(mesh),网格中可以定义分叉(fork)、连接(join)、循环(loop)。数据流入这样的处理网格就能够并行的被处理。你可以认为网格是一种升级版的管道,实际上很多时候就是被当作管道来使用。使用场景可以是“分析文本文件中词频”,也可以是“处理生产者/消费者问题”。
参考资料当然也是官方文档:https://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx。
Scala有Akka,其实微软研究院也推出了Orleans来支持了Actor模型的实现,当然也有Akka.NET可用。Orleans设计的目标是为了方便程序员开发需要大规模扩展的云服务, 可用于实现DDD+EventSourcing/CQRS系统。
官方网站是:http://dotnet.github.io/orleans/
最近几天一直在读代震军的博客,他是Discuz!NT的设计者,读了他的一系列关于Discuz!NT的架构设计文章,大呼过瘾,特别是Discuz!NT在解决高访问高并发时所设计的一系列方案,本人尤其感兴趣。写这篇文章的目的,算是对初次阅读之后的总结备忘吧,以便以后有时间亲自测试,如果能在生产环境中得到应用,那就更有参考价值了。
测试方法:
本地模拟测试网站高访问高并发采用的测试工具是大名鼎鼎的Loadrunner,这个工具做测试的一般都知道。在代震军的博客中,有以下几篇介绍了通过Loadrunner进行压力并发测试。
当DiscuzNT遇上了Loadrunner(上)
http://www.cnblogs.com/daizhj/archive/2009/09/25/1573926.html
当DiscuzNT遇上了Loadrunner(中)
http://www.cnblogs.com/daizhj/archive/2009/09/27/1574897.html
当DiscuzNT遇上了Loadrunner(下)
http://www.cnblogs.com/daizhj/archive/2009/09/27/1575091.html
Discuz!NT是一个论坛程序,是典型的互联网应用,在设计时本身就考虑了互联网应用场景下高并发高访问量的需求,在普通开源版本中,主要采用的缓存机制来提高系统的性能。
相关文章如下:
Discuz!NT 缓存设计简析 [原创]
http://www.cnblogs.com/daizhj/archive/2007/08/15/855163.html
这个缓存机制使用的是.Net本身提供的缓存功能,System.Web.Caching.Cache
这个方案可以解决一般访问量不是很大的站点的需求,更高一级的,可以通过增加Web园工作进程来达到提升性能的需求,而且这个方案里面,已经解决多进程下缓存同步的问题。
在Discuz!NT企业版中,提供了更高层次的解决方案,使用了分布式缓存机制,引入了Memcached、Redis、LLServer,相关文章如下:
Discuz!NT中集成Memcached分布式缓存
http://www.cnblogs.com/daizhj/archive/2009/03/23/1386652.html
在Discuz!NT中进行缓存分层(本地缓存+memcached)
http://www.cnblogs.com/daizhj/archive/2009/11/17/1604436.html
Discuz!NT中的Redis架构设计
http://www.cnblogs.com/daizhj/archive/2011/02/21/1959511.html
Discuz!NT跨站缓存同步
http://www.cnblogs.com/daizhj/archive/2010/06/18/discuznt_memcache_syncdata.html
Discuz!NT中的LLServer架构设计
http://www.cnblogs.com/daizhj/archive/2011/08/26/discuznt_llserver_arch.html
Memcached是danga.com(运营LiveJournal的技术团队)开发的一套分布式内存对象缓存系统,用于在动态系统中减少数据库负载,提升性能。具体的介绍可以参考:
Memcached深度分析
http://www.cnblogs.com/luluping/archive/2009/01/14/1375456.html
通过以上的方案,能解决大部分高访问高并发的需求,因为论坛产品的特殊性,读写比大概是4:1,所以首先应该在读数据方面进行减压优化。
Discuz!NT在缓解Web服务器压力上采用了如下的方案。
将用户上传的附件通过FTP方式传送到另外一台服务器上,相关的文章如下:
Discuz!NT中远程附件的功能实现[FTP协议]
http://www.cnblogs.com/daizhj/archive/2008/07/28/1254648.html
Discuz!NT静态文件缓存(SQUID)解决方案
http://www.cnblogs.com/daizhj/archive/2010/06/10/1692758.html
通过以上的方案,Web服务器压力小了,性能也提升了,但是如果遇到更高的并发访问量,单台Web服务器还是不能满足需求,Discuz!NT采取了负载均衡的方案。使用了LVS+KEEPALIVED、NGINX等。相关文章如下:
Discuz!NT负载均衡解决方案(HA)之—LVS(Linux Virtual Server)
http://www.cnblogs.com/daizhj/archive/2010/06/13/1693673.html
Discuz!NT负载均衡解决方案(HA)之—LVS(Linux Virtual Server)
http://www.cnblogs.com/daizhj/archive/2010/06/13/1693673.html
Discuz!NT负载均衡方案
http://www.cnblogs.com/daizhj/archive/2010/06/24/1667422.html
使用的是nginx,使用nginx作为前端负载均衡,这个确实很有吸引力,有时间能试用下就好。
在Discuz!NT中,数据库作为数据持久化工具,必定在并发访问频繁且负载压力较大的情况下成为系统性能的‘瓶颈’。即使使用上面的本地缓存等方式来解决频繁访问数据库的问题,但仍旧会有大量的并发请求要访问动态数据, 其中的‘读写分离’方案就是一种被广泛采用的方案。相关文章:
Discuz!NT数据库读写分离方案
http://www.cnblogs.com/daizhj/archive/2010/06/21/dbsnap_master_slave_database.html
全文搜索方案:
Discuz!NT企业版之Sphinx全文搜索(上)
http://www.cnblogs.com/daizhj/archive/2010/06/28/discuznt_entlib_sphinx_one.html
Discuz!NT企业版之Sphinx全文搜索(下)
http://www.cnblogs.com/daizhj/archive/2010/06/30/discuznt_entlib_sphinx_two.html
处理大数据量:
Discuz!NT千万级数据量上的两驾马车–TokyoCabinet,MongoDB
http://www.cnblogs.com/daizhj/archive/2010/07/22/1781140.html
好了,上面就是Discuz!NT企业版为了提升性能采取的一系列方案,确实对asp.net互联网应用很有参考价值,其中用到的很多开源产品都是基于Linux的,如Memcached、Redis、LLServer、SQUID、NGINX、LVS、Sphinx,虽然有些产品有Windows版本,但是其性能表现能力远远比不上Linux上面,看来在Web应用上,性能方面的表现以及开源产品的研究,Linux远远的走在了Windows前面。