【.net core】电商平台升级之微服务架构应用实战

一、前言

这篇文章本来是继续分享IdentityServer4 的相关文章,由于之前有博友问我关于微服务相关的问题,我就先跳过IdentityServer4的分享,进行微服务相关的技术学习和分享。微服务在我的分享目录里面是放到四月份开始系列文章分享的,这里就先穿越下,提前安排微服务应用的开篇文章 电商系统升级之微服务架构的应用。本博客以及公众号坚持以架构的思维来分享技术,不仅仅是单纯的分享怎么使用的Demo

二、场景

先来回顾下我上篇文章 Asp.Net Core 中IdentityServer4 授权中心之应用实战 中,电商架构由单体式架构拆分升级到多网关架构

升级之前

升级之后:

然而升级之后问题又来了,由于之前增加了代理商业务并且把授权中心支付网关单独拆出来了,这使得公司的业务订单量翻了几十倍,这个时候整个电商系统达到了瓶颈,如果再不找解决方案系统又得宕机了。

2.1 问题及解决方案

经过技术的调研及问题分析,导致这个瓶颈的问题主要有以下几个原因,只需要把下面问题解决就可以得到很大的性能提升

  • 每天的订单量暴增,导致订单数据太大,然而整个电商系统数据存储在一个数据库中,并且是单表单数据库(未进行读写分离),以致于订单数据持续暴增。

  • 相关业务需要依赖订单查询,订单数据查询慢以至于拖垮数据库

  • 整个电商系统连接数达到瓶颈(已经分布式部署,在多加服务器会损耗更多的经费而达不到最佳性价比)

为了一劳永逸的解决以上问题,经过技术的调研,决定对订单业务做如下升级改造:

  • 拆分独立的订单微服务(本章节着重分享)

  • 使用ES进行数据迁移(按年进行划分,并且进行读写分离,这里就不着重讲,下次来跟大家一起学习和分享)

  • 增加分布式缓存 (也不是本次的重点,后续再来跟大家学习和分享)

经过升级后的架构图如下:

架构图说明:

  • 右边同一颜色的代表还是原先电商系统的单体式架构,为拆分的单体架构业务,其中在业务处理上夹杂了一层分布式缓存的处理

  • 左边的是微服务的架构,是这次升级拆分后的架构,其中数据库也已经从原有的数据库拆分并且数据迁移到了ES集群中,并进行了读写分离。

  • 订单服务可以随意扩容成分布式服务,通过一些工具动态扩展服务及服务器的支持。

  • 右边的业务后续也可以进行拆分,拆分成不同的业务服务。

  • 后续升级还可以考虑消息队列等相关方面,架构图中未构思(后续再来分享升级用到的相关技术,这里还是回归到本文的核心微服务

三、微服务概述

微服务的相关概念我就不多说了,以下就先简单概况下微服务带来的利和弊。

3.1 微服务的优势

  • 使大型的复杂应用程序可以持续交付和持续部署:持续交付和持续部署是DevOps的一部分,DevOps是一套快速、频繁、可靠的软件交付实践。高效的DevOps组织通常将软件部署到生产环境时面临更少的问题和故障。DevOps工具有DockerKubernetsJenkinsGit等。

  • 每个服务相对较小并容易维护:微服务架构相比单体应用要小的多,开发者理解服务中的逻辑代码更容易。代码库小,打包,启动服务速度也快。

  • 服务可以独立部署:每个服务都可以独立于其他服务进行部署

  • 服务可以独立扩展:服务可以独立扩展,不论是采用X轴扩展的实例克隆,还是Z轴的流量分区方式。此外每个服务都可以部署到适合它们需求的硬件之上

  • 微服务架构可以实现团队的自治:可以根据服务来把开发团队拆分。每个团队都有自己负责的微服务,而不用关心不属于他们负责的服务。

  • 更容易实验和采纳新的技术:最后,微服务可以消除对某个技术栈的长期依赖。因为服务更小,使用更换的编程语言和技术来重写一项服务变得有可能,这也意味着,对一项新技术尝试失败后,可以直接丢弃这部分工作而不至于给整个应用带来失败的风险。

  • 更好的容错性:微服务架构也可以实现更换的故障隔离。例如,某个服务引发的致命错误,不会影响其他服务。其他服务仍然正常运行。

  • 服务可以独立扩容:对于整个架构来说,可以随意选择相关业务进行扩容和负载,通过相关技术工具动态进行随意扩容

3.2 微服务的劣势

  • 服务拆分和定义是一项挑战:采用微服务架构首当其冲的问题,就是根本没有一个具体的、良好定义的算法可以完成服务的拆分工作。与软件开发一样,服务的拆分和定义更像一门艺术。更糟糕的是,如果对系统的服务拆分出现了偏差,很有可能会构建出一个分布式的单体应用;一个包含了一大堆互相之间紧耦合的服务,却又必须部署在一起的所谓分布式系统。这将会把单体架构和微服务架构两者的弊端集于一身。

  • 分布式系统带来的各种复杂性、使开发、测试和部署变得更困难:使用微服务架构的另一个问题是开发人员必须处理创建分布式系统的额外复杂性。服务必须是进程间通信。这比简单的方法调用要复杂的多。

  • 当部署跨越多个服务的功能时需要谨慎地协调更多的开发团队:使用微服务架构的另外一项挑战在于当部署跨越多个服务的功能时需要谨慎地协调更多开发团队。必须制定一个发布计划,把服务按照依赖关系进行排序。这跟单体架构下部署多个组件的方式截然不同。

  • 开发者需要思考到底应该在应用的什么阶段使用微服务架构:使用微服务架构的另一个问题是决定在应用程序生命周期的哪个阶段开始使用这种架构。

  • 跨服务数据的问题:在单体应用中,所有的数据都在一个数据库中,而在微服务架构中,每个服务都有自己的数据库,想要获取,操作其他服务的数据,只能通过该服务提供API进行调用,这样就带来一个问题,进程通信的问题,如果涉及到事务,那么还需要使用Saga来管理事务,增加了开发的难度。

3.3 微服务拆分原则

说到单体架构拆分,那也不是随意拆分,是要有一定的原则,拆分的好是优势,拆分的不好是混乱。以下是我查阅资料以及我的经验总结出来的拆分原则

  • 1、单一职责、高内聚低耦合

  • 2、微服务粒度适中

  • 3、考虑团队结构

  • 4、以业务模型切入

  • 5、演进式拆分

  • 6、避免环形依赖与双向依赖

  • 7、DDD(可以考虑使用领域驱动设计去进行底层服务的设计,后续会单独分析该设计的相关文章)

四、微服务实战

好了,到这里大家已经对微服务有了一定的理解,就不继续详细概述相关理念的东西,下面来直接撸代码,让大家熟悉微服务的应用。这里我使用 莫堇蕈 在github 上开源的微服务框架,框架源代码地址 :https://github.com/overtly/core-grpc  (我这里强烈推荐该框架,目前已经比较成熟的用于公司生产环境

4.1 core-grpc 微服务框架的优势:

  • 集成Consul 实现服务发现和注册以及健康检查等机制

  • 实时监听服务状态

  • 多节点 轮询机制

  • 故障转移,拉入黑名单

  • 支持.Net Core 和Framework 两种框架

  • 实现基于Grpc的微服务

  • 部署支持环境变量

4.2 实战

创建Jlion.NetCore.OrderService 订单微服务

我们用vs2019 创建控制台应用程序 选择框架.Net Core 3.1 命名为Jlion.NetCore.OrderService 后面简称订单服务,创建完后我们通过nuget包引入 core-grpc微服务框架,如下图:目前core-grpc微服务框架,最新正式发布版本是 1.0.3

引用了core-grpc 后我们还需要安装一个工具VS RPC Menu,这个工具也是大神免费提供的,图片如下:

由于微软官方下载比较慢,我这里共享到 百度网盘,百度网盘下载地址如下:

链接: https://pan.baidu.com/s/1twpmA4_aErrsg-m0ICmOPw 提取码: cshs

如果通过下载后安装不是vs 集成安装方式,下载完成后需要关闭vs 2019相关才能正常安装。

VS RPC Menu 工具说明如下:

  • 用于客户端代码生成 支持Grpc 和Thrift 我们再在 订单服务项目 中创建OrderRequest.proto文件,这个是Grpc 的语法,不了解该语法的同学可以 点击 gRPC 官方文档中文版_V1.0 进行学习,地址:http://doc.oschina.net/grpc?t=56831

OrderRequest.proto代码如下:

syntax = "proto3";
package Jlion.NetCore.OrderService.Service.Grpc;//定义订单查找参数实体
message OrderSearchRequest{string OrderId = 1; //定义订单IDstring Name = 2;
}//定义订单实体
message OrderRepsonse{string OrderId = 1;string Name = 2;double Amount = 3;int32 Count = 4;string Time = 5;
}//定义订单查找列表
message OrderSearchResponse{bool Success = 1;string ErrorMsg = 2;repeated OrderRepsonse Data = 3;
}

上面主要是定义了几个消息实体, 我们再创建JlionOrderService.proto,代码如下:

syntax = "proto3";
package Jlion.NetCore.OrderService.Service.Grpc;import "OrderRequest.proto";service JlionOrderService{rpc Order_Search(OrderSearchRequest) returns (OrderSearchResponse){}
}

上面的代码中都可以看到最上面有 package Jlion.NetCore.OrderService.Service.Grpc 代码,这是声明包名也就是后面生成代码后的命名空间,这个很重要。同时定义了JlionOrderService服务入口,并且定义了一个订单搜索的方法Order_Search,到这里我们已经完成了一小部分了。

生成客户端代码

再在JlionOrderService.proto文件里面右键 》选择Grpc代码生成》Grpc 代码 会自动生存微服务客户端代码 。生存工具中具有如下功能:

  • 生存Grpc客户端代码

  • Grpc 编译(不常用)

  • Grpc 打包(常用,用来把客户端dll发布到nuget服务器上)

  • 还可以对Thrift 代码进行生成和打包

创建Jlion.NetCore.OrderService.Grpc 类库

把刚刚通过工具生成的Grpc客户端代码直接copy到 Jlion.NetCore.OrderService.Grpc这个类库中(必须和上面Grpc 的代码声明的package 一致)以下简称订单服务客户端,并且需要通过Nuget包添加Overt.Core.Grpc 的依赖,代码结构如下:Jlion.NetCore.OrderService.Grpc类库已经构建完成,现在让 Jlion.NetCore.OrderService 服务引用Jlion.NetCore.OrderService.Grpc 类库

订单服务中 实现自己的IHostedService

创建HostService类,继承IHostedService代码如下:

public class HostedService : IHostedService
{readonly ILogger _logger;readonly JlionOrderServiceBase _grpcServImpl;public HostedService(ILogger<HostedService> logger,JlionOrderServiceBase grpcService){_logger = logger;_grpcServImpl = grpcService;}//服务的启动机相关配置public Task StartAsync(CancellationToken cancellationToken){return Task.Factory.StartNew(() =>{var channelOptions = new List<ChannelOption>(){new ChannelOption(ChannelOptions.MaxReceiveMessageLength, int.MaxValue),new ChannelOption(ChannelOptions.MaxSendMessageLength, int.MaxValue),};GrpcServiceManager.Start(BindService(_grpcServImpl), channelOptions: channelOptions, whenException: (ex) =>{_logger.LogError(ex, $"{typeof(HostedService).Namespace.Replace(".", "")}开启失败");throw ex;});System.Console.WriteLine("服务已经启动");_logger.LogInformation($"{nameof(Jlion.NetCore.OrderService.Service).Replace(".", "")}开启成功");}, cancellationToken);}//服务的停止public Task StopAsync(CancellationToken cancellationToken){return Task.Factory.StartNew(() =>{GrpcServiceManager.Stop();_logger.LogInformation($"{typeof(HostedService).Namespace.Replace(".", "")}停止成功");}, cancellationToken);}}

上面代码主要是创建宿主机并且实现了StartAsync 服务启动及StopAsync 服务停止方法。我们创建完HostedServicce代码再来创建之前定义的Grpc服务的方法实现类JlionOrderServiceImpl,代码如下:

public partial class JlionOrderServiceImpl : JlionOrderServiceBase
{private readonly ILogger _logger;private readonly IServiceProvider _serviceProvider;public JlionOrderServiceImpl(ILogger<JlionOrderServiceImpl> logger, IServiceProvider provider){_logger = logger;_serviceProvider = provider;}public override async Task<OrderSearchResponse> Order_Search(OrderSearchRequest request, ServerCallContext context){//TODO 从底层ES中查找订单数据,//可以设计成DDD 方式来进行ES的操作,这里我就为了演示直接硬编码了var response = new OrderSearchResponse();try{response.Data.Add(new OrderRepsonse(){Amount = 100.00,Count = 10,Name = "订单名称测试",OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),Time = DateTime.Now.ToString()});response.Data.Add(new OrderRepsonse(){Amount = 200.00,Count = 10,Name = "订单名称测试2",OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),Time = DateTime.Now.ToString()});response.Data.Add(new OrderRepsonse(){Amount = 300.00,Count = 10,Name = "订单名称测试2",OrderId = DateTime.Now.ToString("yyyyMMddHHmmss"),Time = DateTime.Now.ToString()});response.Success = true;}catch (Exception ex){response.ErrorMsg = ex.Message;_logger.LogWarning("异常");}return response;}}

再修改Program代码,并把HostedServiceJlionOrderServiceImpl 注入到容器中,代码如下:

 class Program{static void Main(string[] args){var host = new HostBuilder().UseConsoleLifetime() //使用控制台生命周期.ConfigureAppConfiguration((context, configuration) =>{configuration.AddJsonFile("appsettings.json", optional: true).AddEnvironmentVariables();}).ConfigureLogging(logger =>{logger.AddFilter("Microsoft", LogLevel.Critical).AddFilter("System", LogLevel.Critical);}).ConfigureServices(ConfigureServices).Build();AppDomain.CurrentDomain.UnhandledException += (sender, e) =>{var logFactory = host.Services.GetService<ILoggerFactory>();var logger = logFactory.CreateLogger<Program>();logger.LogError(e.ExceptionObject as Exception, $"UnhandledException");};host.Run();}/// <summary>/// 通用DI注入/// </summary>/// <param name="context"></param>/// <param name="services"></param>private static void ConfigureServices(HostBuilderContext context, IServiceCollection services){//HostedService 单例注入到DI 中services.AddSingleton<IHostedService, HostedService>();services.AddTransient<JlionOrderServiceBase, JlionOrderServiceImpl>();}}

到了这里简单的微服务已经编码完成,但是还缺少两个配置文件,我们创建appsettings.json配置文件和consulsettings.json 服务注册发现的配置文件consulsettings.json配置文件如下:

{"ConsulServer": {"Service": {"Address": "127.0.0.1:8500"// 你的Consul 服务注册及发现配置地址}}
}

上面的地址配置只是简单的例子,我这里假定我的Consul服务地址是 127.0.0.1:8500 等下服务启动会通过这个地址进行注册。

appsettings.json配置文件如下:

{"GrpcServer": {"Service": {"Name": "JlionOrderService","Port": 10001,"HostEnv": "serviceaddress","Consul": {"Path": "dllconfigs/consulsettings.json"}}}
}

我这里服务监听了10001 端口,后面注册到Consul中也会看到该端口 官方完整的配置文件如下:

{"GrpcServer": {"Service": {"Name": "OvertGrpcServiceApp",                    // 服务名称使用服务名称去除点:OvertGrpcServiceApp"Host": "service.g.lan",                          // 专用注册的域名 (可选)格式:ip[:port=default]"HostEnv": "serviceaddress",                      // 获取注册地址的环境变量名字(可选,优先)环境变量值格式:ip[:port=default]"Port": 10001,                                    // 端口自定义"Consul": {"Path": "dllconfigs/consulsettings.json"        // Consul路径}}}
}

好了,订单服务已经全部完成了,订单服务服务整体结构图如下:

好了,我们这里通过命令行启动下JlionOrderService服务,生产环境你们可以搭建在Docker 容器里面

我们可以来看下我之前搭建好的Consul服务 ,打开管理界面,如图:图片中可以发现刚刚启动的服务已经注册进去了,但是里面有一个健康检查未通过,主要是由于服务端不能访问我本地的订单服务,所有健康检查不能通过。你可以在你本地搭建 Consul服务用于测试。

我本地再来开启一个服务,配置中的的端口号由10001 改成10002,再查看下Consul的管理界面,如下图:发现已经注册了两个服务,端口号分别是10001 和10002,这样可以通过自定化工具自动添加服务及下架服务,分布式服务也即完成。到这里订单服务的启动已经完全成功了,我们接下来是需要客户端也就是上面架构图中的电商业务网关或者支付网关等等要跟订单服务进行通讯了。

创建订单网关(跟订单服务进行通信)

创建订单网关之前我先把上面的 订单服务客户端 类库发布到我的nuget包上,这里就不演示了。我发布的测试包名称JlionOrderServiceDemo nuget官方可以搜索找到。你们也可以直接搜索添加到你们的Demo中进行测试。我通过VS 2019 创建Asp.Net Core 3.1 框架的WebApi 取名为Jlion.NetCore.OrderApiService 下面简称订单网关服务现在我把前面发布的微服务客户端依赖包 JlionOrderServiceDemo 添加到订单网关服务中,如下图:

现在在订单网关服务中添加OrderController api控制器,代码如下:

namespace Jlion.NetCore.OrderApiService.Controllers
{[Route("[controller]")][ApiController]public class OrderController : ControllerBase{private readonly IGrpcClient<OrderService.Service.Grpc.JlionOrderService.JlionOrderServiceClient> _orderService;public OrderController (IGrpcClient<OrderService.Service.Grpc.JlionOrderService.JlionOrderServiceClient> orderService){_orderService = orderService;}[HttpGet("getlist")]public async Task<List<OrderRepsonse>> GetList(){var respData =await _orderService.Client.Order_SearchAsync(new OrderService.Service.Grpc.OrderSearchRequest(){Name = "test",OrderId = "",});if ((respData?.Data?.Count ?? 0) <= 0){return new List<OrderRepsonse>();}return respData.Data.ToList();}}
}

代码中通过构造函数注入 OrderService 并且提供了一个GetList的接口方法。接下来我们还需要把OrderService.Service.Grpc.JlionOrderService注入到容器中,代码如下:

public void ConfigureServices(IServiceCollection services)
{services.AddControllers();//注册Grpc 客户端,具体可以查看源代码services.AddGrpcClient();
}

现在整个订单网关服务项目结构如下图:

项目中有两个最重要的配置dllconfig//Jlion.NetCore.OrderService.Grpc.dll.jsonconsulsettings.json 他们分别是干什么的呢?我们先分别来看我本地这两个配置的内容Jlion.NetCore.OrderService.Grpc.dll.json 配置如下:

{"GrpcClient": {"Service": {"Name": "JlionOrderService",  // 服务名称与服务端保持一致"MaxRetry": 0,                // 最大可重试次数,默认不重试"Discovery": {"Consul": {              // Consul集群,集群优先原则"Path": "dllconfigs/consulsettings.json"},"EndPoints": [           // 单点模式{"Host": "127.0.0.1","Port": 10001}]}}}
}

Jlion.NetCore.OrderService.Grpc.dll.json 配置主要是告诉订单网关服务订单服务应该怎样进行通信,以及通信当中的一些参数配置。我为了测试,本地使用单点模式,不使用Consul模式consulsettings.json 配置如下:

{"ConsulServer": {"Service": {"Address": "127.0.0.1:8500"}}
}

有没有发现这个配置和之前服务端的配置一样,主要是告诉订单网关服务(客户端调用者)和订单服务服务端服务发现的集群地址,如果上面的配置是单点模式则这个配置不会起作用。

到这里订单网关服务 (客户调用端)编码完成,我们开始启动它:我这里固定5003端口,现在完美的启动了,我们访问下订单接口,看下是否成功。访问结果如下图:

微服务完美的运行成功。

上面的构建微服务还是比较麻烦,官方提供了比较快速构建你需要的微服务方式,不需要写上面的那些代码,那些代码全部通过模板的方式进行构建你的微服务,有需要学习的可以到点击微服务项目构建模板使用教程教程,地址:https://www.cnblogs.com/jlion/p/12494525.html

为了更好的维护开源项目以及技术交流,特意创建了一个交流群,QQ群号:1083147206 有兴趣者可以加入交流

文章中的Demo 代码已经提交到github 上,代码地址:https://github.com/a312586670/IdentityServerDemo

微服务框架开源项目地址:https://github.com/overtly/core-grpc 

♥ 给个[在看],是对我最大的支持 ♥

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

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

相关文章

c语言将一个已知头结点的单链表逆序_C语言实现常用数据结构:静态链表数组实现(第5篇)...

「今天是学习C语言第 148 天」纸上学来终觉浅&#xff0c;绝知此事要躬行。—— 陆游「冬夜读书示子聿」# 静态链表使用数组实现&#xff0c;利用数组下标代替指针&#xff0c;从而实现数据结点之间的先后关系。实现要点&#xff1a;1.数组下标为0的位置为头结点&#xff0c;指…

集成平台集群任务动态分派

源宝导读&#xff1a;MIP集成平台是为了解决企业大量异构系统之间快速、稳定集成的需要&#xff0c;助力企业数字化转型&#xff0c;明源云自主研发的平台系统。本文将对"事件任务分派"场景的架构设计以及实践成果进行分享。背景MIP集成平台是为了解决企业大量异构系…

dotcpp1115 DNA-打印图案

题目描述 小强从小就喜欢生命科学&#xff0c;他总是好奇花草鸟兽从哪里来的。终于&#xff0c; 小强上中学了&#xff0c;接触到了神圣的名词–DNA.它有一个双螺旋的结构。这让一根筋的小强抓破头皮&#xff0c;“要是能画出来就好了” 小强喊道。现在就请你帮助他吧 输入 输…

akb48_AKB48里历史——六年的终结

注&#xff1a;这是2012年发行的一本在BUBUKA连载的基础上补充了一些内容的粉丝公式教科书&#xff0c;从里面找了部分内容翻译了一下&#xff0c;节选的内容主要说的是2011年的事情&#xff0c;以当时作者的视角&#xff0c;是AKB48第一次新老粉丝换代的时期。前田敦子和大岛优…

[头脑风暴] 解读Docker Bridge网络模型

背景这几天在研究Kubernetes&#xff0c; 遇到一个有意思的nodejs镜像&#xff1a;luksa/kubia# 不带端口映射启动容器 docker run -it -d luksa/kubia # 连接到默认的Bridge网桥&#xff0c;容器IP是 172.17.0.2之后&#xff0c;在宿主机使用容器IP和8080 端口可访问该容器…

kodi pvr 不能安装_「家庭影音串流」电视最强播放器KODI使用方法

本文作者&#xff1a;空翻的帕兹文章适用电脑手机等全平台设备&#xff0c;在用户没有nas的情况下如何使用串流电视最强播放器KODIKodi是由XBMC基金會開發的開源媒體播放器&#xff0c;原名XBMC(最後一個以XBMC命名的版本是13.2「Gotham」&#xff0c;14.0 「Helix」是第一個以…

docker部署flask项目

项目本地运行 1.到github或者自己创建一个flask项目&#xff0c;确保在本地是可以运行成功的 2.上传到自己的代码仓库 服务器部署 1.安装docker yum install docker -y2.配置加速器 DaoCloud加速器采用自主研发的智能路由及缓存技术&#xff0c;并引入了现金的协议层优化…

.NET Core开发实战(第24课:文件提供程序:让你可以将文件放在任何地方)--学习笔记...

24 | 文件提供程序&#xff1a;让你可以将文件放在任何地方文件提供程序核心类型&#xff1a;1、IFileProvider2、IFileInfo3、IDirectoryContentsIFileProvider 是访问各种各样文件提供程序的接口通过这样子抽象的定义&#xff0c;让我们与具体的抽象文件的读取的代码进行了隔…

连接mysql数据库_解决Navicat连接MySQL数据库报错问题

今天在用Navicat连接另外一台主机上的MySQL时报错&#xff1a;Host is not allowed to connect to this MySQL server默认安装的mysql无法远程连接是因为MySQL默认配置了不支持远程连接引起的。解决方法&#xff1a;一、本地主机上登录root用户找到mysql.exe所在路径&#xff0…

洛谷T172098 子串-substr

代码如下&#xff1a; #include <iostream> #include <cstring> using namespace std;int main() {int cnt;cin >> cnt;string a, b;while (cnt--) {int n, m;cin >> n >> m;cin >> a;cin >> b;int ans 0 ;for (int i 0; i < …

在Ocelot中使用自定义的中间件(二)

在上文中《在Ocelot中使用自定义的中间件&#xff08;一&#xff09;》&#xff0c;我介绍了如何在Ocelot中使用自定义的中间件来修改下游服务的response body。今天&#xff0c;我们再扩展一下设计&#xff0c;让我们自己设计的中间件变得更为通用&#xff0c;使其能够应用在不…

机器学习理论引导 电子版_机器学习理论篇1:机器学习的数学基础(2)

本节主要就是讲述的机器学习的数学基础&#xff0c;提到数学基础&#xff0c;可能一眼就会是满眼的枯燥、没意思&#xff0c;但是成就英雄的路上注定了孤独&#xff0c;要想要真正的在学术上有所突破就必须挨得住寂寞&#xff0c;受得住孤独&#xff0c;才能真正的走进熟悉直到…

洛谷T172100 商店-贪心

解题思路&#xff1a; 贪心 代码如下&#xff1a; #include <iostream> #include <algorithm> using namespace std; const int N 100010; int a[N], w[N]; int ans;int main() {int n, m;cin >> n >> m;for (int i 1; i < n; i)cin >> a…

终结“永恒之蓝”后,再战“永恒之黑”

引子&#xff1a;2003年的“抗击非典”&#xff0c;17年后的2020年“抗击新冠”。2017年的“永恒之蓝”&#xff0c;3年后的2020年“永恒之黑”。历史&#xff1a;2017年5月13日&#xff0c;在“胖哥技术堂”中发布了《截杀“WannaCrypt”&#xff0c;终结“永恒之蓝”》。三年…

使用.Net Core编写命令行工具(CLI)

使用.Net Core编写命令行工具(CLI)命令行工具(CLI)命令行工具(CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面&#xff0c;它通常不支持鼠标&#xff0c;用户通过键盘输入指令&#xff0c;计算机接收到指令后&#xff0c;予以执行。通常认为&#xff0c;命令行工具(C…

与php有区别_php://output和php://stdout的区别

php中文网最新课程每日17点准时技术干货分享PHP包含了以php://开头的一系列输出输出流&#xff0c;如php://stdin, php://stdout等。今天查看代码时&#xff0c;忽然想到一个问题&#xff1a;php://output和php://stdout有什么区别&#xff1f;从PHP的官方文献中找答案&#xf…

浅谈 Kubernetes Scheduling-Framework 插件的实现

最近几个月一直在研究 kubernetes 的 scheduling-framework 调度框架&#xff0c;发现还是十分有意思的&#xff0c;我自己也实现了一个基于 scheduling-framework 调度框架的自定义调度器&#xff0c;希望感兴趣的同学一起学习&#xff1a;https://github.com/NJUPT-ISL/Yoda-…

C++实现线段树(lazy-tag方法)-区间修改,区间查询

代码如下&#xff1a; #include <iostream> using namespace std; const int N 10010; typedef long long LL; LL input[N];struct node {int l, r;LL sum;LL add; } tree[4 * N];void build(int l, int r, int u) {tree[u].l l;tree[u].r r;if (l r) {tree[u].sum …

.NET Core开发实战(第25课:路由与终结点:如何规划好你的Web API)--学习笔记(上)...

25 | 路由与终结点&#xff1a;如何规划好你的Web API路由系统在 ASP.NET MVC 框架里面就已经存在了&#xff0c;在 ASP.NET Core 框架里面进行了改进路由系统的核心作用是指 URL 和 应用程序 Controller 的对应关系的一种映射这个映射关系实际上有两种作用&#xff1a;1、把 U…

Angular SPA基于Ocelot API网关与IdentityServer4的身份认证与授权(一)

好吧&#xff0c;这个题目我也想了很久&#xff0c;不知道如何用最简单的几个字来概括这篇文章&#xff0c;原本打算取名《Angular单页面应用基于Ocelot API网关与IdentityServer4ASP.NET Identity实现身份认证与授权》&#xff0c;然而如你所见&#xff0c;这样的名字实在是太…