C语言如何连接与操作Elasticsearch?

99ANYc3cd6
预计阅读时长 36 分钟
位置: 首页 C语言 正文

这是一个非常具体且常见的需求,尤其是在高性能、低延迟的场景下,比如日志处理、数据分析管道等,C 语言提供了无与伦比的性能和资源控制能力。

c语言elasticsearch
(图片来源网络,侵删)

核心思想

C 语言本身不提供原生的 Elasticsearch 客户端,与 Java 的官方客户端不同,我们需要使用 HTTP 客户端库 来向 Elasticsearch 的 REST API 发送 HTTP 请求,并接收和解析 JSON 响应。

一个完整的 C 语言 Elasticsearch 客户端通常由以下几个部分组成:

  1. HTTP 客户端库:负责建立网络连接、发送 HTTP 请求和接收响应。
  2. JSON 解析库:负责将 Elasticsearch 返回的 JSON 响应解析成 C 语言中的数据结构(如结构体),以便于程序处理。
  3. 可选的封装库:一些第三方库已经将 HTTP 客户端和 JSON 解析器封装起来,提供了更简洁的 API,甚至实现了部分官方客户端的功能。

使用 libcurl + cURL (最常用、最灵活)

这是最主流、最推荐的方法。libcurl 是一个强大的、功能丰富的客户端 URL 传输库,支持几乎所有类型的网络协议,包括 HTTP/HTTPS,你需要自己处理 JSON 的序列化和反序列化。

安装依赖

你需要确保你的系统上安装了 libcurl 开发库。

c语言elasticsearch
(图片来源网络,侵删)
  • 在 Debian/Ubuntu 上:
    sudo apt-get update
    sudo apt-get install libcurl4-openssl-dev
  • 在 CentOS/RHEL/Fedora 上:
    sudo yum install libcurl-devel
  • 在 macOS 上 (使用 Homebrew):
    brew install curl

    Homebrew 默认会安装头文件,所以通常不需要额外步骤。

选择一个 JSON 库

你需要一个 C 语言的 JSON 库。Jansson 是一个非常好的选择,它简单、易用且功能强大。

  • 安装 Jansson:
    • 从源码编译安装 (推荐):
      wget https://github.com/akheron/jansson/archive/refs/tags/v2.14.tar.gz
      tar -xzf v2.14.tar.gz
      cd jansson-2.14
      autoreconf -i
      ./configure
      make
      sudo make install
    • 或者使用包管理器:
      • sudo apt-get install libjansson-dev (Debian/Ubuntu)
      • sudo yum install jansson-devel (CentOS/RHEL)

编写代码示例

下面是一个完整的示例,演示如何使用 libcurljansson 来:

  • 创建一个索引。
  • 向索引中添加一个文档。
  • 从索引中搜索一个文档。

文件结构:

c语言elasticsearch
(图片来源网络,侵删)
.
├── es_client.c
└── Makefile

