參考了前輩所分享有關hangefire的相關文章與官方文件,
除了提供排程性的工作如:
射後不理-Fire-and-forget ,
延遲-Delayed ,
定時-Recurring 處裡外.
還提供了
延續-Continuations
如果說射後不理是一次性作業.
那麼Continuations 則是射中目標後下一步該做甚麼!
如果較長且需冗長的作業我們又需要先得到快速的回應,
確實使用
射後不理-Fire-and-forget ,
延遲-Delayed ,
定時-Recurring
會達到API迅速回應的需求.
但如果希望每一個冗長的需求,
做完會有一個確實做完的進度回應,
又不希望Client等待,
這時候官方建議可以使用Continuations搭配SignalR
參考連結:Tracking the progress
以下情境與實作皆剛好肚子不爭氣的時候想到,
至於
射後不理-Fire-and-forget ,
延遲-Delayed ,
定時-Recurring
這三種方法的使用與Hangfire Dashboard的設定方式,
小弟在此就不多做描述.
會附上前輩們的文章連結請各位參考
再此如有描述錯誤的地方也請前輩多多指導...^^a
情境
這時候我想到,
小時候媽媽在煮飯.突然發現忘了買醬油,或雞蛋水果...等等之類的.
我想應該就喊著.大明幫我買個醬油,之後就忙著先處裡廚房別的事務.
等到大明把醬油買回來,接續其他的事務!
這時候我想到,
小時候媽媽在煮飯.突然發現忘了買醬油,或雞蛋水果...等等之類的.
我想應該就喊著.大明幫我買個醬油,之後就忙著先處裡廚房別的事務.
等到大明把醬油買回來,接續其他的事務!
專案結構如下圖
Hanfire_Signalr
\Hubs\SampleHUB.cs
public class SampleHUB : Hub
{
/// <summary>
/// 提供HangFire_API作業完成後呼叫
/// </summary>
/// <param name="taskId"></param>
/// <param name="who"></param>
/// <param name="message"></param>
public void Send(string taskId, string who, string message)
{
//提供receiveMessage,當作業完成讓Client端接收訊息
Clients.All.receiveMessage(taskId, who, message);
}
}
HangFire_API
\EvenCls\BuysomethingCls.cs
public class BuysomethingCls
{
/// <summary>
/// 由WEB傳來的參數轉換為中文
/// </summary>
private static readonly Dictionary<string, string> transString = new Dictionary<string, string>() {
{"buyEgg","雞蛋"},
{"buydaoyo","醬油"},
{"buyfruit","水果"}
};
/// <summary>
///
/// </summary>
/// <param name="name"></param>
/// <param name="message"></param>
public void GobuySomething(string name, string message)
{
//Do Somthing 買醬油,買雞蛋,買水果....
}
/// <summary>
/// 當GobuySomething事件處理完成後呼叫,將訊息推往至SignalR
/// </summary>
/// <param name="parentId"></param>
/// <param name="name"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task GoHome(string parentId, string name, string message)
{
string feedbackmessage = string.Format("阿母)))~{0},買回來了", transString.Where(w => w.Key == message).Select(s => s.Value).FirstOrDefault());
using (HubConnection connection = new HubConnection("http://localhost:50925/habfireSR"))
{
IHubProxy myHub = connection.CreateHubProxy("SampleHUB");
// 與SignalR連線
await connection.Start().ContinueWith(task =>
{
//連線成功時
if (!task.IsFaulted)
{
//呼叫SampleHUB中提供Send的方法,將TaskId,誰去買,跟訊息傳送過去
myHub.Invoke("Send", parentId, name, feedbackmessage);
}
});
connection.Stop();
}
}
}
\Controllers\BuyOrderController.cs
public IHttpActionResult hangefire( string name, string message)
{
BackgroundJobClient _jobs = new BackgroundJobClient();
//新增一個作業
var parentId = _jobs.Enqueue<BuysomethingCls>(x => x.GobuySomething(name, message));
//作業完成後執行
_jobs.ContinueWith<BuysomethingCls>(parentId, x => x.GoHome(parentId, name, message));
//回傳TaskId
return Ok(parentId);
}
public IHttpActionResult taskcontinuation(string name, string message)
{
BuysomethingCls buyCls = new BuysomethingCls();
//新增一個作業
var firstTask = Task.Run(() => {
buyCls.GobuySomething(name, message);
});
//作業完成後執行
Task continuationTask = firstTask.ContinueWith(async (antecedent) =>
{
int parentId = antecedent.Id;
await buyCls.GoHome(parentId.ToString(), name, message);
});
//回傳TaskId
return Ok(firstTask.Id);
}
在上述中hangefire與taskcontinuation的方法中,
功能上是相同的,
只是hangefire此方法流程上會與taskcontinuation有些差異如官網提供的下圖
當我們每發一次請求,就等於是新增一個作業存入在Job Storage(MongoDB,MSSQL,Memory),透過Hangfire Sever在做背景處理!
所以我們可以透過Hangfire所提供的Hangfire Dashboard查看作業是否有順利完成,甚至是否有錯誤需要重新執行等等..如下圖
http://your Domain/hangfire
Hangfire_web_client
\Views\Home\Index.cshtml
<script>
/*暫存由HangFire_API回傳的Task ID*/
Globallocalstory = [];
/*呼叫HangFire_API 位置*/
GlobalAPILocation = { BuyOrder: function (whosend, doshomthing) { return 'http://localhost:56321/api/BuyOrder/hangfire' + "/" + whosend + "/" + doshomthing } };
//GlobalAPILocation = { BuyOrder: function (whosend, doshomthing) { return 'http://localhost:56321/api/BuyOrder/taskcontinuation' + "/" + whosend + "/" + doshomthing } };
var clientGobusomthing = {
/*按鈕觸發事件*/
ButtonClickEvent: function (obj) {
$(obj).attr('disabled', 'true');
var selectele = $(obj).parent().find('select');
var whobuyele = $(obj).parent().find('b1');
var eleTextid = $(obj).next().attr('id');
var whobuytext = $(whobuyele).text();
var selecteleVal = $(selectele).val();
$.get(GlobalAPILocation.BuyOrder(whobuytext, selecteleVal), clientGobusomthing.ajaxCallBack.bind({ whobuy: whobuytext, selectvalue: selecteleVal, textId: eleTextid }));
},
/*取回API回傳Call Back事件*/
ajaxCallBack: function (data) {
var addobj = { taskid: data, whobuy: this.whobuy, selecteleVal: this.selectvalue, textid: this.textId };
Globallocalstory.push(addobj);
clientGobusomthing.UpdateTextUI(this.textId, '出發中~~');
},
/*註冊接收SignalR回傳訊息*/
ReceverSignalrMsg: function (taskId, who, message) {
var result = $.grep(Globallocalstory, function (obj) { return obj.taskid == taskId });
if (result.length > 0) {
clientGobusomthing.UpdateTextUI(result[0].textid, message);
var findIndes = Globallocalstory.findIndex(function (obj) { return obj.taskid == result[0].taskid; });
Globallocalstory.splice(findIndes, 1);
$('#' + result[0].textid).parents().find('button').attr('disabled', false);
}
},
/*更新UI文字*/
UpdateTextUI: function (tagId, msg) {
$('#' + tagId).text(msg);
}
};
$(function () {
$.connection.hub.url = "http://localhost:50925/habfireSR"
var hfhub = $.connection.sampleHUB;
hfhub.client.receiveMessage = clientGobusomthing.ReceverSignalrMsg;
$.connection.hub.start({ jsonp: true });
});
</script>