Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)

1.声明

当前内容用于本人学习和复习之用,当前内容包括Zuul的基本使用,和使用Zuul替换前面的Demo中的AppDemo

前面的Demo:添加了Fegin的Demo

2.Zuul的基本定义和基本用法(个人理解)

定义:当前的Zuul网关,这个就相当于海关一样,任何东西进来都需要检查,可以通过海关进行交易和传输东西,可以进行验证检查,通过路径匹配的方式实现服务提供者的调用(相当于前面的AppDemo门面)

基本使用

  1. 为需要添加网关pom依赖
  2. 为在需要使用的SpringBoot程序中启用@EnableZuulProxy
  3. 编写配置文件,指定erueka的注册中心,设定路径匹配
    3.1 网关路径匹配为:zuul.routes.<服务名称>.path=/<服务名称>/**
    3.2 路径转发调用的服务名称zuul.routes.<服务名称>.serviceId=<服务名称>-service-provider

基本上通过访问/hello/hello就会映射到hello-service-provider这个服务中的hello方法中

3.创建新的ZuulApp用于替换前面的AppDemo

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/

4.测试网关的使用

首先启动注册中心,然后启动网关,然后启动pay服务和permiss服务
Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)_第1张图片
Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)_第2张图片
测试成功!

5.发现问题

1.如果我们使用网关访问的时候出现了响应超时,则会直接报异常,熔断器没有作用

2.如果一个服务出现宕机或者这个服务整个宕机就会出现异常报错(我们关闭permiss服务)

  1. Caused by:com.netflix.zuul.exception.ZuulException: Forwarding error
  2. Caused by: com.netflix.hystrix.exception.HystrixRuntimeException: permiss-service-provider timed-out and no fallback available.
  3. Caused by: java.util.concurrent.TimeoutException: null

发现控制台出现大量的异常(一般是超时或者服务全部宕机导致的问题),页面也是SpringBoot的StaticView

3.如何解决当前的异常返回情况,使用json返回?

6.解决问题

1.关于第一个问题,如果使用Zuul请求的时候服务超时问题,暂时没有解决办法(找到后会写在这里)

2.关于第二个问题,本人尝试(继承ZuulFilter也无法解决)访问百度发现这里:StackOverflow,其实配置类也无法解决,本人通过debug方式发现注入无效,并且通过设置filterOrder为-1也无法解决(找到后写在这里)

问题1和问题2的解决办法

  1. 由于上面两个都是服务出现问题导致的和熔断器有关,通过百度查询发现了可以通过实现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注册中心,和当前的网关中心,随便访问一个不存在的网关显示结果如下
Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)_第3张图片

成功解决!

3.第三个问题的解决方案

  1. 使用@ControllerAdvice+@RequestMapping("/error")+@ExceptionHandler(value = { Throwable.class }),通过本人测试,无法解决(具体原因,Zuul出现的问题是Servlet的问题不是MVC的问题,所以它无法解决)
    Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)_第4张图片

  2. 使用实现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";
	}
}

Spring Cloud Netflix之使用Zuul优化前面的Demo(使用网关方式替换AppDemo)_第5张图片
这个基本解决

7.配置自定义的ZuulFilter

主要通过继承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;
	}

}

8.总结

1.通过使用Zuul发现了一些问题,那就是访问超时或者访问服务不存在的问题

2.在Zuul中抛出的异常不能使用@ControllerAdvice来解决,只能使用实现ErrorController来实现

3.通过Zuul实现了类似AppDemo中的内容,并且简化了配置但是熔断器的配置却失效了

4.如果要为当前的网关统一添加某些能力或者过滤验证操作,可以通过继承ZuulFilter方式来实现(例如每个访问都必须携带token)

5.可以通过配置zuul.ignored-patterns=/**/hello/**方式忽略某些匹配的路劲

以上纯属个人见解,如有问题请联系本人!

你可能感兴趣的:(SpringCloud)