eShopOnContainers 知多少[7]:Basket microservice

引言
Basket microservice(购物车微服务)主要用于处理购物车的业务逻辑,包括:
- 购物车商品的CRUD
- 订阅商品价格更新事件,进行购物车商品同步处理
- 购物车结算事件发布
- 订阅订单成功创建事件,进行购物车的清空操作
架构模式

如上图所示,本微服务采用数据驱动的CRUD微服务架构,来执行购物车商品的维护操作。并使用Redis数据库进行持久化。
这种类型的服务在单个 ASP.NET Core Web API 项目中即可实现所有功能,该项目包括数据模型类、业务逻辑类及其数据访问类。其项目结构如下:

核心技术选型:
- ASP.NET Core Web API
- Entity Framework Core
- Redis
- Swashbuckle(可选)
- Autofac
- Eventbus
- Newtonsoft.Json
实体建模和持久化
该微服务的核心领域实体是购物车,其类图如下:
其中CustomerBasket与BasketItem为一对多关系,使用仓储模式进行持久化。
- 通过对
CustomerBasket对象进行json格式的序列化和反序列化来完成在redis中的持久化和读取。 - 以单例模式注入redis连接
ConnectionMultiplexer,该对象最终通过构造函数注入到RedisBasketRepository中。
services.AddSingleton<ConnectionMultiplexer>(sp =>
{
var settings = sp.GetRequiredService<IOptions<BasketSettings>>().Value;
var configuration = ConfigurationOptions.Parse(settings.ConnectionString, true);
configuration.ResolveDns = true;
return ConnectionMultiplexer.Connect(configuration);
});
事件的注册和消费
在本服务中主要需要处理以下事件的发布和消费:
- 事件发布:当用户点击购物车结算时,发布用户结算事件。
- 事件消费:订单创建成功后,进行购物车的清空
- 事件消费:商品价格更新后,进行购物车相关商品的价格同步
private void ConfigureEventBus(IApplicationBuilder app)
{
var eventBus = app.ApplicationServices.GetRequiredService<IEventBus>();
eventBus.Subscribe<ProductPriceChangedIntegrationEvent, ProductPriceChangedIntegrationEventHandler>();
eventBus.Subscribe<OrderStartedIntegrationEvent, OrderStartedIntegrationEventHandler>();
}
以上都是基于事件总线来达成。
认证和授权
购物车管理界面是需要认证和授权。那自然需要与上游的Identity Microservice进行衔接。在启动类进行认证中间件的配置。
private void ConfigureAuthService(IServiceCollection services)
{
// prevent from mapping "sub" claim to nameidentifier.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = identityUrl;
options.RequireHttpsMetadata = false;
options.Audience = "basket";
});
}
protected virtual void ConfigureAuth(IApplicationBuilder app)
{
if (Configuration.GetValue<bool>("UseLoadTest"))
{
app.UseMiddleware<ByPassAuthMiddleware>();
}
app.UseAuthentication();
}
手动启用断路器
在该微服务中,定义了一个中断中间件:FailingMiddleware,通过访问http://localhost:5103/failing获取该中间件的启用状态,通过请求参数指定:即通过http://localhost:5103/failing?enable和http://localhost:5103/failing?disable来手动中断和恢复服务,来模拟断路,以便用于测试断路器模式。
开启断路后,当访问购物车页面时,Polly在重试指定次数依然无法访问服务时,就会抛出BrokenCircuitException异常,通过捕捉该异常告知用户稍后再试。
public class CartController : Controller
{
//…
public async Task<IActionResult> Index()
{
try
{
var user = _appUserParser.Parse(HttpContext.User);
//Http requests using the Typed Client (Service Agent)
var vm = await _basketSvc.GetBasket(user);
return View(vm);
}
catch (BrokenCircuitException)
{
// Catches error when Basket.api is in circuit-opened mode
HandleBrokenCircuitException();
}
return View();
}
private void HandleBrokenCircuitException()
{
TempData["BasketInoperativeMsg"] = "Basket Service is inoperative, please try later on. (Business message due to Circuit-Breaker)";
}
}

