用「现代化餐厅」的例子讲透微服务技术

29 阅读9分钟

想象你要经营一个餐厅

传统餐厅(单体应用)

csharp

// 就像一个传统大厨房,所有厨师挤在一起
public class 传统餐厅
{
    private List<厨师> 所有厨师 = new List<厨师>();
    
    public void 接受订单(订单 顾客订单)
    {
        // 1. 接待员处理订单(Web层)
        var 处理后的订单 = 接待员.处理订单(顾客订单);
        
        // 2. 同一个厨房处理所有菜品(业务层)
        if (处理后的订单.菜品类型 == "中餐")
        {
            厨师A.做中餐(处理后的订单);
        }
        else if (处理后的订单.菜品类型 == "西餐")
        {
            厨师B.做西餐(处理后的订单);
        }
        else if (处理后的订单.菜品类型 == "甜点")
        {
            厨师C.做甜点(处理后的订单);
        }
        
        // 3. 统一收银(数据层)
        收银员.记录订单(处理后的订单);
    }
    
    // 问题:
    // 1. 中餐厨师忙,西餐厨师闲着也不能帮忙
    // 2. 厨房装修(部署)要停业整修
    // 3. 想增加日本料理,要改造整个厨房
}

微服务餐厅(现代化餐厅)

csharp

// 像现代化美食广场,每个摊位独立经营
public class 现代化美食广场
{
    // 每个摊位都是独立的服务
    private 中餐摊位 中餐服务 = new 中餐摊位();
    private 西餐摊位 西餐服务 = new 西餐摊位();
    private 甜点摊位 甜点服务 = new 甜点摊位();
    private 收银台服务 收银服务 = new 收银台服务();
    private 配送服务 配送服务 = new 配送服务();
    
    // 订单协调员(API网关)
    public async Task 处理订单(订单 顾客订单)
    {
        // 1. 订单协调员分配任务给各个摊位
        List<Task> 任务列表 = new List<Task>();
        
        if (顾客订单.包含中餐)
            任务列表.Add(中餐服务.制作菜品(顾客订单.中餐部分));
        
        if (顾客订单.包含西餐)
            任务列表.Add(西餐服务.制作菜品(顾客订单.西餐部分));
            
        if (顾客订单.包含甜点)
            任务列表.Add(甜点服务.制作甜点(顾客订单.甜点部分));
        
        // 2. 所有摊位并行工作
        await Task.WhenAll(任务列表);
        
        // 3. 收银服务独立计费
        await 收银服务.计算总价(顾客订单);
        
        // 4. 配送服务独立配送
        await 配送服务.安排配送(顾客订单);
    }
}

微服务架构的核心组件

1. 服务拆分 - 每个摊位独立经营

csharp

// 中餐服务(独立摊位)
public class 中餐服务
{
    private string 服务地址 = "http://chinese-service:8080";
    private 自己的数据库 数据库 = new 自己的数据库();
    
    public async Task<菜品> 制作菜品(订单 订单)
    {
        // 完全独立的中餐制作流程
        // 有自己的厨房、食材、厨师
        return await 做川菜(订单);
    }
    
    // 可以独立部署、独立扩展、独立升级
}

// 西餐服务(另一个独立摊位)
public class 西餐服务
{
    private string 服务地址 = "http://western-service:8081";
    private 另一个数据库 数据库 = new 另一个数据库();
    
    public async Task<菜品> 制作菜品(订单 订单)
    {
        // 完全独立的西餐制作流程
        return await 做法国大餐(订单);
    }
    
    // 可以用不同的技术栈:西餐服务用Java,中餐服务用C#
}

2. API网关 - 餐厅总服务台

csharp

public class 餐厅总服务台
{
    private readonly 用户认证服务 _认证服务;
    private readonly 订单路由服务 _路由服务;
    private readonly 限流服务 _限流服务;
    