es_client.c

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>
#include <jansson.h>
// 定义 Elasticsearch 的地址
#define ES_URL "http://localhost:9200"
#define INDEX_NAME "my_c_index"
#define TYPE_NAME "_doc" // Elasticsearch 7.x 之后,type 默认为 _doc
// 用于存储 libcurl 响应的结构体
struct MemoryStruct {
  char *memory;
  size_t size;
};
// 回调函数,用于 libcurl 接收数据
static size_t
WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
  size_t realsize = size * nmemb;
  struct MemoryStruct *mem = (struct MemoryStruct *)userp;
  char *ptr = realloc(mem->memory, mem->size + realsize + 1);
  if(!ptr) {
    /* out of memory! */
    printf("not enough memory (realloc returned NULL)\n");
    return 0;
  }
  mem->memory = ptr;
  memcpy(&(mem->memory[mem->size]), contents, realsize);
  mem->size += realsize;
  mem->memory[mem->size] = 0;
  return realsize;
}
// 1. 创建索引
int create_index(CURL *curl) {
    char url[256];
    snprintf(url, sizeof(url), "%s/%s", ES_URL, INDEX_NAME);
    CURLcode res;
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);  // will be grown by realloc
    chunk.size = 0;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "libcurl-agent/1.0");
    // curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); // 启用详细输出,方便调试
    printf("Creating index '%s'...\n", INDEX_NAME);
    res = curl_easy_perform(curl);
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    } else {
        printf("Response from create_index: %s\n\n", chunk.memory);
    }
    free(chunk.memory);
    return (int)res;
}
// 2. 索引一个文档
int index_document(CURL *curl) {
    char url[256];
    snprintf(url, sizeof(url), "%s/%s/%s/1", ES_URL, INDEX_NAME, TYPE_NAME);
    // 使用 Jansson 构建 JSON 负载
    json_t *json_obj = json_object();
    json_object_set_new(json_obj, "user", json_string("kimchy"));
    json_object_set_new(json_obj, "post_date", json_string("2009-11-15T14:12:12"));
    json_object_set_new(json_obj, "message", json_string("Trying out Elasticsearch, so far so good?"));
    const char *json_payload = json_dumps(json_obj, 0); // 将 JSON 对象转为字符串
    printf("Indexing document with payload: %s\n", json_payload);
    CURLcode res;
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);
    chunk.size = 0;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_payload); // 设置 POST 数据
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    res = curl_easy_perform(curl);
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    } else {
        printf("Response from index_document: %s\n\n", chunk.memory);
    }
    // 清理
    free(chunk.memory);
    json_decref(json_obj); // 释放 JSON 对象
    free((void*)json_payload); // 释放 JSON 字符串
    return (int)res;
}
// 3. 搜索文档
int search_document(CURL *curl) {
    char url[256];
    snprintf(url, sizeof(url), "%s/%s/_search", ES_URL, INDEX_NAME);
    // 构建查询 JSON
    json_t *query_obj = json_object();
    json_t *query = json_object();
    json_t *term = json_object();
    json_object_set_new(term, "user", json_string("kimchy"));
    json_object_set_new(query, "term", term);
    json_object_set_new(query_obj, "query", query);
    const char *json_query = json_dumps(query_obj, 0);
    printf("Searching with query: %s\n", json_query);
    CURLcode res;
    struct MemoryStruct chunk;
    chunk.memory = malloc(1);
    chunk.size = 0;
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET");
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, json_query);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    res = curl_easy_perform(curl);
    if(res != CURLE_OK) {
        fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
    } else {
        printf("Response from search_document: %s\n\n", chunk.memory);
    }
    // 清理
    free(chunk.memory);
    json_decref(query_obj);
    free((void*)json_query);
    return (int)res;
}
int main(void) {
    CURL *curl;
    CURLcode res;
    // 初始化 libcurl 全局环境
    curl_global_init(CURL_GLOBAL_ALL);
    // 初始化一个 easy 句柄
    curl = curl_easy_init();
    if(!curl) {
        fprintf(stderr, "Failed to initialize curl easy handle.\n");
        return 1;
    }
    // 执行操作
    create_index(curl);
    index_document(curl);
    search_document(curl);
    // 清理 easy 句柄
    curl_easy_cleanup(curl);
    // 清理 libcurl 全局环境
    curl_global_cleanup();
    return 0;
}

Makefile

CC = gcc
CFLAGS = -Wall -g -O2
LIBS = -lcurl -ljansson
TARGET = es_client
all: $(TARGET)
$(TARGET): es_client.c
    $(CC) $(CFLAGS) -o $(TARGET) es_client.c $(LIBS)
clean:
    rm -f $(TARGET)

编译和运行

  1. 将代码保存为 es_client.cMakefile
  2. 确保你的 Elasticsearch 服务正在运行 (localhost:9200)。
  3. 在终端中执行:
    make
    ./es_client

你应该能看到程序的输出,包括创建索引、索引文档和搜索文档的过程,以及 Elasticsearch 返回的 JSON 响应。


