MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C#微服务架构设计(基于gRPC和Ocelot)

2024-09-246.5k 阅读

1. 微服务架构概述

在当今的软件开发领域,微服务架构已经成为构建大型、复杂且可扩展应用程序的主流方式。与传统的单体架构不同,微服务架构将一个大型应用拆分成多个小型、独立的服务,每个服务都围绕着具体的业务功能进行构建,可以独立开发、部署和扩展。

微服务架构的核心优势在于其灵活性和可扩展性。每个微服务都可以根据自身业务需求选择最适合的技术栈,独立地进行升级和维护。这使得开发团队能够更加敏捷地响应业务变化,快速迭代产品功能。同时,当某个微服务出现故障时,不会影响整个系统的其他部分,从而提高了系统的可靠性和容错性。

2. gRPC 简介

gRPC 是由 Google 开发并开源的高性能、通用的 RPC(Remote Procedure Call,远程过程调用)框架。它基于 HTTP/2 协议设计,旨在提供高效、简洁的远程通信解决方案。

2.1 gRPC 核心概念

  • 服务定义:gRPC 使用 Protocol Buffers(protobuf)来定义服务接口和消息格式。Protobuf 是一种轻便高效的结构化数据存储格式,类似于 JSON 或 XML,但具有更高的性能和更小的空间占用。通过编写 .proto 文件,我们可以定义服务的方法以及输入输出参数的结构。
  • Stub:gRPC 客户端和服务器端通过生成的 Stub 进行通信。Stub 为客户端提供了与服务端方法对应的本地调用接口,使得客户端可以像调用本地方法一样调用远程服务。服务端则通过实现 Stub 中的接口方法来提供服务。
  • HTTP/2:gRPC 基于 HTTP/2 协议,这使得它具备了 HTTP/2 的诸多优势,如多路复用、二进制分帧、头部压缩等。这些特性大大提高了通信效率,减少了延迟,特别适合在高并发、低带宽环境下的远程调用。

2.2 gRPC 工作流程

  1. 定义服务:首先,我们在 .proto 文件中定义 gRPC 服务。例如,定义一个简单的 Greeter 服务:
syntax = "proto3";

package greet;

// 定义请求消息
message HelloRequest {
  string name = 1;
}

// 定义响应消息
message HelloReply {
  string message = 1;
}

// 定义服务
service Greeter {
  rpc SayHello(HelloRequest) returns (HelloReply);
}
  1. 生成代码:使用 protoc 工具根据定义的 .proto 文件生成不同语言的代码。对于 C#,可以使用 grpc_tools_csharp_plugin 生成客户端和服务器端的代码。
  2. 实现服务:在服务器端,实现 .proto 文件中定义的服务接口。例如,在 C# 中:
using Grpc.Core;
using System.Threading.Tasks;

namespace GreeterServer
{
    public class GreeterService : Greeter.GreeterBase
    {
        public override Task<HelloReply> SayHello(HelloRequest request, ServerCallContext context)
        {
            return Task.FromResult(new HelloReply
            {
                Message = "Hello, " + request.Name
            });
        }
    }
}
  1. 调用服务:在客户端,通过生成的 Stub 调用远程服务:
using Grpc.Net.Client;
using System;

namespace GreeterClient
{
    class Program
    {
        static async Task Main(string[] args)
        {
            using var channel = GrpcChannel.ForAddress("https://localhost:5001");
            var client = new Greeter.GreeterClient(channel);
            var reply = await client.SayHelloAsync(new HelloRequest { Name = "World" });
            Console.WriteLine("Greeting: " + reply.Message);
        }
    }
}

3. Ocelot 简介

Ocelot 是一个基于.Net 的开源 API 网关。在微服务架构中,API 网关扮演着重要的角色,它作为系统的单一入口,负责处理所有外部请求,并将请求路由到相应的微服务。

3.1 Ocelot 核心功能

  • 路由:Ocelot 可以根据请求的 URL、HTTP 方法等条件将请求路由到不同的微服务。通过配置文件,我们可以灵活地定义路由规则。
  • 负载均衡:当存在多个相同功能的微服务实例时,Ocelot 支持多种负载均衡算法,如轮询、随机等,将请求均匀地分配到各个实例上,提高系统的可用性和性能。
  • 认证与授权:Ocelot 可以集成多种认证和授权机制,如 JWT(JSON Web Token)认证,确保只有合法的请求才能访问微服务。

