spring-cloud Hoxton.SR11 中的spring-cloud-consul-config 模块有些小坑

前言:

我们商城中用的spring-cloud版本是 1.3.1.RELEASE,spring-boot 1.5.17.RELEASE 版本,这个版本里有很多问题,具体不多说了,

举个印象深刻的例子吧,压测环境下调用redis发现性能下降的明显,后来翻阅了下源代码发现

if (dbIndex > 0) {
			try {
				select(dbIndex);
			} catch (DataAccessException ex) {
				close();
				throw ex;
			}
		}

这段代码在 spring-data-redis-1.8.6.RELEASE.jar 中 于是升级到spring-data-redis-1.8.22.RELEASE.jar 解决该问题

以上这种问题的坑还有很多,spring cloud和boot的代码质量真的很一般,当然只要是人写的代码都会有bug这个不否认(抱歉 我有代码洁癖)

后续:

正好公司也在做资产项目,于是找了个晚上时间搭建了一套spring cloud Hoxton.SR11 版 ,也为这边项目以后升级做个准备。这个版本的注册中心我看用consul很不错,

项目结构如下:

扫描二维码关注公众号,回复: 14383067 查看本文章

初次使用consul 了解了下 简单便捷 对 服务和配置中心提供了很好的支持,还是分开的。

之前dawdler的注册中心都是用的zookeeper,必定个人也觉得cp用来做注册中心有些浪费,老版本的spring-cloud注册中心用的是eureka(eureka还是很不错的 1x已经成熟了 不再维护了 所以不打算集成eureka),于是想深入下consul。

网上google看了下资料 同时去官方也看了下文档,文档里有一句话:Values in the KV store cannot be larger than 512kb.

这个是需要注意的不要超过512kb,单个value的值。

github上找了下java的client包,分别为 consul-api 和 consul-client 

本人大概对比了下两个组件 consul-api比较轻量级,consul-client是基于okhttp(4x kotlin)封装的比较人性化的一套组建。

继续动手,dawdler中缺少统一配置中心模块,之前想考虑用携程的apollo 我们项目中在用这个,用户体验也不错,就是安装有点麻烦,另外client端搞了一堆jar包有些重。

于是考虑用consul试水,那么问题来了,配置中心要能够动态刷新bean属性,实现了一套刷新bean属性的框架(未来会提交到dawdler中,这个版本包含了mybatis 、pinpoint插件等等功能),接下来看主要代码

基于consul-client实现:


		Consul client = Consul.builder().withHostAndPort(HostAndPort.fromParts("192.168.199.128", 8500)).withReadTimeoutMillis(15000).build(); 
		final KeyValueClient kvClient = client.keyValueClient();
		KVCache cache = KVCache.newCache(kvClient, "/");
		cache.addListener(newValues -> {
			newValues.forEach((k,v)->{
				ConfigData data = ConfigDataCache.getConfigData(k);
				if(data == null || data.getVersion() != v.getModifyIndex()) {
					ConfigMappingDataCache.refresh(k);
					ConfigDataCache.addConfigData(k, v.getValueAsString().get(), v.getModifyIndex());
					Map<Object,Set<Field>> fieldsMap = PathMappingTargetCache.getPathMappingTargetMaps(k);
					if(fieldsMap != null) {
						fieldsMap.forEach((fk,fv)->{
							fv.forEach(field->{
								try {
									Refresher.injectConfig(fk, field, field.getAnnotation(MyConf.class));
								} catch (IllegalArgumentException | IllegalAccessException | IOException e) {
									e.printStackTrace();
								}
							});
						});
					}
				}
			});
		});
		cache.start();
		cache.awaitInitialized(10000, TimeUnit.MILLISECONDS);
	

看是很完美,consul提供了 wait参数请求是这样的:http://192.168.199.128:8500/v1/kv/?wait=15s&recurse=true&index=1878

这个url的含义是获取所有的kv,同时如果本地版本和consul的index不同 那么直接返回,如果相同就会阻塞 wait(时间单位为秒)

可我发现了个问题,无论怎么样都会返回所有的 内容(key和value),这样就面临一个问题,如果像我们现在这种大型电商平台 拆分了几十个服务的,每个里面的配置都很大,无论变化还是没变化都返回这些信息这样是很消耗带宽的,如果非要用这种方式 那我建议做好 path的层及规划 按服务来配置path ,