    // 顾客只接触总服务台
    public async Task<响应> 处理顾客请求(HttpRequest 请求)
    {
        // 1. 统一认证(检查会员卡)
        var 用户 = await _认证服务.验证身份(请求);
        
        // 2. 限流控制(防止太多人同时点餐)
        await _限流服务.检查请求频率(用户);
        
        // 3. 智能路由(根据菜品分配到不同摊位)
        if (请求.Path.Contains("/中餐"))
            return await 路由到中餐服务(请求);
        else if (请求.Path.Contains("/西餐"))
            return await 路由到西餐服务(请求);
        else if (请求.Path.Contains("/甜点"))
            return await 路由到甜点服务(请求);
        else
            return new 响应 { 状态码 = 404 };
    }
}

3. 服务发现 - 摊位位置导航

csharp

// 服务注册中心(像商场导航屏幕)
public class 摊位导航系统
{
    private Dictionary<string, 摊位信息> 所有摊位 = new Dictionary<string, 摊位信息>();
    
    // 新摊位开张时注册
    public void 注册摊位(string 摊位名称, string 地址, int 容量)
    {
        所有摊位[摊位名称] = new 摊位信息
        {
            地址 = 地址,
            状态 = "营业中",
            当前负载 = 0,
            最大容量 = 容量
        };
        
        Console.WriteLine($"{摊位名称}已注册,地址:{地址}");
    }
    
    // 顾客找摊位时使用
    public 摊位信息 查找最佳摊位(string 菜品类型)
    {
        var 候选摊位 = 所有摊位.Values
            .Where(摊位 => 摊位.状态 == "营业中")
            .Where(摊位 => 摊位.当前负载 < 摊位.最大容量)
            .OrderBy(摊位 => 摊位.当前负载)
            .FirstOrDefault();
            
        return 候选摊位;
    }
}

完整的微服务餐厅示例

项目结构:

text

美食广场/
├── 总服务台/           # API Gateway
├── 中餐服务/           # ChineseFoodService
├── 西餐服务/           # WesternFoodService
├── 甜点服务/           # DessertService
├── 收银服务/           # PaymentService
├── 配送服务/           # DeliveryService
└── 导航系统/           # Service Discovery

每个服务的独立配置:

csharp

// 中餐服务的Program.cs
var builder = WebApplication.CreateBuilder(args);

// 独立配置自己的数据库
builder.Services.AddDbContext<中餐数据库>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("中餐数据库")));

// 独立配置自己的缓存
builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = builder.Configuration.GetConnectionString("中餐缓存");
});

// 独立配置自己的API
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// 独立的路由
app.MapGet("/api/chinese/menu", () => 获取菜单());
app.MapPost("/api/chinese/order", (订单 订单) => 处理订单(订单));
app.MapGet("/api/chinese/status/{orderId}", (string orderId) => 查询状态(orderId));

app.Run();

// 这个服务可以:
// 1. 独立部署(不影响其他摊位)
// 2. 独立扩展(多开几个中餐窗口)
// 3. 独立升级(改进川菜做法)

服务间的通信:

csharp

public class 订单协调服务
{
    private readonly HttpClient _httpClient;
    private readonly 导航系统 _导航系统;
    
    public async Task 处理复杂订单(复杂订单 订单)
    {
        // 1. 并行调用多个服务(像同时让多个摊位开工)
        var 任务列表 = new List<Task>();
        
        // 中餐部分
        var 中餐摊位 = _导航系统.查找最佳摊位("中餐");
        var 中餐任务 = _httpClient.PostAsJsonAsync($"{中餐摊位.地址}/order", 订单.中餐部分);
        任务列表.Add(中餐任务);
        
        // 西餐部分
        var 西餐摊位 = _导航系统.查找最佳摊位("西餐");
        var 西餐任务 = _httpClient.PostAsJsonAsync($"{西餐摊位.地址}/order", 订单.西餐部分);
        任务列表.Add(西餐任务);
        
        // 2. 等待所有菜品完成
        await Task.WhenAll(任务列表);
        
        // 3. 调用收银服务
        var 收银摊位 = _导航系统.查找最佳摊位("收银");
        await _httpClient.PostAsJsonAsync($"{收银摊位.地址}/calculate", 订单);
        
        // 4. 调用配送服务
        var 配送摊位 = _导航系统.查找最佳摊位("配送");
        await _httpClient.PostAsJsonAsync($"{配送摊位.地址}/arrange", 订单);
    }
}

微服务的关键特性

1. 独立部署 - 每个摊位可以单独装修

csharp

