[翻译] 使用 Serverless 和 .NET Core 构建飞速发展的架构

作者:Samuele Resca

Serverless 技术为开发人员提供了一种快速而独立的方式将实现投入生产。这种技术在企业的技术栈中日益流行,自 2017 年以来,它一直是 ThoughtWorks 技术雷达的实验级别的技术[译注:技术雷达是 ThoughtWorks 每半年发布的前沿技术解析]。

本篇文章的第一部分介绍了有关 Serverless 计算的基本概念。第二部分展示了如何构建 .NET Core 的 Lambda 函数,其中使用了 AWS 的 Serverless 框架。

Serverless 计算的好处

Serverless 技术是 FaaS(功能即服务)技术体系的一部分。随着云计算的采用,这些技术变得越来越受欢迎。如今,serverless 实现被提升为云计算提供商的首选技术,无论是私有云还是公有云。

此外,典型的软件服务和系统会通过在内存中保留大量数据并在复杂数据源中写入成批数据来完成操作。
然而一般而言,像 Serverless 一样的 FaaS 技术旨在通过尽可能快地处理许多小请求和事件,来使我们的系统保持快速响应。Serverless 组件通常与运行它们的云服务商所提供的事件紧密耦合:一个通知、一个队列调度的事件或者一个来自 API 网关的请求,都被视为此组件中包含的一小部分计算的触发器。这也就是云服务商的定价系统基于请求数而不是基于计算时间的主要原因。

再者,serverless 组件通常在执行时间上有一些限制。与每种技术一样,serverless 并不适合每一个解决方案和系统。但是事实上,它确实简化了软件工程师的一些工作,lambda 部署周期通常很快,开发人员只需要做少量工作就可以快速将新功能投入生产。此外,使用 serverless 技术构建组件意味着开发人员无需担心扩展问题或故障,让云提供商去关心这些问题吧。

最后,我们还应该知道 serverless 函数是无状态的。因此,基于此技术构建的每个系统都更加模块化和松耦合。

Serverless 的痛点

但是这种能力和敏捷性却不是没有代价的。首先,serverless 函数是在云上执行的,它们通常由与云提供商紧密耦合的事件触发,因此调试它们并不容易。这就是为什么要使它的作用域保持尽可能小,并且始终将函数的核心逻辑与外部组件和事件分隔开的原因。此外,用单元测试和集成测试覆盖 serverless 代码非常重要。

其次,就像微服务架构一样,它具有大量的服务,但是关注的范围很小,因此很难对 serverless 的组件进行监控,某些问题也很难检测。总之,很难对不同的 serverless 组件之间的体系结构和依赖性有一个全面的认识。因此,云提服务商和第三方公司都在提供监控和系统分析功能的一体式工具上投入了大量资金。

体验一下 serverless 计算

现如今,根据业务需求快速进化的架构以往任何时候都更为重要。数据驱动的体验是这个过程的一部分。此外,在发布新功能之前,我们应该实现MVP(译注:最小可行化产品)并在部分客户群上测试它。如果实验结果是肯定的,则值得在MVP上进行投资,以将其转化为我们产品的功能。

是的,serverless 计算提供了这样一种方法,可以在不考虑基础设施的情况下快速进化我们的架构。Serverless 轻量级开销提供了一种实现一次性 MVP 的方法,用于试验新功能和新特性。此外,它们还可以很容易地启动和关闭。

使用 .NET Core 来实现 AWS Lambda 函数

这一节将介绍使用 .NET Core 的一些 AWS Lambdas 的简单实现。该例子涉及三个关键技术:

  • AWS 是承载我们 serverless 功能的云服务商;

  • serverless 框架,它是将 Lambdas 放入 AWS 的非常有用的工具。作为一个通用的框架,它兼容所有主要的云服务商;

  • .NET Core 是微软提供的开源的、跨平台的框架;

我们将要讨论的示例也放在了 GitHub 上,URL 如下: serverless/examples/aws-dotnet-rest-api-with-dynamodb。该示例是 serverless 框架提供的一些模板项目的一部分。

AWS Lambda 项目遵循以下功能架构:

总结一下,该功能实现了对数据的一些读取/写入操作。客户端通过API网关发出HTTP请求,lambda 项目定义了三个函数:GetItem、InsertItem 和 UpdateItem。它们都对 DynamoDB 表进行操作。

项目结构

我们将要实现的解决方案具有以下项目结构:

  • src/DotNetServerless.Application 该项目包含了由 Serverless 执行的核心逻辑;

  • src/DotNetServerless.Lambda 该项目包含了 Serverless 函数的入口点以及所有与 AWS 紧密耦合的组件;

  • tests/DotNetServerless.Tests 该项目包含了 Serverless 功能的单元测试和集成测试;

领域项目

让我们从 application 层开始分析。项目的核心实体是 Item 类,它表示 DynamoDB(译注:AWS的一种数据库) 表中存储的实体:

using Amazon.DynamoDBv2.DataModel;

namespace DotNetServerless.Application.Entity
{
public class Item
{
[DynamoDBHashKey]
public string Id { get; set; }
[DynamoDBRangeKey]
public string Code { get; set; }
[DynamoDBProperty]
public string Description { get; set; }
[DynamoDBProperty]
public bool IsChecked { get; set; }
}
}

实体的字段使用了一些特性进行修饰,以便使用 DynamoDb 存储模型映射它们。Item 实体被 IItemsRepository 接口引用,该接口定义用于存储数据的操作:

using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Amazon.DynamoDBv2;
using Amazon.DynamoDBv2.DataModel;
using Amazon.DynamoDBv2.DocumentModel;
using DotNetServerless.Application.Entities;
using DotNetServerless.Application.Infrastructure.Configs;

namespace DotNetServerless.Application.Infrastructure.Repositories
{
public interface IItemRepository
{
Task<IEnumerable<T>> GetById<T>(string id, CancellationToken cancellationToken);

Task Save(Item item, CancellationToken cancellationToken);
}

public class ItemDynamoRepository : IItemRepository
{
private readonly AmazonDynamoDBClient _client;
private readonly DynamoDBOperationConfig _configuration;

public ItemDynamoRepository(DynamoDbConfiguration configuration,
IAwsClientFactory<AmazonDynamoDBClient> clientFactory)
{
_client = clientFactory.GetAwsClient();
_configuration = new DynamoDBOperationConfig
{
OverrideTableName = configuration.TableName,
SkipVersionCheck = true
};
}

public async Task Save(Item item, CancellationToken cancellationToken)
{
using (var context = new DynamoDBContext(_client))
{
await context.SaveAsync(item, _configuration, cancellationToken);
}
}

public async Task<IEnumerable<T>> GetById<T>(string id, CancellationToken cancellationToken)
{
var resultList = new List<T>();
using (var context = new DynamoDBContext(_client))
{
var scanCondition = new ScanCondition(nameof(Item.Id), ScanOperator.Equal, id);
var search = context.ScanAsync<T>(new[] {scanCondition}, _configuration);

while (!search.IsDone)
{
var entities = await search.GetNextSetAsync(cancellationToken);
resultList.AddRange(entities);
}
}

return resultList;
}
}
}

IItemRepository 的实现定义了两个基本操作:

  • Save,允许调用者插入和更新实体;

  • GetById,根据 ID 返回对象;

最后,DotNetServerless.Application 的顶层是 Handler 部分。并且
,整个 application 项目都基于中介模式,以保证 AWS 函数和核心逻辑之间的松散耦合。让我们以创建项目处理程序的定义为例:

using System;
using System.Threading;
using System.Threading.Tasks;
using DotNetServerless.Application.Entities;
using DotNetServerless.Application.Infrastructure.Repositories;
using DotNetServerless.Application.Requests;
using MediatR;

namespace DotNetServerless.Application.Handlers
{
public class CreateItemHandler : IRequestHandler<CreateItemRequest, Item>
{
private readonly IItemRepository _itemRepository;

public CreateItemHandler(IItemRepository itemRepository)
{
_itemRepository = itemRepository;
}

public async Task<Item> Handle(CreateItemRequest request, CancellationToken cancellationToken)
{
var item = request.Map();
item.Id = Guid.NewGuid().ToString();

await _itemRepository.Save(item, cancellationToken);

return item;
}
}
}

如您所见,代码非常简单。CreateItemHandler 实现了 IRequestHandler,它使用内置的依赖注入来解析 IItemRepository 接口。处理程序的 Handler 方法仅将传入的请求与Item实体映射,并调用IItemRepository接口提供的Save方法。

函数项目

