Zuul说明:
网络请求入口,服务路由,负载均衡,权限控制,为微服务架构提供了前门保护作用,同时将权限控制这些较重的非业务逻辑内容迁移到服务器路由层面,是的服务集群主体能够具备跟高的可复用性和可测试性,并且支持负载均衡ribbon和和断路器hystrix功能
工程结构
spring-cloud-05-zuul-hello-provider [hello模块服务发布端]
spring-cloud-05-zuul-luck-provider [luck模块服务发布端]
spring-cloud-05-zuul-eureka [服务注册中心]
spring-cloud-05-zuul-hello-getway [服务网关]
spring-cloud-05-zuul-eureka [服务注册中心] 代码同上篇,不做说明
spring-cloud-05-zuul-hello-provider [hello模块服务发布端]
备注: 发布两个服务@RestController public class HelloController { @GetMapping("/hello") public String hello() { System.err.println("---hello zuul --"); return "--- hello zuul-----"; } @GetMapping("/token") public String token(@RequestHeader("userId") String userId,@RequestHeader("roleId") String roleId) { System.err.println("---hello zuul --"); return "--- token zuul ,userId="+userId+" , roleId="+roleId+ " --"; } @PostMapping("/upload") public String hello(@RequestParam("file") MultipartFile file) { System.err.println("---hello 附件名称:"+file.getOriginalFilename()); try { FileUtils.writeByteArrayToFile(new File("/Users/wlx/sxt/code/spring-cloud-master/spring-cloud-05-zuul-hello-provider/src/main/resources/META-INF/resources/"+file.getOriginalFilename()),file.getBytes()); } catch (IOException e) { e.printStackTrace(); return "--- upload zuul error --"; } return "--- upload zuul ok --"; } }
/hello 普通服务
/token 需要网关权限验证服务
/upload 附件上传服务
spring: application: name: hello-provider http: encoding: charset: UTF-8 multipart: enabled: true #启用 file-size-threshold: 10 #当数据量大于该值时,内容将被写入文件。 max-file-size: 50MB #最大文件大小 max-request-size: 100MB # 请求信息大小(包含文字等信息) server: port: 7001 context-path: / eureka: client: service-url: defaultZone: http://localhost:8001/eureka/
备注: 使用http.multipart上传附件配置
端口: 7001
@SpringBootApplication @EnableDiscoveryClient public class HelloApplication { public static void main(String[] args) { SpringApplication.run(HelloApplication.class, args); } }备注: @EnableDiscoveryClient注册到配置中心
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div> <form action="http://17.9.16.101:7003/hello-service/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file" /></br> <input type="submit" value="普通上传" /> </form> </div> </hr> <div> <!--说明:通过下面的代码发现前面多了一个 zuul,表示绕过zuul的内部DispatchServlet直接路由到我们的具体微服务, 这样可以避免二次配置(文件大小..., servle默认限制为10兆),并且非常方便, 只要符合 /zuul/*的配置即可--> <form action="http://17.9.16.101:7003/zuul/hello-service/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file" /></br> <input type="submit" value="通过zuul网关地址上传" /> </form> </div> </body> </html>
备注: 1:路径: resources/META-INF/resources/index.html
2: 两种方式实现附件上传, 第一种没有加 zuul/ 如果在上传附件大于10MB的时候, 网关zuul的DispatchServlet 就会拦截服务,
第二种加了zuul,绕过网关zuul的内部DispatchServlet直接路由到我们的具体微服务,hello-provider,由于服务配置的最大文件大小为40MB, 此时如果附件
大于10MB是ok的
spring-cloud-05-zuul-luck-provider [luck模块服务发布端]
@RestController public class LuckController { @GetMapping("/luck") public String hello() throws InterruptedException { System.err.println("---luck zuul --"); return "--- luck zuul --"; } }
备注: 添加服务 /luck
spring: application: name: luck-provider server: port: 7002 context-path: / eureka: client: service-url: defaultZone: http://localhost:8001/eureka/
备注:服务端口号7002
spring-cloud-05-zuul-hello-getway [服务网关]
<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> </dependencies>备注: 导入 spring-cloud-starter-zuul 包依赖
spring: application: name: getway-consumer server: port: 7003 context-path: / eureka: client: service-url: defaultZone: http://localhost:8001/eureka/ #路由规则 zuul: routes: api-hello: #自定义 path: /hello-service/** #自定义 service-id: hello-provider # 服务名称 api-luck: path: /luck-service/** service-id: luck-provider
备注 1: 服务端口 7003
2: 定义路由规则,注意 service-id 要置顶服务名称
@SpringBootApplication @EnableDiscoveryClient @EnableZuulProxy //启动zuul public class GetwayApplication { public static void main(String[] args) { SpringApplication.run(GetwayApplication.class, args); } }备注: 通过 @EnableZuulProxy 启动zuul
@Component //注入到spring容器 public class CustomAuthFilter extends ZuulFilter { /** * pre:再请求被路由之前调用 * routing: 再请求被路由之中调用 * post: 再请求被路由之后调用 * error: 处理请求发生错误时调用 */ @Override public String filterType() { return "pre" ; // post/error/routing } /** * 多个路由情况,该路由执行优先级, 值越小优先级越高 */ @Override public int filterOrder() { return 0; } /** * 是否执行 (启动) * @return */ @Override public boolean shouldFilter() { return true; } /** * 执行内容,业务自己编写 */ @Override public Object run() { //http请求 --> zuul (run 方法) ---> requestContext RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request =ctx.getRequest(); String token =request.getHeader("token"); String uri =request.getRequestURI(); if(uri.contains("hello-service/upload") || uri.contains("hello-service/hello") || uri.contains("hi-service/luck")){ return ctx; } if(StringUtils.isEmpty(token)){ System.err.println("---------no token,auth faild ------------ "); ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(401); ctx.setResponseBody("---------no token,auth faild ------------ "); return null ; } ctx.addZuulRequestHeader("userId","11"); ctx.addZuulRequestHeader("roleId","121"); return ctx; } }
备注: 1:添加网关过滤器,可进行权限控制,实现ZuulFilter ,并重写 filterType/filterType/shouldFilter/run, 含义见备注
2: 对服务 hello / luck /upload 不进行权限校验,直接路由到服务
/** * desc 指定断熔某个服务/自定义响应内容 * */ @Component public class HelloServiceZuulFailBackProvider implements ZuulFallbackProvider{ //指定要段熔的服务名: appName @Override public String getRoute() { return "hello-service"; } @Override public ClientHttpResponse fallbackResponse() { return new ClientHttpResponse() { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.BAD_REQUEST; } @Override public int getRawStatusCode() throws IOException { return this.getStatusCode().value(); } //状态文本信息 @Override public String getStatusText() throws IOException { return this.getStatusCode().getReasonPhrase(); } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("-----service error----".getBytes()); } //response 相应头信息 @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders =new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); return httpHeaders; } }; }
备注: 1: 置顶某个服务段熔策略,可自定义实现方法
2: 实现ZuulFallbackProvider接口,实现自定义方法内容
运行期望结果:
截图1: 服务注册中心 四个服务正常启动
截图2: 通过网关地址 http://17.9.16.101:7003/hello-service/hello 和 http://17.9.16.101:7003/luck-service/luck 路由到不同的服务地址,调用服务和返回不同结果
截图3: 通过网关地址http://17.9.16.101:7003/hello-service/token, 会进行头部token校验,通过调用服务,不通过直接返回
截图4: 通过html,http://17.9.16.101:7001/index.html,附件上传30MB附件,普通情况,网关校验,大于10MB上传失败,添加zuul绕过网关,通过服务配置,上传成功
截图5:服务段熔,自定义结果
截图一:
截图二:
截图三:
截图四:失败
截图四:成功