当前位置: 首页 > backend >正文

(一)微服务(垂直AP/分布式缓存/装饰器Pattern)


文章目录

  • 项目地址
  • 一、创建第一个垂直API
    • 1.1 创建Common层
      • 1. ICommand接口
      • 2. IQuery接口
    • 1.2 创建API
      • 1. 实体
      • 2. Handler
      • 3. endpoint
    • 1.3 使用Marten作为ORM
  • 二、Redis缓存
    • 2.1 使用缓存装饰器
      • 1. 创建装饰器
      • 2. 注册装饰器
    • 2.2 创建docker-compose
      • 1. docker-compose
      • 2. docker-compose.override


项目地址

  • 教程作者:
  • 教程地址:
  • 代码仓库地址:
  • 所用到的框架和插件:
dbt 
airflow

一、创建第一个垂直API

1.1 创建Common层

在这里插入图片描述

1. ICommand接口

  1. ICommand.cs
using MediatR;
namespace BuildingBlocks.CQRS;
public interface ICommand : ICommand<Unit>; 
public interface ICommand<out TResponse> : IRequest<TResponse>;
  1. ICommandHandler.cs
using MediatR;
namespace BuildingBlocks.CQRS;
public interface ICommandHandler<in TCommand>  //无返回值: ICommandHandler<TCommand, Unit>where TCommand : ICommand<Unit>;public interface ICommandHandler<in TCommand, TResponse> //有返回值: IRequestHandler<TCommand, TResponse>where TCommand : ICommand<TResponse>where TResponse : notnull;

in(Contravariant):只在参数中使用的泛型类型

2. IQuery接口

  1. IQuery.cs
using MediatR;
namespace BuildingBlocks.CQRS;
public interface IQuery<out TResponse> : IRequest<TResponse>  where TResponse : notnull;
  1. IQueryHandler:
namespace BuildingBlocks.CQRS;
public interface IQueryHandler<in TQuery, TResponse>: IRequestHandler<TQuery, TResponse>where TQuery : IQuery<TResponse>where TResponse : notnull;

1.2 创建API

在这里插入图片描述

1. 实体

namespace Catalog.API.Models;
public class Product
{public Guid Id { get; set; }public string Name { get; set; } = default!;public List<string> Category { get; set; } = new();public string Description { get; set; } = default!;public string ImageFile { get; set; } = default!;public decimal Price { get; set; }
}

2. Handler

namespace Catalog.API.Products.CreateProduct;
public record CreateProductCommand(string Name, List<string> Category, string Description, string ImageFile, decimal Price): ICommand<CreateProductResult>;
public record CreateProductResult(Guid Id);internal class CreateProductCommandHandler : ICommandHandler<CreateProductCommand, CreateProductResult>
{public async Task<CreateProductResult> Handle(CreateProductCommand command, CancellationToken cancellationToken){var product = new Product{Name = command.Name,Category = command.Category,Description = command.Description,ImageFile = command.ImageFile,Price = command.Price};return new CreateProductResult(Guid.NewGuid());        }
}

3. endpoint

namespace Catalog.API.Products.CreateProduct;
public record CreateProductRequest(string Name, List<string> Category, string Description, string ImageFile, decimal Price);
public record CreateProductResponse(Guid Id);
public class CreateProductEndpoint : ICarterModule
{public void AddRoutes(IEndpointRouteBuilder app){app.MapPost("/products",async (CreateProductRequest request, ISender sender) =>{var command = request.Adapt<CreateProductCommand>();var result = await sender.Send(command);var response = result.Adapt<CreateProductResponse>();return Results.Created($"/products/{response.Id}", response);}).WithName("CreateProduct").Produces<CreateProductResponse>(StatusCodes.Status201Created).ProducesProblem(StatusCodes.Status400BadRequest).WithSummary("Create Product").WithDescription("Create Product");}
}

1.3 使用Marten作为ORM

  • Marten只能用于postgresql的ORM使用

二、Redis缓存

2.1 使用缓存装饰器

1. 创建装饰器

  • 给basket添加装饰器,原来的 var basket = await repository.GetBasket(userName, cancellationToken);被其他的方法包裹,这样就被装饰了
 namespace Basket.API.Data;public class CachedBasketRepository(IBasketRepository repository, IDistributedCache cache) : IBasketRepository
{public async Task<ShoppingCart> GetBasket(string userName, CancellationToken cancellationToken = default){var cachedBasket = await cache.GetStringAsync(userName, cancellationToken);if (!string.IsNullOrEmpty(cachedBasket))return JsonSerializer.Deserialize<ShoppingCart>(cachedBasket)!;var basket = await repository.GetBasket(userName, cancellationToken);await cache.SetStringAsync(userName, JsonSerializer.Serialize(basket), cancellationToken);return basket;}public async Task<ShoppingCart> StoreBasket(ShoppingCart basket, CancellationToken cancellationToken = default){await repository.StoreBasket(basket, cancellationToken);await cache.SetStringAsync(basket.UserName, JsonSerializer.Serialize(basket), cancellationToken);return basket;}public async Task<bool> DeleteBasket(string userName, CancellationToken cancellationToken = default){await repository.DeleteBasket(userName, cancellationToken);await cache.RemoveAsync(userName, cancellationToken);return true;}
}

