微服务整合gateway

1.概述简介

官网:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

     简单点说gateway就是api路由的统一管理,在请求到达服务之前网关可以替我们做很多事情,诸如:过滤,权限,熔断,降级,限流,

    SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

    (在Spring Boot应用程序中,我们可以使用网关gateway来实现请求路由和负载均衡。然而,在使用网关gateway时,可能会出现无法下载文件的问题  我就碰到这个坑 流异常关闭---multipart/form-data request failed. Stream ended unexpected,还有一个坑就是上传文件 不行,因为springboot集成gateway后协议走的netty协议了,因为内部协议的关系可能存在的问题,就把文件服务独立出来了当时用的七彩牛 也有可能是平台问题,只做了独立服务处处理不走网关了。。。。。。才解决)

直接上图简洁点:

工作原理:

上代码:

1.依赖

  <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <!--        注册发现-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!--        配置管理-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <dependency>
            <groupId>weiyu.saas.system</groupId>
            <artifactId>weiyu-saas-system-common</artifactId>
        </dependency>


        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>

2.创建一个httpLogBean用来封装响应 自己定义即可

@Data
@NoArgsConstructor
@AllArgsConstructor
public class HttpLogBean {
//    URI、Method、QueryString、POST请求的Body、响应信息和来源IP
    private String uri;
    private String method;
    private String params;
    private String headers;
    private String body;
    private String response;
    private String requestId;
}

3.添加过滤器 权限过滤--LogAndAuthFilter 

@Component
@Slf4j
public class LogAndAuthFilter implements GlobalFilter, Ordered {

    private final LogService logService;
    private final AuthService authService;

    public LogAndAuthFilter(LogService logService, AuthService authService){
        this.logService = logService;
        this.authService = authService;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        if (exchange.getRequest().getPath().toString().contains("/export")){
            log.info("跳出导出文件时候改变流");//流中断关闭解决
            return chain.filter(exchange);
        }
        HttpLogBean httpLogBean = new HttpLogBean();
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        ServerHttpResponse serverHttpResponse = exchange.getResponse();

        String requestId = UniqueIdUtil.newUniqueId("requestId");

        String method = serverHttpRequest.getMethodValue().toUpperCase();
        httpLogBean.setMethod(method);
        httpLogBean.setUri(serverHttpRequest.getURI().toString());
        httpLogBean.setRequestId(requestId);

        MultiValueMap<String, String> query = serverHttpRequest.getQueryParams();
        Map<String, String> params = new HashMap<>();
        query.forEach((k, v) -> {
            params.put(k, v.get(0));
        });

        httpLogBean.setParams(JSON.toJSONString(params));
        if("POST".equals(method)) {
            DataBuffer body = (DataBuffer) exchange.getAttributes().get("cachedRequestBody");
            httpLogBean.setBody(body.toString());
        }
        //权限校验
        if (authService.isIgnoreAuth(serverHttpRequest)){
            return rute(exchange,chain,httpLogBean);
        }else{
            ResponseResult b = authService.checkToken(serverHttpRequest);
            if(b.getCode()!= StatusCode.SUCCESS.code) {
                String resp = JSON.toJSONString(b);
                httpLogBean.setResponse(resp);
                logService.logCreate(httpLogBean);
                DataBuffer bodyDataBuffer = serverHttpResponse.bufferFactory().wrap(resp.getBytes());
                serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                return serverHttpResponse.writeWith(Mono.just(bodyDataBuffer));
            }
        }
        String account = authService.getAccount(serverHttpRequest); //获取账号 
        ServerHttpRequest newServerHttpRequest = serverHttpRequest.mutate().headers(new Consumer<HttpHeaders>() {
            @Override
            public void accept(HttpHeaders httpHeaders) {
                httpHeaders.remove("Authorization");
                if (account!=null){
                    httpHeaders.add("user", account);
                }
                httpHeaders.add("requestId", requestId);
            }
        }).build();
        ServerWebExchange build = exchange.mutate().request(newServerHttpRequest).build();
        return rute(build,chain,httpLogBean);
    }

    @Override
    public int getOrder() {
        return -1;//order顺序不能错 否者启动报错这里为-1
    }


    public Mono<Void> rute(ServerWebExchange exchange, GatewayFilterChain chain,HttpLogBean httpLogBean){
        ServerHttpResponse serverHttpResponse = exchange.getResponse();
        DataBufferFactory bufferFactory = serverHttpResponse.bufferFactory();
        ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(serverHttpResponse) {
            @Override
            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    return super.writeWith(fluxBody.map(dataBuffer -> {
                        byte[] content = new byte[dataBuffer.readableByteCount()];
                        dataBuffer.read(content);
                        DataBufferUtils.release(dataBuffer);
                        String resp = new String(content, Charset.forName("UTF-8"));
                        System.out.println("===============返回");
                        httpLogBean.setResponse(resp);
                        logService.logCreate(httpLogBean);
                        byte[] uppedContent = new String(content, Charset.forName("UTF-8")).getBytes();
                        return bufferFactory.wrap(uppedContent);
                    }));
                }else {
                    System.out.println("1111111111111111");
                }
                return super.writeWith(body);
            }
        };
        return chain.filter(exchange.mutate().response(decoratedResponse).build());
    }
}
Component
public class CachePostBodyFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest serverHttpRequest = exchange.getRequest();
        String method = serverHttpRequest.getMethodValue();
        if("POST".equalsIgnoreCase(method)) {
                ServerRequest serverRequest = new DefaultServerRequest(exchange);
            Mono<String> bodyToMono = serverRequest.bodyToMono(String.class);
                return bodyToMono.flatMap(body -> {

                    NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(new UnpooledByteBufAllocator(false));
                    DataBuffer bodyDataBuffer = nettyDataBufferFactory.wrap(body.getBytes());
//这里要注意 put 进去的buffer 很多地方都是PooledDataBuffer ,但是我每次走到网关就报我的buffer是有问题 这个问题追溯源码好久才定位到 是应该要放DataBuffer类型而不是PooledDataBuffer 坑死我了
                    exchange.getAttributes().put("cachedRequestBody", bodyDataBuffer);
                    ServerHttpRequest newRequest = new ServerHttpRequestDecorator(serverHttpRequest) {
                        @Override
                        public HttpHeaders getHeaders() {
                            HttpHeaders httpHeaders = new HttpHeaders();
                            httpHeaders.putAll(super.getHeaders());
                            httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
                            return httpHeaders;
                        }
                        @Override
                        public Flux<DataBuffer> getBody() {
                            return Flux.just(bodyDataBuffer);
                        }
                    };
                    return chain.filter(exchange.mutate().request(newRequest).build());
                });
            }
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return -2;//这里order是-2
    }

4.配置白名单:

package weiyu.saas.system.gateway.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

import java.util.List;

@Configuration
@ConfigurationProperties(prefix = "system")
public class TokenWhiteListConfig {
    // 配置文件使用list接收
    private List<String> whiteList;

    public List<String> getWhiteList() {
        return whiteList;
    }

    public void setWhiteList(List<String> whiteList) {
        this.whiteList = whiteList;
    }
}


//bootstrap.yml 配置
# 访问白名单
system:
  whiteList:
    - /aa/login/loginTest
    - /allauth/login/loginTest
 

5.回调函数

@RestController
public class FallbackController {
    @RequestMapping("/fallback")
    public ResponseResult defaultFallback() {
        return new ResponseResult(StatusCode.SERVER_ERROR.code,"服务异常","");
    }
}

最后启动类加@EnableDiscoveryClient注解  大功告成   注意:如果请求体没有参数 只能get请求

,否者网关那边是不会转发过来的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值