Topshelf and Katana: a unified Web and service architecture

Source: Internet
Author: User
Tags configuration settings hosting

Transferred from: https://msdn.microsoft.com/zh-cn/magazine/dn745865.aspx

Asp.nettopshelf and Katana: a unified Web and service architecture

Wes McClure

Download the code sample

Using IIS to host ASP. NET WEB applications has become an industry standard for more than ten years. The process of building such applications is relatively straightforward, but deploying them is not easy. Deploying such applications requires subtle knowledge of the application configuration hierarchy, subtle differences in the version of IIS history, and cumbersome settings for websites, applications, and virtual directories. Many of the critical infrastructure typically eventually resides in a manually configured IIS component outside of the application.

It is difficult to provide support in IIS if your application is not only satisfied with WEB requests, but also supports long-running requests, recurring jobs, and other processing work. The solution is typically to create a separate Windows service to host these components. But this requires a completely separate deployment process, which doubles the workload. The last option is to let the Web and the service process communicate with each other. A very simple application can quickly become a very complex application.

Figure 1 shows a typical architecture. The WEB layer is responsible for processing quick requests and providing the UI to the system. Long-running requests are delegated to the service, which can also handle repetitive jobs and processing work. In addition, the service provides the Web tier with the status of current and future work to include in the UI.


Figure 1: Traditional decoupled Web and service architectures

New method

Fortunately, new technologies are emerging that can dramatically simplify the development and deployment of WEB and service applications. Thanks to the Katana project (katanaproject.codeplex.com) and the specifications provided by OWIN (owin.org), it is now possible to self-host WEB applications without using IIS while still supporting many of the ubiquitous ASP. , such as WebApi and SignalR. Web Self-hosting can also be embedded with topshelf (topshelf-project.com) in the most basic console application, making it easy to create Windows services. As a result, the WEB and service components can coexist in the same process, as shown in Figure 2 . This eliminates the overhead of developing external communication layers, standalone projects, and standalone deployment processes.


Figure 2: Unified Web and Service architecture (including Katana and Topshelf)

This feature is not a new feature. Topshelf has been available for many years to help streamline Windows service development, and many open source self-hosted WEB frameworks, such as Nancy, can be used. However, it was not until OWIN developed into a Katana project that there was an industry standard that was expected to replace hosting WEB applications in IIS. In addition, Nancy and many of the open source components are also used in conjunction with the Katana project, allowing you to create an eclectic and flexible framework.

Topshelf seems to be optional, but that is not the case. If service development cannot be simplified, self-hosted WEB development becomes extremely inefficient. Topshelf simplifies service development by taking services as console applications and hosting them as services. When you want to deploy, Topshelf automatically handles the installation and startup of the application as a Windows service (completely eliminating the overhead of processing InstallUtil), automating the nuances of service items and service components, and attaching the debugger to the service when an error occurs. Topshelf also allows you to specify multiple parameters in your code or to configure multiple parameters (such as service names) during installation through the command line.

To illustrate how to use Katana and topshelf to unify the Web and service components, I'll build a simple SMS application. I'll start with the API for receiving text messages and scheduling SMS send queues. This will show how easy it is to handle long-running requests. I will then add an API query method that returns the unread SMS count, which will also show how easy it is to query the service state from the Web component.

Next, I'll add a management interface to illustrate that self-hosted Web components can still build a rich web interface. To complete SMS processing, I'll add a component to send SMS messages in the order in the queue to show the included service components.

To highlight one of the best parts of this architecture, I'll create a psake script to reflect the simplification of the deployment.

In order to highlight the combination of Katana and topshelf, I intend not to elaborate on any of the projects. For more information, see Getting Started with Katana projects (BIT.LY/1H9XABL) and "easily create Windows services with Topshelf" (Bit.ly/1h9xreh).

You must have a console application before you start

Topshelf is designed to simplify the development and deployment of Windows services by starting with a simple console application. To start creating the SMS application, I need to create a C # console application and then install the Topshelf NuGet package from the Package Manager console.

When the console application starts, I need to configure topshelf Hostfactory to abstract host the application as a console (in the development environment) and the service (in the production environment):

private static int Main() { var exitCode = HostFactory.Run(host => { }); return (int) exitCode; }

Hostfactory will return an exit code that will help you discover the failure and stop the operation during service installation. The service methods provided by the managed Configurator can be used to specify the custom type of the entry point that represents the application code. Topshelf this as a service to be hosted, because Topshelf is a framework for simplifying the creation of Windows services:

host.Service<SmsApplication>(service => { });

