libserverframe/src/sf_service.c

630 lines
19 KiB
C

//sf_service.c
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "fastcommon/logger.h"
#include "fastcommon/sockopt.h"
#include "fastcommon/shared_func.h"
#include "fastcommon/pthread_func.h"
#include "fastcommon/sched_thread.h"
#include "fastcommon/ioevent_loop.h"
#include "sf_global.h"
#include "sf_nio.h"
#include "sf_service.h"
#if defined(OS_LINUX)
#include <sys/eventfd.h>
#endif
static bool bTerminateFlag = false;
static void sigQuitHandler(int sig);
static void sigHupHandler(int sig);
static void sigUsrHandler(int sig);
#if defined(DEBUG_FLAG)
static void sigDumpHandler(int sig);
#endif
struct worker_thread_context {
SFContext *sf_context;
struct nio_thread_data *thread_data;
};
struct accept_thread_context {
SFContext *sf_context;
int server_sock;
};
static void *worker_thread_entrance(void *arg);
static int sf_init_free_queues(const int task_arg_size)
{
#define ALLOC_CONNECTIONS_ONCE 1024
static bool sf_inited = false;
int result;
int m;
int init_connections;
int alloc_conn_once;
if (sf_inited) {
return 0;
}
sf_inited = true;
if ((result=set_rand_seed()) != 0) {
logCrit("file: "__FILE__", line: %d, "
"set_rand_seed fail, program exit!", __LINE__);
return result;
}
m = g_sf_global_vars.min_buff_size / (64 * 1024);
if (m == 0) {
m = 1;
} else if (m > 16) {
m = 16;
}
alloc_conn_once = ALLOC_CONNECTIONS_ONCE / m;
init_connections = g_sf_global_vars.max_connections < alloc_conn_once ?
g_sf_global_vars.max_connections : alloc_conn_once;
if ((result=free_queue_init_ex(g_sf_global_vars.max_connections,
init_connections, alloc_conn_once, g_sf_global_vars.
min_buff_size, g_sf_global_vars.max_buff_size,
task_arg_size)) != 0)
{
return result;
}
return 0;
}
int sf_service_init_ex(SFContext *sf_context,
sf_alloc_thread_extra_data_callback
alloc_thread_extra_data_callback,
ThreadLoopCallback thread_loop_callback,
sf_accept_done_callback accept_done_callback,
sf_set_body_length_callback set_body_length_func,
sf_deal_task_func deal_func, TaskCleanUpCallback task_cleanup_func,
sf_recv_timeout_callback timeout_callback, const int net_timeout_ms,
const int proto_header_size, const int task_arg_size)
{
int result;
int bytes;
struct worker_thread_context *thread_contexts;
struct worker_thread_context *thread_ctx;
struct nio_thread_data *thread_data;
struct nio_thread_data *pDataEnd;
pthread_t tid;
pthread_attr_t thread_attr;
sf_context->realloc_task_buffer = g_sf_global_vars.
min_buff_size < g_sf_global_vars.max_buff_size;
sf_context->accept_done_func = accept_done_callback;
sf_set_parameters_ex(sf_context, proto_header_size, set_body_length_func,
deal_func, task_cleanup_func, timeout_callback);
if ((result=sf_init_free_queues(task_arg_size)) != 0) {
return result;
}
if ((result=init_pthread_attr(&thread_attr, g_sf_global_vars.
thread_stack_size)) != 0)
{
logError("file: "__FILE__", line: %d, "
"init_pthread_attr fail, program exit!", __LINE__);
return result;
}
bytes = sizeof(struct nio_thread_data) * sf_context->work_threads;
sf_context->thread_data = (struct nio_thread_data *)malloc(bytes);
if (sf_context->thread_data == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, strerror(errno));
return errno != 0 ? errno : ENOMEM;
}
memset(sf_context->thread_data, 0, bytes);
bytes = sizeof(struct worker_thread_context) * sf_context->work_threads;
thread_contexts = (struct worker_thread_context *)malloc(bytes);
if (thread_contexts == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, strerror(errno));
return errno != 0 ? errno : ENOMEM;
}
sf_context->thread_count = 0;
pDataEnd = sf_context->thread_data + sf_context->work_threads;
for (thread_data=sf_context->thread_data,thread_ctx=thread_contexts;
thread_data<pDataEnd; thread_data++,thread_ctx++)
{
thread_data->thread_loop_callback = thread_loop_callback;
if (alloc_thread_extra_data_callback != NULL) {
thread_data->arg = alloc_thread_extra_data_callback(
(int)(thread_data - sf_context->thread_data));
}
else {
thread_data->arg = NULL;
}
if (ioevent_init(&thread_data->ev_puller,
g_sf_global_vars.max_connections + 2, net_timeout_ms, 0) != 0)
{
result = errno != 0 ? errno : ENOMEM;
logError("file: "__FILE__", line: %d, "
"ioevent_init fail, "
"errno: %d, error info: %s",
__LINE__, result, strerror(result));
return result;
}
result = fast_timer_init(&thread_data->timer,
2 * g_sf_global_vars.network_timeout, g_current_time);
if (result != 0) {
logError("file: "__FILE__", line: %d, "
"fast_timer_init fail, "
"errno: %d, error info: %s",
__LINE__, result, strerror(result));
return result;
}
if ((result=init_pthread_lock(&thread_data->waiting_queue.lock)) != 0) {
return result;
}
#if defined(OS_LINUX)
FC_NOTIFY_READ_FD(thread_data) = eventfd(0, EFD_NONBLOCK);
if (FC_NOTIFY_READ_FD(thread_data) < 0) {
result = errno != 0 ? errno : EPERM;
logError("file: "__FILE__", line: %d, "
"call eventfd fail, "
"errno: %d, error info: %s",
__LINE__, result, strerror(result));
break;
}
FC_NOTIFY_WRITE_FD(thread_data) = FC_NOTIFY_READ_FD(thread_data);
#else
if (pipe(thread_data->pipe_fds) != 0) {
result = errno != 0 ? errno : EPERM;
logError("file: "__FILE__", line: %d, "
"call pipe fail, "
"errno: %d, error info: %s",
__LINE__, result, strerror(result));
break;
}
if ((result=fd_add_flags(FC_NOTIFY_READ_FD(thread_data),
O_NONBLOCK)) != 0)
{
break;
}
#endif
thread_ctx->sf_context = sf_context;
thread_ctx->thread_data = thread_data;
if ((result=pthread_create(&tid, &thread_attr,
worker_thread_entrance, thread_ctx)) != 0)
{
logError("file: "__FILE__", line: %d, "
"create thread failed, startup threads: %d, "
"errno: %d, error info: %s",
__LINE__, (int)(thread_data - sf_context->thread_data),
result, strerror(result));
break;
}
}
pthread_attr_destroy(&thread_attr);
return result;
}
int sf_service_destroy_ex(SFContext *sf_context)
{
struct nio_thread_data *pDataEnd, *thread_data;
free_queue_destroy();
pDataEnd = sf_context->thread_data + sf_context->work_threads;
for (thread_data=sf_context->thread_data; thread_data<pDataEnd;
thread_data++)
{
fast_timer_destroy(&thread_data->timer);
}
free(sf_context->thread_data);
sf_context->thread_data = NULL;
return 0;
}
static void *worker_thread_entrance(void *arg)
{
struct worker_thread_context *thread_ctx;
thread_ctx = (struct worker_thread_context *)arg;
__sync_fetch_and_add(&thread_ctx->sf_context->thread_count, 1);
ioevent_loop(thread_ctx->thread_data,
sf_recv_notify_read,
thread_ctx->sf_context->task_cleanup_func,
&g_sf_global_vars.continue_flag);
ioevent_destroy(&thread_ctx->thread_data->ev_puller);
__sync_fetch_and_sub(&thread_ctx->sf_context->thread_count, 1);
return NULL;
}
static int _socket_server(const char *bind_addr, int port, int *sock)
{
int result;
*sock = socketServer(bind_addr, port, &result);
if (*sock < 0) {
return result;
}
if ((result=tcpsetserveropt(*sock, g_sf_global_vars.network_timeout)) != 0) {
return result;
}
return 0;
}
int sf_socket_server_ex(SFContext *sf_context)
{
int result;
const char *bind_addr;
sf_context->inner_sock = sf_context->outer_sock = -1;
if (sf_context->outer_port == sf_context->inner_port) {
if (*sf_context->outer_bind_addr == '\0' ||
*sf_context->inner_bind_addr == '\0') {
bind_addr = "";
return _socket_server(bind_addr, sf_context->outer_port,
&sf_context->outer_sock);
} else if (strcmp(sf_context->outer_bind_addr,
sf_context->inner_bind_addr) == 0) {
bind_addr = sf_context->outer_bind_addr;
if (is_private_ip(bind_addr)) {
return _socket_server(bind_addr, sf_context->
inner_port, &sf_context->inner_sock);
} else {
return _socket_server(bind_addr, sf_context->
outer_port, &sf_context->outer_sock);
}
}
}
if ((result=_socket_server(sf_context->outer_bind_addr,
sf_context->outer_port, &sf_context->outer_sock)) != 0)
{
return result;
}
if ((result=_socket_server(sf_context->inner_bind_addr,
sf_context->inner_port, &sf_context->inner_sock)) != 0)
{
return result;
}
return 0;
}
static void *accept_thread_entrance(void *arg)
{
struct accept_thread_context *accept_context;
int incomesock;
struct sockaddr_in inaddr;
socklen_t sockaddr_len;
struct fast_task_info *task;
char szClientIp[IP_ADDRESS_SIZE];
accept_context = (struct accept_thread_context *)arg;
while (g_sf_global_vars.continue_flag) {
sockaddr_len = sizeof(inaddr);
incomesock = accept(accept_context->server_sock,
(struct sockaddr*)&inaddr, &sockaddr_len);
if (incomesock < 0) { //error
if (!(errno == EINTR || errno == EAGAIN)) {
logError("file: "__FILE__", line: %d, "
"accept fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
}
continue;
}
getPeerIpaddr(incomesock,
szClientIp, IP_ADDRESS_SIZE);
if (tcpsetnonblockopt(incomesock) != 0) {
close(incomesock);
continue;
}
task = free_queue_pop();
if (task == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc task buff failed, you should "
"increase the parameter: max_connections",
__LINE__);
close(incomesock);
continue;
}
strcpy(task->client_ip, szClientIp);
task->canceled = false;
task->ctx = accept_context->sf_context;
task->event.fd = incomesock;
task->thread_data = accept_context->sf_context->thread_data +
incomesock % accept_context->sf_context->work_threads;
if (accept_context->sf_context->accept_done_func != NULL) {
accept_context->sf_context->accept_done_func(task,
accept_context->server_sock ==
accept_context->sf_context->inner_sock);
}
if (sf_nio_notify(task, SF_NIO_STAGE_INIT) != 0) {
close(incomesock);
free_queue_push(task);
}
}
return NULL;
}
void _accept_loop(struct accept_thread_context *accept_context,
const int accept_threads)
{
pthread_t tid;
pthread_attr_t thread_attr;
int result;
int i;
if (accept_threads <= 0) {
return;
}
if ((result=init_pthread_attr(&thread_attr, g_sf_global_vars.
thread_stack_size)) != 0)
{
logWarning("file: "__FILE__", line: %d, "
"init_pthread_attr fail!", __LINE__);
}
else {
for (i=0; i<accept_threads; i++) {
if ((result=pthread_create(&tid, &thread_attr,
accept_thread_entrance,
accept_context)) != 0)
{
logError("file: "__FILE__", line: %d, "
"create thread failed, startup threads: %d, "
"errno: %d, error info: %s",
__LINE__, i, result, strerror(result));
break;
}
}
pthread_attr_destroy(&thread_attr);
}
}
void sf_accept_loop_ex(SFContext *sf_context, const bool block)
{
struct accept_thread_context *accept_contexts;
int count;
int bytes;
if (sf_context->outer_sock >= 0) {
count = 2;
} else {
count = 1;
}
bytes = sizeof(struct accept_thread_context) * count;
accept_contexts = (struct accept_thread_context *)malloc(bytes);
if (accept_contexts == NULL) {
logError("file: "__FILE__", line: %d, "
"malloc %d bytes fail, errno: %d, error info: %s",
__LINE__, bytes, errno, strerror(errno));
return;
}
accept_contexts[0].sf_context = sf_context;
accept_contexts[0].server_sock = sf_context->inner_sock;
if (sf_context->outer_sock >= 0) {
accept_contexts[1].sf_context = sf_context;
accept_contexts[1].server_sock = sf_context->outer_sock;
if (sf_context->inner_sock >= 0) {
_accept_loop(accept_contexts, sf_context->accept_threads);
}
if (block) {
_accept_loop(accept_contexts + 1, sf_context->accept_threads - 1);
accept_thread_entrance(accept_contexts + 1);
} else {
_accept_loop(accept_contexts + 1, sf_context->accept_threads);
}
} else {
if (block) {
_accept_loop(accept_contexts, sf_context->accept_threads - 1);
accept_thread_entrance(accept_contexts);
} else {
_accept_loop(accept_contexts, sf_context->accept_threads);
}
}
}
#if defined(DEBUG_FLAG)
static void sigDumpHandler(int sig)
{
static bool bDumpFlag = false;
char filename[256];
if (bDumpFlag) {
return;
}
bDumpFlag = true;
snprintf(filename, sizeof(filename),
"%s/logs/sf_dump.log", g_sf_global_vars.base_path);
//manager_dump_global_vars_to_file(filename);
bDumpFlag = false;
}
#endif
static void sigQuitHandler(int sig)
{
if (!bTerminateFlag) {
bTerminateFlag = true;
g_sf_global_vars.continue_flag = false;
logCrit("file: "__FILE__", line: %d, "
"catch signal %d, program exiting...",
__LINE__, sig);
}
}
static void sigHupHandler(int sig)
{
logInfo("file: "__FILE__", line: %d, "
"catch signal %d", __LINE__, sig);
}
static void sigUsrHandler(int sig)
{
logInfo("file: "__FILE__", line: %d, "
"catch signal %d, ignore it", __LINE__, sig);
}
int sf_setup_signal_handler()
{
struct sigaction act;
memset(&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
act.sa_handler = sigUsrHandler;
if(sigaction(SIGUSR1, &act, NULL) < 0 ||
sigaction(SIGUSR2, &act, NULL) < 0)
{
logCrit("file: "__FILE__", line: %d, "
"call sigaction fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
logCrit("exit abnormally!\n");
return errno;
}
act.sa_handler = sigHupHandler;
if(sigaction(SIGHUP, &act, NULL) < 0) {
logCrit("file: "__FILE__", line: %d, "
"call sigaction fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
logCrit("exit abnormally!\n");
return errno;
}
act.sa_handler = SIG_IGN;
if(sigaction(SIGPIPE, &act, NULL) < 0) {
logCrit("file: "__FILE__", line: %d, "
"call sigaction fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
logCrit("exit abnormally!\n");
return errno;
}
act.sa_handler = sigQuitHandler;
if(sigaction(SIGINT, &act, NULL) < 0 ||
sigaction(SIGTERM, &act, NULL) < 0 ||
sigaction(SIGQUIT, &act, NULL) < 0)
{
logCrit("file: "__FILE__", line: %d, "
"call sigaction fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
logCrit("exit abnormally!\n");
return errno;
}
#if defined(DEBUG_FLAG)
memset(&act, 0, sizeof(act));
sigemptyset(&act.sa_mask);
act.sa_handler = sigDumpHandler;
if(sigaction(SIGUSR1, &act, NULL) < 0 ||
sigaction(SIGUSR2, &act, NULL) < 0)
{
logCrit("file: "__FILE__", line: %d, "
"call sigaction fail, errno: %d, error info: %s",
__LINE__, errno, strerror(errno));
logCrit("exit abnormally!\n");
return errno;
}
#endif
return 0;
}
int sf_startup_schedule(pthread_t *schedule_tid)
{
#define SCHEDULE_ENTRIES_COUNT 3
ScheduleArray scheduleArray;
ScheduleEntry scheduleEntries[SCHEDULE_ENTRIES_COUNT];
int index;
scheduleArray.entries = scheduleEntries;
scheduleArray.count = 0;
memset(scheduleEntries, 0, sizeof(scheduleEntries));
index = scheduleArray.count++;
INIT_SCHEDULE_ENTRY(scheduleEntries[index], sched_generate_next_id(),
TIME_NONE, TIME_NONE, 0,
g_sf_global_vars.sync_log_buff_interval,
log_sync_func, &g_log_context);
if (g_sf_global_vars.rotate_error_log) {
log_set_rotate_time_format(&g_log_context, "%Y%m%d");
index = scheduleArray.count++;
INIT_SCHEDULE_ENTRY(scheduleEntries[index], sched_generate_next_id(),
0, 0, 0, 86400, log_notify_rotate, &g_log_context);
if (g_sf_global_vars.log_file_keep_days > 0) {
log_set_keep_days(&g_log_context,
g_sf_global_vars.log_file_keep_days);
index = scheduleArray.count++;
INIT_SCHEDULE_ENTRY(scheduleEntries[index], sched_generate_next_id(),
1, 0, 0, 86400, log_delete_old_files, &g_log_context);
}
}
return sched_start(&scheduleArray, schedule_tid,
g_sf_global_vars.thread_stack_size, (bool * volatile)
&g_sf_global_vars.continue_flag);
}
void sf_set_current_time()
{
g_current_time = time(NULL);
g_sf_global_vars.up_time = g_current_time;
srand(g_sf_global_vars.up_time);
}
void sf_enable_thread_notify_ex(SFContext *sf_context, const bool enabled)
{
struct nio_thread_data *thread_data;
struct nio_thread_data *pDataEnd;
pDataEnd = sf_context->thread_data + sf_context->work_threads;
for (thread_data=sf_context->thread_data; thread_data<pDataEnd;
thread_data++)
{
thread_data->notify.enabled = enabled;
}
}