// 传统餐厅:装修要停业
public class 传统餐厅装修
{
    public void 升级厨房()
    {
        停业();              // 整个餐厅不能营业
        拆除旧厨房();        // 影响所有菜品制作
        建造新厨房();        // 耗时很长
        重新开业();          // 风险很大
    }
}

// 微服务餐厅:逐个摊位装修
public class 美食广场升级
{
    public async Task 升级中餐摊位()
    {
        中餐服务.暂停营业();    // 只影响中餐
        await 部署新版本中餐服务();  // 不影响西餐、甜点
        中餐服务.恢复营业();    // 其他摊位正常营业
        
        // 顾客还是可以点西餐和甜点
    }
}

2. 弹性伸缩 - 热门摊位多开窗口

csharp

// 根据负载自动调整
public class 自动伸缩管理器
{
    public void 监控并调整()
    {
        // 监控每个摊位的负载
        foreach (var 摊位 in 所有摊位)
        {
            if (摊位.当前负载 > 摊位.最大负载的80%)
            {
                Console.WriteLine($"{摊位.名称}太忙了,增加一个窗口");
                增加服务实例(摊位);  // 自动扩容
            }
            else if (摊位.当前负载 < 摊位.最大负载的20%)
            {
                Console.WriteLine($"{摊位.名称}太闲了,减少一个窗口");
                减少服务实例(摊位);  // 自动缩容
            }
        }
    }
}

// 结果:
// 午餐时间:中餐服务开5个实例
// 下午茶时间:甜点服务开3个实例,中餐服务只开1个实例

3. 容错设计 - 一个摊位出问题不影响其他

csharp

public class 智能订单处理
{
    public async Task<订单结果> 处理订单(订单 订单)
    {
        try
        {
            // 尝试调用中餐服务
            return await 中餐服务.制作菜品(订单);
        }
        catch (Exception ex)
        {
            // 中餐摊位出问题了!
            Console.WriteLine("中餐服务故障,启用备选方案");
            
            // 方案1:重试(等一会儿再试试)
            await Task.Delay(1000);
            return await 中餐服务.制作菜品(订单);
            
            // 方案2:降级(用简单菜品代替)
            // return await 提供简餐(订单);
            
            // 方案3:熔断(暂时不点中餐,只点其他)
            // return new 订单结果 { 说明 = "中餐暂不可用,已为您取消中餐部分" };
        }
        finally
        {
            // 即使中餐出问题,西餐和甜点还能正常制作
            await 西餐服务.制作菜品(订单);
            await 甜点服务.制作甜点(订单);
        }
    }
}

实际案例:电商平台微服务化

传统电商(单体应用):

csharp

public class 传统电商系统
{
    // 所有功能挤在一起
    public void 处理用户请求()
    {
        // 用户认证
        // 商品查询
        // 库存检查
        // 订单创建
        // 支付处理
        // 物流安排
        // 所有代码在一个项目里,改一处就要重新部署整个系统
    }
}

微服务电商:

csharp

// 拆分成独立的服务
var 服务列表 = new Dictionary<string, string>
{
    {"用户服务", "负责注册、登录、用户信息"},
    {"商品服务", "负责商品目录、搜索、详情"},
    {"库存服务", "负责库存管理、补货提醒"},
    {"订单服务", "负责创建订单、订单状态"},
    {"支付服务", "负责支付处理、退款"},
    {"物流服务", "负责发货、物流跟踪"},
    {"推荐服务", "负责个性化推荐"},
    {"评价服务", "负责商品评价、评分"}
};

// 用户下单的流程:
public async Task 用户下单流程()
{
    // 1. 用户服务:验证用户身份 ✓
    // 2. 商品服务:检查商品信息 ✓
    // 3. 库存服务:扣减库存 ✓
    // 4. 订单服务:创建订单记录 ✓
    // 5. 支付服务:处理支付 ✓
    // 6. 物流服务:安排发货 ✓
    // 7. 推荐服务:更新推荐算法 ✓
    // 8. 评价服务:邀请用户评价 ✓
    
    // 每个服务都可以独立开发、部署、扩展
}

微服务的挑战和解决方案

挑战1:服务间通信复杂

csharp

