Spring Cloud Zuul
Zuul负责路由转发和过滤
总览
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、熔断器、智能路由、配置管理等,由这几个基础组件相互协作,共同组建了一个简单的微服务系统。一个简单的微服务系统如下图:
在 Spring Cloud 微服务系统中,一种常见的负载均衡方式是,客户端的请求首先经过负载均衡(Zuul、Ngnix),再到达服务网关(Zuul 集群),然后再到具体的服。服务统一注册到高可用的服务注册中心集群,服务的所有的配置文件由配置服务管理,配置服务的配置文件放在 GIT 仓库,方便开发人员随时改配置。
创建Zuul转发
配置依赖和完善工程结构
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>com.outlook.liufei32</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>1.0.0-SNAPSHOT</version> <relativePath>../spring-cloud-dependencies/pom.xml</relativePath> </parent> <artifactId>spring-cloud-zuul</artifactId> <packaging>jar</packaging> <name>spring-cloud-zuul</name> <url></url> <inceptionYear>2019-Now</inceptionYear> <dependencies> <!-- Spring Boot Begin --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Spring Boot End --> <!-- Spring Cloud Begin --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-zuul</artifactId> </dependency> <!-- Spring Cloud End --> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.outlook.liufei32.spring.cloud.zuul.ZuulApplication</mainClass> </configuration> </plugin> </plugins> </build> </project>
application.yml
spring: application: name: spring-cloud-zuul server: port: 8769 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ zuul: routes: #配置路由节点 api-a: path: /api/a/** #**即全目录匹配 serviceId: spring-cloud-web-admin-ribbon #以 /api/a 开头的请求都转发给 hello-spring-cloud-web-admin-ribbon api-b: path: /api/b/** serviceId: spring-cloud-web-admin-feign #以 /api/b 开头的请求都转发给 hello-spring-cloud-web-admin-feign
启动类注解
package com.outlook.liufei32.spring.cloud.zuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; @SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ZuulApplication { public static void main( String[] args ) { SpringApplication.run(ZuulApplication.class, args); } }
自称Zuul基本配置完成,启动访问路径
http://localhost:8769/api/a/hi?message=HelloZuul
就能看到对应的结果
Hi,your message is :"HelloZuul" i am from port:8763
创建失败回调
package com.outlook.liufei32.spring.cloud.zuul.provider; import com.fasterxml.jackson.databind.ObjectMapper; 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; import java.util.HashMap; import java.util.Map; @Component public class WebAdminFeignFallbackProvider implements FallbackProvider { @Override public String getRoute() { //返回你需要为哪个微服务提供回退 // ServiceId,如果需要所有调用都支持回退,则 return "*" 或 return null return "spring-cloud-web-admin-feign"; } /** * 路由 hello-spring-cloud-web-admin-feign 失败时的回调 * @param route * @param cause * @return */ @Override public ClientHttpResponse fallbackResponse( String route, Throwable cause ) { return new ClientHttpResponse() { /** * 网关向 api 服务请求失败了,但是消费者客户端向网关发起的请求是成功的, * 不应该把 api 的 404,500 等问题抛给客户端 * 网关和 api 服务集群对于客户端来说是黑盒 * @return * @throws IOException */ @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return HttpStatus.OK.value(); } @Override public String getStatusText() throws IOException { return HttpStatus.OK.getReasonPhrase(); } @Override public void close() { } //返回流,内容就是字符串json自定义的内容 @Override public InputStream getBody() throws IOException { ObjectMapper objectMapper = new ObjectMapper(); Map<String, Object> map = new HashMap<>(); map.put("status", 200); map.put("message", "无法连接,请检查您的网络"); return new ByteArrayInputStream(objectMapper.writeValueAsString(map).getBytes("UTF-8")); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); // 和 getBody 中的内容编码一致 headers.setContentType(MediaType.APPLICATION_JSON_UTF8); return headers; } }; } }
创建服务过滤
继承 ZuulFilter 类并在类上增加 @Component 注解就可以使用服务过滤功能,(在servlet中继承的是fliter,springMVC继承的是interceptorhandler)
创建ZuulFilter过滤类,并@Component
代码详细:
package com.outlook.liufei32.spring.cloud.zuul.filter; import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @Component public class LoginFilter extends ZuulFilter { /** * 配置过滤类型,有四种不同生命周期的过滤器类型 * 1. pre:路由之前 * 2. routing:路由之时 * 3. post:路由之后 * 4. error:发送错误调用 * @return */ @Override public String filterType() { return "pre"; } /** * 配置过滤的顺序 * @return */ @Override public int filterOrder() { return 0; } /** * 配置是否需要过滤:true/需要,false/不需要 * @return */ @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体业务代码 * @return * @throws ZuulException */ @Override public Object run() throws ZuulException { //从请求中获取请求上下文RequestContext RequestContext context = RequestContext.getCurrentContext(); //从上下文中获取HttpServletRequest,就是传统的servlet HttpServletRequest HttpServletRequest request = context.getRequest(); //获取请求的参数,这里模拟一个请求参数令牌 String token = request.getParameter("token"); if (null == token) { //如果令牌为空则在zuul的请求中设置不发送请求 context.setSendZuulResponse(false); context.setResponseStatusCode(401); try { //从上下文获得response,并设置类型和编码 HttpServletResponse response = context.getResponse(); response.setContentType("text/html;charset=utf-8"); //向请求响应流中写入内容 response.getWriter().write("非法请求"); } catch (IOException e) { e.printStackTrace(); } } return null; } }
访问
http://localhost:8769/api/a/hi?message=swagger
结果:
非法请求
带上token参数则正常访问
http://localhost:8769/api/a/hi?message=swagger&token=124
结果:
Hi,your message is : swagger i am from port : 8763
本博客为Swagger-Ranger的笔记分享,文章会持续更新
文中源码地址: https://github.com/Swagger-Ranger
欢迎交流指正,如有侵权请联系作者确认删除: liufei32@outlook.com
来源:https://www.cnblogs.com/Swagger-Ranger/p/10671522.html