Как я могу отслеживать изменения в базе данных от единства без переполнения сетевой карты - PullRequest
0 голосов
/ 31 января 2019

У меня есть веб-сервер, который содержит базу данных SQL.На этом сервере / базе данных есть два «пользователя».Одним из них является фактический пользователь, которому они будут отправлять изменения через UnityWebRequest, используя HTTP: Post.Это внесет изменения в базу данных.

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

Что я пробовал

У меня есть функция единства, которая вызывает веб-сервер через HTTP:Сообщение.Веб-сервер заходит в бесконечный цикл, делая вызовы к базе данных, что-то вроде

$variable = $_REQUEST['variable_to_monitor'];
$stmt = $pdo->prepare("SELECT variable_to_monitor FROM table_name;")
while(true){
    $stmt->execute();
    results = $stmt->fetchAll()[0];
    if ($variable != results['variable_to_monitor']){
        die(results['variable_to_monitor']);
    }
}

Это удерживает веб-сервер и делает слишком много вызовов к базе данных.

Я бы хотел, чтобы Unity могла сделать один вызов веб-серверу, который присвоил ему определенное состояние, веб-сервер будет сравнивать указанное состояние с базой данных до тех пор, пока база данных не изменится, а затем, когда изменения в БД откликнутся наединство с обновленным состоянием.Я хочу сделать это, не совершая миллиона вызовов SELECT в базу данных в секунду.

Код Unity


    void Update()
    {
        if(hasResponse)
        {
            hasResponse = false;
            StartCoroutine(SendRequest());
        }
    }

    IEnumerator SendRequest(WWWForm form = null, Action<string> callback = null)
    {
        if(null == form) form = new WWWForm();
        form.AddField("request", "monitor_variable");
        form.AddField("variable_to_monitor", this.variable_to_monitor);

        UnityWebRequest www = UnityWebRequest.Post(url, form);
        yield return www.SendWebRequest();

        if (www.isNetworkError)
        {
            Debug.Log("Network Error");
        }
        else if (www.isHttpError)
        {
            Debug.Log("Http Error");
        }
        else
        {
            if(null == callback)
            {
                Debug.Log(www.downloadHandler.text);
            }
            else
            {
                if (!www.downloadHandler.text.Contains("New Request Started"))
                {
                    hasResponse = true;
                    callback(www.downloadHandler.text);
                }
                else
                {
                    Debug.Log(www.downloadHandler.text);
                }
            }
        }
    }

1 Ответ

0 голосов
/ 31 января 2019

Конечно, вы можете и всегда должны ждать в Unity для завершения UnityWebRequest !Вы должны всегда использовать, например, Coroutine

И затем вы также можете добавить, например, временное смещение, чтобы вы не делали новый запрос прямо сейчас, но, возможно, подождите несколько секунд.

Лично я бы также добавил обратные вызовы для ответа, что делает его более гибким и многократно используемым, например:

public void MakeRepeatedRequests(string url, float timeOffset, Action<string> successCallback, Action<string> errorCallback)
{
    StartCoroutine(RepeatedRequestRoutine(url, timeOffset, successCallback, errorCallback);
}

private IEnumerator RepeatedRequestRoutine(string url, float timeOffset, Action<string> successCallback, Action<string> errorCallback)
{
    // No worries this still looks like a while loop (well it is ^^)
    // but the yield makes sure it doesn't block Unity's UI thread
    while(true)
    {
        // Configure your request
        var request = UnityWebRequest.Post(url);

        // Make the request and wait until the it has finished (has a response or timeout)
        // a yield kind of tells this routine to leave, let Unity render this frame
        // and continue from here in the next frame
        yield return request.SendWebRequest();

        if(request.isNetworkError || request.isHttpError) 
        {
            errorCallback?.Invoke(request.error);
        }
        else 
        {
            successCallback?.Invoke(request.downloadHandler.text);
        }

        // Now wait for the timeOffset
        yield return new WaitForSeconds(timeOffset);
    }
}

Теперь вы можете вызывать его, например, для ожидания через 2 секунды после последнего запроса закончено

// ...

    MakeRepeatedRequests ("http://example.com", 2.0f, OnSuccess, OnError);

// ...

private void OnSuccess(string success)
{
    Debug.LogFormat(this, "Nice it worked! Server said:/"{0}/"", success);

    // Do something e.g. using success
}

private void OnError(string error)
{
    Debug.LogFormat(this, "Oh no, request failed with \"{0}\"", error);
}

или также сделать то же самое, используя лямбда-выражения

MakeRepeatedRequests ("http://example.com", 2.0f, 

    success =>
    {
        Debug.LogFormat(this, "Nice it worked! Server said:/"{0}/"", success);

        // Do something e.g. using success
    }, 

    error =>
    {
        Debug.LogFormat(this, "Oh no, request failed with \"{0}\"", error);

        // Do something else
    });

Даже если вы не хотите timeOffset, это по крайней мере ждетчтобы каждый запрос завершался, прежде чем запускать следующий, поэтому ваш сервер должен выполнить только один запрос к БД и вернуть либо успех, либо ошибку.

В вашем случае я бы в любом случае ответил (200) успешным (потому чтоошибку следует отправлять только в том случае, если на сервере действительно была ошибка), но либо отправьте пустой string, либо какой-либо заранее определенный, который можно проверить в Unity (что-то вроде, например, "!NO_CHANGE!").

, поэтому на сервересторона без альoop что-то вроде, например

results = "SELECT * FROM table_name;"
if ($_REQUEST['variable_name'] != results['variable_name']){
    echo results['variable_name'];
} else {
    echo "!NO_CHANGE!";
}

, чем вы могли бы позже в Unity проверить это

if(string.Equals(success, "!NO_CHANGE!")) return;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...