直接上代码:
#ifndef CDATABASE_CONNECTION_POOL_H
#define CDATABASE_CONNECTION_POOL_H
#include <libpq-fe.h>
#include <windows.h> // 使用 Windows 线程库
// Initialize the database connection pool
void initConnectionPool(int poolSize, const char* conninfo);
// Get a connection from the pool
PGconn* getConnectionFromPool();
// Return a connection to the pool
void releaseConnectionToPool(PGconn* conn);
#endif // CDATABASE_CONNECTION_POOL_H
#include "CDatabaseConnectionPool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {
PGconn** connections;
int poolSize;
int currentSize;
CRITICAL_SECTION lock; // 使用 Windows 的临界区对象来替代 pthread_mutex_t
} ConnectionPool;
static ConnectionPool pool;
void initConnectionPool(int poolSize, const char* conninfo) {
pool.connections = (PGconn**)malloc(poolSize * sizeof(PGconn*));
if (!pool.connections) {
fprintf(stderr, "Failed to allocate memory for connection pool.\n");
return;
}
pool.poolSize = poolSize;
pool.currentSize = 0;
InitializeCriticalSection(&pool.lock); // 初始化临界区对象
for (int i = 0; i < poolSize; i++) {
PGconn* conn = PQconnectdb(conninfo);
if (PQstatus(conn) == CONNECTION_BAD) {
fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));
PQfinish(conn);
}
else {
pool.connections[pool.currentSize++] = conn;
fprintf(stderr, "Connection %d created and added to pool.\n", i);
}
}
}
PGconn* getConnectionFromPool() {
PGconn* conn = NULL;
EnterCriticalSection(&pool.lock); // 进入临界区
if (pool.currentSize > 0) {
conn = pool.connections[--pool.currentSize];
fprintf(stderr, "Connection retrieved from pool. Remaining connections: %d\n", pool.currentSize);
}
else {
fprintf(stderr, "No connections available in pool.\n");
}
LeaveCriticalSection(&pool.lock); // 离开临界区
return conn;
}
void releaseConnectionToPool(PGconn* conn) {
EnterCriticalSection(&pool.lock); // 进入临界区
if (pool.currentSize < pool.poolSize) {
pool.connections[pool.currentSize++] = conn;
fprintf(stderr, "Connection returned to pool. Available connections: %d\n", pool.currentSize);
}
else {
PQfinish(conn); // 如果连接池已满,则关闭此连接
fprintf(stderr, "Connection discarded as pool is full.\n");
}
LeaveCriticalSection(&pool.lock); // 离开临界区
}
#ifndef TILE_DATABASE_HANDLER_H
#define TILE_DATABASE_HANDLER_H
#include <libpq-fe.h>
#include <stdlib.h>
typedef struct {
PGconn* conn;
} TileDatabaseHandler;
// Create and initialize the TileDatabaseHandler from the connection pool
TileDatabaseHandler* createTileDatabaseHandlerFromPool();
// Destroy the TileDatabaseHandler and free its resources
void destroyTileDatabaseHandler(TileDatabaseHandler* handler);
// Get the tile data from the database given z, x, y coordinates
char* getTile(TileDatabaseHandler* handler, int z, int x, int y, size_t* dataLength);
#endif // TILE_DATABASE_HANDLER_H
#include "TileDatabaseHandler.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "CDatabaseConnectionPool.h"
// Function to create and initialize TileDatabaseHandler from the connection pool
TileDatabaseHandler* createTileDatabaseHandlerFromPool() {
PGconn* conn = getConnectionFromPool();
if (!conn) {
fprintf(stderr, "Failed to get a connection from the pool\n");
return NULL;
}
TileDatabaseHandler* handler = (TileDatabaseHandler*)malloc(sizeof(TileDatabaseHandler));
if (!handler) {
releaseConnectionToPool(conn);
fprintf(stderr, "Failed to allocate memory for TileDatabaseHandler\n");
return NULL;
}
handler->conn = conn;
return handler;
}
// Function to destroy the TileDatabaseHandler and return the connection to the pool
void destroyTileDatabaseHandler(TileDatabaseHandler* handler) {
if (handler) {
if (handler->conn) {
releaseConnectionToPool(handler->conn);
}
free(handler);
}
}
// Function to get tile data from the database given z, x, y coordinates
char* getTile(TileDatabaseHandler* handler, int z, int x, int y, size_t* dataLength) {
if (!handler || !handler->conn) return NULL;
const char* queryTemplate = "SELECT data FROM Tile WHERE z = $1 AND x = $2 AND y = $3";
const char* params[3];
char zStr[12];
char xStr[12];
char yStr[12];
snprintf(zStr, sizeof(zStr), "%d", z - 1);
snprintf(xStr, sizeof(xStr), "%d", x);
snprintf(yStr, sizeof(yStr), "%d", y);
params[0] = zStr;
params[1] = xStr;
params[2] = yStr;
PGresult* res = PQexecParams(handler->conn, queryTemplate, 3, NULL, params, NULL, NULL, 1);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr, "SELECT failed: %s", PQerrorMessage(handler->conn));
PQclear(res);
return NULL;
}
if (PQntuples(res) == 0) {
PQclear(res);
return NULL;
}
*dataLength = PQgetlength(res, 0, 0);
if (*dataLength == 0) {
PQclear(res);
return NULL;
}
char* tileData = (char*)malloc(*dataLength);
if (!tileData) {
PQclear(res);
fprintf(stderr, "Failed to allocate memory for tile data\n");
return NULL;
}
memcpy(tileData, PQgetvalue(res, 0, 0), *dataLength);
PQclear(res);
return tileData;
}
#include "httpd.h"
#include "http_log.h"
#include "http_config.h"
#include "http_protocol.h"
#include "ap_config.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <apr_pools.h>
#include <apr_tables.h>
#include <apr_strings.h>
#include <apr_hash.h>
#include "TileDatabaseHandler.h"
#include "CDatabaseConnectionPool.h"
typedef struct {
TileDatabaseHandler* dbHandler;
char* tileDataBuffer;
size_t tileDataLength;
} tilehandler_context;
static apr_hash_t* tileCache;
static tilehandler_context* sharedContext = NULL;
tilehandler_context* create_tilehandler_context() {
tilehandler_context* context = (tilehandler_context*)malloc(sizeof(tilehandler_context));
if (context) {
context->dbHandler = createTileDatabaseHandlerFromPool();
context->tileDataBuffer = NULL;
context->tileDataLength = 0;
}
return context;
}
void destroy_tilehandler_context(tilehandler_context* context) {
if (context) {
if (context->tileDataBuffer) {
free(context->tileDataBuffer);
}
destroyTileDatabaseHandler(context->dbHandler);
free(context);
}
}
const char* get_tile_data(tilehandler_context* context, int z, int x, int y) {
if (!context) return NULL;
char key[64];
snprintf(key, sizeof(key), "%d:%d:%d", z, x, y);
char* cachedTileData = apr_hash_get(tileCache, key, APR_HASH_KEY_STRING);
if (cachedTileData) {
context->tileDataBuffer = cachedTileData;
context->tileDataLength = strlen(cachedTileData);
return context->tileDataBuffer;
}
context->tileDataBuffer = getTile(context->dbHandler, z, x, y, &(context->tileDataLength));
if (context->tileDataBuffer && context->tileDataLength > 0) {
char* tileDataCopy = apr_pstrdup(apr_hash_pool_get(tileCache), context->tileDataBuffer);
apr_hash_set(tileCache, apr_pstrdup(apr_hash_pool_get(tileCache), key), APR_HASH_KEY_STRING, tileDataCopy);
}
return context->tileDataBuffer;
}
static int tilehandler_module_handler(request_rec* r) {
if (strcmp(r->handler, "tilehandler_module")) {
return DECLINED;
}
if (r->method_number != M_GET) {
return HTTP_METHOD_NOT_ALLOWED;
}
int z, x, y;
if (sscanf(r->uri, "/tiles/%d/%d/%d", &z, &x, &y) != 3) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Failed to parse URI: %s", r->uri);
return HTTP_BAD_REQUEST;
}
if (!sharedContext) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Shared context is not initialized");
return HTTP_INTERNAL_SERVER_ERROR;
}
const char* tileData = get_tile_data(sharedContext, z, x, y);
if (tileData && sharedContext->tileDataLength > 0) {
ap_set_content_type(r, "image/jpeg");
apr_table_setn(r->headers_out, "Content-Length", apr_psprintf(r->pool, "%zu", sharedContext->tileDataLength));
ap_rwrite(tileData, sharedContext->tileDataLength, r);
}
else {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "Tile not found for z=%d, x=%d, y=%d", z, x, y);
ap_set_content_type(r, "text/plain");
ap_rprintf(r, "Tile not found");
return HTTP_NOT_FOUND;
}
return OK;
}
static int tilehandler_module_init(apr_pool_t* pool, apr_pool_t* plog, apr_pool_t* ptemp, server_rec* s) {
sharedContext = create_tilehandler_context();
if (!sharedContext) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, s, "Failed to initialize shared tilehandler context");
return HTTP_INTERNAL_SERVER_ERROR;
}
tileCache = apr_hash_make(pool);
return OK;
}
static void tilehandler_module_child_init(apr_pool_t* p, server_rec* s) {
initConnectionPool(10, "host=192.168.101.201 port=4321 dbname=Tile user=postgres password=root");
}
static void tilehandler_module_register_hooks(apr_pool_t* p) {
ap_hook_child_init(tilehandler_module_child_init, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_handler(tilehandler_module_handler, NULL, NULL, APR_HOOK_MIDDLE);
ap_hook_post_config(tilehandler_module_init, NULL, NULL, APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA tilehandler_module = {
STANDARD20_MODULE_STUFF,
NULL,
NULL,
NULL,
NULL,
NULL,
tilehandler_module_register_hooks
};