快速建立你的服務器程序--single進程池模型

一、概述
  本節主要描述了以進程池模式建立服務器程序的過程,而該進程池框架是以 acl_master 模板爲管理進程,以 acl_single_server 單一進程池模式爲半駐留進程池模板建立的。該進程池模型有以下特色:
    1.1)半駐留進程池特徵;
    1.2)一個網絡鏈接對應一個工做進程。
二、建立過程(以 acl_project/samples/master/single_echo 爲例,ACL庫是跨平臺的,但 acl_master 服務器框架僅能運行在UNIX平臺下)
    在acl_project/samples/master/single_echo/ 目錄下存放着一個以 acl_single_server 爲服務器模型的echo服務程序。在該目錄下應該有 main.c, app_log.c, app_log.h, Makefile, Makefile.elib 四個文件,所須要修改的只是 main.c 文件,其它幾個文件無須修改。
2.1)編寫源文件
    a)包含 ACL 庫的頭文件: #include "lib_acl.h"
    b)調用服務函數 acl_single_server_main() 並註冊相關函數:
    函數原型:void acl_single_server_main(int argc, char **argv, ACL_SINGLE_SERVER_FN service,...);
    argc, argv: 是 main() 入口的兩個參數;
    service :是用戶本身的服務工做函數指針,該函數是以註冊函數的方式註冊進服務框架模板並由服務框架調用的;
    ...:是一些不定參數,這些參數都是可選的,這些不定參數是以「類型:指針」的方式傳遞給服務框架的,由服務框架根據類型自動進行分析,經常使用類型有:
    ACL_MASTER_SERVER_INT_TABLE--爲int類型的配置項集合的結構數組指針(該類型與由框架讀取用戶所須要的配置項相關);
    ACL_MASTER_SERVER_STR_TABLE--字符串類型的配置項集合的結構數組指針(該類型與由框架讀取用戶所須要的配置項相關);
    ACL_MASTER_SERVER_BOOL_TABLE--布爾類型的配置項集合的結構數組指針(該類型與由框架讀取用戶所須要的配置項相關);
    ACL_MASTER_SERVER_PRE_INIT--該類型代表後面的參數爲一函數指針,服務進程啓動後會自動切換成普通用戶身份,該類型所表明的函數會在服務切換成普通用戶身份前進行調用;
    ACL_MASTER_SERVER_POST_INIT--該類型代表後面的參數爲一函數指針,當服務框架將該服務工做進程切換成普通用戶身份後所調用的函數;
    ACL_MASTER_SERVER_PRE_ACCEPT--該類型代表後面的參數爲一函數指針,當服務框架監聽到監聽套接口上有新的客戶端鏈接到達,在用戶 accept()接受該鏈接以前能夠先回調用戶的註冊函數,此函數指針即爲該回調函數;
    ACL_MASTER_SERVER_EXIT--該類型代表後面的參數爲一函數指針,當該服務進程退出所回調的用戶的註冊函數。
2.2)源文件展現linux

/* main.c */
#include "lib_acl.h"
#include "app_log.h"
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

static char *var_cfg_single_banner;
static int   var_cfg_single_timeout;

static ACL_CONFIG_INT_TABLE __conf_int_tab[] = {
	{ "single_timeout", 60, &var_cfg_single_timeout, 0, 0 },
	{ 0, 0, 0, 0, 0 },
};

static ACL_CONFIG_STR_TABLE __conf_str_tab[] = {
	{ "single_banner", "hello, welcome!", &var_cfg_single_banner },
	{ 0, 0, 0 },
};

static void __service(ACL_VSTREAM *stream, char *service, char **argv acl_unused)
{
	const char *myname = "__service";
	char  buf[4096];
	int   n, ret;

	/*
	 * Sanity check. This service takes no command-line arguments.
	 */
	if (argv[0])
		acl_msg_fatal("%s(%d)->%s: unexpected command-line argument: %s",
				__FILE__, __LINE__, myname, argv[0]);

	acl_msg_info("%s(%d)->%s: service name = %s, rw_timeout = %d",
			__FILE__, __LINE__, myname, service, stream->rw_timeout);

	acl_msg_info("total alloc: %d", acl_mempool_total_allocated());
	do {
		acl_watchdog_pat();

		n = acl_vstream_readline(stream, buf, sizeof(buf) - 1);
		if (n == ACL_VSTREAM_EOF) {
			acl_msg_info("%s(%d)->%s: read over",
					__FILE__, __LINE__, myname);
			break;
		}

		ret = acl_vstream_writen(stream, buf, n);
		if (ret != n) {
			acl_msg_info("%s(%d)->%s: write error = %s",
					__FILE__, __LINE__, myname, strerror(errno));
			break;
		}
	} while (0);
}

static void __pre_accept(char *name acl_unused, char **argv acl_unused)
{
}

