VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > 编程开发 > c#编程 >
  • C#中关于微服务基础——API网关设计

第八部分:微服务架构设计
8. 微服务基础——API网关设计
实例介绍
之前做电商微服务架构时,每个服务都暴露独立的API,前端需要记住十几个服务的地址和端口,对接成本极高;每个服务都要实现Token校验、权限控制、限流熔断,代码重复率达到60%;服务迭代时接口版本混乱,经常出现前端调vb.net教程C#教程python教程SQL教程access 2010教程用旧接口导致的兼容性问题。引入API网关后,所有请求通过统一入口转发,前端只需记住网关地址;鉴权、限流、日志等横切关注点统一在网关实现,服务代码减少60%;通过路由规则实现接口版本管理,兼容性问题彻底解决。这节就带你从零设计和实现API网关,包括路由转发、统一鉴权、限流熔断、灰度发布、监控日志、协议转换,覆盖微服务网关的核心场景。
需求分析
API网关要解决“统一入口、横切关注点复用、服务治理、版本管理、高可用性、可扩展性”的问题,具体需求如下:
1.统一入口:所有客户端请求通过网关转发到后端服务,前端无需记住多个服务地址;
2.路由转发:根据请求路径、方法、Header等条件,将请求转发到对应的后端服务;
3.统一鉴权:集中处理用户身份验证、权限控制,避免每个服务重复实现;
4.限流熔断:对请求进行流量控制,防止服务过载;实时监控服务健康状态,自动熔断降级;
5.灰度发布:将部分请求转发到新版本服务,实现平滑升级;
6.监控日志:记录所有请求的日志,监控网关和后端服务的性能指标;
7.协议转换:支持REST、gRPC、WebSocket等多种协议转换,适配不同服务;
8.响应聚合:将多个服务的响应聚合后返回给客户端,减少网络请求次数;
9.高可用性:支持集群部署,避免单点故障;
10.可扩展性:通过插件化架构,方便添加新功能(如缓存、参数校验);
代码实现
前置条件:.NET 8 SDK;Visual Studio 2022;需安装以下NuGet包:
Yarp.ReverseProxy:微软官方的反向代理库,用于实现API网关
Microsoft.IdentityModel.Tokens:JWT令牌验证
Polly:熔断降级库
Serilog.AspNetCore:日志框架
Prometheus.AspNetCore:监控指标收集
场景1:Yarp反向代理——基础路由转发
使用Yarp实现API网关的基础路由转发功能,将请求转发到后端服务。
步骤1:创建ASP.NET Core项目
1.打开Visual Studio → 新建项目 → 选择“ASP.NET Core Web应用”模板;
2.命名项目为ApiGateway → 选择.NET 8框架;
3.安装NuGet包:Yarp.ReverseProxy;
步骤2:配置路由规则(appsettings.json)
json

	{
	"Logging": {
	"LogLevel": {
	"Default": "Information",
	"Microsoft.AspNetCore": "Warning"
	}
	},
	"ReverseProxy": {
	"Routes": {
	// 用户服务路由
	"user-service-route": {
	"ClusterId": "user-service-cluster",
	"Match": {
	"Path": "/api/users/{**catch-all}"
	},
	"Transforms": [
	{ "RemoveRequestHeader": "X-Forwarded-For" },
	{ "PathRemovePrefix": "/api/users" }
	]
	},
	// 订单服务路由
	"order-service-route": {
	"ClusterId": "order-service-cluster",
	"Match": {
	"Path": "/api/orders/{**catch-all}"
	},
	"Transforms": [
	{ "RemoveRequestHeader": "X-Forwarded-For" },
	{ "PathRemovePrefix": "/api/orders" }
	]
	},
	// 商品服务路由
	"product-service-route": {
	"ClusterId": "product-service-cluster",
	"Match": {
	"Path": "/api/products/{**catch-all}"
	},
	"Transforms": [
	{ "RemoveRequestHeader": "X-Forwarded-For" },
	{ "PathRemovePrefix": "/api/products" }
	]
	}
	},
	"Clusters": {
	// 用户服务集群
	"user-service-cluster": {
	"Destinations": {
	"user-service-1": {
	"Address": "https://localhost:5002/"
	},
	"user-service-2": {
	"Address": "https://localhost:5003/"
	}
	},
	"LoadBalancingPolicy": "RoundRobin" // 负载均衡策略:轮询
	},
	// 订单服务集群
	"order-service-cluster": {
	"Destinations": {
	"order-service-1": {
	"Address": "https://localhost:5004/"
	}
	}
	},
	// 商品服务集群
	"product-service-cluster": {
	"Destinations": {
	"product-service-1": {
	"Address": "https://localhost:5005/"
	}
	}
	}
	}
	},
	"AllowedHosts": "*"
	}

