首页 > 编程知识 正文

zuul路由原理,自定义网络路由

时间:2023-05-03 20:03:43 阅读:136117 作者:977

注意

zul提供了内部路由,即在所有服务之间相互访问时通过zul进行。 但这不是正确的用法。 zul接受的请求应该都来自外部,主要指前端(浏览器/app )。 内部服务的相互调用只需通过Eureka Server进行服务的发现和调用即可。

开始zuul服务

使用Spring Cloud启动zuul很简单。 引入spring-cloud-starter-zuul,提供注释EnableZuulProxy就可以了。

org.springframework.cloud

spring-cloud-starter-zuul

@EnableZuulProxy

@SpringBootApplication

public class ZuulApplication {

publicstaticvoidmain (string [ ] args ) {

spring application.run (zuula pplication.class,args );

}

@Bean

公共简单过滤器简单过滤器(

返回新简单过滤器(;

}

}

路由配置

zuul的路由策略缺省在配置文件中实现,但也可以从插件中读取配置中心或数据库中的配置。 以下均以配置文件为例。

不使用Eureka

如果zuul不协助Eureka服务发现服务,则zuul路由将根据URL进行路由。 例如,以下结构:

spring:

APP :

name :应用程序网关

服务器:

端口: 5555

zuul:

routes:

用户服务:

path: /myusers/**

URL :http://127.0.0.1:4444

每次用户访问以zuul的/myusers为前缀的路径时,zuul都会将请求移动到127.0.0.1:4444。

使用Eureka

基于URL的资源调配存在一个问题,即在将服务本身调度到其他节点时,Zuul无法识别。 如果利用Eureka注册服务的能力,Zuul也能去Eureka Server获取服务的地址,并根据所有服务的实例进行轮询/熔断/重试,那就更好了。 当然,Zuul和Eureka是Netflix开发的,所以可以在它们之间进行合作。

首先,为Zuul使用Eureka进行其他配置。 这包括添加spring-cloud-starter-euraka相关性,以及配置zuul APP应用程序的Eureka相关注释。

org.springframework.cloud

spring-cloud-starter-eureka

@EnableZuulProxy

@EnableDiscoveryClient

@EnableCircuitBreaker

@SpringBootApplication

public class ZuulApplication {

publicstaticvoidmain (string [ ] args ) {

spring application.run (zuula pplication.class,args );

}

@Bean

公共简单过滤器简单过滤器(

返回新简单过滤器(;

}

}

EnableDiscoveryClient用于使Zuul能够访问Eureka Server并注册自己,EnableCircuitBreaker提供在Zuul传输的同时具有熔断能力的能力

现在,您不再需要在配置文件中写入目标服务器的地址,而是可以将其替换为serviceId。 此外,还必须将Eureka Server的地址告诉Zuul。

spring:

APP :

name :应用程序网关

服务器:

端口: 5555

eureka:

客户端:

服务:

dfaultzone :http://localhost :1111/eureka /

zuul:

routes:

user-service : /我的用户/* *

路由负载平衡

当Zuul发出服务请求时,实际上使用Ribbon来实现

。Ribbon 我们前面说过,它就是为了服务负载均衡的。

后台起两个 user 服务。

再通过 Zuul 进行访问,可以看到后台两个服务被轮询调用。

路由熔断

试着用 Hystrix 为路由加上熔断功能。在Spring Cloud Camden.SR1版本之前(包括这个版本),Zuul 并没有提供熔断功能,在这个版本后面,我们就可以通过写自定义的 fallback 方法,并且将其指定给某个 route 来实现该 route 访问出问题的熔断处理。

所以接口应该包括两部分:

此熔断是处理哪个 route 的。

熔断的返回什么。

这个接口就是 ZuulFallbackProvider。

public interface ZuulFallbackProvider {

/**

* The route this fallback will be used for.

* @return The route the fallback will be used for.

*/

public String getRoute();

/**

* Provides a fallback response.

* @return The fallback response.

*/

public ClientHttpResponse fallbackResponse();

}

实现类通过实现 getRoute 方法,告诉 Zuul 它是负责哪个 route 定义的熔断。而 fallbackResponse 方法则是告诉 Zuul 断路出现时,它会提供一个什么返回值来处理请求。

比如为了 user 服务,写一个如下的断路器。

