SpringBoot之Zuul使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010473656/article/details/82350656

Zuul 是 Netflix 开源的微服务网关,Spring Cloud 对 Zuul 进行了整合和增强。在 SpringCloud 体系中,Zuul 担任着网关的角色,对发送到服务端的请求进行一些预处理,比如安全验证、动态路由、负载分配等。

Zuul 的核心是 Filters,根据执行时期分为以下几类:
PRE:这种过滤器在请求被路由之前调用
ROUTING:这种过滤器将请求路由到微服务
POST:这种过滤器在路由到微服务以后执行
ERROR:在其他阶段发生错误时执行该过滤器

下面对于配置进行说明:

pom的配置:

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

由于还没有使用eureka,所以只引入zuul的集成包就可以。版本由 spring-boot-starter-parent自动管理。

启动类添加 @EnableZuulProxy 注解:

@SpringBootApplication
@EnableZuulProxy
public class MainWebApplication {
    private static Logger logger=LoggerFactory.getLogger(MainWebApplication.class);
    public static void main(String[] args) {
        SpringApplication.run(MainWebApplication.class, args);
        logger.info("***>>>>>>>Server startup>>>");
    }
}

application.properties的配置:

#服务器不用注册到其他服务器
eureka.client.registerWithEureka=false

#服务器不用去服务器抓取注册信息
eureka.client.fetchRegistry=false

application-cloudConfig.properties 配置:

#本地环境配置zuul转发的规则:
spring.application.name=acs-msettle-gateway
zuul.routes.acs-deposit.path=/acsDeposit/**
zuul.routes.acs-deposit.url=http://127.0.0.1:8260/

zuul 转发 有两种配置:

1、根据 eureka server 的serviceId 转发:

zuul.routes.serviceName.path=/exampleService/**
zuul.routes.serviceName.serviceId=serviceId

注:
zuul.routes 是固定的
serviceName 是可以随便写的,但最好根据要路由的服务取
serviceId 是 eureka 服务注册时的名称
exampleService 是前端请求某个微服务的一个公共的路径名,如下面的acsDeposit

//根据Id查询
url1 = "${basePath}/acsDeposit/propConfig/getPropConfigById?param=" + param;  
//新增配置
url2 = "${basePath}/acsDeposit/propConfigSub/addProportionConfig.do";

而微服务在 RequestMapping 时可以不包含acsDeposit

2、根据具体 URL 转发:

zuul.routes.serviceName.path=/exampleService/**
zuul.routes.serviceName.url=http://127.0.0.1:8260/

上述参数与1的取名规则相同,不再解释。

项目当前由于尚未使用eureka,所以采用了第二种转发规则。这种转发有很多好处,最大的好处就是不用再写一大堆的dubbo接口了,项目的页面都放在一个服务上,在使用 Zuul 之前,所有的交互都要写成dubbo接口,使用Zuul可以直接HTTP调用,方便很多。

下面讲下Zuul使用中遇到的问题:

1、Zuul 转发404,没有转到微服务上

(一)微服务需要构造成web服务结构,转发请求404还是因为请求url的问题,微服务需要配置下url匹配规则:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
    /**
     * setUseSuffixPatternMatch : 设置是否是后缀模式匹配,如“/user”是否匹配/user.*,默认真即匹配;
     * setUseTrailingSlashMatch : 设置是否自动后缀路径模式匹配,如“/user”是否匹配“/user/”,默认真即匹配;
     */
    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(true).setUseTrailingSlashMatch(true);
    }
}

(注:配置类要放在可以使注解生效的路径下)

(二)application.properties 中没有配置对application-cloudConfig.properties的加载:

spring.profiles.active=dataSource,cloudConfig

2、Zuul 转发请求,原Session丢失

Zuul 的核心是Filters,在请求转发的各个时期Zuul都自定义了一些Filter用来对原请求进行封装,从而转发。由于尚未引入Ribbon,所以没有被 RibbonRoutingFilter 拦截。
转发的处理是发生在 route 时期,所有的Filter都要继承ZuulFilter,下面说一下ZuulFilter的结构,下面是自定义的一个Filter:

import com.alibaba.fastjson.JSONObject;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE;

public class ZuulUserFilter extends ZuulFilter {


    /**
     * 返回过滤类型,有:pre,route,post,error
     */
    @Override
    public String filterType() {
        return PRE_TYPE;
    }

    /**
     * 返回过滤的优先级,数字越大,优先级越高,越早执行
     */
    @Override
    public int filterOrder() {
        return 101;
    }

    /**
     * 设置过滤条件
     */
    @Override
    public boolean shouldFilter() {
        return RequestContext.getCurrentContext().getRouteHost() != null
                && RequestContext.getCurrentContext().sendZuulResponse();
    }

    /**
     * 具体的操作
     */
    @Override
    public Object run() {
        RequestContext context = RequestContext.getCurrentContext();
        HttpServletRequest request = context.getRequest();
        User user = (User) request.getSession().getAttribute(Cons.S_USER);
        Map<String, List<String>> requestQueryParams = context.getRequestQueryParams();

        if (requestQueryParams==null) {
            requestQueryParams=new HashMap<>();
        }

        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(JSONObject.toJSONString(user));
        requestQueryParams.put(Cons.S_USER, arrayList);
        context.setRequestQueryParams(requestQueryParams);

        return null;
    }
}

每个方法都有详细注释,这里不在解释。
Zuul 在没有使用 Ribbon 的情况下,在route时期执行的Filter是SimpleHostRoutingFilter,在这个过滤器中,对原请求进行了重组,但是却因为清空了Cookie,使得原本可能存在于Session中的一些重要数据丢失。比如登录的用户信息就保存在Session中,转发后Session丢失,使得微服务不能获取到请求用户的信息。

网上解决方案-在application.properties中清空敏感header:

zuul.sensitive-headers=

(但是配置后却没有生效,知道原因的小伙伴请留言哈)

最后,解决方案就是上面代码中的做法,将用户相关参数封装成请求参数,因为请求参数不会被过滤掉。而后在微服务中获取:

/**
 * 新增占比配置
 */
@RequestMapping("addProportionConfig")
@ResponseBody
public Object addProportionConfig(HttpServletRequest request, ProportionConfig config) {
    SettleResponse response = new SettleResponse(RetCode.SUCCESS);
    try {
        String userInfo = request.getParameterMap().get(Constant.S_USER)[0];
        User user = JSONObject.parseObject(userInfo, User.class);
        logger.info("【新增占比配置】操作人:{}, 请求参数={}", user.getLoginName(), config);
        proportionConfigService.addProportionConfig(config);
    } catch (Exception e) {
        response.setResult(RetCode.FAILURE, "新增占比配置系统异常!");
        logger.error("【新增占比配置】系统异常,异常原因", e);
        }
    return response;
}

是的,很难看,大家可以放在某个工具类里封装一下。嗯,还是很怪异。但是能暂时的解决问题,因为是系统内部的微服务调用,所以还可以忍。只能坐等大神指导啦。

猜你喜欢

转载自blog.csdn.net/u010473656/article/details/82350656