步骤3:配置Yarp服务(Program.cs)
csharp

	using Yarp.ReverseProxy.Configuration;
	
	var builder = WebApplication.CreateBuilder(args);
	
	// 添加Yarp反向代理服务
	builder.Services.AddReverseProxy()
	.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
	
	var app = builder.Build();
	
	// 启用Yarp反向代理
	app.MapReverseProxy();
	
	app.Run();

场景2:统一鉴权——JWT令牌验证
在网关中实现JWT令牌验证,只有携带有效令牌的请求才能访问后端服务。
步骤1:添加JWT验证配置(appsettings.json)
json

	{
	"Jwt": {
	"Issuer": "ApiGateway",
	"Audience": "Microservices",
	"SecretKey": "YourSuperSecretKeyThatIsAtLeast32CharactersLong!"
	}
	}

步骤2:实现JWT验证中间件(Middleware/JwtAuthenticationMiddleware.cs)
csharp

	using Microsoft.IdentityModel.Tokens;
	using System.IdentityModel.Tokens.Jwt;
	using System.Text;
	
	namespace ApiGateway.Middleware;
	
	public class JwtAuthenticationMiddleware
	{
	private readonly RequestDelegate _next;
	private readonly IConfiguration _configuration;
	
	public JwtAuthenticationMiddleware(RequestDelegate next, IConfiguration configuration)
	{
	_next = next;
	_configuration = configuration;
	}
	
	public async Task InvokeAsync(HttpContext context)
	{
	// 跳过登录、注册等无需鉴权的接口
	var path = context.Request.Path;
	if (path.StartsWithSegments("/api/auth") || path.StartsWithSegments("/health"))
	{
	await _next(context);
	return;
	}
	
	// 从Header获取Token
	var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
	if (string.IsNullOrEmpty(token))
	{
	context.Response.StatusCode = StatusCodes.Status401Unauthorized;
	await context.Response.WriteAsync("未提供授权令牌");
	return;
	}
	
	try
	{
	// 验证Token
	var tokenHandler = new JwtSecurityTokenHandler();
	var key = Encoding.ASCII.GetBytes(_configuration["Jwt:SecretKey"]!);
	tokenHandler.ValidateToken(token, new TokenValidationParameters
	{
	ValidateIssuerSigningKey = true,
	IssuerSigningKey = new SymmetricSecurityKey(key),
	ValidateIssuer = true,
	ValidIssuer = _configuration["Jwt:Issuer"],
	ValidateAudience = true,
	ValidAudience = _configuration["Jwt:Audience"],
	ClockSkew = TimeSpan.Zero // 关闭时钟偏移容忍
	}, out SecurityToken validatedToken);
	
	// 从Token中获取用户信息
	var jwtToken = (JwtSecurityToken)validatedToken;
	var userId = jwtToken.Claims.First(x => x.Type == "userId").Value;
	var roles = jwtToken.Claims.Where(x => x.Type == "role").Select(x => x.Value).ToList();
	
	// 将用户信息存入HttpContext,供后续中间件使用
	context.Items["UserId"] = userId;
	context.Items["Roles"] = roles;
	
	await _next(context);
	}
	catch (Exception ex)
	{
	context.Response.StatusCode = StatusCodes.Status401Unauthorized;
	await context.Response.WriteAsync($"授权失败:{ex.Message}");
	}
	}
	}
	
	// 中间件扩展方法
	public static class JwtAuthenticationMiddlewareExtensions
	{
	public static IApplicationBuilder UseJwtAuthentication(this IApplicationBuilder builder)
	{
	return builder.UseMiddleware<JwtAuthenticationMiddleware>();
	}
	}