static void __pre_jail_init(char *name acl_unused, char **argv acl_unused)
{
	acl_mempool_open(512000000, 1);
	/* 是否採用 libcore 的日誌記錄 */
#ifdef  HAS_LIB_CORE
# ifdef USE_LIBCORE_LOG
	app_set_libcore_log();
# endif
#endif
}

static void __post_jail_init(char *name acl_unused, char **argv acl_unused)
{
}

static void service_exit(char *service acl_unused, char **argv acl_unused)
{
#ifdef  HAS_LIB_CORE
# ifdef USE_LIBCORE_LOG
	app_libcore_log_end();
# endif
#endif
}

int main(int argc, char *argv[])
{
	acl_single_server_main(argc, argv, __service,
			ACL_MASTER_SERVER_INT_TABLE, __conf_int_tab,
			ACL_MASTER_SERVER_STR_TABLE, __conf_str_tab,
			ACL_MASTER_SERVER_PRE_INIT, __pre_jail_init,
			ACL_MASTER_SERVER_PRE_ACCEPT, __pre_accept,
			ACL_MASTER_SERVER_POST_INIT, __post_jail_init,
			ACL_MASTER_SERVER_EXIT, service_exit,
			0);
	exit (0);
}


2.3) 配置文件:single_echo.cf

service single
{
# 進程是否禁止運行
master_disable = no
# 服務地址及端口號
master_service = :5003
# 服務監聽爲域套接口
# master_service = single_echo.sock
# 服務類型
master_type = inet
# master_type = unix
# 是否只容許私有訪問, 若是爲 y, 則域套接口建立在 {install_path}/var/log/private/ 目錄下,
# 若是爲 n, 則域套接口建立在 {install_path}/var/log/public/ 目錄下,
master_private = n
master_unpriv = n
# 是否須要 chroot: n -- no, y -- yes
master_chroot = n
# 每隔多長時間觸發一次,單位爲秒(僅對 trigger 模式有效)
master_wakeup = -
# 最大進程數
master_maxproc = 10
# 進程程序名
master_command = single_echo
# 進程啓動參數,只能爲: -u [是否容許以某普通用戶的身份運行]
# master_args =
# 進程日誌記錄文件
master_log = {install_path}/var/log/single_echo.log
# 傳遞給服務子進程的環境變量, 能夠經過 getenv("SERVICE_ENV") 得到此值
# master_env = logme:FALSE, priority:E_LOG_INFO, action:E_LOG_PER_DAY, flush:sync_flush, imit_size:512,\
#   sync_action:E_LOG_SEM, sem_name:/tmp/single_echo.sem

# 每一個進程實例處理鏈接數的最大次數,超過此值後進程實例主動退出
single_use_limit = 250
# 每一個進程實例的空閒超時時間,超過此值後進程實例主動退出
# single_idle_limit = 180
# 記錄進程PID的位置(對於多進程實例來講沒有意義)
single_pid_dir = {install_path}/var/pid
# 進程運行時所在的路徑
single_queue_dir = {install_path}/var
# 讀寫超時時間, 單位爲秒
single_rw_timeout = 1800
# 讀緩衝區的緩衝區大小
single_buf_size = 8192
# 進程運行時的用戶身份
single_owner = root

# single_in_flow_delay = 1
# single_owner = owner
# 用 select 進行循環時的時間間隔
# 單位爲秒
# single_delay_sec = 1
# 單位爲微秒
# single_delay_usec = 5000
# single_daemon_timeout = 1800
}

2.4)編譯源文件
    生成 single_echo 可執行程序
    make
2.5)拷貝文件
    將 single_echo 拷貝至 acl_project/dist/master/libexec/linux32 (假設操做系統是LINUX 32位平臺的) 目錄,將 single_echo.cf 拷貝至acl_project/dist/master/conf/service/ 目錄,從而將 single_echo 置於 acl_master 守護管理進程的控制範圍內。
2.6) 安裝
    cd acl_project/dist/master; chmod 755 setup.sh; ./setup.sh /opt/acl
2.7)啓動框架管理控制進程(acl_master)
   /opt/acl/sh/start.sh
2.8)手工測試
    telnet 127.0.0.1 5003
    看是否正常鏈接服務器,若是鏈接成功,則隨意輸入一些字符而後按回車發送,看服務器是否將所發送的數據回顯給發送者;若是鏈接不成功或服務器未正常回顯,請查看日誌文件:/opt/acl/var/log/single_echo,並找出出錯緣由。

我的微博:http://weibo.com/zsxxszgit

下載:http://sourceforge.net/projects/acl/github

svn:svn checkout svn://svn.code.sf.net/p/acl/code/trunk acl-code數組

github:https://github.com/zhengshuxin/acl服務器

QQ 羣:242722074網絡

相關文章
相關標籤/搜索