/* Create a word database object. */ TCWDB *tcwdbnew(void){ TCWDB *wdb = tcmalloc(sizeof(*wdb)); wdb->mmtx = tcmalloc(sizeof(pthread_rwlock_t)); if(pthread_rwlock_init(wdb->mmtx, NULL) != 0) tcmyfatal("pthread_rwlock_init failed"); wdb->idx = tcbdbnew(); if(!tcbdbsetmutex(wdb->idx)) tcmyfatal("tcbdbsetmutex failed"); wdb->open = false; wdb->cc = NULL; wdb->icsiz = WDBCCDEFICSIZ; wdb->lcnum = 0; wdb->dtokens = NULL; wdb->dids = NULL; wdb->etnum = WDBDEFETNUM; wdb->opts = 0; wdb->fwmmax = WDBDEFFWMMAX; wdb->synccb = NULL; wdb->syncopq = NULL; wdb->addcb = NULL; wdb->addopq = NULL; return wdb; }
int main(int argc, char *argv[], char *envp[]) { int c; char *synchttp_settings_listen = "0.0.0.0"; int synchttp_settings_port = 2688; char *synchttp_settings_datapath = NULL; bool synchttp_settings_daemon = false; int synchttp_settings_timeout = 60; /* 单位:秒 */ synchttp_settings_syncinterval = 5; /* 单位:秒 */ int synchttp_settings_cachenonleaf = 1024; /* 缓存非叶子节点数。单位:条 */ int synchttp_settings_cacheleaf = 2048; /* 缓存叶子节点数。叶子节点缓存数为非叶子节点数的两倍。单位:条 */ int synchttp_settings_mappedmemory = 104857600; /* 单位:字节 */ synchttp_settings_pidfile = "/tmp/synchttp.pid"; synchttp_settings_auth = NULL; /* 验证密码 */ /* 启动选项 */ while ((c = getopt(argc, argv, "l:p:x:t:s:c:m:i:a:w:dh")) != -1) { switch (c) { case 'l': synchttp_settings_listen = strdup(optarg); break; case 'p': synchttp_settings_port = atoi(optarg); break; case 'x': synchttp_settings_datapath = strdup(optarg); /* synchttp数据库文件存放路径 */ if (access(synchttp_settings_datapath, W_OK) != 0) { /* 如果目录不可写 */ if (access(synchttp_settings_datapath, R_OK) == 0) { /* 如果目录可读 */ chmod(synchttp_settings_datapath, S_IWOTH); /* 设置其他用户具可写入权限 */ } else { /* 如果不存在该目录,则创建 */ create_multilayer_dir(synchttp_settings_datapath); } if (access(synchttp_settings_datapath, W_OK) != 0) { /* 如果目录不可写 */ fprintf(stderr, "synchttp database directory not writable\n"); } } break; case 't': synchttp_settings_timeout = atoi(optarg); break; case 's': synchttp_settings_syncinterval = atoi(optarg); break; case 'c': synchttp_settings_cachenonleaf = atoi(optarg); synchttp_settings_cacheleaf = synchttp_settings_cachenonleaf * 2; break; case 'm': synchttp_settings_mappedmemory = atoi(optarg) * 1024 * 1024; /* 单位:M */ break; case 'i': synchttp_settings_pidfile = strdup(optarg); break; case 'a': synchttp_settings_auth = strdup(optarg); break; case 'd': synchttp_settings_daemon = true; break; case 'h': default: show_help(); return 1; } } /* 判断是否加了必填参数 -x */ if (synchttp_settings_datapath == NULL) { show_help(); fprintf(stderr, "Attention: Please use the indispensable argument: -x <path>\n\n"); exit(1); } /*curl初始化全局信息*/ if ((curl_global_init(CURL_GLOBAL_ALL)) != CURLE_OK) { fprintf(stderr, "Curl global init fail.\n"); exit(1); } /* 数据表路径 */ int synchttp_settings_dataname_len = 1024; char *synchttp_settings_dataname = (char *) tccalloc(1, synchttp_settings_dataname_len); sprintf(synchttp_settings_dataname, "%s/synchttp.db", synchttp_settings_datapath); /* 打开数据表 */ synchttp_db_tcbdb = tcbdbnew(); tcbdbsetmutex(synchttp_db_tcbdb); /* 开启线程互斥锁 */ tcbdbtune(synchttp_db_tcbdb, 1024, 2048, 50000000, 8, 10, BDBTLARGE); tcbdbsetcache(synchttp_db_tcbdb, synchttp_settings_cacheleaf, synchttp_settings_cachenonleaf); tcbdbsetxmsiz(synchttp_db_tcbdb, synchttp_settings_mappedmemory); /* 内存缓存大小 */ /* 判断表是否能打开 */ if (!tcbdbopen(synchttp_db_tcbdb, synchttp_settings_dataname, BDBOWRITER | BDBOCREAT)) { fprintf(stderr, "Attention: Unable to open the database.\n\n"); exit(1); } /* 释放变量所占内存 */ free(synchttp_settings_dataname); /* 如果加了-d参数,以守护进程运行 */ if (synchttp_settings_daemon == true) { pid_t pid; /* Fork off the parent process */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } /* If we got a good PID, then we can exit the parent process. */ if (pid > 0) { exit(EXIT_SUCCESS); } } /* 将进程号写入PID文件 */ FILE *fp_pidfile; fp_pidfile = fopen(synchttp_settings_pidfile, "w"); fprintf(fp_pidfile, "%d\n", getpid()); fclose(fp_pidfile); /* 派生synchttp子进程(工作进程) */ pid_t synchttp_worker_pid_wait; pid_t synchttp_worker_pid = fork(); /* 如果派生进程失败,则退出程序 */ if (synchttp_worker_pid < 0) { fprintf(stderr, "Error: %s:%d\n", __FILE__, __LINE__); exit(EXIT_FAILURE); } /* synchttp父进程内容 */ if (synchttp_worker_pid > 0) { /* 处理父进程接收到的kill信号 */ /* 忽略Broken Pipe信号 */ signal(SIGPIPE, SIG_IGN); /* 处理kill信号 */ signal(SIGINT, kill_signal_master); signal(SIGKILL, kill_signal_master); signal(SIGQUIT, kill_signal_master); signal(SIGTERM, kill_signal_master); signal(SIGHUP, kill_signal_master); /* 处理段错误信号 */ signal(SIGSEGV, kill_signal_master); /* 如果子进程终止,则重新派生新的子进程 */ while (1) { synchttp_worker_pid_wait = wait(NULL); if (synchttp_worker_pid_wait < 0) { continue; } usleep(100000); synchttp_worker_pid = fork(); if (synchttp_worker_pid == 0) { break; } } } /*****************************************子进程处理************************************/ /* 忽略Broken Pipe信号 */ signal(SIGPIPE, SIG_IGN); /* 处理kill信号 */ signal(SIGINT, kill_signal_worker); signal(SIGKILL, kill_signal_worker); signal(SIGQUIT, kill_signal_worker); signal(SIGTERM, kill_signal_worker); signal(SIGHUP, kill_signal_worker); /* 处理段错误信号 */ signal(SIGSEGV, kill_signal_worker); /*创建消息队列监听进程*/ pthread_t synchttp_dispatch_tid; pthread_create(&synchttp_dispatch_tid, NULL, (void *) synchttp_dispatch, NULL); /* 创建定时同步线程,定时将内存中的内容写入磁盘 */ pthread_t synchttp_worker_tid; pthread_create(&synchttp_worker_tid, NULL, (void *) synchttp_worker, NULL); /* 外部请求处理部分 */ struct evhttp *synchttpd; event_init(); synchttpd = evhttp_start(synchttp_settings_listen, synchttp_settings_port); if (synchttpd == NULL) { fprintf(stderr, "Error: Unable to listen on %s:%d\n\n", synchttp_settings_listen, synchttp_settings_port); kill(0, SIGTERM); exit(1); } evhttp_set_timeout(synchttpd, synchttp_settings_timeout); evhttp_set_gencb(synchttpd, synchttp_handler, NULL); event_dispatch(); evhttp_free(synchttpd); }
static int control(ErlDrvData handle, unsigned int command, char* buf, int count, char** res, int res_size) { tcbdb_drv_t *driver = (tcbdb_drv_t*)handle; TCBDB *bdb = driver->bdb; int index = 1, tp, sz, version; char key[MAX_KEY_SIZE], value[MAX_VALUE_SIZE]; long key_size, value_size; long long long_tmp; bool rs; const char *value_tmp = NULL; ei_x_buff x; ei_x_new_with_version(&x); if (bdb == NULL && command != OPEN) return ei_error(&x, "database_not_opened", res, res_size); ei_decode_version(buf, &index, &version); switch (command) { case OPEN: // open(Filepath::string()) if (bdb == NULL) { ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_STRING_EXT) return ei_error(&x, "invalid_argument", res, res_size); TCBDB *bdb = tcbdbnew(); tcbdbsetmutex(bdb); tcbdbsetcache(bdb, 104800, 51200); tcbdbsetxmsiz(bdb, 1048576); tcbdbtune(bdb, 0, 0, 0, 7, -1, BDBTLARGE); char *file = driver_alloc(sz + 1); ei_decode_string(buf, &index, file); rs = tcbdbopen(bdb, file, BDBOWRITER | BDBOCREAT); driver_free(file); if (!rs) return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); driver->bdb = bdb; ei_x_encode_atom(&x, "ok"); } else return ei_error(&x, "database already opened", res, res_size); break; case CLOSE: tcbdbclose(bdb); tcbdbdel(bdb); driver->bdb = NULL; ei_x_encode_atom(&x, "ok"); break; case PUT: case PUTDUP: case PUTCAT: case PUTKEEP: // put({Key::binary(), Value::binary()}) ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_SMALL_TUPLE_EXT || sz != 2) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_tuple_header(buf, &index, &sz); ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &key[0], &key_size); ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT) return ei_error(&x, "invalid_argument", res, res_size); if (sz <= MAX_VALUE_SIZE) { ei_decode_binary(buf, &index, &value[0], &value_size); switch (command) { case PUT: rs = tcbdbput(bdb, &key[0], key_size, &value[0], value_size); break; case PUTDUP: rs = tcbdbputdup(bdb, &key[0], key_size, &value[0], value_size); break; case PUTCAT: rs = tcbdbputcat(bdb, &key[0], key_size, &value[0], value_size); break; default: rs = tcbdbputkeep(bdb, &key[0], key_size, &value[0], value_size); } } else { void *p = driver_alloc(sz); if (p) { ei_decode_binary(buf, &index, p, &value_size); switch (command) { case PUT: rs = tcbdbput(bdb, &key[0], key_size, p, value_size); break; case PUTDUP: rs = tcbdbputdup(bdb, &key[0], key_size, p, value_size); break; case PUTCAT: rs = tcbdbputcat(bdb, &key[0], key_size, p, value_size); break; default: rs = tcbdbputkeep(bdb, &key[0], key_size, p, value_size); } driver_free(p); } else return ei_error(&x, "too long value", res, res_size); }; if (!rs) return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); ei_x_encode_atom(&x, "ok"); break; case GET: // get(Key::binary()) -> {ok, Value} | {error, not_found} ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &key[0], &key_size); value_tmp = tcbdbget3(bdb, &key[0], key_size, &sz); ei_x_encode_tuple_header(&x, 2); if (value_tmp != NULL) { ei_x_encode_atom(&x, "ok"); ei_x_encode_binary(&x, value_tmp, sz); } else { ei_x_encode_atom(&x, "error"); ei_x_encode_atom(&x, "not_found"); } break; case GETDUP: // get(Key::binary()) -> {ok, Values} ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &key[0], &key_size); TCLIST *vals = tcbdbget4(bdb, key, key_size); if (vals) { ei_x_encode_tuple_header(&x, 2); ei_x_encode_atom(&x, "ok"); int j; for (j=0; j<tclistnum(vals); j++) { value_tmp = tclistval(vals, j, &sz); ei_x_encode_list_header(&x, 1); ei_x_encode_binary(&x, value_tmp, sz); } tclistdel(vals); ei_x_encode_empty_list(&x); } else { ei_x_encode_tuple_header(&x, 2); ei_x_encode_atom(&x, "ok"); ei_x_encode_empty_list(&x); } break; case REMOVE: // remove(Keys::list()) // remove(Key::binary()) // remove({Key::binary(), Value::binary()}) ei_get_type(buf, &index, &tp, &sz); if (tp == ERL_LIST_EXT) { int count, j; ei_decode_list_header(buf, &index, &count); for (j=0; j<count; j++) { ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &key[0], &key_size); if (!tcbdbout3(bdb, &key[0], key_size)) return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); } ei_x_encode_atom(&x, "ok"); } else if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) { ei_decode_binary(buf, &index, &key[0], &key_size); tcbdbout3(bdb, &key[0], key_size); ei_x_encode_atom(&x, "ok"); } else if (tp == ERL_SMALL_TUPLE_EXT && sz == 2) { ei_decode_tuple_header(buf, &index, &sz); // get key ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &key[0], &key_size); // get value ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_VALUE_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &value[0], &value_size); // remove by key&value BDBCUR *cur = tcbdbcurnew(bdb); if (!tcbdbcurjump(cur, &key[0], key_size)) return ei_error(&x, "record_not_found", res, res_size); bool removed = false, not_found = false; while (!removed && !not_found) { int cur_key_size, cur_val_size; const void *curkey = tcbdbcurkey3(cur, &cur_key_size); if (cur_key_size == key_size && memcmp(curkey, key, key_size) == 0) { const void *curval = tcbdbcurval3(cur, &cur_val_size); if (cur_val_size == value_size && memcmp(curval, value, value_size) == 0) { tcbdbcurout(cur); removed = true; } else if (!tcbdbcurnext(cur)) not_found = true; } else not_found = true; } if (not_found) ei_x_encode_atom(&x, "not_found"); else ei_x_encode_atom(&x, "ok"); } else return ei_error(&x, "invalid_argument", res, res_size); break; case RANGE: /* * range({Prefix::binary(), limit:integer()}) * range({StartKey::binary(), BeginInclusion::boolean(), EndKey::binary(), EndInclusion::binary(), limit:integer()}) */ ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_SMALL_TUPLE_EXT || sz < 2) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_tuple_header(buf, &index, &sz); ei_get_type(buf, &index, &tp, &sz); if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) { char keys[MAX_KEY_SIZE], keyf[MAX_KEY_SIZE]; long keys_size, keyf_size; int keys_inc, keyf_inc; long max = -1; TCLIST *range; ei_decode_binary(buf, &index, &keys[0], &keys_size); ei_get_type(buf, &index, &tp, &sz); if (tp == ERL_ATOM_EXT) { // range ei_decode_boolean(buf, &index, &keys_inc); ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_BINARY_EXT || sz > MAX_KEY_SIZE) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_binary(buf, &index, &keyf[0], &keyf_size); ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_ATOM_EXT) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_boolean(buf, &index, &keyf_inc); ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_INTEGER_EXT) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_long(buf, &index, &max); range = tcbdbrange(bdb, &keys[0], keys_size, keys_inc == 1, &keyf[0], keyf_size, keyf_inc == 1, max); } else if (tp == ERL_INTEGER_EXT) { // prefix ei_get_type(buf, &index, &tp, &sz); if (tp != ERL_INTEGER_EXT) return ei_error(&x, "invalid_argument", res, res_size); ei_decode_long(buf, &index, &max); range = tcbdbfwmkeys(bdb, &keys[0], keys_size, max); } else return ei_error(&x, "invalid_argument", res, res_size); const char *key; int key_size, value_size; int idx, cnt = 0, rcount = tclistnum(range); ei_x_encode_tuple_header(&x, 2); ei_x_encode_atom(&x, "ok"); BDBCUR *cur = tcbdbcurnew(bdb); for (idx=0; idx<rcount; idx++) { key = tclistval(range, idx, &key_size); TCLIST *vals = tcbdbget4(bdb, key, key_size); if (vals) { int j; for (j=0; j<tclistnum(vals); j++) { ei_x_encode_list_header(&x, 1); value_tmp = tclistval(vals, j, &value_size); ei_x_encode_binary(&x, value_tmp, value_size); if (max >= 0 && ++cnt >= max) break; } tclistdel(vals); } idx++; } tcbdbcurdel(cur); tclistdel(range); ei_x_encode_empty_list(&x); } else return ei_error(&x, "invalid_argument", res, res_size); break; case SYNC: // sync() if (!tcbdbsync(bdb)) return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); ei_x_encode_atom(&x, "ok"); break; case INFO: // info() ei_x_encode_tuple_header(&x, 3); ei_x_encode_atom(&x, "ok"); long_tmp = tcbdbrnum(bdb); ei_x_encode_longlong(&x, long_tmp); long_tmp = tcbdbfsiz(bdb); ei_x_encode_longlong(&x, long_tmp); break; case ITERATE: // Read(none) -> {ok, Key} | {error, not_found} // Read(Key::binary()) -> {ok, Key} | {error, not_found} ei_get_type(buf, &index, &tp, &sz); BDBCUR *cur = tcbdbcurnew(bdb); if (tp == ERL_BINARY_EXT && sz <= MAX_KEY_SIZE) { ei_decode_binary(buf, &index, &key[0], &key_size); rs = tcbdbcurjump(cur, &key[0], key_size) && tcbdbcurnext(cur); } else rs = tcbdbcurfirst(cur); if (rs) { int key_size; const char *key = tcbdbcurkey3(cur, &key_size); ei_x_encode_tuple_header(&x, 2); ei_x_encode_atom(&x, "ok"); ei_x_encode_binary(&x, key, key_size); tcbdbcurdel(cur); } else { tcbdbcurdel(cur); return ei_error(&x, "not_found", res, res_size); } break; case VANISH: // vanish() -> ok if (!tcbdbvanish(bdb)) return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); ei_x_encode_atom(&x, "ok"); break; case BACKUP: // backup(path::string()) -> ok | {error, Reason} ei_get_type(buf, &index, &tp, &sz); if (tp == ERL_STRING_EXT) { char *file = driver_alloc(sz + 1); ei_decode_string(buf, &index, file); if (tcbdbcopy(driver->bdb, file)) ei_x_encode_atom(&x, "ok"); else return ei_error(&x, tcbdberrmsg(tcbdbecode(bdb)), res, res_size); } else return ei_error(&x, "invalid_argument", res, res_size); break; default: return ei_error(&x, "invalid_command", res, res_size); } if (res_size < x.index) (*res) = (char *)driver_alloc(x.index); int n = x.index; memcpy(*res, x.buff, x.index); ei_x_free(&x); return n; };
/* initialize */ JNIEXPORT void JNICALL Java_tokyocabinet_BDB_initialize (JNIEnv *env, jobject self){ TCBDB *bdb = tcbdbnew(); tcbdbsetmutex(bdb); (*env)->SetLongField(env, self, bdb_fid_ptr, (intptr_t)bdb); }