目录
  • zuul-API网关
    • 统一调用入口
      • 创建项目-zuul
      • 导入依赖
      • application.yml配置
      • 主程序
      • 启动测试
    • 统一权限校验-zuul 请求过滤
    • 集成Ribbon
      • zuul + ribbon 负载均衡
      • zuul + ribbon 重试
      • pom.xml 添加 spring-retry 依赖
      • 配置 zuul 开启重试,并配置 ribbon 重试参数
    • 集成Hystrix
      • zuul + hystrix 降级
      • 创建降级类
      • ItemFallBack
      • OrderFallBack
      • 降低 hystrix 超时时间
      • 启动服务,测试降级
      • zuul + hystrix 数据监控
      • 暴露 hystrix.stream 监控端点
      • 测试
        • 开启监控
      • zuul + turbine 聚合监控
      • 熔断测试
    • zuul和feign的比较

 

zuul-API网关

zuul API 网关,为微服务应用提供统一的对外访问接口。
zuul 还提供过滤器,对所有微服务提供统一的请求校验。

89--spring cloud (zuul-API网关/与feign的比较)_spring cloud

统一调用入口

创建项目-zuul

导入依赖

Eureka Client,zuul

导入基本commons依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.tedu</groupId>
    <artifactId>sp11-zuul</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>sp11-zuul</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.tedu</groupId>
            <artifactId>sp01-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml配置

spring:
  application:
    name: zuul
server:
  port: 3001
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
zuul:
  routes:
    item-service: /item-service/**
    user-service: /user-service/**
    order-service: /order-service/**

主程序

主程序添加@EnableZuulProxy注解,启动zuul

package cn.tedu.sp11;

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

@EnableZuulProxy
@SpringBootApplication
public class Sp11ZuulApplication {

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

}

启动测试

  • http://eureka1:2001
  • http://localhost:3001/item-service/35
  • http://localhost:3001/item-service/decreaseNumber
    使用postman,POST发送以下格式数据:
    [{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
  • http://localhost:3001/user-service/7
  • http://localhost:3001/user-service/7/score?score=100
  • http://localhost:3001/order-service/123abc
  • http://localhost:3001/order-service/

统一权限校验-zuul 请求过滤

89--spring cloud (zuul-API网关/与feign的比较)_spring cloud_02

添加过滤器,判断用户权限

  • 自定义过滤器,继承过滤器父类 ZuulFilter
  • 只需要添加 @Component 注解,zuul会完成自动配置
package cn.tedu.sp11.filter;
//过滤器

import cn.tedu.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang3.StringUtils;
import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
@Component
public class AccessFilter extends ZuulFilter {
    /*
    filterType 指过滤器类型  pre post routing error
     */
    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }
    /*
    filterOrder 顺序号 指定过滤器插入的位置
    在默认过滤器中,第5个过滤器在上下文对象中添加了service-id,
    在第5个过滤器之后,层能从上下文对象访问到service-id

     */
    @Override
    public int filterOrder() {
        return 6;
    }
    /*
        对当前请求来说,是否要进行过滤,
        如果返回true,则要进行过滤,会执行过滤的run()方法
        如果返回false,跳过过滤代码,继续执行后面的流程
     */
    @Override
    public boolean shouldFilter() {
        //判断用户调用是否是商品服务
        //如果是商品服务,则进行过滤
        //如果不是,则不过滤
        //当前请求的上下文对象
        RequestContext ctx = RequestContext.getCurrentContext();
        //从上下文对象中获取客户端调用的service id
        String serviceId =(String) ctx.get(FilterConstants.SERVICE_ID_KEY);
        return "item-service".equals(serviceId);
    }
    /*
    过滤代码
    他的返回值,在当前版本中,没有启用,返回任何数据都无效
     */
    @Override
    public Object run() throws ZuulException {
        //
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        String token = request.getParameter("token");
        if (StringUtils.isBlank(token)){
            //没有token 则阻止访问
            ctx.setSendZuulResponse(false);

            //直接向客户端发送响应
            //返回JsonResult : { code:400 ,msg :not log in ,data : null}
            ctx.setResponseStatusCode(JsonResult.NOT_LOGIN);
            ctx.setResponseBody(JsonResult.err()
                    .code(JsonResult.NOT_LOGIN)
                    .msg("not login")
                    .toString());
        }


        return null;
    }
}

集成Ribbon

zuul + ribbon 负载均衡

zuul 已经集成了 ribbon,默认已经实现了负载均衡

zuul + ribbon 重试

pom.xml 添加 spring-retry 依赖

  • 需要 spring-retry 依赖
<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>
1234

配置 zuul 开启重试,并配置 ribbon 重试参数

  • 需要开启重试,默认不开启
