但如果你想理解Java大数据平台开发、我的项目零碎的优化实战。请持续向下浏览。

我的项目背景

该我的项目是银行自用我的项目,是多租户的数据查问平台。可能很多人对这个概念不是很分明,别急,容我做个简略的介绍,就明确这个零碎是干嘛的了。

我的项目简介

首先,整个零碎是基于Dubbo的分布式系统架构,数据存储对立存储在数据仓库。数据仓库提供多种存储形式,包含MySQL、HDFS、HBSE、Hive、Impala、Spark、ElasticSearch等等。而如果让业务方去做数据存取操作,显然是十分麻烦的。所以在业务零碎与数据仓库之间再搭建了一个数据查问零碎——这就是本篇文章的配角。

零碎架构

我的项目源码

这里会给大家展现我的项目的局部源码,当然,所展现的源码都是功能性的而非我的项目业务相干(即任何我的项目都能够有这些代码),大家能够先找找茬。


通过4张图,大家应该对该零碎之前的编码程度有了大抵的理解。上面我将一一解锁每张源码图的故事。

源码1:

源码1是我在做性能调试的时候发现的一个BUG,逻辑非常简单,就是比对两个id是否相等。但为什么这就产生BUG了呢?
很简略,就是包装类的缓存!
Integer和Long类型会有1个byte的缓存,即 -128 ~ 127,当比拟数的返回在此之间时,因为都是应用的缓存。验证代码如下:

package demo;public class IntegerCacheDemo {    public static void main(String[] args) {        compare(1,1);        compare(127,127);        compare(128,128);        compareWithEquals(1,1);        compareWithEquals(127,127);        compareWithEquals(128,128);    }    /**     * 谬误的包装类比拟     * @param a     * @param b     */    public static void compare(Integer a, Integer b){        System.out.println(a == b ? a + " == " + b:a + " != " + b);    }    /**     * 正确的的包装类比拟     * @param a     * @param b     */    public static void compareWithEquals(Integer a, Integer b){        System.out.println(a.equals(b) ? a + " == " + b:a + " != " + b);    }}

测试后果:

测试的后果印证了后面的说法。

家喻户晓, == 比拟是间接比拟的地址,而因为缓存的起因,包装类缓存所指向的都是同一个对象,所有 == 判断返回true,而当超出了缓存的返回,包装类的对象都是新创建的地址,应用 == 判断会返回false,而equals判断应用的是重写的equals办法,Integer的equals办法如下:

    public boolean equals(Object obj) {        //判断类型是否雷同        if (obj instanceof Integer) {            //如果雷同则判断值是否雷同,this.value存储的是int类型值, == 与               //Integer比拟,会触发主动拆箱, 即等价于  int == int 判断            return this.value == (Integer)obj;        } else {            return false;        }    }

再来看IntegerCache的源码吧。

留神看全红局部,置信大家都明确了吧。 其余的包装类如Long、Short、Byte等都有对应的缓存,而且都是一个byte的取值范畴。

源码2:

请留神,源码2是在上线第二天就引起了线上事变。

  1. 业务形容

业务方通过查问接口调用查问平台,查问平台通过Zookepper拜访到Hbase获取数据并返回。

  1. 问题排查

通过谬误日志,能够查到过后有很多申请查问失败,并且偶然会有一个查问胜利,且失败数量是成线性增长的趋势。过后我就依据教训判断是连贯出了问题。
果然,通过查看zookeeper日志,发现的确报连接数超过最大限度,但业务方反馈业务才上线,应用人数也就10来人。那么能够判断,代码存在BUG。

  1. 问题解决

首先,批改代码上线是须要通过一个流程的,不适宜短时间解决。 而咱们zookepper的最大连接数配置的是100,咱们先将最大连接数调整到600,而后查找代码BUG修复。

通过走查代码,发现代码中有一个十分低级且致命的低级谬误(大家有没有发现呢?),就是图2的try-cache中的代码,调用了createConnection(conf)办法两次,其中一个连贯返回给调用者,而另外一个连贯创立后则没有返回。返回的连贯会在应用后正确敞开,而没有返回的连贯因为永远不可能会有调用者,也就不可能手动开释,而只能期待超时主动开释,超时工夫在代码中也看到了-30000ms。这就解释了为什么并发不高的状况下,连贯首先挂掉了。去掉此段代码即可。

4.优化降级

请留神看上一段加粗的文字,我为什么加粗呢? 必定是另有乾坤啊,哈哈哈哈~~~!
图2中有3个代码片段, 能够看到,整个操作流程是 : 获取配置 -> 创立Util对象 -> 创立连贯 -> 查问 -> 敞开连贯。

OMG!OMG!OMG! 不得不惊叹在21世纪的20年代, 竟然还能看到这样的代码。OK,两个问题,其一,整个流程少了个连接池吧? 其二,util对象竟然是要new进去应用。 不应用连接池的弊病无需多说,太浪费资源了。咱们能够看看在单机并发下TCP连接数。

看到那个顶上去的尖了吗。 并发也不是特地高, 20线程 * 200 次循环。 能够设想,如果在生产环境,并发量如果略微下来一点,这机器是最先扛不住的。

ok,持续整。优化思路:1、配置和工具类拆散,创立配置对象,而不创立工具对象 2、 应用连接池治理连贯,这一点比拟好办,Hbase的Java客户端提供了连接池。通过优化,TCP连贯根本比较稳定,优化代码我这里就不贴了,代码还是不少,重要的是思路而不是代码。

源码3 + 源码4:

源码3比较简单,就是最根本的JDBC获取连贯操作,同样的问题,整个操作都是创立连贯、查问、敞开连贯,而没有应用到连接池。但这一点和Hbase操作又有所不同,Hbase的数据源在零碎中只有一个,而JDBC的数据源就十分多了,包含MySQL、Hive、Impala都是应用JDBC来连贯的,而每一个数据库就是一个数据源。这样咱们零碎中就会有十分多的数据源,而不是繁多的数据资源管理。

而源码4我认为是比拟好的代码,通过令牌池的机制限度了单台服务器的最大数据库连贯数量,这种思维在高并发中也能够应用。 绝对于限流的一种机制,他最大限度的保障了服务器的稳定性,不像源码2那样间接导致服务不可用。

这里的优化思路就是在一个数据源对应一个连接池,而多个数据源则对应多个连接池,而后对多个连接池进行缓存,当申请拜访的时候,首先依据申请查找到对应的连接池,而后再从连接池获取一个连贯返回。这样就解决了频繁创立连贯的问题。这种形式临时晋升了零碎的并发度,但这种形式对服务器的本地资源占用比拟多,还有其余的解决方案,比方开源的中间件MyCat等。

如果你看到了这里,证实你太有急躁了 哈哈~!!