zuul网关路由
一 环境准备:
首先好一套简单的springcloud基本框架(zhangpba-springcloud)
公共代码:study-common
注册中心:study-eureka 端口:8815
文件服务:study-file 端口:8816
用户服务:study-user 端口:8817
1 其中study-user外露两个接口,外露接口的代码:
http://127.0.0.1:8817/client/getFile?name=名称参数【利用feign调用study-file的外露接口(getHost)】
http://127.0.0.1:8817/client/postFile 【利用feign调用study-file的外露接口(postHost)】
package com.study.user.controller; import com.study.vo.User; import com.study.user.feign.FileServiceFeign; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; /** * 测试feign接口:fegin接口消费方 * * @author zhangpba */ @RestController public class FeignTestController { private static final Logger logger = LoggerFactory.getLogger(FeignTestController.class); @Autowired private FileServiceFeign fileServiceFeign; /** * 测试post请求:消费方 * * @param user 用户参数 * @return */ @RequestMapping(value = "/client/postFile", method = RequestMethod.POST) public String postFile(User user) { return fileServiceFeign.postFileHost(user); } /** * 测试get请求:消费方 * * @param name 参数 * @return */ @RequestMapping(value = "/client/getFile", method = RequestMethod.GET) public String getFile(String name) { return fileServiceFeign.getFileHost(name); } }
2 study-file外露两个接口,外露接口的代码:
http://127.0.0.1:8816/getFileHost?name=名称参数
http://127.0.0.1:8816/postFileHost?name=名称参数
package com.study.file.controller; import com.study.vo.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; /** * 测试feign接口:fegin接口服务提供方 * * @author zhangpba */ @RestController public class FeginTestController { private static final Logger logger = LoggerFactory.getLogger(FeginTestController.class); /** * 测试feign接口的get请求:服务提供方 * * @param name * @return */ @RequestMapping(value = "/getFileHost", method = RequestMethod.GET) public String getFileHost(@RequestParam("name") String name) { logger.info("进入feign服务提供者:getFileHost"); return "我是file服务get请求返回的数据:" + name; } /** * 测试feign接口的post请求:服务提供方 * * @param user * @return */ @RequestMapping(value = "/postFileHost", method = RequestMethod.POST, produces = "application/json; charset=UTF-8") public String postFileHost(@RequestBody User user) { logger.info("进入feign服务提供者:postFileHost"); return "我是file服务post请求返回的数据: " + user; } }
二 网关服务准备
增加网关服务:study-zuul 端口号:8888
搭建一个springboot服务,注册到study-eureka中,并添加zuul的包
pom.xml
<!--2021-06-01 zuul网关--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency> <!--zuul网关的重试机制,不是使用ribbon内置的重试机制是借助spring-retry组件实现的重试 开启zuul网关重试机制需要增加下述依赖--> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency>
启动类 StudyZuulApplication
package com.study.zuul; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.zuul.EnableZuulProxy; /** * @author zhangpba * @EnableZuulProxy - 开启Zuul网关。 * 当前应用是一个Zuul微服务网关。会在Eureka注册中心中注册当前服务。并发现其他的服务。 * Zuul需要的必要依赖是spring-cloud-starter-zuul。 */ @SpringBootApplication @EnableEurekaClient @EnableDiscoveryClient @EnableZuulProxy public class StudyZuulApplication { public static void main(String[] args) { SpringApplication.run(StudyZuulApplication.class, args); } }
三 测试
3.1 测试网关的代理功能
配置增加,请求‘http://127.0.0.1:8888/api/user/client/getFile?name=测试zuul’,可以看到直接请求的是zuul的端口8888,但是可以请求到study-file中,说明网关代理了study-user,而study-user又调用了study-file,所以此时网关已经代理了配置文件中的study-user的外露接口
zuul: prefix: /api # 配置请求路径前缀,所有基于此前缀的请求都由zuul网关提供代理 routes: study-user-service.path: /user/** # 使用路径方式匹配路由规则 study-user-service.serviceId: study-user # 使用服务名称匹配
study-file打印的日志如下:
2021-06-02 23:03:37.368 INFO 7312 --- [nio-8816-exec-4] c.s.file.controller.FeginTestController : 进入feign服务提供者:getFileHost
3.2 测试网关的路由功能
3.2.1 配置file路由,请求‘http://127.0.0.1:8888/api/file/getFileHost?name=测试zuul’,网关可以能代理study-file的外露接口
zuul: prefix: /api # 配置请求路径前缀,所有基于此前缀的请求都由zuul网关提供代理 routes: study-user-service.path: /user/** # 使用路径方式匹配路由规则 study-user-service.serviceId: study-user # 使用服务名称匹配 study-file-service.path: /file/** # 使用路径方式匹配路由规则 study-file-service.serviceId: study-file # 使用服务名称匹配
study-file输出的日志如下:
2021-06-02 23:20:20.083 INFO 7312 --- [nio-8816-exec-2] c.s.file.controller.FeginTestController : 进入feign服务提供者:getFileHost
3.2.2 不配置file路由,请求‘http://127.0.0.1:8888/api/file/getFileHost?name=测试zuul’,返回404找不到路径,说明网关不能代理study-file的外露接口。
zuul: prefix: /api # 配置请求路径前缀,所有基于此前缀的请求都由zuul网关提供代理 routes: study-user-service.path: /user/** # 使用路径方式匹配路由规则 study-user-service.serviceId: study-user # 使用服务名称匹配 # study-file-service.path: /file/** # 使用路径方式匹配路由规则 # study-file-service.serviceId: study-file # 使用服务名称匹配
请求结果如下
因此,配置为:http://ip:port/api/appservice/**的请求提供zuul网关代理,可以将要访问服务进行前缀分类
3.3 测试网关的容错机制
增加容错回调代码:
package com.study.zuul.service; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider; 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.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 如果需要在Zuul网关服务中增加容错处理fallback,需要实现接口ZuulFallbackProvider * spring-cloud框架,在Edgware版本(包括)之后,声明接口ZuulFallbackProvider过期失效, * 提供了新的ZuulFallbackProvider的子接口 - FallbackProvider * 在老版本中提供的ZuulFallbackProvider中,定义了两个方法。 * - String getRoute() * 当前的fallback容错处理逻辑处理的是哪一个服务。可以使用通配符‘*’代表为全部的服务提供容错处理。 * 如果只为某一个服务提供容错,返回对应服务的spring.application.name值。 * - ClientHttpResponse fallbackResponse() * 当服务发生错误的时候,如何容错。 * 新版本中提供的FallbackProvider提供了新的方法。 * - ClientHttpResponse fallbackResponse(Throwable cause) * 如果使用新版本中定义的接口来做容错处理,容错处理逻辑,只运行子接口中定义的新方法。也就是有参方法。 * 是为远程服务发生异常的时候,通过异常的类型来运行不同的容错逻辑。 * * @author zhangpba * @date 2021-06-07 */ @Component public class TestFallBackProvider implements ZuulFallbackProvider { /** * 返回fallback处理哪一个服务。返回的是服务的名称 * <p> * 推荐 - 为指定的服务定义特性化的fallback逻辑。 * 推荐 - 提供一个处理所有服务的fallback逻辑。 * 好处 - 服务某个服务发生超时,那么指定的fallback逻辑执行。如果有新服务上线,未提供fallback逻辑,有一个通用的。 * * @return 返回的是服务的名称 */ @Override public String getRoute() { return "study-user"; } /** * fallback逻辑。在早期版本中使用。(此版本就是早期版本) * Edgware版本之后,ZuulFallbackProvider接口过期,提供了新的子接口FallbackProvider * 子接口中提供了方法ClientHttpResponse fallbackResponse(Throwable cause)。 * 优先调用子接口新定义的fallback处理逻辑。 * * @return */ @Override public ClientHttpResponse fallbackResponse() { System.out.println(" ClientHttpResponse fallbackResponse()"); List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> data = new HashMap<>(); data.put("message", "服务器正忙,请稍后重试!"); result.add(data); ObjectMapper mapper = new ObjectMapper(); String msg = ""; try { msg = mapper.writeValueAsString(result); } catch (JsonProcessingException e) { msg = ""; } return this.executeFallback(HttpStatus.OK, msg, "application", "json", "utf-8"); } /** * 具体处理过程。 * * @param status 容错处理后的返回状态,如200正常GET请求结果,201正常POST请求结果,404资源找不到错误等。使用spring提供的枚举类型对象实现。HttpStatus * @param contentMsg 自定义的响应内容。就是反馈给客户端的数据。 * @param mediaType 响应类型,是响应的主类型, 如: application、text、media。 * @param subMediaType 响应类型,是响应的子类型, 如: json、stream、html、plain、jpeg、png等。 * @param charsetName 响应结果的字符集。这里只传递字符集名称,如: utf-8、gbk、big5等。 * @return ClientHttpResponse 就是响应的具体内容。 * 相当于一个HttpServletResponse。 */ private final ClientHttpResponse executeFallback(final HttpStatus status, String contentMsg, String mediaType, String subMediaType, String charsetName) { return new ClientHttpResponse() { /** * 设置响应头信息 * * @return */ @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); MediaType mt = new MediaType(mediaType, subMediaType, Charset.forName(charsetName)); headers.setContentType(mt); return headers; } /** * 设置响应体 * * @return 响应体的流 * @throws IOException */ @Override public InputStream getBody() throws IOException { String content = contentMsg; return new ByteArrayInputStream(content.getBytes()); } /** * 设置响应体的状态码 * * @return 状态码 httpStatus * @throws IOException */ @Override public HttpStatus getStatusCode() throws IOException { return status; } /** * 设置响应体的状态码 int * @return int * @throws IOException */ @Override public int getRawStatusCode() throws IOException { return 0; } /** * 设置响应体的状态码 String * @return * @throws IOException */ @Override public String getStatusText() throws IOException { return this.getStatusCode().getReasonPhrase(); } /** * 回收资源 * * 用于回收当前fallback逻辑开启的资源对象的。 * 不要关闭getBody方法返回的那个输入流对象。 */ @Override public void close() { } }; } /** * fallback逻辑。优先调用。可以根据异常类型动态决定处理方式。 */ // @Override // public ClientHttpResponse fallbackResponse(Throwable cause) { // System.out.println("ClientHttpResponse fallbackResponse(Throwable cause)"); // if(cause instanceof NullPointerException){ // // List<Map<String, Object>> result = new ArrayList<>(); // Map<String, Object> data = new HashMap<>(); // data.put("message", "网关超时,请稍后重试"); // result.add(data); // // ObjectMapper mapper = new ObjectMapper(); // // String msg = ""; // try { // msg = mapper.writeValueAsString(result); // } catch (JsonProcessingException e) { // msg = ""; // } // // return this.executeFallback(HttpStatus.GATEWAY_TIMEOUT, // msg, "application", "json", "utf-8"); // }else{ // return this.fallbackResponse(); // } // } }
只开启study-eureka、study-zuul,关闭study-user,请求:http://localhost:8888/api/user/client/getFile?name=测试zuul,可以看到通过网关请求访问study-user时,请求超时,超时的提示信息是由我们刚写的容错代码body体返回来的
返回结果:
本文来自博客园,作者:zhangpba,转载请注明原文链接:https://www.cnblogs.com/zhangpb/p/14843398.html