Hangfire Highlighter Tutorial

Hangfire是一个开源且商业免费使用的工具函数库。可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows Service后台任务执行器。且任务信息可以被持久保存。内置提供集成化的控制台。 原文

Hangfire目前资料不多,官方文档提供两个教程 Sending Mail in Background with ASP.NET MVC 和 Highlighter Tutorial,根据第二个教程山寨了一把,把文中的Entity Framework 改成了 Dapper,Sql Server数据库改成了LocalDB。结合自己的理解把原文翻译一下(第一次翻译,如有不妥尽情拍砖)

Simple sample :https://github.com/odinserj/Hangfire.Highlighter

Full sample:http://highlighter.hangfire.iosources

目录

  • Overview 概述
  • Setting up the project 设置项目

    • Prerequisites  前置条件
    • Creating a project 创建项目
    • Hiliting the code  醒目代码
  • The problem
  • Solving a problem
    • Installing Hangfire
    • Moving to background
  • Conclusion

概述

  考虑到你正在构建一个像GitHub Gists的代码片段库web应用程序,并且想实现语法高亮的特性。为了提供用户体验,甚至你想让它在一个用户禁用JavaScript的浏览器中也能正常工作。

  为了支持这种场景,并且缩短项目开发时间,你会选择使用一个Web Service实现语法高亮,比如:http://pygments.appspot.com or http://www.hilite.me.

Note

Although this feature can be implemented without web services (using different syntax highlighter libraries for .NET), we are using them just to show some pitfalls regarding to their usage in web applications.

You can substitute this example with real-world scenario, like using external SMTP server, another services or even long-running CPU-intensive task. 这段不翻译了,难。

设置项目

Tip

This section contains steps to prepare the project. However, if you don’t want to do the boring stuff or if you have problems with project set-up, you can download the tutorial source code and go straight to The problem section.

前置条件

这个教程使用VS2012 (安装了Web Tools 2013 for Visual Studio 2012 补丁),你也可以使用VS2013。这个项目用到了.NET4.5,ASP.NET MVC5 和SQL Server 2008 Express 或更高版本。

注意:那个补丁就是为了在创建项目时有Asp.net MVC5 Empty Project 这个默认的项目模板,我下载了安装报错,找不到对应的包。SqlServer 2008 是为了存储代码片段和Hangfire的任务数据,他还用到了EF做为orm框架。

我这里使用VS2013+LocalDB+Dapper实现类似功能。

创建项目

打开VS2013->Web->ASP.NET Web 应用程序->弹出框里选择 MVC 模板,身份验证 我改成了 无身份验证

创建完成之后默认就有个HomeController.cs 控制器。其中的Index Action 看起来是这样的:

  public class HomeController : Controller
{
public ActionResult Index()
{
return View();
} public ActionResult About()
{
ViewBag.Message = "Your application description page."; return View();
} public ActionResult Contact()
{
ViewBag.Message = "Your contact page."; return View();
}
}

默认的Index视图也存在了,可以直接F5运行,看看是不是显示了默认页面。

定义模型类(Defining a model )

在Models文件夹下创建CodeSnippet.cs 文件

 CodeSnippet

接下来官网文档是安装EntityFramework包,我这里使用整理过的Dapper+DapperExtensions实现的简易ORM(参照地址 ),文件放在Data文件夹下,如:

创建一个Services 文件夹,创建 HighLightRepository.cs 文件

 HighLightRepository.cs

这里用到的连接字符串名字是 DefaultConnection。

创建LocalDB数据库文件(Defining a model )

在vs2013中打开SQL Server 对象资源管理器,右键“添加 Sql Server”,Server name 后面输入 (localdb)\v11.0.

右键添加数据库ZereoesHangfire,数据库位置 我这里选择了App_Data 文件夹。

新建数据库表CodeSnippet,执行是点击“更新”按钮,弹出一个sql确认框,运行后就可以创建表了,默认用的是dbo架构。

Our database is ready to use! 上面这部分没有按照文档中的操作。

创建视图和动作(Creating actions and views)

现在是向我们的项目注入活力的时候了,请按照描述的样子修改文件。

 HomeController
 Index.cshtml
 Create.cshtml
 Details.cshtml

添加MiniProfiler

Install-Package MiniProfiler,我一般使用界面添加,不在控制台下操作。

安装后,修改Global.asax.cs文件,添加如下代码:

        protected void Application_BeginRequest()
{
StackExchange.Profiling.MiniProfiler.Start();
}
protected void Application_EndRequest()
{
StackExchange.Profiling.MiniProfiler.Stop();
}

修改_Layout.cshtml

<head>
<!-- ... -->
@StackExchange.Profiling.MiniProfiler.RenderIncludes()
</head>

修改web.config

  <!--下面是手动添加的-->