注入过滤器
在配置MVC服务时指定了两个过滤器:全局异常过滤器和模型验证过滤器。
// Add framework services.
services.AddMvc(options =>
{
options.Filters.Add(typeof(HttpGlobalExceptionFilter));
options.Filters.Add(typeof(ValidateModelStateFilter));
}).AddControllersAsServices();
- 全局异常过滤器是通过定义
BasketDomainException异常和HttpGlobalExceptionFilter过滤器来实现的。 - 模型验证过滤器是通过继承
ActionFilterAttribute特性实现的ValidateModelStateFilter来获取模型状态中的错误。
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.ModelState.IsValid)
{
return;
}
var validationErrors = context.ModelState
.Keys
.SelectMany(k => context.ModelState[k].Errors)
.Select(e => e.ErrorMessage)
.ToArray();
var json = new JsonErrorResponse
{
Messages = validationErrors
};
context.Result = new BadRequestObjectResult(json);
}
}
SwaggerUI认证授权集成
因为默认启用了安全认证,所以为了方便在SwaggerUI界面进行测试,那么我们就必须为其集成认证授权。代码如下:
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Info
{
Title = "Basket HTTP API",
Version = "v1",
Description = "The Basket Service HTTP API",
TermsOfService = "Terms Of Service"
});
options.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/authorize",
TokenUrl = $"{Configuration.GetValue<string>("IdentityUrlExternal")}/connect/token",
Scopes = new Dictionary<string, string>()
{
{ "basket", "Basket API" }
}
});
options.OperationFilter<AuthorizeCheckOperationFilter>();
});
其中有主要做了三件事:
- 配置授权Url
- 配置TokenUrl
- 指定授权范围
- 注入授权检查过滤器
AuthorizeCheckOperationFilter用于拦截需要授权的请求
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
// Check for authorize attribute
var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType<AuthorizeAttribute>().Any() ||
context.ApiDescription.ActionAttributes().OfType<AuthorizeAttribute>().Any();
if (hasAuthorize)
{
operation.Responses.Add("401", new Response { Description = "Unauthorized" });
operation.Responses.Add("403", new Response { Description = "Forbidden" });
operation.Security = new List<IDictionary<string, IEnumerable<string>>>();
operation.Security.Add(new Dictionary<string, IEnumerable<string>>
{
{ "oauth2", new [] { "basketapi" } }
});
}
}
}
最后
本服务较之前讲的Catalog microservice 而言,主要是多了一个认证和redis存储。
eShopOnContainers 知多少[7]:Basket microservice的更多相关文章
- eShopOnContainers 知多少[1]:总体概览
引言 在微服务大行其道的今天,Java阵营的Spring Boot.Spring Cloud.Dubbo微服务框架可谓是风水水起,也不得不感慨Java的生态圈的火爆.反观国内.NET阵营,微服务却不愠 ...
- eShopOnContainers 知多少[2]:Run起来
环境准备 Win10(开启Hyper-V) .NET Core SDK Docker for Windows VS2017 or VS Code Git SQL Server Management S ...
- eShopOnContainers 知多少[8]:Ordering microservice
1. 引言 Ordering microservice(订单微服务)就是处理订单的了,它与前面讲到的几个微服务相比要复杂的多.主要涉及以下业务逻辑: 订单的创建.取消.支付.发货 库存的扣减 2. 架 ...
- eShopOnContainers 知多少[3]:Identity microservice
首先感谢晓晨Master和EdisonChou的审稿!也感谢正在阅读的您! 引言 通常,服务所公开的资源和 API 必须仅限受信任的特定用户和客户端访问.那进行 API 级别信任决策的第一步就是身份认 ...
- eShopOnContainers 知多少[4]:Catalog microservice
引言 Catalog microservice(目录微服务)维护着所有产品信息,包括库存.价格.所以该微服务的核心业务为: 产品信息的维护 库存的更新 价格的维护 架构模式 如上图所示,本微服务采用简 ...
- eShopOnContainers 知多少[9]:Ocelot gateways
引言 客户端与微服务的通信问题永远是一个绕不开的问题,对于小型微服务应用,客户端与微服务可以使用直连的方式进行通信,但对于对于大型的微服务应用我们将不得不面对以下问题: 如何降低客户端到后台的请求数量 ...
- eShopOnContainers 知多少[5]:EventBus With RabbitMQ
1. 引言 事件总线这个概念对你来说可能很陌生,但提到观察者(发布-订阅)模式,你也许就很熟悉.事件总线是对发布-订阅模式的一种实现.它是一种集中式事件处理机制,允许不同的组件之间进行彼此通信而又不需 ...
- eShopOnContainers 知多少[11]:服务间通信之gRPC
引言 最近翻看最新3.0 eShopOncontainers源码,发现其在架构选型中补充了 gRPC 进行服务间通信.那就索性也写一篇,作为系列的补充. gRPC 老规矩,先来理一下gRPC的基本概念 ...
- eShopOnContainers 知多少[12]:Envoy gateways
1. 引言 在最新的eShopOnContainers 3.0 中Ocelot 网关被Envoy Proxy 替换.下面就来简要带大家了解下Envoy,并尝试梳理下为什么要使用Envoy替代Ocelo ...
随机推荐
- 在WinForm应用程序中快速实现多语言的处理
在国际化环境下,越来越多的程序需要做多语言版本,以适应各种业务需求的变化.在Winform应用程序中实现多语言也有常规的处理方式处理,不过需要针对每个语言版本,重新修改Winform界面的显示,对一些 ...
- erlang的脚本执行---escript
1.概述: 作为程序员对于脚本语言应该很熟悉了,脚本语言的优点很多,如快速开发.容易编写.实时开发和执行, 我们常用的脚本有Javascript.shell.python等,我们的erlang语言也有 ...
- swagger在nginx下出现无法请求接口的问题
在Nginx配置绑定域名的时候,增加proxy_set_header即可 示例如下: server { listen ; server_name xxx.dev.internal.XXX.com; l ...
- EF Core Model更新迁移
EF Core 迁移 感觉就是以前EF Code First的自动同步数据库功能 内容:在你新增.更新TableModel后,如何自动化的更新DB中的真实Table.以及对这些更改进行一个版本控制. ...
- hdu-3071 Gcd & Lcm game---质因数分解+状态压缩+线段树
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3071 题目大意: 给定一个长度为n的序列m次操作,操作的种类一共有三种 查询 L :查询一个区间的所 ...
- Http Header信息
REMOTE_ADDR – 访问客户端的 IP 地址 HTTP_VIA – 如果有该条信息, 就证明您使用了代理服务器,代理服务器的地址就是后面的数值. HTTP_X_FORWARDED_FOR – ...
- mybatis一对一映射配置详解
听说mybatis一对一有三种写法,今天我试了一下. 数据库表准备 为了偷懒,我直接就拿用户权限菜单里的菜单表和菜单与权限的中间表做实现,他们原来是多对多的关系,这边我假设这两张表是一对一. 表 g ...
- Vim手册
什么是 vim? Vim是从 vi 发展出来的一个文本编辑器.代码补完.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用. 简单的来说, vi 是老式的字处理器,不过功能已经很齐全了,但是 ...
- 手把手教你使用 netlify 实现前端的 自动部署 + HTTPS
随着开源工具越来越多,特别是nodejs构建微服务器之快,实现前端自动化部署越来越简单了,有可能[10行js代码+10行sh脚本+设置github的webhook]就能实现,但是如果你和我一样,就是& ...
- Spring MVC 的 Java Config ( 非 XML ) 配置方式
索引: 开源Spring解决方案--lm.solution 参看代码 GitHub: solution/pom.xml web/pom.xml web.xml WebInitializer.java ...