65.9K
CodeProject 正在变化。 阅读更多。
Home

深入微服务架构 - 第二部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (4投票s)

2018年12月2日

CPOL

7分钟阅读

viewsIcon

11708

如何使用Microsoft OWIN构建微服务

引言

本文的第一部分详细讨论了微服务架构(MSA),并尝试解释了 MSA 的基本术语和概念。本部分将讨论 OWIN 框架,并尝试简要介绍其在构建基于 MSA 的服务中的用法。在开始阅读本部分之前,如果您对 MSA 没有背景知识,我建议您阅读第一部分

阅读第一部分:深入微服务架构 - 第一部分

背景

Open Web Server Interface for .NET (OWIN) 标准出现之前,Web 服务器(如 IIS、Apache Tomcat,甚至 HTTP.sys(对于独立应用程序))是 Web 应用程序和 HTTP 协议之间的连接。这意味着任何面向 Web 的软件(如 Web 服务或 Web 应用程序)都使用上述 Web 服务器之一,而没有标准的接口来通过 HTTP 协议进行通信。

这种方式存在几个问题,但最严重的问题是平台依赖性,这意味着基于 IIS 的应用程序无法在 Apache 服务器上运行,或者需要付出极大的成本才能使其正常工作。例如,考虑在 Apache 上运行 ASP.NET 应用程序,或者在独立应用程序(如自托管 Web 服务(由 Windows 服务托管的 Web 服务))的情况下,通过 HTTP.sys 直接通信带来了一些限制,例如并发连接数限制为 1000(请参阅HttpServerQueueLengthProperty)。

Windows Communication Function (WCF) 是第一个尝试通过在 Web 服务器和应用程序(在此情况下为服务)之间添加一个额外的层(在此情况下为框架)来解决这些问题。

WCF 允许您通过 HTTP 或 TCP 协议将数据作为异步消息从一个服务终结点发送到另一个。它还引入了几个新概念,使 SOA 实现更简单,例如 ABC(地址、绑定、契约)等。但很快,软件工程师就意识到 WCF 存在不必要的复杂性,这通常是由WCF 架构带来的。

Windows Communication Foundation 架构(引用自微软官方网站)

WCF 的缺点可以总结如下:

  1. 复杂性 - 许多软件开发人员难以理解
  2. 互操作性 - 由于 WCF 是微软对 SOA 的实现,它使得该框架的所有部分都高度依赖于微软的许可,从而降低了该技术的互操作性
  3. 昂贵 - 运行它需要更多的硬件资源
  4. 灵活性差 - 使用 WCF 开发 RESTful 服务简直是痛苦
  5. 工作量大 - WCF 的开发速度比使用 OWIN 的 NancyFx 慢得多

为什么选择 Microsoft OWIN?

Microsoft OWIN 正是针对这些问题,通过定义 Web 服务器和 Web 应用程序之间的标准接口来尝试解决它们。Microsoft 开发的 OWIN 框架,以 NuGet 包的形式分发,定义了 Web 服务器和 Web 应用程序之间的接口,并消除了对 System.Web 的依赖。这本身就允许您独立于 Web 服务器托管 Web 应用程序,换句话说,它允许您将服务托管在 IIS、Apache、作为独立的 Windows 服务,甚至在控制台应用程序中。下图以图形方式展示了我已经解释过的内容。

