
eShopOnContainers 知多少[7]:Basket microservice
发布日期:2021-05-09 06:35:57
浏览次数:17
分类:博客文章
本文共 5934 字,大约阅读时间需要 19 分钟。
引言
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(sp =>{ var settings = sp.GetRequiredService >().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(); eventBus.Subscribe (); eventBus.Subscribe ();}
以上都是基于事件总线来达成。
认证和授权
购物车管理界面是需要认证和授权。那自然需要与上游的Identity Microservice
进行衔接。在启动类进行认证中间件的配置。
private void ConfigureAuthService(IServiceCollection services){ // prevent from mapping "sub" claim to nameidentifier. JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); var identityUrl = Configuration.GetValue("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 ("UseLoadTest")) { app.UseMiddleware (); } app.UseAuthentication();}
手动启用断路器
在该微服务中,定义了一个中断中间件:FailingMiddleware
,通过访问http://localhost:5103/failing
获取该中间件的启用状态,通过请求参数指定:即通过http://localhost:5103/failing?enable
和http://localhost:5103/failing?disable
来手动中断和恢复服务,来模拟断路,以便用于测试断路器模式。
BrokenCircuitException
异常,通过捕捉该异常告知用户稍后再试。 public class CartController : Controller{ //… public async TaskIndex() { 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("IdentityUrlExternal")}/connect/authorize", TokenUrl = $"{Configuration.GetValue ("IdentityUrlExternal")}/connect/token", Scopes = new Dictionary () { { "basket", "Basket API" } } }); options.OperationFilter ();});
其中有主要做了三件事:
- 配置授权Url
- 配置TokenUrl
- 指定授权范围
- 注入授权检查过滤器
AuthorizeCheckOperationFilter
用于拦截需要授权的请求
public class AuthorizeCheckOperationFilter : IOperationFilter{ public void Apply(Operation operation, OperationFilterContext context) { // Check for authorize attribute var hasAuthorize = context.ApiDescription.ControllerAttributes().OfType().Any() || context.ApiDescription.ActionAttributes().OfType ().Any(); if (hasAuthorize) { operation.Responses.Add("401", new Response { Description = "Unauthorized" }); operation.Responses.Add("403", new Response { Description = "Forbidden" }); operation.Security = new List >>(); operation.Security.Add(new Dictionary > { { "oauth2", new [] { "basketapi" } } }); } }}
最后
本服务较之前讲的Catalog microservice 而言,主要是多了一个认证和redis存储。
发表评论
最新留言
感谢大佬
[***.8.128.20]2025年04月12日 17时40分23秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
Python常见问题 - 文件模式a+读取不了文件
2019-03-06
Jenkins - 部署在Tomcat容器里的Jenkins,提示“反向代理设置有误”
2019-03-06
Jenkins(3)- 安装Jenkins过程中遇到问题的排查思路
2019-03-06
Cypress系列(14)- 环境变量详解
2019-03-06
centos7 - 安裝 Python 3.7
2019-03-06
用前端姿势玩docker【一】Docker通俗理解常用功能汇总与操作埋坑
2019-03-06
基于jquery的简洁树形折叠菜单
2019-03-06
Spread for ASP.NET技术白皮书
2019-03-06
Asp.Net MVC4入门指南(4):添加一个模型
2019-03-06
我看TechEd 2012之技术热点
2019-03-06
Python和JavaScript在使用上有什么区别?
2019-03-06
实战使用Axure设计App,使用WebStorm开发(1) – 用Axure描述需求
2019-03-06
Windows10的革命之路-全新UWP开发平台
2019-03-06
程序员Web面试之前端框架等知识
2019-03-06
ASP.NET MVC 5 - 视图
2019-03-06
2017年前端框架、类库、工具大比拼
2019-03-06
wxWidgets源码分析(1) - App启动过程
2019-03-06
wxWidgets源码分析(2) - App主循环
2019-03-06
wxWidgets源码分析(3) - 消息映射表
2019-03-06