扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
SpringCloud中如何使用Zuul路由网关,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。
公司主营业务:网站设计、成都网站制作、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。创新互联公司是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。创新互联公司推出翁源免费做网站回馈大家。
网关可以将所有的api统一暴露,保护其他服务api接口和信息,以防止被外界直接调用
网关可以做身份认证和权限认证,防止非法请求操作API,保护服务
网关可以实现监控功能,实时日志输出,对请求进行记录
网关可以实现流量监控,便于在高流量情况下对服务进行降级
Zuul可以和Ribbon、Eureka相结合,实现智能路由和负载均衡
通过一定策略,把请求流量分发到集群状态的多个实例中
可以将API从内部接口中分离出来,方便单测
Zuul基于Servlet实现,通过自定义的ZuulServlet来对请求进行控制,Zuul中有一系列的过滤器,这些过滤器是同样是Zuul的实现关键,请求发起和响应期间,通过这些过滤器实现Zuul的功能。具体有以下四个:
PRE过滤器:在请求路由到具体的服务之前执行,用途:安全验证(身份校验,参数校验、ip黑白名单);
ROUTING过滤器 :在请求的服务到具体的微服务实例时执行,用途:进行网络请求(默认使用HttpClient);
POST过滤器:在请求路由到微服务之后执行,用途:统计信息,回传响应;
ERROR过滤器:在其他过滤器发生错误的时候执行,用途:保证请求能够正确响应;
ZuulFilter中的方法有以下四个,继承ZuulFilter并且重写以下四个方法即可实现一个过滤器。
public String filterType(); 返回该Filter的类型,即如上四种过滤器。
public int filterOrder(); 返回该过滤器的执行顺序。
public boolean shouldFilter(); 返回该过滤器是否需要执行。
public Object run(); 执行具体的过滤逻辑。
ZuulServlet
是Zuul的核心Servlet,负责初始化ZuulFilter
并且编排这些过滤器,具体代码在service()
方法中。
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { try { this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse); RequestContext context = RequestContext.getCurrentContext(); context.setZuulEngineRan(); try { this.preRoute(); } catch (ZuulException var13) { this.error(var13); this.postRoute(); return; } try { this.route(); } catch (ZuulException var12) { this.error(var12); this.postRoute(); return; } try { this.postRoute(); } catch (ZuulException var11) { this.error(var11); } } catch (Throwable var14) { this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName())); } finally { RequestContext.getCurrentContext().unset(); } }
RequestContext
是ZuulFilter
中负责上下文衔接的角色,其本身是一个ConcurrentHashMap
,包含Request和Response、routeType、routeHost等上下文需要的对象。
父级项目zuul-test/pom.xml
com.calvin.zuul zuul-test pom 1.0-SNAPSHOT eureka-server user-service zuul-service org.springframework.boot spring-boot-starter-parent 1.5.3.RELEASE UTF-8 UTF-8 1.8 org.springframework.cloud spring-cloud-dependencies Dalston.SR4 pom import
zuul-service/pom.xml
zuul-test com.calvin.zuul 1.0-SNAPSHOT 4.0.0 zuul-service org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-zuul org.springframework.boot spring-boot-maven-plugin
zuul-service/ZuulApplication.java
/** ** 启动类 *
* @author Calvin * @date 2019/10/25 * @since 1.0 */ @SpringBootApplication @EnableEurekaClient @EnableZuulProxy public class ZuulServerApplication { public static void main(String[] args) { SpringApplication.run(ZuulServerApplication.class, args); } }
zuul-service/application.yml
spring: application: name: zuul-service server: port: 8030 eureka: client: service-url: defaultZone: http://localhost:8010/eureka/ instance: hostname: localhost zuul: routes: user-api: path: /user/** serviceId: user-service
user-service/pom.xml
zuul-test com.calvin.zuul 1.0-SNAPSHOT 4.0.0 user-service org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-feign org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-maven-plugin
user-service/application.yml
spring: application: name: user-service server: port: 8020 eureka: client: service-url: defaultZone: http://localhost:8010/eureka/ instance: hostname: localhost
user-service/UserApplication.java
/** ** 启动类 *
* @author Calvin * @date 2019/10/25 * @since 1.0 */ @SpringBootApplication @EnableEurekaClient @EnableFeignClients public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class, args); } }
user-service/controller/UserController.java
/** ** * @author Calvin * @date 2019/10/25 * @since */ @RestController public class UserController { /** * 简单构建一个User */ @GetMapping("/userDetail/{userId}") public SysUser getUserDetail(@PathVariable("userId") String userId){ SysUser sysUser = new SysUser(); sysUser.setId(userId); sysUser.setAge(20); sysUser.setPassword(MD5Utils.digestAsHex("123456")); sysUser.setUsername("Calvin"); //图片来自百度 sysUser.setHeadImg("https://b-ssl.duitang.com/uploads/item/201510/17/20151017181645_c5hWE.thumb.700_0.jpeg"); return sysUser; } }
依次启动eureka-server, user-server, zuul-server
浏览器调用 http://localhost:8030/user/userDetail/1
从调用结果中可以看到我们从zuul-service中调用了user-service的方法,并且调用成功。从而证明路由配置可用;
如需配置版本号,我们只需要咱zuul-service/application.yml中添加配置:zuul.prefix=v1
如果需要在Zuul中实现服务熔断,只需要实现ZuulFallbackProvider
接口,重写其中两个方法,通过getRoute()
方法返回我们需要熔断的路由,通过fallbackResponse()
方法来重写熔断时执行的逻辑。
如下,我们实现第一个user-service的熔断器
/** ** user-service熔断器 *
* * @author Calvin * @date 2019/10/27 * @since */ @Component public class UserServiceCallback implements ZuulFallbackProvider { @Override public String getRoute() { return "user-service"; } @Override public ClientHttpResponse fallbackResponse() { return new CommonClientResponse(); } }
贴上CommonClientResponse
的代码,就是针对ClientHttpResponse接口的封装
/** *封装的通用返回类
* * @author Calvin * @date 2019/10/27 * @since */ public class CommonClientResponse implements ClientHttpResponse { @Override public HttpStatus getStatusCode() throws IOException { return HttpStatus.OK; } @Override public int getRawStatusCode() throws IOException { return 0; } @Override public String getStatusText() throws IOException { return "success"; } @Override public void close() { } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("hello , this is zuul fallback".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); return headers; } }
接下来做个测试,我们停掉user-service服务,然后再访问 http://localhost:8030/user/userDetail/1
结果当然是我们定义的熔断器中返回的内容了!
如果需要对其他服务使用同一个熔断器,只需要在
getRoute()
方法中返回通配符return "*"
就可以了
ZuulFilter
是Zuul实现过滤和网关的关键,此类有四个枚举值,分别代表Zuul中的过滤器类型。如果需要实现过滤,只需要继承ZuulFilter
,并且指定其过滤器类型,枚举值为:
/** * {@link ZuulFilter#filterType()} error type. */ String ERROR_TYPE = "error"; /** * {@link ZuulFilter#filterType()} post type. */ String POST_TYPE = "post"; /** * {@link ZuulFilter#filterType()} pre type. */ String PRE_TYPE = "pre"; /** * {@link ZuulFilter#filterType()} route type. */ String ROUTE_TYPE = "route";
简单实现一个过滤器
/** *header过滤器
* * @author Calvin * @date 2019/10/27 * @since */ @Component public class TokenFilter extends ZuulFilter { private static final Logger logger = LoggerFactory.getLogger(TokenFilter.class); @Override public String filterType() { return PRE_TYPE; } @Override public int filterOrder() { return 0; } @Override public boolean shouldFilter() { return true; } @Override public Object run() { RequestContext requestContext = RequestContext.getCurrentContext(); HttpServletRequest request = requestContext.getRequest(); logger.info("request_url : {}, request_headers: {}",request.getRequestURI(), JSON.toJSON(request.getHeaderNames()).toString()); return null; } }
重新启动zuul-service,调用服务控制台已经可以输出如下内容:
2019-11-12 22:04:36.726 INFO 58984 --- [nio-8030-exec-4] com.calvin.zuul.filter.TokenFilter : request_url : /user/userDetail/1, request_headers: ["host","connection","cache-control","upgrade-insecure-requests","user-agent","sec-fetch-user","accept","sec-fetch-site","sec-fetch-mode","accept-encoding","accept-language","cookie"]
若需要拦截请求,或者设置白名单等,在RequestContext中设置好自己的statusCode等,就可以了
requestContext.setSendZuulResponse(false); requestContext.setResponseStatusCode(401); ctx.getResponse().getWriter().write("there is no token found,please relogin!")
看完上述内容,你们掌握SpringCloud中如何使用Zuul路由网关的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注创新互联行业资讯频道,感谢各位的阅读!
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流