blob: e4e1c871c139125e246b893536e22104306997e7 [file]
#include "GHttpClient.h"
#include "absl/strings/str_join.h"
#include "fmt/format.h"
#include <atomic>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <functional>
#include <string>
#include <thread>
#include "LoggerImpl.h"
ROCKETMQ_NAMESPACE_BEGIN
GHttpClient::GHttpClient() : shutdown_(false) {
grpc_core::ExecCtx exec_ctx;
grpc_httpcli_context_init(&http_context_);
grpc_pollset* pollset = static_cast<grpc_pollset*>(gpr_zalloc(grpc_pollset_size()));
grpc_pollset_init(pollset, &http_mtx_);
http_polling_entity_ = grpc_polling_entity_create_from_pollset(pollset);
GRPC_CLOSURE_INIT(&destroy_, &GHttpClient::destroyPollingEntity, &http_polling_entity_, grpc_schedule_on_exec_ctx);
}
GHttpClient::~GHttpClient() {
SPDLOG_INFO("GHttpClient::~GHttpClient() starts");
shutdown();
grpc_core::ExecCtx exec_ctx;
grpc_httpcli_context_destroy(&http_context_);
grpc_pollset_shutdown(grpc_polling_entity_pollset(&http_polling_entity_), &destroy_);
SPDLOG_INFO("GHttpClient::~GHttpClient() completed");
}
const int64_t GHttpClient::POLL_INTERVAL = 1000;
const int GHttpClient::STATUS_OK = 200;
void GHttpClient::poll() {
grpc_core::ExecCtx exec_ctx;
while (!shutdown_) {
gpr_mu_lock(http_mtx_);
grpc_error_handle error = grpc_pollset_work(grpc_polling_entity_pollset(&http_polling_entity_), &worker_,
grpc_core::ExecCtx::Get()->Now() + POLL_INTERVAL);
gpr_mu_unlock(http_mtx_);
if (error) {
SPDLOG_WARN("grpc_pollset_work failed");
} else {
SPDLOG_TRACE("grpc_pollset_work returned. grpc_pollset_size: {}", grpc_pollset_size());
}
submit0();
}
SPDLOG_INFO("GHttpClient::poll completed");
}
void GHttpClient::start() {
SPDLOG_INFO("GHttpClient::start()");
if (!shutdown_) {
loop_ = std::thread(std::bind(&GHttpClient::poll, this));
SPDLOG_INFO("GHttpClient starts to poll");
}
}
void GHttpClient::shutdown() {
bool expected = false;
if (shutdown_.compare_exchange_strong(expected, true, std::memory_order_relaxed)) {
SPDLOG_INFO("GHttpClient::shutdown()");
if (loop_.joinable()) {
loop_.join();
SPDLOG_INFO("GHttpClient#loop thread quit OK");
}
}
}
void GHttpClient::get(
HttpProtocol protocol, const std::string& host, std::uint16_t port, const std::string& path,
const std::function<void(int, const absl::flat_hash_map<std::string, std::string>&, const std::string&)>& cb) {
auto http_invocation_context = new HttpInvocationContext();
std::string http_host = fmt::format("{}:{}", host, port);
http_invocation_context->host = http_host;
http_invocation_context->path = path;
http_invocation_context->request.host = const_cast<char*>(http_invocation_context->host.c_str());
http_invocation_context->request.http.path = const_cast<char*>(http_invocation_context->path.c_str());
http_invocation_context->request.handshaker = &grpc_httpcli_plaintext;
http_invocation_context->callback = cb;
{
absl::MutexLock lk(&pending_requests_mtx_);
pending_requests_.emplace_back(http_invocation_context);
SPDLOG_TRACE("Add HTTP request to pending list");
}
{
grpc_core::ExecCtx exec_ctx;
SPDLOG_TRACE("Prepare to grpc_pollset_kick");
{
gpr_mu_lock(http_mtx_);
grpc_error_handle error = grpc_pollset_kick(grpc_polling_entity_pollset(&http_polling_entity_), nullptr);
gpr_mu_unlock(http_mtx_);
if (GRPC_ERROR_NONE != error) {
SPDLOG_WARN("grpc_pollset_kick failed");
} else {
SPDLOG_TRACE("grpc_pollset_kick completed");
}
}
}
}
void GHttpClient::submit0() {
absl::MutexLock lk(&pending_requests_mtx_);
if (pending_requests_.empty()) {
SPDLOG_TRACE("No pending HTTP requests");
return;
}
SPDLOG_DEBUG("Add {} pending HTTP requests to pollset", pending_requests_.size());
grpc_core::ExecCtx exec_ctx;
{
for (auto it = pending_requests_.begin(); it != pending_requests_.end();) {
auto http_invocation_context = *it;
SPDLOG_TRACE("Prepare to create quota");
grpc_resource_quota* resource_quota = grpc_resource_quota_create("get");
SPDLOG_TRACE("Quota created");
SPDLOG_TRACE("grpc_httpcli_get starts");
grpc_httpcli_get(
&http_context_, &http_polling_entity_, resource_quota, &http_invocation_context->request,
grpc_core::ExecCtx::Get()->Now() + absl::ToInt64Milliseconds(absl::Seconds(3)),
GRPC_CLOSURE_CREATE(&GHttpClient::onCompletion, http_invocation_context, grpc_schedule_on_exec_ctx),
&http_invocation_context->response);
SPDLOG_TRACE("grpc_httpcli_get completed");
grpc_resource_quota_unref_internal(resource_quota);
SPDLOG_TRACE("Resource quota unref completed");
it = pending_requests_.erase(it);
}
}
}
void GHttpClient::destroyPollingEntity(void* arg, grpc_error_handle error) {
auto polling_entity = reinterpret_cast<grpc_polling_entity*>(arg);
grpc_pollset_destroy(grpc_polling_entity_pollset(polling_entity));
}
void GHttpClient::onCompletion(void* arg, grpc_error_handle error) {
auto context = reinterpret_cast<HttpInvocationContext*>(arg);
absl::flat_hash_map<std::string, std::string> metadata;
if (error) {
context->callback(500, metadata, std::string());
SPDLOG_WARN("HTTP request failed");
delete context;
return;
}
for (size_t i = 0; i < context->response.hdr_count; i++) {
grpc_http_header* hdr = context->response.hdrs + i;
metadata.insert({std::string(hdr->key), std::string(hdr->value)});
}
std::string body(context->response.body, context->response.body_length);
SPDLOG_DEBUG("HTTP response received. Code: {}, response-headers: {}, response-body: {}", context->response.status,
absl::StrJoin(metadata, ",", absl::PairFormatter("=")), body);
context->callback(context->response.status, metadata, body);
delete context;
}
ROCKETMQ_NAMESPACE_END