SpringCloud2组件之Zuul详解

Zuul:

  Spring Cloud使用Zuul来作为路由网关,网关的功能对于分布式网站十分重要。Zuul主要有三个功能:

  • 路由:将请求路由到真实额服务器上,进而保护真实服务器的IP地址,避免直接地攻击真实服务器。
  • 负载均衡:将请求按照一点的算法分摊到多个微服务节点上,减缓单点的压力。
  • 过滤器:判断请求是否为有效请求,一旦判定失败,就可以将请求阻止,避免发送到真实的服务器,这样就能降低真实服务器的压力。

开发环境:

  • 开发工具:IntelliJ IDEA
  • Java版本:1.8
  • Spring Boot版本:2.1.6.RELEASE
  • Spring Cloud版本:Greenwich.SR1

1、创建server-eureka微服务:

(1)server-eureka组件选择

(2)server-eureka目录

(3)application.yml

server:
  #服务端口
  port: 9000
spring:
  application:
    #服务名称
    name: server
eureka:
  #配置环境
  environment: dev
  #配置数据中心
  datacenter: nanjing
  instance:
    #注册服务器名称
    hostname: localhost
  client:
    #是否注册到服务中心
    register-with-eureka: false
    #是否检索服务
    fetch-registry: false
    service-url:
      #客户端服务域
      defaultZone: http://localhost:9000/eureka/

(4)ServerEurekaApplication

package com.ming.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer //开启Eureka服务端
public class ServerEurekaApplication {

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

2、创建client-user微服务:

(1)client-user组件选择

(2)client-user目录

(3)application.yml

#配置第一个用户微服务
server:
  #服务端口
  port: 7001
spring:
  #区分不同环境下的配置文件(启动工程时使用)
  profiles: user1
  #服务名称
  application:
    name: user
eureka:
  client:
    service-url:
      #服务注册地址
      defaultZone: http://localhost:9000/eureka/
#隔离线
---
#配置第二个用户微服务
server:
  #服务端口
  port: 7002
spring:
  #区分不同环境下的配置文件(启动工程时使用)
  profiles: user2
  #服务名称
  application:
    name: user
eureka:
  client:
    service-url:
      #服务注册地址
      defaultZone: http://localhost:9000/eureka/

(4)ClientUserApplication

package com.ming.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient  //开启Eureka客户端
public class ClientUserApplication {

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

(5)UserController

package com.ming.user.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {

   @RequestMapping("/timeout")
    public String timeout() {
		
		//调用服务时间
        Timestamp time = new Timestamp(System.currentTimeMillis());
        //生成一个2000之内的随机数
        long ms = (long) (2000 * Math.random());
        System.out.println("服务耗时:" + ms + " ms");
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("调用服务时间:" + time + ",服务耗时:" + ms + " ms");
        return "调用服务时间:" + time + " -> 未超时,服务正常";
    }
}

3、创建client-zuul微服务:

(1)client-zuul组件选择

SpringCloud2组件之Zuul详解_第1张图片

(2)client-zuul目录

SpringCloud2组件之Zuul详解_第2张图片

(3)application.yml

server:
  #服务端口,浏览器默认端口,地址栏可不用显示输入该端口号
  port: 80
spring:
  #服务名称
  application:
    name: zuul
eureka:
  client:
    service-url:
      #服务注册地址
      defaultZone: http://localhost:9000/eureka/

(4)ClientZuulApplication

package com.ming.zuul;

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

@SpringBootApplication
@EnableZuulProxy  //开启zuul网关(默认引入断路机制)
public class ClientZuulApplication {

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

4、工程测试:

(1)配置client-user运行环境

  client-user微服务要启动两个实例,来测试Zuul的负载均衡功能,配置运行环境如下:

SpringCloud2组件之Zuul详解_第3张图片 SpringCloud2组件之Zuul详解_第4张图片

(2)微服务运行目录

SpringCloud2组件之Zuul详解_第5张图片

(3)测试原始路由功能

  依次点击ServerEurekaApplication、ClientUserApplication1、ClientUserApplication2、ClientZuulApplication,工程都启动成功后。在浏览器地址栏访问 http://localhost:9000,其结果如下(四个微服务实例启动正常):

SpringCloud2组件之Zuul详解_第6张图片

在浏览器地址栏访问 http://localhost/user/timeout, 其结果如下:

SpringCloud2组件之Zuul详解_第7张图片

  在 http://localhost/user/timeout 中,localhost代表的是请求Zuul服务,因为采用的是默认的80端口,所以浏览器地址可以不给出端口号,user代表的是client-user微服务的serviceId (application.yml中的spring.application.name属性值),而timeout是请求路径,这样就会将请求转发到client-user微服务。

(4)测试负载均衡功能

改写client-zuul微服务配置文件application.yml:
server:
  #服务端口,浏览器默认端口,地址栏可不用显示输入
  port: 80
spring:
  #服务名称
  application:
    name: zuul
zuul:
  routes:
    user-service:
      #配置client-user微服务请求路径
      path: /u/**
      #指定转发地址,zuul将请求转发到client-user微服务上,由于给定了服务端口号,无法实现负载均衡
      #url: http://localhost:7001/
      #指定服务ID,zuul将请求转发到client-user微服务上,自动使用服务端负载均衡,分摊请求
      serviceId: user
eureka:
  client:
    service-url:
      #服务注册地址
      defaultZone: http://localhost:9000/eureka/
在service包下创建ZuulFallBackService类,实现降级服务,Zuul默认启用断路机制:
package com.ming.zuul.service;

import org.springframework.cloud.netflix.zuul.filters.route.FallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

@Service
public class ZuulFallBackService implements FallbackProvider {

    @Override
    public String getRoute() {
    	//client-user微服务serviceId(spring.application.name)
        return "user";
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {

        return new ClientHttpResponse() {

            @Override
            public HttpHeaders getHeaders() {
				//设置请求头
                HttpHeaders httpHeaders = new HttpHeaders();
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                return httpHeaders;
            }

            @Override
            public InputStream getBody() {
            	//设置请求体
                return new ByteArrayInputStream("超时,服务异常".getBytes());
            }

            @Override
            public HttpStatus getStatusCode() {
           		//设置响应状态
                return HttpStatus.REQUEST_TIMEOUT;
            }

            @Override
            public int getRawStatusCode() {
           		//设置响应状态码
                return HttpStatus.REQUEST_TIMEOUT.value();
            }

            @Override
            public String getStatusText() {
           		//设置响应状态信息
                return HttpStatus.REQUEST_TIMEOUT.getReasonPhrase();
            }

            @Override
            public void close() {
            }
        };
    }
}

  依次点击ServerEurekaApplication、ClientUserApplication1、ClientUserApplication2、ClientZuulApplication,工程都启动成功后。在浏览器地址栏访问 http://localhost:9000,其结果如下(四个微服务实例启动正常):

SpringCloud2组件之Zuul详解_第8张图片

  在浏览器地址栏访问 http://u/timeout, 其结果如下:

SpringCloud2组件之Zuul详解_第9张图片

然后查看控制台ClientUserApplication1,结果如下:

默认响应时长超过1000ms,启用熔断机制,进行降级服务。然而每个请求默认有1次重试机会,查看控制台ClientUserApplication2,如下:

第二次重新请求,响应未超时,微服务被正常调用。从上面图中可以看到,两次请求的client-zuul服务端口号分别为7002、7001,Zuul默认采用轮询机制实现负载均衡。
  再次在浏览器地址栏访问 http://u/timeout, 其结果如下:

SpringCloud2组件之Zuul详解_第10张图片

查看控制台ClientUserApplication1、ClientUserApplication2,结果如下:

两次请求均超时,启用熔断机制,进行降级服务。

(5)测试过滤器功能

  访问 http://localhost/u/timeout?serialId=521&authCode=123, 当serialId与authCode一致时,Zuul转发请求到client-user微服务;访问 http://localhost/u/timeout?serialId=521&authCode=521, 当serialId与authCode不一致时,不再转发到微服务,浏览器页面显示 " 服务认证失败!"。

在service包下创建ZuulFilterService类:
package com.ming.zuul.service;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;

@Service
public class ZuulFilterService extends ZuulFilter {

    @Override
    public String filterType() {
        //设置前置过滤
        return "pre";
    }

    @Override
    public int filterOrder() {
        //设置过滤器顺序,数字越小,优先级越高
        return 0;
    }

    @Override
    public boolean shouldFilter() {

        //获取当前请求上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        //获取HttpServletRequest对象
        HttpServletRequest request = currentContext.getRequest();
        //获取请求参数序列Id
        String serialId = request.getParameter("serialId");
        //如果存在序列Id,则启用过滤器
        return !StringUtils.isEmpty(serialId);
    }

    @Override
    public Object run() {

        //获取当前请求上下文
        RequestContext currentContext = RequestContext.getCurrentContext();
        //获取HttpServletRequest对象
        HttpServletRequest request = currentContext.getRequest();
        //获取请求参数序列Id
        String serialId = request.getParameter("serialId");
        //获取请求参数认证码
        String authCode = request.getParameter("authCode");
        if (!serialId.equals(authCode)) {
            //设置不在转发请求
            currentContext.setSendZuulResponse(false);
            //设置HTTP状态响应码
            currentContext.setResponseStatusCode(401);
            //设置响应编码,防止出现响应页面乱码
            currentContext.getResponse().setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
            //设置响应体
            currentContext.setResponseBody("服务认证失败!");
        }
        return null;
    }
}

  依次点击ServerEurekaApplication、ClientUserApplication1、ClientUserApplication2、ClientZuulApplication,工程都启动成功后。在浏览器地址栏访问 http://localhost:9000,其结果如下(四个微服务实例启动正常):

SpringCloud2组件之Zuul详解_第11张图片

在浏览器地址栏访问 http://localhost/u/timeout?serialId=521&authCode=123, 其结果如下:

SpringCloud2组件之Zuul详解_第12张图片

在浏览器地址栏访问 http://localhost/u/timeout?serialId=521&authCode=521, 其结果如下:

SpringCloud2组件之Zuul详解_第13张图片

你可能感兴趣的:(Spring,Cloud)