OWIN 位于 Web 服务器(托管软件)和您的应用程序(Web 应用程序)之间,让您可以无需修改即可针对任何主机。(来源:http://owinframework.com/content/documentation/concepts/overview

Microsoft OWIN 还支持中间件,这意味着它允许应用程序拥有一个由 Func<Task> 引用链接起来的中间件组件管道。中间件是软件的软件,意味着软件层通过一种机制互相链接,允许链中的每个部分通过调用方法将数据和序列控制权传递给下一部分。

换句话说,每个部分都有自己的生命周期,并将独立地作为一个函数或类进行操作(在此阅读有关 ASP.NET Core 中间件的更多信息)。

当然,当我们谈论中间件时,我们也期望有一个机制允许开发人员添加自己的中间件。Microsoft OWIN 提供了 AppBuilder 类,它是 IAppBuilder 的具体实现,允许开发人员通过调用 IAppBuilder 接口中定义的 Use 扩展方法集来将中间件添加到链中。

using Microsoft.Owin;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ClinetOwinNancy
{
    public class CustomMiddleware
    {
        public void Build(IAppBuilder app)
        {
            app.Use(Invoke);
        }

        private Task Invoke(IOwinContext context, Func<Task> next)
        {
            return next();
        }
    }
}

Microsoft OWIN Hello World

现在是时候着手 OWIN 框架,看看如何使用它了。在从仓库下载源代码之前,您需要记住,在打开项目后,以下 NuGet 包需要被恢复/重新安装(通常情况下,Visual Studio 会处理这部分)。

Microsoft.AspNet.WebApi.Client   {5.2.6} 
Microsoft.AspNet.WebApi.Core     {5.2.6} 
Microsoft.AspNet.WebApi.Owin     {5.2.6}  
Microsoft.AspNet.WebApi.OwinSelf {5.2.6}
Microsoft.Owin                   {2.0.2} 
Microsoft.Owin.Host.HttpListener {2.0.2} 
Microsoft.Owin.Hosting           {2.0.2} 
Newtonsoft.Json                  {6.0.4} 
Owin                             {1.0} 

否则,您也可以使用以下 PM 命令简单地安装它们。

Install-Package Microsoft.AspNet.WebApi.OwinSelfHost

Using the Code

以下部分将解释三个主要类 StartupApiControllerProgram 的用法,它们在 OWIN 框架上保持服务运行。为了简化,我将从现在开始称 OWIN 框架为 OwinFx。

配置用于自托管的 Web API

AppBuilderIAppBuilder 接口的实现者)需要通过 HttpConfiguration 类在自托管模式下进行配置。以下代码展示了此类配置的步骤。

using Owin;
using System.Web.Http;

namespace OwinFxMicroservice
{
    public class Startup
    {
        // This code configures Web API. The Startup class is specified as a type
        // parameter in the WebApp.Start method.
        public void Configuration(IAppBuilder appBuilder)
        {
            // Configure Web API for self-host. 
            var config = new HttpConfiguration();
            CreateHttpConfig(config);
            appBuilder.UseWebApi(config);
        }

