乐趣区

关于java:这一篇对项目的优化实战能让你面试加分30

但如果你想理解 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 等。

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

退出移动版