programing

MariaDB 서버에 대한 Python Connection은 약 24시간마다 닫힙니다.

copysource 2022. 9. 22. 22:51
반응형

MariaDB 서버에 대한 Python Connection은 약 24시간마다 닫힙니다.

RFID 태그가 부착된 아이템을 추적할 수 있는 제품을 만들고 있습니다. 키오스크에서 스와이핑하고 데이터베이스에 로그인하는 것은 간단합니다.

데이터베이스는 실제로 키오스크에서 호스트되며(베스트 프랙티스는 아닌 것으로 알고 있습니다), 키오스크에서 실행되는 메인 프로그램은 GuiZero를 사용하는 파이썬 프런트 엔드입니다.키오스크는 라즈베리 파이로 라스비안 데스크톱을 운영하고 있다.

모든 것이 정상적으로 동작하지만, (약) 프로그램이 시작되고 나서 24시간 후에 데이터베이스 쿼리가 서버로 전송될 때까지 정상적으로 동작합니다.python-mariadb-connector는 다음과 같이 응답합니다.2055: Lost connection to MySQL server at '127.0.0.1:3306', system error: 32 Broken pipe이 문제의 트러블 슈팅을 약 1개월 정도 실시해 왔습니다만, 무엇이 이 문제를 해결할 수 있는지 알 수 있습니다.

이런 일이 있을 때마다 파이썬 프런트 엔드 내에서 접속을 갱신하려고 했지만 그것도 잘 되지 않습니다.이 문제를 해결할 수 있는 유일한 방법은 프로그램을 다시 시작하는 것입니다. 사용자는 이 프로그램을 다시 시작하는 방법을 신뢰할 수 없습니다.

한 가지 주의할 점은 24시간까지는 모든 것이 완벽하게 작동한다는 것입니다.

도움이 필요하신가요?

의존관계:

Python 3.7
(pip) mysql-connector v2.2.9
MariaDB: 10.3.17-MariaDB-0+deb10ul Raspbian 10
Raspbian GNU/Linux v10

타임아웃 변수

MariaDB [locker]> show variables like "%timeout";
+---------------------------------------+-------+
| Variable_name                         | Value |
+---------------------------------------+-------+
| connect_timeout                       | 10    |
| delayed_insert_timeout                | 300   |
| idle_readonly_transaction_timeout     | 0     |
| idle_transaction_timeout              | 0     |
| idle_write_transaction_timeout        | 0     |
| innodb_flush_log_at_timeout           | 1     |
| innodb_lock_wait_timeout              | 50    |
| innodb_rollback_on_timeout            | OFF   |
| interactive_timeout                   | 28800 |
| lock_wait_timeout                     | 86400 |
| net_read_timeout                      | 30    |
| net_write_timeout                     | 60    |
| rpl_semi_sync_master_timeout          | 10000 |
| rpl_semi_sync_slave_kill_conn_timeout | 5     |
| slave_net_timeout                     | 60    |
| thread_pool_idle_timeout              | 60    |
| wait_timeout                          | 28800 |
+---------------------------------------+-------+

여기 코드가 있습니다. 166행은 오류가 처리되는 곳입니다.(내 처리는 아무것도 하지 않는다) Ctrl+F를 누르면 "aghiulg"가 표시됩니다.

#!/usr/bin/python3.7
from datetime import datetime
from sys import exit

# Classes
class User:
    def __init__ (self, rfid, name):
        self.name = name
        self.rfid = rfid

class Key:
    def __init__ (self, rfid, kid, prop, loc):
        self.rfid = rfid
        self.kid = kid
        self.prop = prop.split("/")
        self.loc = loc

    def toString (self):
        return "[{}] {}".format(self.kid, "/".join(self.prop[:3]))

# Slack
import slack

slackBotToken = "OBFUSCATEDFORSTACKOVERFLOW"
slackClient = slack.WebClient(slackBotToken)
slackID = None

def getTimeString ():
    now = datetime.now()
    return now.strftime("%m/%d/%Y %H:%M:%S")

