Контекст соединения является продвинутой темой при программировании с использованием нашего фреймворка. При работе с ним могут возникнуть некоторые сложности. Из предыдущих примеров можно заметить, что ни в клиентских, ни в серверных задачах нет способа указать конкретное соединение. Однако есть бизнес-сценарии, особенно на стороне сервера, где может потребоваться поддерживать состояние соединения. Это значит, нам нужно связывать некоторый контекст с соединением. Наш фреймворк предоставляет механизм контекста соединения для использования пользователями.
Протокол HTTP можно считать полностью бессостоятельным протоколом, где сессия HTTP реализуется через cookies. Такой подход наиболее дружественный для нашего фреймворка. Аналогично работает Kafka. Redis и MySQL имеют явно состоятельные соединения. Redis использует команду SELECT для указания текущей базы данных на соединении. А MySQL представляет собой чисто состоятельный протокол. При использовании клиентских задач для Redis или неатомарных MySQL, поскольку URL уже содержит всю информацию о выборе соединения, включая:
Фреймворк автоматически выполняет вход и выбирает повторно используемые соединения на основе этой информации, поэтому пользователю не требуется беспокоиться о контексте соединения. Поэтому в нашем фреймворке запрещены команды SELECT для Redis и USE для MySQL, так как они используются для изменения базы данных, которая должна быть создана новым URL. Для атомарных MySQL задач можно использовать фиксированное соединение, подробнее об этом см. в документации по MySQL. Однако если мы реализуем сервер для протокола Redis, то нам понадобится знать состояние текущего соединения.
Кроме того, мы можем воспользоваться событиями освобождения контекста соединения для обнаружения закрытия соединения удалённой стороной.
Важно отметить, что обычно только серверные задачи требуют использования контекста соединения, и это должно происходить только внутри функции process. Это самый безопасный и простой метод. Однако задачи также могут использовать или изменять контекст соединения внутри callback, но тогда следует учитывать проблему конкурентного доступа. Мы подробно рассмотрим эту проблему ниже. Любая сетевая задача может вызвать интерфейсы для получения объекта соединения, чтобы получить или изменить контекст соединения. В файле WFTask.h используется следующий шаблон:
template<class REQ, class, RESP>
class WFNetworkTask : public CommRequest
{
public:
virtual WFConnection *get_connection() const = 0;
...
};
Файл WFConnection.h содержит интерфейсы для работы с объектами соединения:
class WFConnection : public CommConnection
{
public:
void *get_context() const;
void set_context(void *context, std::function<void (void *)> deleter);
void set_context(void *context);
void *test_set_context(void *test_context, void *new_context,
std::function<void (void *)> deleter);
void *test_set_context(void *test_context, void *new_context);
};
Метод get_connection() может быть вызван только внутри процесса или callback, и если он вызывается внутри callback, необходимо проверить, что возвращаемое значение не равно NULL. Если удалось получить объект WFConnection, можно работать с контекстом соединения. Контекст соединения представлен указателем типа void *. Установка контекста соединения позволяет передать функцию-удалителю, которая будет автоматически вызвана при закрытии соединения. Если вызывается интерфейс без параметра функции-удалителя, можно установить новый контекст, сохранив прежнюю функцию-удалитель.
Клиентская задача создаётся до того, как соединение определяется, поэтому использование контекста соединения возможно только внутри callback. Серверная задача может использовать контекст соединения в двух местах — внутри процесса и callback. При использовании контекста соединения внутри callback следует учитывать проблему конкурентного доступа, так как одно и то же соединение может быть переиспользовано многими задачами одновременно. Поэтому рекомендуется обращаться к контексту соединения только внутри процесса, так как во время выполнения процесса соединение не будет переиспользовано или освобождено, что делает этот метод самым простым и безопасным. Обратите внимание, что процесс здесь относится только к внутреннему содержанию функции process, после завершения которой вызов get_connection всегда вернёт NULL. Метод test_set_context() класса WFConnection предназначен для решения проблемы конкурентного доступа при использовании контекста соединения внутри callback, однако его использование не рекомендуется. В общем случае, если вы не очень хорошо знакомы с системой, рекомендуется использовать контекст соединения только внутри функции process серверной задачи.
Предположим, что cookie в запросе очень большой, тогда очевидно, что это увеличивает объём передаваемых данных. Мы можем решить эту проблему с помощью контекста соединения на стороне сервера.
Мы соглашаемся, что cookie в запросе HTTP действует для всех последующих запросов на данном соединении, и последующие запросы могут не отправлять cookie в своих заголовках.
Вот пример кода на стороне сервера:
void process(WFHttpTask *server_task)
{
protocol::HttpRequest *req = server_task->get_req();
protocol::HttpHeaderCursor cursor(req);
WFConnection *conn = server_task->get_connection();
void *context = conn->get_context();
std::string cookie;
if (cursor.find("Cookie", cookie))
{
if (context)
delete (std::string *)context;
context = new std::string(cookie);
conn->set_context(context, [](void *p) { delete (std::string *)p; });
}
else if (context)
cookie = *(std::string *)context;
...
}
Этот подход позволяет экономить трафик, согласовывая передачу cookie только для первого запроса на соединении.
На стороне клиента для этого требуется новый callback, который используется следующим образом:
using namespace protocol;
void prepare_func(WFHttpTask *task)
{
if (task->get_task_seq() == 0)
task->get_req()->add_header_pair("Cookie", my_cookie);
}
int some_function()
{
WFHttpTask *task = WFTaskFactory::create_http_task(...);
task->set_prepare(prepare_func);
...
}
В этом примере, когда http-task является первым запросом на соединении, мы устанавливаем cookie. Для последующих запросов, согласно соглашению, cookie больше не передается.
Кроме того, внутри функции prepare можно безопасно использовать контекст соединения, так как prepare не происходит конкурентно для одного и того же соединения.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )