个人网站传音石之前的版本是在页面打开的时候,才去读取RSS源,以致于第一次访问的时候速度很慢。虽然RSS Toolkit也有自己的缓存机制,但是整体的运行速度还是很慢,而且并不利于一些有价值的新闻内容进行保存。所以考虑了定时多线程读取RSS源的方法。
个人网站http://www.massany.cn/之前的版本是在页面打开的时候,才去读取RSS源,以致于第一次访问的时候速度很慢。虽然RSS Toolkit也有自己的缓存机制,但是整体的运行速度还是很慢,而且并不利于一些有价值的新闻内容进行保存。所以考虑了定时多线程读取RSS源的方法。
在Global.aspx的Application_Start里,设置了一个定时器,定时读取RSS源。这里的关键是读取RSS源的多线程处理问题,一开始使用了Thread类来启动多个线程,每个线程都访问一个公共的Queue对象来分配读取任务。其实在C#里还有更简单的方法就可以完成多线程操作,使用线程池ThreadPool类。使用ThreadPool可以不必再关注线程的线节,只需要提供回调方法,线程的调度由线程池来完成。
取数据源的代码很简单。
DataSet ds = new DataSet();
string strSql = "select DefaultState,ID from Widget where Url = 'Widgets/RssWidget.ascx'";
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
//出错的频道暂时不进行处理。
if (_ErrQueue.Count > 0)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
strSql += " and (";
foreach (DataRow dr in _ErrQueue)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
strSql += " id <> " + dr["id"] + " or ";
}
strSql += " 1=0 )";
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
SqlHelper.FillDataset(SqlHelper.ConnectionString, CommandType.Text, strSql, ds, null);
下面将回调方法加入线程池队列:
foreach (DataRow dr in ds.Tables[0].Rows)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
//_Queue.Enqueue(dr);
ThreadPool.QueueUserWorkItem(new WaitCallback(CallBack), dr);
}
回调方法CallBack,要特别注意这个lock,之前的代码并没有这个lock,导致了生插入到数据库中的频道内容顺序是混乱的,有了lock后,保证了插入的记录是按频道来排序的。
lock (_Lock)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
GenericRssChannel channel = GenericRssChannel.LoadChannel(GetUrl(dr["DefaultState"] as string));
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
foreach (GenericRssElement item in channel.Items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
object objResult = SqlHelper.ExecuteScalar(SqlHelper.ConnectionString, CommandType.Text, string.Format("select count(*) from RssChannel where WidgetId = {0} and link = '{1}'", dr["id"], item.Attributes["link"]));
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (objResult == null ||
Convert.ToInt32(objResult) < 1)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
string strPubDate = "";
DateTime dtPubDate;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (!item.Attributes.TryGetValue("pubDate", out strPubDate))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
dtPubDate = Convert.ToDateTime(strPubDate);
}
else
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
dtPubDate = DateTime.Now;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
string strSql = null;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
//SqliteHelper.ExecuteNonQuery(string.Format(_DeleteSql, item.Attributes["link"]));
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
string strCatetory = "";
string strDescription = "";
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (!item.Attributes.TryGetValue("category", out strCatetory))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
strCatetory = "";
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
if (!item.Attributes.TryGetValue("description", out strDescription))
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
strDescription = "";
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
strSql = string.Format(_InsertSql, item.Attributes["title"].Replace("'", "\"")
, dr["id"]
, item.Attributes["link"]
, strDescription.Replace("'", "\"")
, strCatetory
, dtPubDate);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
SqlHelper.ExecuteNonQuery(SqlHelper.ConnectionString, CommandType.Text, strSql);
}
}
}
catch (System.Xml.XmlException)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_ErrQueue.Enqueue(dr);
}
catch (System.Net.WebException)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_ErrQueue.Enqueue(dr);
}
catch (Exception ex)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
log.Error(ex.Message, ex);
_ErrQueue.Enqueue(dr);
}
}
再启动一批线程来处理出错的频道:
for (int i = 0; i < _ErrQueue.Count; i++)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ErrorCallBack),_ErrQueue.Dequeue());
}
错误处理回调方法:
static void ErrorCallBack(Object stateInfo)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
LoadXML(stateInfo as DataRow);
Thread.Sleep(1000);
}
看看,是简单吧!对于多线程和RSS频道缓存,这是目前我能想到的解决办法,如果大家有更好的主意,不妨交流一下。
我的联系方式:
e-mail:jinyongjinyong@gmail.com
QQ:24615289