API 网关的职能

API 网关的分类与功能

下面讲解自定义过滤器 之记录日志功能应用!

全局过滤器拦截请求打印日志:

代码:

package com.liu.learn.filter;import cn.hutool.core.util.ObjectUtil;
import com.alibaba.nacos.common.utils.StringUtils;
import com.liu.learn.utils.WebUtils;
import com.liu.learn.vo.GatewayLog;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.BodyInserter;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.HandlerStrategies;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.Map;/*** 日志过滤器,用于记录日志*/
@Slf4j
@Component
public class AccessLogFilter implements GlobalFilter, Ordered {//@Autowired//private AccessLogService accessLogService;public final static String ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER = "@IgnoreAccessLogGlobalFilter";private final List<HttpMessageReader<?>> messageReaders = HandlerStrategies.withDefaults().messageReaders();@Overridepublic int getOrder() {return 10;}@Override@SuppressWarnings("unchecked")public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {if (exchange.getAttribute(ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER) != null) {return chain.filter(exchange);}ServerHttpRequest request = exchange.getRequest();// 请求路径String requestPath = request.getPath().pathWithinApplication().value();Route route = getGatewayRoute(exchange);String ipAddress = WebUtils.getIpAddress(request);GatewayLog gatewayLog = new GatewayLog();gatewayLog.setSchema(request.getURI().getScheme());gatewayLog.setRequestMethod(request.getMethodValue());gatewayLog.setRequestPath(requestPath);gatewayLog.setTargetServer(route.getId());gatewayLog.setRequestTime(new Date());gatewayLog.setIp(ipAddress);MediaType mediaType = request.getHeaders().getContentType();if(MediaType.APPLICATION_FORM_URLENCODED.isCompatibleWith(mediaType) || MediaType.APPLICATION_JSON.isCompatibleWith(mediaType)){return writeBodyLog(exchange, chain, gatewayLog);}else{return writeBasicLog(exchange, chain, gatewayLog);}}private Mono<Void> writeBasicLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog accessLog) {StringBuilder builder = new StringBuilder();MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ","));}accessLog.setRequestBody(builder.toString());//获取响应体ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, accessLog);return chain.filter(exchange.mutate().response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {// 打印日志writeAccessLog(accessLog);}));}/*** 解决 request body 只能读取一次问题,* 参考: org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory* @param exchange* @param chain* @param gatewayLog* @return*/@SuppressWarnings("unchecked")private Mono writeBodyLog(ServerWebExchange exchange, GatewayFilterChain chain, GatewayLog gatewayLog) {ServerRequest serverRequest = ServerRequest.create(exchange,messageReaders);Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body ->{gatewayLog.setRequestBody(body);return Mono.just(body);});// 通过 BodyInserter 插入 body(支持修改body), 避免 request body 只能获取一次BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);HttpHeaders headers = new HttpHeaders();headers.putAll(exchange.getRequest().getHeaders());// the new content type will be computed by bodyInserter// and then set in the request decoratorheaders.remove(HttpHeaders.CONTENT_LENGTH);CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);return bodyInserter.insert(outputMessage,new BodyInserterContext()).then(Mono.defer(() -> {// 重新封装请求ServerHttpRequest decoratedRequest = requestDecorate(exchange, headers, outputMessage);// 记录响应日志ServerHttpResponseDecorator decoratedResponse = recordResponseLog(exchange, gatewayLog);// 记录普通的return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build()).then(Mono.fromRunnable(() -> {// 打印日志writeAccessLog(gatewayLog);}));}));}/*** 打印日志* @author javadaily* @date 2021/3/24 14:53* @param gatewayLog 网关日志*/private void writeAccessLog(GatewayLog gatewayLog) {log.info(gatewayLog.toString());}private Route getGatewayRoute(ServerWebExchange exchange) {return exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);}/*** 请求装饰器,重新计算 headers* @param exchange* @param headers* @param outputMessage* @return*/private ServerHttpRequestDecorator requestDecorate(ServerWebExchange exchange, HttpHeaders headers,CachedBodyOutputMessage outputMessage) {return new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic HttpHeaders getHeaders() {long contentLength = headers.getContentLength();HttpHeaders httpHeaders = new HttpHeaders();httpHeaders.putAll(super.getHeaders());if (contentLength > 0) {httpHeaders.setContentLength(contentLength);} else {// TODO: this causes a 'HTTP/1.1 411 Length Required' // on// httpbin.orghttpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");}return httpHeaders;}@Overridepublic Flux<DataBuffer> getBody() {return outputMessage.getBody();}};}/*** 记录响应日志* 通过 DataBufferFactory 解决响应体分段传输问题。*/private ServerHttpResponseDecorator recordResponseLog(ServerWebExchange exchange, GatewayLog gatewayLog) {ServerHttpResponse response = exchange.getResponse();DataBufferFactory bufferFactory = response.bufferFactory();return new ServerHttpResponseDecorator(response) {@Overridepublic Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {if (body instanceof Flux) {Date responseTime = new Date();gatewayLog.setResponseTime(responseTime);// 计算执行时间long executeTime = (responseTime.getTime() - gatewayLog.getRequestTime().getTime());gatewayLog.setExecuteTime(executeTime);// 获取响应类型,如果是 json 就打印String originalResponseContentType = exchange.getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);if (ObjectUtil.equal(this.getStatusCode(), HttpStatus.OK) && StringUtils.isNotBlank(originalResponseContentType) && originalResponseContentType.contains("application/json")) {Flux<? extends DataBuffer> fluxBody = Flux.from(body);return super.writeWith(fluxBody.buffer().map(dataBuffers -> {// 合并多个流集合,解决返回体分段传输DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();DataBuffer join = dataBufferFactory.join(dataBuffers);byte[] content = new byte[join.readableByteCount()];join.read(content);// 释放掉内存DataBufferUtils.release(join);String responseResult = new String(content, StandardCharsets.UTF_8);gatewayLog.setResponseData(responseResult);return bufferFactory.wrap(content);}));}}// if body is not a flux. never got there.return super.writeWith(body);}};}
}