举个例子: 订单服务 KVCache.newCache(kvClient, "/order"); 这样只有订单的节点获取所有关于订单的配置

而商品服务可以 采用  KVCache.newCache(kvClient, "/goods");

但这并不完美 还是有额外的消耗,于是脑洞大开 看看spring-cloud 是怎么实现的?

直接上源码:org.springframework.cloud.consul.config.ConfigWatch 中的 watchConfigKeyValues方法

以下是核心代码

for (String context : this.consulIndexes.keySet()) {

			// turn the context into a Consul folder path (unless our config format
			// are FILES)
			if (this.properties.getFormat() != FILES && !context.endsWith("/")) {
				context = context + "/";
			}

			try {
				Long currentIndex = this.consulIndexes.get(context);
				if (currentIndex == null) {
					currentIndex = -1L;
				}

				if (log.isTraceEnabled()) {
					log.trace("watching consul for context '" + context + "' with index " + currentIndex);
				}

				// use the consul ACL token if found
				String aclToken = this.properties.getAclToken();
				if (StringUtils.isEmpty(aclToken)) {
					aclToken = null;
				}

				Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken,
						new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
//注意 下面代码是我优化后的,需要注释上面的代码 放开下面的代码即可
//				Response<List<String>> response = this.consul.getKVKeysOnly(context, this.properties.getProfileSeparator(), aclToken
//						,new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));
				// if response.value == null, response was a 404, otherwise it was a
				// 200, reducing churn if there wasn't anything
				if (response.getValue() != null && !response.getValue().isEmpty()) {
					Long newIndex = response.getConsulIndex();

					if (newIndex != null && !newIndex.equals(currentIndex)) {
						// don't publish the same index again, don't publish the first
						// time (-1) so index can be primed
						if (!this.consulIndexes.containsValue(newIndex) && !currentIndex.equals(-1L)) {
							if (log.isTraceEnabled()) {
								log.trace("Context " + context + " has new index " + newIndex);
							}
							RefreshEventData data = new RefreshEventData(context, currentIndex, newIndex);
							this.publisher.publishEvent(new RefreshEvent(this, data, data.toString()));
						}
						else if (log.isTraceEnabled()) {
							log.trace("Event for index already published for context " + context);
						}
						this.consulIndexes.put(context, newIndex);
					}
..............................................................

好家伙,spring-cloud也是获取了所有信息,看这行代码:

Response<List<GetValue>> response = this.consul.getKVValues(context, aclToken,
                        new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));

spring-cloud用的是consul-api 来实现的,至于下面我优化的代码:

Response<List<String>> response = this.consul.getKVKeysOnly(context, this.properties.getProfileSeparator(), aclToken, new QueryParams(this.properties.getWatch().getWaitTime(), currentIndex));

直接返回keys即可,不需要取value 如果有数据有变动再去拉取key,这个已经给spring-cloud提了个pr了,不知道能否通过.

发现的这个问题还是挺麻烦的,特别是服务多的情况下并发拉取配置文件还是很耗内部带宽的,以上纯属个人愚见,如有意见欢迎指出(确实对spring不熟 求轻点喷  只是个框架 只是个框架 只是个框架  咱不是搞学术的... 我们是码农,现在评论被关闭了 发文章也需要审核了 .... 估计看不到被人喷了)。

btw: 开发搞了这么久了,很少用spring来做项目,必定启动慢,spring用的少,做过一套一元购商城(设计完,搭建好框架,写了些开奖和其他游戏的核心代码)再就是现在的电商平台用的spring-cloud了,之前都是团队人开发来用,我基本也不写业务代码,遇到的bug也不愿多说了,写到这突然想起去一家做法院系统的公司面试,问我为什么要自己写框架,为什么不用spring,我也是不好说什么,随时说了句spring mvc的 antpath匹配有一次是多余的,那4个面试官都是做技术的 有一个看了看我 嘲讽了下 伸出手指比划着 no....  我当时也是年轻没忍住就喷了他句 你看过源码么?他回答说 没有,我就说他认知不够。现在想想好无聊...一个框架 嘲讽就嘲讽吧...  又何必较真....

{{o.name}}
{{m.name}}

猜你喜欢

转载自my.oschina.net/u/3861980/blog/5080608
今日推荐