步骤3:注册中间件(Program.cs)
csharp

	using ApiGateway.Middleware;
	using Yarp.ReverseProxy.Configuration;
	
	var builder = WebApplication.CreateBuilder(args);
	
	builder.Services.AddReverseProxy()
	.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
	
	var app = builder.Build();
	
	// 启用JWT认证中间件(在MapReverseProxy之前)
	app.UseJwtAuthentication();
	
	app.MapReverseProxy();
	
	app.Run();

场景3:限流熔断——Polly集成
使用Polly实现限流和熔断降级,保护后端服务。
步骤1:实现限流熔断中间件(Middleware/PollyMiddleware.cs)
csharp

	using Polly;
	using Polly.CircuitBreaker;
	using Polly.RateLimit;
	
	namespace ApiGateway.Middleware;
	
	public class PollyMiddleware
	{
	private readonly RequestDelegate _next;
	// 限流策略:每秒最多处理100个请求
	private readonly AsyncRateLimitPolicy _rateLimitPolicy = Policy.RateLimitAsync(100, TimeSpan.FromSeconds(1));
	// 熔断策略:连续5次失败后熔断30秒
	private readonly AsyncCircuitBreakerPolicy _circuitBreakerPolicy = Policy.Handle<Exception>()
	.CircuitBreakerAsync(5, TimeSpan.FromSeconds(30),
	(ex, duration) => Console.WriteLine($"服务熔断,持续时间:{duration.TotalSeconds}秒"),
	() => Console.WriteLine("服务恢复"));
	
	public PollyMiddleware(RequestDelegate next)
	{
	_next = next;
	}
	
	public async Task InvokeAsync(HttpContext context)
	{
	try
	{
	// 执行限流和熔断策略
	await Policy.WrapAsync(_rateLimitPolicy, _circuitBreakerPolicy)
	.ExecuteAsync(async () => await _next(context));
	}
	catch (RateLimitRejectedException)
	{
	context.Response.StatusCode = StatusCodes.Status429TooManyRequests;
	await context.Response.WriteAsync("请求过于频繁,请稍后再试");
	}
	catch (BrokenCircuitException)
	{
	context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
	await context.Response.WriteAsync("服务暂时不可用,请稍后再试");
	}
	}
	}
	
	// 中间件扩展方法
	public static class PollyMiddlewareExtensions
	{
	public static IApplicationBuilder UsePolly(this IApplicationBuilder builder)
	{
	return builder.UseMiddleware<PollyMiddleware>();
	}
	}

步骤2:注册中间件(Program.cs)
csharp

	using ApiGateway.Middleware;
	using Yarp.ReverseProxy.Configuration;
	
	var builder = WebApplication.CreateBuilder(args);
	
	builder.Services.AddReverseProxy()
	.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
	
	var app = builder.Build();
	
	app.UseJwtAuthentication();
	app.UsePolly(); // 启用限流熔断中间件
	
	app.MapReverseProxy();
	
	app.Run();

场景4:灰度发布——基于Header的路由规则
通过请求Header中的版本信息,将部分请求转发到新版本服务。
步骤1:配置灰度路由规则(appsettings.json)
json

	{
	"ReverseProxy": {
	"Routes": {
	"product-service-route": {
	"ClusterId": "product-service-cluster",
	"Match": {
	"Path": "/api/products/{**catch-all}"
	},
	"Transforms": [
	{ "RemoveRequestHeader": "X-Forwarded-For" },
	{ "PathRemovePrefix": "/api/products" }
	],
	"Metadata": {
	"GrayRelease": {
	"HeaderName": "X-API-Version",
	"HeaderValue": "v2",
	"GrayClusterId": "product-service-cluster-v2"
	}
	}
	}
	},
	"Clusters": {
	"product-service-cluster": {
	"Destinations": {
	"product-service-1": {
	"Address": "https://localhost:5005/"
	}
	}
	},
	"product-service-cluster-v2": {
	"Destinations": {
	"product-service-v2-1": {
	"Address": "https://localhost:5006/"
	}
	}
	}
	}
	}
	}