<system.webServer>
<handlers>
<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />
</handlers>
</system.webServer>

醒目(高亮)代码(Hiliting the code)

这是我们应用的和兴功能。我们将使用 http://hilite.me 提供的HTTP API 来完成高亮工作。开始消费这个API之情,先安装  Microsoft.Net.Http 包。

Install-Package Microsoft.Net.Http

这个类库提供简单的异步API用于发送HTTP请求和接收HTTP响应。所以,让我们用它来创建一个HTTP请求到hilite.me 服务。

// ~/Controllers/HomeController.cs

/* ... */

public class HomeController
{
/* ... */ private static async Task<string> HighlightSourceAsync(string source)
{
using (var client = new HttpClient())
{
var response = await client.PostAsync(
@"http://hilite.me/api",
new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "lexer", "c#" },
{ "style", "vs" },
{ "code", source }
})); response.EnsureSuccessStatusCode(); return await response.Content.ReadAsStringAsync();
}
} private static string HighlightSource(string source)
{
// Microsoft.Net.Http does not provide synchronous API,
// so we are using wrapper to perform a sync call.
return RunSync(() => HighlightSourceAsync(source));
} private static TResult RunSync<TResult>(Func<Task<TResult>> func)
{
return Task.Run<Task<TResult>>(func).Unwrap().GetAwaiter().GetResult();
}
}

然后,在HomeController.Create 方法中调用它

   [HttpPost]
public ActionResult Create([Bind(Include = "SourceCode")] CodeSnippet snippet)
{
try
{
if (ModelState.IsValid)
{
snippet.CreatedAt = DateTime.UtcNow;
// We'll add the highlighting a bit later. //方案一:直接调用接口实现高亮
using (StackExchange.Profiling.MiniProfiler.StepStatic("Service call"))
{
snippet.HighlightedCode = HighlightSource(snippet.SourceCode);
snippet.HighlightedAt = DateTime.Now;
} HighLightRepository.Instance.Insert(snippet); return RedirectToAction("Details", new { id = snippet.Id });
} return View(snippet); }
catch (HttpRequestException)
{
ModelState.AddModelError("", "Highlighting service returned error. Try again later.");
}
return View(snippet);
}

Note

We are using synchronous controller action method, although it is recommended to use asynchronous one to make network calls inside ASP.NET request handling logic. As written in the given article, asynchronous actions greatly increase application capacity, but does not help to increase performance. You can test it by yourself with a sample application – there are no differences in using sync or async actions with a single request.

This sample is aimed to show you the problems related to application performance. And sync actions are used only to keep the tutorial simple. 不翻译,难。

问题

Tip

You can use the hosted sample to see what’s going on.

现在,运行这个程序,尝试创建一些代码片段,从一个小的开始。你有没有注意到一点延时,当你点击 Create 按钮后?

在我的开发机器上,它看起来用了0.5秒才重定向到详情页面。让我们看看MiniProfiler是什么导致这个延时:

我这里比他好点,用了1.3秒。

像我们看到的,调用web service是我们最大的问题。当我们尝试创建一个稍微大点的代码块时会发生什么?

.......省略两个截图.......

当我们扩大代码片段时,滞后也在增大。此外,考虑到语法高亮Web Service()经历着高负载,或者存在潜在的网络风险。或者考虑到这是CPU密集型任务,你也不能优化。

Moreover, consider that syntax highlighting web service (that is not under your control) experiences heavy load, or there are latency problems with network on their side. Or consider heavy CPU-intensive task instead of web service call that you can not optimize well.

Your users will be annoyed with un-responsive application and inadequate delays.

你的用户将对无响应的应用、严重的延时感到愤怒。

解决问题Solving a problem

面对这样的问题你能做什么? Async controller actions 没有什么帮助,像我前面earlier说的。你应该采用某种方法提取Web Service的调用和处理到HTTP请求之外,在后台运行。这里是一些方法:

  • Use recurring tasks and scan un-highlighted snippets on some interval.
  • Use job queues. Your application will enqueue a job, and some external worker threads will listen this queue for new jobs.

Ok, great. But there are several difficulties related to these techniques. The former requires us to set some check interval. Shorter interval can abuse our database, longer interval increases latency.

第一种是轮训没有高亮的片段,第二种是使用一个任务对立。

第一种轮训间隔小了数据库受不了,间隔打了用户受不了。

后一种方法解决了这个问题,但是带来了另外一个问题。队列是否可以持久化?你需要多少工作者线程?怎么协调他们?他们应当工作在哪,ASP.NET 应用内还是外,windows 服务?

最后一个问题是asp.net 应用处理长时间运行请求的痛点。

Warning

DO NOT run long-running processes inside of your ASP.NET application, unless they are prepared to die at any instruction and there is mechanism that can re-run them.

They will be simple aborted on application shutdown, and can be aborted even if the IRegisteredObject interface is used due to time out.