spring:
  application:
    name: zuul
    
server:
  port: 3001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka

zuul:
  retryable: true

#  routes:
#    item-service: /item-service/**
#    user-service: /user-service/**
#    order-service: /order-service/**
    
ribbon:
  ConnectTimeout: 1000
  ReadTimeout: 1000
  MaxAutoRetriesNextServer: 1
  MaxAutoRetries: 1

集成Hystrix

默认已经启用了hystrix,不用做任何基础配置

降级代码:

实现 FallbackProvider 接口,按接口规则来实现降级代码,在实现类上添加 @Component 注解

zuul + hystrix 降级

创建降级类

  • getRoute() 方法中指定应用此降级类的服务id,**号或null值可以通配所有服务

ItemFallBack

package cn.tedu.sp11.fallback;

import cn.tedu.web.util.JsonResult;
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.Component;

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

@Component
public class ItemFallBack implements FallbackProvider {
    /*
    返回一个servicid 表示针对哪个服务使用当前的降级类
    返回* 或者 null 值 ,表示对所有服务执行当前降级类
     */
    @Override
    public String getRoute() {
        return "item-service";
    }
    /*
    封装降级响应的对象
     */
    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK; //封装状态码和状态文本
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return  HttpStatus.OK.value();
            }

            @Override
            public String getStatusText() throws IOException {
                return  HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                String josn = JsonResult.err().msg("调用远程商品服务失败").toString();
                ByteArrayInputStream inputStream = new ByteArrayInputStream(josn.getBytes("UTF-8"));


                return inputStream;
            }

            @Override
            public HttpHeaders getHeaders() {
                //Content-Type:application/json
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);

                return headers;
            }
        };
    }
}

OrderFallBack

package cn.tedu.sp11.fallback;

import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
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.Component;

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

@Component
@Slf4j
public class OrderFallBack implements FallbackProvider {
    @Override
    public String getRoute() {
        return "order-service"; //"*"; //null;
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        return response();
    }

    private ClientHttpResponse response() {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return HttpStatus.OK.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return HttpStatus.OK.getReasonPhrase();
            }

            @Override
            public void close() {
            }

            @Override
            public InputStream getBody() throws IOException {
                log.info("fallback body");
                String s = JsonResult.err().msg("后台服务错误").toString();
                return new ByteArrayInputStream(s.getBytes("UTF-8"));
            }

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

降低 hystrix 超时时间

在application.yml文件中添加超时配置

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 500

启动服务,测试降级

http://localhost:3001/item-service/35

zuul + hystrix 数据监控

暴露 hystrix.stream 监控端点

  • zuul 已经包含 actuator 依赖
management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

测试

  • 启动服务,查看暴露的监控端点
    http://localhost:3001/actuator
    http://localhost:3001/actuator/hystrix.stream

开启监控

启动 sp08-hystrix-dashboard,填入 zuul 的监控端点路径,开启监控

http://localhost:4001/hystrix

填入监控端点:
http://localhost:3001/actuator/hystrix.stream

89--spring cloud (zuul-API网关/与feign的比较)_spring cloud_03

必须通过zuul网关访问后台服务才会生成监控数据

  • http://localhost:3001/item-service/35
  • http://localhost:3001/item-service/decreaseNumber
    使用postman,POST发送以下格式数据:
    [{"id":1, "name":"abc", "number":23},{"id":2, "name":"def", "number":11}]
  • http://localhost:3001/user-service/7
  • http://localhost:3001/user-service/7/score?score=100
  • http://localhost:3001/order-service/123abc
  • http://localhost:3001/order-service/

zuul + turbine 聚合监控

修改 turbine 项目,聚合 zuul 服务实例

添加zuul服务

在turbine的application.yml配置文件中配置

spring:
  application:
    name: turbin
    
server:
  port: 5001
  
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:2001/eureka, http://eureka2:2002/eureka
      
turbine:
  app-config: order-service, zuul
  cluster-name-expression: new String("default")

使用hystrix仪表盘, 对 turbine 监控端点进行监控, 此端点聚合了订单服务和zull网关服务的监控数据
http://localhost:5001/turbine.stream

89--spring cloud (zuul-API网关/与feign的比较)_spring cloud_04

熔断测试

ab -n 20000 -c 50 http://localhost:3001/order-service/123abc

89--spring cloud (zuul-API网关/与feign的比较)_spring cloud_05

zuul和feign的比较

  • 都能做远程调用,都可以集成ribbon 和 hystrix
  • zuul 在最前面使用,做统一调用入口,而feign用在服务之间
  • zuul不推荐开启重试,feign不推荐开启hystrix(会造成程序的混乱)