函数项目包含 lambda 功能的入口点。它定义了三个函数类,它们表示 AWS 的 lambda:CreateItemFunction, GetItemFunction 和 UpdateItemFunction; 稍后我们将看到,每个函数都将使用 API 网关的特定路由进行映射。

让我们以 CreateItem 函数为例,对函数定义进行一些深入探讨:

using System;
using System.Threading.Tasks;
using Amazon.Lambda.APIGatewayEvents;
using Amazon.Lambda.Core;
using DotNetServerless.Application.Requests;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json;

namespace DotNetServerless.Lambda.Functions
{
public class CreateItemFunction
{
private readonly IServiceProvider _serviceProvider;

public CreateItemFunction() : this(Startup
.BuildContainer()
.BuildServiceProvider())
{
}

public CreateItemFunction(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}

[LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
public async Task<APIGatewayProxyResponse> Run(APIGatewayProxyRequest request)
{
var requestModel = JsonConvert.DeserializeObject<CreateItemRequest>(request.Body);
var mediator = _serviceProvider.GetService<IMediator>();

var result = await mediator.Send(requestModel);

return new APIGatewayProxyResponse { StatusCode = 201, Body = JsonConvert.SerializeObject(result)};
}
}
}

上面提到的代码定义了函数的入口点。首先,它声明一个构造函数,并使用Startup类公开的BuildContainer和BuildServiceProvider方法。稍后我们将看到,这些方法是为初始化依赖项注入容器而提供的。CreateItem 函数的 Run 方法使用 Lambda 序列器属性进行修饰,这意味着它是函数的入口点。此外,运行函数使用 APIGatewayProxyRequest 请求和 APIGatewayProxyReposne 作为 lambda 计算的输入和输出。

依赖注入

该项目使用了 .NET Core 内置的依赖注入。Startup 类定义了 BuildContainer 静态方法,该方法返回一个新的 ServiceCollection,其中包含实体之间的依赖关系映射:

using System.IO;
using DotNetServerless.Application.Infrastructure;
using DotNetServerless.Application.Infrastructure.Configs;
using DotNetServerless.Application.Infrastructure.Repositories;
using DotNetServerless.Lambda.Extensions;
using MediatR;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace DotNetServerless.Lambda
{
public class Startup
{
public static IServiceCollection BuildContainer()
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddEnvironmentVariables()
.Build();

return ConfigureServices(configuration);
}


private static IServiceCollection ConfigureServices(IConfigurationRoot configurationRoot)
{
var services = new ServiceCollection();

services
.AddMediatR()
.AddTransient(typeof(IAwsClientFactory<>), typeof(AwsClientFactory<>))
.AddTransient<IItemRepository, ItemDynamoRepository>()
.BindAndConfigure(configurationRoot.GetSection("DynamoDbConfiguration"), new DynamoDbConfiguration())
.BindAndConfigure(configurationRoot.GetSection("AwsBasicConfiguration"), new AwsBasicConfiguration());

return services;
}
}
}

Startup使用ConfigureServices初始化新的ServiceCollection并与其一起解决依赖关系。此外,它还使用 BindAndConfigure 方法创建一些配置对象。BuildContainer方法将由函数调用,以解决依赖项。

测试我们的代码

如前所述,测试一下我们的代码,对于持续集成和交付是非常重要的,尤其是在lambda项目中。在这种情况下,测试将覆盖 IMediator 接口和处理程序之间的集成。此外,它们还覆盖了依赖项注入部分。让我们看看 CreateItemFunctionTests 的实现:

using System.Threading;
using System.Threading.Tasks;
using Amazon.Lambda.APIGatewayEvents;
using DotNetServerless.Application.Entities;
using DotNetServerless.Application.Infrastructure.Repositories;
using DotNetServerless.Application.Requests;
using DotNetServerless.Lambda;
using DotNetServerless.Lambda.Functions;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Moq;
using Newtonsoft.Json;
using Xunit;

