springcloud alibab-网关

1.微服务容易出现的问题

(1)客户端要维护服务端的各个地址,代码困难

(2)认证,鉴权复杂

(3)跨域问题

2.为了解决上述问题,所以引入api网关,它的作用就是提供系统的统一入口,封装程序的内部结构,为客户端提供统一服务,一些与业务本身的功能的公共逻辑就可以在这里实现,例如:认证,鉴权,监控和路由转发

3.流程

(1)新建项目api-gateway

(2)导入jar包

<!--此模块不能引入web包-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-nacos-discovery</artifactId>
</dependency>

记住,千万不要导入web包,会冲突

(3)在启动类上加入@EnableDiscoveryClient

(4)修改配置文件

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true #可以让gateway发现nacos中的微服务
      routes:   #路由数组,就是指当前请求满足什么条件时转到哪个微服务
        - id: product_route #当前路由的标识,唯一
          #uri: http://localhost:8081  #请求要转发的地址
          uri: lb://service-product  #lb:负载均衡 后面跟着的是微服务的具体标识
          order: 1  #路由的优先级,数字越小级别越高
          predicates:   #断言,就是路由转发要满足的条件
            - Path=/product-serv/** #当请求路径满足Path指定的规则时,才能进行路由转发
          filters: #过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 #转发之前去掉一层路径

4.断言

就是说在什么条件下才能执行真正的路由

内置断言工厂

(1)基于DateTime类型的断言工厂

(2)基于远程地址的断言工厂

(3)基于cookie的断言工厂

(4)基于head的断言工厂

(5)基于host的断言工厂

(6)基于Methon请求方法的断言工厂

(7)基于path请求路径的断言工厂

(8)基于jquery请求参数的断言工厂

(9)基于路由权重的断言工厂

自定义断言工厂

(1)在配置文件中添加断言配置 - Age=18,60

(2)自定义断言工厂,实现断言方法

import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 泛型,用于接收一个配置类,配置类用于接收配置文件中的配置
 */
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    /**
     * 用于配置文件中获取参数值赋值到配置类中到属性上
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        //这里的配置要求和配置文件中的顺序一致
        return Arrays.asList("minAge", "maxAge");
    }

    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            //从serverWebExchange获取传入的参数
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
                if (StringUtils.isNotEmpty(ageStr)) {
                    int age = Integer.parseInt(ageStr);
                    return age > config.getMinAge() && age < config.getMaxAge();
                }
                return true;
            }
        };
    }

    @Data
   public static class Config { 
    private Integer minAge;
    private Integer maxAge;
   }
}

5.过滤器

作用:在请求的传递过程中,对请求和响应做一些手脚

生命周期:Pre(身份认证,记录访问调试信息)和Post(添加请求头,收集统计信息和指标)

分类:局部过滤器(作用在某一路由上)(GatewayFilter),全局过滤器(作用在全部路由上)(GlabolFilter)

 

内置局部过滤器

AddRequestHeader  为原始请求添加Header

AddRequestParamter  为原始请求添加请求参数

AddResponseHeader  为原始请求添加Header

SetStatus  为原始请求添加状态码

。。。

 

自定义局部过滤器

(1)修改配置文件

- Log=true, false   #控制日志是否开启

(2)自定义一个过滤工厂,实现方法

import lombok.Data;
import lombok.NoArgsConstructor;
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;

import java.util.Arrays;
import java.util.List;

/**
 * 自定义局部过滤器
 */
@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("consoleLog", "cacheLog");
    }

    @Override
    public GatewayFilter apply(LogGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if(config.isCacheLog()) {
                    System.out.println("cache日志已开启");
                }
                if(config.isConsoleLog()) {
                    System.out.println("console日志已开启");
                }
                return chain.filter(exchange);
            }
        };
    }

    /**
     * 自定义内部类,用来接收参数
     */
    @Data
    @NoArgsConstructor
    public class Config{

        private boolean consoleLog;

        private boolean cacheLog;
    }
}

 

全局过滤器

内置全局过滤器

无需配置

uri: lb://service-product  #lb:负载均衡 后面跟着的是微服务的具体标识

这就是一个全局过滤器

 

自定义全局过滤器

鉴权

/**
 * 自定义全局过滤器(统一鉴权)
 */
@Slf4j
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    /**
     * 用于编写过滤器的逻辑
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (!StringUtils.equals("admin",token)){
            //认证失败
            log.info("认证失败了。。。");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 用来标识当前过滤器的优先级
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

6.网关限流

(1)基于路由维度的限流(在配置文件中配置的路由条目,资源名为对应的routeId)

导入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

编写配置类

 

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.exception.SentinelGatewayBlockExceptionHandler;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.reactive.result.view.ViewResolver;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.*;

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolvers;
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /**
     * 初始化一个限流过滤器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product-rule")   //资源名称,对应的路由id
                .setCount(1)        //限流阈值
                .setIntervalSec(1));    //统计时间窗口
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 配置限流的异常处理器
     * @return
     */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap();
                map.put("code", 0);
                map.put("message", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK).contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

(2)自定义api维度(用户可以用sentinel提供的api来自定义一些api分组)

    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("product_api1").setCount(1).setIntervalSec(1));
        rules.add(new GatewayFlowRule("product_api2").setCount(1).setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    /**
     * 自定义api分组
     */
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition apiDefinition1 = new ApiDefinition("product_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>(){{
                    //以/product-serv/product/api1开头的请求
                    add(new ApiPathPredicateItem().setPattern("/product-serv/product/api1/**")
                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition apiDefinition2 = new ApiDefinition("product_api2")
                .setPredicateItems(new HashSet<ApiPredicateItem>(){{
                    //以/product-serv/product/api2/demo1 完整的url路径匹配
                    add(new ApiPathPredicateItem().setPattern("/product-serv/product/api2/demo1")
                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(apiDefinition1);
        definitions.add(apiDefinition2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

  

 

posted @ 2020-05-21 21:31  豆莱浆渠  阅读(409)  评论(0编辑  收藏  举报