本文共 6685 字,大约阅读时间需要 22 分钟。
依赖:
org.springframework.cloud spring-cloud-starter-eureka org.springframework.cloud spring-cloud-starter-zuul org.springframework.boot spring-boot-starter-web
在程序的启动类EurekaZuulClientApplication 加上@EnableEurekaClient注解,开启EurekaClient的功能;加上@SpringBootApplication注解,表明自己是一一个Spring Boot工程;加上@EnableZuulProxy注解,开启Zuul的功能。代码如下:
@SpringBootApplication@EnableEurekaClient@EnableZuulProxypublic class EurekaZuulClientApplication { public static void main(String[] args) { SpringApplication.run(EurekaZuulClientApplication.class, args); }}
配置:
server: port: 5000spring: application: name: service-zuuleureka: client: service-url: defaultZone: http://localhost:8761/eureka/zuul: routes: hiapi: path: /hiapi/** serviceId: eureka-client ribbonapi: path: /ribbonapi/** serviceId: eureka-ribbon-client feignapi: path: /feignapi/** serviceId: eureka-feign-client
在本案例中,zuul.routes.hiapi.path为"/hiapi/**",zuul.routes.hiapi.serviceId为"eureka-client",这两个配置就可以将以"/hiapi"开头的Url路由到eureka-client服务。其中,zuul.routes.hiapi 中的“hiapi" 是自己定义的,需要指定它的path 和serviceld, 两者配合使用,就可以将指定类型的请求Url 路由到指定的ServiceId。
同理,满足以“/ribbonapi" 开头的请求Url都会被分发到eureka-ibbon- client, 满足以“/feignapi/"开头的请求Url都会被分发到eureka-feign-client服务。如果某服务存在多个实例,Zuul结合Ribbon会做负载均衡,将请求均分的部分路由到不同的服务实例。
依次启动工程eureka-server、 eureka-client、 eureka-ribbon-client、 eureka-feign-client和eureka-zuul-client,其中eureka-client启动两个实例,端口为8762和8763。在浏览器上多次访问htp:/:ocalhost:000hipihinamn= forezp
,浏览器会交替显示以下内容:
hi forezp, i am from port:8762hi forezp, i am from port:8763
可见Zuul在路由转发做了负载均衡。同理,多次访问htpt:ocalhost:5000/ei/gnapi/jin hi?name-forezp和ht://ocahtosto000/rbbonapihi? name=forezp,也可以看到相似的内容。
如果不需要用Ribbon做负载均衡,可以指定服务实例的Url,用zuul.routes.hiapi.url配置指定,这时就不需要配置zuul.routes.hiapi.serviceld了
。一旦指定了 Url, Zuul 就不能做负载均衡了,而是直接访问指定的Url, 在实际的开发中这种做法是不可取的。修改配置的代码如下:
zuul: routes: hiapi: path: /hiapi/** url: http://localhost:8762
如果你想指定Url,并且想做负载均衡,那么就需要自己维护负载均衡的服务注册列表。首先,将ribbon.eureka.enabled改为false,即Ribbon负载均衡客户端不向Eureka Client获取服务注册列表信息
。然后需要自己维护一份注册列表, 该注册列表对应的服务名为hiapi-v1(这个名字可自定义),通过配置hiapi-v1.ribbon.listOfServers来配置多个负载均衡的Url。代码如下:
zuul: routes: path: /hiapi/** srerviceId: hiapi-v1ribbon: eureka: enabled: falsehiapi-v1: ribbon: listOfServers: http://localhost:8762,http://localhost:8763
如果想给每一个服务的 API接口加前缀,例如ht://oclthost:50000 /v1/hiapihi?name=forezp/,即在所有的API接口上加一一个v1作为版本号。这时需要用到zul.prefix的配置,配置示例代码如下:
zuul: routes: hiapi: path: /hiapi/** serviceId: eureka-client ribbonapi: path: /ribbonapi/** serviceId: eureka-ribbon-client feignapi: path: /feignapi/** serviceId: eureka-feign-client prefix: /v1
重新启动eureka- zuul-service服务,在浏览器上访问http://localhost:5000/v1/hiapi/hi?name=test
,浏览器会显示:
Zuul作为Netflix组件,可以与Ribbon、Eureka和Hystrix等组件相结合,实现负载均衡、熔断器的功能。
在默认情况下,Zuul 和Ribbon相结合,实现了负载均衡的功能
。
实现ZzulFallbackProvider的接口
。实现该接口有两个方法, ZuulFallbackProvider 的源码如下:
实现一个针对eureka-client服务的熔断器,当eureka-client的服务出现故障时,进入熔断逻辑,向浏览器输入一句错误提示,代码如下:
@Componentpublic class MyFallbackProvider implements ZuulFallbackProvider { @Override public String getRoute() { return "eureka-client"; } @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("oooops!error,i'm the fallback.".getBytes()); } @Override public HttpHeaders getHeaders() { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_JSON); return httpHeaders; } }; }}
重新启动eureka-zuul-client 工程,并且关闭eureka-client 的所有实例,在浏览器上访问htp://ocalhost:5000 /hiapi/hi?name-forezp,浏览器显示:
如果需要所有的路由服务都加熔断功能,只需要在getRoute()方法上返回"*
"的匹配符: @Override public String getRoute() { return "*"; }
实现过滤器很简单,只需要继承ZuulFiter,并实现ZuulFiter中的抽象方法,包括filterType()和filterOrder(),以及IZuulFilter的shouldFilter()和Object run()的两个方法
。
在本例中,检查请求的参数中是否传了token这个参数,如果没有传,则请求不被路由到具体的服务实例,直接返回响应,状态码为401。
@Componentpublic class MyFilter extends ZuulFilter { private static Logger log = LoggerFactory.getLogger(MyFilter.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 ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String 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 (IOException e) { e.printStackTrace(); } } log.info("ok"); return null; }}
重新启动服务,打开浏览器,访问http://localhost:5000/hiapi/hi?name=forezp
,浏览器显示:
http://localhost:5000/hiapi/hi?name=forezp&token=HJHKLL
可见,MyFilter 这个Bean注入IoC容器之后,对请求进行了过滤,并在请求路由转发之前进行了逻辑判断。在实际开发中,可以用此过滤器进行安全验证。本例的架构图如图:
Zuul是采用了类似于SpringMVC的DispatchServlet来实现的,采用的是异步阻塞模型,所以性能比Ngnix差
。由于Zuul和其他Netflix组件可以相互配合、无缝集成,Zuul 很容易就能实现负载均衡、智能路由和熔断器等功能。在大多数情况下,Zuul都是以集群的形式存在的。由于Zuul的横向扩展能力非常好,所以当负载过高时,可以通过添加实例来解决性能瓶颈。
一种常见的使用方式是对不同的渠道使用不同的Zuul来进行路由
,例如移动端共用一个Zuul网关实例,Web端用另一个Zuul网关实例,其他的客户端用另外一个Zuul实例进行路由。这种不同的渠道用不同Zul实例的架构如下图所示:
转载地址:http://zopqb.baihongyu.com/