namespace DotNetServerless.Tests.Functions
{
public class CreateItemFunctionTests
{
public CreateItemFunctionTests()
{
_mockRepository = new Mock<IItemRepository>();
_mockRepository.Setup(_ => _.Save(It.IsAny<Item>(), It.IsAny<CancellationToken>())).Returns(Task.CompletedTask);

var serviceCollection = Startup.BuildContainer();

serviceCollection.Replace(new ServiceDescriptor(typeof(IItemRepository), _ => _mockRepository.Object,
ServiceLifetime.Transient));

_sut = new CreateItemFunction(serviceCollection.BuildServiceProvider());
}

private readonly CreateItemFunction _sut;
private readonly Mock<IItemRepository> _mockRepository;

[Fact]
public async Task run_should_trigger_mediator_handler_and_repository()
{
await _sut.Run(new APIGatewayProxyRequest {Body = JsonConvert.SerializeObject(new CreateItemRequest())});
_mockRepository.Verify(_ => _.Save(It.IsAny<Item>(), It.IsAny<CancellationToken>()), Times.Once);
}

[Theory]
[InlineData(201)]
public async Task run_should_return_201_created(int statusCode)
{
var result = await _sut.Run(new APIGatewayProxyRequest {Body = JsonConvert.SerializeObject(new CreateItemRequest())});
Assert.Equal(result.StatusCode, statusCode);
}
}
}

如您所见,上述代码执行了我们的函数,并且对已注入的依赖项执行一些验证,并验证 IItemRepository 公开的 Save 方法是否被调用。因为一些原因,测试类并没有覆盖 DynamoDb 的特性。此外,当我们将复杂的实体和操作结合在一起时,可以使用 Docker 容器通过一些集成测试来覆盖数据库部分。对了,提到 .NET Core 和 AWS 的话题,.NET AWS 团队有一个很好的工具来改进 lambda 的测试:LambdaTestTool

部署项目

让我们来看看如何将项目导入AWS。为此,我们将使用 serverless 框架。该框架的定义是:

serverless 框架是一个 CLI 工具,允许用户构建和部署自动缩放、按执行付费、事件驱动的函数。

为了把 serverless 添加我们的项目,我们应该执行以下命令:

npm install serverless --save-dev

定义基础架构

默认情况下,基础架构的定义将放在 serverless.yml 文件中。该文件看起来像这样:

service: ${file(env.configs.yml):feature}

frameworkVersion: ">=1.6.0 <2.1.0"

provider:
name: aws
stackName: ${file(env.configs.yml):feature}-${file(env.configs.yml):environment}
runtime: dotnetcore2.1
region: ${file(env.configs.yml):region}
accountId: ${file(env.configs.yml):accountId}
environment:
DynamoDbConfiguration__TableName: ${file(env.configs.yml):dynamoTable}

iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/${self:provider.environment.DynamoDbConfiguration__TableName}"

package:
artifact: bin/release/netcoreapp2.1/deploy-package.zip

functions:
create:
handler: DotNetServerless.Lambda::DotNetServerless.Lambda.Functions.CreateItemFunction::Run
events:
- http:
path: items
method: post
cors: true

get:
handler: DotNetServerless.Lambda::DotNetServerless.Lambda.Functions.GetItemFunction::Run
events:
- http:
path: items/{id}
method: get
cors: true

update:
handler: DotNetServerless.Lambda::DotNetServerless.Lambda.Functions.UpdateItemFunction::Run
events:
- http:
path: items
method: put
cors: true

resources:
Resources:
ItemsDynamoDbTable:
Type: 'AWS::DynamoDB::Table'
DeletionPolicy: Retain
Properties:
AttributeDefinitions:
- AttributeName: Id
AttributeType: S
- AttributeName: Code
AttributeType: S
KeySchema:
- AttributeName: Id
KeyType: HASH
- AttributeName: Code
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: ${self:provider.environment.DynamoDbConfiguration__TableName}

以上代码使用 AWS 的 cloud formation 对基础架构进行一些操作。provider 节点定义了有关 lambda 的某些信息,例如堆栈名称、运行时以及有关AWS账户的一些信息。此外,它还描述了 lambda 的角色和授权,例如,应该允许 lambda 对 DynamoDb 表执行操作。functions 节点定义了不同的 lambda 函数,并将其与特定的 HTTP 路径进行映射。最后,resources 节点用于设置 DynamoDB 表模式。

配置文件

serverless.yml 定义通常与另一个 YAML 文件结合使用,该文件仅定义与环境相关的配置。例如,DynamoDbConfiguration__TableName 节点就是这种情况,该节点使用以下语法从另一个 YAML 文件获取信息:${file(env.configs.yml):dynamoTable}。以下代码段显示了 env.config.yml 文件的一个示例:

feature: <feature_name>
version: 1.0.0.0
region: <aws_region>
environment: <environment>
accountId: <aws_account_id>
dynamoTable: <dynamo_table_name>

最后的想法

这篇文章涵盖了一些关于 serverless 计算的理论主题,以及 .Net Core 实现 lambda 函数的例子。着重讲解了如何使用 serverless 计算来快速推进我们的架构。此外,勇于尝试是一个不断发展的产品很关键的一方面,它对于快速适应业务的变化是很重要的。

最后,您可以在以下存储库中找到一些 serverless 的 lambda 示例。
serverless/examples/aws-dotnet-rest-api-with-dynamodb

原文链接:https://www.cnblogs.com/Rwing/p/fast-growing-architectures-with-serverless-and-net-core.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/312814.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

经典排序算法(7)——堆排序算法详解

堆排序&#xff08;Heap sort&#xff09;是指利用堆&#xff08;最大堆、最小堆&#xff09;这种数据结构所设计的一种排序算法。堆是一个完全二叉树的结构&#xff0c;并同时满足如下性质&#xff1a;即子结点的键值或索引总是小于&#xff08;或者大于&#xff09;它的父节点…

经典排序算法(8)——归并排序算法详解

归并排序&#xff08;Merge sort&#xff09;&#xff0c;是创建在归并操作上的一种有效的排序算法&#xff0c;效率为O(nlog n)。该算法是采用分治法&#xff08;Divide and Conquer&#xff09;的一个非常典型的应用&#xff0c;且各层分治递归可以同时进行。 一、算法基本思…

祝贺王远当选为中国区第二位 Teams MVP

今天一上班就传来喜讯&#xff0c;Microsoft Teams 大中华区技术社区专家委员会成员之一的王远成功当选了2020-2021年度微软最有价值专家&#xff08;MVP)&#xff0c;这是对他在基于Office 365的音视频会议系统&#xff08;尤其是在Microsoft Teams&#xff09;方面的深入研究…

经典排序算法(9)——桶排序算法详解

桶排序&#xff08;Bucket sort&#xff09;或所谓的箱排序&#xff0c;并不是比较排序&#xff0c;它不受到 O(nlogn) 下限的影响。 一、算法基本思想 &#xff08;1&#xff09;基本思想 桶排序工作的原理是将数组分到有限数量的桶子里&#xff0c;每个桶子再个别排序&#x…

[原]排错实战——使用process explorer替换任务管理器

前言 一般&#xff0c;我们会使用任务管理器查看系统中有哪些进程在运行&#xff0c;强制杀掉某个进程。可是系统自带的任务管理器功能有限&#xff0c;process explorer是一个功能更强大的工具。它可以让我们查看更多更详细的信息&#xff08; 比如查看某个进程的父进程&#…

ABP vNext中使用开源日志面板 LogDashboard

ABP vNext 使用 logdashboard本文示例源码&#xff1a;https://github.com/liangshiw/LogDashboard/tree/master/samples/abpvnextABPABP是aspnetcore3.0的开源web应用程序框架&#xff0c;非常适合现代web应用程序。有关ABP的更多内容可以查看官方文档Logdashboard可以直接在基…

经典排序算法(10)——基数排序算法详解

基数排序&#xff08;Radix sort&#xff09;是一种非比较型整数排序算法&#xff0c;其原理是将整数按位数切割成不同的数字&#xff0c;然后按每个位数分别比较。 一、算法基本思想 &#xff08;1&#xff09;基本思想 基数排序是基于桶排序来实现。通过键值的部分信息&#…

经典排序算法(11)——计数排序算法详解

计数排序&#xff08;Counting sort&#xff09;是一个非基于比较的排序算法&#xff0c;该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时&#xff0c;它的复杂度为Ο(nk)&#xff08;其中k是整数的范围&#xff09;&#xff0c;快于任何比较…

GitHub Actions,卧槽!牛批!

“ 阅读本文大概需要 19 分钟。 ”前段时间我更新了我的分布式爬虫管理框架—— Gerapy&#xff08;话都说到这儿了打个广告&#xff0c;跟繁琐的命令行说拜拜&#xff01;Gerapy分布式爬虫管理框架来袭&#xff01;&#xff0c;哇&#xff0c;哇&#xff0c;就是&#xff0c;哇…

平衡二叉树AVL详解

