博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
实现高可用的两种方案与实战
阅读量:5931 次
发布时间:2019-06-19

本文共 6410 字,大约阅读时间需要 21 分钟。

我之前在一片文章 中做了一个负载均衡的实验,其主要架构如下:

架构

debian1作为调度服务器承担请求分发的任务,即用户访问的是debian1,然后debain1把请求按照一定的策略发送给应用服务器:debian2或者debain3,甚至更多的debain4、5、6......

状态数据可以放在外部的分布式缓存服务分布式数据库服务中,这样应用服务本身就是无状态的,所以机器增减都是很容易的,应用的高可用是有保证的(对于有状态的高可用不仅要注意机器增减与切换、还要注意备份冗余数据一致性等问题)。但是当时忽略了一个地方,那就是调度服务器debian1本身的高可用性没有考虑到,存在单点问题。

高可用的首要想法就是双机热备,故障时自动切换,所以我们要给debian1加一个备机debain1'。我现在按照自己的知识粗浅的把解决方案分为两类:客户端有感知的高可用对客户端透明的高可用,并分别挑选一个示例做一下实验。

注:下面实现高可用都用的是双机热备,为了方便,把调度服务器debian1简称为主机,把调度服务器debian1的备机debian1'简称为备机

客户端有感知的高可用

客户端有感知的高可用,也就是需要客户端的配合,客户端自己去确认服务器的变更并切换访问的目标。比如说我们的主机、备机都在ZooKeeper(或者其他类似的注册中心比如redis)中进行注册,客户端监听ZooKeeper中服务器的信息,发现主机下线自己就切换访问备机即可。

ZooKeeper伪集群搭建

首先在本机搭建包含3个节点的ZooKeeper伪集群。在下载版本3.5.4-beta,解压,然后复制3份,每一份都要做如下操作:

  • 进入conf文件夹 创建一个配置文件zoo.cfg。代码如下:
initLimit=10    syncLimit=5    clientPort=2181(每个节点不同:2181,3181,4181)    tickTime=2000    dataDir=E:/zookeeper-3.5.4-1/data(每个节点不同:3.5.4-2,3.5.4-3)    dataLogDir=E:/zookeeper-3.5.4-1/datalog(每个节点不同,同上)    server.1=192.168.*.*::2888:3888(实验机器的局域网IP或者直接localhost)    server.2=192.168.*.*::4888:5888    server.3=192.168.*.*::6888:7888
  • 创建上面的dataDirdataLogDir,并在dataDir目录下必须创建myid文件,写入不同的整数ID,也就是上面的server.x的x,比如1
  • 分别进入bin目录,在zkServer.cmdcall之前加入set ZOOCFG=../conf/zoo.cfg 并用其启动。

顺带一提,代码开发我就使用我之前的项目了,因为这个项目中的NameNode或者DataNode也可以用ZooKeeper实现高可用,欢迎和我一起完善这个项目,一块进步。

调度服务端开发

调度服务器主要向ZooKeeper注册自己,并向客户端提供服务。我们使用来和ZooKeeper交互,特别要注意版本问题。

主要代码如下:

public static void main(String... arg) throws Exception {    thisNode = ManagementFactory.getRuntimeMXBean().getName();    logger.debug("my pid: {}",thisNode);    // 构造连接    CuratorFramework curator = CuratorFrameworkFactory            .builder()            .connectString(CONNECT_ADDR)            .connectionTimeoutMs(CONNECTION_TIMEOUT)//连接创建超时时间            .sessionTimeoutMs(SESSION_TIMEOUT)//会话超时时间            .retryPolicy(policy)            .build();    curator.start();    // 创建节点也就是成为master,阻塞等待    boolean result = becomeMaster(curator);    if (result){        logger.info("Successfully Became Master");    }else {        logger.info("Failed to Became Master");    }    // 监听    NodeCache cache = new NodeCache(curator, MASTER_NODE_PATH,false);    cache.getListenable().addListener(()->{        ChildData data = cache.getCurrentData();        if (data != null){            String path = data.getPath();            Stat stat = data.getStat();            String dataString = new String(data.getData());            logger.debug("masterNode info, path:{},data:{},stat,{}",path,dataString,stat);        }else {            logger.info("masterNode is down, try to become Master");            if (becomeMaster(curator)){                logger.info("Successfully tried to Became Master");            }else {                logger.info("Failed to try to Became Master");            }        }    });    cache.start(true);}// 确认masterprivate static boolean confirm(CuratorFramework curator) throws Exception {    masterNode = new String(curator.getData().forPath(MASTER_NODE_PATH));    logger.info("masterNode: {}",masterNode);    return thisNode.equals(masterNode);}// 成为masterprivate static boolean becomeMaster(CuratorFramework curator) throws Exception {    String path= "";    try {         path =  curator.create()                .creatingParentContainersIfNeeded()                .withMode(CreateMode.EPHEMERAL)                .forPath(MASTER_NODE_PATH,thisNode.getBytes());        logger.debug(path);    }catch (Exception e){        logger.error(e.getMessage());    }    return MASTER_NODE_PATH.equals(path);}

完整代码在上。

客户端开发

客户端主要向ZooKeeper监听调度服务器变更事件,并向其发起应用请求。实际上应用服务器也可以使用这部分代码来监听调度服务器的变化。

主要代码如下:

public static void main(String... arg) throws Exception {    CuratorFramework curator = CuratorFrameworkFactory            .builder()            .connectString(CONNECT_ADDR)            .connectionTimeoutMs(CONNECTION_TIMEOUT)            .sessionTimeoutMs(SESSION_TIMEOUT)            .retryPolicy(policy)            .build();    curator.start();     NodeCache cache = new NodeCache(curator, MASTER_NODE_PATH,false);    cache.getListenable().addListener(()->{        ChildData data = cache.getCurrentData();        if (data != null){            String path = data.getPath();            Stat stat = data.getStat();            String dataString = new String(data.getData());            logger.debug("masterNode info, path:{},data:{},stat,{}",path,dataString,stat);            masterInfo = dataString;        }else {            logger.info("masterNode is down, waiting");        }    });    cache.start(true);    // 获得主机,阻塞等待      try {        masterInfo =  new String(curator.getData().forPath(MASTER_NODE_PATH));    }catch (Exception e){        logger.error("no masterInfo");        masterInfo = null;    }    while (masterInfo==null);    logger.info("masterInfo:{}",masterInfo);}

完整代码在上。

对客户端透明的高可用

对客户端透明的高可用,也就是客户端不需要做什么工作,服务器切换不切换客户端根本不知道也不关心。主要实现方式有两种,一种是客户端通过域名访问主机,那么监控主机下线后就把域名重新分配给备机,当然这个切换会有时间成本,视定义的DNS缓存时间而定;第二种就是客户端通过IP访问主机,监控到主机下线后就通过IP漂移技术把对外的IP(或者说虚拟IP)分配给备机,这样就能做到及时的切换。

实际环境中常常使用keepalived来实现IP漂移。

搭建过程参考了和

首先主机、备机都要安装keepalived,然后配置主机/etc/keepalived/keepalived.conf

vrrp_instance VI_1 {    state MASTER       # MASTER表示此实例是主机,BACKUP表示此实例是备机    interface eth0     # 网卡名称,亦即网络接口    virtual_router_id 51    priority 100    advert_int 1       # 心跳检查时间间隔,单位秒    authentication {   # 认证方式 是 密码的方式        auth_type PASS        auth_pass 1111    }    virtual_ipaddress {# 虚拟IP地址,也就是对外开放的IP        10.23.8.80    }}    virtual_server 10.23.8.80 80 {    # 虚拟服务器,也就是对外开放的IP与端口    delay_loop 6    lb_algo wlc                   # 负载均衡调度算法 此处是 加权最少连接    lb_kind NAT                   # 有 DR,NAT,TUN三种         persistence_timeout 600    protocol TCP    real_server 172.18.1.11 80 {# 后端的 应用服务器        weight 100              # 节点的权重        TCP_CHECK {            connect_timeout 3   # 3秒超时        }    }    real_server 172.18.1.12 80 {# 后端的 应用服务器        weight 100        TCP_CHECK {            connect_timeout 3        }    }    real_server 172.18.1.13 80 {# 后端的 应用服务器        weight 100        TCP_CHECK {            connect_timeout 3        }    }}

配置备机/etc/keepalived/keepalived.conf,与主机类似,但是state是backup,且权重较低即可:

vrrp_instance VI_1 {    state BACKUP    interface eth1    virtual_router_id 51    priority 90    advert_int 1    authentication {         auth_type PASS        auth_pass 1111    }    virtual_ipaddress {        10.23.8.80    }}

反思

说白了,这两种高可用的实现方式前者是在应用层实现的,而后者是在传输层实现的,那么我们就可以想到,计算机网络的每一层其实都是可以做负载均衡和高可用的。

查看,来自

转载地址:http://fsutx.baihongyu.com/

你可能感兴趣的文章
【faster-rcnn】训练自己的数据——修改图片格式、类别
查看>>
C#:额外知识点
查看>>
防止表单重复提交
查看>>
【iCore3应用开发平台】发布 iCore3 应用开发平台出厂代码rev0.0.6
查看>>
leetcode - First Missing Positive
查看>>
CentOS 7.0系统安装配置步骤详解
查看>>
深入学习semaphore
查看>>
eth0 eth0:1 eth0.1 的区别
查看>>
Code a simple telnet client using sockets in python
查看>>
海归人才网
查看>>
xdmcp配置_百度百科
查看>>
Android应用开发基础篇(11)-----ViewFlipper
查看>>
NEWS - AdminStudio 11.5发布
查看>>
博客园的“随笔、文章、新闻、日记”有啥区别
查看>>
ASP.NET温故而知新学习系列之深度剖析ASP.NET架构—ASP.NET请求的处理过程(一)...
查看>>
cocos2d-x的初步学习二十一之iosandroid跨平台环境配置
查看>>
Python学习入门基础教程(learning Python)--5.1 Python下文件处理基本过程
查看>>
Eclipse 的一些调试技巧(转)
查看>>
OpenGL 阴影之Shadow Mapping和Shadow Volumes
查看>>
Mysql初始化root密码和允许远程访问
查看>>