def log (s):
    time = getTimeString()

    # stdout
    print("[{}] {}".format(time, s))


    # slack
    slackErr = False
    try:
        slackClient.chat_postMessage(channel = "#keys", text = s)
    except concurrent.futures._base.TimeoutError:
        slackErr = True

    # file
    with open("/home/pi/key-locker/log.txt", "a+") as f:
        f.write("[{}] {}\n".format(time, s))
        if slackErr:
            f.write("Couldn't write that to Slack, oops.")

def xlog (s):
    try:
        slackClient.chat_postMessage(channel = "OBFUSCATED", text = s)
        return True
    except concurrent.futures._base.TimeoutError:
        return False

# guizero
from guizero import App, Text, TextBox, info

app = App(title = "Key Locker")
app.tk.attributes("-fullscreen", True)
REFOCUS_TIMER = 500
USER_TIMEOUT = 60 * 1000

# MariaDB
import mysql.connector as mariadb

def connectDB ():
    xlog("Reconnecting to database...")
    return mariadb.connect(user="OBFUSCATED", password="OBFUSCATED", database="OBFUSCATED")

# mdb = mariadb.connect(user="OBFUSCATED", password="OBFUSCATED", database="OBFUSCATED")
mdb = connectDB()
cursor = mdb.cursor(buffered = True)

def focusUIN (uin = None):
    if uin:
        uin.focus()

def onTextBoxKey (e, f = None):
    global uidBox, kidBox, bigText, underText, currentUser, currentKey, escapeKeys

    if len(e.key) == 0:
        return

    if f == uidBox and ord(e.key) == 13:
        uid = uidBox.value
        if uid.lower() in escapeKeys:
            exit(0)
        else:
            currentUser = codeToUser(uid)
            if currentUser:
                uidBox.cancel(focusUIN)
                uidBox.disable()

                kidBox.enable()
                kidBox.repeat(REFOCUS_TIMER, focusUIN, args = [kidBox])
                kidBox.when_key_pressed = lambda e: onTextBoxKey(e, f = kidBox)

                bigText.value = "Scan Key"
                underText.value = "Welcome, {}.".format(currentUser.name)

                app.after(USER_TIMEOUT, restart, args = ["User Timeout"])
            else:
                restart("That user doesn't exist.")
    elif f == kidBox and ord(e.key) == 13:
        kid = kidBox.value
        if kid.lower() in escapeKeys:
            exit(0)
        else:
            app.cancel(restart)

            currentKey = codeToKey(kid)
            if currentKey:
                kidBox.cancel(focusUIN)
                kidBox.disable()

                inLocker = (currentKey.loc.lower() == "locker")
                success = (checkout(currentUser, currentKey) if inLocker else checkin(currentUser, currentKey))
                if success:
                    restart("{} checked {} the {} keys.".format(currentUser.name, "out" if inLocker else "in", currentKey.toString()))
                else:
                    restart("System error, try again.")
            else:
                restart("That key doesn't exist.")

def restart (subText = ". . ."):
    global uidBox, kidBox, bigText, underText

    uidBox.value = ""
    uidBox.enable()
    uidBox.cancel(focusUIN)
    uidBox.repeat(REFOCUS_TIMER, focusUIN, args = [uidBox])
    uidBox.focus()

    kidBox.value = ""
    kidBox.cancel(focusUIN)
    kidBox.disable()

    bigText.value = "Scan Badge"
    underText.value = subText

# App
escapeKeys = "letmeout pleaseijustwanttoseemywifeandkidsagain".split(" ")
currentUser = None
currentKey = None

def codeToUser (uid = None):
    xlog("Trying to find user by RFID...")
    if uid:
        try:
            cursor.execute("select Name from user where RFID = '{}';".format(uid))
            names = []
            for n in cursor:
                names.append(n)
            if len(names) != 1:
                xlog("Ran the function, and literally got no matches for that user rfid.")
                xlog("Restarting...")
                exit(0)
                return None
            else:
                xlog("Found user: {}".format(names[0][0]))
                return User(uid, names[0][0])
        except mariadb.Error as e: # aghiulg
            xlog("Database error trying to find the user.\n{}".format(e))
            # fatalError(e)
            return None
    else:
        xlog("They didn't give me an RFID.")
        return None