步骤2:实现灰度发布中间件(Middleware/GrayReleaseMiddleware.cs)
csharp

	using Yarp.ReverseProxy.Configuration;
	using Yarp.ReverseProxy.Transforms;
	
	namespace ApiGateway.Middleware;
	
	public class GrayReleaseMiddleware
	{
	private readonly RequestDelegate _next;
	private readonly IProxyConfigProvider _proxyConfigProvider;
	
	public GrayReleaseMiddleware(RequestDelegate next, IProxyConfigProvider proxyConfigProvider)
	{
	_next = next;
	_proxyConfigProvider = proxyConfigProvider;
	}
	
	public async Task InvokeAsync(HttpContext context)
	{
	var route = context.GetRoute();
	if (route == null)
	{
	await _next(context);
	return;
	}
	
	// 检查路由是否配置了灰度发布规则
	if (route.Metadata.TryGetValue("GrayRelease", out var grayReleaseConfig) &&
	grayReleaseConfig is Dictionary<string, string> config)
	{
	var headerName = config["HeaderName"];
	var headerValue = config["HeaderValue"];
	var grayClusterId = config["GrayClusterId"];
	
	// 检查请求Header是否匹配灰度规则
	if (context.Request.Headers.TryGetValue(headerName, out var requestHeaderValue) &&
	requestHeaderValue == headerValue)
	{
	// 将请求转发到灰度集群
	var proxyConfig = _proxyConfigProvider.GetConfig();
	var grayCluster = proxyConfig.Clusters.FirstOrDefault(c => c.ClusterId == grayClusterId);
	if (grayCluster != null)
	{
	context.Items["Cluster"] = grayCluster;
	}
	}
	}
	
	await _next(context);
	}
	}
	
	// 中间件扩展方法
	public static class GrayReleaseMiddlewareExtensions
	{
	public static IApplicationBuilder UseGrayRelease(this IApplicationBuilder builder)
	{
	return builder.UseMiddleware<GrayReleaseMiddleware>();
	}
	}

步骤3:注册中间件(Program.cs)
csharp

	using ApiGateway.Middleware;
	using Yarp.ReverseProxy.Configuration;
	
	var builder = WebApplication.CreateBuilder(args);
	
	builder.Services.AddReverseProxy()
	.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
	
	var app = builder.Build();
	
	app.UseJwtAuthentication();
	app.UsePolly();
	app.UseGrayRelease(); // 启用灰度发布中间件
	
	app.MapReverseProxy();
	
	app.Run();

场景5:监控日志——Serilog+Prometheus集成
使用Serilog记录请求日志,使用Prometheus监控网关性能指标。
步骤1:配置Serilog日志(Program.cs)
csharp

	using ApiGateway.Middleware;
	using Serilog;
	using Yarp.ReverseProxy.Configuration;
	
	// 配置Serilog日志
	Log.Logger = new LoggerConfiguration()
	.WriteTo.Console()
	.WriteTo.File("logs/gateway-.txt", rollingInterval: RollingInterval.Day)
	.CreateLogger();
	
	var builder = WebApplication.CreateBuilder(args);
	
	builder.Host.UseSerilog(); // 替换默认日志框架
	
	builder.Services.AddReverseProxy()
	.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
	
	// 添加Prometheus监控
	builder.Services.AddEndpointsApiExplorer();
	builder.Services.AddSwaggerGen();
	builder.Services.AddMetrics();
	
	var app = builder.Build();
	
	app.UseSwagger();
	app.UseSwaggerUI();
	
	app.UseJwtAuthentication();
	app.UsePolly();
	app.UseGrayRelease();
	
	// 启用Prometheus指标端点
	app.UseMetricsAllMiddleware();
	app.UseMetricsAllEndpoints();
	
	app.MapReverseProxy();
	
	app.Run();