一、平衡二叉树的定义 平衡二叉树&#xff08;Balanced Binary Tree&#xff09;又被称为AVL树&#xff0c;它且具有以下性质&#xff1a; &#xff08;1&#xff09;它是一棵空树或它的左右两个子树的高度差的绝对值不超过1&#xff1b; &#xff08;2&#xff09;并且左右两个…

【壹个小技巧】一看就会的CI/CD :Github Actions

什么是 CI/CD?我这里先不说概念&#xff0c;先说一个平时开发的场景问题&#xff1a;我们平时开发一个项目&#xff0c;经常会遇到这些“小”问题&#xff1a;就是如何保证自己的项目是正确的&#xff0c;至少拿给别人的时候&#xff0c;可以编译运行的&#xff1f;或者说多人…

Hyper-V虚拟机自动添加检查点和导出备份

背景说明笔者使用Hyper-V在内部搭建了大量的环境和系统&#xff0c;比如&#xff1a;k8s内部集群Azure Devops Server(TFS>VSTS>Azure Devops Server)SharePoint…大部分基本上都是用于内部研究、测试等场景&#xff0c;但是为了避免很多麻烦&#xff0c;必要的备份还是必…

哈夫曼树详解

一、哈夫曼树的定义 &#xff08;1&#xff09;简单路径长度 所谓树的简单路径长度&#xff0c;是指从树的跟节点到每个节点的路径长度之和。 完全二叉树是简单路径长度更小的二叉树。 &#xff08;2&#xff09;加权路径长度 所谓树的加权路径长度&#xff0c;是指树中所以带…

深入理解.NET Core的基元(三) - 深入理解runtimeconfig.json

原文&#xff1a;Deep-dive into .NET Core primitives, part 3: runtimeconfig.json in depth作者&#xff1a;Nate McMaster[1]译文&#xff1a;深入理解.NET Core 的基元&#xff08;三&#xff09; - 深入 runtimeconfig.json作者&#xff1a;Lamond Lu前情回顾深入理解.NE…

B-树、B+树、B*树详解

一、B树 B树是一种多路搜索树&#xff08;并不是二叉的&#xff09;&#xff0c;性质如下&#xff1a; 1、定义任意非叶子结点最多只有M个儿子&#xff1b;且M>2&#xff1b; 2、根结点的儿子数为[2, M]&#xff1b; 3、除根结点以外的非叶子结点的儿子数为[M/2, M]&#…

微软正在开发基于Rust的安全编程语言

此前&#xff0c;微软表示正探索将 Rust 作为 C 和 C 的安全替代方案&#xff0c;并且也对外展示了使用 Rust 重写 Windows 组件的体验。根据微软的说法&#xff0c;Rust 是一种从根本上考虑安全性的编程语言&#xff0c;他们将尝试使用 Rust 重写各种产品&#xff0c;因为在过…

堆树(最大堆、最小堆)详解

一、堆树的定义 堆树的定义如下&#xff1a; &#xff08;1&#xff09;堆树是一颗完全二叉树&#xff1b; &#xff08;2&#xff09;堆树中某个节点的值总是不大于或不小于其孩子节点的值&#xff1b; &#xff08;3&#xff09;堆树中每个节点的子树都是堆树。 当父节点的键…

HttpClientFactory日志不好用,自己扩展一个?

前言.NetCore2.1新推出HttpClientFactory工厂类&#xff0c; 替代了早期的HttpClient&#xff0c;并新增了弹性Http调用机制 (集成Policy组件)。替换的初衷还是简单说下&#xff1a;① using(var client new HttpClient()) 调用Dispose()方法&#xff0c;并不会很快释放底层So…

树、二叉树简介

一、树的定义 树是由n&#xff08;n>1&#xff09;个有限节点组成一个具有层次关系的集合&#xff0c;它有如下特点&#xff1a; 1、每个节点有零个或多个子节点&#xff1b; 2、没有父节点的节点称为根节点&#xff1b; 3、每一个非根节点有且只有一个父节点&#xff1b; 4…

Quartz.net定时任务的使用及获取正在运行的JOB

定时任务管理类实现了如下功能&#xff1a;1、对定时任务进行管理 2、创建定时任务&#xff0c;需要给定时任务一个job的名称 3、判断给定的job的任务是否已存在 4、停止定时任务的功能namespace MyUKD.Quartz{ public class QuartzSchedulerMgr { private static readonly ILo…