构建 ASP.NET REST Web 服务(三)

原文:Building RESTful Web Services with .NET Core

协议:CC BY-NC-SA 4.0

七、扩展 RESTful 服务——Web 服务的性能

在网络世界中,每个人都在编写或寻找一个网络应用。 随着需求的增加,每个 web 应用都需要能够服务更多的请求——有时一天数千个请求。 因此,应该编写应用来处理这种巨大的请求。

例如,假设您是负责开发公司旗舰产品 FlixOne Store 的开发和支持团队的一员。 该产品很受欢迎,并获得了吸引力,导致您的电子商务网站(FlixOne)被消费者流量淹没。 你系统里的支付服务很慢,几乎导致整个系统瘫痪,导致你失去客户。 虽然这是一个想象的场景,但它可以发生在现实生活中,并可能导致业务损失。 为了避免这种情况,您应该考虑 FlixOne 应用的可伸缩性。

可伸缩性是关键系统最重要的非功能需求之一。 用几百个事务服务几个用户与用几百万个事务服务几百万个用户是不一样的。

在本章中,我们将一般地讨论可伸缩性。 我们还将讨论如何扩展 RESTful 服务,在设计它们时要考虑什么,以及如何使用不同的模式(包括对常规应用也有帮助的技术、库和工具)来避免级联故障。

读完本章,你将会学到:

  • 聚类
  • 负载平衡
  • 可伸缩性简介

聚类

集群是在多个服务器上提供相同服务的一种方法。 通过添加更多的服务器,您可以避免不受控制的情况,例如故障转移、系统崩溃等等。 在数据库上下文中,集群指的是多个服务器实例连接到单个服务器的能力。 容错和负载均衡是集群的两个主要优点。

负载平衡

在集群时,负载平衡器是一个有用的工具。 您可以将负载平衡定义为一种设备,它帮助在集群服务器内或跨集群服务器分发网络或应用流量,并提高应用的响应能力。

在实现中,负载均衡器被放置在客户机和服务器之间。 它有助于在多个服务器上平衡多个应用请求。 换句话说,负载均衡器减少了单个服务器的时间,并防止了应用服务器故障。

它是如何工作的?

负载平衡器的工作是确保应用的服务器可用。 如果一个应用的服务器不可用,负载均衡器会将所有新请求重定向到可用的服务器,如下图所示:

<assets/867aa115-47b5-4579-8b1b-e94c3e9f7bc8.png>

在上面的图中,您可以看到负载均衡器在其典型环境中,其中一个系统接受来自互联网上不同来源的多个请求,然后由负载均衡器从多个服务器管理这些请求。