yml配置

server:port: 9090spring:application:name: @artifactId@cloud:nacos:discovery:server-addr: 172.17.169.81:8848gateway:discovery:locator:enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由routes:- id: gloabl_filter # 路由的id,没有规定规则但要求唯一,建议配合服务名#匹配后提供服务的路由地址#uri: http://localhost:8070/payment/get/31uri: lb://payment-service#uri: http://localhost:8070predicates:#- Path=/filter/** # 断言,路径相匹配的进行路由- Path=/filter/payment/get/** # 断言,路径相匹配的进行路由#- After=2021-01-20T17:42:47.789-07:00[America/Denver]#- Before=2022-01-20T17:42:47.789-07:00[America/Denver]#- Cookie=username,liu#- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数#- Host=**.baidu.com#- Method=GET#- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由# 过滤filters:- StripPrefix=1#- RewritePath=/api/(?<segment>.*), /$\{segment}#- AddRequestHeader=X-Request-red, blue#- id: payment_route2# uri: http://localhost:8001# predicates:#Path=/payment/lb/** #断言,路径相匹配的进行路由

打印的日志

2021-05-20 12:35:18.018  INFO 69288 --- [ctor-http-nio-4] com.liu.learn.filter.AccessLogFilter     : GatewayLog(targetServer=gloabl_filter, requestPath=/payment/get/31, requestMethod=GET, schema=http, requestBody=, responseData=null, ip=0:0:0:0:0:0:0:1, requestTime=Thu May 20 12:35:17 CST 2021, responseTime=null, executeTime=0)

请求经过路由器就会打印日志,如何让不需要记录日志的请求跳过全局过滤器呢?

结合全局过滤器应用

先新建一个gateway过滤器

package com.liu.learn.filter;import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
public class IgnoreAccessLogFilterFactor extends AbstractGatewayFilterFactory <IgnoreAccessLogFilterFactor.Config>{public IgnoreAccessLogFilterFactor() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {return this::filter;}public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true);return chain.filter(exchange);}public static class Config {}@Overridepublic String name() {return "IgnoreAccessLogFilter";}
}

关键代码:

public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true);return chain.filter(exchange);
}

相当于在全局过滤器加一个标识

全局过滤器判断:

if (exchange.getAttribute(ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER) != null) {return chain.filter(exchange);
}

yml代码

