zuul网关

引言

  • 项目的迭代,服务要拆分,服务要合并,微服务名称变动使得客户端代码无法稳定
  • 认证操作,需要在每一个模块中都添加认证的操作,统一的把安全性校验都放在Zuul中
  • 处理所有请求的通用逻辑

zuul网关_第1张图片

环境搭建

    <dependencies>
        
        <dependency>
            <groupId>com.alibaba.cloudgroupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
        dependency>

		
        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-zuulartifactId>
        dependency>
        
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-actuatorartifactId>
        dependency>
    dependencies>
package com.qf.zuul;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;

@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulApp {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class,args);
    }
}


yml

server:
  port: 80
spring:
  application:
    name: service-zuul
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848


#开启 监控功能 http://localhost/actuator/routes
management:
  endpoints:
    enabled-by-default: true #暴露所有端点信息
    web:
      exposure:
        include: '*'  #以web方式暴露
# zuul的配置
zuul:
  # 自定义服务
  routes:
    # 微服务名: 匹配的路径  zuul直接可以去除/ss,但是gateway必须得重写uri,或者内置去除前缀过滤器
    service-provide: /ss/**

Zuul的路由信息查看

开启zuul信息监控

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-actuatorartifactId>
dependency>

yml添加配置


# 查看zuul的监控界面(开发时,配置为*,上线,不要配置)
management:
  endpoints:
    web:
      exposure:
        include: "*"

http://localhost/actuator/routes

自定义服务配置

# zuul的配置
zuul:
  # 自定义服务
  routes:
  # 微服务名: 匹配的路径 zuul直接可以去除/ss,但是gateway必须得重写uri,或者内置去除前缀过滤器
    search: /ss/**
    customer: /cc/**  

自动生成注册微服务的路由信息

@SpringBootApplication
@EnableDiscoveryClient   //将网关服务注册到Nacos注册中心
@EnableZuulProxy  // 自动创建路由表
public class ZuulApp {
    public static void main(String[] args) {
        SpringApplication.run(ZuulApp.class);
    }
}

Zuul过滤器

执行流程

zuul网关_第2张图片

Zuul过滤器使用

继承ZuulFilter

@Component
public class TestZuulFilter extends ZuulFilter {
    @Override
    public String filterType() {
        // 前置过滤器
        return FilterConstants.PRE_TYPE;
    }

    //指定过滤器的执行顺序
    @Override
    public int filterOrder() {
        return 10;
    }

    //配置是否启用
    @Override
    public boolean shouldFilter() {
        return true;
    }

    //指定过滤器中的具体业务代码
    @Override
    public Object run() throws ZuulException {
        System.out.println("TestZuulFilter is called ......");
        return null;
    }
}

PreFilter实现token校验

@Override
    public Object run() throws ZuulException {

        RequestContext requestContext = RequestContext.getCurrentContext();

        final HttpServletRequest request = requestContext.getRequest();

        String token = request.getParameter("token");

        if(token == null || !"123".equalsIgnoreCase(token)){

            // 不会把请求转发给后端微服务了
            requestContext.setSendZuulResponse(false);

            final HttpServletResponse response = requestContext.getResponse();
            try {
                response.setContentType("text/html;charset=utf-8");
                response.getWriter().println("没有提供合法的token,zuul拦截了改请求.....");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

Zuul动态路由

@Component
public class RoutingFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        // 把动态路由过滤器放到自己的过滤器链的最后一个
        return 100;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //1. 获取Request对象
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();

        //2. 获取参数,servicekey
        String servicekey = request.getParameter("servicekey");
        String method = request.getParameter("method");

        servicekey = getMicoservieID(servicekey);
        method = getMicoservieMethod(method);

        context.put(FilterConstants.SERVICE_ID_KEY,servicekey);
            // 设置发送给微服务的哪个接口,value部分是接口的路径
        context.put(FilterConstants.REQUEST_URI_KEY,method);
        return null;
    }

    private String getMicoservieMethod(String method) {
        return "/customer/test12";
    }

    private String getMicoservieID(String servicekey) {
        return "customer-v1";
    }
}

计算所有请求的执行时间

1.前置过滤器

package com.qf.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class TimePreFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return 90;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
       //记录请求到达该过滤器的时间
        long startTime = System.currentTimeMillis();
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        //把初始时间戳放入request对象中
        request.setAttribute("startTime",startTime);

        return null;
    }
}

2.后置过滤器

package com.qf.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
@Component
public class TimePostFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.POST_TYPE;
    }

    @Override
    public int filterOrder() {
        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {

        //获取 初始时间戳
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();
        long startTime = (long) request.getAttribute("startTime");
        long endTime=System.currentTimeMillis();
        System.out.println("time user: "+(endTime-startTime));

        return null;
    }
}

检查前端是否参数传的齐全

1.过滤器1

package com.qf.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

@Component
public class Myfilter01 extends ZuulFilter {
    @Override
    public String filterType() {
        //说明该过滤器的类型,前置或后置
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        //设置过滤器的执行顺序,序号越小,越先执行
        return 100;
    }

    @Override
    public boolean shouldFilter() {
        //该过滤器是否生效,true生效
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        //过滤器要执行的功能写在这里
        System.out.println("过滤器MyFilter01 的run方法被调用了");

        //获取Request对象
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        //获取系统有哪些必传参数
      HashSet<String> sysnames = getParamNames();
        Set<String> requestNames = request.getParameterMap().keySet();

//        //判断是否都包含
//        boolean b = requestNames.containsAll(names);

              //做集合减法
        sysnames.removeAll(requestNames);

         if (sysnames.size()>0){
             //缺少了必传参数
             //让请求不要在发送给后续的微服务了
             currentContext.setSendZuulResponse(false);
             //返回错误信息
             HttpServletResponse response = currentContext.getResponse();
             response.setContentType("text/html;charset=utf-8");
             try {
                 response.getWriter().println("缺少了必传参数,请检查"+sysnames);
             } catch (IOException e) {
                 e.printStackTrace();
             }

         }
            return null;//固定写法
    }

    private HashSet<String> getParamNames() {
        HashSet<String> names = new HashSet<>();
        names.add("token");
        names.add("sign");
        return names;
    }
}

2.过滤器2

package com.qf.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

@Component
public class Myfilter02 extends ZuulFilter {
    @Override
    public String filterType() {
        //说明该过滤器的类型,前置或后置
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        //设置过滤器的执行顺序,序号越小,越先执行
        return 110;
    }

    @Override
    public boolean shouldFilter() {
        //该过滤器是否生效,true生效
        RequestContext currentContext = RequestContext.getCurrentContext();
        boolean b = currentContext.sendZuulResponse();//判断过滤器发送请求是否置为false,默认为true
        return b;//false 不走run方法,否则就走,
    }

    @Override
    public Object run() throws ZuulException {
        //过滤器要执行的功能写在这里
        System.out.println("过滤器MyFilter02 的run方法被调用了");
        return null;//固定写法
    }
}

动态路由例子

package com.qf.zuul.filter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

@Component
public class MyRoutingFilter extends ZuulFilter {
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        //注意,该过滤器放在所有前置过滤器的最后
        return 200;
    }

    @Override
    public boolean shouldFilter() {
        //该过滤器是否生效,true生效
        RequestContext currentContext = RequestContext.getCurrentContext();
        boolean b = currentContext.sendZuulResponse();
        return b;
    }

    @Override 
    public Object run() throws ZuulException {
        //根据请求中的参数,找到要发送的微服务的服务名以及对应的接口的url路径;
        RequestContext currentContext = RequestContext.getCurrentContext();
        HttpServletRequest request = currentContext.getRequest();

        String serviceName = request.getParameter("serviceName");
        String method = request.getParameter("serviceMethod");

        //查询servicename 对应的微服务名字
       String microName= getMicroName(serviceName);
       String microPath= getMicroPath(method);

       //指定请求发送给哪个微服务
        currentContext.put(FilterConstants.SERVICE_ID_KEY,microName);
        //指定发送给微服务的哪个接口(即接口的路径是什么)
        currentContext.put(FilterConstants.REQUEST_URI_KEY,microPath);


        return null;
    }

    private String getMicroPath(String method) {
            return "/echo/abc";
    }


    private String getMicroName(String serviceName) {
        return "service-provide";
    }
}

你可能感兴趣的:(java)