In .NET, this arrangement is also known as a web farm (https://www.codeproject.com/Articles/114910/What-is-the-difference-between-Web-Farm-and-Web-Ga).

负载均衡器使用各种算法,也称为负载均衡器方法:最小连接法、轮询法、最小响应时间法、最小带宽法、最小数据包法、自定义负载法等等。

负载均衡器在应用的可伸缩性中扮演着重要的角色,因为它确保应用的服务器对服务器请求可用。 请注意,您将需要在不更改代码的情况下安排硬件基础设施,以满足负载平衡器的需求(然而,有些场景需要更改代码)。 市场上有很多负载平衡器,如 Incapsula (https://www.incapsula.com/),F5 (https://www.f5.com/),Citrix Netscaler (https://www.citrix.com/),直流发电机(https://dyn.com/),亚马逊弹性负载平衡,和亚马逊 ELB(【显示】https://aws.amazon.com/)。

在接下来的章节中,我们将研究扩展系统的不同方法。

介绍了可伸缩性

每个应用都有自己的服务请求的能力。 应用的能力是指它的性能以及在负载增加时它如何满足其目标。

许多 web 应用将此称为在规定时间内的请求数。

在设计 web 应用时,做出正确的设计决策是非常重要的; 设计决策会影响服务的可伸缩性。 一定要达到适当的平衡,以便您的方法考虑服务及其基础设施以及任何伸缩需求。

性能和可伸缩性是系统的两个不同特征。 性能处理的是系统的吞吐量,而可伸缩性处理的是为更多的用户或更多的事务提供所需的吞吐量。

缩放(垂直缩放)

向上扩展(也称为垂直扩展)是通过向同一台机器添加更多资源(如内存或更快的处理器)来实现可伸缩性的一种方法。 这并不总是适用于所有应用,因为成本也是考虑垂直扩展时的一个因素。

您还可以升级资源或硬件,而不是向您的机器添加新资源。 例如,如果您有 8gb 的 RAM,您可以将其升级到 16gb,同样的事情也适用于处理器和其他资源。 不幸的是,随着硬件的升级,您可以对机器进行多大的扩展是有限制的。 这可能导致简单地转移瓶颈,而不是解决提高可伸缩性的真正问题。

您还可以将应用迁移到完全不同的机器上,例如简单地将应用迁移到更强大的 MacOS 上。

垂直扩展不涉及任何代码更改,所以它是一项简单的任务,但它涉及额外的成本,因为它是一项相当昂贵的技术。 Stack Overflow 是那些基于. net 的垂直伸缩系统中罕见的例子之一。

向外缩放(水平缩放)

向上扩展、向外扩展或水平扩展会向服务请求添加更多的服务器或节点,而不是资源。 如果不想扩展应用,总有办法向外扩展。

当应用代码不依赖于它所运行的服务器时,向外扩展是一种成功的策略。 但是,如果请求需要在特定的服务器上执行,也就是说,如果应用代码具有服务器亲和性,则很难向外扩展。 对于无状态代码,它更容易在任何服务器上执行。 因此,当无状态代码在水平伸缩的机器或集群上运行时,可伸缩性得到了提高。由于横向缩放的性质,它是整个行业常用的方法。 有许多用这种方式管理大型可伸缩系统的例子,如谷歌、Amazon 和 Microsoft。

线性可伸缩性

线性可伸缩性是指应用 Amdahl 定律(https://en.wikipedia.org/wiki/Amdahl%27s_law)垂直扩展应用。 在这里,您还可以考虑并行计算。

并行计算是一种计算体系结构,它表示多个处理器同时执行处理。

在你的应用中线性可伸缩性的好处包括:

  • 不需要更改代码
  • 可以很容易地添加额外的资源
  • 有物理可用性

分布式缓存

在分布式缓存技术的帮助下,我们可以提高 RESTful web 服务(web API)的可伸缩性。 分布式缓存可以存储在集群的多个节点上。 分布式缓存提高了 web 服务的吞吐量,因为缓存不再需要对任何外部资源进行 I/O 访问。

这种方法有以下优点:

  • 客户得到相同的结果
  • 分布式缓存由持久性存储进行备份,并作为不同的远程进程运行; 即使应用服务器重新启动或出现任何问题,也不会影响缓存
  • 源数据存储所发出的请求较少

缓存持久化数据(数据层缓存)

与应用性能类似,您还应该考虑数据库性能。 通过缓存持久化数据,在向数据库添加缓存层后,您将获得更好的性能。 当应用中大量使用读请求时,这一点也很重要。 现在我们将以 EF Core 的缓存级别为例。

一级缓存

这是 EF Core 启用的内建会话缓存。 从服务的第一个请求开始,从数据库中检索对象并存储在 EF Core 会话中。 换句话说,EF Object Context 和 DbContext 维护它们所管理的实体的状态信息。 一旦上下文不再可用,它的状态信息也就消失了。

第二级缓存

对于以分布式方式开发的应用,或者需要持久化数据的长时间运行的请求,如 web 应用,二级缓存是很重要的。 二级缓存存在于事务或应用的范围之外,并且这些缓存可用于任何上下文或实例。 您可以使用应用可用的缓存机制,而不用编写自己的代码,比如 Memcached。

应用缓存

应用缓存或应用层缓存有助于缓存应用中的任何对象。 这进一步提高了应用的可伸缩性。 在下一节中,我们将讨论各种可用的缓存机制。

CacheCow

当你想在客户端和服务器上都实现 HTTP 缓存时,就会用到 CacheCow。 这是一个轻量级的库.NET web API 支持目前是可用的。 CacheCow 是开源的,并带有一个 MIT 许可,可以在 GitHub 上获得(https://github.com/aliostad/CacheCow)。

要开始使用 CacheCow,你需要通过以下步骤为服务器和客户端做好准备:

  1. 在你的 ASP 中安装Install-Package CacheCow.ServerNuGet 包.NET Web API 项目; 这将是您的服务器。

  2. 在客户端项目中安装Install-Package CacheCow.ClientNuGet 包; 客户端应用将是 WPF、Windows 窗体、控制台或任何其他 web 应用。

  3. 创建一个缓存存储。 您需要创建一个缓存

    store

    在服务器端,需要一个数据库来存储缓存元数据(https://github.com/aliostad/CacheCow/wiki/Getting-started#cache-store)。

Memcached

Memcached 是一个可定制的开源项目; 您可以使用源代码,并根据您的需求添加和更新它。 Memcached 的官方页面(https://memcached.org/)定义为:

“An in-memory key-value store for small chunks of arbitrary data (strings, objects) from results of database calls, API calls, or page rendering.” Refer to https://www.deanhume.com/memcached-for-c-a-walkthrough/ for a complete walkthrough.

Azure 复述,缓存

Azure Redis Cache 是建立在一个叫做 Redis(https://github.com/antirez/redis)的开源存储之上的,它是一个内存中的数据库,并在磁盘上持久化。 根据微软的描述(https://azure.microsoft.com/en-in/services/cache/):

“Azure Redis Cache is based on the popular open source Redis cache. It gives you access to a secure, dedicated Redis cache, managed by Microsoft and accessible from any application within Azure.”

开始 Azure Redis 缓存是非常简单的,如果你采取以下步骤:

  1. 创建一个 web API 项目。 参考前面章节中的代码示例。

  2. 实现复述。 转诊点请使用https://github.com/StackExchange/StackExchange.Redis。 另外,安装Install-Package StackExchange.RedisNuGet 包。

  3. 更新 CacheConnection 的配置文件(https://docs.microsoft.com/en-us/azure/redis-cache/cache-dotnet-how-to-use-azure-redis-cache#NuGet)。

  4. 然后,在 Azure(https://docs.microsoft.com/en-us/azure/redis-cache/cache-web-app-howto#publish-and-run-in-azure)上发布。

通信(异步)

沟通这个术语是不言自明的; 它是服务之间的交互行为。 这方面的例子包括如下:

  • 在同一应用中与另一个服务通信的服务
  • 与应用外部的另一个服务通信的服务(外部服务)
  • 与组件(内部或外部)通信的服务

当消息或数据通过线路时,该通信通过 HTTP 协议进行。

应用的性能会影响服务之间的通信方式。

异步通信是帮助扩展应用的方法之一。 在 ASP。 . NET Core,我们可以通过使用异步 HTTP 调用(异步编程):https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/

在处理异步通信时,您应该小心操作,例如,在添加带有映像的新产品时。 设计了一个系统,可以创建不同大小的图像的缩略图。 这是一项耗时的任务,如果处理不当,可能会导致性能下降。 从设计的角度来看,异步操作在此场景中不起作用。 在这里,您应该实现类似于带有回调的任务之类的东西,它告诉系统作业何时完成。 有时,您可能还需要中间件来处理请求。

实现异步通信的最佳方法是使用异步 RESTful API。

在创建可伸缩系统时,必须始终考虑异步通信。

总结

在本章中,我们讨论了可伸缩性,包括可用的库、工具等等。 然后,我们讨论了如何扩展 RESTful 服务,在设计它们时要考虑什么,以及如何使用不同的模式避免级联失败。

在接下来的章节中,我们将讨论并构建一个 web 客户机来调用和使用 RESTful 服务。

十、构建 Web 客户机——使用 Web 服务

到目前为止,在本书中,我们已经创建了 RESTful 服务,以便我们可以在项目内部或外部调用或使用这些服务。 在本章中,我们将讨论这些服务的一些用例,以及使用 RESTful web 服务的技术和方法。

在本章中,我们将涵盖以下主题:

  • 使用 RESTful web 服务
  • 构建一个 REST web 客户机

使用 RESTful web 服务

到目前为止,我们已经创建了 RESTful 服务,并在代码示例的帮助下讨论了服务器端代码。 我们使用了外部第三方工具,如 Postman 和 Advanced RESTClient,来使用这些服务。 我们还使用了假对象并在单元测试期间使用了这些服务。 虽然这些使用示例很有帮助,但它们并没有显示 RESTful 服务的真正实力,因为它们要么测试了其功能,要么验证了其输出。

在某些情况下,您可能需要在类似于控制器的另一个应用中使用或使用这些服务,甚至需要在您自己的应用中使用这些服务。 这些应用可以是以下任何一种:

  • 基于控制台的
  • 基于 Web 的
  • 移动或任何其他设备

让我们来看看我们已经讨论过的应用之一:假设在集成在线支付系统时,您需要某种机制来实现或使用外部 API(在本例中是 PayPal)。 在这种情况下,我们已经讨论过的外部工具,比如 Postman 和 Advanced RESTClient,就无能为力了; 为了满足您的需求,您需要一个 REST 客户机。

下图说明了如何在 HTTP 客户机的帮助下使用 REST 客户机使用服务。 在下面的图中,REST 客户机正在与用 ASP 开发的外部服务和网络服务交互(请求、响应).NET Core 或位于相同或不同的服务器上。

<assets/a80a5a5f-efbd-4d42-a85e-16b910703271.png>

WebConsoleMobile等是在 REST 客户端帮助下使用这些服务的客户端。

现在,我们将讨论如何构建一个 REST 客户机,以便使用应用中的其他 RESTful web 服务(即 api)。

构建一个 REST web 客户机

基于 rest 的服务可能是 web 应用的一部分,也可能不是。 web 应用可以调用或使用来自同一应用的外部 api 或服务。 在服务和使用这些服务的应用之间实现交互或通信(请求、响应)的程序称为客户机

客户机帮助应用与 api 进行通信(请求、响应)。

在本节中,我们将创建一个 web 客户机。 web 客户端是用 ASP 编写的应用或程序。 净的核心。

在构建测试 web 客户端之前,我们需要讨论必须调用什么。

继续我们的 FlixOne 书店示例,下表列出了我们将调用和消费的产品和服务:

| API 资源 | 描述 |
| GET /api/product | 获取产品列表。 |
| GET /api/product{id} | 得到一个产品。 |
| PUT /api/product{id} | 更新现有产品。 |
| DELETE /api/product{id} | 删除现有产品。 |
| POST /api/product | 添加新产品。 |

我们的 FlixOne 产品服务旨在完成以下任务:

  • 添加新产品
  • 更新现有产品
  • 删除现有产品
  • 检索一个产品

我们已经确保 Swagger 支持我们的产品 api(更多信息请参阅前面的章节),所以让我们开始吧。 要开始这个项目,请遵循以下步骤:

  1. 首先,运行 Visual Studio 2017
  2. 选择文件|打开
  3. 选择项目 flixon.com . bookstore . productservice
  4. 通过按F5或直接从菜单中单击来运行项目
  5. 输入以下 URL:http://localhost:10065/swagger/

现在,您应该看到产品 api 的 Swagger 文档,如下面的截图所示:

<assets/4202f70f-415a-491c-affe-7cf9831b0210.png>

Product APIs documentation

烹饪 web 客户端

我们已经讨论了需要使用哪些 api 以及哪些资源返回什么,所以现在是时候烹饪我们的 web 客户端了,这样我们就可以使用和调用我们的产品 api。 要做到这一点,请遵循以下步骤:

  1. 要为我们的新项目创建一个全新的解决方案,转到文件|新|项目(或按Ctrl+Shift+N),如下截图所示。

<assets/7f232c75-ae75-41b4-84b9-5ff7b5735f92.png>

  1. 从新项目中,选择 ASP.NET Core Web 应用。
  2. 将项目命名为FlixOne.BookStore.WebClient,然后单击 OK,如下图所示:

<assets/4dd65c06-4f52-43fd-9c75-68a654d22aae.png>

  1. 从 ASP。 选择“Web Application”,单击“OK”,如下图所示:

<assets/b8014e2a-003d-4805-8349-e71cb4fc5e87.png>

  1. 现在运行项目调试|开始调试或按F5键。
  2. 你现在应该看到一个默认的网站模板。
  3. 现在我们将使用RestSharp创建一个 web 客户端。 我们需要添加对 RestSharp 的支持,以获得通过 HTTP 协议调用 API 资源的功能。

RestSharp 是一个轻量级的 HTTP 客户端库。 您可以根据需要进行更改,因为它是一个开源库。 您可以在https://github.com/restsharp/RestSharp找到完整的存储库。

  1. 使用 Open package Manager(右键单击 Solution Explorer 中的 Solution)添加一个 NuGet 包,如下图所示:

<assets/c88554eb-4b19-480f-8597-e0b8d2645b0c.png>

  1. 搜索 RestSharp 并勾选包含预发布的复选框,然后单击安装,如下截图所示:

<assets/64ac9dd2-1ba4-409d-a422-2104d598a3f1.png>

Selecting the RestSharp NuGet package

  1. 所需的软件包现在将安装,如下截图所示:

<assets/65a1722d-161f-4ab7-a8d5-0e24d31e113d.png>

Installing the RestSharp package

在继续之前,让我们首先确保我们的产品 api 正常工作。 运行产品 API 项目,打开 Swagger,点击GET /api/product/productlist资源如下:

<assets/d2bd41c1-e23c-40be-805d-0fefb43d419d.png>

执行了前面的资源后,你应该看到一个完整的产品列表,如下面的截图所示:

<assets/dbc16f11-5f14-4aa5-bbf3-a41d6d6c9cc6.png>

Try all available resources to make sure that your product APIs are working correctly.

编写代码

到目前为止,我们已经准备好了为 REST web 客户端编写代码所需的东西; 在本节中,我们将编写实际的代码:

  1. 添加一个简单的代码来调用或使用您的产品 api。

如果你创建了一个新的项目在同一个解决方案(参见第一步**一节烹饪的 web 客户端),请确保该项目产品 API 是运行在开始之前您的 web 客户端项目。

  1. Client文件夹中添加一个新类(Ctrl+Shift+C),命名为RestSharpWebClient

<assets/2981769f-110c-479f-a737-834a493a847b.png>

  1. 现在打开RestSharpWebClient类并添加以下代码:
private readonly RestClient _client = new RestClient("http://localhost:10065/api/");

前面的代码初始化 RestSharp 的 RestClient,并将基本 URL 作为字符串或 URI 接受。

URI 代表统一资源标识符,是用于标识资源的字符串的表示形式。

您可能遇到有多个环境的场景; 在这种情况下,您应该根据您的环境将 URI 存储在指向它的位置。 例如,您可以为您的开发环境使用 URIhttp://devserver:10065/api/,也可以为您的 QA 环境使用 URIhttp://testenv:10068/api/。 您应该将这些键存储在config文件或类似的地方,以便可以方便地访问这些值。 我们推荐使用new RestClient(somevariableforURI);

In our application, product APIs are running on localhost and the listening port 10065. This may be different in your case.

让我们讨论下面的代码片段来调用或使用GET /api/product /productlist资源并填充完整的产品列表,如下所示:

public List<ProductViewModel> GetProducts()
{
  var request = new RestRequest("product/productlist", Method.GET);
  var response = _client.Execute<List<ProductViewModel>>(request);
  return response.Data ?? new List<ProductViewModel> {new
  ProductViewModel()};
}

在这里,我们使用RestRequest创建了一个GET请求,其中传递了一个资源和方法。

要使用productid获得特定的产品,请输入以下代码:

public ProductViewModel GetProductDetails(string productId)
{
  var request = new RestRequest("product/{productid}", Method.GET);
  request.AddParameter("productid", productId);
  var response = _client.Execute<ProductViewModel>(request);
  return response.Data ?? new ProductViewModel();
}

在前面的代码块中,GetProductDetails方法做了与GetProducts方法相似的事情。 不同之处在于它接受参数productId

下面是我们 REST 客户端的完整代码:

public class RestSharpWebClient
{
  private readonly RestClient _client = new
  RestClient("http://localhost:10065/api/");
  public List<ProductViewModel> GetProducts()
  {
    var request = new RestRequest("product/productlist", 
    Method.GET);
    var response = _client.Execute<List<ProductViewModel>>
    (request);
    //To avoid any exception lets return an empty view model
    //On production environment return exact exception or your 
    custom code
    return response.Data ?? new List<ProductViewModel> {new
    ProductViewModel()};
  }
  public ProductViewModel GetProductDetails(string productId)
  {
    var request = new RestRequest("product/{productid}", 
    Method.GET);
    request.AddParameter("productid", productId);
    var response = _client.Execute<ProductViewModel>(request);
    //To avoid any exception lets return an empty view model
    //On production environment return exact exception or your 
    custom code
    return response.Data ?? new ProductViewModel();
  }
  public bool AddProduct(ProductViewModel product)
  {
    var request = new RestRequest("product/addproduct", 
    Method.POST);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.OK;
  }
  public bool UpdateProduct(string id, ProductViewModel product)
  {
    var request = new RestRequest("updateproduct", Method.PUT);
    request.AddQueryParameter("productid", id);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.NoContent;
  }
  public bool DeleteProduct(string id, ProductViewModel product)
  {
    var request = new RestRequest("deleteproduct", Method.DELETE);
    request.AddQueryParameter("productid", id);
    request.AddBody(product);
    var response = _client.Execute(request);
    return response.StatusCode == HttpStatusCode.NoContent;
  }
}

通过前面的代码片段,您现在已经添加了调用和使用产品 api 的功能。

实现 REST web 客户端

基于 rest 的服务可能是你的 web 应用的一部分,也可能不是,但我们仍然需要理解如何实现它们。

所以,现在是时候做些真正的工作了。 将ProductController添加到项目中,并执行以下操作:

public ActionResult Index()
{
  var webClient = new RestSharpWebClient();
  var products = webClient.GetProducts();
  return View("ProductList", products);
}

看一下前面的代码片段。 我们调用了RestSharpWebClientGetProducts方法,并在Index.cshtml视图中填充了完整的产品列表。

要添加另一个操作方法,请输入以下我们的ProductController的完整代码。 下面的代码片段包含了Index操作方法,并给了我们一个产品列表:

public class ProductController : Controller
{
  public ActionResult Index()
  {
    var webClient = new RestSharpWebClient();
    var products = webClient.GetProducts();
    return View("ProductList", products);
  }
  public ActionResult Details(string id)
  {
    var webClient = new RestSharpWebClient();
    var products = webClient.GetProductDetails(id);
    return View(products);
  }

现在让我们看看两种Create动作方法:HttpGetHttpPost。 第一个为我们提供一个输入屏幕,第二个使用HttpPost方法发布所有数据(输入值)。 在服务器端,您可以在IFormCollection参数中接收所有数据,还可以轻松地编写逻辑来获取ProductViewModel中的所有值。

  public IActionResult Create()
  {
    return View();
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public IActionResult Create(IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = Guid.NewGuid(),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid("77DD5B53-8439-49D5-9CBC-DC5314D6F190"),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.AddProduct(product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }

You can also write a HttpPost method that accepts a parameter of the type ProductViewModel.

下面的代码片段向我们展示了Edit动作方法的代码,它与Create动作方法相似,但它更新了现有数据,而不是插入新的数据:


  public ActionResult Edit(string id)
  {
    var webClient = new RestSharpWebClient();
    var product = webClient.GetProductDetails(id);
    return View(product);
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public ActionResult Edit(string id, IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = new Guid(collection["ProductId"]),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid(collection["CategoryId"]),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.UpdateProduct(id, product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }

动作方法用于从数据库或集合中删除特定的记录或数据。 HttpGet的操作方法Delete根据给定的 ID 获取一条记录,并显示准备修改的数据。 HttpPost的另一个Delete操作将修改后的数据发送到服务器进行进一步处理。 这意味着系统可以删除数据和记录。


  public ActionResult Delete(string id)
  {
    var webClient = new RestSharpWebClient();
    var product = webClient.GetProductDetails(id);
    return View(product);
  }
  [HttpPost]
  [ValidateAntiForgeryToken]
  public ActionResult Delete(string id, IFormCollection collection)
  {
    try
    {
      var product = new ProductViewModel
      {
        ProductId = new Guid(collection["ProductId"]),
        ProductName = collection["ProductName"],
        ProductDescription = collection["ProductDescription"],
        ProductImage = collection["ProductImage"],
        ProductPrice = Convert.ToDecimal(collection["ProductPrice"]),
        CategoryId = new Guid(collection["CategoryId"]),
        CategoryName = collection["CategoryName"],
        CategoryDescription = collection["CategoryDescription"]
      };
      var webClient = new RestSharpWebClient();
      var producresponse = webClient.DeleteProduct(id, product);
      if (producresponse)
      return RedirectToAction(nameof(Index));
      throw new Exception();
    }
    catch
    {
      return View();
    }
  }
}

现在让我们从Shared文件夹中打开_Layout.cshtml,添加以下一行,添加一个链接到我们新添加的ProductController:

<li><a asp-area="" asp-controller="Product" asp-action="Index">Web Client</a></li>

当你运行项目时,你应该看到一个名为 Web Client 的新菜单,如下面的截图所示:

<assets/0860e52c-5016-46d2-a6df-d0812d31377e.png>

我们现在准备看到一些结果。 单击 Web Client 菜单,您将看到以下屏幕:

<assets/1626a7e2-dcdc-4ad2-983c-4410f4e9784a.png>

在前面的屏幕中,您可以执行其他操作,以及调用和使用您的产品 api—即创建、编辑和删除。

总结

基于 rest 的服务的创建对于任何项目都是重要的,但是如果没有办法使用这些服务,那么它们就毫无用处。 在本章中,我们了解了如何将 RestSharp 支持添加到我们的 web 项目中,并使用我们预先开发的产品 api。 我们还创建了一个 web 客户端,它可以通过使用 ASP 在网页上呈现输出来使用 web 服务。 净的核心。

在下一章中,我们将讨论微服务的热门话题,下一个层次的服务分离。 我们将讨论微服务如何通信,它们的优势是什么,以及我们为什么需要它们。

十一、微服务简介

到目前为止,我们已经通过实践示例介绍了 RESTful api,并创建了小型应用。 在前一章中,我们开发了一个应用,并讨论了 RESTful API、安全性、测试、性能和部署。

本章简要介绍了微服务,它是我们 RESTful 服务之旅的下一站。 在本章中,我们将介绍微服务的基本组件,并使用一个正在转换为微服务的单一应用的例子。

我们将涵盖以下议题:

  • microservices 是什么?
  • 交流 microservices
  • Microservices 测试策略
  • 可伸缩性
  • ASP 中的微服务生态系统.NET Core

概述 microservices

简单地说,当将应用或模块划分为更小的、独立的服务时,其结果也称为微服务。 这些小部件也可以独立部署。

如果我们回顾历史,我们会发现微服务这个术语第一次被使用是在 2011 年的软件架构师研讨会上。 2012 年 3 月,詹姆斯·刘易斯(James Lewis)提出了他对“微服务”一词的一些看法。 到 2013 年底,IT 行业的各种团体已经开始讨论微服务,到 2014 年,微服务已经变得足够流行,被认为是大型企业的有力竞争者。

那么,到底什么是微服务呢? 有太多的定义,所以你可以根据自己对这个术语的理解或者你可能有什么样的用例和讨论来定义微服务。 让我们来看看官方网站对微服务的定义:(来源:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-overview-microservices)

“Microservice applications are composed of small, independently versioned, and scalable customer-focused services that communicate with each other over standard protocols with well-defined interfaces.”

Microservice 属性

在前一节中,我们看到微服务完全独立于系统中的其他服务,并在它们自己的进程中运行。 根据这个定义,有一些属性定义了微服务完全独立于其他组件。 让我们先看看核心属性是什么:

  • 独立功能:不要试图在单个微服务中实现太多。 相反,设计它只有一个原因,并做好它。 这意味着设计应该尽量避免依赖于功能的任何其他部分。 在我看来,这部分非常重要,因为它为其他属性奠定了基础。
  • 隔离的数据和状态:每个服务拥有自己的数据和状态。 它不与任何其他应用或部分共享所有权。
  • 独立部署:上述各点的累积效应。 这有助于您进行连续部署。
  • 技术采用:考虑了前两点后,这就容易多了,因为这对任何现有模块都不再有影响。 这里的美妙之处在于,你可以在两种不同的技术中拥有两种不同版本的微服务。 非常有益的。
  • 一致性和弹性:它必须是完美的。 如果您不能指望某个服务在预期的时间内返回,或者指望它总是可用,那么它的全部目的就不复存在了。

理解 microservice 架构

我们已经讨论了微服务体系结构是如何开发包含一组较小服务的单个应用的。 这些服务是独立的,运行在它们自己的进程中。

换句话说,我们可以说微服务是隔离我们的服务的一种方式,这样它们就可以为设计、开发、部署和升级的目的而彼此独立地处理。

微服务有很多好处,具体如下:

  • 较小的代码库:每个服务都较小,因此更容易作为一个单元进行开发和部署
  • 独立环境的易用性:随着服务的分离,所有的开发人员都独立工作,独立部署,没有人担心任何模块依赖

交流 microservices

在处理微服务体系结构时,仔细考虑消息传递机制的选择非常重要。 如果忽略了这一方面,那么就会破坏使用微服务体系结构进行设计的整个目的。

让我们继续讨论同步和异步消息传递以及不同的消息传递格式的选择。

同步消息传递

当系统期望从服务中得到及时的响应,并且系统等待该响应,直到从服务接收到响应为止,它被称为同步消息。 REST 是微服务体系结构中最受欢迎的选择之一。 它很简单,并且支持 HTTP 请求-响应,因此不需要寻找替代方案。 这也是大多数微服务实现使用 HTTP(基于 api 的样式)的原因之一。

异步消息传递

当系统没有立即期待来自服务的响应时,它可以在不阻塞调用的情况下继续处理,它被调用为异步消息

消息格式

在过去的几年里,使用 MVC 之类的东西让我迷上了 JSON 格式。 您还可以考虑 XML。 这两种格式都可以很好地处理带有 API 样式的 HTTP 资源。 如果需要使用二进制消息格式,也可以使用二进制消息格式。 我们在这里不推荐任何格式,您可以选择任何消息格式。

为什么我们应该使用微服务

人们已经探索了大量的模式和架构,其中一些受到欢迎,而另一些则在互联网流量之战中失利。 每个解决方案都有自己的优点和缺点,所以对于公司来说,快速响应可伸缩性、高性能和易于部署等基本需求变得越来越重要。 任何不具成本效益的单一方面都很容易对大型企业产生负面影响,从而决定企业是否盈利。 下图显示了选择微服务的优势:

<assets/d5c09102-7af7-4006-bdbf-567d945b720c.png>

这就是我们看到微服务拯救企业系统架构师的地方。 在这种架构风格的帮助下,他们可以确保自己的设计没有任何问题。 同样重要的是要考虑到这一目标是以具有成本效益和及时的方式实现的。

微服务架构是如何工作的

在前面的部分中,我们讨论了微服务体系结构,并试图对这个术语进行更深入的阐述。 现在,您可以看到微服务架构可能如何工作; 您可以根据自己的设计方法使用任何组合。 在使用微服务架构时,需要记住以下几点:

  • 它是为现代时代编写的程序,在这个时代我们应该遵循所有的 SOLID 原则。 它是面向对象编程(OOP)。
  • 这是向其他或外部组件公开功能的最佳方式,以便任何编程语言都可以使用这些功能,而不依赖于任何用户界面或服务(web 服务、API、REST 服务等)。
  • 整个系统是协同工作的,而不是相互联系和依赖的。
  • 每个组件负责自己的功能。
  • 它将代码。 分离的代码是可重用的。

microservices 的优点

以下是微服务的一些优势:

  • 您不需要投资来使整个应用具有可伸缩性。 对于购物车,我们可以简单地对产品搜索模块和订单处理模块进行负载平衡,而留下库存管理、订单取消和发货确认等不常用的操作服务。
  • 我们可以很容易地匹配一个组织的部门层次。 由于大型企业中有不同的部门赞助产品开发,这可能是一个巨大的优势。
  • 由于代码已经以一种不依赖于具有独立功能的其他模块的代码的方式完成,如果做得好,那么一个微服务中的更改影响另一个微服务的机会是非常小的。
  • 由于整个应用更像是一组相互隔离的生态系统——如果需要,我们可以一次部署一个微服务。 任何一个服务的故障都不需要导致整个系统瘫痪。
  • 你可以在一夜之间将一个微服务或一大堆微服务移植到不同的技术上,而你的用户甚至都不知道这一点。 不用说,你需要维护这些服务合同。
  • 这是暗含的,但这里有必要提醒一下。 确保异步调用被很好地使用,并且同步调用没有真正阻塞整个信息流。 良好地使用数据分区。 我们稍后会讲到这个,所以现在不用担心。
  • 在竞争激烈的环境中,这无疑是一个优势,因为如果您对新特性请求或系统中对新技术的采用反应缓慢,用户可能很快就会失去兴趣。

微服务体系结构的先决条件

在采用微服务架构达成一致后,明智的做法是具备以下先决条件:

  • 随着开发的快速周转,需求变得更加苛刻。 它要求您尽可能快地部署和测试。 如果只是少量的服务,那就不是问题。 然而,随着服务数量的增加,这可能很快就会挑战现有的基础设施和实践。 例如,您的 Q/A 和登台环境可能不再足以测试从开发团队返回的构建的数量。
  • 随着应用进入公共领域,用不了多久,开发与 Q/A 的古老脚本就会再次上演。 这次的不同之处在于,生意岌岌可危。 因此,您需要准备以自动方式快速响应,以便在需要时识别根本原因。
  • 随着微服务数量的增加,您将很快需要一种方法来监控整个系统的运行和健康状况,以防止任何可能的瓶颈或问题。 如果没有监视已部署微服务的状态和由此产生的业务功能的方法,任何团队都不可能采取主动的部署方法。

扩展

扩展是任何企业在试图迎合日益增长的用户基础时所面临的最大挑战之一。

可伸缩性就是系统/程序处理不断增长的工作的能力。 换句话说,可伸缩性是指系统/程序可伸缩的能力。

系统的可伸缩性是它处理不断增加的工作负载的能力。 我们可以使用两种主要的可伸缩性策略或类型来扩展应用。

垂直扩展

在垂直扩展中,我们分析现有的应用,以找出由于执行时间较长而导致应用变慢的模块的哪些部分。 提高代码的效率可能是一种策略,这样可以减少内存的消耗。 这种减少内存消耗的做法可以适用于特定模块,也可以适用于整个应用。 另一方面,由于此策略中涉及的明显挑战,我们可以向现有 IT 基础设施添加更多资源,比如升级 RAM、添加更多磁盘驱动器,等等,而不是更改应用。 这两种垂直扩展路径的收益都有一定的限制,即在特定的时间点之后,所产生的收益将趋于稳定。 记住这一点很重要; 这种扩展需要停机时间。

水平扩展

在水平扩展中,我们将深入研究那些对整体性能影响较大的模块。 我们考虑诸如高并发性等因素,以使我们的应用能够服务于增加的用户基础。 我们还将实现负载平衡来处理更大量的工作。 向集群中添加更多服务器的选项不需要停机时间,这无疑是一个优势。 具体情况可能会有所不同,所以我们需要检查在电力、许可证和冷却方面的额外成本是否值得。

DevOps 文化

在 DevOps 的帮助下,一个团队应该强调开发团队和另一个运营团队的协作。 我们应该建立一个系统,让开发、问答和基础设施团队协作。

自动化

基础设施设置可能是一项非常耗时的工作。 在基础设施准备就绪时,开发人员可以空闲。 在加入团队并做出贡献之前,他或她将等待一段时间。 基础设施设置的过程不应该阻止开发人员变得高效,因为它会降低整体生产力。 这应该是一个自动化的过程。 通过使用 Chef 或 PowerShell,我们可以轻松地创建虚拟机,并在需要时快速增加开发人员数量。 这样,我们的开发人员就可以从加入团队的第一天就开始工作。

测试

测试是任何应用的关键任务,而在使用微服务时则更为复杂。 我们必须将测试方法划分如下:

  • 采用 TDD 时,开发人员需要测试他或她自己的代码。 测试只是验证功能是否按预期工作的另一段代码。 如果发现任何功能不能满足测试代码,相应的单元测试将失败。 该功能可以很容易地修复,因为它知道问题在哪里。 为了实现这一点,我们可以利用 MSTest 或单元测试等框架。
  • Q/A 团队可以使用脚本来自动化他们的任务。 他们可以利用 QTP 或 Selenium 框架来创建脚本。

部署

部署是一个巨大的挑战。 为了克服这个问题,我们可以引入 CI。 在这个过程中,我们需要设置一个 CI 服务器。 随着 CI 的引入,整个过程现在是自动化的。 任何团队成员只要使用版本控制 TFS 或 Git 检入代码,CI 过程就会开始工作。 它确保生成新代码,并在集成测试的同时运行单元测试。 在这两个场景中,不管是成功的构建还是其他情况,团队都会收到关于结果的警告。 这使得团队能够快速响应问题。

接下来,我们有连续部署。 在这里,我们介绍各种环境,即开发环境、登台环境、Q/ a 环境,等等。 现在,只要任何团队成员检入代码,持续集成就开始发挥作用。 它调用单元/集成测试套件,构建系统,并将其推出到我们已经设置的各种环境中。 这样,开发团队为 Q/ a 提供合适的构建的周转时间就减少了。

ASP 中的微服务生态系统.NET Core

每当我想到 ASP 中的微服务生态系统.NET Core 系统我考虑了各种小 api、异步编程、回调、事件触发等等。 事实上,生态系统要大得多,而且不知何故也更复杂。

我们已经讨论过,微服务体系结构风格是一种为大型应用创建小型和独立单元的方式。 如果不使用各种工具和实用程序,这是不可能的。

下图是一个典型的微服务架构风格的示意图,描述了不同的客户端对不同服务的请求以及这个请求是如何验证的:

<assets/7d146cdd-d566-4abe-8651-be846b45fae5.png>

一个典型的微服务生态系统由以下组件组成,您将在 ASP 中了解这些组件.NET Core。

Azure 服务架构——微服务平台

平台是任何生态系统的必备组件。 它支持系统,工作顺利,并产生预期的结果。 Azure Service Fabric 只是微软提供的一个平台,它在微服务生态系统中非常受欢迎。 它提供容器部署和业务流程。

官方文件请参见:https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-overview

“Azure Service Fabric is a distributed systems platform that makes it easy to package, deploy, and manage scalable and reliable microservices and containers.”

无状态和有状态服务——一种服务编程模型

健壮的服务编程模型是微服务生态系统的支柱。 一个人应该知道他/她应该使用什么样的服务模型根据他/她的需求:

  • Stateless:服务在客户端请求之间不保持任何状态。 也就是说,服务不知道,也不关心后续的请求是否来自已经发出/没有发出前一个请求的客户端。 当我们有外部数据存储时,这是最好的服务编程模型。 我们的服务可以基于与外部数据库存储上的数据进行交互和持久化的无状态服务编程模型。
  • 有状态:服务保持一种可变状态,主动处理或保留特定于服务所要处理的任务的状态数据。

通信——服务之间交换数据的一种方式

如果微服务都是关于服务的,那么服务之间的通信应该是健壮的。 通信是服务之间交换数据的一种方式。 服务使用 Rest API(即 HTTP 调用请求/响应)进行通信,它们本质上是同步的。

当服务彼此通信时,它们实际上是在交换数据,这也称为服务之间的消息传递。 在处理微服务体系结构时,仔细考虑消息传递机制的选择是非常重要的。 如果忽略了这一方面,那么就会危及使用微服务体系结构进行设计的整个目的。 在单片应用中,这不是一个问题,因为组件的业务功能是通过函数调用调用的。 另一方面,这是通过松散耦合的 web 服务级消息传递实现的,其中服务主要基于 SOAP。 微服务消息传递机制应该是简单和轻量级的。

在微服务体系结构的各种框架或协议之间进行选择,没有固定的规则。 然而,这里有几点值得考虑。 首先,它的实现应该足够简单,而不会给系统增加任何复杂性。 其次,它应该足够轻,记住微服务体系结构可能严重依赖于服务间消息传递这一事实。 让我们继续讨论同步和异步消息传递以及不同的消息传递格式的选择。

总结

微服务体系结构风格提供了某些好处。 它使开发变得又快又容易。 它允许 DevOps (CI 和 CD)团队在地理位置上分离,平稳地工作并同步。 应用被划分为小的服务组件或部件,因此维护很容易。 这允许开发团队让业务发起人选择首先响应什么行业趋势。 这将带来成本效益、更好的业务响应、及时的技术采用、有效的扩展和消除对人类的依赖。

在本章中,你已经了解了 ASP.NET 中典型的微服务体系结构风格和微服务生态系统。

现在,我建议你阅读以下关于微服务的内容,以提高你的技能:

【论文复现】一种基于价格弹性矩阵的居民峰谷分时电价激励策略【需求响应】(Matlab代码实现)内容概要:本文介绍了一种基于价格弹性矩阵的居民峰谷分时电价激励策略,旨在通过需求响应机制优化电力系统的负荷分布。该研究利用Matlab进行代码实现,构建了居民用电行为与电价变动之间的价格弹性模型,通过分析不同时间段电价调整对用户用电习惯的影响,设计合理的峰谷电价方案,引导用户错峰用电,从而实现电网负荷的削峰填谷,提升电力系统运行效率与稳定性。文中详细阐述了价格弹性矩阵的构建方法、优化目标函数的设计以及求解算法的实现过程,并通过仿真验证了所提策略的有效性。; 适合人群:具备一定电力系统基础知识和Matlab编程能力,从事需求响应、电价机制研究或智能电网优化等相关领域的科研人员及研究生。; 使用场景及目标:①研究居民用电行为对电价变化的响应特性;②设计并仿真基于价格弹性矩阵的峰谷分时电价激励策略;③实现需求响应下的电力负荷优化调度;④为电力公司制定科学合理的电价政策提供理论支持和技术工具。; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,深入理解价格弹性建模与优化求解过程,同时可参考文中方法拓展至其他需求响应场景,如工业用户、商业楼宇等,进一步提升研究的广度与深度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值