SpringCloud-Zuul常用路由网关

应用场景

微服务在真实应用中一般都会有N多个服务节点组成的服务集群,每个服务都可能依赖多个服务进行本身的业务处理,比如:用户登录操作可能会调用 账号验证服务、身份鉴别服务、生成日志服务、路由转向服务、受权服务等一系列服务才能完成一次登录操作。这样会带来几个问题:

  1. 客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性
  2. 登录信息认证繁琐,访问每个服务都要进行一次认证
  3. 每个服务都通过http访问,导致http请求次数增加,效率不高拖慢系统性能
  4. 多个服务存在跨域请求问题,处理起来比较复杂

【无路由】

我们需要解决这些问题,尝试想一下,不要让前端客户端直接调用后台诸多微服务,我们的服务集群从整个应用系统层面划分,不管大小服务都是服务层的系统元素,对服务管理而言业务被拆分成无数小颗粒度松耦合的独立服务,但对于前端调用服务的用户来说,所有服务是一个整体。对于前台客户端应用而言后台服务应该仍然类似于单体应用一样,要求只有一个服务调度入口即可,于是我们可以在客户端和服务端之间增加一个API网关,所有的外部请求先通过这个微服务网关。前台客户端只需跟网关进行交互,而由网关进行各个微服务的调用。熟悉面向对象设计模式的人应该很容易想到这是“门户模式”的典型应用。经“门户模式”的设计改良后有如下优点:

  1. 减少客户端与微服务之间的调用次数,提高效率
  2. 便于监控,可在网关中监控数据,可以做统一切面任务处理
  3. 便于认证,只需要在网关进行认证即可,无需每个微服务都进行认证
  4. 降低客户端调用服务端的复杂度
    在这里插入图片描述

SpringCloud已经为我们考虑到这一点需求并提供解决方案,SpringCloud通集成Zuul组件完成对服务的路由功能。

概念简介

zuul是Netflix设计用来为所有面向设备、web网站提供服务的所有应用的门面,zuul可以提供动态路由、监控、弹性扩展、安全认证等服务,他还可以根据需求将请求路由到多个应用中。

搭配使用

当然在Netflix的使用中,zuul也结合了Netflix的其他微服务组件一起使用。
• Hystrix:用来服务降级及熔断
• Ribbon:用来作为软件的负载均衡,微服务之间相互调用是通过ribbon作为软件负载均衡使用负载到微服务集群内的不同的实例
• Turbin:监控服务的运行状况
• Feign:用作微服务之间发送rest请求的组件,可以将rest调用类似spring的其他bean一样直接注入使用功能
• Eureka:服务注册中心
入门示例
我们用Idea来创建测试项目,用Spring Initializr可以顺利完成创建过程。
在这里插入图片描述

最终的项目代码框架如下图。
在这里插入图片描述

引入依赖

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

系统配置

#访问端口
server:
  port: 8766
#服务名称
spring:
  application:
    name: routing.zull.web
  #安全认证信息
  security:
    user:
      name: admin
      password: 123456
#服务注册
eureka:
  server:
    register: localhost:8760
  client:
    serviceUrl:
      defaultZone: http://${spring.security.user.name}:${spring.security.user.password}@${eureka.server.register}/eureka/

zuul:
  #如果是url的方式,则zuul.host开头的生效。
  host:
    connect-timeout-millis: 6000
    socket-timeout-millis: 6000
    #如果路由方式是serviceId的方式,那么ribbon的生效
  ribbon:
    ReadTimeout: 3000
    SocketTimeout: 3000
  routes:
      #路由节点a
    api-a:
      path: /api-a/**
      serviceId: EUREKA.CLIENT.FIRST.WEBAPI
      #路由节点b
    api-b:
      path: /api-b/**
      serviceId: EUREKA.CLIENT.FIRST.WEBAPI
      #路由节点c
    api-c:
      path: /api-c/**
      serviceId: EUREKA.CLIENT.FIRST.WEBAPI

启动类

@SpringBootApplication/*服务启动*/
@EnableZuulProxy/*服务路由*/
@EnableEurekaClient/*服务注册*/
@EnableDiscoveryClient/*服务发现*/
public class RoutingZullWebApplication {

    public static void main(String[] args) {
        SpringApplication.run(RoutingZullWebApplication.class, args);
    }
}

启动测试

【贴图】

反向代理

Spring-Zuul作为一款优秀的路由网关组件支持反向代理功能,通过添加路由配置项url来实现网络资源重定向功能。
路由节点api-d对https://blog.csdn.net反向代理

   api-d:
      path: /proxy/**
      url: https://blog.csdn.net

当客户端访问http://xxx.xxx.xxx.xxx:xx/proxy 的时候,网关路由自动重定向https://blog.csdn.net资源。
• api-d自定义的前缀
• path匹配的地址
• url路由到哪个服务

启动测试
http://localhost:8766/proxy/xxxlllbbb
在这里插入图片描述

过滤器

Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。
PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
ERROR:在其他阶段发生错误时执行该过滤器。
除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
Zuul中默认实现的Filter。
下面是最常用的Filter作用。

/**
 * 限流
 */
