文章点击数更新

对文章单击数的更新,考虑到并发的问题。

这里是通过队列的方式来进行解决。

1、模型修改

1
2
3
4
5
6
7
8
9
10
11
public int ArticelClassId { get; set; }

/// <summary>
/// ----------------------文章被单击浏览的次数
/// </summary>
public int ACount { get; set; }
/// <summary>
/// 文章属于哪个类别
/// </summary>
///
public ArticelClass? ArticelClass { get; set; }

修改ArticelInfo模型。

同时对该模型的配置做一个简单的修改ArticelInfoConfig.cs

1
2
3
builder.Property(x => x.Origin).HasMaxLength(100).IsRequired();
builder.Property(x => x.PhotoUrl).HasMaxLength(500).IsRequired();
builder.Property(x => x.ACount).HasDefaultValue(0);//-------------指定了默认值

重新执行数据迁移的操作

2、创建队列

Cms.Common项目中创建静态类QueueHelper.cs

该类中的代码如下: 分布式队列(Redis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace Cms.Common
{
public static class QueueHelper
{
private static ConcurrentQueue<int> _likeQueue = new ConcurrentQueue<int>();

/// <summary>
/// 入队
/// </summary>
/// <param name="articelId"></param>
public static void EnqueueLikeOperation(int articelId)
{
_likeQueue.Enqueue(articelId);
}
/// <summary>
/// 出队
/// </summary>
/// <param name="articelId"></param>

public static bool DeEnquenuLikeOperation(out int articelId)
{
return _likeQueue.TryDequeue(out articelId!);
}
}
}

3、入队操作

当打开某篇文章的详情页面的时候,就将所访问的文章的编号写入到队列中。

所以,我们需要在生成静态页面所用到的模版文件中,写ajax代码,发送异步请求。

修改ArticelTemplateInfo.html中的代码

1
2
<link rel="stylesheet" type="text/css" href="/ArticelTemplate/ArticelStyle.css ">
<script src="/js/axios.js"></script>

先导入axios(注意路径是绝对路径)

1
2
3
4
5
6
7
8
9
10
11
</html>
<script type="text/javascript">
axios({
method: 'get',
url: "/articelInfo/EditAcount?articelId=" + $data.id

}).then(({ data: res }) => {
document.getElementById("acount").innerHTML = res.acount
})
</script>

这样所生成的所有静态页面中都会有以上的代码

这里将所访问的文章的编号发送到服务端,然后服务端返回所访问的文章的点击数。

将所返回的单击数,更新到页面中

1
2
3
4
5
<div class="artcelTop"><span>时间:2015/9/14 23:58:07 </span><span>信息来源:$data.Origin</span>

<span id="acount">点击:$data.ACount</span> <!-------添加了id属性--->

<span>【收藏此文】【字体:大 中 小】</span></div>

下面修改一下服务端的代码

修改ArticelInfoController.cs控制器中的代码

1
2
3
4
5
6
7
8
9
10
11
/// <summary>
/// 修改文章阅读数量
/// </summary>
/// <returns></returns>
public async Task<IActionResult> EditAcount()
{
int articelId = Convert.ToInt32(Request.Query["articelId"]);
var articelInfo = await articelInfoService.LoadEntities(a => a.Id == articelId).FirstOrDefaultAsync();
QueueHelper.EnqueueLikeOperation(articelId);
return Json(new { StatusCode = 200, Acount = articelInfo!.ACount });
}

以上接收到所访问的文章编号以后,插入到队列中,同时查询一下所访问的文章以前数据库中存储的点击数,并且将其返回。

4、出队操作

问题:什么时候扫描队列呢?

在项目启动的时候就开启一个新的线程来扫描队列,所以修改Cms.Web/Program.cs中的代码

如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var app = builder.Build();

// ----------开启一个新的线程,扫描队列
void StartProcessingQueue()
{
Task.Run(async () =>
{
while (true)
{
int articleId = 0;
if (QueueHelper.DeEnquenuLikeOperation(out articleId))
{
// 执行点击逻辑

// 直接从容器中获取MyDbContext的实例
MyDbContext dbContext = app.Services.GetService<MyDbContext>()!;

var articel = await dbContext.Articels.Where(a => a.Id == articleId).FirstOrDefaultAsync();
articel!.ACount = articel.ACount + 1;
await dbContext.SaveChangesAsync();

}
else
{
// 队列为空,休眠一会儿,防止CPU空转
await Task.Delay(5000); // 休眠时间可以根据需要调整
}
}
});
}

StartProcessingQueue();

调用QueueHelper.DeEnquenuLikeOperation方法,出队,获取对应的文章编号。

然后,这里直接根据从队列中获取的文章编号,查询数据库,更新点击数量。

这里由于业务比较简单,所以直接从容器中获取MyDbContext的实例,然后更新数据库中的点击数量。