// 解决方案:使用消息队列(像餐厅的传菜窗口)
public class 消息队列系统
{
    // 中餐摊位做好菜,放到传菜窗口
    public async Task 中餐完成(string 订单号, 菜品 做好的菜)
    {
        await 消息队列.发布消息("菜品完成", new 
        {
            订单号 = 订单号,
            菜品 = 做好的菜,
            摊位 = "中餐"
        });
    }
    
    // 配送服务监听传菜窗口
    public void 配送服务监听()
    {
        消息队列.订阅消息("菜品完成", async (消息) =>
        {
            if (消息.摊位 == "中餐" && 消息.订单号 == 我的订单号)
            {
                await 开始配送(消息.菜品);
            }
        });
    }
}

挑战2:数据一致性

csharp

// 解决方案:Saga模式(分布式事务)
public class 订单Saga协调器
{
    public async Task 处理订单事务(订单 订单)
    {
        try
        {
            // 步骤1:扣减库存
            await 库存服务.锁定库存(订单.商品, 订单.数量);
            
            // 步骤2:创建订单
            var 订单ID = await 订单服务.创建订单(订单);
            
            // 步骤3:扣款
            await 支付服务.扣款(订单.用户, 订单.总价);
            
            // 步骤4:安排发货
            await 物流服务.发货(订单ID, 订单.地址);
            
            // 所有步骤成功,事务完成
            Console.WriteLine("订单处理成功");
        }
        catch (Exception ex)
        {
            // 任何一个步骤失败,补偿前面的操作
            Console.WriteLine("订单处理失败,开始回滚");
            
            // 补偿逻辑
            await 库存服务.恢复库存(订单.商品, 订单.数量);
            await 订单服务.取消订单(订单ID);
            await 支付服务.退款(订单.用户, 订单.总价);
            // 物流还没开始,不用补偿
        }
    }
}

挑战3:监控和调试困难

csharp

// 解决方案:分布式追踪(像订单追踪单)
public class 订单追踪系统
{
    public async Task 处理订单(订单 订单)
    {
        // 生成唯一的追踪ID
        var 追踪ID = Guid.NewGuid().ToString();
        
        // 每个服务处理时都记录日志
        using (var 追踪 = 分布式追踪.开始追踪(追踪ID, "用户下单"))
        {
            // 记录每个步骤
            追踪.记录事件("开始用户认证");
            await 用户服务.认证(订单.用户);
            
            追踪.记录事件("开始库存检查");
            await 库存服务.检查(订单.商品);
            
            // ... 其他步骤
            
            // 最终可以在监控系统看到完整的调用链
            // 用户服务 → 库存服务 → 订单服务 → 支付服务 → 物流服务
        }
    }
}

总结比喻

微服务 = 现代化美食广场

  1. 传统餐厅(单体应用)

    • 一个大厨房做所有菜
    • 一个厨师生病,整个餐厅瘫痪
    • 想增加新菜系要改造整个厨房
    • 所有顾客挤在一个收银台
  2. 美食广场(微服务架构)

    • 独立摊位:每个摊位专门做一类菜(中餐、西餐、甜点等)
    • 独立经营:每个摊位有自己的厨师、食材、收银(独立数据库、独立部署)
    • 互不影响:中餐摊位装修,西餐摊位照常营业
    • 弹性伸缩:午餐时间多开中餐窗口,下午茶时间多开甜点窗口
    • 专业分工:每个摊位专注于自己的特色菜
    • 统一协调:总服务台(API网关)协调所有摊位

核心价值:

  • 灵活性:可以随时增加新摊位(新服务)
  • 弹性:一个摊位出问题不影响其他摊位
  • 可扩展:热门摊位可以多开几个窗口
  • 技术多样性:不同摊位可以用不同技术(中餐用炒锅,西餐用烤箱)
  • 独立部署:每个摊位可以单独升级装修

适用场景:

  • 大型复杂系统(像综合购物中心)
  • 需要快速迭代的系统(经常要推出新菜品)
  • 高可用性要求的系统(不能因为一个摊位出问题就整个关门)
  • 团队规模较大的项目(不同团队负责不同摊位)

这就是微服务:把一个庞大复杂的单体系统,拆分成多个小型、独立、专注的服务,像把一个大厨房改造成专业的美食广场!