Zuul案例、常见使用方式

搭建Zuul服务

依赖:

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

在程序的启动类EurekaZuulClientApplication 加上@EnableEurekaClient注解,开启EurekaClient的功能;加上@SpringBootApplication注解,表明自己是一一个Spring Boot工程;加上@EnableZuulProxy注解,开启Zuul的功能。代码如下:

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

配置:

server:
  port: 5000

spring:
  application:
    name: service-zuul

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
zuul:
  routes:
    hiapi:
      path: /hiapi/**
      serviceId: eureka-client
    ribbonapi:
      path: /ribbonapi/**
      serviceId: eureka-ribbon-client
    feignapi:
      path: /feignapi/**
      serviceId: eureka-feign-client

在本案例中,zuul.routes.hiapi.path为"/hiapi/**",zuul.routes.hiapi.serviceId为"eureka-client",这两个配置就可以将以"/hiapi"开头的Url路由到eureka-client服务。其中,zuul.routes.hiapi 中的“hiapi" 是自己定义的,需要指定它的path 和serviceld, 两者配合使用,就可以将指定类型的请求Url 路由到指定的ServiceId。

同理,满足以“/ribbonapi" 开头的请求Url都会被分发到eureka-ibbon- client, 满足以“/feignapi/"开头的请求Url都会被分发到eureka-feign-client服务。如果某服务存在多个实例,Zuul结合Ribbon会做负载均衡,将请求均分的部分路由到不同的服务实例。

依次启动工程eureka-server、 eureka-client、 eureka-ribbon-client、 eureka-feign-client和eureka-zuul-client,其中eureka-client启动两个实例,端口为8762和8763。在浏览器上多次访问htp:/:ocalhost:000hipihinamn= forezp,浏览器会交替显示以下内容:

hi forezp, i am from port:8762
hi forezp, i am from port:8763

可见Zuul在路由转发做了负载均衡。同理,多次访问htpt:ocalhost:5000/ei/gnapi/jin hi?name-forezp和ht://ocahtosto000/rbbonapihi? name=forezp,也可以看到相似的内容。

如果不需要用Ribbon做负载均衡,可以指定服务实例的Url,用zuul.routes.hiapi.url配置指定,这时就不需要配置zuul.routes.hiapi.serviceld了。一旦指定了 Url, Zuul 就不能做负载均衡了,而是直接访问指定的Url, 在实际的开发中这种做法是不可取的。修改配置的代码如下:

zuul:
	routes:
		hiapi:
			path: /hiapi/**
			url: http://localhost:8762

如果你想指定Url,并且想做负载均衡,那么就需要自己维护负载均衡的服务注册列表。首先,将ribbon.eureka.enabled改为false,即Ribbon负载均衡客户端不向Eureka Client获取服务注册列表信息。然后需要自己维护一份注册列表, 该注册列表对应的服务名为hiapi-v1(这个名字可自定义),通过配置hiapi-v1.ribbon.listOfServers来配置多个负载均衡的Url。代码如下:

zuul:
	routes:
		path: /hiapi/**
		srerviceId: hiapi-v1
ribbon:
	eureka:
		enabled: false

hiapi-v1:
	ribbon:
		listOfServers: http://localhost:8762,http://localhost:8763
在Zuul上配置API接口的版本号

如果想给每一个服务的 API接口加前缀,例如ht://oclthost:50000 /v1/hiapihi?name=forezp/,即在所有的API接口上加一一个v1作为版本号。这时需要用到zul.prefix的配置,配置示例代码如下:

zuul:
  routes:
    hiapi:
      path: /hiapi/**
      serviceId: eureka-client
    ribbonapi:
      path: /ribbonapi/**
      serviceId: eureka-ribbon-client
    feignapi:
      path: /feignapi/**
      serviceId: eureka-feign-client
  prefix: /v1

重新启动eureka- zuul-service服务,在浏览器上访问http://localhost:5000/v1/hiapi/hi?name=test,浏览器会显示:
在这里插入图片描述

在Zuul上配置熔断器

Zuul作为Netflix组件,可以与Ribbon、Eureka和Hystrix等组件相结合,实现负载均衡、熔断器的功能。

在默认情况下,Zuul 和Ribbon相结合,实现了负载均衡的功能
在Zuul中实现熔断功能需要实现ZzulFallbackProvider的接口。实现该接口有两个方法,

  • 一个是getRoute()方法,用于指定熔断功能应用于哪些路由的服务;
  • 另一个方法fallbackResponse()为进入熔断功能时执行的逻辑。

ZuulFallbackProvider 的源码如下:
在这里插入图片描述

实现一个针对eureka-client服务的熔断器,当eureka-client的服务出现故障时,进入熔断逻辑,向浏览器输入一句错误提示,代码如下:

@Component
public class MyFallbackProvider implements ZuulFallbackProvider {
    @Override
    public String getRoute() {
        return "eureka-client";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("oooops!error,i'm the fallback.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                return httpHeaders;
            }
        };
    }
}

重新启动eureka-zuul-client 工程,并且关闭eureka-client 的所有实例,在浏览器上访问htp://ocalhost:5000 /hiapi/hi?name-forezp,浏览器显示:
在这里插入图片描述
如果需要所有的路由服务都加熔断功能,只需要在getRoute()方法上返回"*"的匹配符:

	@Override
	public String getRoute() {
		return "*";
	}
在Zuul中使用过滤器

实现过滤器很简单,只需要继承ZuulFiter,并实现ZuulFiter中的抽象方法,包括filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()的两个方法

  • filterType()即过滤器的类型,它有4种类型,分别是“pre"“post”“routing"和error”。
  • filterOrder()是过滤顺序, 它为一个Int类型的值, 值越小,越早执行该过滤器。
  • shouldFilter()表示该过滤器是否过滤逻辑,如果为true,则执行run()方法;如果为false,则不执行run()方法。
  • run()方法写具体的过滤的逻辑。

在本例中,检查请求的参数中是否传了token这个参数,如果没有传,则请求不被路由到具体的服务实例,直接返回响应,状态码为401。

@Component
public class MyFilter extends ZuulFilter {

    private static Logger log = LoggerFactory.getLogger(MyFilter.class);

    @Override
    public String filterType() {
        return PRE_TYPE;
    }

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

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

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String accessToken = request.getParameter("token");
        if (accessToken == null) {
            log.warn("token is empty!");
            ctx.setSendZuulResponse(false);
            ctx.setResponseStatusCode(401);
            try {
                ctx.getResponse().getWriter().write("token is empty!");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        log.info("ok");
        return null;
    }
}

重新启动服务,打开浏览器,访问http://localhost:5000/hiapi/hi?name=forezp,浏览器显示:
在这里插入图片描述
再次输入http://localhost:5000/hiapi/hi?name=forezp&token=HJHKLL
在这里插入图片描述

可见,MyFilter 这个Bean注入IoC容器之后,对请求进行了过滤,并在请求路由转发之前进行了逻辑判断。在实际开发中,可以用此过滤器进行安全验证。本例的架构图如图:
在这里插入图片描述

Zuul的常见使用方式

Zuul是采用了类似于SpringMVC的DispatchServlet来实现的,采用的是异步阻塞模型,所以性能比Ngnix差。由于Zuul和其他Netflix组件可以相互配合、无缝集成,Zuul 很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数情况下,Zuul都是以集群的形式存在的。由于Zuul的横向扩展能力非常好,所以当负载过高时,可以通过添加实例来解决性能瓶颈。

一种常见的使用方式是对不同的渠道使用不同的Zuul来进行路由,例如移动端共用一个Zuul网关实例,Web端用另一个Zuul网关实例,其他的客户端用另外一个Zuul实例进行路由。这种不同的渠道用不同Zul实例的架构如下图所示:

在这里插入图片描述
另外一种常见的集群是通过Ngnix和Zuul相互结合来做负载均衡。暴露在最外面的是Ngnix主从双热备进行Keepalive, Ngnix 经过某种路由策略,将请求路由转发到Zuul集群上,Zul最终将请求分发到具体的服务上。架构图如下图所示:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值