3.2 Ocelot 配置示例

  1. 安装 Ocelot 包:在项目中通过 NuGet 安装 Ocelot 包。
  2. 配置路由:在 appsettings.json 文件中配置 Ocelot 路由规则。例如,将请求 /api/greeter 路由到 Greeter 微服务:
{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/greeter/{**catchall}",
            "DownstreamScheme": "https",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5001
                }
            ],
            "UpstreamPathTemplate": "/api/greeter/{**catchall}",
            "UpstreamHttpMethod": [ "Get", "Post" ]
        }
    ]
}
  1. 在 Startup.cs 中配置 Ocelot
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

namespace OcelotGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot(Configuration);
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseOcelot().Wait();
        }
    }
}

4. C# 中基于 gRPC 和 Ocelot 的微服务架构设计

4.1 项目结构规划

在一个典型的基于 gRPC 和 Ocelot 的微服务项目中,我们可以按照以下结构进行组织:

  • Gateway:包含 Ocelot 网关项目,负责接收外部请求并路由到相应的微服务。
  • Services:存放各个微服务项目,每个微服务使用 gRPC 进行通信。
  • Protos:存放所有的 .proto 文件,这些文件定义了 gRPC 服务接口和消息格式。可以在多个微服务项目中共享这些文件,确保服务接口的一致性。

4.2 微服务间通信

  1. 服务定义与实现:以一个电商系统为例,假设我们有一个 ProductService 和一个 OrderService。在 Protos 目录下定义 product.protoorder.proto 文件。
// product.proto
syntax = "proto3";

package product;

message Product {
  string id = 1;
  string name = 2;
  double price = 3;
}

message GetProductRequest {
  string productId = 1;
}

message GetProductResponse {
  Product product = 1;
}

service ProductService {
  rpc GetProduct(GetProductRequest) returns (GetProductResponse);
}
// order.proto
syntax = "proto3";

package order;

import "product.proto";

message OrderItem {
  Product product = 1;
  int32 quantity = 2;
}

message CreateOrderRequest {
  repeated OrderItem items = 1;
}

message CreateOrderResponse {
  string orderId = 1;
}

service OrderService {
  rpc CreateOrder(CreateOrderRequest) returns (CreateOrderResponse);
}

ProductService 项目中实现 ProductService 接口:

using Grpc.Core;
using System.Threading.Tasks;

namespace ProductService
{
    public class ProductServiceImpl : ProductService.ProductServiceBase
    {
        public override Task<GetProductResponse> GetProduct(GetProductRequest request, ServerCallContext context)
        {
            // 模拟从数据库获取产品信息
            var product = new Product { Id = request.ProductId, Name = "Sample Product", Price = 10.0 };
            return Task.FromResult(new GetProductResponse { Product = product });
        }
    }
}

OrderService 项目中,通过 gRPC 调用 ProductService 获取产品信息,然后创建订单:

using Grpc.Core;
using Grpc.Net.Client;
using System.Threading.Tasks;

namespace OrderService
{
    public class OrderServiceImpl : OrderService.OrderServiceBase
    {
        public override async Task<CreateOrderResponse> CreateOrder(CreateOrderRequest request, ServerCallContext context)
        {
            using var channel = GrpcChannel.ForAddress("https://localhost:5002");
            var productClient = new ProductService.ProductServiceClient(channel);
            var orderId = "12345";
            foreach (var item in request.Items)
            {
                var productRequest = new GetProductRequest { ProductId = item.Product.Id };
                var productResponse = await productClient.GetProductAsync(productRequest);
                // 根据获取的产品信息计算订单总价等逻辑
            }
            return new CreateOrderResponse { OrderId = orderId };
        }
    }
}

4.3 网关配置

Gateway 项目中,配置 Ocelot 路由规则,将外部请求路由到 ProductServiceOrderService

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/product/{**catchall}",
            "DownstreamScheme": "https",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5002
                }
            ],
            "UpstreamPathTemplate": "/api/product/{**catchall}",
            "UpstreamHttpMethod": [ "Get" ]
        },
        {
            "DownstreamPathTemplate": "/api/order/{**catchall}",
            "DownstreamScheme": "https",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5003
                }
            ],
            "UpstreamPathTemplate": "/api/order/{**catchall}",
            "UpstreamHttpMethod": [ "Post" ]
        }
    ]
}