server:port: 9090spring:application:name: @artifactId@cloud:nacos:discovery:server-addr: 172.17.169.81:8848gateway:discovery:locator:enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由routes:- id: gloabl_filter # 路由的id,没有规定规则但要求唯一,建议配合服务名#匹配后提供服务的路由地址#uri: http://localhost:8070/payment/get/31uri: lb://payment-service#uri: http://localhost:8070predicates:#- Path=/filter/** # 断言,路径相匹配的进行路由- Path=/filter/payment/get/** # 断言,路径相匹配的进行路由#- After=2021-01-20T17:42:47.789-07:00[America/Denver]#- Before=2022-01-20T17:42:47.789-07:00[America/Denver]#- Cookie=username,liu#- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数#- Host=**.baidu.com#- Method=GET#- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由# 过滤filters:- StripPrefix=1#- RewritePath=/api/(?<segment>.*), /$\{segment}#- AddRequestHeader=X-Request-red, blue#- id: payment_route2# uri: http://localhost:8001# predicates:#Path=/payment/lb/** #断言,路径相匹配的进行路由- id: no_filteruri: lb://payment-servicepredicates:- Path=/pay/**filters:- StripPrefix=1#- SetPath=/{test}- IgnoreAccessLogFilter

http://localhost:9090/pay/payment/get/31 这个请求依旧会打印日志

但有一个局限:

gateWay过滤器必须在全局过滤器之前执行才会有效。

修改代码:

package com.liu.learn.filter;import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;@Component
@Slf4j
public class IgnoreAccessLogFilterFactor extends AbstractGatewayFilterFactory <IgnoreAccessLogFilterFactor.Config>{public IgnoreAccessLogFilterFactor() {super(Config.class);}@Overridepublic GatewayFilter apply(Config config) {//config.setIgnoreGlobalFilter(true);return new InnerFilter(config);}/*public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {log.info(">>>>>>>>>>>>>>>>>>>局部过滤器");exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true);return chain.filter(exchange);}*//*** 创建一个内部类,来实现2个接口,指定顺序* 这里通过Ordered指定优先级*/private class InnerFilter implements GatewayFilter, Ordered {private Config config;InnerFilter(Config config) {this.config = config;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {/*System.out.println("  pre 自定义过滤器工厂 AAAA  " + this.getClass().getSimpleName());boolean root = true == config.isIgnoreGlobalFilter();if (root) {System.out.println("  is root ");} else {System.out.println("  is no root ");}// 在then方法里的,相当于aop中的后置通知return chain.filter(exchange).then(Mono.fromRunnable(() -> {System.out.println("  post 自定义过滤器工厂 AAAA " + this.getClass().getSimpleName());}));*/log.info("进入innerFilter=====" + config.isIgnoreGlobalFilter());if (config.isIgnoreGlobalFilter() == true) {exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true);}return chain.filter(exchange);}@Overridepublic int getOrder() {return -1000;}}public static class Config {boolean ignoreGlobalFilter;public boolean isIgnoreGlobalFilter() {return ignoreGlobalFilter;}public void setIgnoreGlobalFilter(boolean ignoreGlobalFilter) {this.ignoreGlobalFilter = ignoreGlobalFilter;}}@Overridepublic String name() {return "IgnoreAccessLogFilter";}
}

创建一个内部类,来实现局部过滤器的执行顺序

private class InnerFilter implements GatewayFilter, Ordered {private Config config;InnerFilter(Config config) {this.config = config;}@Overridepublic Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {/*System.out.println("  pre 自定义过滤器工厂 AAAA  " + this.getClass().getSimpleName());boolean root = true == config.isIgnoreGlobalFilter();if (root) {System.out.println("  is root ");} else {System.out.println("  is no root ");}// 在then方法里的,相当于aop中的后置通知return chain.filter(exchange).then(Mono.fromRunnable(() -> {System.out.println("  post 自定义过滤器工厂 AAAA " + this.getClass().getSimpleName());}));*/log.info("进入innerFilter=====" + config.isIgnoreGlobalFilter());if (config.isIgnoreGlobalFilter() == true) {exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true);}return chain.filter(exchange);}@Overridepublic int getOrder() {return -1000;//此数值要比跳过的过滤器顺序号要小,才会跳过}
}
@Override
public GatewayFilter apply(Config config) {//config.setIgnoreGlobalFilter(true);return new InnerFilter(config);
} 返回这个内部类过滤器

配置文件

server:port: 9090spring:application:name: @artifactId@cloud:nacos:discovery:server-addr: 172.17.169.81:8848gateway:discovery:locator:enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由routes:- id: gloabl_filter # 路由的id,没有规定规则但要求唯一,建议配合服务名#匹配后提供服务的路由地址#uri: http://localhost:8070/payment/get/31uri: lb://payment-service#uri: http://localhost:8070predicates:#- Path=/filter/** # 断言,路径相匹配的进行路由- Path=/filter/payment/** # 断言,路径相匹配的进行路由#- After=2021-01-20T17:42:47.789-07:00[America/Denver]#- Before=2022-01-20T17:42:47.789-07:00[America/Denver]#- Cookie=username,liu#- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数#- Host=**.baidu.com#- Method=GET#- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由# 过滤filters:- StripPrefix=1#- RewritePath=/api/(?<segment>.*), /$\{segment}#- AddRequestHeader=X-Request-red, blue#- id: payment_route2# uri: http://localhost:8001# predicates:#Path=/payment/lb/** #断言,路径相匹配的进行路由- id: no_filteruri: lb://payment-servicepredicates:- Path=/pay/**filters:- StripPrefix=1#- SetPath=/{test}#- IgnoreAccessLogFilter- name: IgnoreAccessLogFilter #本路由跳过全局过滤器args:ignoreGlobalFilter: true

name: IgnoreAccessLogFilter #本路由跳过全局过滤器
              args:
                ignoreGlobalFilter: true

为gateway局部过滤器指定条件参数,指定为true

if (config.isIgnoreGlobalFilter() == true) { exchange.getAttributes().put(AccessLogFilter.ATTRIBUTE_IGNORE_LOG_GLOBAL_FILTER, true); }只有指定为true才会跳过。

工具类获取ip地址ServerHttpRequest方式

package com.liu.learn.utils;import org.springframework.http.HttpHeaders;
import org.springframework.http.server.reactive.ServerHttpRequest;public class WebUtils {public static String getIpAddress(ServerHttpRequest request) {HttpHeaders headers = request.getHeaders();String ip = headers.getFirst("x-forwarded-for");if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) {// 多次反向代理后会有多个ip值,第一个ip才是真实ipif (ip.indexOf(",") != -1) {ip = ip.split(",")[0];}}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("WL-Proxy-Client-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("HTTP_CLIENT_IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("HTTP_X_FORWARDED_FOR");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = headers.getFirst("X-Real-IP");}if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {ip = request.getRemoteAddress().getAddress().getHostAddress();}return ip;}}

HttpServletRequest  方式

package com.liu.utils;import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;public class WebUtils {public static String getIpAddr(HttpServletRequest request){String ipAddress = request.getHeader("x-forwarded-for");if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("Proxy-Client-IP");}if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getHeader("WL-Proxy-Client-IP");}if(ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {ipAddress = request.getRemoteAddr();if(ipAddress.equals("127.0.0.1") || ipAddress.equals("0:0:0:0:0:0:0:1")){//根据网卡取本机配置的IPInetAddress inet=null;try {inet = InetAddress.getLocalHost();} catch (UnknownHostException e) {e.printStackTrace();}ipAddress= inet.getHostAddress();}}//对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割if(ipAddress!=null && ipAddress.length()>15){ //"***.***.***.***".length() = 15if(ipAddress.indexOf(",")>0){ipAddress = ipAddress.substring(0,ipAddress.indexOf(","));}}return ipAddress;}
}

Spring Cloud Gateway-过滤器按照order排序

系统全局过滤器执行顺序(名称、order)

RemoveCachedBodyFilter                        HIGHEST_PRECEDENCE = Integer.MIN_VALUE

AdaptCachedBodyGlobalFilter                  HIGHEST_PRECEDENCE = Integer.MIN_VALUE

NettyWriteResponseFilter                        -1

ForwardPathFilter                                      0

GatewayMetricsFilter                                0

RouteToRequestUrlFilter                         10000

WeightCalculatorWebFilter                     10001

LoadBalancerClientFilter                        10100

WebsocketRoutingFilter                        LOWEST_PRECEDENCE -1

NettyRoutingFilter                                  LOWEST_PRECEDENCE =Integer.MAX_VALUE

ForwardRoutingFilter                             LOWEST_PRECEDENCE =Integer.MAX_VALUE

路由的优先级是按order来排序,如果order越小,优先级越高

SpringCloud Alibaba 2021微服务实战十三 gateway 全局过滤器打印日志及如何忽略全局过滤器相关推荐

  1. SpringCloud Alibaba 2021微服务实战三十二 集成RocketMQ实现分布式事务

    目录 基于RocketMQ分布式事务 - 完整示例 2.解决方案 2.1.本地消息表方案 2.2.RocketMQ事务消息方案 一.事务消息 二.订单服务 1.事务日志表 2.TransactionM ...

  2. 微服务实战系列之SpringCloud Alibaba学习(四)

    微服务实战系列之SpringCloud Alibaba: 微服务实战系列之SpringCloud Alibaba学习(一) 微服务实战系列之SpringCloud Alibaba学习(二) 微服务实战 ...

  3. SpringCloud Alibaba微服务实战(七) - 路由网关(Gateway)全局过滤

    说在前面 全局过滤器作用于所有的路由,不需要单独配置,我们可以用它来实现很多统一化处理的业务需求,比如权限认证,IP 访问限制,监控,限流等等. 创建路由网关(Gateway)启动服务cloud-ac ...

  4. SpringCloud Alibaba微服务实战(五) - Sentinel实现限流熔断

    什么是Sentinel? 请查看文章:SpringCloud Alibaba微服务实战(一) - 基础环境搭建 构建服务消费者cloud-sentinel进行服务调用 服务创建请查看文章:Spring ...

  5. SpringCloud Alibaba微服务实战(四) - Nacos Config 配置中心

    说在前面 Nacos 是阿里巴巴开源的一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台.Nacos Config就是一个类似于SpringCloud Config的配置中心. 一.启动N ...

  6. SpringCloud Alibaba微服务实战(三) - Nacos服务创建消费者(Feign)

    什么是Feign Feign 是一个声明式的伪 Http 客户端,它使得写 Http 客户端变得更简单.使用 Feign,只需要创建一个接口并注解.它具有可插拔的注解特性,可使用 Feign 注解和 ...

  7. SpringCloud Alibaba微服务实战三 - 服务调用

    SpringCloud Alibaba微服务实战三 - 服务调用 通过前面两篇文章我们准备好了微服务的基础环境并运行注册服务到nacos上了 统一接口返回结构 在开始今天的正餐之前我们先把上篇文章中那 ...

  8. controller调用controller的方法_SpringCloud Alibaba微服务实战三 - 服务调用

    导读:通过前面两篇文章我们准备好了微服务的基础环境并让accout-service 和 product-service对外提供了增删改查的能力,本篇我们的内容是让order-service作为消费者远 ...

  9. java 限流熔断_SpringCloud Alibaba微服务实战五 - 限流熔断

    简介 Sentinel是面向分布式服务框架的轻量级流量控制框架,主要以流量为切入点,从流量控制,熔断降级,系统负载保护等多个维度来维护系统的稳定性.在SpringCloud体系中,sentinel主要 ...

最新文章

  1. Python核心编程学习日记之错误处理
  2. 【十大经典数据挖掘算法】PageRank
  3. goupby 两个值 结果变了_一道问题引出的python中可变数据类型与不可变数据类型...
  4. 【渝粤教育】电大中专职业健康与安全_1作业 题库
  5. c语言磁盘文件只有写没读,C语言的磁盘文件问题
  6. 测试面试题集-Linux常用命令
  7. UVA11347 Multifactorials【阶乘+组合】
  8. PAT (Basic Level) Practice1020 月饼
  9. C#多线程的用法2-线程的生命周期
  10. 数学连乘和累加运算符号_数学所有的公式和符号
  11. codevs1515 瞎搞+Lucas
  12. KMP 深入理解next数组
  13. Meta-HAR: Federated Representation Learning for Human Activity Recognition
  14. statsby: 不用循环语句的循环
  15. 关于Java文件路径问题 1
  16. 土壤水势传感器是什么
  17. RT-Thread编程手册
  18. 【Matlab身份证识别】身份证号码识别【含GUI源码 014期】
  19. VR全景的拍摄与制作
  20. 更改VisualStudio的字体大小

热门文章

  1. Outlook邮箱如何在手机上登录
  2. LeetCode 904. 水果成篮【fruit-into-baskets】
  3. 通过NFS服务器将设备目录挂载到Windows目录
  4. [OS-Linux]详解Linux的文件系统、inode和动静态库
  5. 借大数据玩自主酒店,携程的酒店业之困能不能解?
  6. 【iKcamp线下】微信小程序技术沙龙
  7. ubuntu安装Linux集成服务,Hyper-v R2中安装ubuntu后,安装集成环境。--梦飞翔的地方(梦翔天空)...
  8. TabLayout的指示器和文字的边距
  9. iOS中“事件”的前因后果
  10. 第四次团队作业——项目Alpha版本发布