        private static void CreateHttpConfig(HttpConfiguration config)
        {
            config.Routes.MapHttpRoute(
                 name: "DefaultApi",
                 routeTemplate: "api/{controller}/{id}",
                 defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

IAppBuilder 是 *Owin.dll* 的一个关键接口,它为 AppBuilder 类提供了一个具体的接口,该接口为您提供了一个 Use 方法来将自定义中间件注入 OWIN 管道,它看起来就像上面的代码。

public interface IAppBuilder
{
   IDictionary<string, object> Properties { get; }
   object Build(Type returnType);
   IAppBuilder New();
   IAppBuilder Use(object middleware, params object[] args);
}

添加 Web API 控制器

您的自定义 Web API Controller 需要继承自名为 ApiControllerabstract 类,该类实现了 IHttpController IDisposable 两个接口。该类实际上将处理 DELETEGETPOSTPUT 请求。在其他一些框架中,这个类被称为路由或模块。

using System;
using System.Collections.Generic;
using System.Web.Http;

namespace OwinFxMicroservice
{
    /// <summary>
    /// The Costume Web Api Controller
    /// </summary>
    public class ValuesController : ApiController
    {
        /// <summary>
        /// GET api/values 
        /// </summary>
        /// <returns>IEnu</returns>
        public IEnumerable<string> Get() => new string[] { "Hello", "World", "...!" };

        // GET api/values/3
        public string Get(int id) => (id == 1) ? "Hello" : (id == 2) ? 
                      "World" : (id == 3) ? "...!" : "No world found... ;-)";

        // POST api/values 
        public void Post([FromBody]string value) => 
                    Console.WriteLine($"The received value is {value}");

        // PUT api/values/5 
        public void Put(int id, [FromBody]string value)
        {
            //TODO: Write your Put logic here..
        }

        // DELETE api/values/5 
        public void Delete(int id)
        {
            //TODO: Write your Delete logic here..
        }
    }
}

如何调用您的服务?

到目前为止,我们开发了一个非常简单的服务(假设该服务具有微服务粒度),现在是时候看看如何使用该服务了。基本上,在这个阶段,我想向您展示两种方法:第一种,从代码中调用服务;第二种,通过第三方应用程序(如 Postman)调用,这在最终发布之前测试和调试服务时非常有用且方便。

通过 HttpClient 调用服务

.NET 允许您创建一个 HttpClient 类的实例,并传入您的 Uri(服务的基地址加上路由),然后异步使用 GetPostDeletePut。请看下面的代码。

// Create HttpCient and make a request to api/values 
var client = new HttpClient();
var response = client.GetAsync(new Uri(baseAddress + "api/values")).Result;

这就是我们从代码中调用已开发服务的方式。以下部分将显示 Program.cs 类中的代码。

using Microsoft.Owin.Hosting;
using Newtonsoft.Json;
using System;
using System.Net.Http;
using System.Text;

namespace OwinFxMicroservice
{
    class Program
    {
        static void Main(string[] args)
        {
            string baseAddress = "https://:9000/";

            // Start OWIN host 
            using (WebApp.Start<Startup>(url: baseAddress))
            {
                // Create HttpCient and make a request to api/values 
                var client = new HttpClient();

                #region GET
                Console.WriteLine
                ("///////////////////////// GET HAS BEEN SENT ///////////////////////////////////////");
                var response = client.GetAsync(new Uri(baseAddress + "api/values")).Result;
                Console.WriteLine(response);
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
                #endregion

                Console.WriteLine("Press any key to continue with the POST message.");
                Console.ReadLine();

                #region POST
                Console.WriteLine
                ("///////////////////////// POST HAS BEEN SENT ///////////////////////////////////");
                var stringContent = new StringContent(JsonConvert.SerializeObject("Hello World...!"), 
                                    Encoding.UTF8, "application/json");
                response = client.PostAsync
                           (new Uri(baseAddress + "api/values"), stringContent).Result;
                Console.WriteLine(response);
                Console.WriteLine(response.Content.ReadAsStringAsync().Result);
                #endregion

                Console.WriteLine("Press any key to exit or call Postman for more tests.");
                Console.ReadLine();
            }
        }
    }
}

通过 Postman 调用服务

Postman 是一个 API 开发环境,它允许您通过一系列端点调用 HTTP 请求,同时跟踪请求和响应。要了解更多关于 Postman 的信息,我想将您重定向到Postman 官方网站上的以下文章。

发送 Get 请求

要查看我们开发的服务的 Postman 工作方式,请运行该服务,并通过 Postman 创建一个 Get 请求(请参阅下图),然后调用 localhost:9000/api/values 路由。我假设在此步骤之后,您应该在响应体中看到以下响应。

要创建 Get 请求,请从组合框中选择 GET 方法,输入端点(localhost:9000/api/values),然后点击蓝色的 Send 按钮。

如果您尝试 localhost:9000/api/values/1,您将只得到“Hello”作为结果。请注意,消息的类型默认是 JSON。

发送 Post 请求

要创建 Post 请求,请从组合框中选择 POST 方法,输入端点(localhost:9000/api/values),然后转到 Body 部分,选择 Raw,将类型设置为 JSON,并在 Body 中写入“Hello World ...!”,然后点击蓝色的 Send 按钮。

现在,如果您在 Post 方法上设置断点,您应该能够调试并查看服务端的接收内容。

// POST api/values 
public void Post([FromBody]string value) => Console.WriteLine($"The received value is {value}");

为什么选择 NancyFx?

Nancy 的开发者说:“Nancy 是一个轻量级、低仪式感的框架,用于在 .NET 和 Mono 上构建基于 HTTP 的服务。该框架的目标是尽可能少地干预,并为所有交互提供一个超级顺畅的路径”。我对此不再赘述,因为这是对 Nancy 最好的描述,我个人觉得 Nancy 的使用非常方便,而且不太复杂。

Nancy 被设计为一个特定领域语言 (DSL),用于处理 DELETEGETHEADOPTIONSPOSTPUTPATCH 请求。

本文的下一部分将讨论如何使用 NancyFx 来构建微服务。

转到下一部分

转到上一部分

© . All rights reserved.