5. 安全与认证

在微服务架构中,安全与认证是至关重要的。gRPC 和 Ocelot 都提供了相应的机制来保障系统的安全性。

5.1 gRPC 安全

  1. TLS 加密:gRPC 支持通过 TLS(Transport Layer Security)进行通信加密。在服务器端,可以配置证书来启用 TLS。例如,在 Kestrel 服务器中,可以在 Program.cs 中配置:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace ProductService
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
               .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseKestrel(options =>
                    {
                        options.ConfigureHttpsDefaults(httpsOptions =>
                        {
                            httpsOptions.ServerCertificate = LoadCertificate();
                        });
                    });
                    webBuilder.UseStartup<Startup>();
                });

        private static X509Certificate2 LoadCertificate()
        {
            // 加载证书逻辑
            return new X509Certificate2("certificate.pfx", "password");
        }
    }
}

在客户端,通过 GrpcChannelOptions 配置 TLS 相关参数:

using Grpc.Net.Client;
using System;

namespace OrderService
{
    class Program
    {
        static async Task Main(string[] args)
        {
            var httpHandler = new HttpClientHandler();
            httpHandler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;

            var channel = GrpcChannel.ForAddress("https://localhost:5002", new GrpcChannelOptions
            {
                HttpHandler = httpHandler
            });
            var client = new ProductService.ProductServiceClient(channel);
            // 调用服务逻辑
        }
    }
}
  1. 认证:gRPC 支持多种认证方式,如基于令牌的认证。可以在客户端请求中添加认证令牌,在服务器端通过拦截器验证令牌。例如,创建一个自定义拦截器:
using Grpc.Core;
using Grpc.Core.Interceptors;
using System;

public class AuthenticationInterceptor : Interceptor
{
    public override async Task<TResponse> UnaryServerHandler<TRequest, TResponse>(
        TRequest request,
        ServerCallContext context,
        UnaryServerMethod<TRequest, TResponse> continuation)
    {
        var token = context.RequestHeaders.GetValue("Authorization")?.ToString().Replace("Bearer ", "");
        if (string.IsNullOrEmpty(token) ||!ValidateToken(token))
        {
            throw new RpcException(new Status(StatusCode.Unauthenticated, "Invalid token"));
        }
        return await continuation(request, context);
    }

    private bool ValidateToken(string token)
    {
        // 验证令牌逻辑
        return true;
    }
}

在服务器端注册拦截器:

using Grpc.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace ProductService
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc(options =>
            {
                options.Interceptors.Add<AuthenticationInterceptor>();
            });
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<ProductServiceImpl>();
            });
        }
    }
}

5.2 Ocelot 安全

  1. 认证与授权:Ocelot 可以集成 JWT 认证。首先,安装 Ocelot.Provider.Authentication 包。然后,在 appsettings.json 中配置认证:
{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/product/{**catchall}",
            "DownstreamScheme": "https",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5002
                }
            ],
            "UpstreamPathTemplate": "/api/product/{**catchall}",
            "UpstreamHttpMethod": [ "Get" ],
            "AuthenticationOptions": {
                "AuthenticationProviderKey": "TestKey",
                "AllowedScopes": []
            }
        }
    ],
    "GlobalConfiguration": {
        "AuthenticationProviders": [
            {
                "Scheme": "Bearer",
                "Name": "TestKey",
                "Handler": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler",
                "Options": {
                    "Authority": "https://localhost:5000",
                    "Audience": "resource_server"
                }
            }
        ]
    }
}

Startup.cs 中配置认证服务:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;

namespace OcelotGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot(Configuration).AddAuthentication();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseAuthentication();
            app.UseOcelot().Wait();
        }
    }
}

6. 监控与日志

在微服务架构中,监控与日志对于系统的运维和故障排查至关重要。

6.1 监控

  1. gRPC 监控:gRPC 本身提供了一些内置的监控指标,如请求次数、响应时间等。可以通过 Prometheus 和 Grafana 来收集和展示这些指标。首先,安装 Grpc.AspNetCore.Server.Reflection 包,启用 gRPC 反射,以便 Prometheus 能够发现服务。 在 Startup.cs 中配置:
using Grpc.AspNetCore.Server.Reflection;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace ProductService
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
            services.AddGrpcReflection();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<ProductServiceImpl>();
                endpoints.MapGrpcReflectionService();
            });
        }
    }
}

然后,配置 Prometheus 抓取 gRPC 服务的指标。在 prometheus.yml 文件中添加:

scrape_configs:
  - job_name: 'grpc_service'
    static_configs:
      - targets: ['localhost:5002']
    metrics_path: /metrics
    params:
      module: [grpc]
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: localhost:9115

最后,通过 Grafana 连接 Prometheus,创建仪表盘展示 gRPC 服务的监控指标。 2. Ocelot 监控:Ocelot 可以通过集成 Ocelot.Metrics 包来提供监控指标。安装包后,在 appsettings.json 中配置:

{
    "ReRoutes": [
        {
            "DownstreamPathTemplate": "/api/product/{**catchall}",
            "DownstreamScheme": "https",
            "DownstreamHostAndPorts": [
                {
                    "Host": "localhost",
                    "Port": 5002
                }
            ],
            "UpstreamPathTemplate": "/api/product/{**catchall}",
            "UpstreamHttpMethod": [ "Get" ]
        }
    ],
    "MetricsOptions": {
        "Enabled": true,
        "ExcludedRoutes": [],
        "Prometheus": {
            "Version": "v2",
            "Endpoint": "/metrics"
        }
    }
}

Startup.cs 中配置:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Ocelot.Metrics;

namespace OcelotGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot(Configuration).AddMetrics();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.UseOcelot().Wait();
            app.UseMetricServer();
            app.UseHttpMetrics();
        }
    }
}

Prometheus 可以通过配置抓取 Ocelot 网关的指标,同样可以在 Grafana 中展示。

6.2 日志

  1. gRPC 日志:在 gRPC 服务中,可以使用 ILogger 接口进行日志记录。在 Startup.cs 中注入 ILogger
using Grpc.Core;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

namespace ProductService
{
    public class Startup
    {
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddGrpc();
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
        {
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGrpcService<ProductServiceImpl>();
            });
            logger.LogInformation("ProductService started");
        }
    }
}

在服务实现类中使用 ILogger

using Grpc.Core;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;

namespace ProductService
{
    public class ProductServiceImpl : ProductService.ProductServiceBase
    {
        private readonly ILogger<ProductServiceImpl> _logger;

        public ProductServiceImpl(ILogger<ProductServiceImpl> logger)
        {
            _logger = logger;
        }

        public override Task<GetProductResponse> GetProduct(GetProductRequest request, ServerCallContext context)
        {
            _logger.LogInformation($"Received request for product {request.ProductId}");
            var product = new Product { Id = request.ProductId, Name = "Sample Product", Price = 10.0 };
            return Task.FromResult(new GetProductResponse { Product = product });
        }
    }
}
  1. Ocelot 日志:Ocelot 可以集成多种日志框架,如 Serilog。首先,安装 Serilog.AspNetCoreSerilog.Sinks.File 包。然后,在 Program.cs 中配置 Serilog:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;

namespace OcelotGateway
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Log.Logger = new LoggerConfiguration()
               .WriteTo.File("ocelot.log", rollingInterval: RollingInterval.Day)
               .CreateLogger();

            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
               .UseSerilog()
               .ConfigureWebHostDefaults(webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
}

在 Ocelot 配置中,可以通过中间件记录请求和响应日志:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
using Ocelot.Middleware;
using Serilog;

namespace OcelotGateway
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddOcelot(Configuration);
        }

        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            app.Use(async (context, next) =>
            {
                Log.Information($"Request to {context.Request.Path} started");
                await next();
                Log.Information($"Request to {context.Request.Path} completed with status code {context.Response.StatusCode}");
            });
            app.UseOcelot().Wait();
        }
    }
}

通过以上步骤,我们详细介绍了如何在 C# 中基于 gRPC 和 Ocelot 设计微服务架构,涵盖了服务间通信、安全认证、监控与日志等关键方面,希望能为您构建高效、可靠的微服务系统提供帮助。