Next, I'll create the smsapplication type to contain the logic to make the self-hosted Web server and traditional Windows service components run faster. This type of customization will include at least the behavior to be performed when the application starts or stops:

public class SmsApplication { public void Start() { } public void Stop() { } }

Because I chose to use the normal old CLR object (POCO) of the service type, I gave Topshelf a lambda expression to build an instance of the Smsapplication type and specified the start and stop methods:

service.ConstructUsing(() => new SmsApplication()); service.WhenStarted(a => a.Start()); service.WhenStopped(a => a.Stop());

Since topshelf allows you to configure multiple service parameters in your code, I use SetDescription, Setdisplayname, and Setservicename to describe and name the services that will be installed in the production environment:

host.SetDescription("An application to manage sending sms messages and provide message status."); host.SetDisplayName("Sms Messaging"); host.SetServiceName("SmsMessaging"); host.RunAsNetworkService();

Finally, I use Runasnetworkservice to instruct Topshelf to configure the service to run as a network Service account. You can change this account to any account that adapts to your environment. For more service options, see Topshelf Configuration document (Bit.ly/1rafmiq).

Running a service as a console application is as simple as starting an executable file. Figure 3 shows the output of the start and stop SMS executable file. Because this is a console application, you see the same behavior when you start the application in Visual Studio.


Figure 3: Run the service as a console application

Integration API

Once the topshelf system is in place, I can start working on the API for the application. Because the Katana project provides components for self-hosted OWIN pipelines, I install the Microsoft.Owin.SelfHost package to consolidate the self-hosted components. This package references multiple packages, of which two packages are important for self-hosting. First, Microsoft.Owin.Hosting provides a set of components for hosting and running Owin pipelines. Second, Microsoft.Owin.Host.HttpListener provides the implementation of the HTTP server.

Inside Smsapplication, I created a self-hosted WEB application using the WEBAPP type provided by the managed package:

protected IDisposable WebApplication; public void Start() { WebApplication = WebApp.Start<WebPipeline>("http://localhost:5000"); }

The WebApp startup method requires two parameters, one that specifies the generic parameter that will configure the type of the OWIN pipeline, and the other is the URL to listen for the request. A WEB application is a resource that can be freed. When the Smsapplication instance is stopped, I release the WEB application:

public void Stop() { WebApplication.Dispose(); }

One big advantage of using OWIN is that I can take advantage of familiar components. First, I'll use WEBAPI to create the API. I need to install the Microsoft.AspNet.WebApi.Owin package to integrate the WEBAPI into the Owin pipeline. I will then create the Webpipeline type to configure the OWIN pipeline and insert the WebAPI middleware. In addition, I will configure WEBAPI to use attribute routing:

public class WebPipeline { public void Configuration(IAppBuilder application) { var config = new HttpConfiguration(); config.MapHttpAttributeRoutes(); application.UseWebApi(config); } }

I can now create an API method for receiving text messages and scheduling SMS send queues:

public class MessageController :ApiController { [Route("api/messages/send")] public void Send([FromUri] SmsMessageDetails message) { MessageQueue.Messages.Add(message); } }

Smsmessagedetails contains the payload of the SMS. The Send action adds a text message to the queue for asynchronous processing of the text message later. MessageQueue is the global blockingcollection. In a real-world application, this might mean that you need to consider other factors, such as persistence and extensibility:

public static readonly BlockingCollection<SmsMessageDetails> Messages;

In a decoupled web and service architecture, the delivery of asynchronous processing of long-running requests, such as sending text messages, requires communication between the Web and the service process. Querying the service state by adding an API method means adding more traffic overhead. The unified approach makes it easy to share state information between WEB and serviced components. To demonstrate this, I added the Pendingcount query to the API:

[Route("api/messages/pending")] public int PendingCount() { return MessageQueue.Messages.Count; }
Build a rich-content UI

Although the API is easy to use, self-hosted WEB applications still need to support a visual interface. I suspect that the ASP. NET MVC Framework or derived framework will be used as OWIN middleware in the future. Nancy is temporarily compatible and has a package that supports the core parts of the Razor view engine.

I'll install the Nancy.owin package to add support for Nancy and install Nancy.Viewengines.Razor to consolidate the Razor view engine. In order to insert Nancy into the OWIN pipeline, I need to register it after registering WebApi so that it does not capture the route I mapped to the API. By default, if a resource is not found, Nancy returns an error, and WEBAPI passes a request that it cannot process back to the pipeline:

application.UseNancy();

For more information on how to use the Nancy and OWIN pipelines together, see "Hosting Nancy with OWIN" (Bit.ly/1gqjiye).