很多问题吧?Relax,你可以使用 Hangfire.它基于持久化队列解决应用程序重启,使用“可靠的获取”捕获意外线程终止,包含协调逻辑运行有多个工作者线程。并且可以足够简单的使用它。

Note

YOU CAN process your long-running jobs with Hangfire inside ASP.NET application – aborted jobs will be restarted automatically.

安装Hangfire (Installing Hangfire)

Install-Package Hangfire

安装完Hangfire,添加或更新OWIN Startup 类。在App_Start 文件夹下添加 Startup.cs 文件

[assembly: OwinStartup(typeof(Zeroes.Snippet.Highlight.App_Start.Startup))]

namespace Zeroes.Snippet.Highlight.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// 有关如何配置应用程序的详细信息,请访问 http://go.microsoft.com/fwlink/?LinkID=316888
GlobalConfiguration.Configuration.UseSqlServerStorage("DefaultConnection"); app.UseHangfireDashboard();
app.UseHangfireServer();
}
}
}

当应用程序第一次启动时会自动创建所有的表。

移至后台(Moving to background)

首先,我们需要定义一个后台任务方法,当工作者线程捕获到高亮任务时调用。我们将简单的定义它为一个静态方法,在 HomeController 中,使用 snippetId  参数。

// ~/Controllers/HomeController.cs

/* ... Action methods ... */

// Process a job

// Process a job
public static void HighlightSnippet(int snippetId)
{

var snippet = HighLightRepository.Instance.GetById<CodeSnippet>(snippetId);
if (snippet == null) return;

snippet.HighlightedCode = HighlightSource(snippet.SourceCode);
snippet.HighlightedAt = DateTime.UtcNow;

HighLightRepository.Instance.Update(snippet);
}

 

注意这是一个简单的方法,不包含任何Hangfire相关的方法。他调用仓储实例获取一个代码片段,调用Web Service。(这里多线程抵用了HighLightRepository)

然后,我们需要一个地方调用这个方法加入队列。所以,让我们修改 Create 方法:

  [HttpPost]
public ActionResult Create([Bind(Include = "SourceCode")] CodeSnippet snippet)
{
try
{
if (ModelState.IsValid)
{
snippet.CreatedAt = DateTime.UtcNow;
//方案一:直接调用接口实现高亮
//using (StackExchange.Profiling.MiniProfiler.StepStatic("Service call"))
//{
// snippet.HighlightedCode = HighlightSource(snippet.SourceCode);
// snippet.HighlightedAt = DateTime.Now;
//} HighLightRepository.Instance.Insert(snippet);
//方案二:加入任务队列
using (StackExchange.Profiling.MiniProfiler.StepStatic("Job 队列"))
{
// Enqueue a job
BackgroundJob.Enqueue(() => HighlightSnippet(snippet.Id));
} return RedirectToAction("Details", new { id = snippet.Id });
} return View(snippet); }
catch (HttpRequestException)
{
ModelState.AddModelError("", "Highlighting service returned error. Try again later.");
}
return View(snippet);
}

终于快完了。尝试创建一些代码片段看看时间线吧。(不要担心你会看到空白页,我一会就处理他)

Good,1.3秒到0.27秒了。但是还存在另外一个问题。你是否注意到有时你重定向的页面内没有任何源码?发生这个是因为我们的视图里包含下面这行:

<div>@Html.Raw(Model.HighlightedCode)</div>

修改Details.cshtml

 Details.cshtml

另外你需要拉取你的应用程序使用AJAX,直到它返回高亮代码。

 HighlightedCode

Or you can also use send a command to users via SignalR channel from your HighlightSnippet method. But that’s another story.

Note

Please, note that user still waits until its source code will be highlighted. But the application itself became more responsive and he is able to do another things while background job is processed.

总结

In this tutorial you’ve seen that:

  • Sometimes you can’t avoid long-running methods in ASP.NET applications.
  • Long running methods can cause your application to be un-responsible from the users point of view.
  • To remove waits you should place your long-running method invocation into background job.
  • Background job processing is complex itself, but simple with Hangfire.
  • You can process background jobs even inside ASP.NET applications with Hangfire.

翻译、贴代码用了快3个小时,现在都快吐了。如果您觉着有点用处,就点个赞吧、赞吧、赞吧!

希望更多的人一起学习这个组件,下载地址(稍后补上,CSDN上传太慢了)

补两个界面:

 
分类: C#

