zoukankan      html  css  js  c++  java
  • spring-cloud学习之2.搭建请求网关spring-cloud-getway

    一:准备

    请求网关,顾名思义,所有请求都有网关统一处理,路由至各个服务,getway是spring最新网关,有取代zuul的趋势,具体请百度。

    1.导包

    getway包:

    <!--gateway 网关依赖,内置webflux 依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>

    getway熔断:

     <!--        熔断-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            </dependency>

    getway自带限流功能,内部使用的是redis

       <!--        redis-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
            </dependency>

    eureka实例:

      <!--eureka 客户端 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>

    2:配置:

    server:
      port: 2001
    spring:
      application:
        name: dandelion-getway
      cloud:
        gateway:
          discovery:
            locator:
              # 是否和服务注册与发现组件结合,设置为 true 后可以直接使用应用名称调用服务
              enabled: true
    
          # 路由配置中心
          routes:
            # 服务中心
            - id: dandelion-api #id 以-开头 唯一即可
              uri: lb://dandelion-api  #lb://代表服务内转发,后跟服务名称
              #断言 和过滤差不多意思,其中有配置具体百度
              predicates:
                - Method=GET  #只接受get方法
                - Path=/system/** #只接收/system开头的路径
                # 过滤器配置  getway有两种过滤方式,GatewayFilter和GlobalFilter
                # GatewayFilter : 需要通过spring.cloud.routes.filters 配置在具体路由下,只作用在当前路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上
                #GlobalFilter : 全局过滤器,不需要在配置文件中配置,作用在所有的路由上。
              filters:
                # 验证码处理
                - ImgCodeFilter
                #设置StripPrefix=1表示从二级url路径转发,即http://localhost:2001/auth/demo将会转发到http://localhost:2002/demo
                - StripPrefix=1
                - name: RequestRateLimiter  #固定名称
                  args:
                    #配置限流键的解析器
                    key-resolver: '#{@ipRequestLimiter}'
                    #令牌桶每秒填充速率,1s/1次
                    redis-rate-limiter.replenishRate: 1
                    # 令牌桶总数量
                    redis-rate-limiter.burstCapacity: 1
                # 降级配置
                - name: Hystrix #固定名称
                  args:
                    name: fallbackcmd
                    fallbackUri: 'forward:/fallback'
    
      redis:
        host: 192.168.211.128
        jedis:
          pool:
            max-wait: 300ms
        timeout: 1 #单位秒
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:1001/eureka
      instance:
        instance-id:  ${spring.application.name}:${server.port}
        prefer-ip-address: true     #访问路径可以显示IP地址
    
    hystrix:
      command:
        default:  #default全局有效,service id指定应用有效
          execution:
            timeout:
              enabled: true  #是否启用超时 默认启用
            isolation:
              thread:
                timeoutInMilliseconds: 1000 # 命令执行超时时间,默认1000ms

    3.过滤器:

    package club.dandelion.cloud.getway.filter;
    
    import club.dandelion.cloud.common.R;
    import club.dandelion.cloud.common.cons.Constants;
    import club.dandelion.cloud.common.exception.ValidateCodeException;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import lombok.SneakyThrows;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.core.io.buffer.DataBufferUtils;
    import org.springframework.data.redis.core.StringRedisTemplate;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.net.URI;
    import java.nio.CharBuffer;
    import java.nio.charset.StandardCharsets;
    import java.util.concurrent.atomic.AtomicReference;
    
    /**
     * 验证码处理
     *
     * @author jiang
     */
    @Component
    public class ImgCodeFilter extends AbstractGatewayFilterFactory<ImgCodeFilter.Config> {
        private final static String AUTH_URL = "/auth/login";
    
        @Autowired
        private StringRedisTemplate redisTemplate;
    
        public ImgCodeFilter() {
            super(Config.class);
        }
    
        @Override
        public GatewayFilter apply(Config config) {
    
            return (exchange, chain) -> {
                ServerHttpRequest request = exchange.getRequest();
                URI uri = request.getURI();
                // 不是登录请求,直接向下执行
                if (!StringUtils.containsIgnoreCase(uri.getPath(), AUTH_URL)) {
                    return chain.filter(exchange);
                }
                try {
                    String bodyStr = resolveBodyFromRequest(request);
                    JSONObject bodyJson = JSONObject.parseObject(bodyStr);
                    String code = (String) bodyJson.get("captcha");
                    String randomStr = (String) bodyJson.get("randomStr");
                    // 校验验证码
                    checkCode(code, randomStr);
                } catch (Exception e) {
                    e.printStackTrace();
                    ServerHttpResponse response = exchange.getResponse();
                    response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
                    response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                    String msg = JSON.toJSONString(R.error(e.getMessage()));
                    DataBuffer bodyDataBuffer = response.bufferFactory().wrap(msg.getBytes());
                    return response.writeWith(Mono.just(bodyDataBuffer));
                }
                return chain.filter(exchange);
            };
        }
    
        /**
         * 获取请求体
         *
         * @param serverHttpRequest
         * @return
         */
        private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest) {
            // 获取请求体
            Flux<DataBuffer> body = serverHttpRequest.getBody();
            AtomicReference<String> bodyRef = new AtomicReference<>();
            body.subscribe(buffer -> {
                CharBuffer charBuffer = StandardCharsets.UTF_8.decode(buffer.asByteBuffer());
                DataBufferUtils.release(buffer);
                bodyRef.set(charBuffer.toString());
            });
    
            return bodyRef.get();
        }
    
        /**
         * 检查code
         *
         * @param code
         * @param randomStr
         */
        @SneakyThrows
        private void checkCode(String code, String randomStr) {
            if (StringUtils.isBlank(code)) {
                throw new ValidateCodeException("验证码不能为空");
            }
            if (StringUtils.isBlank(randomStr)) {
                throw new ValidateCodeException("验证码不合法");
            }
            String key = Constants.DEFAULT_CODE_KEY + randomStr;
            String saveCode = redisTemplate.opsForValue().get(key);
            redisTemplate.delete(key);
            if (!code.equalsIgnoreCase(saveCode)) {
                throw new ValidateCodeException("验证码不合法");
            }
        }
    
        /**
         * 必须要有
         */
        public static class Config {
        }
    }
    View Code
    package club.dandelion.cloud.getway.config;
    
    import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import reactor.core.publisher.Mono;
    
    /**
     * getway限流配置
     *
     * @author jiang
     */
    @Configuration
    public class HttpRequestLimiter {
    
        @Bean
        public KeyResolver ipRequestLimiter() {
            return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
        }
    
    }
    View Code

    4.熔断反馈:

    import club.dandelion.cloud.common.R;
    import com.alibaba.fastjson.JSON;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.MediaType;
    import org.springframework.stereotype.Component;
    import org.springframework.web.reactive.function.BodyInserters;
    import org.springframework.web.reactive.function.server.HandlerFunction;
    import org.springframework.web.reactive.function.server.ServerRequest;
    import org.springframework.web.reactive.function.server.ServerResponse;
    import reactor.core.publisher.Mono;
    
    import java.util.Optional;
    
    import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR;
    
    /**
     * 错误信息
     *
     * @author jiang
     */
    @Slf4j
    @Component
    public class HystrixFallbackHandler implements HandlerFunction<ServerResponse> {
        @Override
        public Mono<ServerResponse> handle(ServerRequest serverRequest) {
            Optional<Object> originalUris = serverRequest.attribute(GATEWAY_ORIGINAL_REQUEST_URL_ATTR);
            originalUris.ifPresent(originalUri -> log.error("网关执行请求:{}失败,hystrix服务降级处理", originalUri));
            return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR.value()).contentType(MediaType.APPLICATION_JSON_UTF8)
                    .body(BodyInserters.fromValue(JSON.toJSONString(R.error("服务已被降级熔断"))));
        }
    }

    5:路径路由 相当于controller

    
    
    import club.dandelion.cloud.getway.handler.HystrixFallbackHandler;
    import club.dandelion.cloud.getway.handler.ImgCodeHandler;
    import lombok.AllArgsConstructor;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.http.MediaType;
    import org.springframework.web.reactive.function.server.RequestPredicates;
    import org.springframework.web.reactive.function.server.RouterFunction;
    import org.springframework.web.reactive.function.server.RouterFunctions;

    /**
    * 没有controller所以配置路由信息
    * <p>
    * RouterFunction使用RequestPredicate将传入请求映射到HandlerFunction。
    * <p>
    * AllArgsConstructor lombok注解,代表有所有的构造参数 所以下面两个属性是根据构造参数注入的
    *
    * @author jiang
    */
    @Configuration
    @AllArgsConstructor
    public class RouterFunctionConfiguration {

    private HystrixFallbackHandler hystrixFallbackHandler;
    private ImgCodeHandler imgCodeHandler;

    @Bean
    public RouterFunction<?> routerFunction() {
    return RouterFunctions
    .route(RequestPredicates.path("/fallback").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
    hystrixFallbackHandler)
    .andRoute(RequestPredicates.GET("/code").and(RequestPredicates.accept(MediaType.TEXT_PLAIN)),
    imgCodeHandler);
    }
    }
  • 相关阅读:
    20145334赵文豪 《信息安全系统设计基础》第2周学习总结
    20145334赵文豪《信息安全系统设计基础》第1周学习总结
    关于第五周大家学习问题的总结
    20145334 第五次 java 实验报告
    20145334 《Java程序设计》第10周学习总结
    实验四 Android开发基础
    # 20145334 《Java程序设计》第9周学习总结
    20145334实验三《敏捷开发与XP实践》
    实验二:面向对象设计
    程序的机器级表示内容补充及扩展
  • 原文地址:https://www.cnblogs.com/bchange/p/12706424.html
Copyright © 2011-2022 走看看