def codeToKey (kid = None):
    if kid:
        try:
            cursor.execute("select KeyringID, Properties, Location from keyring where RFID = {};".format(kid))
            keys = []
            for k in cursor:
                keys.append(k)
            if len(keys) != 1:
                return None
            else:
                keys = keys[0]
                return Key(kid, keys[0], keys[1], keys[2])
        except mariadb.Error as e:
            # fatalError(e)
            return None
    else:
        return None

def checkout(user, key):
    global mdb

    log("Checking out k{} ({}) to {}.".format(key.kid, ", ".join(key.prop), user.name))
    try:
        cursor.execute("update keyring set Location = '{}' where KeyringID = {}".format(user.name, key.kid))
        mdb.commit()
        return True
    except mariadb.Error as e:
        # fatalError(e)
        mdb = connectDB()
        return False

def checkin (user, key):
    global mdb

    log("Checking in k{} ({}) from {}.".format(key.kid, ", ".join(key.prop), user.name))
    try:
        cursor.execute("update keyring set Location = 'locker' where KeyringID = {}".format(key.kid))
        mdb.commit()
        return True
    except mariadb.Error as e:
        # fatalError(e)
        mdb = connectDB()
        return False

def fatalError (e):
    log("Error: {}".format(e))
    log("Fatal error encountered, Key Locker turning off. Next user must open it by double clicking the desktop link.")
    exit()

# First Run
if __name__ == "__main__":
    bigText = Text(app, "Scan Badge", size = 40, color = "black")
    underText = Text(app, ". . .", size = 15, color = "black")

    uidBox = TextBox(app)
    uidBox.repeat(REFOCUS_TIMER, focusUIN, args = [uidBox])
    uidBox.when_key_pressed = lambda e: onTextBoxKey(e, f = uidBox)

    kidBox = TextBox(app, enabled = False)

    auditText = Text(app, size = 10, color = "black")

    app.display()

interactive_displays의 기본값은 28800초 = 8시간입니다.이는 8시간이 지나면 서버가 자동으로 연결을 닫는 것을 의미합니다.

이 값을 늘려보고 연결이 끊길지 확인해보겠습니다.

PEP-249에 따르면 연결 핸들이 비활성화되는 즉시 모든 커서 객체가 비활성화됩니다.MySQL Connector/Python에는 자동 재연결 옵션이 없고 연결 개체를 커서에 다시 할당할 수 없으므로 커서를 재사용하기 전에 다시 작성해야 합니다.

DB 오류에서 새 커서가 아닌 새 연결을 만듭니다.기존 커서가 현재 닫혀 있는 초기 연결에 여전히 바인딩되어 있기 때문에 오류가 계속 발생합니다.

새 연결을 만들 때마다 커서를 다시 초기화하면 문제가 없습니다.

로컬로 호스트된 MariaDB 인스턴스를 사용하고 있으므로 TCP/IP 대신 Unix 소켓을 사용하여 DB에 연결해 보십시오.호스트 베이스의 파이어 월(fire wall)이나, TCP 스택에 관한 그 외의 네트워크의 이상에 간섭하고 있는 경우는, 기본적으로 이러한 방법으로 우회할 수 있습니다.

MariaDB에 ./var/run/mysqld/mysqld.sock(미치다)패스에 그합니다).

> show variables like 'socket';
+---------------+-----------------------------+
| Variable_name | Value                       |
+---------------+-----------------------------+
| socket        | /var/run/mysqld/mysqld.sock |
+---------------+-----------------------------+

그런 다음 연결 명령을 다음과 같이 설정합니다.

mariadb.connect(user="Donuts", password="Candy", database="Cake", unix_socket="/var/run/mysqld/mysqld.sock")

참고 자료: https://dev.mysql.com/doc/connector-python/en/connector-python-connectargs.html

언급URL : https://stackoverflow.com/questions/59788696/python-connection-to-mariadb-server-closes-about-every-24-hours

반응형