创建sping boot项目 sp06 - zuul
eureka client
zuul
sp01
3.application.yml配置springcloud1 com.drhj 0.0.1-SNAPSHOT 4.0.0 com.drhj sp06-zuul 0.0.1-SNAPSHOT sp06-zuul Demo project for Spring Boot 1.8 org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.boot spring-boot-starter-test test org.springframework.cloud spring-cloud-starter-netflix-zuul com.drhj sp01-commons 0.0.1-SNAPSHOT org.springframework.boot spring-boot-maven-plugin
路由转发规则
spring:
application:
name: zuul
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
# **包含深层路径
# * 只包含一层路径
# 服务id设置成功访问子路径,是默认规则
# zuul根据注册表的注册信息完成自动配置
# 最好手动配置
zuul:
routes:
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
4.在启动类添加@EnableZuulProxy
package com.drhj.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);
}
}
5.测试
注意将之前的人为阻塞注销
启动网关服务,访问如下网页进行测试
http://localhost:3001/item-service/iu56y4t3
http://localhost:3001/user-service/8
http://localhost:3001/user-service/8/score?score=100
http://localhost:3001/order-service/8o7i6u5y4t
http://localhost:3001/order-service/add
1)新建ZuulFilter 子类
2)按zuul的规则实现
3)添加@Component注解
package com.drhj.sp06.filter;
import cn.drhj.web.util.JsonResult;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import org.apache.commons.lang.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,route,post,error
@Override
public String filterType() {
//return “pre”
return FilterConstants.PRE_TYPE;
}
//顺序号
@Override
public int filterOrder() {
return 6;
}
//针对当前请求进行判断
//是否要执行下面的过滤代码
@Override
public boolean shouldFilter() {
//调用后台商品服务需要检查权限
//调用用户和订单可以直接访问
//获得RequestContext对象
RequestContext ctx = RequestContext.getCurrentContext();
//从上下文对象获取访问的后台服务id
String serviceId = (String)ctx.get(FilterConstants.SERVICE_ID_KEY);//“service”
//判断服务id是否是"item-service"
return "item-service".equals(serviceId);
}
//过滤代码,检查用户权限
@Override
public Object run() throws ZuulException {
//http://localhost:3001/item-service/t45t4?token=123456
//获取请求上下文对象
RequestContext ctx = RequestContext.getCurrentContext();
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.err().code(400).msg("Not Login,未登录").toString();
ctx.addZuulResponseHeader("Content-Type","application/json;charset=UTF-8");
ctx.setResponseBody(json);
}
//zuul当前版本中,这个返回值不起任何作用
return null;
}
}
2.测试
重启zuul网关服务
测试不含有参数token的网址 http://localhost:3001/item-service/t45t4
测试含有参数token的网址 http://localhost:3001/item-service/t45t4?token=100
1)zuul默认启用了负载均衡
2)zuul默认没有启用重试
因为在入口位置进行重试,会造成后台大面积服务压力翻倍,可能造成故障传播,雪崩
配置网关服务的applition.ymlorg.springframework.retry spring-retry
spring:
application:
name: zuul
server:
port: 3001
eureka:
client:
service-url:
defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka
# **包含深层路径
# * 只包含一层路径
# 服务id设置成功访问子路径,是默认规则
# zuul根据注册表的注册信息完成自动配置
# 最好手动配置
zuul:
routes:
item-service: /item-service/**
user-service: /user-service/**
order-service: /order-service/**
retryable: true
# 对所有服务的通用配置
ribbon:
MaxAutoRetries: 1
#针对一个服务单独配置重试参数
item-service:
ribbon:
MaxAutoRetries: 0
测试
打开之前写的手动阻塞
访问 http://localhost:3001/item-service/t45t4?token=100
查看后台
8001端口
8002端口
可见,实现了负载均衡,itemservice由于被设置了不允许重试,所以只执行一次,没有发生重试,只是更换了服务器
四,Zuul集成Hystrix 容错和限流 1.介绍Hystrix是容错和限流工具
1)Hystrix容错 – 降级
调用后台服务出错(异常,阻塞,服务崩溃),可以执行当前服务的一段代码,直接向客户端返回降级结果
- 错误提示
- 缓存的结果
- 根据业务逻辑,返回任意的结果
2)Hystrix限流 – 熔断 - 当流量过大,造成后台服务故障,可以断开链路,限制后台服务的访问流量,等待后台服务恢复
- 断路器打开的条件
10秒20次请求(必须首先满足)
50%出错,执行降级代码 - 半开状态
断路器打开一段时间后,会进入半开状态
会尝试发送一次客户端调用,
– 0成功,关闭断路器,恢复正常
– 失败,继续保持打开状态
所以不用添加依赖了
按zuul的规则实现接口的方法
编写item-service的降级类
package com.drhj.sp06.fb;
import cn.drhj.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 ItmFB implements FallbackProvider {
@Override
public String getRoute() {
return "item-service";
}
//发送给客户端的降级响应
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
//封装500状态码和error状态文本
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
//返回500
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.value();
}
//返回error状态文本
@Override
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
}
@Override
public void close() {
}
//返回自定义结果
@Override
public InputStream getBody() throws IOException {
//JsonResult - {code:500,msg:调用商品失败,data:null}
String json = JsonResult.err().code(500).msg("调用商品失败").toString();
return new ByteArrayInputStream(json.getBytes("UTF-8"));
//BAIS流内存数组的流,不占用底层系统资源,不需要关闭
}
//设置返回的数据类型
@Override
public HttpHeaders getHeaders() {
HttpHeaders h = new HttpHeaders();
h.add("Content-Type","application/json;charset=UTF-8");
return h;
}
};
}
}
编写order-service的降级类
package com.drhj.sp06.fb;
import cn.drhj.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 OrderFB implements FallbackProvider {
@Override
public String getRoute() {
return "order-service";
}
//发送给客户端的降级响应
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
//封装500状态码和error状态文本
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR;
}
//返回500
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.value();
}
//返回error状态文本
@Override
public String getStatusText() throws IOException {
return HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase();
}
@Override
public void close() {
//用来关闭下面方法中的流
//BAIS 流内存数组的流,不占用底层系统资源,不需要关闭
}
//返回自定义结果
@Override
public InputStream getBody() throws IOException {
//JsonResult - {code:500,msg:调用商品失败,data:null}
String json = JsonResult.err().code(500).msg("调用订单失败").toString();
return new ByteArrayInputStream(json.getBytes("UTF-8"));
}
//设置返回的数据类型
@Override
public HttpHeaders getHeaders() {
HttpHeaders h = new HttpHeaders();
h.add("Content-Type","application/json;charset=UTF-8");
return h;
}
};
}
}
测试
访问item-service 服务的网址
http://localhost:3001/item-service/t45t4?token=100
由于阻塞会降级
访问order-service 服务的网址
http://localhost:3001/order-service/1



