当前位置: 首页 > news >正文

springCloud-gateway按照服务名动态路由的改造(二)

按照服务名动态路由

如果按照我们上面的理解,所有的路由在程序中都对应一个RouteDefenition对象,下面我们来看看gateway是如何根据服务名来生成对应的路由对象RouteDefenition的?

打开GatewayDiscoveryClientAutoConfiguration,根据我们的配置会生成一个DiscoveryClientRouteDefinitionLocator对象bean。

下面我们分析一下DiscoveryClientRouteDefinitionLocator的构造方法和getRouteDefinitions方法,看看是如何生成RouteDefinition的。

在构造方法中,如上图所示,调用了ReactiveDiscoveryClient.getSerivce方法,采用的是响应式编程,返回一个Flux<List<ServiceInstance>>流对象,流对象内部的元素为注册中心的服务实例。

下面我们再看getRouteDefinitions方法,

该方法会遍历所有服务实例,为每个服务生成一个路由。如上图所示

设置uri

在buildRouteDefinition中,new了一个RouteDefinition,并设置了路由的id属性为固定前缀+serviceId,并设置了uri属性为lb://serviceId,如下图所示:

protected RouteDefinition buildRouteDefinition(Expression urlExpr,
			ServiceInstance serviceInstance) {
		String serviceId = serviceInstance.getServiceId();
		RouteDefinition routeDefinition = new RouteDefinition();
		routeDefinition.setId(this.routeIdPrefix + serviceId);
		String uri = urlExpr.getValue(this.evalCtxt, serviceInstance, String.class);
		routeDefinition.setUri(URI.create(uri));
		// add instance metadata
		routeDefinition.setMetadata(new LinkedHashMap<>(serviceInstance.getMetadata()));
		return routeDefinition;
	}

设置断言Predicate

紧接着,遍历了配置类里的Predicate,为路由设置了断言,如下图所示:

 

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);
}

这里注意到断言是从this.properties.getPredicates()取得,但是我们没有在配置文件中配置断言,是不是意味着取得的值为空呢,其实不然,

继续打开GatewayDiscoveryClientAutoConfiguration,我们可以看到类里显式创建了一个DiscoveryLocatorProperties,并通过调用initPredicates设置了断言,调用initFilters方法设置了过滤器,如下图所示:

可以分析出使用的断言为PathRoutePredicateFactory,并设置了pattern为 /serviceId/**,跟我们在配置文件中配置成 -Path=xxx是一样的,在上面给routeDefenition设置断言时是会将serviceId替换成具体的服务名。至此,断言设置完成。

	@Bean
	public DiscoveryLocatorProperties discoveryLocatorProperties() {
		DiscoveryLocatorProperties properties = new DiscoveryLocatorProperties();
		properties.setPredicates(initPredicates());
		properties.setFilters(initFilters());
		return properties;
	}



public static List<PredicateDefinition> initPredicates() {
		ArrayList<PredicateDefinition> definitions = new ArrayList<>();
		// TODO: add a predicate that matches the url at /serviceId?

		// add a predicate that matches the url at /serviceId/**
		PredicateDefinition predicate = new PredicateDefinition();
		predicate.setName(normalizeRoutePredicateName(PathRoutePredicateFactory.class));
		predicate.addArg(PATTERN_KEY, "'/'+serviceId+'/**'");
		definitions.add(predicate);
		return definitions;
	}

	

设置filter

设置filter的代码如下,也是从this.properties.getFilters()中取得。

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);
					}

在上面我们分析断言时,看到同时通过initFilters方法给properties设置了filters属性,使用的是RewritePathGatewayFilterFactory,同时指定了重写的规则,即会把路径中的服务名去掉,然后再请求到下游服务。如下图所示:

public static List<FilterDefinition> initFilters() {
		ArrayList<FilterDefinition> definitions = new ArrayList<>();

		// add a filter that removes /serviceId by default
		FilterDefinition filter = new FilterDefinition();
		filter.setName(normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
		String regex = "'/' + serviceId + '/(?<remaining>.*)'";
		String replacement = "'/${remaining}'";
		filter.addArg(REGEXP_KEY, regex);
		filter.addArg(REPLACEMENT_KEY, replacement);
		definitions.add(filter);

		return definitions;
	}

至此,filter设置完成。

我们可以通过gateway暴露的endpoint来查看所有的路由信息,如下图:

​​

相关文章:

  • springCloud-gateway按照服务名动态路由的改造(三)
  • 前端传入数字,后端用枚举接收统一处理
  • Jackson2ObjectMapperBuilderCustomizer不生效解决
  • feign如何启用httpClient、OKhttp
  • easyExcel读数据后在写入另一个excel
  • nginx 丢失端口问题
  • centos 安装docker及docker-compose
  • 多个docker-compose文件共享一个网络
  • idea 远程调试springboot项目
  • spring-cloud-consul配置中心获取指定路径下的配置
  • 将red5项目部署到tomcat下,并且red5项目嵌入到J2EE项目中
  • mysql 本机登录错误提示及解决方法
  • 禅道程序员的10条原则
  • 完全删除MYSQL:windows 7下完全删除MYSQL,windows xp 下完全删除MYSQL
  • 小故事大人生 -----七个顶级心理寓言
  • Angular数据绑定机制
  • CentOS6 编译安装 redis-3.2.3
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • django开发-定时任务的使用
  • es6(二):字符串的扩展
  • extjs4学习之配置
  • HTML5新特性总结
  • HTTP那些事
  • input的行数自动增减
  • Java,console输出实时的转向GUI textbox
  • js正则,这点儿就够用了
  • k8s如何管理Pod
  • npx命令介绍
  • seaborn 安装成功 + ImportError: DLL load failed: 找不到指定的模块 问题解决
  • vue:响应原理
  • vue数据传递--我有特殊的实现技巧
  • WebSocket使用
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 前端
  • 微信小程序开发问题汇总
  • 怎么将电脑中的声音录制成WAV格式
  • raise 与 raise ... from 的区别
  • ​Distil-Whisper:比Whisper快6倍,体积小50%的语音识别模型
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • # centos7下FFmpeg环境部署记录
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • # 透过事物看本质的能力怎么培养?
  • $(selector).each()和$.each()的区别
  • (173)FPGA约束:单周期时序分析或默认时序分析
  • (31)对象的克隆
  • (C++17) std算法之执行策略 execution
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第2节(共同的基类)
  • (二)fiber的基本认识
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (十八)devops持续集成开发——使用docker安装部署jenkins流水线服务
  • (四)TensorRT | 基于 GPU 端的 Python 推理
  • (四)汇编语言——简单程序
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (转)大型网站架构演变和知识体系
  • ***利用Ms05002溢出找“肉鸡