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



项目地址

  • 教程作者:
  • 教程地址:
  • 代码仓库地址:
  • 所用到的框架和插件:
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: postgres
  basketdb:
    image: postgres
  distributedcache:
    image: redis
  catalog.api:
    image: ${DOCKER_REGISTRY-}catalogapi
    build:
      context: .
      dockerfile: Services/Catalog/Catalog.API/Dockerfile
  basket.api:
    image: ${DOCKER_REGISTRY-}basketapi
    build:
      context: .
      dockerfile: Services/Basket/Basket.API/Dockerfile
volumes:
  postgres_catalog:
  postgres_basket:

2. docker-compose.override

  • 用来配置本地具体环境
version: '3.4'

services:
  catalogdb:
    container_name: catalogdb
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=CatalogDb
    restart: always
    ports:
        - "5432:5432"
    volumes:
      - postgres_catalog:/var/lib/postgresql/data/ 
      
  basketdb:
    container_name: basketdb
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=BasketDb
    restart: always
    ports:
        - "5433:5432"
    volumes:
      - postgres_basket:/var/lib/postgresql/data/ 

  distributedcache:
    container_name: distributedcache
    restart: always
    ports:
      - "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=true
    depends_on:
      - catalogdb
    ports:
      - "6000:8080"
      - "6060:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro

  basket.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:6379
    depends_on:
      - basketdb
      - distributedcache
    ports:
      - "6001:8080"
      - "6061:8081"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/home/app/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/home/app/.aspnet/https:ro
室内AP、室外AP分布式AP和楼栋AP都是无线接入点(Access Point)的不同类型。它们的主要区别在于使用场景和功能特点。 1. 室内AP:室内AP是指安装在室内的无线接入点,主要用于覆盖室内的无线信号。它通常具有较小的覆盖范围和较低的发射功率,适合在办公室、会议室、酒店、商场等室内场所使用。室内AP通常支持多种无线协议,如802.11a/b/g/n/ac等。 2. 室外AP:室外AP是指安装在室外的无线接入点,主要用于覆盖室外的无线信号。它通常具有较大的覆盖范围和较高的发射功率,适合在校园、广场、公园等室外场所使用。室外AP通常具有防水、防尘、抗干扰等特点,能够适应各种恶劣的室外环境。 3. 分布式AP分布式AP是指将多个AP连接在起,形成个统的无线网络。分布式AP可以提供更大的覆盖范围和更高的网络容量,适合在大型企业、学校、医院等场所使用。分布式AP通常具有自动负载均衡、自动信道选择、自动干扰避免等功能,能够提高网络的可靠性和稳定性。 4. 楼栋AP:楼栋AP是指将多个AP安装在个楼栋内,形成个楼栋级别的无线网络。楼栋AP可以提供更大的覆盖范围和更高的网络容量,适合在大型住宅小区、写字楼等场所使用。楼栋AP通常具有自动信道选择、自动干扰避免等功能,能够提高网络的可靠性和稳定性。 --相关问题--: 1. 什么是WLAN? 2. 无线网络有哪些
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值