diff --git a/ChangeLog b/ChangeLog index fdf12f3..a9fd660 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2019-06-21 Alibaba Cloud. + * VERSION: 1.6.8 + * IMPROVE: add hypervisor. + * IMPROVE: add key filter in `rump` mode. + * IMPROVE: add prometheus metrics with url: "localhost:$http_profile/metrics" 2019-06-13 Alibaba Cloud. * VERSION: 1.6.7 * IMPROVE: split big key in `rump` mode. diff --git a/README.md b/README.md index 550745c..c25680c 100644 --- a/README.md +++ b/README.md @@ -87,3 +87,9 @@ We also provide some tools for synchronization in Shake series.
Plus, we have a WeChat group so that users can join and discuss, but the group user number is limited. So please add my WeChat number: `vinllen_xingge` first, and I will add you to this group.
+# Thanks +--- +| Username | Mail | +| :------: | :------: | +| ceshihao | davidzheng23@gmail.com | +| wangyiyang | wangyiyang.kk@gmail.com | diff --git a/build.sh b/build.sh index 48e9af0..1284ea6 100755 --- a/build.sh +++ b/build.sh @@ -13,6 +13,9 @@ else fi branch=$branch","$cid +output=./bin/ +rm -rf ${output} + # make sure we're in the directory where the script lives SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" cd "$SCRIPT_DIR" @@ -36,5 +39,16 @@ info=$info","$t echo "[ BUILD RELEASE ]" run_builder='go build -v' -$run_builder -ldflags "-X $info" -o "bin/redis-shake" "./src/redis-shake/main/main.go" +$run_builder -ldflags "-X $info" -o "${output}/redis-shake" "./src/redis-shake/main/main.go" echo "build successfully!" + +# copy scripts +cp scripts/start.sh ${output}/ +cp scripts/stop.sh ${output}/ + +if [ "Linux" == "$(uname -s)" ];then + # hypervisor + gcc -Wall -O3 scripts/hypervisor.c -o ${output}/hypervisor -lpthread +elif [ "Darwin" == "$(uname -s)" ];then + printf "\\nWARNING !!! MacOS doesn't supply hypervisor\\n" +fi diff --git a/scripts/hypervisor.c b/scripts/hypervisor.c new file mode 100644 index 0000000..ca0b605 --- /dev/null +++ b/scripts/hypervisor.c @@ -0,0 +1,260 @@ +/* + * Compilation: + * gcc -Wall -O3 hypervisor.c -o hypervisor + * + * */ + +#include +#include +#include +#include + +#ifdef __linux__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include + + +#define MAXOPT 256 +#define INTERVAL 5 +#define MAXINTERVAL 180 + +#define USAGE() do{ \ + fprintf(stderr, "Usage : %s [--daemon] --exec=\"command arg1 arg2 arg3 ...\"\n", argv[0]); \ +}while(0) + +static char *cmd, *cmdopt[MAXOPT + 1]; +static int daemonize; + +static int parseopt(int argc, char *argv[]); +static int set_nonblock(int fd); +static int getstatus(char *buf, int size, int status); + +static int parseopt(int argc, char *argv[]) +{ + int ch, i; + char *token, *tmpptr, *cmdstr; + + cmdstr = cmd = NULL; + daemonize = 0; + for(i = 0; i < MAXOPT + 1; i++){ + cmdopt[i] = NULL; + } + + struct option long_options[] = { + {"daemon",optional_argument,NULL,'d'}, + {"exec",required_argument,NULL,'e'}, + {0,0,0,0}, + }; + + while((ch=getopt_long(argc, argv, "dec:", long_options, NULL)) != -1) { + switch(ch) + { + case 'e': + if((cmdstr = strdup(optarg)) == NULL ) + return -1; + break; + case 'd': + daemonize = 1; + break; + default: + USAGE(); + return -1; + } + } + + if(cmdstr == NULL){ + USAGE(); + return -1; + } + + for(i = 0;i < MAXOPT + 1;cmdstr = NULL, i++){ + token = strtok_r(cmdstr, " \t\n", &tmpptr); + if(token == NULL){ + break; + } else { + cmdopt[i] = strdup(token); + + if(i == 0){ + cmd = strdup(token); + } + } + } + + if( (cmd == NULL) || (strlen(cmd) == 0) ){ + fprintf(stderr, "Error, cmd should not be empty.\n"); + return -1; + } + + if(i == MAXOPT + 1){ + fprintf(stderr, "Argument too long\n"); + return -1; + } + + cmdopt[i] = NULL; + + return 0; +} + +static int set_nonblock(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) { + return -1; + } + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +static int getstatus(char *buf, int size, int status) +{ + int n, len; + + len = size; + + if(WIFEXITED(status)){ + n = snprintf(buf, len, "- normal termination, exit status = %d\n", WEXITSTATUS(status)); + } else if(WIFSIGNALED(status)) { + n = snprintf(buf, len, "- abnormal termination, signal number = %d%s", + WTERMSIG(status), +#ifdef WCOREDUMP + WCOREDUMP(status) ? "-> core file generated" : ""); +#else + ""); +#endif + } else if(WIFSTOPPED(status)) { + n = snprintf(buf, len, "child stopped, signal number = %d\n", WSTOPSIG(status)); + } + + return n; +} + + + +void go_daemon() { + int fd; + + if (fork() != 0) exit(0); /* parent exits */ + setsid(); /* create a new session */ + + /* Every output goes to /dev/null. If Redis is daemonized but + * * the 'logfile' is set to 'stdout' in the configuration file + * * it will not log at all. */ + if ((fd = open("/tmp/mongo4bls.output", O_RDWR, 0)) != -1) { + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) close(fd); + } +} + + +int main(int argc, char *argv[]) +{ + int ssec = INTERVAL, ret, status; + int first_start = 1; + int pipefd[2], waited, alive, isdaemon; + char buf[1024], info[4096]; + pid_t pid; + time_t now, last = time(NULL); + + if((ret = parseopt(argc, argv)) < 0 ) + exit(ret); + + daemonize ? go_daemon() : 0; + + while(1){ + if(pipe(pipefd) < 0){ + fprintf(stderr, "- make pipe error : %s\n", strerror(errno)); + exit(-1); + } + + if( (ret = set_nonblock(pipefd[0])) < 0 ){ + fprintf(stderr, "- set read nonblock error : %s\n", strerror(errno)); + exit(-1); + } + + if((pid = fork()) < 0){ + fprintf(stderr, "- call fork() error : %s\n", strerror(errno)); + exit(-1); + } else if (pid > 0){ + close(pipefd[1]); + alive = waited = 1; + isdaemon = 0; + while(alive){ + if(waited){ + if(pid != waitpid(pid, &status, 0)){ + sleep(INTERVAL); + continue; + } else { + fprintf(stderr, "- child process[%d] terminated .\n",pid); + if (first_start && (time(NULL)-last)<=5) { + fprintf(stderr,"- child process killed in %ld seconds , may wrong ! exit !\n",(time(NULL)-last)); + exit(-1); + } else + first_start = 0; + waited = 0; + } + } + + ret = read(pipefd[0], buf, sizeof(buf)); + if(ret < 0){ + if(errno == EAGAIN){ + if(isdaemon == 0){ + fprintf(stderr, "- this daemon process has no output !.\n"); + isdaemon = 1; + } + sleep(INTERVAL); + continue; + } else { + fprintf(stderr, "- read pipe error : %s\n", strerror(errno)); + exit(-1); + } + } else if(ret == 0) { + alive = 0; + close(pipefd[0]); + fprintf(stderr, "- read zero from pipe of children.\n"); + if(isdaemon == 0){ + getstatus(info, sizeof(info), status); + fprintf(stderr, "- extra info: %s\n", info); + } else { + strcpy(info, ""); + } + continue; + } else { + fprintf(stderr, " - read pipe return: %d bytes\n", ret); + exit(-1); + } + } + + fprintf(stderr, "- process: \"%s\" exit, restart it\n", cmd); + + sleep(ssec); + + now = time(NULL); + if(now - last > 3600){ + ssec = INTERVAL; + last = now; + } else { + ssec = (ssec << 1) < MAXINTERVAL ? (ssec << 1) : MAXINTERVAL; + } + } else { + close(pipefd[0]); + fprintf(stderr, "- execute: \"%s\"\n", cmd); + if(execvp(cmd, cmdopt) < 0){ + fprintf(stderr, "- execute: \"%s\" error, %s\n", cmd, strerror(errno)); + exit(-1); + } + + } + } + + return 0; +} diff --git a/scripts/start.sh b/scripts/start.sh new file mode 100755 index 0000000..47ce723 --- /dev/null +++ b/scripts/start.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +catalog=$(dirname "$0") + +cd "${catalog}" || exit 1 + +if [ $# != 2 ] ; then + echo "USAGE: $0 [conf] [mode]" + exit 0 +fi + +name="redis-shake" + +if [ "Darwin" == "$(uname -s)" ];then + printf "\\nWARNING !!! MacOs doesn't supply to use this script, please use \"./%s -conf=config_file_name\" manual command to run\\n" "$name" + exit 1 +fi + +./hypervisor --daemon --exec="./$name -conf=$1 -type=$2 1>>$name.output 2>&1" 1>>hypervisor.output 2>&1 diff --git a/scripts/stop.sh b/scripts/stop.sh new file mode 100755 index 0000000..695ef63 --- /dev/null +++ b/scripts/stop.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +#kill -9 "$(cat "$1")" +if [ $# != 1 ] ; then + echo "USAGE: $0 [pid filename which by default is 'redis-shake.pid']" + exit 0 +fi +ppid=$(ps -ef | awk '{if ($2=='`cat $1`') print $3}') +[ -z $ppid ] && echo "[Fail] No process number for $(cat "$1")." && exit 1 +if [ $ppid -eq 1 ];then + kill -9 "$(cat "$1")" +else + kill -9 "$ppid" "$(cat "$1")" +fi diff --git a/src/redis-shake/metric/metric.go b/src/redis-shake/metric/metric.go index 232245e..a5fbd35 100644 --- a/src/redis-shake/metric/metric.go +++ b/src/redis-shake/metric/metric.go @@ -225,7 +225,7 @@ func (m *Metric) GetAvgDelay() interface{} { } func (m *Metric) GetAvgDelayFloat64() float64 { - return m.AvgDelay.Get(false).(float64) + return float64(m.AvgDelay.Get(false).(int64)) } func (m *Metric) AddNetworkFlow(dbSyncerID int, val uint64) { diff --git a/src/redis-shake/metric/variables.go b/src/redis-shake/metric/variables.go index 163cfcd..51aa486 100644 --- a/src/redis-shake/metric/variables.go +++ b/src/redis-shake/metric/variables.go @@ -34,7 +34,10 @@ type MetricRest struct { } func NewMetricRest() []MetricRest { - detailMapList := runner.GetDetailedInfo().([]map[string]interface{}) + var detailMapList []map[string]interface{} + if rawInfo := runner.GetDetailedInfo(); rawInfo != nil { + detailMapList = runner.GetDetailedInfo().([]map[string]interface{}) + } if detailMapList == nil || len(detailMapList) == 0 { return []MetricRest{ {