Две схемы и реальный бой для достижения высокой доступности

задняя часть сервер GitHub ZooKeeper балансировки нагрузки
Две схемы и реальный бой для достижения высокой доступности

Я был в статье раньшеИспользование Nginx+Redis для достижения сбалансированной нагрузки при совместном использовании сеансов.Эксперимент по балансировке нагрузки был проведен в , и его основная архитектура выглядит следующим образом:

Пучокdebian1в видедиспетчерский сервервзять на себя задачу запроса распространения, то есть пользователь получает доступ кdebian1,Потомdebain1сделать запрос в соответствии с определеннымСтратегияОтправитьСервер приложений: debian2 или debain3, и даже большедебен4, 5, 6...

государствоиданныеможно разместить снаружиСлужба распределенного кэшаиСлужба распределенной базы данных, такслужба приложенийОн сам по себе не имеет состояния, поэтому легко увеличить или уменьшить машину, и гарантируется высокая доступность приложения (не только обращайте внимание на высокую доступность с сохранением состояния)Машинное увеличение и уменьшение и переключение, также обратите внимание наРезервное копирование,согласованность данныхИ другие вопросы). Но в то время упускали из виду одну вещь, т.сервер расписания debian1Сама по себе высокая доступность не рассматривается, и есть одна проблемная точка.

Первая идея высокой доступностиГорячее резервирование двух систем, автоматическое переключение в случае сбоя, поэтому мы должны датьdebian1добавить резервную копиюdebain1'. Теперь я делю решения на две категории в соответствии с моими собственными знаниями:Клиенты оценили высокую доступность,Высокая доступность, прозрачная для клиентови выберите пример для проведения эксперимента.

Примечание. В следующих режимах высокой доступности используется горячее резервное копирование двух систем.сервер расписания debian1Упоминается какхозяин,ПучокРезервная машина debian1' сервера планирования debian1Упоминается какРезервная машина.

Клиенты оценили высокую доступность

Клиент воспринимает высокую доступность, то есть ему требуется сотрудничество клиента, а клиент подтверждает смену сервера и переключает цель доступа. Например, наш хост и резерв зарегистрированы в ZooKeeper (или других подобных центрах регистрации, таких как redis), клиент отслеживает информацию сервера в ZooKeeper и переключается на резерв, когда хост находится в автономном режиме.

Построение псевдокластера ZooKeeper

Сначала создайте систему из 3 узлов на локальной машине.Псевдокластер ZooKeeper. существуетОфициальный сайтСкачать версию3.5.4-beta, распаковать, а затем сделать три копии, каждую из которых нужно сделать следующим образом:

  • Перейдите в папку 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
    
  • создать вышеуказанноеdataDirиdataLogDir, И вdataDirкаталог должен быть созданmyidфайл, напишите другой целочисленный идентификатор, который указан вышеserver.x的x, например 1

  • Введите каталог bin соответственно вzkServer.cmdсерединаcallприсоединился раньшеset ZOOCFG=../conf/zoo.cfgи использовать его для начала.

Кстати, для разработки кода я использую свой предыдущий проектCHKV, потому что в этом проектеNameNodeилиDataNodeтакже можно использоватьZooKeeperЧтобы добиться высокой доступности, добро пожаловать, чтобы улучшить этот проект вместе со мной и добиться прогресса вместе.

Планирование разработки сервера

диспетчерский серверВ основном регистрируйтесь в 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);
}

// 确认master
private static boolean confirm(CuratorFramework curator) throws Exception {
    masterNode = new String(curator.getData().forPath(MASTER_NODE_PATH));
    logger.info("masterNode: {}",masterNode);
    return thisNode.equals(masterNode);

}

// 成为master
private 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);
}

Полный код находится наGitHubначальство.

Развитие клиента

клиентВ основном слушаю 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);

}

Полный код находится наGitHubначальство.

Высокая доступность, прозрачная для клиентов

Высокая доступность прозрачна для клиента, то есть клиенту не нужно выполнять какую-либо работу, и клиент не знает и не заботится о том, переключен ли сервер или нет. Существует два основных метода реализации, один из них — клиентский.Доступ через доменное имяхост, то после того, как хост мониторинга отключится, доменное имя будет переназначено на резервный хост.Конечно, это переключение будет стоить времени, в зависимости от заданного времени DNS-кэша, второй - клиентДоступ через IPХост, после контроля того, что хост находится в автономном режиме, он пройдетдрейф IPЭта технология назначает внешний IP-адрес (или виртуальный IP-адрес) резервной машине, чтобы обеспечить своевременное переключение.

Часто используется в реальных условияхkeepalivedдля достижения дрейфа IP.

Обратитесь к процессу сборкиThe keepalived solution for LVSиОфициальная документация сайта

Сначала нужно установить 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, аналогично хосту, но состояние резервное и вес меньше:

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
    }
}

отражение

Грубо говоря, первая из этих двух реализаций высокой доступности находится в стадии разработки.прикладной уровеньреализуется, в то время как последнийтранспортный уровеньЕсли это реализовано, то мы можем думать, что каждый уровень компьютерной сети действительно может выполнять балансировку нагрузки и высокую доступность.

Проверятьоригинальный, отMageekChiu