@Component
public class AccessFilter extends MyFilter {
    // 每秒放多少个令牌 (100个)
    private static final RateLimiter rateLimiter = RateLimiter.create(1);

    public AccessFilter() {
        super(FilterType.PRE, 0);
    }
    @Override
    public Object execute(HttpServletRequest request, RequestContext ctx) throws Exception {
        String msg = String.format("AccessFilter 流量限制>>%s: %s", request.getMethod(), request.getRequestURL().toString());
        System.out.println(msg);
        //限流、限速
        if (!rateLimiter.tryAcquire()) {
            String msgs="访问太频繁了,请稍后。。。";
            System.out.println(msgs);
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.getResponse().setContentType("application/json; charset=UTF-8");
            BaseData bdata = BaseData.result(401, "访问限制", msgs);
            // 内容重新写入
            ctx.setResponseBody(JSON.toJSONString(bdata));
            return null;
        }
        return null;
    }
}
/**
 * @路由之前执行
 * token 身份验证
 */
@Component
public class AuthenticateFilter extends MyFilter{
    public AuthenticateFilter() {
        super(FilterType.PRE, 1);
    }

    @Override
    public Object execute(HttpServletRequest request, RequestContext ctx) throws Exception {
        String msg = String.format("PreFilter 权限验证>>%s: %s", request.getMethod(), request.getRequestURL().toString());
        System.out.println(msg);
        Object accessToken = request.getParameter("token");
        if (accessToken == null) {
            System.out.println("token is empty");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            ctx.getResponse().setContentType("application/json; charset=UTF-8");
            BaseData bdata = BaseData.result(401, "权限验证", "token is empty");
            ctx.setResponseBody(JSON.toJSONString(bdata));
        }
        return null;
    }
}
**
 * @发送错误调用
 * 路由容错处理
 */
@Component
public class ErrorFilter extends MyFilter {
    public ErrorFilter() {
        super(FilterType.ERROR, -1);
    }

    @Override
    public boolean shouldFilter() {
        return RequestContext.getCurrentContext().containsKey("throwable");
    }

    @Override
    public Object run() {

        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletResponse response = ctx.getResponse();
        String msg = String.format("ErrorFilter>>%s: %s", ctx.getRequest().getMethod(), ctx.getRequest().getRequestURL().toString());
        System.out.println(msg);
        Object e = ctx.get("throwable");
        if (e != null && e instanceof ZuulException) {
            ZuulException zuulException = (ZuulException) e;
            ctx.remove("throwable");
            ctx.setSendZuulResponse(false);
            ctx.getResponse().setContentType("application/json; charset=UTF-8");
            BaseData bdata = BaseData.result(401, "服务故障", zuulException.getMessage());
            ctx.setResponseBody(JSON.toJSONString(bdata));
        }

        return null;
    }
}

/**
 * @路由之后执行
 * 对服务接果进行统一处理
 */
@Component
public class PostFilter extends MyFilter {
    public PostFilter() {

        super(FilterType.POST, 2);
    }

    @Override
    public Object execute(HttpServletRequest request, RequestContext ctx) throws Exception {
        String msg = String.format("PostFilter 返回结果>>%s: %s", request.getMethod(), request.getRequestURL().toString());
        System.out.println(msg);
        // 获取返回值内容,加以处理
        InputStream stream = ctx.getResponseDataStream();
        String body = StreamUtils.copyToString(stream, Charset.forName("UTF-8"));
        BaseData bdata = BaseData.result(ctx.getResponseStatusCode(), "请求成功", body);
        // 内容重新写入
        ctx.setResponseBody(JSON.toJSONString(bdata));
        return null;
    }
}

禁用指定的Filter
可以在application.yml中配置需要禁用的filter,格式:
zuul:
FormBodyWrapperFilter:
pre:
disable: true

的高可用

我们实际使用Zuul的方式如上图,不同的客户端使用不同的负载将请求分发到后端的Zuul,Zuul在通过Eureka调用后端服务,最后对外输出。因此为了保证Zuul的高可用性,前端可以同时启动多个Zuul实例进行负载,在Zuul的前端使用Nginx或者F5进行负载转发以达到高可用性。

至此关于SpringCloud-Zuul常用路由网关就介绍完了。

上一篇 SpringCloud-Hystrix 服务熔断器

下一篇 SpringCloud-Gatway高级路由网关

SpringCloud终极教程之核心讲解

源代码地址:https://github.com/crexlb/cre.springcloud.examples

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黒木涯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值