To build the admin state interface, I added the Nancy module and mapped the State Route rendering state view while passing the unread SMS count as the View model:

public class StatusModule :NancyModule { public StatusModule() { Get["/status"] = _ => View["status", MessageQueue.Messages.Count]; } }

The view is not very appealing at this point, just a simple count of unread text messages is displayed:

>Status<strong>@Model</strong> messages pending.

I'll use a simple launch navigation bar to enrich the view, as shown in Figure 4 . Using the start navigation bar requires static content that hosts the boot style sheet.


Figure 4: Management Status page

I can use Nancy to host static content, but OWIN has the advantage of blending and matching middleware, so I'll use the newly released Microsoft.Owin.StaticFiles package that belongs to the Katana project. The Staticfiles package provides the file services middleware. I will add it to the starting point of the OWIN pipeline so that the Nancy static file service will not start.

application.UseFileServer(new FileServerOptions { FileSystem = new PhysicalFileSystem("static"), RequestPath = new PathString("/static") });

The FileSystem parameter indicates where the file server is looking for the file to serve. I am using a folder named "Static". REQUESTPATH Specifies the route prefix that is used to listen for this content request. In this example, I choose to map the name "static", but these names do not have to be consistent. I use the following link in the layout to refer to the Launch style sheet (I naturally put the launch style sheet into a CSS folder in a static folder):

<link rel="stylesheet" href="/static/css/bootstrap.min.css">
Introduction to static content and views

Before I go on, I want to tell you a very useful tip to help you develop self-hosted WEB applications. Typically, you set up static content and MVC views to be copied to the output directory so that self-hosted WEB components can discover that they are related to the currently executing assembly. Not only is this an easy-to-forget burden operation, but changing the view and static content also requires recompiling the application, which will definitely stifle productivity. Therefore, I recommend that you do not copy static content and views to the output directory, but instead configure the middleware (such as Nancy and Fileserver) to map to the development folder.

By default, the debug output directory for the console application is Bin/debug, so in the development environment, I instruct Fileserver to browse the two directories above the current directory to find the static folder that contains the launch style sheet:

FileSystem = new PhysicalFileSystem(IsDevelopment() ?"../../static" :"static")

Then, to instruct Nancy where to find the view, I'll create a custom nancypathprovider:

public class NancyPathProvider :IRootPathProvider { public string GetRootPath() { return WebPipeline.IsDevelopment() ?Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\") :AppDomain.CurrentDomain.BaseDirectory; } }

Similarly, if I am running in the development mode of Visual Studio, use the same check method to browse the two directories above the base directory. I have left the implementation of isdevelopment to your discretion; You can use simple configuration settings, or you can write code to detect when an application starts from Visual Studio.

In order to register this custom root path provider, I created a custom nancybootstrapper and overridden the default Rootpathprovider property to create an instance of Nancypathprovider:

public class NancyBootstrapper :DefaultNancyBootstrapper { protected override IRootPathProvider RootPathProvider { get { return new NancyPathProvider(); } } }

When I add Nancy to the OWIN pipeline, I pass the Nancybootstrapper instance in the option:

application.UseNancy(options => options.Bootstrapper = new NancyBootstrapper());
Send SMS

Receiving text messages only completes half of the work, and the app still needs the SMS sending process. This is a traditional process that exists in an independent service. In this unified solution, I can add only the smssender that started with the application. I'll add this functionality to the Smsapplication startup method (in real-world applications, you should add the ability to stop and release this resource):

public void Start() { WebApplication = WebApp.Start<WebPipeline>("http://localhost:5000"); new SmsSender().Start(); }

In the Smssender startup method, I started a long-running task to send a text message:

public class SmsSender { public void Start() { Task.Factory.StartNew(SendMessages, TaskCreationOptions.LongRunning); } }

When the WEBAPI send operation receives a text message, it adds it to the SMS queue as an interception collection. I created a sendmessages method for intercepting text messages. This may be attributed to the abstraction layer behind getconsumingenumerable. When a group of text messages arrives, it immediately starts sending them:

private static void SendMessages() { foreach (var message in MessageQueue.Messages.GetConsumingEnumerable()) { Console.WriteLine("Sending:" + message.Text); } }

This does not matter if you are speeding up running multiple Smssender instances to extend the SMS delivery capacity. In a real-world application, you want to safely stop the enumeration by passing CancellationToken to getconsumingenumerable. To learn more about intercepting collections, you might want to visit Bit.ly/qgicm7 and Bit.ly/1m6sqli.

Easy deployment

Because of the use of Katana and topshelf, it is very easy to develop a portfolio of services and WEB applications. One of the extraordinary advantages of this powerful combination is that it simplifies the deployment process to a large extent. I'll cover a simple two-step deployment using Psake (Github.com/psake/psake). I'm not going to provide a reliable script for actual production purposes; I just want to show how simple the process is, no matter what kind of tool you use.

The first step is to build the application. I created a build task that calls MSBuild using the solution path and created a release version (the output will end up in Bin/release):

properties { $solution_file = "Sms.sln" } task build { exec { msbuild $solution_file /t:Clean /t:Build /p:Configuration=Release /v:q } }

The second step is to deploy the application as a service. I created a deployment task that relies on build tasks and declared the delivery directory that holds the path to the installation location. For the sake of simplicity, I only deploy to the local directory. Then, I created the executable variable that points to the console application executable in the delivery directory:

task deploy -depends build { $delivery_directory = "C:\delivery" $executable = join-path $delivery_directory ‘Sms.exe‘

First, the deployment task checks to see if the delivery directory exists. If it can find the delivery directory, it assumes that the service is deployed. In this case, the deployment task uninstalls the service and deletes the delivery directory:

if (test-path $delivery_directory) { exec { & $executable uninstall } rd $delivery_directory -rec -force }

Next, the deployment task deploys the new code by copying the build output to the delivery directory, and then copies the views and static folders to the delivery directory:

copy-item ‘Sms\bin\Release‘ $delivery_directory -force -recurse -verbose copy-item ‘Sms\views‘ $delivery_directory -force -recurse -verbose copy-item ‘Sms\static‘ $delivery_directory -force -recurse –verbose

Finally, the deployment task installs and starts the service:

exec { & $executable install start }

When you deploy the service, make sure that the Isdevelopment implementation returns FALSE, or you will see an "Access Denied" exception message when the file server cannot find a static folder. Additionally, there are times when you may have problems reinstalling the service on each deployment. If the service is already installed, then another policy is to stop, update, and start the service.

As you can see, the deployment process has been largely streamlined. There is absolutely no need to use IIS and installutil; The deployment process is only one, not two, and there is no need to worry about how the WEB and service tiers will communicate. This deployment task can be run repeatedly when you build unified WEB and service Applications!

Looking to the future

The best way to determine if the composite model is right for you is to try a low-risk project. Developing and deploying applications using this architecture is fairly straightforward. This will be a learning curve that does not often occur (for example, if you use Nancy for MVC). However, even if the composition method does not work, using OWIN also has an extraordinary advantage, that is, you can still use ASP. NET Host (MICROSOFT.OWIN.HOST.SYSTEMWEB) to host the OWIN pipeline inside IIS. Give it a try and see what your thoughts will look like.

Wes McClure leverage his expertise to help customers quickly deliver high-quality software that dramatically increases the value they create for their customers. He likes to talk about all the topics related to software development, as well as a pluralsight author and describes his experiences on devblog.wesmcclure.com. You can contact him by [email protected] .

Heartfelt thanks to the following technical experts for reviewing this article: Howard dierking (Microsoft), Damian Hickey, Chris Patterson (Relayhealth), Chris Ross (Microsoft), and Travis Smith
Howard Dierking is a project manager for the Windows Azure frameworks and Tools team, with an emphasis on ASP. NET, NuGet, and Web APIs. Dierking was previously an editor in charge of the MSDN magazine and was responsible for the developer certification program for Microsoft Learning. Prior to Microsoft's inauguration, he had more than 10 years of experience working with developers and application architects, with a focus on distributed systems.

Chris Ross is a software design engineer at Microsoft who is primarily responsible for all work related to networking and OWIN.

Chris Patterson is a relayhealth (McKesson Corporation's connectivity business) architect responsible for the architecture and development of applications and services for the following purposes: by linking patients, providers, Pharmacies are associated with financial institutions to speed up the delivery of care services. Chris is a major contributor to Topshelf and Masstransit, and Microsoft has awarded him the most valuable expert award for his contributions to the technology community.

Damian Hickey is a software developer who is primarily responsible for developing ddd\cqrs\es-based applications. He advocates the use of. NET open source software and has contributed to various projects such as Nancy and Neventstore. He occasionally makes speeches, but all people listen attentively, and he sometimes writes his own blog (http://dhickey.ie). You can contact him via [email protected] /@randompunter

Travis Smith is a developer advocating the use of the Atlassian and Atlassian markets. Travis helps promote open web, multilingual and emerging web technologies. Travis has contributed to many open source projects, including Topshelf and Masstransit. Travis can be found in the event of a developer who is passionate about making good software or on the Internet (http://travisthetechie.com).

Topshelf and Katana: a unified Web and service architecture

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.