diff --git a/HISTORY b/HISTORY index 199d45e..6f5993c 100644 --- a/HISTORY +++ b/HISTORY @@ -3,6 +3,7 @@ Version 1.27 2016-04-09 * add function fd_set_cloexec * php-fastcommon.spec.in support PHP 7 * add file lock and unlock functions + * add id generator for multi processes Version 1.26 2016-03-16 * add logger parameter: compress_log_days_before diff --git a/src/Makefile.in b/src/Makefile.in index 53f233f..9711902 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -11,7 +11,7 @@ FAST_SHARED_OBJS = hash.lo chain.lo shared_func.lo ini_file_reader.lo \ fast_timer.lo process_ctrl.lo fast_mblock.lo \ connection_pool.lo fast_mpool.lo fast_allocator.lo \ fast_buffer.lo multi_skiplist.lo flat_skiplist.lo \ - system_info.lo fast_blocked_queue.lo + system_info.lo fast_blocked_queue.lo id_generator.lo FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \ logger.o sockopt.o base64.o sched_thread.o \ @@ -20,7 +20,7 @@ FAST_STATIC_OBJS = hash.o chain.o shared_func.o ini_file_reader.o \ fast_timer.o process_ctrl.o fast_mblock.o \ connection_pool.o fast_mpool.o fast_allocator.o \ fast_buffer.o multi_skiplist.o flat_skiplist.o \ - system_info.o fast_blocked_queue.o + system_info.o fast_blocked_queue.o id_generator.o HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \ shared_func.h pthread_func.h ini_file_reader.h _os_define.h \ @@ -29,8 +29,8 @@ HEADER_FILES = common_define.h hash.h chain.h logger.h base64.h \ fast_timer.h process_ctrl.h fast_mblock.h \ connection_pool.h fast_mpool.h fast_allocator.h \ fast_buffer.h skiplist.h multi_skiplist.h flat_skiplist.h \ - skiplist_common.h system_info.h fast_blocked_queue.h \ - php7_ext_wrapper.h + skiplist_common.h system_info.h fast_blocked_queue.h \ + php7_ext_wrapper.h id_generator.h ALL_OBJS = $(FAST_STATIC_OBJS) $(FAST_SHARED_OBJS) diff --git a/src/id_generator.c b/src/id_generator.c new file mode 100644 index 0000000..fc190f8 --- /dev/null +++ b/src/id_generator.c @@ -0,0 +1,177 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastDFS source kit. +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" +#include "shared_func.h" +#include "local_ip_func.h" +#include "id_generator.h" + +int id_generator_init_ex(struct idg_context *context, const char *filename, + const int machine_id, const int mid_bits) +{ + int result; + int mid; + if (mid_bits < 2 || mid_bits > 20) + { + logError("file: "__FILE__", line: %d, " + "invalid bits of machine id: %d", + __LINE__, mid_bits); + context->fd = -1; + return EINVAL; + } + if (machine_id < 0 || machine_id >= (1 << mid_bits)) + { + logError("file: "__FILE__", line: %d, " + "invalid machine id: %d", + __LINE__, machine_id); + context->fd = -1; + return EINVAL; + } + if (machine_id != 0) + { + mid = machine_id; + } + else + { + const char *local_ip; + struct in_addr ip_addr; + + local_ip = get_first_local_ip(); + if (local_ip == NULL) + { + logError("file: "__FILE__", line: %d, " + "can't get local ip address", __LINE__); + context->fd = -1; + return ENOENT; + } + + if (inet_pton(AF_INET, local_ip, &ip_addr) != 1) + { + logError("file: "__FILE__", line: %d, " + "invalid local ip address: %s", + __LINE__, local_ip); + context->fd = -1; + return EINVAL; + } + + printf("ip_addr: %s, s_addr: %08X\n", local_ip, ip_addr.s_addr); + printf("mask number: %08X\n", (1 << mid_bits)); + + mid = (ip_addr.s_addr >> (32 - mid_bits)) & ((1 << mid_bits) - 1); + } + + if ((context->fd = open(filename, O_RDWR | O_CREAT, 0644)) < 0) + { + result = errno != 0 ? errno : EACCES; + logError("file: "__FILE__", line: %d, " + "open file \"%s\" fail, " + "errno: %d, error info: %s", __LINE__, + filename, result, STRERROR(result)); + return result; + } + + context->machine_id = mid; + context->mid_bits = mid_bits; + context->sn_bits = 32 - mid_bits; + context->masked_mid = ((int64_t)mid) << context->sn_bits; + context->sn_mask = ((int64_t)1 << context->sn_bits) - 1; + + printf("mid: %08X, masked_mid: %016llX, sn_mask: %08llX\n", mid, context->masked_mid, context->sn_mask); + + return 0; +} + +void id_generator_destroy(struct idg_context *context) +{ + if (context->fd >= 0) + { + close(context->fd); + context->fd = -1; + } +} + +int id_generator_next(struct idg_context *context, int64_t *id) +{ + int result; + int len; + int bytes; + int64_t sn; + char buff[32]; + char *endptr; + + if ((result=file_write_lock(context->fd)) != 0) + { + return result; + } + + do + { + if (lseek(context->fd, 0L, SEEK_SET) == -1) + { + result = errno != 0 ? errno : EACCES; + logError("file: "__FILE__", line: %d, " + "file lseek fail, " + "errno: %d, error info: %s", __LINE__, + result, STRERROR(result)); + sn = 0; + break; + } + + if ((bytes=read(context->fd, buff, sizeof(buff) - 1)) < 0) + { + result = errno != 0 ? errno : EACCES; + logError("file: "__FILE__", line: %d, " + "file read fail, " + "errno: %d, error info: %s", __LINE__, + result, STRERROR(result)); + sn = 0; + break; + } + *(buff + bytes) = '\0'; + + sn = strtoll(buff, &endptr, 10); + ++sn; + + if (lseek(context->fd, 0L, SEEK_SET) == -1) + { + result = errno != 0 ? errno : EACCES; + logError("file: "__FILE__", line: %d, " + "cal lseek fail, " + "errno: %d, error info: %s", __LINE__, + result, STRERROR(result)); + break; + } + + len = sprintf(buff, "%019"PRId64, sn); + if ((bytes=write(context->fd, buff, len)) != len) + { + result = errno != 0 ? errno : EACCES; + logError("file: "__FILE__", line: %d, " + "file write %d bytes fail, written: %d bytes, " + "errno: %d, error info: %s", __LINE__, + len, bytes, result, STRERROR(result)); + break; + } + } while (0); + + file_unlock(context->fd); + + *id = (time(NULL) << 32) | context->masked_mid | (sn & context->sn_mask); + return result; +} + diff --git a/src/id_generator.h b/src/id_generator.h new file mode 100644 index 0000000..706aa74 --- /dev/null +++ b/src/id_generator.h @@ -0,0 +1,84 @@ +/** +* Copyright (C) 2008 Happy Fish / YuQing +* +* FastDFS may be copied only under the terms of the GNU General +* Public License V3, which may be found in the FastDFS source kit. +* Please visit the FastDFS Home Page http://www.csource.org/ for more detail. +**/ + +/** + 64 bits id generator for multi processes, the generated id format: + 32 bits timestamp + X bits machine id + Y bits serial number +*/ + +#ifndef ID_GENERATOR_H +#define ID_GENERATOR_H + +#include +#include +#include +#include +#include +#include +#include "common_define.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct idg_context { + int fd; + int machine_id; + int mid_bits; //bits of machine id + int sn_bits; //bits of serial number + int64_t masked_mid; + int64_t sn_mask; +}; + +/** +* init function + set mid_bits to 16 + set machine_id to 2 bytes of local ip address +* parameter: +* context: the id generator context +* filename: the filename to store id +* return error no, 0 for success, none zero for fail +*/ +int id_generator_init(struct idg_context *context, const char *filename); + +/** +* init function +* parameter: +* context: the id generator context +* filename: the filename to store id +* machine_id: the machine id, 0 for auto generate by local ip address +* mid_bits: the bits of the machine id, such as 16 +* return error no, 0 for success, none zero for fail +*/ +int id_generator_init_ex(struct idg_context *context, const char *filename, + const int machine_id, const int mid_bits); + +/** +* init function +* parameter: +* context: the id generator context +* filename: the filename to store id +* return error no, 0 for success, none zero for fail +*/ +void id_generator_destroy(struct idg_context *context); + +/** +* generate next id +* parameter: +* context: the id generator context +* id: store the id +* return error no, 0 for success, none zero for fail +*/ +int id_generator_next(struct idg_context *context, int64_t *id); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/tests/Makefile b/src/tests/Makefile index e0228cd..aeb9171 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -4,7 +4,8 @@ COMPILE = $(CC) -g -O1 -Wall -D_FILE_OFFSET_BITS=64 -g -DDEBUG_FLAG INC_PATH = -I/usr/include/fastcommon LIB_PATH = -lfastcommon -lpthread -ALL_PRGS = test_allocator test_skiplist test_multi_skiplist test_mblock test_blocked_queue +ALL_PRGS = test_allocator test_skiplist test_multi_skiplist test_mblock test_blocked_queue \ + test_id_generator all: $(ALL_PRGS) .c: diff --git a/src/tests/test_id_generator.c b/src/tests/test_id_generator.c new file mode 100644 index 0000000..3eb5137 --- /dev/null +++ b/src/tests/test_id_generator.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "logger.h" +#include "shared_func.h" +#include "sched_thread.h" +#include "ini_file_reader.h" +#include "sockopt.h" +#include "id_generator.h" + +int main(int argc, char *argv[]) +{ + struct idg_context context; + int result; + int i; + int64_t id; + const int machine_id = 0; + const int mid_bits = 8; + + result = id_generator_init_ex(&context, "/tmp/sn.txt", + machine_id, mid_bits); + if (result != 0) + { + return result; + } + + id_generator_next(&context, &id); + printf("id: %"PRId64", %016llX\n", id, id); + for (i=0; i<1000000; i++) + { + result = id_generator_next(&context, &id); + if (result != 0) + { + break; + } + } + printf("id: %"PRId64", %016llX\n", id, id); + + id_generator_destroy(&context); + return 0; +} +