Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/microwind/ai-skills --skill api명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | API网关设计 |
| description | 当设计API网关时,分析路由策略,优化性能负载,解决安全问题。验证网关架构,设计统一入口,和最佳实践。 |
| license | MIT |
API网关是微服务架构中的关键组件,作为所有客户端请求的统一入口点。API网关负责路由转发、负载均衡、认证授权、限流熔断、监控日志等功能。不当的网关设计会导致性能瓶颈、单点故障、安全风险。
核心原则: 好的API网关应该高性能、高可用、安全可靠、易于扩展。坏的网关会成为系统瓶颈,影响整个微服务架构。
始终:
触发短语:
问题: 网关成为性能瓶颈
原因: 单点架构,处理能力不足
解决: 水平扩展,异步处理,缓存优化
问题: 高并发下响应延迟
原因: 同步阻塞处理,资源竞争
解决: 异步非阻塞,连接池优化
问题: 网关单点故障
原因: 缺乏高可用设计
解决: 集群部署,健康检查,故障转移
问题: 服务依赖故障传播
原因: 缺乏熔断机制
解决: 实现熔断降级,超时控制
// Spring Cloud Gateway 路由配置
@Configuration
public class GatewayConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-service", r -> r.path("/api/users/**")
.filters(f -> f.stripPrefix(2)
.circuitBreaker(config -> config.setName("userCircuit"))
.retry(retryConfig -> retryConfig.setRetries(3))
.requestRateLimiter(config -> config
.setKeyResolver(userKeyResolver())
.setRateLimit(100)))
.uri("lb://user-service"))
.route("order-service", r -> r.path("/api/orders/**")
.filters(f -> f.stripPrefix(2)
.addRequestHeader("X-Request-Source", "gateway")
.addResponseHeader("X-Response-Source", "gateway"))
.uri("lb://order-service"))
.route("product-service", r -> r.path("/api/products/**")
.filters(f -> f.stripPrefix(2)
.hystrix(config -> config.setName("productCircuit"))
.fallbackUri("forward:/fallback"))
.uri("lb://product-service"))
.build();
}
@Bean
public KeyResolver userKeyResolver() {
return exchange -> exchange.getRequest()
.getHeaders().getFirst()
.map(userId -> (String) userId)
.defaultIfEmpty();
}
}
, Ordered {
JwtTokenUtil jwtTokenUtil;
Mono<Void> {
exchange.getRequest();
(isPublicPath(request.getPath().value())) {
chain.filter(exchange);
}
extractToken(request);
(token == || !jwtTokenUtil.validateToken(token)) {
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
exchange.getResponse().setComplete();
}
jwtTokenUtil.getUsernameFromToken(token);
exchange.getRequest().mutate()
.header(, username)
.build();
chain.filter(exchange.mutate().request(modifiedRequest).build());
}
{
-;
}
{
path.startsWith() ||
path.equals() ||
path.equals();
}
String {
request.getHeaders().getFirst();
(bearerToken != && bearerToken.startsWith()) {
bearerToken.substring();
}
;
}
}
, Ordered {
Map<String, RateLimiter> rateLimiters = <>();
Mono<Void> {
getClientId(exchange.getRequest());
rateLimiters.computeIfAbsent(clientId,
id -> RateLimiter.create());
(rateLimiter.tryAcquire()) {
chain.filter(exchange);
} {
exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
exchange.getResponse().setComplete();
}
}
{
-;
}
String {
request.getHeaders().getFirst()
.orElse(request.getRemoteAddress().getAddress().getHostAddress());
}
}
// 熔断器配置
@Configuration
public class CircuitBreakerConfig {
@Bean
public ReactiveResilience4JCircuitBreakerFactory circuitBreakerFactory() {
ReactiveResilience4JCircuitBreakerFactory factory =
new ReactiveResilience4JCircuitBreakerFactory();
factory.configureDefault(id -> CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值50%
.waitDurationInOpenState(Duration.ofSeconds(30)) // 熔断开启时间30秒
.slidingWindowSize(10) // 滑动窗口大小
.slidingWindowType(SlidingWindowType.COUNT_BASED) // 基于计数
.minimumNumberOfCalls(5) // 最少调用次数
.build());
return factory;
}
@Bean
public ReactiveResilience4JRetryFactory retryFactory() {
ReactiveResilience4JRetryFactory factory =
new ReactiveResilience4JRetryFactory();
factory.configureDefault(id -> RetryConfig.custom()
.maxAttempts(3) // 最大重试次数
.waitDuration(Duration.ofSeconds(1)) // 重试间隔
.retryExceptions(IOException.class, TimeoutException.class) // 重试异常
.build());
return factory;
}
}
// 降级处理
@Component
{
Mono<ServerResponse> {
Map<String, Object> body = <>();
body.put(, );
body.put(, );
body.put(, System.currentTimeMillis());
ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(body));
}
Mono<ServerResponse> {
Map<String, Object> body = <>();
body.put(, );
body.put(, );
body.put(, System.currentTimeMillis());
ServerResponse.status(HttpStatus.SERVICE_UNAVAILABLE)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(body));
}
}
// 自定义负载均衡策略
@Component
public class CustomLoadBalancer implements ReactorServiceInstanceLoadBalancer {
private final String serviceId;
private final ServiceInstanceListSupplier serviceInstanceListSupplier;
public CustomLoadBalancer(String serviceId,
ServiceInstanceListSupplier serviceInstanceListSupplier) {
this.serviceId = serviceId;
this.serviceInstanceListSupplier = serviceInstanceListSupplier;
}
@Override
public Mono<Response<ServiceInstance>> choose(Request request) {
return serviceInstanceListSupplier.get().next()
.map(instances -> selectInstance(instances, request));
}
private Response<ServiceInstance> selectInstance(
List<ServiceInstance> instances, Request request) {
if (instances.isEmpty()) {
return new DefaultResponse(null);
}
// 基于权重的负载均衡
return new DefaultResponse(weightedRoundRobin(instances));
}
private ServiceInstance weightedRoundRobin(List<ServiceInstance> instances) {
// 计算总权重
int totalWeight = instances.stream()
.mapToInt(instance -> getWeight(instance))
.sum();
ThreadLocalRandom.current().nextInt(totalWeight);
;
(ServiceInstance instance : instances) {
currentWeight += getWeight(instance);
(randomWeight < currentWeight) {
instance;
}
}
instances.get();
}
{
Map<String, String> metadata = instance.getMetadata();
Integer.parseInt(metadata.getOrDefault(, ));
}
}
{
Map<String, ServiceHealth> serviceHealthMap = <>();
{
serviceHealthMap.forEach((serviceId, health) -> {
checkServiceHealth(health.getInstance());
health.updateHealth(isHealthy);
(!isHealthy) {
log.warn(, serviceId, health.getLastError());
}
});
}
{
{
String.format(,
instance.getHost(), instance.getPort());
ResponseEntity<String> response = restTemplate.getForEntity(healthUrl, String.class);
response.getStatusCode().is2xxSuccessful();
} (Exception e) {
log.error(, instance.getInstanceId(), e);
;
}
}
{
serviceHealthMap.get(serviceId);
health != && health.isHealthy();
}
}
// Redis缓存配置
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // 缓存10分钟
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.transactionAware()
.build();
}
}
// 缓存过滤器
@Component
public class CacheFilter implements GlobalFilter, Ordered {
@Autowired
private CacheManager cacheManager;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String cacheKey = generateCacheKey(request);
// 检查缓存
Cache cache cacheManager.getCache();
cache.get(cacheKey, String.class);
(cachedResponse != ) {
exchange.getResponse();
response.setStatusCode(HttpStatus.OK);
response.getHeaders().add(, );
response.getHeaders().add(, );
response.bufferFactory().wrap(cachedResponse.getBytes());
response.writeWith(Mono.just(buffer));
}
chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
exchange.getResponse();
(response.getStatusCode().is2xxSuccessful()) {
cache.put(cacheKey, getResponseBody(response));
response.getHeaders().add(, );
}
}));
}
{
-;
}
String {
request.getMethod().name() + +
request.getPath().value() + +
request.getQueryParams().toSingleValueMap().hashCode();
}
String {
;
}
}
// 请求监控
@Component
public class RequestMonitor {
private final MeterRegistry meterRegistry;
private final Timer.Sample sample;
public RequestMonitor(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
this.sample = Timer.start(meterRegistry);
}
@EventListener
public void handleRequestEvent(RequestEvent event) {
switch (event.getType()) {
case REQUEST_DATA_RECEIVED:
sample.start(Timer.builder("gateway.request")
.tag("method", event.getRoute().getUri().getMethod())
.tag("path", event.getRoute().getId())
.register(meterRegistry));
break;
case RESPONSE_DATA_RECEIVED:
sample.stop(Timer.builder("gateway.request")
.tag("status", String.valueOf(event.getResponse().getStatusCode().value()))
.register(meterRegistry));
break;
case EXCEPTION:
meterRegistry.counter("gateway.errors",
"exception", event.getThrowable().getClass().getSimpleName())
.increment();
break;
}
}
}
// 日志过滤器
@Component
public class LoggingFilter implements , Ordered {
LoggerFactory.getLogger(LoggingFilter.class);
Mono<Void> {
System.currentTimeMillis();
chain.filter(exchange).then(Mono.fromRunnable(() -> {
System.currentTimeMillis();
endTime - startTime;
exchange.getRequest();
exchange.getResponse();
log.info(,
request.getMethod().name(),
request.getPath().value(),
response.getStatusCode().value(),
duration);
}));
}
{
Ordered.LOWEST_PRECEDENCE;
}
}