2. 注册装饰器

  • 安装需要的包
     <PackageReference Include="Scrutor" Version="4.2.2" /><PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1" />
  • 注册装饰器
builder.Services.AddScoped<IBasketRepository, BasketRepository>(); //原来的方法
builder.Services.Decorate<IBasketRepository, CachedBasketRepository>();  //装饰过后带redis缓存的//注册redis
builder.Services.AddStackExchangeRedisCache(options =>
{options.Configuration = builder.Configuration.GetConnectionString("Redis");//options.InstanceName = "Basket";
});  
  • 配置redis 的ConnectionString
 appsettings.json

2.2 创建docker-compose

1. docker-compose

  1. docker-compose.yml 用来说明微服务需要的Container有哪些
version: '3.4'
services:catalogdb:image: postgresbasketdb:image: postgresdistributedcache:image: rediscatalog.api:image: ${DOCKER_REGISTRY-}catalogapibuild:context: .dockerfile: Services/Catalog/Catalog.API/Dockerfilebasket.api:image: ${DOCKER_REGISTRY-}basketapibuild:context: .dockerfile: Services/Basket/Basket.API/Dockerfile
volumes:postgres_catalog:postgres_basket:

2. docker-compose.override

  • 用来配置本地具体环境
version: '3.4'services:catalogdb:container_name: catalogdbenvironment:- POSTGRES_USER=postgres- POSTGRES_PASSWORD=postgres- POSTGRES_DB=CatalogDbrestart: alwaysports:- "5432:5432"volumes:- postgres_catalog:/var/lib/postgresql/data/ basketdb:container_name: basketdbenvironment:- POSTGRES_USER=postgres- POSTGRES_PASSWORD=postgres- POSTGRES_DB=BasketDbrestart: alwaysports:- "5433:5432"volumes:- postgres_basket:/var/lib/postgresql/data/ distributedcache:container_name: distributedcacherestart: alwaysports:- "6379:6379"catalog.api:environment:- ASPNETCORE_ENVIRONMENT=Development- ASPNETCORE_HTTP_PORTS=8080- ASPNETCORE_HTTPS_PORTS=8081- ConnectionStrings__Database=Server=catalogdb;Port=5432;Database=CatalogDb;User Id=postgres;Password=postgres;Include Error Detail=truedepends_on:- catalogdbports:- "6000:8080"- "6060:8081"volumes:- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro- ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:robasket.api:environment:- ASPNETCORE_ENVIRONMENT=Development- ASPNETCORE_HTTP_PORTS=8080- ASPNETCORE_HTTPS_PORTS=8081- ConnectionStrings__Database=Server=basketdb;Port=5432;Database=BasketDb;User Id=postgres;Password=postgres;Include Error Detail=true- ConnectionStrings__Redis=distributedcache:6379depends_on:- basketdb- distributedcacheports:- "6001:8080"- "6061:8081"volumes:- ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro- ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
http://www.xdnf.cn/news/10117.html

相关文章:

  • Bootstrap项目 - 个人作品与成就展示网站
  • 【数据集】高分辨率(1 km)月尺度中国气候(降水+最高/低温)数据集(1952–2019)
  • 【目标检测】【ICCV 2021】条件式DETR实现快速训练收敛
  • Flume 自定义拦截器开发实战:添加时间戳与 JSON 处理
  • AI感知与行动:考拉悠然发布空间智能世界模型,让AI走进物理世界
  • AcroForm 表单:动态字段值调整及避免使用 “event.value“
  • 中国城市间地理距离矩阵(2024)
  • 历年南京理工大学计算机保研上机真题
  • linux常用命令笔记
  • 进程生命周期
  • Java八股-数据类型转换有哪些?类型互转会有什么问题?为什么用bigDecimal 不用double ?自动装箱和拆箱?包装类?
  • 简单说一说Modern ABAP这个概念
  • ZC-OFDM雷达通信一体化减小PAPR——直接限幅法
  • CSS级联样式(基础知识)备忘录
  • C# 结合PaddleOCRSharp搭建Http网络服务
  • MySQL大表结构变更利器:pt-online-schema-change原理与实战指南
  • ⼤模型驱动的DeepInsight Copilot在蚂蚁的技术实践
  • LINUX530 rsync定时同步 环境配置
  • 【c语言输入不大于26的整数,输出全部大写字母输入3输出ABC】2022-1-30
  • Java限制单价小数位数方法
  • sward V1.1.4版本发布,支持文档审批及文档导出
  • 天气预报中的AI:更准确的预测如何实现
  • quic为什么没有被大规模应用?
  • LangChain-自定义Tool和Agent结合DeepSeek应用实例
  • 【面试 - 遇到的问题 - 优化 - 地图】腾讯地图轨迹回放 - 回放的轨迹时间要和现实时间对应(非匀速)
  • Bonjour
  • Python----目标检测(《Fast R-CNN》和Fast R-CNN)
  • 如何成为一名优秀的产品经理
  • 2359.找到离给定两个节点最近的节点
  • AC220V整流滤波电路Multisim仿真