使用现成的 C 客户端库 (更高级)

如果你不希望手动处理 HTTP 和 JSON,可以使用一些已经封装好的 C 语言 Elasticsearch 客户端库,这些库提供了更接近官方客户端 API 的接口。

libesclient

这是一个比较知名的轻量级库,它封装了 libcurl 和 cjson (另一个流行的 JSON 库)。

特点:

  • API 设计简洁。
  • 支持基本的 CRUD 操作。
  • 需要你自行管理内存。

使用示例 (伪代码):

#include <libesclient.h>
// ... 初始化 ...
es_client_t *client = es_client_init("localhost", 9200, NULL, NULL);
// 索引文档
json_t *doc = json_pack("{s:s, s:i}", "name", "test_doc", "value", 123);
es_client_index(client, "my_index", "my_type", "1", doc, NULL);
// 搜索文档
json_t *query = json_pack("{s:{s:{s:s}}}", "query", "match", "name", "test_doc");
json_t *results = es_client_search(client, "my_index", query, NULL);
// ... 解析 results ...
json_decref(doc);
json_decref(query);
json_decref(results);
es_client_cleanup(client);

CCR (C REST Client for Elasticsearch)

这是一个更现代、功能更全面的库,灵感来源于官方的 Java 客户端。

特点:

  • 支持连接池。
  • 提供了更丰富的 API,包括聚合、滚动查询等。
  • 更复杂的构建和依赖管理。

使用示例 (伪代码):

#include <elasticsearch/ccr.h>
// ... 初始化连接池 ...
ccr_pool_t *pool = ccr_pool_create("localhost", 9200);
// 创建文档
ccr_document_t *doc = ccr_document_new();
ccr_document_set_field(doc, "user", "c_user");
ccr_document_set_field(doc, "message", "Hello from CCR!");
// 索入文档
ccr_index_t *index = ccr_index_new(pool, "my_ccr_index");
ccr_index_create(index, doc);
// 搜索
ccr_search_t *search = ccr_search_new(pool, "my_ccr_index");
ccr_search_query(search, ccr_query_match("user", "c_user"));
ccr_search_response_t *response = ccr_search_perform(search);
// ... 处理响应 ...
ccr_document_free(doc);
ccr_pool_destroy(pool);

总结与对比

特性 方案一: libcurl + Jansson 方案二: libesclient / CCR
灵活性 极高,你可以控制每一个 HTTP 请求头、参数和细节。 中等,API 是预设好的,自定义能力受限。
易用性 较低,需要手动处理 JSON 序列化/反序列化、错误处理等。 较高,封装了底层细节,API 更直观。
依赖 两个独立的、轻量级的库 (libcurl, jansson)。 一个集成的库,但可能内部依赖其他库(如 cjson)。
功能 完整,只要你能构造出合法的 JSON,就可以调用任何 ES API。 基本覆盖,通常只实现了最常用的 CRUD 和搜索功能。
学习曲线 陡峭,需要了解 HTTP 协议、JSON 格式以及这两个库的用法。 平缓,学习库提供的 API 即可。
适用场景 需要极致控制、性能优化,或实现非常特殊、非标准的 ES API 的场景。 快速开发,对标准 CRUD 和搜索操作有需求的常规应用。

推荐选择

  • 对于初学者或快速原型开发:如果你不想陷入 HTTP 和 JSON 的细节,libesclient 是一个不错的选择。
  • 对于生产环境或高性能需求强烈推荐使用 libcurl + Jansson (方案一),虽然初期搭建麻烦一些,但它给你带来了无与伦比的灵活性、控制力和对性能的精确把握,在长期维护和复杂业务逻辑面前,这种灵活性是巨大的优势。
-- 展开阅读全文 --
头像
织梦cms默认图片滚动代码如何使用?
« 上一篇 2025-12-20
C语言中AFXGetMainWnd函数如何正确使用?
下一篇 » 2025-12-20

相关文章

取消
微信二维码
支付宝二维码

目录[+]