spring cloud alibaba 学习(三十)spring cloud gateway DiscoveryClientRouteDefinitionLocator


前言

DiscoveryClientRouteDefinitionLocator 用于从配置中心加载服务信息,并创建相应的路由。我这里使用的配置中心是nacos。


一、DiscoveryLocatorProperties

@ConfigurationProperties("spring.cloud.gateway.discovery.locator")
public class DiscoveryLocatorProperties {

	//服务发现开关
	private boolean enabled = false;
	//路由id的前缀
	private String routeIdPrefix;
	//过滤服务
	private String includeExpression = "true";
	//gateway为每一个服务创建了一个router,这个router将以服务名开头的请求路径转发到对应的服务
	private String urlExpression = "'lb://'+serviceId";
	//将请求路径的服务名改为小写
	private boolean lowerCaseServiceId = false;
	//路由断言
	private List<PredicateDefinition> predicates = new ArrayList<>();
	//路由过滤器
	private List<FilterDefinition> filters = new ArrayList<>();
}

在配置文件中打开服务发现的开关

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

二、DiscoveryClientRouteDefinitionLocator

通过服务发现获取服务的路由信息

1、bean 的创建

	@Configuration(proxyBeanMethods = false)
	@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled", matchIfMissing = true)
	public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {

		@Bean
		//打开了开关
		@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
		public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(
				ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
			return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
		}

	}

2、获取服务信息

	public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,
			DiscoveryLocatorProperties properties) {
		this(discoveryClient.getClass().getSimpleName(), properties);
		//通过服务发现客户端从nacos服务端获取服务信息
		serviceInstances = discoveryClient.getServices()
				.flatMap(service -> discoveryClient.getInstances(service).collectList());
	}

3、获取nacos所有服务名

(1)discoveryClient.getServices( )

NacosReactiveDiscoveryClient.java

	@Override
	public Flux<String> getServices() {
		return Flux.defer(() -> {
			try {
				return Flux.fromIterable(serviceDiscovery.getServices());
			}
			catch (Exception e) {
				log.error("get services from nacos server fail,", e);
				return Flux.empty();
			}
		}).subscribeOn(Schedulers.boundedElastic());
	}

(2)serviceDiscovery.getServices( )

NacosServiceDiscovery.java

	public List<String> getServices() throws NacosException {
		//组
		String group = discoveryProperties.getGroup();
		//从nacos获取所有注册的服务信息
		ListView<String> services = namingService().getServicesOfServer(1,
				Integer.MAX_VALUE, group);
		return services.getData();
	}

4、从nacos服务端获取服务信息

(1)discoveryClient.getInstances(service)

	@Override
	public Flux<ServiceInstance> getInstances(String serviceId) {

		return Mono.justOrEmpty(serviceId).flatMapMany(loadInstancesFromNacos())
				.subscribeOn(Schedulers.boundedElastic());
	}

(2)loadInstancesFromNacos( )

	private Function<String, Publisher<ServiceInstance>> loadInstancesFromNacos() {
		return serviceId -> {
			try {
				return Flux.fromIterable(serviceDiscovery.getInstances(serviceId));
			}
			catch (NacosException e) {
				log.error("get service instance[{}] from nacos error!", serviceId, e);
				return Flux.empty();
			}
		};
	}

(3)

	public List<ServiceInstance> getInstances(String serviceId) throws NacosException {
		String group = discoveryProperties.getGroup();
		//进入nacos服务发现流程
		List<Instance> instances = namingService().selectInstances(serviceId, group,
				true);
		return hostToServiceInstanceList(instances, serviceId);
	}

三、RefreshRoutesEvent

首先从NacosWatch开始分析

1、NacosWatch 实现了 SmartLifecycle 接口,在它的 start( ) 方法中会定时调度 nacosServicesWatch( ),默认间隔时30s

	this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
					this::nacosServicesWatch, this.properties.getWatchDelay());

2、nacosServicesWatch()

	public void nacosServicesWatch() {

		//发布心跳事件 HeartbeatEvent
		this.publisher.publishEvent(
				new HeartbeatEvent(this, nacosWatchIndex.getAndIncrement()));

	}

3、RouteRefreshListener

接收 HeartbeatEvent,并发布刷新路由事件RefreshRoutesEvent

	@Override
	public void onApplicationEvent(ApplicationEvent event) {
		if (event instanceof ContextRefreshedEvent) {
			ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event;
			if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) {
				reset();
			}
		}
		else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) {
			reset();
		}
		else if (event instanceof ParentHeartbeatEvent) {
			ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
		else if (event instanceof HeartbeatEvent) {
			//处理心跳事件
			HeartbeatEvent e = (HeartbeatEvent) event;
			resetIfNeeded(e.getValue());
		}
	}
private void resetIfNeeded(Object value) {
		if (this.monitor.update(value)) {
			reset();
		}
	}
private void reset() {
		//发布事件 RefreshRoutesEvent
		this.publisher.publishEvent(new RefreshRoutesEvent(this));
	}

4、CachingRouteLocator

接收 RefreshRoutesEvent 事件,并发布路由刷新结果事件 RefreshRoutesResultEvent

通过 fetch() 方法获取路由信息,分别从 PropertiesRouteDefinitionLocator 、InMemoryRouteDefinitionRepository 、DiscoveryClientRouteDefinitionLocator 获取路由信息。

	@Override
	public void onApplicationEvent(RefreshRoutesEvent event) {
		try {
			fetch().collect(Collectors.toList()).subscribe(
					list -> Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals -> {
						applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this));
						cache.put(CACHE_KEY, signals);
					}, this::handleRefreshError), this::handleRefreshError);
		}
		catch (Throwable e) {
			handleRefreshError(e);
		}
	}

5、从 DiscoveryClientRouteDefinitionLocator 获取服务发现相关路由

(1)getRouteDefinitions( )

	@Override
	public Flux<RouteDefinition> getRouteDefinitions() {

		SpelExpressionParser parser = new SpelExpressionParser();
		Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
		Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

		Predicate<ServiceInstance> includePredicate;
		if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
			includePredicate = instance -> true;
		}
		else {
			includePredicate = instance -> {
				Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
				if (include == null) {
					return false;
				}
				return include;
			};
		}
		//从nacos服务端获取服务信息
		return serviceInstances.filter(instances -> !instances.isEmpty()).map(instances -> instances.get(0))
				.filter(includePredicate).map(instance -> {
					//为每个服务构建RouteDefinition、PredicateDefinition、FilterDefinition
					RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);

					final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

					for (PredicateDefinition original : this.properties.getPredicates()) {
						PredicateDefinition predicate = new PredicateDefinition();
						predicate.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
							predicate.addArg(entry.getKey(), value);
						}
						routeDefinition.getPredicates().add(predicate);
					}

					for (FilterDefinition original : this.properties.getFilters()) {
						FilterDefinition filter = new FilterDefinition();
						filter.setName(original.getName());
						for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
							String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
							filter.addArg(entry.getKey(), value);
						}
						routeDefinition.getFilters().add(filter);
					}

					return routeDefinition;
				});
	}

总结

定时从nacos获取服务信息,为每个服务创建路由信息。
在用户请求时就将对应服务的请求转发到对应的服务上。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: Spring Cloud Alibaba Gateway是一个基于Spring Cloud Gateway的网关,它提供了一些额外的功能,例如动态路由、限流、熔断、安全控制等。它可以帮助开发人员快速构建微服务架构中的网关,提高系统的可靠性和可扩展性。同时,它还提供了一些与阿里云相关的功能,例如阿里云API网关的集成。 ### 回答2: Spring Cloud Alibaba Gateway是一款能够帮助开发者快速构建微服务网关的工具,它基于Spring Cloud Gateway开发,同时结合了Alibaba的Nacos、Sentinel等组件,可以为用户提供高可用、高性能的网关服务。 Spring Cloud Alibaba Gateway主要功能包括请求转发、路由控制、安全认证、限流、熔断等。而其中最重要的功能就是路由控制,开发者可以通过配置路由表,将请求转发到不同的服务节点,支持灰度发布、蓝绿发布等策略,同时也可以动态修改路由表,保证服务节点的动态变化。 此外,Spring Cloud Alibaba Gateway还支持全局限流、API限流、参数限流等多种限流方式,能够保证服务的稳定性和安全性。而在熔断方面,Spring Cloud Alibaba Gateway也提供了多种熔断策略,支持自定义熔断逻辑等功能,可以有效避免服务节点因故障而导致的级联故障。 总体来说,Spring Cloud Alibaba Gateway让微服务架构变得更加简单,同时提高了系统的可靠性、可扩展性和可维护性。如果你对微服务网关感兴趣,那么Spring Cloud Alibaba Gateway是一款值得尝试的工具。 ### 回答3: Spring Cloud Alibaba GatewaySpring Cloud Alibaba家族中的一员,它是一个基于Spring Cloud Gateway,并集成了阿里巴巴相关组件的API网关。Spring Cloud Alibaba Gateway作为微服务架构中的一个重要组件,可以帮助实现应用程序的路由和负载均衡。它为用户提供多种转发、路由、限流、熔断等策略,同时支持Web Sockets、MQTT等通信协议,可满足各种用户需求。Spring Cloud Alibaba Gateway优点如下: 1. 高可用性:Spring Cloud Alibaba Gateway通过实现多个节点组成的集群来保证其高可用性,当某个节点故障时,集群中的其他节点可以接管其工作,保证服务的持续性。 2. 路由代理:Spring Cloud Alibaba Gateway支持多种路由代理策略,支持通过URI、Header、Cookie、QueryParam等基于多种条件进行路由的功能。 3. 熔断降级:通过集成Sentinel,Spring Cloud Alibaba Gateway能够实现对服务的熔断降级,进而实现故障的快速恢复,保证服务的高可靠性。 4. 限流控制:Spring Cloud Alibaba Gateway可以对API进行限流控制,通过限制每秒钟的请求数量,防止请求过多导致系统崩溃。 5. 自定义插件:Spring Cloud Alibaba Gateway提供了多个可扩展插件,可以实现更好的自定义功能,如身份验证、请求转换、请求重定向等。 总之,Spring Cloud Alibaba Gateway是一个功能强大的API网关,它提供了丰富的路由、限流、熔断降级等策略,可以满足不同类型的业务需求。同时,通过架构高可用性,Spring Cloud Alibaba Gateway能够实现对服务的快速恢复,保证服务的稳定性和可靠性。显然,Spring Cloud Alibaba Gateway适合各种企业级应用,特别是基于云计算、微服务架构的企业。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_lrs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值