Первоначально написано Фернандо Ипар и Мартин Арриета
Это третий пост в нашей серии MySQL Fabric. Если вы пропустили предыдущие два, мы начали с общего представления , а затем обсудили функции высокой доступности MySQL Fabric (HA) . MySQL Fabric был RC, когда мы начали эту серию, но недавно она стала GA. Вы можете прочитать пресс-релиз здесь , и увидеть эту запись в блоге от Oracle Mats Kindahl для более подробной информации. В нашем предыдущем посте мы показали простую настройку HA, управляемую с помощью MySQL Fabric, включая некоторые базовые сценарии сбоев. Сегодня мы представим похожий сценарий с точки зрения разработчика приложения, используя Python Connectorдля примеров. Если вы будете следовать примерам этих постов, вы заметите, что UUID для серверов будет меняться. Это потому, что мы перестраиваем среду между запусками. Символические названия остаются неизменными. Тем не менее, вот наша обычная настройка 3 узла:
[vagrant@store ~]$ mysqlfabric group lookup_servers mycluster Command : { success = True return = [{'status': 'SECONDARY', 'server_uuid': '3084fcf2-df86-11e3-b46c-0800274fb806', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.70.101'}, {'status': 'SECONDARY', 'server_uuid': '35cc3529-df86-11e3-b46c-0800274fb806', 'mode': 'READ_ONLY', 'weight': 1.0, 'address': '192.168.70.102'}, {'status': 'PRIMARY', 'server_uuid': '3d3f6cda-df86-11e3-
Для наших тестов мы будем использовать этот простой скрипт:
import mysql.connector from mysql.connector import fabric from mysql.connector import errors import time config = { 'fabric': { 'host': '192.168.70.100', 'port': 8080, 'username': 'admin', 'password': 'admin', 'report_errors': True }, 'user': 'fabric', 'password': 'f4bric', 'database': 'test', 'autocommit': 'true' } fcnx = None print "starting loop" while 1: if fcnx == None: print "connecting" fcnx = mysql.connector.connect(**config) fcnx.set_property(group='mycluster', mode=fabric.MODE_READWRITE) try: print "will run query" cur = fcnx.cursor() cur.execute("select id, sleep(0.2) from test.test limit 1") for (id) in cur: print id print "will sleep 1 second" time.sleep(1) except errors.DatabaseError: print "sleeping 1 second and reconnecting" time.sleep(1) del fcnx fcnx = mysql.connector.connect(**config) fcnx.set_property(group='mycluster', mode=fabric.MODE_READWRITE) fcnx.reset_cache() try: cur = fcnx.cursor() cur.execute("select 1") except errors.InterfaceError: fcnx = mysql.connector.connect(**config) fcnx.set_property(group='mycluster', mode=fabric.MODE_READWRITE) fcnx.reset_cache()
Этот простой скрипт запрашивает соединение MODE_READWRITE, а затем выдает выборки в цикле. Причина, по которой он запрашивает соединитель RW , заключается в том, что нам легче спровоцировать сбой, поскольку у нас есть два ВТОРИЧНЫХ узла, которые можно использовать для запросов, если мы запросим соединение MODE_READONLY . Выбор включает в себя короткий сон, чтобы было легче поймать его в SHOW PROCESSLIST . Для работы этому сценарию необходима таблица test.test, которая существует в группе mycluster . Выполнение следующих операторов в узле PRIMARY сделает это:
mysql> create database if not exists test; mysql> create table if not exists test.test (id int unsigned not null auto_increment primary key) engine = innodb; mysql> insert into test.test values (null);
Имея дело с неудачей
Когда все настроено, мы можем запустить скрипт, а затем вызвать ПЕРВИЧНЫЙ сбой. В этом случае мы смоделируем ошибку, закрыв на ней mysqld:
mysql> select @@hostname; +-------------+ | @@hostname | +-------------+ | node3.local | +-------------+ 1 row in set (0.00 sec) mysql> show processlist; +----+--------+--------------------+------+------------------+------+-----------------------------------------------------------------------+----------------------------------------------+ | Id | User | Host | db | Command | Time | State | Info | +----+--------+--------------------+------+------------------+------+-----------------------------------------------------------------------+----------------------------------------------+ | 5 | fabric | store:39929 | NULL | Sleep | 217 | | NULL | | 6 | fabric | node1:37999 | NULL | Binlog Dump GTID | 217 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL | | 7 | fabric | node2:49750 | NULL | Binlog Dump GTID | 216 | Master has sent all binlog to slave; waiting for binlog to be updated | NULL | | 16 | root | localhost | NULL | Query | 0 | init | show processlist | | 20 | fabric | 192.168.70.1:55889 | test | Query | 0 | User sleep | select id, sleep(0.2) from test.test limit 1 | +----+--------+--------------------+------+------------------+------+-----------------------------------------------------------------------+----------------------------------------------+ 5 rows in set (0.00 sec) [vagrant@node3 ~]$ sudo service mysqld stop Stopping mysqld: [ OK ]
Пока это происходит, вот вывод из скрипта:
will sleep 1 second will run query (1, 0) will sleep 1 second will run query (1, 0) will sleep 1 second will run query (1, 0) will sleep 1 second will run query sleeping 1 second and reconnecting will run query (1, 0) will sleep 1 second will run query (1, 0) will sleep 1 second will run query (1, 0)
«Спит 1 секунду и повторное подключение» означает строку сценарий получил исключение при выполнении запроса (когда ОСНОВНОЙ узел был остановлен, ждал одну секунды , а затем вновь. Следующие строки подтверждают , что все вернулось в нормальное состоянии после повторного соединения. Соответствующей кусок кода, который обрабатывает переподключение, это:
fcnx = mysql.connector.connect(**config) fcnx.set_property(group='mycluster', mode=fabric.MODE_READWRITE) fcnx.reset_cache()
Если fcnx.reset_cache () не вызывается, драйвер больше не будет подключаться к серверу xml-rpc, а вместо этого будет использовать свой локальный кэш статуса группы. Поскольку основной узел находится в автономном режиме, это приведет к неудачной попытке повторного подключения. Сброс кеша заставляет драйвер подключаться к серверу xml-rpc и получать актуальную информацию о статусе группы. Если происходит больше сбоев и в группе нет ПЕРВИЧНОГО (или кандидата на продвижение) узла, появляется следующая ошибка:
will run query (1, 0) will sleep 1 second will run query sleeping 1 second and reconnecting will run query Traceback (most recent call last): File "./reader_test.py", line 34, in cur = fcnx.cursor() File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 1062, in cursor self._connect() File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 1012, in _connect exc)) mysql.connector.errors.InterfaceError: Error getting connection: No MySQL server available for group 'mycluster'
Работает без MySQL Fabric
Как мы уже говорили в предыдущих статьях, сервер XML-PRC может стать единой точкой отказа при определенных обстоятельствах. В частности, существует не менее двух проблемных сценариев, когда этот сервер не работает:
- Когда узел выходит из строя
- Когда предпринимаются новые попытки подключения
Первый случай достаточно очевиден. Если MySQL Fabric не запущен и узел выходит из строя, никаких действий не предпринимается, и клиенты будут получать сообщение об ошибке всякий раз, когда отправляют запрос отказавшему узлу. Это хуже, если ПЕРВИЧНЫЙпроисходит сбой, так как отказоустойчивости не произойдет, и кластер будет недоступен для записи. Второй случай означает, что пока MySQL Fabric не работает, новые подключения к группе установить невозможно. Это связано с тем, что при подключении к группе клиенты, поддерживающие MySQL Fabric, сначала подключаются к серверу XML-RPC, чтобы получить список узлов и ролей, и только затем используют свой локальный кэш для принятия решений. Одним из способов смягчения этого является использование пула соединений, что снижает необходимость в создании новых соединений и, следовательно, сводит к минимуму вероятность сбоя из-за сбоя MySQL Fabric. Это, конечно, предполагает, что что-то контролирует MySQL Fabric, гарантируя, что некоторый хост предоставляет услугу XML-PRC. Если это не так, сбой будет отложен, но в любом случае это произойдет. Вот пример того, что происходит, когда MySQL Fabric не работает иПЕРВИЧНЫЙ узел выходит из строя:
Traceback (most recent call last): File "./reader_test.py", line 35, in cur.execute("select id, sleep(0.2) from test.test limit 1") File "/Library/Python/2.7/site-packages/mysql/connector/cursor.py", line 491, in execute self._handle_result(self._connection.cmd_query(stmt)) File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 1144, in cmd_query self.handle_mysql_error(exc) File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 1099, in handle_mysql_error self.reset_cache() File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 832, in reset_cache self._fabric.reset_cache(group=group) File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 369, in reset_cache self.get_group_servers(group, use_cache=False) File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 478, in get_group_servers inst = self.get_instance() File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 390, in get_instance if not inst.is_connected: File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 772, in is_connected self._proxy._some_nonexisting_method() # pylint: disable=W0212 File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xmlrpclib.py", line 1224, in __call__ return self.__send(self.__name, args) File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/xmlrpclib.py", line 1578, in __request verbose=self.__verbose File "/Library/Python/2.7/site-packages/mysql/connector/fabric/connection.py", line 272, in request raise InterfaceError("Connection with Fabric failed: " + msg) mysql.connector.errors.InterfaceError: Connection with Fabric failed:
Это происходит, когда делается новая попытка подключения после сброса локального кэша.
Убедиться, что MySQL Fabric работает
На момент написания этой статьи, пользователь должен убедиться, что MySQL Fabric запущен и работает. Это означает, что вы можете использовать все, что вам удобно с точки зрения ГА, например, кардиостимулятор . Хотя это и добавляет сложности в настройку, сервер XML-RPC очень прост в управлении, поэтому должен работать простой менеджер ресурсов. Для серверной части MySQL Fabric не зависит от механизма хранения, поэтому простым способом решения этой проблемы может быть использование небольшого кластера MySQL, настроенного для обеспечения доступности серверной части. Команда MySQL рассказала о такой настройке здесь, Мы считаем, что подход ndb, вероятно, является самым простым для предоставления HA на уровне хранилища MySQL Fabric, но считаем, что сама MySQL Fabric должна обеспечивать или облегчать достижение HA на уровне сервера XML-RPC. Если в качестве хранилища используется ndb, это означает, что любой узел может выполнить запись, что, в свою очередь, означает, что несколько экземпляров XML-PRC должны иметь возможность одновременной записи в хранилище. Это означает, что теоретически улучшить это можно так же просто, как позволить драйверам с поддержкой Fabric получать список серверов Fabric вместо одного IP-адреса и порта для подключения.
Что дальше
В последних двух статьях мы представили функции высокой доступности MySQL Fabric, увидели, как он обрабатывает сбои на уровне узлов, как использовать базы данных MySQL с драйвером, поддерживающим MySQL Fabric, и что остается нерешенным на данный момент. В нашем следующем посте мы рассмотрим функции Sharding в MySQL Fabric.