当前内容用于本人学习和复习之用,当前内容包括Zuul的基本使用,和使用Zuul替换前面的Demo中的AppDemo
前面的Demo:添加了Fegin的Demo
定义:当前的Zuul网关,这个就相当于海关一样,任何东西进来都需要检查,可以通过海关进行交易和传输东西,可以进行验证检查,通过路径匹配的方式实现服务提供者的调用(相当于前面的AppDemo门面)
基本使用
基本上通过访问/hello/hello就会映射到hello-service-provider这个服务中的hello方法中
1.pom依赖
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.3.7.RELEASEversion>
<relativePath />
parent>
<groupId>SpringCloud-AppDemo-ZuulServicegroupId>
<artifactId>SpringCloud-AppDemo-ZuulServiceartifactId>
<version>0.0.1-SNAPSHOTversion>
<packaging>jarpackaging>
<name>SpringCloud-AppDemo-ZuulServicename>
<url>http://maven.apache.orgurl>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Brixton.SR5spring-cloud.version>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>${spring-cloud.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-eurekaartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
2.创建SpringBoot入口类
@EnableZuulProxy
@SpringCloudApplication
public class AppDemoZuulApp {
public static void main(String[] args) {
SpringApplication.run(AppDemoZuulApp.class, args);
}
}
3.编写application.properties中的内容
server.port=2020
spring.application.name=zuul-service
#config pay service
zuul.routes.pay.path=/pay/**
zuul.routes.pay.serviceId=pay-service-provider
#config userInfo service
zuul.routes.userinfo.path=/userinfo/**
zuul.routes.userinfo.serviceId=userinfo-service-provider
#config permiss service
zuul.routes.permiss.path=/permiss/**
zuul.routes.permiss.serviceId=permiss-service-provider
#config eureka register center
eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
首先启动注册中心,然后启动网关,然后启动pay服务和permiss服务
测试成功!
1.如果我们使用网关访问的时候出现了响应超时,则会直接报异常,熔断器没有作用
2.如果一个服务出现宕机或者这个服务整个宕机就会出现异常报错(我们关闭permiss服务)
Caused by:com.netflix.zuul.exception.ZuulException: Forwarding error
Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: permiss-service-provider timed-out and no fallback available.
Caused by: java.util.concurrent.TimeoutException: null
发现控制台出现大量的异常(一般是超时或者服务全部宕机导致的问题),页面也是SpringBoot的StaticView
3.如何解决当前的异常返回情况,使用json返回?
1.关于第一个问题,如果使用Zuul请求的时候服务超时问题,暂时没有解决办法(找到后会写在这里)
2.关于第二个问题,本人尝试(继承ZuulFilter也无法解决
)访问百度发现这里:StackOverflow,其实配置类也无法解决
,本人通过debug方式发现注入无效,并且通过设置filterOrder为-1也无法解决(找到后写在这里)
问题1和问题2的解决办法
可以通过实现ZuulFallbackProvider来实现操作,但是我的版本太老了(没有这个类)所以只有替换版本
,参考:解决方案==修改SpringBoot的版本,修改SpringCloud的版本如下
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>1.5.2.RELEASEversion>
<relativePath />
parent>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<java.version>1.8java.version>
<spring-cloud.version>Dalston.RELEASEspring-cloud.version>
properties>
然后通过实现ZuulFallbackProvider来实现网关的熔断操作
@Component
public class DefaultZuulFallbackProvider implements ZuulFallbackProvider {
@Override
public String getRoute() {
return "*";
}
@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("当前访问的服务不可达,请稍后重试!".getBytes());
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
直接启动eureka注册中心,和当前的网关中心,随便访问一个不存在的网关显示结果如下
成功解决!
3.第三个问题的解决方案
使用@ControllerAdvice+@RequestMapping("/error")+@ExceptionHandler(value = { Throwable.class })
,通过本人测试,无法解决(具体原因,Zuul出现的问题是Servlet的问题不是MVC的问题,所以它无法解决
)
使用实现ErrorController方式(本人测试,可以使用,但是存在问题,后台继续报错)
所以这里直接贴出返回json错误数据的办法
@RestController
public class CustomErrorController implements ErrorController {
@RequestMapping(path = "/error")
public String handle(HttpServletRequest request, Exception ex) {
Object status = request.getAttribute("javax.servlet.error.status_code");
Object reason = request.getAttribute("javax.servlet.error.message");
return "{\"status\":\"" + status + "\",\"reason\":\"" + reason + "\"}";
}
@Override
public String getErrorPath() {
return "error";
}
}
主要通过继承ZuulFilter方式实现,用于验证每次访问的时候都必须携带name参数
/*@Component*/
public class LogFilter extends ZuulFilter {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext requestContext = RequestContext.getCurrentContext();
HttpServletRequest request = requestContext.getRequest();
log.info("request :" + request.getLocalAddr() + ",requestURL :" + request.getRequestURI() + ",method :"
+ request.getMethod());
String name = request.getParameter("name");
if (name == null || "".equals(name.trim())) {
requestContext.setSendZuulResponse(false);
requestContext.setResponseStatusCode(401);
HttpServletResponse response = requestContext.getResponse();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.write("{\"result\":\"请求为携带name属性\"}");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
if (writer != null) {
writer.flush();
writer.close();
}
}
requestContext.setResponse(response);
return null;
}
return null;
}
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
}
1.通过使用Zuul发现了一些问题,那就是访问超时或者访问服务不存在的问题
2.在Zuul中抛出的异常不能使用@ControllerAdvice来解决,只能使用实现ErrorController来实现
3.通过Zuul实现了类似AppDemo中的内容,并且简化了配置但是熔断器的配置却失效了
4.如果要为当前的网关统一添加某些能力或者过滤验证操作,可以通过继承ZuulFilter方式来实现
(例如每个访问都必须携带token)
5.可以通过配置zuul.ignored-patterns=/**/hello/**
方式忽略某些匹配的路劲
以上纯属个人见解,如有问题请联系本人!