文章目录
Zuul API 网关
统一的入口
统一的权限校验
集成Ribbon
集成Hystrix
统一的入口
- 新建spring模块: sp06-zuul
- 添加依赖:
zuul
sp01
eureka
继承springcloud1
<?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">
<parent>
<artifactId>springcloud1</artifactId>
<groupId>cn.tedu</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>cn.tedu</groupId>
<artifactId>sp06-zuul</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sp06-zuul</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.tedu</groupId>
<artifactId>sp01-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- yml
spring:
application:
name: zuul
server:
port: 3001
# eureka注册中心配置
eureka:
client:
service-url:
# 可以从云服务商购买不同地点的eureka服务器
# 这里可以改成云服务商提供的地点
# 自己的服务器只能写defaultZone
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
转发规则:
# zuul网关配置
zuul:
routes:
# **包含深层子路径
# *只包含一层路径
# service-id作为访问子路径,是默认设置
# 根据注册表中的注册信息,zuul可以自动配置
# 最好自己手动配置,防止注册表不全
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
- 启动类注解: @EnableZuulProxy
package cn.tedu.sp06;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@EnableZuulProxy
@SpringBootApplication
public class Sp06ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(Sp06ZuulApplication.class, args);
}
}
统一的权限校验
zuul的过滤器 ZuulProxy,可以过滤客户端请求,在过滤器中可以检查访问权限
http://localhost:3001/item-service/asfdf 没有登陆,不允许访问
http://localhost:3001/item-service/asfdf?token=s4y431 已登录,允许访问
- 新建过滤器类: AccessFilter 按照 Zuul 的规则实现
新建AccessFilter过滤器类,继承ZuulFilter,实现方法,根据业务重写方法
package cn.tedu.sp06.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 {
/*设置过滤器的类型:
* pre(前置过滤器),routing(路由过滤器),post,error
* */
@Override
public String filterType() {
// return "per";//直接输入过滤器名设置过滤器类型
return FilterConstants.PRE_TYPE;//调用常量设置过滤器类型
}
/* 设置顺序号(默认过滤器有五个,自己创建的过滤器要插入到那个位置,就填第几个)
*/
@Override
public int filterOrder() {
//前置过滤器有五个默认的过滤器,从1开始--5结束
return 6; //把自己的过滤器放置到末尾
}
/* 针对当前请求,是否要执行下面的过滤代码 */
@Override
public boolean shouldFilter() {
/*
调用商品需要检查权限
调用用户或订单不检查权限
*/
//1. 需要获得一个请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
//2. 从上下文对象中获得调用的后台服务的serviceId
String serviceId =(String) ctx.get(FilterConstants.SERVICE_ID_KEY);
//3. 如果调用的是item-service,就返回true
return serviceId.equals("item-service");
}
/* 过滤代码 (权限判断你在这里写)*/
@Override
public Object run() throws ZuulException {
//http://localhost:3001/item-service/asfdf?token=s4y431
// 获得上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
// 获得request对象(用获得request对象接收token参数)
HttpServletRequest request = ctx.getRequest();
// 接收token参数
String token = request.getParameter("token");
// 如果token不存在
if (StringUtils.isBlank(token)){
// 阻止继续调用
ctx.setSendZuulResponse(false);
// 直接返回响应
// JsonResult - {code: 400 ,msg: 未登录, data : null}
String json = JsonResult.build().code(400).msg("未登录").toString();
ctx.addZuulResponseHeader("Content-Type", "application/json;charset=UTF-8");
ctx.setResponseBody(json);
}
return null;//zuul当前版本,这个返回值不起任何作用
}
}
- 添加注解@Component
zuul的自动配置,会在spring容器中发现过滤实例,完成自动配置
Zuul 集成 Ribbon
- 负载均衡默认启用
- 重试默认禁用
在最前面重试,可能造成后台服务器大面积压力倍增,大面积出现故障 - 启用重试
- 添加spring-retry依赖
<dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
- yml配置启用重试:
zuul.retryable=true
- 添加spring-retry依赖
# zuul网关配置
zuul:
routes:
# **包含深层子路径
# *只包含一层路径
# service-id作为访问子路径,是默认设置
# 根据注册表中的注册信息,zuul可以自动配置
# 最好自己手动配置,防止注册表不全
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
retryable: true
3. 如果需要可以配置重试参数
Zuul 集成 Hystrix
Hystrix是容错和限流工具
- Hystrix容错: 降级
- Hystrix限流: 熔断
Zuul 网关使用Hystrix进行容错处理,执行降级
zuul默认已经启用Hystrix ,任何基础配置都不用做
调用后台服务失败,执行前面模块中的降级代码,向客户端返回降级结果
- 错误提示
- 缓存数据
- 根据业务逻辑,返回任何结果都可以
- 新建降级类: ItemFB,实现 FallbackProvider 接口
package cn.tedu.sp06.fb;
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.client.ClientHttpResponse;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
@Component
public class ItemFB implements FallbackProvider {
/*
设置针对哪个后台服务进行降级
- item-service: 只对商品服务降级
- *: 对所有服务都应用当前降级类
- 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.INTERNAL_SERVER_ERROR; //返回给客户端的数据
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.value(); //响应数据key(200/500/)
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(); //文本字符串
}
@Override
public void close() {
//用来关闭下面的流
//ByteArrayInputStream不占用底层系统资源,不需要关闭
}
@Override
public InputStream getBody() throws IOException { //协议体
// JsonResult -{code: 500, msg:xxx data: null}
String json = JsonResult
.build()
.code(500)
.msg("后台服务出错,请稍后重试")
.toString();
//字符串变成byte[]数组,封装到ByteArrayInputStream
return new ByteArrayInputStream(json.getBytes("UTF-8"));
}
@Override
public HttpHeaders getHeaders() { //协议头
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/json;charset=UTF-8");
return headers;
}
};
}
}
- 添加注解:
@Component
- zuul的自动配置,可以自动发现降级类实例,完成自动配置
zuul集成Hystrix实现限流,熔断
当流量过大,后台服务出现故障,可以断开线路,限制后台故障服务的流量,等待他从故障中恢复
- 断路器打开条件:
- 条件一: 默认是10秒20次请求(必须首先满足)
- 条件二: 50%请求出错,执行了降级代码
- 断路器打开后,会进入半开状态
在半开状态下,会向后台服务尝试发送一次客户端调用.
调用成功自动关闭断路器,恢复正常.
调用失败,继续保持打开状态