逐行讲解
场景1:Yarp反向代理核心代码
1.Routes配置:
user-service-route:定义用户服务的路由规则,匹配路径/api/users/**;
ClusterId:指定路由对应的后端服务集群;
Transforms:请求转换规则,如移除前缀/api/users,将/api/users/123转发到/123;
2.Clusters配置:
user-service-cluster:定义用户服务的集群,包含两个实例;
LoadBalancingPolicy:负载均衡策略,RoundRobin表示轮询;
3.MapReverseProxy:启用Yarp反向代理,处理所有请求;
场景2:JWT验证中间件核心代码
1.InvokeAsync:
跳过无需鉴权的接口(如登录、健康检查);
从Header获取Token,验证Token的合法性;
将用户信息存入HttpContext,供后续中间件使用;
2.TokenValidationParameters:
ValidateIssuerSigningKey:验证签名密钥;
ValidIssuer:验证Token的发行者;
ValidAudience:验证Token的受众;
ClockSkew:关闭时钟偏移容忍,确保Token严格在有效期内;
场景3:Polly限流熔断核心代码
1.RateLimitPolicy:
每秒最多处理100个请求,超过限制返回429状态码;
2.CircuitBreakerPolicy:
连续5次请求失败后,熔断30秒,期间所有请求返回503状态码;
3.Policy.WrapAsync:将限流和熔断策略组合,先执行限流,再执行熔断;
基础知识拓展

  1. API网关架构模式
    集中式网关:所有请求通过单一网关转发,适合中小规模微服务架构;
    微网关:每个业务域部署独立的网关,适合大规模、复杂的微服务架构;
    服务网格:通过Sidecar代理实现服务间通信,如Istio、Linkerd,适合云原生架构;
  2. 负载均衡策略对比
    策略 特点 适用场景
    轮询(Round Robin) 按顺序将请求分发到每个服务实例 所有服务实例性能相近的场景
    最少连接(Least Connections) 将请求分发到当前连接数最少的服务实例 服务实例处理请求时间差异较大的场景
    加权轮询(Weighted Round Robin) 根据服务实例的权重分发请求,权重越高处理越多请求 服务实例性能差异较大的场景
    一致性哈希(Consistent Hashing) 根据请求参数(如用户ID)哈希到固定服务实例 需要会话保持的场景(如购物车服务)
  3. 限流算法对比
    令牌桶算法:以固定速率生成令牌,请求需要获取令牌才能通过,支持突发流量;
    漏桶算法:请求以固定速率处理,超出部分排队等待,适合平滑流量;
    计数器算法:统计单位时间内的请求数,超过阈值则拒绝,实现简单但存在临界问题;
  4. 熔断降级模式
    关闭状态:正常转发请求,监控服务健康状态;
    打开状态:服务故障时,直接返回降级响应,不转发请求;
    半开状态:定期尝试转发请求,若成功则恢复正常,否则继续保持打开状态;
  5. API网关与服务网格对比
特性 API网关 服务网格
部署方式 独立部署的中心化服务 每个服务实例部署Sidecar代理
适用场景 客户端到服务端的流量管理 服务端到服务端的流量管理
功能范围 路由、鉴权、限流、监控 服务发现、负载均衡、熔断、链路追踪
复杂度 低,易于部署和维护 高,需要管理大量Sidecar代理

总结
API网关的核心是统一入口、横切关注点复用、服务治理,让微服务架构更简洁、更易于维护。关键要点:
1.路由转发:使用Yarp实现反向代理,根据规则将请求转发到后端服务;
2.统一鉴权:集中处理JWT令牌验证,避免每个服务重复实现;
3.限流熔断:使用Polly实现流量控制和服务熔断,保护后端服务;
4.灰度发布:通过中间件实现请求分流,支持平滑升级;
5.监控日志:使用Serilog记录日志,Prometheus监控性能指标;
6.高可用性:支持集群部署,通过负载均衡避免单点故障;
比如这个API网关,统一处理鉴权、限流、日志等功能,服务代码减少60%;通过灰度发布实现服务平滑升级,零 downtime;监控系统实时跟踪网关和服务性能,故障响应时间从2小时缩短到5分钟。掌握API网关设计,你就能构建稳定、高效、可维护的微服务架构!

本站原创,转载请注明出处:https://www.xin3721.com/ArticlecSharp/c49489.html


相关教程