Hangfire Highlighter Tutorial的更多相关文章

  1. [翻译+山寨]Hangfire Highlighter Tutorial

    前言 Hangfire是一个开源且商业免费使用的工具函数库.可以让你非常容易地在ASP.NET应用(也可以不在ASP.NET应用)中执行多种类型的后台任务,而无需自行定制开发和管理基于Windows ...

  2. hangfire+bootstrap ace 模板实现后台任务管理平台

    前言 前端时间刚开始接触Hangfire就翻译了一篇官方的教程[翻译+山寨]Hangfire Highlighter Tutorial,后来在工作中需要实现一个异步和定时执行的任务管理平台,就结合bo ...

  3. Hangfire项目实践分享

    Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(De ...

  4. ABP文档 - Hangfire 集成

    文档目录 本节内容: 简介 集成 Hangfire 面板授权 简介 Hangfire是一个综合的后台作业管理器,可以在ABP里集成它替代默认的后台作业管理器,你可以为Hangfire使用相同的后台作业 ...

  5. 如何在Open Live Writer(OLW)中使用precode代码高亮Syntax Highlighter

    早先Microsotf的Windows Live Writer(WLW)现在已经开源了,并且更名为Open Live Writer,但是现在Windows Live Writer还是可以现在,Open ...

  6. ABP源码分析三十九:ABP.Hangfire

    ABP对HangFire的集成主要是通过实现IBackgroundJobManager接口的HangfireBackgroundJobManager类完成的. HangfireBackgroundJo ...

  7. Hangfire入门(任务调度)

    一.简介 英文官网:http://hangfire.io/ 开源地址:https://github.com/HangfireIO Hangfire 不依赖于具体的.NET应用类型,包含.NET 和.N ...

  8. Django 1.7 Tutorial 学习笔记

    官方教程在这里 : Here 写在前面的废话:)) 以前学习新东西,第一想到的是找本入门教程,按照书上做一遍.现在看了各种网上的入门教程后,我觉得还是看官方Tutorial靠谱.书的弊端一说一大推 本 ...

  9. thrift 服务端linux C ++ 与客户端 windows python 环境配置(thrift 自带tutorial为例)

    关于Thrift文档化的确是做的不好.摸索了很久才终于把跨linux与windows跨C++与python语言的配置成功完成.以下是步骤: 1)                 Linux下环境配置 ...

随机推荐

  1. SUSAN检测算子

    USAN区域(核同值区):和核像素的灰度相同会相信的模板像素的区域. 利用这个区域的尺寸.重心.二阶矩等可以帮助检测图像的边缘和角点.利用USAN的面积作为特征可以起到增强边缘和角点的效果. 该方法不 ...

  2. HDOJ1728 BFS【STL__queue_的应用】

    STL__queue_的应用 调用的时候要有头文件: #include<stdlib.h> 或 #include<cstdlib> + #include<queue> ...

  3. Robot Framework与Web界面自动化测试学习笔记:定位到新窗口

    在页面操作中,有时会需要打开新的窗口(新的网页不在当前窗口显示,而是在新的tab页显示), 比如利用  window.open("newurl") 或者  <a href=& ...

  4. 安装DBMS_SHARED_POOL包

    在安装10g gc的时候,会遇到The DBMS_SHARED_POOL package is not executed on the Existing Database这样的一个错误,意思是提示你D ...

  5. C++如何屏蔽双击运行程序功能?

    问题描述: 我们开发过程中可能会经常遇到,需要屏蔽EXE的双击运行功能,只能通过宿主程序(Service或EXE)来启动.比如腾讯的迷你弹窗,就只能通过主程序来启动,而不能直接通过双击来运行. 实现原 ...

  6. BZOJ 3545: [ONTAK2010]Peaks( BST + 启发式合并 + 并查集 )

    这道题很好想, 离线, 按询问的x排序从小到大, 然后用并查集维护连通性, 用平衡树维护连通块的山的权值, 合并就用启发式合并.时间复杂度的话, 排序是O(mlogm + qlogq), 启发式合并是 ...

  7. UML04-顺序图

    1.顺序图由哪几部分组成? 2.下面列出了打印文件时的工作流: 用户通过计算机指定要打印的文件. 打印服务器根据打印机是否空闲,操作打印机打印文件. 如果打印机空闲,则打印机打印文件: 如果打印机忙, ...

  8. SqlServer和Oracle中一些常用的sql语句7 游标

    declare db_cursor4 scroll cursor for select * from 供应商 --声明游标 open db_cursor4 --打开游标 fetch first fro ...

  9. KaOS 2016.04 发布,桌面 Linux 发行版

    KaOS 2016.04 发布了,KaOS是一份桌面Linux发行,其特色在于最新版本的KDE桌面环境及其他流行的使用Qt工具包的软件程序.它最初基于Arch Linux,但从2013年四月起,开发者 ...

  10. 最新OpenCV2.4.6与VS2010开发环境搭建

    OpenCV2.4.6与VS2010开发环境搭建 由于很久没有用OpenCV了,之前用的是1.0版本和VC++6.0.现在已经到了VS2010+OpenCV2.4.6.安装使用之后,发现OpenCV的 ...