Spring Cloud实战系列(五) - 服务网关Zuul 雨点打透心脏的1/2处 2022-03-20 05:28 136阅读 0赞 # 前言 # `Zuul` 是 `Netflix` 开源的一个 `API Gateway` 服务器, 本质上是一个基于 `Servlet` 的 `Web` 应用。在微服务框架 `Spring Cloud` 中,`Zuul` 被作为 **服务的网关**,负责对 **请求** 进行一些 **预处理**,比如:**安全验证**、**动态路由**、**负载分配** 等等。 # 正文 # ## 1. 路由网关 ## 在前面几篇的基础上,新建一个 `service-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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>io.github.ostenant.springcloud</groupId> <artifactId>service-zuul</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-zuul</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Dalston.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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> </project> 在应用程序启动类上,使用 **注解** `@EnableZuulProxy` 开启 **路由网关**: @EnableZuulProxy @EnableEurekaClient @SpringBootApplication public class ServiceZuulApplication { public static void main(String[] args) { SpringApplication.run(ServiceZuulApplication.class, args); } } 在 `application.yml` 文件中配置 `URL` 前缀和 **服务** 的映射关系。首先指定 **服务注册中心** 的地址为 `http://localhost:8761/eureka/`,服务的 **端口号** 为 `8769`。**服务名** 为 `service-zuul`,以 `/api-a/` 为 **前缀** 的请求都转发给 `service-feign` 服务,以 `/api-b/` 为 **前缀** 的请求都转发给 `service-ribbon` 服务。 server: port: 8769 spring: application: name: service-zuul client: service-url: defaultZone: http://localhost:8761/eureka/ zuul: routes: api-a: path: /api-a/** serviceId: service-feign api-b: path: /api-b/** serviceId: service-ribbon 按顺序依次运行 `eureka-server`、`service-hi`、`service-ribbon`、`service-feign`、`service-zuul` 几个应用,其中在 `8762` 和 `8763` **端口** 启动两个 `service-hi` 服务实例。 访问 `http://localhost:8769/api-a/hi?name=zuul` 两次,服务端返回数据如下: > Hi zuul, I am from port: 8763 > > Hi zuul, I am from port: 8762 这说明 `Zuul` 起到了 **路由** 的作用。如果某个 **服务** 存在 **多个实例**,`Zuul` 会结合 `Ribbon` 做 **负载均衡**,将请求 **均分并路由** 到不同的 **服务实例**。 ## 2. 配置过滤器 ## `Zuul` 不仅只作为 **路由**,并且提供 **过滤功能**,包括一些 **安全验证**。继续改造项目,增加一个 `Zuul` 提供的过滤器。 @Component public class MyFilter extends ZuulFilter { private Logger logger = LoggerFactory.getLogger(getClass()); @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } /** * 过滤器的具体逻辑 */ @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); logger.info(String.format("%s >> %s", request.getMethod(), request.getRequestURL().toString())); Object accessToken = request.getParameter("token"); if (accessToken == null) { log.warn("token is empty"); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); try { ctx.getResponse().getWriter().write("token is empty"); } catch (Exception e) { logger.error(e); return null; } } return null; } } `filterType()` 方法表示 **过滤器** 的类型: * **pre**:路由发生之前; * **routing**:路由发生时; * **post**:路由发生之后; * **error**:发送错误调用时。 访问 `http://localhost:8769/api-b/hi?name=zuul`,服务端返回的响应数据如下: > token is empty 访问 `http://localhost:8769/api-b/hi?name=zuul&token=123` 两次,服务端返回的响应数据如下: > Hi zuul, I am from port: 8763 > > Hi zuul, I am from port: 8762 ## 3. 配置API前戳版本号 ## 在上面 `Zuul` 工程 `application.yml` 的基础上增加一条配置 `zuul.prefix: /v1`,然后重启应用,访问 `http://localhost:8769/v1/api-b/hi?name=mark&token=123` 即可。 ## 4. 配置熔断器 ## 在 `Zuul` 的基础上实现 **熔断功能** 很简单,类似实现 **过滤器** 的步骤,只需要实现 `ZuulFallbackProvider` 接口即可,代码如下: @Component public class MyFallbackProvider implements ZuulFallbackProvider { @Override public String getRoute() { return "service-feign"; } @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("Fallback method is invoked!".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); return httpHeaders; } }; } } * **getRoute()**:指定 **熔断器** 作用于哪些 **服务**; * **fallbackResponse()**:指定 **熔断** 时返回的响应数据。 重新启动 `service-zuul` 应用,并关闭所有的 `service-hi` 的 **服务实例**,在浏览器上访问 `http://localhost:8769/v1/api-a/hi?name=mark&token=123`,服务端返回的响应数据如下: > Fallback method is invoked! 如果需要所有的 **路由服务** 都加上 **熔断功能**,只需要在 `getRoute()` 方法上返回 `“*”` 的 **通配符** 即可。 ## 5. Zuul的常见使用方式 ## `Zuul` 采用的是 **同步步阻塞模型**,性能比 `Nginx` 差,由于 `Zuul` 和其他 `Netflix` 组件 **相互配合**、**无缝集成**,`Zuul` 很容易就能实现 **负载均衡**、**智能路由** 和 **熔断器** 等功能。在大多数情况下,`Zuul` 都是以 **集群** 的形式部署。 * 对不同的 **终端用户**,使用不同的 `Zuul` 来进行 **路由**,例如 **移动端** 共用一个 `Zuul` **网关实例**,`Web` 端用另一个 `Zuul` **网关实例**,其他的 **客户端** 用另一个 `Zuul` 实例进行路由。 * 另外一种常见的 **集群部署方式** 就是通过 `Nginx` 和 `Zuul` 相互结合来做 **负载均衡**。 # 参考 # * 方志朋《深入理解Spring Cloud与微服务构建》 -------------------- 欢迎关注技术公众号: 零壹技术栈 ![零壹技术栈][1240_pic_center] 本帐号将持续分享后端技术干货,包括虚拟机基础,多线程编程,高性能框架,异步、缓存和消息中间件,分布式和微服务,架构学习和进阶等学习资料和文章。 [1240_pic_center]: /images/20220320/5497b4f747e94fe886377738055d19fe.png
还没有评论,来说两句吧...