2016-04-04 61 views
0

我們有一個Python服務器,它使用SQLAlchemy從AWS MySQL MultiAZ RDS實例讀取/寫入數據。SQLAlchemy連接在AWS上掛起MySQL MySQL RDS重新啓動併發生故障轉移

我們正在經歷一種行爲,我們希望避免在每當我們觸發故障轉移重新啓動的情況下,已經打開的連接然後發出無限期掛起的連接。雖然這是根據AWS文檔所期望的,但我們希望Python MySQL連接器能夠應對這種情況。

我們在網上發現的最接近的案例是google groups thread,它討論了這個問題並提供了關於Postgres RDS的解決方案。

例如,下面的腳本會在啓動故障轉移重啓時無限期地掛起(從上面提到的谷歌組線程中採用)。

from datetime import datetime 
from time import time, sleep 
from sqlalchemy import Column, Integer, String, create_engine 
from sqlalchemy.orm import sessionmaker 
from sqlalchemy.orm.scoping import scoped_session 
from sqlalchemy.ext.declarative import declarative_base 

import logging 

current_milli_time = lambda: int(round(time() * 1000)) 
Base = declarative_base() 

logging.basicConfig(format='%(asctime)s %(filename)s %(lineno)s %(process)d %(levelname)s: %(message)s', level="INFO") 

class Message(Base): 
    __tablename__ = 'message' 
    id = Column(Integer, primary_key=True) 
    body = Column(String(450), nullable=False) 

engine = create_engine('mysql://<username>:<password>@<db_host>/<db_name>',echo=False, pool_recycle=1800,) 
session_maker = scoped_session(sessionmaker(bind=engine, autocommit=False, autoflush=False)) 
session = session_maker() 

while True: 
    try: 
     ids = '' 
     start = current_milli_time() 
     for msg in session.query(Message).order_by(Message.id.desc()).limit(5): 
      ids += str(msg.id) + ', ' 
      logging.info('({!s}) (took {!s} ms) fetched ids: {!s}'.format(datetime.now().time().isoformat(), current_milli_time() - start, ids)) 

     start = current_milli_time() 
     m = Message() 
     m.body = 'some text' 
     session.add(m) 
     session.commit() 
     logging.info('({!s}) (took {!s} ms) inserted new message'.format(datetime.now().time().isoformat(), current_milli_time() - start)) 

    except Exception, e: 
     logging.exception(e) 
     session.rollback() 
    finally: 
     session_maker.remove() 

    sleep(0.25) 

我們已經試過玩的連接超時,但似乎這個問題是關係到它只是掛一次AWS切換到故障實例已經打開的連接。

我們的問題是 - 有沒有人遇到過這個問題或有可能的方向值得檢查?

+0

這絕對不應該是無縫的客戶端。從[docs](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_RebootInstance.html):「當您強制實施數據庫實例的故障轉移時,Amazon RDS會自動切換到另一個備用副本可用區,並更新數據庫實例的DNS記錄以指向備用數據庫實例,因此,您需要清理並重新建立到數據庫實例的現有連接。 – univerio

+0

你是完全正確的,我想知道如果Python數據庫連接器有一種方式來感知這種情況 – wilfo

+0

你如何設置連接超時?如果現有連接出現問題,聽起來就像遠程主機在關閉時不發送FIN數據包,這很令人擔憂。通常,當您正常關閉主機時,應用程序將優雅地關閉打開的套接字連接。連接不應該掛起,除非中間的某個路由器正在丟棄TCP RST數據包。 – univerio

回答

1

恕我直言,使用SQL連接超時處理switchcover就像黑魔法。每個連接器的行爲總是不同且難以診斷。

如果您再次閱讀@univerio評論,AWS將重新分配SAME RDS端點名稱的新IP地址。在進行交換時,您的RDS端點名稱和舊IP加法器仍在您的服務器實例DNS緩存中。所以這是一個DNS緩存問題,這就是爲什麼AWS要求你「清理......」。

除非您重新啓動SQLAlchemy以再次讀取DNS,否則會話無法知道發生了什麼並且動態切換它。最糟糕的是,這個問題可能發生在SQLAlchemy使用的連接器中。

恕我直言,它不值得努力處理在代碼內切換。我只會訂閱像lambda這樣的AWS服務,它可以在切換事件時採取行動,觸發應用程序服務器重新啓動連接,這將假設反映新的IP地址。