这一章节讲zuul的使用.
在我们生成的Demo项目上右键点击New->Module->spring Initializr, 然后next, 填写Group和Artifact等信息,
这里Artifact填写eurekazuul, 再次next, 选择内容如下的pom.xml:
<?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 http://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.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.xum</groupId> <artifactId>eureka-zuul</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-zuul</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.RELEASE</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> </repository> </repositories> </project>
eureka-zuul模型结构如下:
1. 首先是EurekaZuulApplication.java内容如下:
package com.xum.eurekazuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.netflix.turbine.EnableTurbine; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableZuulProxy // 这里是启用zuul路由的注解 @EnableHystrixDashboard @EnableTurbine public class EurekaZuulApplication { public static void main(String[] args) { SpringApplication.run(EurekaZuulApplication.class, args); } }
2. application.yml内容如下:
eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ server: port: 8773 zuul: // 这里定义了两个api接口的路由, 一个指向ribbon-consumer, 一个指向fegin-client routes: api-a: path: /api-a/** serviceId: ribbon-consumer api-b: path: /api-b/** serviceId: fegin-client turbine: aggregator: cluster-config: default app-config: '*' cluster-name-expression: new String("default") spring: application: name: eureka-zuul management: endpoint: health: show-details: always endpoints: web: exposure: include: '*'
3. 然后是PermissionsFilter.java内容如下:
package com.xum.eurekazuul.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import static org.springframework.cloud.netflix.zuul.filters.support.FilterConstants.PRE_TYPE; @Component public class PermissionsFilter extends ZuulFilter { private static final Logger LOG = LoggerFactory.getLogger(PermissionsFilter.class); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; /*RequestContext ctx = RequestContext.getCurrentContext(); return (boolean) ctx.get("isSuccess");*/ } @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); LOG.info(String.format("%s AccessNameFilter request to %s", request.getMethod(), request.getRequestURL().toString())); String name = request.getParameter("name");// 获取请求的参数 if(null != name && "xum".equalsIgnoreCase(name)) { // 在向eureka-client项目里调用api的时候,检查带的name内容是否是xum ctx.setSendZuulResponse(true); ctx.setResponseStatusCode(200); ctx.set("isSuccess", true); return null; }else{ ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); ctx.setResponseBody("{"result":"name is not correct!"}"); ctx.set("isSuccess", false); return null; } } }
4. 其次就是ApiFallbackProvider.java内容如下:
package com.xum.eurekazuul.provider; import com.netflix.hystrix.exception.HystrixTimeoutException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 ApiFallbackProvider implements FallbackProvider { private static final Logger LOG = LoggerFactory.getLogger(ApiFallbackProvider.class); @Override public String getRoute() { LOG.info("ApiFallbackProvider=>getRoute"); return "*"; } @Override public ClientHttpResponse fallbackResponse(String route, Throwable cause) { LOG.info(String.format("route:%s,exceptionType:%s,stackTrace:%s", route, cause.getClass().getName(), cause.getStackTrace())); String message = ""; if (cause instanceof HystrixTimeoutException) { message = "Timeout"; } else { message = "Service exception"; } return fallbackResponse(message); } public ClientHttpResponse fallbackResponse(String message) { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>getStatusCode"); return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>getRawStatusCode"); return 200; } @Override public String getStatusText() throws IOException { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>getStatusText"); return "OK"; } @Override public void close() { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>close"); } @Override public InputStream getBody() throws IOException { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>getBody"); String bodyText = String.format("{"code": 999,"message": "Service unavailable:%s"}", message); return new ByteArrayInputStream(bodyText.getBytes()); } @Override public HttpHeaders getHeaders() { LOG.info("ApiFallbackProvider=>ClientHttpResponse=>getHeaders"); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }; } }
顺序启动一下项目:
1. eureka-server
2. config-server
3. eureka-client
4. fegin-client
5. ribbon-consumer
6. zuul-client
然后再浏览器或则post man中输入http://localhost:8773/api-a/ribbonconsumer/test?name=xum, 显示内容如下:
在eureka-zuul项目里Event log打印出如下类似的log:
2019-03-31 14:52:51.881 INFO 1976 --- [nio-8773-exec-9] c.x.eurekazuul.filter.PermissionsFilter : POST AccessNameFilter request to http://localhost:8773/api-a/ribbonconsumer/test
当输入http://localhost:8773/api-a/ribbonconsumer/test, 不带任何参数的时候, 显示内容如下, 路由网关提示name不正确.
然后再post man中输入http://localhost:8773/api-b/feign/feignconsumer?name=xum, 内容如下:
也能在eureka-client项目的Event log里看到如下类似的log:
2019-03-31 14:58:27.395 INFO 1976 --- [nio-8773-exec-5] c.x.eurekazuul.filter.PermissionsFilter : POST AccessNameFilter request to http://localhost:8773/api-b/feign/feignconsumer
相同的道理, 如果参数没有name或则name只不对, 路由网关都会提示出错.
当eureka-client项目没有启动的时候, Hyrtrix会进行相应自我保护.