@Component

public class UserServiceFallback implements ZuulFallbackProvider {

//指定要处理的 service。

@Override

public String getRoute(){

return "user-service";

}

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("Cached user info".getBytes());

}

@Override

public HttpHeaders getHeaders() {

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_JSON);

return headers;

}

};

}

关闭 user 服务两个实例中的一个,就会发现熔断被触发了。

这里处理的粒度其实跟之前服务调用的 Feign 或者 Hystrix 是不一样的。之前处理的是某个方法,比如 getUsers,而这里处理的是 route。一般来说,一个服务一个 route(不同服务不同 context),一个服务多个方法(CRUD)。所以如果要这么用的话,其实对于一个服务来说,所有的方法都是同一个 fallback 了。如果想要实现细粒度的 fallback,就必须针对服务的方法去做 route。然而:Zuul 不支持 serviceId + URL 的混合形式。即无法为某个服务的某个接口做单独的 route。下面这种 route 配置是不生效的:

spring:

application:

name: api-gateway

server:

port: 5555

eureka:

client:

serviceUrl:

defaultZone: http://localhost:1111/eureka/

zuul:

routes:

user-service/api/users: /myusers

这种情况下,就只能做 url 方式的配置。或者,不要让 Zuul 自动帮你配置 Proxy,而是只启动 ZuulServer,所有的 filter 自己来实现。在这种时候,Zuul 只能帮你提供固定类型的配置,即 zuul.routes,没有 path、serviceid、url这几个参数。自定义 filter 可以读取参数来决定如何发现服务,如何组装 Ribbon client,如何选择断路器……

路由重试

Bixton 以后的 Zuul 必须要依赖 Spring Retry 来实现重试,否则就要自己实现一个带 Retry 逻辑的 HttpClient 给 Zuul 用。

开启 retry 非常简单,在依赖里面加入 spring-retry,然后配置项加入zuul.retryable=true就可以开启retry。任何一个服务实例关掉路由都会立刻转向另一个。

当然开启重试在某些情况下是有问题的,比如当压力过大,一个实例停止响应时,路由将流量转到另一个实例,很有可能导致最终所有的实例全被压垮。说到底,断路器的其中一个作用就是防止故障或者压力扩散。用了 retry,断路器就只有在该服务的所有实例都无法运作的情况下才能起作用。这种时候,断路器的形式更像是提供一种友好的错误信息,或者假装服务正常运行的假象给使用者。

不用 retry,仅使用负载均衡和熔断,就必须考虑到是否能够接受单个服务实例关闭和 eureka 刷新服务列表之间带来的短时间的熔断。如果可以接受,就无需使用 retry。

Failover

Zuul 本身并没有 Eureka Server 那样的集群模式。对于 Zuul 的高可用,需要像对待普通应用一样,进行额外的工作。分两种模式吧。

多个 Zuul 注册 Eureka

由于 Zuul 配合 Eureka 时,不支持 serviceId+URL 的拼写模式,所以对于内部的服务调用,通过 Zuul 进行路由的意义不大,同样都需要提供 ServiceId,同样需要指定对应接口的 URL,只不过 serviceId 从目标服务的,换成了 Zuul 的。并且还需要知道该服务在 Zuul 上面注册的路由的路径。

原本目标服务需要知道的信息:

TargetServiceId + /ApiURI/

现在目标服务需要知道的信息:

ZuulServiceId + 'RoutingContext' + /ApiURI/

Zuul 所能做的就是把你的请求转给该服务而已。当然,这种情况下 Zuul 可以统一配置断路器、负载均衡以及重试,但是这种逻辑放到调用方会更好,Zuul 做的会比较通用,特别是断路器的逻辑,粒度不如服务直接使用 hystrix 或者 feign 来的细。

多个 Zuul 配合 Haproxy 或者其他 LB

这种方式是比较推荐的,相当于 Zuul 只对外部暴露(内部没有做服务发现,也可以通过这里进来)。也就是说无论是否使用 Eureka,都推荐用 Haproxy 这种 LB 来为 Zuul 服务进行故障转移和负载均衡。

路由实现代码

调用的顺序大致是 SpringMVC -> ZuulControler -> ZuulServlet -> service(route+filter) -> ZuulRunner ,后面再具体分析。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。