elastic-job分布式定时器实践

项目的定时任务比较多。主管想要做成分布式定时器。说起来,我们要用springcloud+docker做分布式集群服务,又要做分布式分片数据库,用spring-session和redis做分布式缓存,rocketmq做消息中间件,几乎所有环节都做成分布式系统了。虽然我不觉得这个项目有这么大的业务需求,但是反正我是学到很多东西了。我们的主管挺奇怪的,他说他都没用过这些东西,但是他十分乐于用这些新东西,平时总是关注行业的最新动态,这种战略思维也是我需要学习的。而现在他要用什么我就学什么就好了,也算是给我指明了方向。

主管给我的方案是elastic-job+zookeeper,zookeeper我之前了解分布式服务器的时候了解过(主要为了面试),其实就是一个服务器集群地址的管理软件。elastic-job底层还是使用的quartz,相当于用zookeeper实现的一个quartz分布式方案。那么第一步,肯定是学习zookeeper了。我会把参考的文章链接都贴出来。

一篇很详细的文章,看的很舒心,比我好几百倍:ZooKeeper配置和学习笔记

因为文章很详细,我就不复制粘贴了,就简单地说一下总结和一些感悟。我发现像zookeeper或者rockmq这种集群方案都有类似主从的关系来保持高可用性,确立主从关系以后,所有分节点数据能够跟主节点同步,如果分节点失效,那么没关系,还有其他分节点,如果主节点失效,这些方案都会有方式选出新的节点取而代之。因此除非所有节点失效,不管是新增节点还是减少节点,对外界来说都是工作的。保持了高度的灵活性,扩展性和可用性,这也是分布式系统的优势吧,缺点就是需要额外的花费来同步数据,处理并发以及执行顺序的问题,以及需要抽象出一层对集群进行管理的体系。感觉跟现代人类的组织体系有异曲同工之妙呢,不管缺少哪个环节,都有可用的人顶上去。

不说废话了,说些紧要的东西,zookeeper有三个端口号,一个是工作端口号,一个是主从zookeeper服务器之间通信的端口号(也用来同步数据),以及一个专门用来选举的端口号(当然并不是民主的选举,而是程序员钦定的),后两个端口号在布置zookeeper集群时有用。

想要使用什么工具,最基础和最重要的部分就是环境和配置了,之后调用api什么的都已经设置好了,照着用就行。

zookeeper配置文件是根目录下的conf文件中的zoo.cfg文件,配置项如下

tickTime=2000 通讯的心跳时间
initLimit=10 服务器集群间能容忍的心跳间隔数(多少心跳时间没有消息就选新领导,这说明保持联系是多么重要,多少反贼都是这么造反的)
syncLimit=5 同步的心跳间隔数,主从节点需要保持同步
dataDir=/tmp/zookeeper 保存数据的目录,这是基本的,什么程序都要保存数据的地方,像鱼一样七秒记忆不存在的

clientPort=2181 工作端口

server.1=192.168.211.1:2888:3888 下面是复制粘贴的

server.A=B:C:D:其中 A 是一个数字,表示这个是第几号服务器;B 是这个服务器的 ip 地址;C 表示的是这个服务器与集群中的 Leader 服务器交换信息的端口(上面的端口Y);D 表示的是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的 Leader,而这个端口就是用来执行选举时服务器相互通信的端口(上面的端口Z)。如果是伪集群的配置方式,由于 B 都是一样,所以不同的 Zookeeper 实例通信端口号不能一样,所以要给它们分配不同的端口号。 
* 在上面配置的dataDir目录下创建myid文件,填写这个节点上的id号,就是server.A=B:C:D配置的A那个号码 

其实配置项很少了,然后启动zookeeper,启动俺就不说了。

然后我最看重的是可视化工具,虽然用命令行看着很帅,很有高手的感觉,但是效率太低,不够直观,像redis我一定要用RedisDesktopManager,不然根本不用(其实是懒)

找了个可以用的,效果如下


不错,使用难度傻瓜级别,不用配置数据库,部署tomcat什么的(吐槽某款本地网页版可视化工具),而且这界面一看就很喜欢,没这么多花里胡哨的。

关于zookeeper的使用,其实zookeeper本质上还是一个数据的媒介,只不过它保存了每个客户端服务器的信息,从这一点上讲,互联网其实就是数据的存储和通信

然后是elastic-job,关于这个我主要参考两个文章

elastic-job详解(一):数据分片

elastic-job+zookeeper实现分布式定时任务调度的使用(springboot版本)

上面这个文章非常详细地说明了elastic-job的原理,还是系列文章,下面的就只是一个demo了。

elastic-job分片的原理就是每当有新的Job实例加入zookeeper中就会进行分片算法


leader是zookeeper中的领导,servers是zookeeper集群中的服务器,现在只有一个,instances是job实例,现在只有一个。关键是sharding分片,比如我在springboot中设置了分片为10,zookeeper会自动为这十个分片分配实例,就是我选中的instance。169.254的ip我查了下,好像是保留ip,就是说并没有查到ip,难道是因为我本地测试的原因。总之单定时器是可以正常运行的。下面我试试单台机器多个端口的定时器。


可以看到instances里多了一个10436,并且5,6,7,8,9分片被分配到了这个新的10436。



可以看到任务是可以正常进行的,注意看分片项。

那么能够正常分片以后就可以考虑分片算法了,结合我过去看过的mycat,和hashMap的原理,我想出了一个初步的方案

因为所有的定时器都是跟公司有关的,所以要按照公司id进行分片

select CompanyId%30 cuter,count(1) from base_company group by cuter


发现分片并不均匀,然后换用select hex(CompanyId)%30 cuter,count(1) from base_company group by cuter


这个就好一点,总之初步方案决定用这个,但是这个研究分片的过程是最重要的,我以后可以随时改变分片的算法,甚至可以写个程序研究不同算法分片的方差之类的

好吧,根本没有用到hashMap的hash算法,因为mysql的方法我并不是很熟,而且mysql的方法可以进行二进制的运算吗?不清楚,不过思路就是这样。我还要研究一下,毕竟上面的方差也很大,分布不均匀

顺便说一下hashMap的hash算法

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

key.hashCode()是32位的int型。h>>>16相当于把前面16位移到了后十六位,然后前十六位用0补充。^是异或运算

运算规则:0^0=0;   0^1=1;   1^0=1;   1^1=0;

用自然语言说明就是当一方是0,那么结果等于另一方的值,当一方是1,那么结果等于另一方的否值

当int小于等于16位时,h>>>16就是0那么没有任何影响,当int大于16位时,h>>>16等于int的前十六位移到后十六位。所以int前十六位不变,但是后十六位会受到前十六位的影响

    public static String convert(String str, int length) {
        if (str.length() < length) {
            String temp = "";
            for (int i = 0; i < length - str.length(); i++) {
                temp += "0";
            }
            return temp + str;
        }
        return str;
    }
    public static String toBinaryString(int a,int length) {
    	return convert(Integer.toBinaryString(a), length);
    }
    public static void main(String[] args) {
    	int h = 1911111111;
    	System.out.println("         h:"+toBinaryString(h, 32));
    	System.out.println("    h>>>16:"+toBinaryString(h>>>16, 32));
	System.out.println("h^(h>>>16):"+toBinaryString(h^(h>>>16), 32));
    }


以上差不多就是初步的实践了


猜你喜欢

转载自blog.csdn.net/qq_36804701/article/details/80664792
今日推荐