├── Blink-code-review-doc.pdf ├── src ├── utils │ ├── jvm_cmd.h │ ├── tests │ │ ├── test_agent_cmd.txt │ │ ├── read_named_pipe.c │ │ ├── test_etcd.c │ │ ├── CMakeLists.txt │ │ ├── test_utils.c │ │ ├── test_blockQueue.c │ │ ├── echo_server.c │ │ └── jvm_gc.c │ ├── http_req_queue.h │ ├── etcd.h │ ├── blockQueue.h │ ├── atomic_integer.h │ ├── loadBalancer.h │ ├── tokenizer.h │ ├── utils.h │ ├── log.h │ ├── http_req_queue.c │ ├── tokenizer_fast_req.c │ ├── blockQueue.c │ ├── loadBalancer.c │ ├── log.c │ ├── etcd.c │ ├── utils.c │ ├── jvm_cmd.c │ └── tokenizer.c ├── config.h ├── provider_agent.h ├── playground │ ├── CMakeLists.txt │ ├── test_simd_instructions.c │ ├── backup_tokenizer.md │ ├── test_fast_req_package.c │ └── test_split.c ├── consumer_agent.h ├── main.c ├── provider_agent.c └── consumer_agent.c ├── scripts ├── stop_all.sh ├── start_etcd.sh ├── start_consumer.sh ├── create_docker_network.sh ├── start_provider_large.sh ├── start_provider_small.sh ├── start_provider_medium.sh └── install_libuv.sh ├── .gitmodules ├── python_playgound ├── analysis │ └── time_series_analysis.py ├── inputs │ ├── body1.txt │ └── body0.txt ├── test_split_decode.py ├── test_dubbo_client.py └── test_dubbo_pressure_async.py ├── .gitignore ├── start-agent.sh ├── LICENSES ├── rxi_log_LICENSE ├── jsmn_LICENSE ├── b64_LICENSE ├── uthash_LICENSE └── picohttpparser_LICENSE ├── LICENSE ├── Dockerfile ├── cmake ├── GetGitRevisionDescription.cmake.in ├── FindLibuv.cmake └── GetGitRevisionDescription.cmake ├── CMakeLists.txt └── README.md /Blink-code-review-doc.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RapidsBlink/service-mesh-agent/HEAD/Blink-code-review-doc.pdf -------------------------------------------------------------------------------- /src/utils/jvm_cmd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //return nspid 4 | int init_jvm_comm_socket(int pid); 5 | 6 | int jvm_run_cmd(int nspid, int argc, char** argv); -------------------------------------------------------------------------------- /src/utils/tests/test_agent_cmd.txt: -------------------------------------------------------------------------------- 1 | /agent provider 3000 28000 http://localhost:2379 log.txt 2 | listen dubbo etcd_url 3 | -------------------------------------------------------------------------------- /scripts/stop_all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker stop consumer 4 | docker stop provider-small 5 | docker stop provider-medium 6 | docker stop provider-large 7 | docker stop etcd -------------------------------------------------------------------------------- /scripts/start_etcd.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -d --name etcd --network=benchmarker -v /tmp/logs:/root/logs registry.cn-hangzhou.aliyuncs.com/aliware2018/alpine-etcd 4 | -------------------------------------------------------------------------------- /scripts/start_consumer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -d --name consumer --cpu-period 50000 --cpu-quota 200000 -m 4G --network=benchmarker -v /tmp/logs:/root/logs -p 8087:8087 agent:latest consumer 4 | 5 | docker logs -f consumer -------------------------------------------------------------------------------- /scripts/create_docker_network.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker network create --driver=bridge --subnet=10.10.10.0/24 --gateway=10.10.10.1 -o "com.docker.network.bridge.name"="benchmarker" -o "com.docker.network.bridge.enable_icc"="true" benchmarker -------------------------------------------------------------------------------- /scripts/start_provider_large.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -d --name provider-large --cpu-period 50000 --cpu-quota 90000 -m 6G --network=benchmarker -v /tmp/logs:/root/logs agent:latest provider-large 4 | 5 | docker logs -f provider-large -------------------------------------------------------------------------------- /scripts/start_provider_small.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -d --name provider-small --cpu-period 50000 --cpu-quota 30000 -m 2G --network=benchmarker -v /tmp/logs:/root/logs agent:latest provider-small 4 | 5 | docker logs -f provider-small -------------------------------------------------------------------------------- /scripts/start_provider_medium.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | docker run --rm -d --name provider-medium --cpu-period 50000 --cpu-quota 60000 -m 4G --network=benchmarker -v /tmp/logs:/root/logs agent:latest provider-medium 4 | 5 | docker logs -f provider-medium -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/20/18. 3 | // 4 | 5 | #ifndef AGENT_CONFIG_H 6 | #define AGENT_CONFIG_H 7 | 8 | #define EVENT_LOOP_NUM 2 // consumer 9 | #define P_EVENT_LOOP_NUM 2 // provider 10 | #define HISTORY_WINDOW_SIZE 64 11 | 12 | #endif //AGENT_CONFIG_H 13 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "3rd-deps/picohttpparser"] 2 | path = 3rd-deps/picohttpparser 3 | url = https://github.com/h2o/picohttpparser.git 4 | [submodule "3rd-deps/base64"] 5 | path = 3rd-deps/base64 6 | url = https://github.com/littlstar/b64.c.git 7 | [submodule "3rd-deps/jsmn"] 8 | path = 3rd-deps/jsmn 9 | url = https://github.com/zserge/jsmn.git 10 | [submodule "3rd-deps/uthash"] 11 | path = 3rd-deps/uthash 12 | url = https://github.com/troydhanson/uthash.git 13 | -------------------------------------------------------------------------------- /src/utils/http_req_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct http_req; 6 | typedef struct http_req http_req_t; 7 | 8 | typedef struct { 9 | http_req_t **buffer; 10 | int capacity; 11 | int head, tail; 12 | size_t generation; 13 | }http_req_queue_t; 14 | 15 | http_req_queue_t *init_http_req_queue_t(int default_size); 16 | 17 | int http_req_queue_push(http_req_queue_t* queue, http_req_t *http_req); 18 | 19 | http_req_t* http_req_queue_pull(http_req_queue_t *queue); 20 | -------------------------------------------------------------------------------- /src/utils/etcd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct { 7 | char *ptr; 8 | size_t len; 9 | } res_string; 10 | 11 | void free_string(res_string *s); 12 | 13 | void init_string(res_string *s); 14 | 15 | char *etcd_grant(char *etcd_url, int ttl); 16 | 17 | int etcd_put(char *etcd_url, char *key, char *leaseID); 18 | 19 | int etcd_get(char *etcd_url, char *key, char **server_addr, int *results_size); 20 | 21 | int etcd_keep_alive(char *etcd_url, char *leaseID); -------------------------------------------------------------------------------- /python_playgound/analysis/time_series_analysis.py: -------------------------------------------------------------------------------- 1 | if __name__ == '__main__': 2 | with open('/home/yche/workspace/comp/logs/2018-05-20_22-51/consumer/logs/consumer.log') as ifs: 3 | lines = filter(lambda line: 'diff time:' in line, ifs.readlines()) 4 | lines = map(lambda line: line.rstrip().split('diff time: ')[-1].split(', endpoint number: '), lines) 5 | # print len(lines) 6 | print set(map(lambda lst: lst[1], lines[-23000:])) 7 | print list(filter(lambda lst: int(lst[0]) > 55000, lines[10000:])) 8 | -------------------------------------------------------------------------------- /src/utils/blockQueue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../consumer_agent.h" 5 | 6 | typedef struct { 7 | uv_mutex_t *mutex; 8 | uv_cond_t *cond; 9 | size_t head, tail, max_size; 10 | int tail_ahead; 11 | http_req_t **http_req_array; 12 | } blockQueue; 13 | 14 | blockQueue *init_blockQueue(int size); 15 | 16 | void free_blockQueue(blockQueue *bq); 17 | 18 | int test_full(blockQueue *bq); 19 | 20 | int test_empty(blockQueue *bq); 21 | 22 | int bq_push(blockQueue *bq, http_req_t *http_req); 23 | 24 | http_req_t *bq_pull(blockQueue *bq); -------------------------------------------------------------------------------- /python_playgound/inputs/body1.txt: -------------------------------------------------------------------------------- 1 | interface=com.alibaba.dubbo.performance.demo.provider.IHelloService&method=hash¶meterTypesString=Ljava%2Flang%2FString%3B¶meter=Tbk4ZGqnHQNRM8Wqr65Sxz8K2wnWHhvcaNuAnTn64geI6AnEHB8cCtEg154rqjTqWIXqMdUwysbjwTivtkbLi8qNHWg1Kri58NPcKS3mpe1lZ0bh48dDhgoNRoAkL548LzGKQNiZb2cKX6geL7srwMjbZ1ybLvCNkHFnMd6cpIMMO8mvyAA3LLH3RydyTGEv2JLP6N9lp3xE1YnI8O28TMu0XKeqBaNzpHQDrNDAMSNZn7Ka896JnEiFr1naM5h7fyff1BqXUfA5rZSedkSXQt4RyY9WvmhEeF3eOfapdb34P64uLlJqfNcBpjfKB5sHaeUmDpBI6j18XhxqvdeE2K69dFSdXavGri0aLyeUlCUX3VE5owNt27yeYNH76qHEC0ZJpBW9BnUTo4BuojqhnyfIkdTb7IEl8lyns6uJPJXTSIuZloVqCcZxoqvh9dlj69BIVLaksjhttv2lAuF9raq5ceXkHWrsgfLEvwphvCc5gHAgliG -------------------------------------------------------------------------------- /scripts/install_libuv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #apt-get install libtool m4 automake 4 | 5 | VERSION=1.20.3 6 | 7 | if [[ -z $1 ]] 8 | then 9 | DIR=$PWD/3rd-deps/libuv 10 | else 11 | DIR=$1 12 | fi 13 | 14 | echo "target path: ${DIR}" 15 | # download source 16 | 17 | curl -L https://github.com/libuv/libuv/archive/v${VERSION}.tar.gz -o libuv.tar.gz 18 | 19 | tar xvzf libuv.tar.gz -C /tmp/ >>/dev/null 20 | rm libuv.tar.gz 21 | 22 | cd /tmp/libuv-${VERSION} 23 | 24 | bash autogen.sh 25 | ./configure --prefix=${DIR} >>/dev/null 26 | 27 | make -j >>/dev/null && make install >>/dev/null 28 | 29 | cd ${DIR} 30 | 31 | rm -rf /tmp/libuv-${VERSION} -------------------------------------------------------------------------------- /src/utils/atomic_integer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | typedef struct{ 7 | int value; 8 | uv_mutex_t lock; 9 | }atomic_int_t; 10 | 11 | 12 | atomic_int_t* init_atomic_integer(int init_value){ 13 | atomic_int_t* atom = malloc(sizeof(atomic_int_t)); 14 | uv_mutex_init(&(atom->lock)); 15 | atom->value = init_value; 16 | return atom; 17 | } 18 | 19 | int atomic_add(atomic_int_t *atom, int delta){ 20 | int ret = atom->value; 21 | uv_mutex_lock(&(atom->lock)); 22 | atom->value += delta; 23 | ret = atom->value; 24 | uv_mutex_unlock(&(atom->lock)); 25 | return ret; 26 | } -------------------------------------------------------------------------------- /src/provider_agent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef struct p_tcp_server_struct_t p_tcp_server_t; 6 | 7 | typedef struct { 8 | uv_tcp_t *tcp_conn_1; 9 | uv_tcp_t *tcp_conn_2; 10 | uv_connect_t *dubbo_connection; 11 | p_tcp_server_t *p_tcp_server; 12 | char *raw_buf1; 13 | char *raw_buf2; 14 | size_t raw_buf1_size, raw_buf2_size; 15 | size_t max_buffer_size; 16 | } tcp_relay_t; 17 | 18 | struct p_tcp_server_struct_t { 19 | uv_loop_t *work_event_loop; 20 | uv_tcp_t agent_server; 21 | int tid; 22 | }; 23 | 24 | int provider(char *provider_type, int server_port, int dubbo_port, char *etcd_url); 25 | -------------------------------------------------------------------------------- /python_playgound/inputs/body0.txt: -------------------------------------------------------------------------------- 1 | interface=com.alibaba.dubbo.performance.demo.provider.IHelloService&method=hash¶meterTypesString=Ljava%2Flang%2FString%3B¶meter=qTKV8XvQTKR0Gd8Vu8Ajd6GBS2rCwSx0odTejVNBkL7nRMIDxI1Ur5Vm5cofQEGdZfPGvx49fIjhtv2eiJtR0WH1wGwT7S25bxJjoot2cld44eFCch0JZsyOfQ5IuK3MQQ96tqh0Ps5ircTkipNcni9BftrZMotdGTgdrfmEf80PsXAkPYajNsTkpvis4kiU5HIxeM7hQyv9kf8FoR9jmPPOn2corOGuM3tJ42a4DoWjPlcTpT5wehicYoV50rqpLgx2khKylJd7tiUa3wYtLX8FFtXcdfoqao7PhETK4a6yy2i5Yk9mILE9PItuAzCrkCgA3blNOySd7GWh16gAcxObpkZTiRbbuuGkwyMsoYZP4TV3akWCcatTXpzKGRPsPsgyguhssmcjb5OAocik9ytadGMSqUP8orQu7Tt26HLS8ybSsCWWFgl81bUYY3VupHUS4JXsI6CAGKEg6erezvLQoIDCUCULJDU1LSNybO2JrZFqMlZs0RFdx0TW8KJisGAgA4wTicYv2wCiSNTCJKo -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # libuv binarys 2 | 3rd-deps/libuv/ 3 | 4 | # jetbrains 5 | .idea/ 6 | cmake-build-* 7 | build/ 8 | 9 | # Prerequisites 10 | *.d 11 | 12 | # Object files 13 | *.o 14 | *.ko 15 | *.obj 16 | *.elf 17 | 18 | # Linker output 19 | *.ilk 20 | *.map 21 | *.exp 22 | 23 | # Precompiled Headers 24 | *.gch 25 | *.pch 26 | 27 | # Libraries 28 | *.lib 29 | *.a 30 | *.la 31 | *.lo 32 | 33 | # Shared objects (inc. Windows DLLs) 34 | *.dll 35 | *.so 36 | *.so.* 37 | *.dylib 38 | 39 | # Executables 40 | *.exe 41 | *.out 42 | *.app 43 | *.i*86 44 | *.x86_64 45 | *.hex 46 | 47 | # Debug files 48 | *.dSYM/ 49 | *.su 50 | *.idb 51 | *.pdb 52 | 53 | # Kernel Module Compile Results 54 | *.mod* 55 | *.cmd 56 | .tmp_versions/ 57 | modules.order 58 | Module.symvers 59 | Mkfile.old 60 | dkms.conf 61 | -------------------------------------------------------------------------------- /src/utils/loadBalancer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../config.h" 5 | 6 | struct http_req; 7 | typedef struct http_req http_req_t; 8 | 9 | typedef struct { 10 | int64_t diff_time_sum; 11 | int head; 12 | int tail; 13 | int remain; 14 | int64_t diff_time_history[HISTORY_WINDOW_SIZE]; 15 | } history_diff_time_t; 16 | 17 | typedef struct { 18 | int num_of_ends_; 19 | history_diff_time_t *history_diff_time_lst[16]; 20 | } load_balancer_t; 21 | 22 | load_balancer_t *init_load_balancer(int num_of_ends); 23 | 24 | history_diff_time_t *init_history_time(); 25 | 26 | int select_endpoint_time_based(load_balancer_t *this, http_req_t *http_req); 27 | 28 | void finish_endpoint_task_time_based(load_balancer_t *this, int end_point_idx, http_req_t *http_req); 29 | 30 | -------------------------------------------------------------------------------- /python_playgound/test_split_decode.py: -------------------------------------------------------------------------------- 1 | import re 2 | import urlparse 3 | 4 | with open('inputs/body1.txt') as ifs: 5 | my_str = ifs.readline().rstrip() 6 | 7 | if __name__ == '__main__': 8 | # test split only 9 | print re.split('[=&]', my_str) 10 | 11 | # test split and decode 12 | data = urlparse.parse_qsl(my_str) 13 | print dict(data) 14 | 15 | lst = ['a', 'z', 'A', 'Z', '0', '9', '-', '/', ';', '%', '=', '&'] 16 | print zip(lst, map(hex, map(ord, lst))) 17 | 18 | lst = list(filter(lambda idx: my_str[idx] == '=' or my_str[idx] == '&', xrange(len(my_str)))) 19 | lst.append(len(my_str)) 20 | print lst 21 | 22 | # print hex 23 | print str(list(reversed(map(hex, [2 ** i - 1 for i in xrange(8)])))).replace("'", '') 24 | 25 | print map(len, ["interface", "method", "parameterTypesString", "parameter"]) 26 | -------------------------------------------------------------------------------- /src/utils/tests/read_named_pipe.c: -------------------------------------------------------------------------------- 1 | #include "../log.h" 2 | #include 3 | int main(int argc, char** argv){ 4 | int messages = 0; 5 | char buf[1024]; 6 | 7 | float eden_ratio; 8 | 9 | while (1) 10 | { 11 | FILE *myStream = fopen(argv[1], "r"); 12 | if (myStream == NULL) 13 | { 14 | log_fatal("open named pipe failed"); 15 | return 1; 16 | } 17 | while (fgets(buf, sizeof(buf), myStream) != 0) 18 | { 19 | if(buf[0] == 'T' || buf[0] == 'S') 20 | continue; 21 | sscanf(buf, "%*s%*s%*s%f", &eden_ratio); 22 | printf("%s\n", buf); 23 | printf("eden ratio %f\n", eden_ratio); 24 | messages++; 25 | } 26 | fclose(myStream); 27 | sleep(1); 28 | } 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /src/utils/tokenizer.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/4/18. 3 | // 4 | 5 | #ifndef AGENT_TOKENIZER_H 6 | #define AGENT_TOKENIZER_H 7 | 8 | #include 9 | 10 | int serial_split(const char *str, short len, short *off); 11 | 12 | int simd_split_efficient(char *str, short len, short *off); 13 | 14 | int generate_body(char *res, char *str_buffer, short *off, short len_off); 15 | 16 | void generate_header(char *res, int body_len, int64_t req_id); 17 | 18 | int generate_res_in_place(char *res, char *str, short len, int64_t req_id); 19 | 20 | int generate_res_in_place_hard_code(char *res, char *str, short len, int64_t req_id); 21 | 22 | // fast req pacakge 23 | int generate_fast_req_package_in_place(char *res, char *str, short len, int64_t req_id); 24 | 25 | int generate_dubbo_package(char *res, char *fast_req_package, int fast_req_len); 26 | #endif //AGENT_TOKENIZER_H 27 | -------------------------------------------------------------------------------- /start-agent.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ETCD_HOST=etcd 4 | ETCD_PORT=2379 5 | ETCD_URL=http://$ETCD_HOST:$ETCD_PORT 6 | 7 | echo ETCD_URL = $ETCD_URL 8 | 9 | lscpu 10 | 11 | if [[ "$1" == "consumer" ]]; then 12 | echo "Starting consumer agent..." 13 | agent consumer 20000 1234 $ETCD_URL /root/logs/consumer.log 14 | elif [[ "$1" == "provider-small" ]]; then 15 | echo "Starting small provider agent..." 16 | agent provider-small 30000 20880 $ETCD_URL /root/logs/provider-s.log 17 | elif [[ "$1" == "provider-medium" ]]; then 18 | echo "Starting medium provider agent..." 19 | agent provider-medium 30000 20880 $ETCD_URL /root/logs/provider-m.log 20 | elif [[ "$1" == "provider-large" ]]; then 21 | echo "Starting large provider agent..." 22 | agent provider-large 30000 20880 $ETCD_URL /root/logs/provider-l.log 23 | else 24 | echo "Unrecognized arguments, exit." 25 | exit 1 26 | fi 27 | -------------------------------------------------------------------------------- /src/utils/tests/test_etcd.c: -------------------------------------------------------------------------------- 1 | 2 | #include "../etcd.h" 3 | #include "../log.h" 4 | #include "../utils.h" 5 | #include "../../../3rd-deps/base64/b64.h" 6 | #include 7 | 8 | int main(int argc, char *argv[]) { 9 | 10 | char *id = etcd_grant(argv[1], 600); 11 | log_info("ID: %s", id); 12 | etcd_put(argv[1], "/dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/192.168.1.2:8200", id); 13 | for (int i = 0; i < 3; i++) { 14 | sleep(4);//sleep 4s 15 | etcd_keep_alive(argv[1], id); 16 | } 17 | 18 | char **servers; 19 | servers = (char**) malloc(sizeof(char*)* 100); 20 | int servers_size = 0; 21 | etcd_get(argv[1], "/dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/", servers, &servers_size); 22 | 23 | for (int i = 0; i < servers_size; i++) { 24 | log_info("get key: %s", servers[i]); 25 | } 26 | 27 | return 0; 28 | } -------------------------------------------------------------------------------- /src/playground/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(test_split test_split.c ../utils/tokenizer.c ../utils/utils.c ../utils/log.c ../utils/jvm_cmd.c) 2 | target_compile_options(test_split PRIVATE -march=nehalem -O2) 3 | target_link_libraries(test_split ${LIBUV_LIBRARIES}) 4 | 5 | add_executable(test_split_avx test_split.c ../utils/tokenizer.c ../utils/utils.c ../utils/log.c ../utils/jvm_cmd.c) 6 | target_compile_options(test_split_avx PRIVATE -march=haswell -O2) 7 | target_link_libraries(test_split_avx ${LIBUV_LIBRARIES}) 8 | 9 | add_executable(test_simd test_simd_instructions.c ../utils/utils.c ../utils/log.c ../utils/jvm_cmd.c) 10 | target_compile_options(test_simd PRIVATE -march=haswell -O2) 11 | target_link_libraries(test_simd ${LIBUV_LIBRARIES}) 12 | 13 | add_executable(test_fast_req_package test_fast_req_package.c ../utils/tokenizer_fast_req.c ../utils/log.c ../utils/utils.c ../utils/tokenizer.c ../utils/jvm_cmd.c) 14 | target_compile_options(test_fast_req_package PRIVATE -march=nehalem -O2) 15 | target_link_libraries(test_fast_req_package ${LIBUV_LIBRARIES}) 16 | 17 | -------------------------------------------------------------------------------- /src/utils/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //input: /dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/[S,M,L]:192.168.1.2:8200 4 | void str_to_server_uri(char *str, char *server_addr, int *port, int *scale); 5 | 6 | char *hostIPaddr(char *eth_name); 7 | 8 | int four_char_to_int(char *length); 9 | 10 | void int_to_four_char(int32_t integer, char *bytes); 11 | 12 | void long_to_8bytes(int64_t integer, void *bytes); 13 | 14 | int64_t bytes8_to_long(void *bytes); 15 | 16 | float unpackFloat(const void *buf); 17 | 18 | int packFloat(void *buf, float x); 19 | 20 | void int_to_string(char* str, int num); 21 | 22 | void get_cpu_info(); 23 | 24 | int64_t get_wall_time(); 25 | 26 | //this is a function from mysql-server 27 | //https://github.com/mysql/mysql-server/blob/5.7/strings/int2str.c 28 | char *int10_to_str(long int val,char *dst,int radix); 29 | 30 | char *resolveToIp4Str(char *hostname); 31 | 32 | int manual_jvm_gc(int java_pid); 33 | 34 | void save_to_binary_file(char* filename, char* buf, int size); 35 | 36 | void increase_long_by_one(unsigned char bytes[8]); -------------------------------------------------------------------------------- /LICENSES/rxi_log_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 rxi 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /LICENSES/jsmn_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Serge A. Zaitsev 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lipeng WANG and Yulin CHE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSES/b64_LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Little Star Media, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/playground/test_simd_instructions.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/4/18. 3 | // 4 | 5 | #include 6 | #include // AVX 7 | 8 | #include "../utils/utils.h" 9 | 10 | void test_avx_tzcnt() { 11 | int x = 1 << 8 | 1 << 3; 12 | printf("x:%d, tz_cnt of x:%d\n", x, __tzcnt_u32(x)); 13 | printf("x:%d, pop_cnt of x:%d\n", x, _mm_popcnt_u32(x)); 14 | 15 | x = 0; 16 | printf("x:%d, tz_cnt of x:%d\n", x, __tzcnt_u32(x)); 17 | printf("x:%d, pop_cnt of x:%d\n", x, _mm_popcnt_u32(x)); 18 | } 19 | 20 | void test_long_8_byte_covnert() { 21 | char bytes[8]; 22 | int64_t num = 1234; 23 | long_to_8bytes(num, bytes); 24 | for (int i = 0; i < 8; i++) { 25 | printf("0x%x,", bytes[i] & 0xff); 26 | } 27 | printf("\n%ld", bytes8_to_long(bytes)); 28 | } 29 | 30 | void test_int_4_byte_covnert() { 31 | char bytes[4]; 32 | int32_t num = 299; 33 | int_to_four_char(num, bytes); 34 | for (int i = 0; i < 4; i++) { 35 | printf("0x%x,", bytes[i] & 0xff); 36 | } 37 | printf("\n%d\n", four_char_to_int(bytes)); 38 | } 39 | 40 | int main() { 41 | test_avx_tzcnt(); 42 | test_int_4_byte_covnert(); 43 | test_long_8_byte_covnert(); 44 | } -------------------------------------------------------------------------------- /LICENSES/uthash_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2005-2014, Troy D. Hanson http://troydhanson.github.com/uthash/ 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 11 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 12 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 13 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 14 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 15 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 16 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 17 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 18 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 19 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 20 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 21 | 22 | -------------------------------------------------------------------------------- /src/consumer_agent.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../3rd-deps/uthash/include/uthash.h" 6 | #include "utils/loadBalancer.h" 7 | 8 | #define HASH_SLOT_NUM (8192) 9 | 10 | struct tcp_server_struct; 11 | typedef struct tcp_server_struct tcp_server_t; 12 | 13 | typedef struct http_req { 14 | uv_tcp_t *client;//this is the connection come from consumer 15 | 16 | char *raw_array; 17 | size_t raw_array_size; 18 | size_t raw_array_max_size; 19 | int request_length; 20 | int content_length; 21 | int64_t req_id; 22 | 23 | char *response_package; 24 | tcp_server_t *tcp_server; 25 | 26 | int64_t ts_req_beg; 27 | } http_req_t; 28 | 29 | typedef struct { 30 | int which; 31 | char *raw_array; 32 | size_t size_of_raw_array; 33 | size_t raw_array_max_length; 34 | uv_tcp_t *tcp_stream; 35 | uv_connect_t *conn; 36 | int conn_status; 37 | tcp_server_t *tcp_server; 38 | } endpoint_server_t; 39 | 40 | struct tcp_server_struct { 41 | uv_loop_t *work_event_loop; 42 | uv_tcp_t server; 43 | int endpoint_connection_count; 44 | int64_t req_id;//uint64 45 | endpoint_server_t *endpoint_servers[16]; 46 | http_req_t *http_req_dict[HASH_SLOT_NUM]; 47 | }; 48 | 49 | int consumer(int server_port, char *etcd_url); 50 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Builder container 2 | FROM gcc:8.1.0 AS gcc_builder 3 | 4 | COPY . /root/workspace/agent 5 | WORKDIR /root/workspace/agent 6 | RUN apt-get update -y && apt-get install -y cmake libcurl4-openssl-dev && \ 7 | ./scripts/install_libuv.sh /usr/local/libuv && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release -DenableLOG=OFF -Dtest=OFF .. \ 8 | && make -j && lscpu 9 | 10 | 11 | FROM registry.cn-hongkong.aliyuncs.com/alicontest18/demo-services AS builder 12 | 13 | # Runner container 14 | FROM registry.cn-hangzhou.aliyuncs.com/aliware2018/debian-jdk8 15 | 16 | COPY --from=builder /root/workspace/services/mesh-provider/target/mesh-provider-1.0-SNAPSHOT.jar /root/dists/mesh-provider.jar 17 | COPY --from=builder /root/workspace/services/mesh-consumer/target/mesh-consumer-1.0-SNAPSHOT.jar /root/dists/mesh-consumer.jar 18 | COPY --from=builder /usr/local/bin/docker-entrypoint.sh /usr/local/bin 19 | 20 | 21 | COPY --from=gcc_builder /usr/local/libuv /usr/local/libuv 22 | COPY --from=gcc_builder /root/workspace/agent/build/agent /usr/local/bin 23 | COPY start-agent.sh /usr/local/bin 24 | 25 | RUN set -ex && mkdir -p /root/logs && apt-get update && apt-get install -y nano procps libcurl4-openssl-dev && apt-get clean 26 | 27 | ENV LD_LIBRARY_PATH /usr/local/libuv/lib:${LD_LIBRARY_PATH} 28 | 29 | ENTRYPOINT ["docker-entrypoint.sh"] 30 | -------------------------------------------------------------------------------- /LICENSES/picohttpparser_LICENSE: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2009-2014 Kazuho Oku, Tokuhiro Matsuno, Daisuke Murase, 3 | * Shigeo Mitsunari 4 | * 5 | * The software is licensed under either the MIT License (below) or the Perl 6 | * license. 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to 10 | * deal in the Software without restriction, including without limitation the 11 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 12 | * sell copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 24 | * IN THE SOFTWARE. 25 | */ 26 | -------------------------------------------------------------------------------- /cmake/GetGitRevisionDescription.cmake.in: -------------------------------------------------------------------------------- 1 | # 2 | # Internal file for GetGitRevisionDescription.cmake 3 | # 4 | # Requires CMake 2.6 or newer (uses the 'function' command) 5 | # 6 | # Original Author: 7 | # 2009-2010 Ryan Pavlik 8 | # http://academic.cleardefinition.com 9 | # Iowa State University HCI Graduate Program/VRAC 10 | # 11 | # Copyright Iowa State University 2009-2010. 12 | # Distributed under the Boost Software License, Version 1.0. 13 | # (See accompanying file LICENSE_1_0.txt or copy at 14 | # http://www.boost.org/LICENSE_1_0.txt) 15 | 16 | set(HEAD_HASH) 17 | 18 | file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) 19 | 20 | string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) 21 | if(HEAD_CONTENTS MATCHES "ref") 22 | # named branch 23 | string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") 24 | if(EXISTS "@GIT_DIR@/${HEAD_REF}") 25 | configure_file("@GIT_DIR@/${HEAD_REF}" "@GIT_DATA@/head-ref" COPYONLY) 26 | else() 27 | configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) 28 | file(READ "@GIT_DATA@/packed-refs" PACKED_REFS) 29 | if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") 30 | set(HEAD_HASH "${CMAKE_MATCH_1}") 31 | endif() 32 | endif() 33 | else() 34 | # detached HEAD 35 | configure_file("@GIT_DIR@/HEAD" "@GIT_DATA@/head-ref" COPYONLY) 36 | endif() 37 | 38 | if(NOT HEAD_HASH) 39 | file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) 40 | string(STRIP "${HEAD_HASH}" HEAD_HASH) 41 | endif() 42 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #include "utils/log.h" 7 | #include "consumer_agent.h" 8 | #include "provider_agent.h" 9 | 10 | //argv[0]->program name, argv[1]->type(consumer agent or provider agent) 11 | //argv[2]->server port, argv[3]->dubbo.protocol.port, argv[4]->etcd_url, argv[5]->log file 12 | int main(int argc, char *argv[]) { 13 | if (argc < 6) { 14 | log_fatal("invalid arguments, exiting."); 15 | return -1; 16 | } 17 | 18 | //set log file descriptor 19 | #ifdef USE_LOG 20 | FILE *log_f; 21 | log_f = fopen(argv[5], "a+"); 22 | log_set_fp(log_f); 23 | #endif 24 | 25 | #ifdef GIT_SHA1 26 | log_info("GIT VERSION: %s", GIT_SHA1); 27 | #endif 28 | 29 | log_info("agent started, type: %s", argv[1]); 30 | 31 | //extract arguments 32 | int server_port = 0, dubbo_port = 0; 33 | char *etcd_url; 34 | server_port = atoi(argv[2]); 35 | dubbo_port = atoi(argv[3]); 36 | etcd_url = argv[4]; 37 | log_info("arguments: server_port->%d, dubbo_port->%d, etcd_url->%s", server_port, dubbo_port, etcd_url); 38 | 39 | if (strcmp(argv[1], "consumer") == 0) { 40 | consumer(server_port, etcd_url); 41 | } else if (strncmp(argv[1], "provider", 8) == 0) { 42 | provider(argv[1], server_port, dubbo_port, etcd_url); 43 | } else { 44 | log_fatal("invalid agent type: %s", argv[1]); 45 | } 46 | 47 | #ifdef USE_LOG 48 | fclose(log_f); 49 | #endif 50 | return 0; 51 | } -------------------------------------------------------------------------------- /src/utils/log.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017 rxi 3 | * 4 | * This library is free software; you can redistribute it and/or modify it 5 | * under the terms of the MIT license. See `log.c` for details. 6 | */ 7 | 8 | #ifndef LOG_H 9 | #define LOG_H 10 | 11 | #ifdef USE_LOG 12 | 13 | #include 14 | #include 15 | 16 | #define LOG_VERSION "0.1.0" 17 | 18 | typedef void (*log_LockFn)(void *udata, int lock); 19 | 20 | enum { 21 | LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR, LOG_FATAL 22 | }; 23 | 24 | #define log_trace(...) log_log(LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) 25 | #define log_debug(...) log_log(LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) 26 | #define log_info(...) log_log(LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) 27 | #define log_warn(...) log_log(LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) 28 | #define log_error(...) log_log(LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) 29 | #define log_fatal(...) log_log(LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) 30 | 31 | void log_set_udata(void *udata); 32 | 33 | void log_set_lock(log_LockFn fn); 34 | 35 | void log_set_fp(FILE *fp); 36 | 37 | void log_set_level(int level); 38 | 39 | void log_set_quiet(int enable); 40 | 41 | void log_log(int level, const char *file, int line, const char *fmt, ...); 42 | 43 | #else //use log 44 | #define log_trace(...) 45 | #define log_debug(...) 46 | #define log_info(...) 47 | #define log_warn(...) 48 | #define log_error(...) 49 | #define log_fatal(...) 50 | #endif //use log 51 | #endif 52 | -------------------------------------------------------------------------------- /src/playground/backup_tokenizer.md: -------------------------------------------------------------------------------- 1 | ```cpp 2 | int avx_split_baseline(char *str, short len, short *off) { 3 | #ifdef DEBUG 4 | assert(len > 0 && len + 16 < 32767); 5 | #endif 6 | memset(str + len, 0, 16); 7 | off[0] = -1; 8 | for (short i = 0, next = 1;;) { 9 | // 1st: advance fo find first '=' 10 | while (true) { 11 | __m128i pivot_u = _mm_set1_epi8('='); 12 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 13 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 14 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 15 | mask |= 1 << 16; 16 | int advance = __tzcnt_u32(mask); 17 | 18 | i += advance; 19 | if (advance != 16) { break; } 20 | } 21 | off[next] = i; 22 | next++; 23 | 24 | // 2nd: advance to find first '&' 25 | while (true) { 26 | __m128i pivot_u = _mm_set1_epi8('&'); 27 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 28 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 29 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 30 | mask |= 1 << 16; 31 | int advance = __tzcnt_u32(mask); 32 | 33 | i += advance; 34 | if (advance != 16 || i >= len) { break; } 35 | } 36 | off[next] = i < len ? i : len; 37 | next++; 38 | 39 | if (i >= len) { return next; } 40 | } 41 | } 42 | ``` -------------------------------------------------------------------------------- /src/utils/http_req_queue.c: -------------------------------------------------------------------------------- 1 | #include "http_req_queue.h" 2 | #include 3 | 4 | http_req_queue_t *init_http_req_queue_t(int default_size) { 5 | http_req_queue_t *queue = malloc(sizeof(http_req_queue_t)); 6 | queue->buffer = malloc(sizeof(http_req_t *) * default_size); 7 | queue->head = queue->tail = 0; 8 | queue->capacity = default_size; 9 | queue->generation = 0; 10 | return queue; 11 | } 12 | 13 | int _http_req_queue_test_empty(http_req_queue_t *queue){ 14 | if(queue->generation == 0 && queue->head == queue->tail) 15 | return 1; 16 | return 0; 17 | } 18 | int _http_req_queue_test_full(http_req_queue_t *queue){ 19 | if(queue->generation == 1 && queue->head == queue->tail) 20 | return 1; 21 | return 0; 22 | } 23 | 24 | int http_req_queue_push(http_req_queue_t *queue, http_req_t *http_req) { 25 | if(_http_req_queue_test_full(queue)){ 26 | return 1;//if full, drop 27 | } 28 | queue->buffer[queue->tail] = http_req; 29 | queue->tail++; 30 | if(queue->tail > queue->capacity){ 31 | queue->tail = 0; 32 | queue->generation = 1; 33 | } 34 | return 0; 35 | } 36 | 37 | http_req_t *http_req_queue_pull(http_req_queue_t *queue) { 38 | if(_http_req_queue_test_empty(queue)) 39 | return NULL; 40 | http_req_t *req = queue->buffer[queue->head]; 41 | queue->head++; 42 | if(queue->head > queue->capacity){ 43 | queue->head = 0; 44 | queue->generation = 0; 45 | } 46 | return req; 47 | } 48 | -------------------------------------------------------------------------------- /cmake/FindLibuv.cmake: -------------------------------------------------------------------------------- 1 | #============================================================================= 2 | # Copyright 2016 The Luvit Authors. All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | #============================================================================= 16 | # Locate libuv library 17 | # This module defines 18 | # LIBUV_FOUND, if false, do not try to link to libuv 19 | # LIBUV_LIBRARIES 20 | # LIBUV_INCLUDE_DIR, where to find uv.h 21 | 22 | FIND_PATH(LIBUV_INCLUDE_DIR NAMES uv.h 23 | HINTS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-deps/libuv/include /usr/local/libuv/include /usr/include) 24 | FIND_LIBRARY(LIBUV_LIBRARIES NAMES uv libuv HINTS ${CMAKE_CURRENT_SOURCE_DIR}/3rd-deps/libuv/lib /usr/local/libuv/lib /usr/lib) 25 | 26 | if(WIN32) 27 | list(APPEND LIBUV_LIBRARIES iphlpapi) 28 | list(APPEND LIBUV_LIBRARIES psapi) 29 | list(APPEND LIBUV_LIBRARIES userenv) 30 | list(APPEND LIBUV_LIBRARIES ws2_32) 31 | endif() 32 | 33 | INCLUDE(FindPackageHandleStandardArgs) 34 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBUV DEFAULT_MSG LIBUV_LIBRARIES LIBUV_INCLUDE_DIR) 35 | -------------------------------------------------------------------------------- /src/utils/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Threads REQUIRED) 2 | add_executable(test_blockQueue test_blockQueue.c ../blockQueue.h ../blockQueue.c) 3 | target_link_libraries(test_blockQueue ${LIBUV_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) 4 | 5 | 6 | add_executable(test_etcd test_etcd.c ../etcd.c ../etcd.h ../log.h ../log.c ../../../3rd-deps/jsmn/jsmn.c 7 | ../../../3rd-deps/jsmn/jsmn.h ../../../3rd-deps/base64/b64.h ../../../3rd-deps/base64/decode.c 8 | ../../../3rd-deps/base64/encode.c ../utils.c ../jvm_cmd.c ../jvm_cmd.h) 9 | target_compile_options(test_etcd PRIVATE -g -O0) 10 | target_link_libraries(test_etcd ${CURL_LIBRARIES} ${LIBUV_LIBRARIES}) 11 | 12 | 13 | 14 | add_executable(test_utils test_utils.c ../etcd.h ../log.h ../log.c ../../../3rd-deps/jsmn/jsmn.c 15 | ../../../3rd-deps/jsmn/jsmn.h ../../../3rd-deps/base64/b64.h ../../../3rd-deps/base64/decode.c 16 | ../../../3rd-deps/base64/encode.c ../utils.c ../jvm_cmd.c ../jvm_cmd.h) 17 | target_compile_options(test_utils PRIVATE -g -O0) 18 | target_link_libraries(test_utils ${CURL_LIBRARIES} ${LIBUV_LIBRARIES}) 19 | 20 | 21 | add_executable(echo_server echo_server.c ../etcd.h ../log.h ../log.c ) 22 | target_compile_options(echo_server PRIVATE -g -O0) 23 | target_link_libraries(echo_server ${LIBUV_LIBRARIES}) 24 | 25 | add_executable(read_named_pipe read_named_pipe.c ../etcd.h ../log.h ../log.c ) 26 | target_compile_options(read_named_pipe PRIVATE -g -O0) 27 | target_link_libraries(read_named_pipe ${LIBUV_LIBRARIES}) 28 | 29 | add_executable(jvm_gc jvm_gc.c ../etcd.h ../log.h ../log.c ) 30 | target_compile_options(jvm_gc PRIVATE -g -O0) 31 | target_link_libraries(jvm_gc ${LIBUV_LIBRARIES}) -------------------------------------------------------------------------------- /src/utils/tests/test_utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "../etcd.h" 6 | #include "../log.h" 7 | #include "../utils.h" 8 | 9 | 10 | int main(int argc, char *argv[]) { 11 | 12 | char *addr = hostIPaddr("eth0"); 13 | if (strlen(addr) == 0) 14 | addr = hostIPaddr("eno1"); 15 | log_info("%s", addr); 16 | 17 | char int_s[40]; 18 | 19 | log_info("wall time:%lld", get_wall_time()); 20 | int_to_string(int_s, 123); 21 | log_info("wall time:%lld", get_wall_time()); 22 | 23 | log_info("%s", int_s); 24 | 25 | log_info("wall time:%lld", get_wall_time()); 26 | usleep(1000); 27 | log_info("wall time:%lld", get_wall_time()); 28 | log_info("max int64: %lld:", INT64_MAX); 29 | 30 | log_info("ip of www.notfound.me: %s", resolveToIp4Str("www.notfound.me")); 31 | 32 | char buf[100]; 33 | packFloat(buf, 0.1212); 34 | log_info("unpack float %f", unpackFloat(buf)); 35 | 36 | static const char *method_detail_values_one = "\n{\"path\":\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"}\n"; 37 | log_info("str length %d", strlen(method_detail_values_one)); 38 | 39 | unsigned char integer[8] = {0, 0, 0, 0, 0, 0, 0, 0}; 40 | for (int i = 0; i < 1024; i++) { 41 | increase_long_by_one(integer); 42 | // log_info("interger = %lld", *((uint64_t *) integer)); 43 | assert(*((uint64_t *) integer) == i + 1); 44 | } 45 | 46 | for(int i = 0 ; i < 1024; i ++) { 47 | int length = i; 48 | int_to_four_char(length, buf); 49 | assert(four_char_to_int(buf) == length); 50 | } 51 | 52 | for(int i = 0 ; i < 1024; i ++) { 53 | int64_t length = i; 54 | long_to_8bytes(length, buf); 55 | assert(bytes8_to_long(buf) == length); 56 | } 57 | 58 | return 0; 59 | } -------------------------------------------------------------------------------- /src/utils/tests/test_blockQueue.c: -------------------------------------------------------------------------------- 1 | #include "../blockQueue.h" 2 | #include "../../consumer_agent.h" 3 | #include 4 | #include 5 | 6 | void *func1(void *bqi) { 7 | blockQueue *bq = (blockQueue *) bqi; 8 | 9 | int xor = 0xef; 10 | for (int i = 0; i < 5000; i++) { 11 | http_req_t *q = malloc(sizeof(http_req_t)); 12 | q->request_length = 8 ^ xor; 13 | int r = bq_push(bq, q); 14 | if (r != 0) { 15 | fprintf(stderr, "block queue is full"); 16 | } 17 | usleep(10); 18 | } 19 | return NULL; 20 | } 21 | 22 | void *func2(void *bqi) { 23 | blockQueue *bq = (blockQueue *) bqi; 24 | int xor = 0xfe; 25 | for (int i = 0; i < 5000; i++) { 26 | http_req_t *q = malloc(sizeof(http_req_t)); 27 | q->request_length = 8 ^ xor; 28 | int r = bq_push(bq, q); 29 | if (r != 0) { 30 | fprintf(stderr, "block queue is full\n"); 31 | } 32 | usleep(10); 33 | } 34 | return NULL; 35 | } 36 | 37 | void *func3(void *bqi) { 38 | blockQueue *bq = (blockQueue *) bqi; 39 | 40 | for (int i = 0; i < 10000; i++) { 41 | http_req_t *req = bq_pull(bq); 42 | if ((req->request_length ^ 0xef) == 8 || (req->request_length ^ 0xfe) == 8) { 43 | //fprintf(stderr, "ok\n"); 44 | } else { 45 | fprintf(stderr, "verify error\n"); 46 | } 47 | } 48 | 49 | return NULL; 50 | 51 | } 52 | 53 | int main() { 54 | 55 | blockQueue *bq = init_blockQueue(5); 56 | 57 | pthread_t p1, p2, p3; 58 | pthread_create(&p3, NULL, func1, (void *) bq); 59 | pthread_create(&p1, NULL, func2, (void *) bq); 60 | pthread_create(&p2, NULL, func3, (void *) bq); 61 | 62 | 63 | pthread_join(p1, NULL); 64 | pthread_join(p2, NULL); 65 | pthread_join(p3, NULL); 66 | // func1(bq); 67 | // func2(bq); 68 | // func3(bq); 69 | return 0; 70 | } -------------------------------------------------------------------------------- /src/utils/tokenizer_fast_req.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/16/18. 3 | // 4 | 5 | #include 6 | 7 | #include "tokenizer.h" 8 | #include "utils.h" 9 | 10 | extern const char header_bytes[]; 11 | 12 | const char *const method_detail_path = "\n{\"path\":\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"}\n"; 13 | static const char *method_detail_values_one = "\"2.0.1\"\n\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"\nnull\n\"hash\"\n\"Ljava/lang/String;\"\n"; 14 | 15 | void generate_fast_req_package_header(char *res, int body_len, int64_t request_id) { 16 | // 1st: length (4 bytes) 17 | int_to_four_char(body_len, res); 18 | // 2nd: left empty (2 bytes), for serialization method and service type id 19 | // 3rd: req id (8 bytes) 20 | long_to_8bytes(request_id, res + 6); 21 | } 22 | 23 | // assume res allocated, return size 24 | int generate_fast_req_package_in_place(char *res, char *str, short len, int64_t req_id) { 25 | int body_len = len - 136; 26 | //log_info("body length %d", body_len); 27 | memcpy(res + 14, str + 136, body_len); 28 | generate_fast_req_package_header(res, body_len, req_id); 29 | return body_len + 14; 30 | } 31 | 32 | int generate_dubbo_package(char *res, char *fast_req_package, int fast_req_len) { 33 | // 1st: magic, event, status (4bytes), req id (8 bytes) 34 | memcpy(res, header_bytes, 4); 35 | memcpy(res + 4, fast_req_package + 6, 8); 36 | 37 | // 2nd: body 38 | memcpy(res + 16, method_detail_values_one, 101 * sizeof(char)); 39 | int offset = 16 + 101; 40 | 41 | res[offset] = '\"'; 42 | offset++; 43 | memcpy(res + offset, fast_req_package + 14, fast_req_len - 14); 44 | offset += fast_req_len - 14; 45 | res[offset] = '\"'; 46 | offset++; 47 | 48 | memcpy(res + offset, method_detail_path, 70); 49 | offset += 70; 50 | 51 | // 3rd: add the body length header 52 | int_to_four_char(offset - 16, res + 12); 53 | return offset; 54 | } -------------------------------------------------------------------------------- /src/utils/blockQueue.c: -------------------------------------------------------------------------------- 1 | #include "blockQueue.h" 2 | 3 | #include 4 | 5 | blockQueue *init_blockQueue(int size) { 6 | blockQueue *bq = (blockQueue *) malloc(sizeof(blockQueue)); 7 | bq->mutex = malloc(sizeof(uv_mutex_t)); 8 | bq->cond = malloc(sizeof(uv_cond_t)); 9 | uv_mutex_init(bq->mutex); 10 | uv_cond_init(bq->cond); 11 | bq->http_req_array = (http_req_t **) malloc(sizeof(http_req_t*) * size); 12 | bq->head = bq->tail = 0; 13 | bq->max_size = (size_t) size; 14 | bq->tail_ahead = 0; 15 | return bq; 16 | } 17 | 18 | void free_blockQueue(blockQueue *bq) { 19 | if (bq != NULL) { 20 | uv_cond_destroy(bq->cond); 21 | uv_mutex_destroy(bq->mutex); 22 | free(bq->cond); 23 | free(bq->mutex); 24 | free(bq->http_req_array); 25 | free(bq); 26 | } 27 | } 28 | 29 | int test_full(blockQueue *bq) { 30 | if (bq->head == bq->tail && bq->tail_ahead == 1) { 31 | return 1; 32 | } 33 | return 0; 34 | } 35 | 36 | int test_empty(blockQueue *bq) { 37 | if (bq->head == bq->tail && bq->tail_ahead == 0) { 38 | return 1; 39 | } 40 | return 0; 41 | } 42 | 43 | //return 1 if full, 0 if success 44 | int bq_push(blockQueue *bq, http_req_t *http_req) { 45 | uv_mutex_lock(bq->mutex); 46 | if (test_full(bq)) { 47 | uv_mutex_unlock(bq->mutex); 48 | return 1;// drop if full 49 | } 50 | bq->http_req_array[bq->tail] = http_req; 51 | bq->tail++; 52 | if (bq->tail == bq->max_size) { 53 | bq->tail = 0; 54 | bq->tail_ahead = 1; 55 | } 56 | uv_mutex_unlock(bq->mutex); 57 | uv_cond_signal(bq->cond);//signal we have one new element 58 | return 0; 59 | } 60 | 61 | http_req_t *bq_pull(blockQueue *bq) { 62 | http_req_t *req = NULL; 63 | uv_mutex_lock(bq->mutex); 64 | while (test_empty(bq)) { 65 | uv_cond_wait(bq->cond, bq->mutex); 66 | } 67 | 68 | req = bq->http_req_array[bq->head]; 69 | bq->head++; 70 | if (bq->head == bq->max_size) { 71 | bq->head = 0; 72 | bq->tail_ahead = 0; 73 | } 74 | uv_mutex_unlock(bq->mutex); 75 | return req; 76 | } -------------------------------------------------------------------------------- /src/playground/test_fast_req_package.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/16/18. 3 | // 4 | 5 | #include 6 | #include 7 | 8 | #include "../utils/log.h" 9 | #include "../utils/tokenizer.h" 10 | #include "../utils/utils.h" 11 | 12 | const char *my_str = "interface=com.alibaba.dubbo.performance.demo.provider.IHelloService&" 13 | "method=hash¶meterTypesString=Ljava%2Flang%2FString%3B" 14 | "¶meter=Tbk4ZGqnHQNRM8Wqr65Sxz8K2wnWHhvcaNuAnTn64geI6AnEHB8cCtEg154" 15 | "rqjTqWIXqMdUwysbjwTivtkbLi8qNHWg1Kri58NPcKS3mpe1lZ0bh48dDhgoNRoAkL548Lz" 16 | "GKQNiZb2cKX6geL7srwMjbZ1ybLvCNkHFnMd6cpIMMO8mvyAA3LLH3RydyTGEv2JLP6N9lp" 17 | "3xE1YnI8O28TMu0XKeqBaNzpHQDrNDAMSNZn7Ka896JnEiFr1naM5h7fyff1BqXUfA5rZSed" 18 | "kSXQt4RyY9WvmhEeF3eOfapdb34P64uLlJqfNcBpjfKB5sHaeUmDpBI6j18XhxqvdeE2K69dFS" 19 | "dXavGri0aLyeUlCUX3VE5owNt27yeYNH76qHEC0ZJpBW9BnUTo4BuojqhnyfIkdTb7IEl8lyns" 20 | "6uJPJXTSIuZloVqCcZxoqvh9dlj69BIVLaksjhttv2lAuF9raq5ceXkHWrsgfLEvwphvCc5gHAgliG"; 21 | 22 | int main() { 23 | // 1st: test encoding middle package 24 | char *res = malloc(4096); 25 | char *my_str_buf = malloc(4096); 26 | strcpy(my_str_buf, my_str); 27 | 28 | int package_size = generate_fast_req_package_in_place(res, my_str_buf, (short) strlen(my_str), 1234678901); 29 | // body len, reserved 2bytes, req id, string 30 | log_info("pacakge size: %d", package_size); 31 | log_info("packcage info: (%d, %ld, %.*s)", four_char_to_int(res + 0), bytes8_to_long(res + 4 + 2), 32 | four_char_to_int(res + 0), res + 4 + 2 + 8); 33 | 34 | // 2nd: test decoding middle package into dubbo package 35 | int body_size = four_char_to_int(res + 0); 36 | char *res_dubbo = malloc(4096); 37 | 38 | int dubbo_size = generate_dubbo_package(res_dubbo, res, body_size); 39 | 40 | log_info("dubbo header magics: 0x%x, 0x%x", res_dubbo[0] & 0xff, res_dubbo[1] & 0xff); 41 | log_info("dubbo header event, status: 0x%x, 0x%x", res_dubbo[2] & 0xff, res_dubbo[3] & 0xff); 42 | log_info("dubbo req id:", bytes8_to_long(res_dubbo + 4)); 43 | log_info("dubbo body len:", four_char_to_int(res_dubbo + 12)); 44 | assert(four_char_to_int(res_dubbo + 12) == dubbo_size - 16); 45 | log_info("dubbo body: %.*s", dubbo_size - 16, res_dubbo + 16); 46 | 47 | free(my_str_buf); 48 | free(res); 49 | free(res_dubbo); 50 | } -------------------------------------------------------------------------------- /src/utils/tests/echo_server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "../log.h" 6 | #define DEFAULT_PORT 28000 7 | #define DEFAULT_BACKLOG 256 8 | 9 | uv_loop_t *loop; 10 | struct sockaddr_in addr; 11 | 12 | typedef struct { 13 | uv_write_t req; 14 | uv_buf_t buf; 15 | } write_req_t; 16 | 17 | void free_write_req(uv_write_t *req) { 18 | write_req_t *wr = (write_req_t*) req; 19 | free(wr->buf.base); 20 | free(wr); 21 | } 22 | 23 | void echo_alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 24 | buf->base = (char*) malloc(suggested_size); 25 | buf->len = suggested_size; 26 | } 27 | 28 | void echo_write(uv_write_t *req, int status) { 29 | if (status) { 30 | fprintf(stderr, "Write error %s\n", uv_strerror(status)); 31 | } 32 | free_write_req(req); 33 | } 34 | 35 | void echo_read(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 36 | if (nread > 0) { 37 | write_req_t *req = (write_req_t*) malloc(sizeof(write_req_t)); 38 | req->buf = uv_buf_init(buf->base, nread); 39 | uv_write((uv_write_t*) req, client, &req->buf, 1, echo_write); 40 | //log_info("read data: %*s", nread, buf); 41 | return; 42 | } 43 | if (nread < 0) { 44 | if (nread != UV_EOF) 45 | fprintf(stderr, "Read error %s\n", uv_err_name(nread)); 46 | uv_close((uv_handle_t*) client, NULL); 47 | } 48 | 49 | free(buf->base); 50 | } 51 | 52 | void on_new_connection(uv_stream_t *server, int status) { 53 | if (status < 0) { 54 | fprintf(stderr, "New connection error %s\n", uv_strerror(status)); 55 | // error! 56 | return; 57 | } 58 | 59 | uv_tcp_t *client = (uv_tcp_t*) malloc(sizeof(uv_tcp_t)); 60 | uv_tcp_init(loop, client); 61 | if (uv_accept(server, (uv_stream_t*) client) == 0) { 62 | uv_read_start((uv_stream_t*) client, echo_alloc_buffer, echo_read); 63 | } 64 | else { 65 | uv_close((uv_handle_t*) client, NULL); 66 | } 67 | } 68 | 69 | int main() { 70 | loop = uv_default_loop(); 71 | 72 | uv_tcp_t server; 73 | uv_tcp_init(loop, &server); 74 | 75 | uv_ip4_addr("0.0.0.0", DEFAULT_PORT, &addr); 76 | 77 | uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0); 78 | int r = uv_listen((uv_stream_t*) &server, DEFAULT_BACKLOG, on_new_connection); 79 | if (r) { 80 | fprintf(stderr, "Listen error %s\n", uv_strerror(r)); 81 | return 1; 82 | } 83 | return uv_run(loop, UV_RUN_DEFAULT); 84 | } -------------------------------------------------------------------------------- /src/utils/loadBalancer.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "loadBalancer.h" 4 | 5 | #include "../consumer_agent.h" 6 | #include "utils.h" 7 | 8 | int direct_send_limit[3] = {8, 8, 8}; 9 | int penalty_send_limit[3] = {140, 200, 200}; 10 | 11 | history_diff_time_t *init_history_time() { 12 | history_diff_time_t *res = malloc(sizeof(history_diff_time_t)); 13 | for (int i = 0; i < HISTORY_WINDOW_SIZE; i++) { res->diff_time_history[i] = 50000; } 14 | res->diff_time_sum = 50000 * HISTORY_WINDOW_SIZE; 15 | res->head = 0; 16 | res->tail = HISTORY_WINDOW_SIZE - 1; 17 | 18 | res->remain = 0; 19 | return res; 20 | } 21 | 22 | load_balancer_t *init_load_balancer(int num_of_ends) { 23 | load_balancer_t *this = malloc(sizeof(load_balancer_t)); 24 | this->num_of_ends_ = num_of_ends; 25 | for (int i = 0; i < num_of_ends; i++) { this->history_diff_time_lst[i] = init_history_time(); } 26 | return this; 27 | } 28 | 29 | inline int64_t estimate_cost(load_balancer_t *this, int index) { 30 | history_diff_time_t *history_diff_time = this->history_diff_time_lst[index]; 31 | if (history_diff_time->remain >= penalty_send_limit[index]) { 32 | return INT64_MAX; 33 | } 34 | if (history_diff_time->remain < direct_send_limit[index]) { 35 | return 0; 36 | } 37 | return history_diff_time->diff_time_sum; 38 | } 39 | 40 | int select_endpoint_time_based(load_balancer_t *this, http_req_t *http_req) { 41 | int min_idx = this->num_of_ends_ - 1; 42 | http_req->ts_req_beg = get_wall_time(); 43 | int64_t min_val = estimate_cost(this, min_idx); 44 | for (int i = this->num_of_ends_ - 2; i >= 0; i--) { 45 | int64_t tmp = estimate_cost(this, i); 46 | if (tmp < min_val) { 47 | min_idx = i; 48 | min_val = tmp; 49 | } 50 | } 51 | this->history_diff_time_lst[min_idx]->remain++; 52 | return min_idx; 53 | } 54 | 55 | void finish_endpoint_task_time_based(load_balancer_t *this, int end_point_idx, http_req_t *http_req) { 56 | this->history_diff_time_lst[end_point_idx]->remain--; 57 | int64_t new_diff_time = get_wall_time() - http_req->ts_req_beg; 58 | 59 | history_diff_time_t *history_diff_time = this->history_diff_time_lst[end_point_idx]; 60 | int64_t oldest_diff = history_diff_time->diff_time_history[history_diff_time->head]; 61 | history_diff_time->head++; 62 | history_diff_time->head %= HISTORY_WINDOW_SIZE; 63 | history_diff_time->diff_time_sum -= oldest_diff; 64 | 65 | history_diff_time->tail++; 66 | history_diff_time->tail %= HISTORY_WINDOW_SIZE; 67 | history_diff_time->diff_time_history[history_diff_time->tail] = new_diff_time; 68 | history_diff_time->diff_time_sum += new_diff_time; 69 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | project(agent C) 3 | 4 | set(CMAKE_C_STANDARD 11) 5 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH}) # To allow CMake to locate our Find*.cmake files 6 | # Options. Turn on with 'cmake -Dmyvarname=ON'. 7 | option(test "Build all tests." ON) # Makes boolean 'test' available. 8 | option(enableUDP "enable UDP between agents" OFF) 9 | option(enableLOG "enable loging" ON) 10 | option(enableUDP_Control "enable Control Channel and mannual gc" OFF) 11 | 12 | add_compile_options(-O3 -Wall) 13 | 14 | if (enableLOG) 15 | add_definitions(-DUSE_LOG) 16 | endif () 17 | 18 | if (enableUDP) 19 | add_definitions(-DUSE_UDP) 20 | endif () 21 | 22 | if (enableUDP_Control) 23 | add_definitions(-DUSE_UDP_CONTROL) 24 | endif () 25 | 26 | if (enableUDP) 27 | if(enableUDP_Control) 28 | message(FATAL_ERROR "cannot use UDP for transmission and control channel at the same time!" ) 29 | endif() 30 | endif() 31 | 32 | include(GetGitRevisionDescription) 33 | get_git_head_revision(GIT_REFSPEC GIT_SHA1) 34 | add_definitions("-DGIT_SHA1=\"${GIT_SHA1}\"") 35 | 36 | include(CheckCCompilerFlag) 37 | CHECK_C_COMPILER_FLAG("-msse4.2" COMPILER_OPT_SSE42_SUPPORTED) 38 | CHECK_C_COMPILER_FLAG("-mpopcnt" COMPILER_OPT_POPCNT_SUPPORTED) 39 | 40 | if (COMPILER_OPT_SSE42_SUPPORTED) 41 | add_compile_options(-msse4.2) 42 | endif () 43 | if (COMPILER_OPT_POPCNT_SUPPORTED) 44 | add_compile_options(-mpopcnt) 45 | endif () 46 | 47 | find_package(Libuv REQUIRED) 48 | if (${LIBUV_FOUND}) 49 | include_directories(${LIBUV_INCLUDE_DIR}) 50 | endif () 51 | 52 | find_package(CURL REQUIRED) 53 | include_directories(${CURL_INCLUDE_DIR}) 54 | 55 | set(CPP_SRC 56 | src/utils/log.h src/utils/log.c 3rd-deps/base64/b64.h 3rd-deps/base64/decode.c 3rd-deps/base64/encode.c 57 | 3rd-deps/picohttpparser/picohttpparser.h 3rd-deps/picohttpparser/picohttpparser.c src/utils/blockQueue.h 58 | src/utils/blockQueue.c src/utils/tokenizer.h src/utils/tokenizer.c src/utils/etcd.h 59 | src/utils/etcd.c 3rd-deps/jsmn/jsmn.h 3rd-deps/jsmn/jsmn.c 3rd-deps/base64/b64.h 3rd-deps/base64/encode.c 60 | 3rd-deps/base64/decode.c src/utils/utils.h src/utils/utils.c src/utils/loadBalancer.h 61 | src/utils/loadBalancer.c src/utils/atomic_integer.h 3rd-deps/uthash/include/uthash.h src/utils/tokenizer_fast_req.c 62 | src/config.h src/utils/jvm_cmd.h src/utils/jvm_cmd.c src/utils/http_req_queue.h src/utils/http_req_queue.c) 63 | 64 | add_executable(agent ${CPP_SRC} src/main.c src/provider_agent.h src/provider_agent.c src/consumer_agent.h src/consumer_agent.c src/utils/tokenizer_fast_req.c) 65 | target_link_libraries(agent ${LIBUV_LIBRARIES} ${CURL_LIBRARIES}) 66 | 67 | if (test) 68 | add_subdirectory(src/utils/tests) 69 | add_subdirectory(src/playground) 70 | endif () -------------------------------------------------------------------------------- /python_playgound/test_dubbo_client.py: -------------------------------------------------------------------------------- 1 | # client 2 | 3 | import socket 4 | 5 | if __name__ == '__main__': 6 | # address = ('127.0.0.1', 20889) 7 | address = ('127.0.0.1', 3000) 8 | 9 | 10 | def get_dubbo_header(): 11 | my_bytes = bytearray() 12 | 13 | # magic 14 | my_bytes.append(0xda) 15 | my_bytes.append(0xbb) 16 | # fastjson | req, 2way, no-event 17 | my_bytes.append(6 | 0b11000000) 18 | # status 19 | my_bytes.append(0x00) 20 | # long: request id 21 | for _ in xrange(8): 22 | my_bytes.append(0x00) 23 | return my_bytes 24 | 25 | 26 | # dubbo version related, not important 27 | def get_body_str(): 28 | dubbo_service_path = 'com.alibaba.dubbo.performance.demo.provider.IHelloService' 29 | dubbo_version = '2.0.1' 30 | # dubbo_version = None 31 | service_version = None 32 | 33 | # varying parameters 34 | parameter_type_str = 'Ljava/lang/String;' 35 | arguments = 'abc' 36 | method_name = 'hash' 37 | attachement = {'path': dubbo_service_path} 38 | 39 | def fast_json_serializer(my_str): 40 | if my_str is None: 41 | return 'null' 42 | if isinstance(my_str, str): 43 | return '"' + my_str + '"' 44 | if isinstance(my_str, dict): 45 | ret = '{' 46 | for k, v in my_str.iteritems(): 47 | if len(ret) > 1: 48 | ret += ',' 49 | ret += fast_json_serializer(k) + ':' + fast_json_serializer(v) 50 | ret += '}' 51 | return ret 52 | 53 | body_str = '\n'.join(map(fast_json_serializer, 54 | [dubbo_version, dubbo_service_path, service_version, 55 | method_name, parameter_type_str, arguments, attachement])) + '\n' 56 | return body_str 57 | 58 | 59 | print get_body_str() 60 | # 1st: encode 61 | body_bytes = str.encode(get_body_str()) 62 | header_bytes = get_dubbo_header() 63 | 64 | 65 | def encode_int(integer): 66 | array = [(integer >> (8 * i)) & 0xff for i in range(3, -1, -1)] 67 | return array 68 | 69 | 70 | print 'body len:', len(body_bytes) 71 | print 'bytes:', encode_int(len(body_bytes)) 72 | 73 | for my_b in encode_int(len(body_bytes)): 74 | header_bytes.append(my_b) 75 | print map(hex, header_bytes) 76 | print len(header_bytes) 77 | 78 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 79 | s.connect(address) 80 | for i in xrange(100): 81 | s.send(header_bytes + body_bytes) 82 | 83 | # 2nd: decode 84 | data = s.recv(512) 85 | print data 86 | hex_lst = map(hex, map(ord, data)) 87 | # print ord(data[3]) 88 | # print 'new line flag:', hex_lst[17] 89 | print 'response value in {0,1,2}:', data[16], ', and result:', data[18:len(data) - 1] 90 | if i % 100 == 0: 91 | print i 92 | 93 | s.close() 94 | -------------------------------------------------------------------------------- /python_playgound/test_dubbo_pressure_async.py: -------------------------------------------------------------------------------- 1 | # client 2 | import asyncore 3 | import socket 4 | 5 | 6 | def get_dubbo_header(): 7 | my_bytes = bytearray() 8 | 9 | # magic 10 | my_bytes.append(0xda) 11 | my_bytes.append(0xbb) 12 | # fastjson | req, 2way, no-event 13 | my_bytes.append(6 | 0b11000000) 14 | # status 15 | my_bytes.append(0x00) 16 | # long: request id 17 | for _ in xrange(8): 18 | my_bytes.append(0x00) 19 | return my_bytes 20 | 21 | 22 | def get_body_str(): 23 | dubbo_service_path = 'com.alibaba.dubbo.performance.demo.provider.IHelloService' 24 | dubbo_version = '2.0.1' 25 | service_version = None 26 | 27 | # varying parameters 28 | parameter_type_str = 'Ljava/lang/String;' 29 | arguments = 'abc' 30 | method_name = 'hash' 31 | attachement = {'path': dubbo_service_path} 32 | 33 | def fast_json_serializer(my_str): 34 | if my_str is None: 35 | return 'null' 36 | if isinstance(my_str, str): 37 | return '"' + my_str + '"' 38 | if isinstance(my_str, dict): 39 | ret = '{' 40 | for k, v in my_str.iteritems(): 41 | if len(ret) > 1: 42 | ret += ',' 43 | ret += fast_json_serializer(k) + ':' + fast_json_serializer(v) 44 | ret += '}' 45 | return ret 46 | 47 | body_str = '\n'.join(map(fast_json_serializer, 48 | [dubbo_version, dubbo_service_path, service_version, 49 | method_name, parameter_type_str, arguments, attachement])) + '\n' 50 | return body_str 51 | 52 | 53 | def get_dubbo_whole_package(): 54 | def encode_int(integer): 55 | array = [(integer >> (8 * i)) & 0xff for i in range(3, -1, -1)] 56 | return array 57 | 58 | # 1st: encode 59 | body_bytes = str.encode(get_body_str()) 60 | header_bytes = get_dubbo_header() 61 | 62 | # print map(hex, header_bytes) 63 | # print len(header_bytes) 64 | # print 'body len:', len(body_bytes) 65 | # print 'bytes:', encode_int(len(body_bytes)) 66 | 67 | for my_b in encode_int(len(body_bytes)): 68 | header_bytes.append(my_b) 69 | return header_bytes + body_bytes 70 | 71 | 72 | class DubboClient(asyncore.dispatcher): 73 | def __init__(self, host, port): 74 | asyncore.dispatcher.__init__(self) 75 | self.create_socket(socket.AF_INET, socket.SOCK_STREAM) 76 | self.connect((host, port)) 77 | self.i = 0 78 | 79 | def handle_connect(self): 80 | pass 81 | 82 | def handle_close(self): 83 | self.close() 84 | 85 | def handle_read(self): 86 | data = self.recv(512) 87 | # print self.i, 'response value in {0,1,2}:', data[16], ', and result:', data[18:len(data) - 1] 88 | print data 89 | 90 | def writable(self): 91 | print self.i 92 | return self.i < 1000 93 | 94 | def handle_write(self): 95 | dubbo_package = get_dubbo_whole_package() 96 | self.send(dubbo_package) 97 | self.i += 1 98 | 99 | 100 | if __name__ == '__main__': 101 | # address = ('127.0.0.1', 20889) 102 | # address = ('127.0.0.1', 3000) 103 | # s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 104 | # s.connect(address) 105 | 106 | # dubbo_package = get_dubbo_whole_package() 107 | # s.send(dubbo_package) 108 | # s.close() 109 | 110 | dubbo_client = DubboClient('127.0.0.1', 3001) 111 | asyncore.loop() 112 | -------------------------------------------------------------------------------- /src/utils/log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017 rxi 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to 6 | * deal in the Software without restriction, including without limitation the 7 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 8 | * sell copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 20 | * IN THE SOFTWARE. 21 | */ 22 | #ifdef USE_LOG 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "log.h" 30 | 31 | static struct { 32 | void *udata; 33 | log_LockFn lock; 34 | FILE *fp; 35 | int level; 36 | int quiet; 37 | } L; 38 | 39 | 40 | static const char *level_names[] = { 41 | "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL" 42 | }; 43 | #ifndef LOG_USE_COLOR 44 | #define LOG_USE_COLOR 45 | #endif 46 | #ifdef LOG_USE_COLOR 47 | static const char *level_colors[] = { 48 | "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m" 49 | }; 50 | #endif 51 | 52 | 53 | static void lock(void) { 54 | if (L.lock) { 55 | L.lock(L.udata, 1); 56 | } 57 | } 58 | 59 | 60 | static void unlock(void) { 61 | if (L.lock) { 62 | L.lock(L.udata, 0); 63 | } 64 | } 65 | 66 | 67 | void log_set_udata(void *udata) { 68 | L.udata = udata; 69 | } 70 | 71 | 72 | void log_set_lock(log_LockFn fn) { 73 | L.lock = fn; 74 | } 75 | 76 | 77 | void log_set_fp(FILE *fp) { 78 | L.fp = fp; 79 | } 80 | 81 | 82 | void log_set_level(int level) { 83 | L.level = level; 84 | } 85 | 86 | 87 | void log_set_quiet(int enable) { 88 | L.quiet = enable ? 1 : 0; 89 | } 90 | 91 | 92 | void log_log(int level, const char *file, int line, const char *fmt, ...) { 93 | if (level < L.level) { 94 | return; 95 | } 96 | 97 | /* Acquire lock */ 98 | lock(); 99 | 100 | /* Get current time */ 101 | time_t t = time(NULL); 102 | struct tm *lt = localtime(&t); 103 | 104 | /* Log to stderr */ 105 | if (!L.quiet) { 106 | va_list args; 107 | char buf[16]; 108 | buf[strftime(buf, sizeof(buf), "%H:%M:%S", lt)] = '\0'; 109 | #ifdef LOG_USE_COLOR 110 | fprintf( 111 | stderr, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", 112 | buf, level_colors[level], level_names[level], file, line); 113 | #else 114 | fprintf(stderr, "%s %-5s %s:%d: ", buf, level_names[level], file, line); 115 | #endif 116 | va_start(args, fmt); 117 | vfprintf(stderr, fmt, args); 118 | va_end(args); 119 | fprintf(stderr, "\n"); 120 | } 121 | 122 | /* Log to file */ 123 | if (L.fp) { 124 | va_list args; 125 | char buf[32]; 126 | buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", lt)] = '\0'; 127 | fprintf(L.fp, "%s %-5s %s:%d: ", buf, level_names[level], file, line); 128 | va_start(args, fmt); 129 | vfprintf(L.fp, fmt, args); 130 | va_end(args); 131 | fprintf(L.fp, "\n"); 132 | } 133 | 134 | /* Release lock */ 135 | unlock(); 136 | } 137 | 138 | #endif -------------------------------------------------------------------------------- /src/playground/test_split.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/3/18. 3 | // 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../utils/tokenizer.h" 12 | 13 | //const char *my_str = "interface=com.alibaba.dubbo.performance.demo.provider.IHelloService&" 14 | // "method=hash¶meterTypesString=Ljava%2Flang%2FString%3B" 15 | // "¶meter=abc"; 16 | const char *my_str = "interface=com.alibaba.dubbo.performance.demo.provider.IHelloService&" 17 | "method=hash¶meterTypesString=Ljava%2Flang%2FString%3B" 18 | "¶meter=Tbk4ZGqnHQNRM8Wqr65Sxz8K2wnWHhvcaNuAnTn64geI6AnEHB8cCtEg154" 19 | "rqjTqWIXqMdUwysbjwTivtkbLi8qNHWg1Kri58NPcKS3mpe1lZ0bh48dDhgoNRoAkL548Lz" 20 | "GKQNiZb2cKX6geL7srwMjbZ1ybLvCNkHFnMd6cpIMMO8mvyAA3LLH3RydyTGEv2JLP6N9lp" 21 | "3xE1YnI8O28TMu0XKeqBaNzpHQDrNDAMSNZn7Ka896JnEiFr1naM5h7fyff1BqXUfA5rZSed" 22 | "kSXQt4RyY9WvmhEeF3eOfapdb34P64uLlJqfNcBpjfKB5sHaeUmDpBI6j18XhxqvdeE2K69dFS" 23 | "dXavGri0aLyeUlCUX3VE5owNt27yeYNH76qHEC0ZJpBW9BnUTo4BuojqhnyfIkdTb7IEl8lyns" 24 | "6uJPJXTSIuZloVqCcZxoqvh9dlj69BIVLaksjhttv2lAuF9raq5ceXkHWrsgfLEvwphvCc5gHAgliG"; 25 | 26 | long timediff(clock_t t1, clock_t t2) { 27 | long elapsed; 28 | elapsed = (long) (((double) t2 - t1) / CLOCKS_PER_SEC * 1000); 29 | return elapsed; 30 | } 31 | 32 | void test_split() { 33 | char *buffer = malloc(sizeof(char) * 4096); 34 | 35 | short *off = malloc(sizeof(short) * 16); 36 | short len = (short) strlen(my_str); 37 | memcpy(buffer, my_str, len); 38 | 39 | // 1st: usage demo 40 | int size = simd_split_efficient(buffer, len, off); 41 | printf("%s, \n", my_str); 42 | printf("=,& : %d, %d\n", '=', '&'); 43 | printf("%d, %d\n", len, size); 44 | for (int i = 0; i < size; i++) { printf("%d\n", off[i]); } 45 | 46 | // 2nd: measure time ----------------- 47 | // 1) avx - efficient 48 | clock_t t1, t2; 49 | long elapsed; 50 | t1 = clock(); 51 | for (int i = 0; i < 5000 * 60; i++) { 52 | simd_split_efficient(buffer, len, off); 53 | } 54 | t2 = clock(); 55 | elapsed = timediff(t1, t2); 56 | printf("simd elapsed: %ld ms\n", elapsed); 57 | 58 | // 2) serial 59 | t1 = clock(); 60 | for (int i = 0; i < 5000 * 60; i++) { 61 | serial_split(buffer, len, off); 62 | } 63 | t2 = clock(); 64 | elapsed = timediff(t1, t2); 65 | printf("serial elapsed: %ld ms\n", elapsed); 66 | // end of measuring time -------------------------- 67 | 68 | free(off); 69 | free(buffer); 70 | } 71 | 72 | void test_generate_body() { 73 | printf("-------------------\n"); 74 | short len = (short) strlen(my_str); 75 | char *str_buffer = malloc(sizeof(char) * 4096); 76 | memcpy(str_buffer, my_str, len); 77 | 78 | clock_t t1, t2; 79 | long elapsed; 80 | int64_t req_id = 0; 81 | t1 = clock(); 82 | for (int i = 0; i < 5000 * 60; i++) { 83 | char *res = malloc(sizeof(char) * 2048); 84 | generate_res_in_place_hard_code(res, str_buffer, len, req_id); 85 | req_id++; 86 | free(res); 87 | } 88 | t2 = clock(); 89 | elapsed = timediff(t1, t2); 90 | printf("total transform time: %ld ms\n", elapsed); 91 | 92 | // last round 93 | char *res = malloc(sizeof(char) * 2048); 94 | int offset = generate_res_in_place(res, str_buffer, len, req_id); 95 | 96 | printf("header:"); 97 | for (int i = 0; i < 16; i++) { printf("0x%x ,", res[i] & 0xff); } 98 | printf("\ntotal length: %d\n", offset - 16); 99 | printf("%.*s", offset - 16, res + 16); 100 | printf("after the new line~~~~~~~~~"); 101 | free(str_buffer); 102 | free(res); 103 | } 104 | 105 | int main() { 106 | test_split(); 107 | test_generate_body(); 108 | } 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Disclaimer 2 | 此项目仅供学习与交流使用,请遵循MIT协议,如果您在任何项目中使用相关代码,请保留此项目的LICENSE文件。 3 | 4 | 5 | # service-mesh-agent 6 | 7 | Service mesh agent in pure c (使用intrinsics编写向量化代码) --- Created By Blink`团队 ([Pisces](https://github.com/WANG-lp) and [yche](https://github.com/CheYulin))。 8 | 9 | ## 下载依赖 10 | 11 | ```zsh 12 | git submodule init 13 | git submodule update 14 | ``` 15 | 16 | ## 本地构建代码 17 | 18 | ```zsh 19 | mkdir -p build && cd build 20 | cmake .. && make -j 21 | ``` 22 | 23 | ## 本仓库的文件 24 | 25 | 文件夹/文件 | 说明 26 | --- | --- 27 | [src](src) | 源代码文件 28 | [cmake](cmake), [CMakeLists.txt](CMakeLists.txt) | 项目构建文件 29 | [Dockerfile](Dockerfile)| 配合阿里云镜像仓库hook生成镜像,agents启动用 [start-agent.sh](start-agent.sh) 30 | [3rd-deps](3rd-deps), [LICENSES](LICENSES) | 第三方依赖和对应licenses 31 | [python_playgound](python_playgound), [scripts](scripts) | 编码和测试中用的python以及shell脚本 32 | [LICENSE](LICENSE) | 该项目的License 33 | 34 | ## 最高跑分统计数据 35 | 36 | ```zsh 37 | [INFO] >>> Pressure with 128 connections. 38 | [DEBUG] Script to execute: 39 | 40 | sleep 5 41 | wrk -t2 -c128 -d60s -T5 --script=./benchmark/wrk.lua --latency http://beita.e100081000032.zmf/invoke 42 | exit 0 43 | 44 | [DEBUG] Return code = 0 45 | [DEBUG] The output is as following: 46 | Running 1m test @ http://beita.e100081000032.zmf/invoke 47 | 2 threads and 128 connections 48 | Thread Stats Avg Stdev Max +/- Stdev 49 | Latency 52.34ms 4.42ms 172.66ms 98.08% 50 | Req/Sec 1.23k 72.84 1.29k 94.33% 51 | Latency Distribution 52 | 50% 51.73ms 53 | 75% 52.19ms 54 | 90% 53.06ms 55 | 99% 60.09ms 56 | 146884 requests in 1.00m, 19.75MB read 57 | Requests/sec: 2446.88 58 | Transfer/sec: 336.92KB 59 | -------------------------- 60 | Durations: 60.03s 61 | Requests: 146884 62 | Avg RT: 52.34ms 63 | Max RT: 172.66ms 64 | Min RT: 51.04ms 65 | Error requests: 0 66 | Valid requests: 146884 67 | QPS: 2446.88 68 | -------------------------- 69 | 70 | [INFO] QPS = 2446.88 71 | [INFO] >>> Pressure with 256 connections. 72 | [DEBUG] Script to execute: 73 | 74 | sleep 5 75 | wrk -t2 -c256 -d60s -T5 --script=./benchmark/wrk.lua --latency http://beita.e100081000032.zmf/invoke 76 | exit 0 77 | 78 | [DEBUG] Return code = 0 79 | [DEBUG] The output is as following: 80 | Running 1m test @ http://beita.e100081000032.zmf/invoke 81 | 2 threads and 256 connections 82 | Thread Stats Avg Stdev Max +/- Stdev 83 | Latency 55.99ms 10.50ms 468.34ms 96.68% 84 | Req/Sec 2.30k 143.87 2.59k 76.83% 85 | Latency Distribution 86 | 50% 54.24ms 87 | 75% 56.72ms 88 | 90% 60.58ms 89 | 99% 75.54ms 90 | 275126 requests in 1.00m, 37.00MB read 91 | Requests/sec: 4583.77 92 | Transfer/sec: 631.16KB 93 | -------------------------- 94 | Durations: 60.02s 95 | Requests: 275126 96 | Avg RT: 55.99ms 97 | Max RT: 468.344ms 98 | Min RT: 51.036ms 99 | Error requests: 0 100 | Valid requests: 275126 101 | QPS: 4583.77 102 | -------------------------- 103 | 104 | [INFO] QPS = 4583.77 105 | [INFO] >>> Pressure with 512 connections. 106 | [DEBUG] Script to execute: 107 | 108 | sleep 5 109 | wrk -t2 -c512 -d60s -T5 --script=./benchmark/wrk.lua --latency http://beita.e100081000032.zmf/invoke 110 | exit 0 111 | 112 | [DEBUG] Return code = 0 113 | [DEBUG] The output is as following: 114 | Running 1m test @ http://beita.e100081000032.zmf/invoke 115 | 2 threads and 512 connections 116 | Thread Stats Avg Stdev Max +/- Stdev 117 | Latency 71.87ms 17.86ms 391.22ms 88.52% 118 | Req/Sec 3.59k 288.36 4.30k 74.92% 119 | Latency Distribution 120 | 50% 67.11ms 121 | 75% 76.07ms 122 | 90% 90.21ms 123 | 99% 141.02ms 124 | 428805 requests in 1.00m, 57.66MB read 125 | Requests/sec: 7144.13 126 | Transfer/sec: 0.96MB 127 | -------------------------- 128 | Durations: 60.02s 129 | Requests: 428805 130 | Avg RT: 71.87ms 131 | Max RT: 391.222ms 132 | Min RT: 51.205ms 133 | Error requests: 0 134 | Valid requests: 428805 135 | QPS: 7144.13 136 | -------------------------- 137 | 138 | [INFO] QPS = 7144.13 139 | ``` 140 | 141 | ## Code Review 文档 142 | 143 | [Blink-code-review-doc.pdf](Blink-code-review-doc.pdf) 144 | -------------------------------------------------------------------------------- /cmake/GetGitRevisionDescription.cmake: -------------------------------------------------------------------------------- 1 | # - Returns a version string from Git 2 | # 3 | # These functions force a re-configure on each git commit so that you can 4 | # trust the values of the variables in your build system. 5 | # 6 | # get_git_head_revision( [ ...]) 7 | # 8 | # Returns the refspec and sha hash of the current head revision 9 | # 10 | # git_describe( [ ...]) 11 | # 12 | # Returns the results of git describe on the source tree, and adjusting 13 | # the output so that it tests false if an error occurs. 14 | # 15 | # git_get_exact_tag( [ ...]) 16 | # 17 | # Returns the results of git describe --exact-match on the source tree, 18 | # and adjusting the output so that it tests false if there was no exact 19 | # matching tag. 20 | # 21 | # git_local_changes() 22 | # 23 | # Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. 24 | # Uses the return code of "git diff-index --quiet HEAD --". 25 | # Does not regard untracked files. 26 | # 27 | # Requires CMake 2.6 or newer (uses the 'function' command) 28 | # 29 | # Original Author: 30 | # 2009-2010 Ryan Pavlik 31 | # http://academic.cleardefinition.com 32 | # Iowa State University HCI Graduate Program/VRAC 33 | # 34 | # Copyright Iowa State University 2009-2010. 35 | # Distributed under the Boost Software License, Version 1.0. 36 | # (See accompanying file LICENSE_1_0.txt or copy at 37 | # http://www.boost.org/LICENSE_1_0.txt) 38 | 39 | if(__get_git_revision_description) 40 | return() 41 | endif() 42 | set(__get_git_revision_description YES) 43 | 44 | # We must run the following at "include" time, not at function call time, 45 | # to find the path to this module rather than the path to a calling list file 46 | get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) 47 | 48 | function(get_git_head_revision _refspecvar _hashvar) 49 | set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") 50 | set(GIT_DIR "${GIT_PARENT_DIR}/.git") 51 | while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories 52 | set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") 53 | get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) 54 | if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) 55 | # We have reached the root directory, we are not in git 56 | set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) 57 | set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) 58 | return() 59 | endif() 60 | set(GIT_DIR "${GIT_PARENT_DIR}/.git") 61 | endwhile() 62 | # check if this is a submodule 63 | if(NOT IS_DIRECTORY ${GIT_DIR}) 64 | file(READ ${GIT_DIR} submodule) 65 | string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) 66 | get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) 67 | get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) 68 | endif() 69 | set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") 70 | if(NOT EXISTS "${GIT_DATA}") 71 | file(MAKE_DIRECTORY "${GIT_DATA}") 72 | endif() 73 | 74 | if(NOT EXISTS "${GIT_DIR}/HEAD") 75 | return() 76 | endif() 77 | set(HEAD_FILE "${GIT_DATA}/HEAD") 78 | configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) 79 | 80 | configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" 81 | "${GIT_DATA}/grabRef.cmake" 82 | @ONLY) 83 | include("${GIT_DATA}/grabRef.cmake") 84 | 85 | set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) 86 | set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) 87 | endfunction() 88 | 89 | function(git_describe _var) 90 | if(NOT GIT_FOUND) 91 | find_package(Git QUIET) 92 | endif() 93 | get_git_head_revision(refspec hash) 94 | if(NOT GIT_FOUND) 95 | set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) 96 | return() 97 | endif() 98 | if(NOT hash) 99 | set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) 100 | return() 101 | endif() 102 | 103 | # TODO sanitize 104 | #if((${ARGN}" MATCHES "&&") OR 105 | # (ARGN MATCHES "||") OR 106 | # (ARGN MATCHES "\\;")) 107 | # message("Please report the following error to the project!") 108 | # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") 109 | #endif() 110 | 111 | #message(STATUS "Arguments to execute_process: ${ARGN}") 112 | 113 | execute_process(COMMAND 114 | "${GIT_EXECUTABLE}" 115 | describe 116 | ${hash} 117 | ${ARGN} 118 | WORKING_DIRECTORY 119 | "${CMAKE_CURRENT_SOURCE_DIR}" 120 | RESULT_VARIABLE 121 | res 122 | OUTPUT_VARIABLE 123 | out 124 | ERROR_QUIET 125 | OUTPUT_STRIP_TRAILING_WHITESPACE) 126 | if(NOT res EQUAL 0) 127 | set(out "${out}-${res}-NOTFOUND") 128 | endif() 129 | 130 | set(${_var} "${out}" PARENT_SCOPE) 131 | endfunction() 132 | 133 | function(git_get_exact_tag _var) 134 | git_describe(out --exact-match ${ARGN}) 135 | set(${_var} "${out}" PARENT_SCOPE) 136 | endfunction() 137 | 138 | function(git_local_changes _var) 139 | if(NOT GIT_FOUND) 140 | find_package(Git QUIET) 141 | endif() 142 | get_git_head_revision(refspec hash) 143 | if(NOT GIT_FOUND) 144 | set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) 145 | return() 146 | endif() 147 | if(NOT hash) 148 | set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) 149 | return() 150 | endif() 151 | 152 | execute_process(COMMAND 153 | "${GIT_EXECUTABLE}" 154 | diff-index --quiet HEAD -- 155 | WORKING_DIRECTORY 156 | "${CMAKE_CURRENT_SOURCE_DIR}" 157 | RESULT_VARIABLE 158 | res 159 | OUTPUT_VARIABLE 160 | out 161 | ERROR_QUIET 162 | OUTPUT_STRIP_TRAILING_WHITESPACE) 163 | if(res EQUAL 0) 164 | set(${_var} "CLEAN" PARENT_SCOPE) 165 | else() 166 | set(${_var} "DIRTY" PARENT_SCOPE) 167 | endif() 168 | endfunction() 169 | -------------------------------------------------------------------------------- /src/utils/etcd.c: -------------------------------------------------------------------------------- 1 | #include "etcd.h" 2 | 3 | #include 4 | 5 | #include "log.h" 6 | #include "../../3rd-deps/jsmn/jsmn.h" 7 | #include "../../3rd-deps/base64/b64.h" 8 | 9 | void free_string(res_string *s) { 10 | if (s != NULL) { 11 | free(s->ptr); 12 | free(s); 13 | } 14 | } 15 | 16 | void init_string(res_string *s) { 17 | s->len = 0; 18 | s->ptr = malloc(s->len + 1); 19 | if (s->ptr == NULL) { 20 | fprintf(stderr, "malloc() failed\n"); 21 | exit(EXIT_FAILURE); 22 | } 23 | s->ptr[0] = '\0'; 24 | } 25 | 26 | 27 | int jsoneq(const char *json, jsmntok_t *tok, const char *s) { 28 | if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start && 29 | strncmp(json + tok->start, s, tok->end - tok->start) == 0) { 30 | return 0; 31 | } 32 | return -1; 33 | } 34 | 35 | char *json_token_to_str(const char *json, jsmntok_t *tok) { 36 | char *str = malloc(sizeof(char) * (tok->end - tok->start + 1)); 37 | sprintf(str, "%.*s", tok->end - tok->start, 38 | json + tok->start); 39 | return str; 40 | } 41 | 42 | size_t writefunc(void *ptr, size_t size, size_t nmemb, res_string *s) { 43 | size_t new_len = s->len + size * nmemb; 44 | s->ptr = realloc(s->ptr, new_len + 1); 45 | if (s->ptr == NULL) { 46 | fprintf(stderr, "realloc() failed\n"); 47 | exit(EXIT_FAILURE); 48 | } 49 | memcpy(s->ptr + s->len, ptr, size * nmemb); 50 | s->ptr[new_len] = '\0'; 51 | s->len = new_len; 52 | 53 | return size * nmemb; 54 | } 55 | 56 | size_t dummy_write(void *ptr, size_t size, size_t nmemb, res_string *s) { 57 | return size * nmemb; 58 | } 59 | 60 | char *etcd_grant(char *etcd_url, int ttl) { 61 | CURL *hnd = curl_easy_init(); 62 | res_string *s = malloc(sizeof(res_string)); 63 | init_string(s); 64 | 65 | char full_url[1024]; 66 | sprintf(full_url, "%s/v3alpha/lease/grant", etcd_url); 67 | curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); 68 | curl_easy_setopt(hnd, CURLOPT_URL, full_url); 69 | log_info("etcd etcd_grant url: %s\n", full_url); 70 | 71 | struct curl_slist *headers = NULL; 72 | headers = curl_slist_append(headers, "Content-Type: application/json"); 73 | curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); 74 | sprintf(full_url, "{\"TTL\":\"%d\"}", ttl); 75 | curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, full_url); 76 | log_info("etcd grant json: %s\n", full_url); 77 | curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, writefunc); 78 | curl_easy_setopt(hnd, CURLOPT_WRITEDATA, s); 79 | 80 | CURLcode ret = curl_easy_perform(hnd); 81 | 82 | char *results = NULL; 83 | if (ret == CURLE_OK) { 84 | log_info("response body: %s", s->ptr); 85 | jsmn_parser parser; 86 | jsmntok_t t[1024]; 87 | jsmn_init(&parser); 88 | 89 | int r = jsmn_parse(&parser, s->ptr, s->len, t, sizeof(t) / sizeof(t[0])); 90 | log_info("tokens: %d", r); 91 | if (r < 0) { 92 | log_fatal("error while parsing json: error code: %d, json_len: %d, json_str: %s", r, s->len, s->ptr); 93 | } 94 | for (int i = 0; i < r; i++) { 95 | if ((t[i].end - t[i].start == 2) && jsoneq(s->ptr, &t[i], "ID") == 0) { 96 | results = json_token_to_str(s->ptr, &t[i + 1]);//found ID 97 | break; 98 | } 99 | } 100 | } 101 | free_string(s); 102 | curl_easy_cleanup(hnd); 103 | curl_global_cleanup(); 104 | return results; 105 | } 106 | 107 | int etcd_put(char *etcd_url, char *key, char *leaseID) { 108 | CURL *hnd = curl_easy_init(); 109 | res_string *s = malloc(sizeof(res_string)); 110 | init_string(s); 111 | 112 | char full_url[1024]; 113 | sprintf(full_url, "%s/v3alpha/kv/put", etcd_url); 114 | curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); 115 | curl_easy_setopt(hnd, CURLOPT_URL, full_url); 116 | log_info("etcd put url: %s\n", full_url); 117 | 118 | struct curl_slist *headers = NULL; 119 | headers = curl_slist_append(headers, "Content-Type: application/json"); 120 | curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); 121 | char *encoded_key = b64_encode((unsigned char *) key, strlen(key)); 122 | log_info("raw_key: %s, encoded_key: %s", key, encoded_key); 123 | sprintf(full_url, "{\"key\": \"%s\", \"value\": \"\", \"lease\":\"%s\"}", encoded_key, leaseID); 124 | curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, full_url); 125 | log_info("etcd put json: %s\n", full_url); 126 | curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, dummy_write); 127 | curl_easy_setopt(hnd, CURLOPT_WRITEDATA, NULL); 128 | CURLcode ret = curl_easy_perform(hnd); 129 | curl_easy_cleanup(hnd); 130 | curl_global_cleanup(); 131 | return ret; 132 | } 133 | 134 | int etcd_get(char *etcd_url, char *key, char **server_addr, int *results_size) { 135 | CURL *hnd = curl_easy_init(); 136 | res_string *s = malloc(sizeof(res_string)); 137 | init_string(s); 138 | 139 | char full_url[1024]; 140 | sprintf(full_url, "%s/v3alpha/kv/range", etcd_url); 141 | curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); 142 | curl_easy_setopt(hnd, CURLOPT_URL, full_url); 143 | log_info("etcd get url: %s\n", full_url); 144 | 145 | struct curl_slist *headers = NULL; 146 | headers = curl_slist_append(headers, "Content-Type: application/json"); 147 | curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); 148 | char *encoded_key = b64_encode((unsigned char *) key, strlen(key)); 149 | sprintf(full_url, "{\"key\": \"%s\", \"range_end\":\"eg==\", \"sort_order\":1}", encoded_key); 150 | curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, full_url); 151 | log_info("etcd get json: %s\n", full_url); 152 | curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, writefunc); 153 | curl_easy_setopt(hnd, CURLOPT_WRITEDATA, s); 154 | 155 | CURLcode ret = curl_easy_perform(hnd); 156 | *results_size = 0; 157 | if (ret == CURLE_OK) { 158 | log_info("response body: %s", s->ptr); 159 | jsmn_parser parser; 160 | jsmntok_t t[1024]; 161 | jsmn_init(&parser); 162 | 163 | int r = jsmn_parse(&parser, s->ptr, s->len, t, sizeof(t) / sizeof(t[0])); 164 | log_info("tokens: %d", r); 165 | if (r < 0) { 166 | log_fatal("error while parsing json: error code: %d, json_len: %d, json_str: %s", r, s->len, s->ptr); 167 | } 168 | int size = 0; 169 | for (int i = 0; i < r; i++) { 170 | if ((t[i].end - t[i].start == 3) && jsoneq(s->ptr, &t[i], "key") == 0) { 171 | char *raw_str = json_token_to_str(s->ptr, &t[i + 1]); 172 | //log_info("raw str: %s", raw_str); 173 | char *decoded_str = (char *) b64_decode(raw_str, strlen(raw_str)); 174 | //log_info("decoded str: %s", decoded_str); 175 | server_addr[size] = decoded_str; 176 | i++; 177 | size++; 178 | } 179 | } 180 | *results_size = size; 181 | } 182 | free_string(s); 183 | curl_easy_cleanup(hnd); 184 | curl_global_cleanup(); 185 | return ret; 186 | } 187 | 188 | int etcd_keep_alive(char *etcd_url, char *leaseID) { 189 | CURL *hnd = curl_easy_init(); 190 | res_string *s = malloc(sizeof(res_string)); 191 | init_string(s); 192 | 193 | char full_url[1024]; 194 | sprintf(full_url, "%s/v3alpha/lease/keepalive", etcd_url); 195 | curl_easy_setopt(hnd, CURLOPT_CUSTOMREQUEST, "POST"); 196 | curl_easy_setopt(hnd, CURLOPT_URL, full_url); 197 | log_info("etcd keep-alive url: %s\n", full_url); 198 | 199 | struct curl_slist *headers = NULL; 200 | headers = curl_slist_append(headers, "Content-Type: application/json"); 201 | curl_easy_setopt(hnd, CURLOPT_HTTPHEADER, headers); 202 | 203 | sprintf(full_url, "{\"ID\":\"%s\"}", leaseID); 204 | 205 | curl_easy_setopt(hnd, CURLOPT_POSTFIELDS, full_url); 206 | log_info("etcd keep-alive json: %s\n", full_url); 207 | curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, dummy_write); 208 | CURLcode ret = curl_easy_perform(hnd); 209 | curl_easy_cleanup(hnd); 210 | curl_global_cleanup(); 211 | return ret; 212 | } -------------------------------------------------------------------------------- /src/utils/utils.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "jvm_cmd.h" 10 | #include "utils.h" 11 | #include "log.h" 12 | 13 | 14 | int64_t get_wall_time() { 15 | struct timeval time; 16 | if (gettimeofday(&time, NULL)) { 17 | // Handle error 18 | return 0; 19 | } 20 | return time.tv_sec * 1000000 + time.tv_usec; 21 | } 22 | 23 | //input: /dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/[S,M,L]:192.168.1.2:8200 24 | void str_to_server_uri(char *str, char *server_addr, int *port, int *scale) { 25 | //log_info("raw server addr: %s", str); 26 | char *pch; 27 | pch = strtok(str + 1, "/"); 28 | char *server_uri = NULL; 29 | while (pch != NULL) { 30 | server_uri = pch; 31 | //printf ("%s\n",pch); 32 | pch = strtok(NULL, "/"); 33 | } 34 | //log_info("server_uri: %s", server_uri); 35 | pch = strtok(server_uri, ":"); 36 | if (pch[0] == '0') { 37 | *scale = 0; 38 | } else if (pch[0] == '1') { 39 | *scale = 1; 40 | } else if (pch[0] == '2') { 41 | *scale = 2; 42 | } else { 43 | log_fatal("unknown provider scale %s", pch); 44 | exit(-1); 45 | } 46 | strcpy(server_addr, strtok(NULL, ":")); 47 | *port = (int) strtol(strtok(NULL, ":"), NULL, 10); 48 | } 49 | 50 | char *hostIPaddr(char *eth_name) { 51 | struct ifaddrs *ifaddr, *ifa; 52 | int family, s; 53 | char host[NI_MAXHOST]; 54 | char *res = malloc(sizeof(char) * NI_MAXHOST); 55 | res[0] = '\0'; 56 | if (getifaddrs(&ifaddr) == -1) { 57 | log_fatal("getifaddrs error"); 58 | exit(EXIT_FAILURE); 59 | } 60 | 61 | /* Walk through linked list, maintaining head pointer so we 62 | can free list later */ 63 | 64 | for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { 65 | if (ifa->ifa_addr == NULL) 66 | continue; 67 | 68 | family = ifa->ifa_addr->sa_family; 69 | 70 | // /* Display interface name and family (including symbolic 71 | // form of the latter for the common families) */ 72 | // 73 | // printf("%s address family: %d%s\n", 74 | // ifa->ifa_name, family, 75 | // (family == AF_PACKET) ? " (AF_PACKET)" : 76 | // (family == AF_INET) ? " (AF_INET)" : 77 | // (family == AF_INET6) ? " (AF_INET6)" : ""); 78 | 79 | /* For an AF_INET* interface address, display the address */ 80 | 81 | if (family == AF_INET || family == AF_INET6) { 82 | s = getnameinfo(ifa->ifa_addr, 83 | (family == AF_INET) ? sizeof(struct sockaddr_in) : 84 | sizeof(struct sockaddr_in6), 85 | host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); 86 | if (s != 0) { 87 | log_fatal("getnameinfo() failed: %s", gai_strerror(s)); 88 | exit(EXIT_FAILURE); 89 | } 90 | if ((family == AF_INET) && (strcmp(eth_name, ifa->ifa_name) == 0)) { 91 | strcpy(res, host); 92 | } 93 | //log_info("%s\taddress: <%s>", ifa->ifa_name, host); 94 | } 95 | } 96 | freeifaddrs(ifaddr); 97 | return res; 98 | } 99 | 100 | int four_char_to_int(char *length) { 101 | //log_info("12 byte hex: %d", ((length[0]&0xff)<<24)); 102 | int64_t len = 0; 103 | len |= ((length[0] & 0xff) << 24); 104 | len |= ((length[1] & 0xff) << 16); 105 | len |= ((length[2] & 0xff) << 8); 106 | len |= (length[3] & 0xff); 107 | //log_info("length: %d", len); 108 | return len; 109 | } 110 | 111 | void int_to_four_char(int32_t integer, char *bytes) { 112 | bytes[0] = (char) ((integer >> 24) & 0xFF); 113 | bytes[1] = (char) ((integer >> 16) & 0xFF); 114 | bytes[2] = (char) ((integer >> 8) & 0xFF); 115 | bytes[3] = (char) ((integer >> 0) & 0xFF); 116 | } 117 | 118 | void long_to_8bytes(int64_t integer, void *bytes) { 119 | unsigned char *b = (unsigned char *) bytes; 120 | unsigned char *p = (unsigned char *) &integer; 121 | memcpy(b, p, sizeof(unsigned char) * 8); 122 | 123 | } 124 | 125 | int64_t bytes8_to_long(void *bytes) { 126 | return *((int64_t *) bytes); 127 | } 128 | 129 | float unpackFloat(const void *buf) { 130 | return *((float *) buf); 131 | } 132 | 133 | int packFloat(void *buf, float x) { 134 | unsigned char *b = (unsigned char *) buf; 135 | unsigned char *p = (unsigned char *) &x; 136 | #if defined (_M_IX86) || (defined (CPU_FAMILY) && (CPU_FAMILY == I80X86)) 137 | b[0] = p[3]; 138 | b[1] = p[2]; 139 | b[2] = p[1]; 140 | b[3] = p[0]; 141 | #else 142 | b[0] = p[0]; 143 | b[1] = p[1]; 144 | b[2] = p[2]; 145 | b[3] = p[3]; 146 | #endif 147 | return 4; 148 | } 149 | 150 | void int_to_string(char *str, int num) { 151 | if (num < 10) { 152 | str[0] = num + '0'; 153 | } else { 154 | int new_val = num / 10; 155 | str[0] = new_val + '0'; 156 | str[1] = num - new_val * 10 + '0'; 157 | } 158 | } 159 | 160 | void get_cpu_info() { 161 | uv_cpu_info_t *uv_cpus_info; 162 | int cpu_count; 163 | uv_cpu_info(&uv_cpus_info, &cpu_count); 164 | log_info("System has %d cpus", cpu_count); 165 | log_info("###################################CPU_INFO###################################"); 166 | for (int i = 0; i < 1; i++) { 167 | uv_cpu_info_t cpu_info = uv_cpus_info[i]; 168 | log_info("CPU model: %s, speed: %dMHZ", cpu_info.model, cpu_info.speed); 169 | } 170 | log_info("#############################################################################"); 171 | uv_free_cpu_info(uv_cpus_info, cpu_count); 172 | } 173 | 174 | /* 175 | Converts integer to its string representation in decimal notation. 176 | 177 | SYNOPSIS 178 | int10_to_str() 179 | val - value to convert 180 | dst - points to buffer where string representation should be stored 181 | radix - flag that shows whenever val should be taken as signed or not 182 | DESCRIPTION 183 | This is version of int2str() function which is optimized for normal case 184 | of radix 10/-10. It takes only sign of radix parameter into account and 185 | not its absolute value. 186 | RETURN VALUE 187 | Pointer to ending NUL character. 188 | */ 189 | char *int10_to_str(long int val, char *dst, int radix) { 190 | char buffer[65]; 191 | char *p; 192 | long int new_val; 193 | unsigned long int uval = (unsigned long int) val; 194 | 195 | if (radix < 0) /* -10 */ 196 | { 197 | if (val < 0) { 198 | *dst++ = '-'; 199 | /* Avoid integer overflow in (-val) for LLONG_MIN (BUG#31799). */ 200 | uval = (unsigned long int) 0 - uval; 201 | } 202 | } 203 | 204 | p = &buffer[sizeof(buffer) - 1]; 205 | *p = '\0'; 206 | new_val = (long) (uval / 10); 207 | *--p = '0' + (char) (uval - (unsigned long) new_val * 10); 208 | val = new_val; 209 | 210 | while (val != 0) { 211 | new_val = val / 10; 212 | *--p = '0' + (char) (val - new_val * 10); 213 | val = new_val; 214 | } 215 | while ((*dst++ = *p++) != 0); 216 | return dst - 1; 217 | } 218 | 219 | char *resolveToIp4Str(char *hostname) { 220 | char *res = malloc(sizeof(char) * 16);//for ipv4 only e.g. '255.255.255.255\0', max length = 16 221 | memset(res, 0, sizeof(char) * 16); 222 | struct hostent *ghbn = gethostbyname(hostname); 223 | if (!ghbn) { 224 | free(res); 225 | log_error("cannot resolve %s", hostname); 226 | return NULL; 227 | } 228 | char *address = inet_ntoa(*(struct in_addr *) ghbn->h_addr); 229 | strcpy(res, address); 230 | return res; 231 | } 232 | 233 | int manual_jvm_gc(int java_pid) { 234 | static int nspid = -1; 235 | if (nspid < 0) { 236 | nspid = init_jvm_comm_socket(java_pid); 237 | } 238 | int argc = 2; 239 | char *argv[2] = {"jcmd", "GC.run"}; 240 | return jvm_run_cmd(nspid, argc, argv); 241 | } 242 | 243 | void save_to_binary_file(char *filename, char *buf, int size) { 244 | FILE *ptr = fopen(filename, "wb"); 245 | fwrite(buf, sizeof(char), size, ptr); 246 | fclose(ptr); 247 | } 248 | 249 | 250 | void increase_long_by_one(unsigned char bytes[8]){ 251 | for(int i = 0; i < 8; i++){ 252 | bytes[i] += 1; 253 | if(bytes[i] != 0) 254 | break; 255 | } 256 | } -------------------------------------------------------------------------------- /src/utils/jvm_cmd.c: -------------------------------------------------------------------------------- 1 | #include "jvm_cmd.h" 2 | 3 | /* 4 | * Copyright 2016 Andrei Pangin 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define MAX_PATH 1024 32 | 33 | 34 | #ifdef __APPLE__ 35 | 36 | #include 37 | 38 | // macOS has a secure per-user temporary directory 39 | const char* get_temp_directory() { 40 | static char temp_path_storage[MAX_PATH] = {0}; 41 | 42 | if (temp_path_storage[0] == 0) { 43 | int path_size = confstr(_CS_DARWIN_USER_TEMP_DIR, temp_path_storage, MAX_PATH); 44 | if (path_size == 0 || path_size > MAX_PATH) { 45 | strcpy(temp_path_storage, "/tmp"); 46 | } 47 | } 48 | return temp_path_storage; 49 | } 50 | 51 | int get_process_info(int pid, uid_t* uid, gid_t* gid, int* nspid) { 52 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 53 | struct kinfo_proc info; 54 | size_t len = sizeof(info); 55 | 56 | if (sysctl(mib, 4, &info, &len, NULL, 0) < 0 || len <= 0) { 57 | return 0; 58 | } 59 | 60 | *uid = info.kp_eproc.e_ucred.cr_uid; 61 | *gid = info.kp_eproc.e_ucred.cr_gid; 62 | *nspid = pid; 63 | return 1; 64 | } 65 | 66 | // This is a Linux-specific API; nothing to do on macOS 67 | int enter_mount_ns(int pid) { 68 | return 1; 69 | } 70 | 71 | #else // Linux 72 | 73 | const char *get_temp_directory() { 74 | return "/tmp"; 75 | } 76 | 77 | int get_process_info(int pid, uid_t *uid, gid_t *gid, int *nspid) { 78 | char status[64]; 79 | snprintf(status, sizeof(status), "/proc/%d/status", pid); 80 | 81 | FILE *status_file = fopen(status, "r"); 82 | if (status_file == NULL) { 83 | return 0; 84 | } 85 | 86 | char *line = NULL; 87 | size_t size; 88 | 89 | while (getline(&line, &size, status_file) != -1) { 90 | if (strncmp(line, "Uid:", 4) == 0) { 91 | // Get the effective UID, which is the second value in the line 92 | *uid = (uid_t) atoi(strchr(line + 5, '\t')); 93 | } else if (strncmp(line, "Gid:", 4) == 0) { 94 | // Get the effective GID, which is the second value in the line 95 | *gid = (gid_t) atoi(strchr(line + 5, '\t')); 96 | } else if (strncmp(line, "NStgid:", 7) == 0) { 97 | // PID namespaces can be nested; the last one is the innermost one 98 | *nspid = atoi(strrchr(line, '\t')); 99 | } 100 | } 101 | 102 | free(line); 103 | fclose(status_file); 104 | return 1; 105 | } 106 | 107 | int enter_mount_ns(int pid) { 108 | // We're leaking the oldns and newns descriptors, but this is a short-running 109 | // tool, so they will be closed when the process exits anyway. 110 | int oldns, newns; 111 | char curnspath[128], newnspath[128]; 112 | struct stat oldns_stat, newns_stat; 113 | 114 | snprintf(curnspath, sizeof(curnspath), "/proc/self/ns/mnt"); 115 | snprintf(newnspath, sizeof(newnspath), "/proc/%d/ns/mnt", pid); 116 | 117 | if ((oldns = open(curnspath, O_RDONLY)) < 0 || 118 | (newns = open(newnspath, O_RDONLY)) < 0) { 119 | return 0; 120 | } 121 | 122 | if (fstat(oldns, &oldns_stat) < 0 || fstat(newns, &newns_stat) < 0) { 123 | return 0; 124 | } 125 | if (oldns_stat.st_ino == newns_stat.st_ino) { 126 | // Don't try to call setns() if we're in the same namespace already. 127 | return 1; 128 | } 129 | 130 | // Some ancient Linux distributions do not have setns() function 131 | return syscall(__NR_setns, newns, 0) < 0 ? 0 : 1; 132 | } 133 | 134 | #endif 135 | 136 | 137 | // Check if remote JVM has already opened socket for Dynamic Attach 138 | static int check_socket(int pid) { 139 | char path[MAX_PATH]; 140 | snprintf(path, MAX_PATH, "%s/.java_pid%d", get_temp_directory(), pid); 141 | 142 | struct stat stats; 143 | return stat(path, &stats) == 0 && S_ISSOCK(stats.st_mode); 144 | } 145 | 146 | // Check if a file is owned by current user 147 | static int check_file_owner(const char *path) { 148 | struct stat stats; 149 | if (stat(path, &stats) == 0 && stats.st_uid == geteuid()) { 150 | return 1; 151 | } 152 | 153 | // Some mounted filesystems may change the ownership of the file. 154 | // JVM will not trust such file, so it's better to remove it and try a different path 155 | unlink(path); 156 | return 0; 157 | } 158 | 159 | // Force remote JVM to start Attach listener. 160 | // HotSpot will start Attach listener in response to SIGQUIT if it sees .attach_pid file 161 | static int start_attach_mechanism(int pid, int nspid) { 162 | char path[MAX_PATH]; 163 | snprintf(path, MAX_PATH, "/proc/%d/cwd/.attach_pid%d", nspid, nspid); 164 | 165 | int fd = creat(path, 0660); 166 | if (fd == -1 || (close(fd) == 0 && !check_file_owner(path))) { 167 | // Failed to create attach trigger in current directory. Retry in /tmp 168 | snprintf(path, MAX_PATH, "%s/.attach_pid%d", get_temp_directory(), nspid); 169 | fd = creat(path, 0660); 170 | if (fd == -1) { 171 | return 0; 172 | } 173 | close(fd); 174 | } 175 | 176 | // We have to still use the host namespace pid here for the kill() call 177 | kill(pid, SIGQUIT); 178 | 179 | int result; 180 | struct timespec ts = {0, 100000000}; 181 | int retry = 0; 182 | do { 183 | nanosleep(&ts, NULL); 184 | result = check_socket(nspid); 185 | } while (!result && ++retry < 10); 186 | 187 | unlink(path); 188 | return result; 189 | } 190 | 191 | // Connect to UNIX domain socket created by JVM for Dynamic Attach 192 | static int connect_socket(int pid) { 193 | int fd = socket(PF_UNIX, SOCK_STREAM, 0); 194 | if (fd == -1) { 195 | return -1; 196 | } 197 | 198 | struct sockaddr_un addr; 199 | addr.sun_family = AF_UNIX; 200 | snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.java_pid%d", get_temp_directory(), pid); 201 | 202 | if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) { 203 | close(fd); 204 | return -1; 205 | } 206 | return fd; 207 | } 208 | 209 | // Send command with arguments to socket 210 | static int write_command(int fd, int argc, char **argv) { 211 | // Protocol version 212 | if (write(fd, "1", 2) <= 0) { 213 | return 0; 214 | } 215 | 216 | int i; 217 | for (i = 0; i < 4; i++) { 218 | const char *arg = i < argc ? argv[i] : ""; 219 | if (write(fd, arg, strlen(arg) + 1) <= 0) { 220 | return 0; 221 | } 222 | } 223 | return 1; 224 | } 225 | 226 | //return nspid 227 | int init_jvm_comm_socket(int pid) { 228 | if (pid == 0) { 229 | perror("Invalid pid provided"); 230 | return 1; 231 | } 232 | uid_t my_uid = geteuid(); 233 | gid_t my_gid = getegid(); 234 | uid_t target_uid = my_uid; 235 | gid_t target_gid = my_gid; 236 | int nspid = pid; 237 | if (!get_process_info(pid, &target_uid, &target_gid, &nspid)) { 238 | fprintf(stderr, "Process %d not found\n", pid); 239 | return 1; 240 | } 241 | 242 | // Make sure our /tmp and target /tmp is the same 243 | if (enter_mount_ns(pid) < 0) { 244 | fprintf(stderr, "WARNING: couldn't enter target process mnt namespace\n"); 245 | } 246 | 247 | // Dynamic attach is allowed only for the clients with the same euid/egid. 248 | // If we are running under root, switch to the required euid/egid automatically. 249 | if ((my_gid != target_gid && setegid(target_gid) != 0) || 250 | (my_uid != target_uid && seteuid(target_uid) != 0)) { 251 | perror("Failed to change credentials to match the target process"); 252 | return 1; 253 | } 254 | 255 | // Make write() return EPIPE instead of silent process termination 256 | signal(SIGPIPE, SIG_IGN); 257 | 258 | if (!check_socket(nspid) && !start_attach_mechanism(pid, nspid)) { 259 | perror("Could not start attach mechanism"); 260 | return 1; 261 | } 262 | return nspid; 263 | } 264 | 265 | int jvm_run_cmd(int nspid, int argc, char **argv) { 266 | int fd = connect_socket(nspid); 267 | if (fd == -1) { 268 | perror("Could not connect to socket"); 269 | return 1; 270 | } 271 | printf("Connected to remote JVM\n"); 272 | if (!write_command(fd, argc, argv)) { 273 | perror("Error writing to socket"); 274 | close(fd); 275 | return 1; 276 | } 277 | 278 | close(fd); 279 | return 0; 280 | } -------------------------------------------------------------------------------- /src/utils/tests/jvm_gc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 Andrei Pangin 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #define MAX_PATH 1024 32 | 33 | 34 | #ifdef __APPLE__ 35 | 36 | #include 37 | 38 | // macOS has a secure per-user temporary directory 39 | const char* get_temp_directory() { 40 | static char temp_path_storage[MAX_PATH] = {0}; 41 | 42 | if (temp_path_storage[0] == 0) { 43 | int path_size = confstr(_CS_DARWIN_USER_TEMP_DIR, temp_path_storage, MAX_PATH); 44 | if (path_size == 0 || path_size > MAX_PATH) { 45 | strcpy(temp_path_storage, "/tmp"); 46 | } 47 | } 48 | return temp_path_storage; 49 | } 50 | 51 | int get_process_info(int pid, uid_t* uid, gid_t* gid, int* nspid) { 52 | int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; 53 | struct kinfo_proc info; 54 | size_t len = sizeof(info); 55 | 56 | if (sysctl(mib, 4, &info, &len, NULL, 0) < 0 || len <= 0) { 57 | return 0; 58 | } 59 | 60 | *uid = info.kp_eproc.e_ucred.cr_uid; 61 | *gid = info.kp_eproc.e_ucred.cr_gid; 62 | *nspid = pid; 63 | return 1; 64 | } 65 | 66 | // This is a Linux-specific API; nothing to do on macOS 67 | int enter_mount_ns(int pid) { 68 | return 1; 69 | } 70 | 71 | #else // Linux 72 | 73 | const char* get_temp_directory() { 74 | return "/tmp"; 75 | } 76 | 77 | int get_process_info(int pid, uid_t* uid, gid_t* gid, int* nspid) { 78 | char status[64]; 79 | snprintf(status, sizeof(status), "/proc/%d/status", pid); 80 | 81 | FILE* status_file = fopen(status, "r"); 82 | if (status_file == NULL) { 83 | return 0; 84 | } 85 | 86 | char* line = NULL; 87 | size_t size; 88 | 89 | while (getline(&line, &size, status_file) != -1) { 90 | if (strncmp(line, "Uid:", 4) == 0) { 91 | // Get the effective UID, which is the second value in the line 92 | *uid = (uid_t)atoi(strchr(line + 5, '\t')); 93 | } else if (strncmp(line, "Gid:", 4) == 0) { 94 | // Get the effective GID, which is the second value in the line 95 | *gid = (gid_t)atoi(strchr(line + 5, '\t')); 96 | } else if (strncmp(line, "NStgid:", 7) == 0) { 97 | // PID namespaces can be nested; the last one is the innermost one 98 | *nspid = atoi(strrchr(line, '\t')); 99 | } 100 | } 101 | 102 | free(line); 103 | fclose(status_file); 104 | return 1; 105 | } 106 | 107 | int enter_mount_ns(int pid) { 108 | // We're leaking the oldns and newns descriptors, but this is a short-running 109 | // tool, so they will be closed when the process exits anyway. 110 | int oldns, newns; 111 | char curnspath[128], newnspath[128]; 112 | struct stat oldns_stat, newns_stat; 113 | 114 | snprintf(curnspath, sizeof(curnspath), "/proc/self/ns/mnt"); 115 | snprintf(newnspath, sizeof(newnspath), "/proc/%d/ns/mnt", pid); 116 | 117 | if ((oldns = open(curnspath, O_RDONLY)) < 0 || 118 | (newns = open(newnspath, O_RDONLY)) < 0) { 119 | return 0; 120 | } 121 | 122 | if (fstat(oldns, &oldns_stat) < 0 || fstat(newns, &newns_stat) < 0) { 123 | return 0; 124 | } 125 | if (oldns_stat.st_ino == newns_stat.st_ino) { 126 | // Don't try to call setns() if we're in the same namespace already. 127 | return 1; 128 | } 129 | 130 | // Some ancient Linux distributions do not have setns() function 131 | return syscall(__NR_setns, newns, 0) < 0 ? 0 : 1; 132 | } 133 | 134 | #endif 135 | 136 | 137 | // Check if remote JVM has already opened socket for Dynamic Attach 138 | static int check_socket(int pid) { 139 | char path[MAX_PATH]; 140 | snprintf(path, MAX_PATH, "%s/.java_pid%d", get_temp_directory(), pid); 141 | 142 | struct stat stats; 143 | return stat(path, &stats) == 0 && S_ISSOCK(stats.st_mode); 144 | } 145 | 146 | // Check if a file is owned by current user 147 | static int check_file_owner(const char* path) { 148 | struct stat stats; 149 | if (stat(path, &stats) == 0 && stats.st_uid == geteuid()) { 150 | return 1; 151 | } 152 | 153 | // Some mounted filesystems may change the ownership of the file. 154 | // JVM will not trust such file, so it's better to remove it and try a different path 155 | unlink(path); 156 | return 0; 157 | } 158 | 159 | // Force remote JVM to start Attach listener. 160 | // HotSpot will start Attach listener in response to SIGQUIT if it sees .attach_pid file 161 | static int start_attach_mechanism(int pid, int nspid) { 162 | char path[MAX_PATH]; 163 | snprintf(path, MAX_PATH, "/proc/%d/cwd/.attach_pid%d", nspid, nspid); 164 | 165 | int fd = creat(path, 0660); 166 | if (fd == -1 || (close(fd) == 0 && !check_file_owner(path))) { 167 | // Failed to create attach trigger in current directory. Retry in /tmp 168 | snprintf(path, MAX_PATH, "%s/.attach_pid%d", get_temp_directory(), nspid); 169 | fd = creat(path, 0660); 170 | if (fd == -1) { 171 | return 0; 172 | } 173 | close(fd); 174 | } 175 | 176 | // We have to still use the host namespace pid here for the kill() call 177 | kill(pid, SIGQUIT); 178 | 179 | int result; 180 | struct timespec ts = {0, 100000000}; 181 | int retry = 0; 182 | do { 183 | nanosleep(&ts, NULL); 184 | result = check_socket(nspid); 185 | } while (!result && ++retry < 10); 186 | 187 | unlink(path); 188 | return result; 189 | } 190 | 191 | // Connect to UNIX domain socket created by JVM for Dynamic Attach 192 | static int connect_socket(int pid) { 193 | int fd = socket(PF_UNIX, SOCK_STREAM, 0); 194 | if (fd == -1) { 195 | return -1; 196 | } 197 | 198 | struct sockaddr_un addr; 199 | addr.sun_family = AF_UNIX; 200 | snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/.java_pid%d", get_temp_directory(), pid); 201 | 202 | if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) { 203 | close(fd); 204 | return -1; 205 | } 206 | return fd; 207 | } 208 | 209 | // Send command with arguments to socket 210 | static int write_command(int fd, int argc, char** argv) { 211 | // Protocol version 212 | if (write(fd, "1", 2) <= 0) { 213 | return 0; 214 | } 215 | 216 | int i; 217 | for (i = 0; i < 4; i++) { 218 | const char* arg = i < argc ? argv[i] : ""; 219 | if (write(fd, arg, strlen(arg) + 1) <= 0) { 220 | return 0; 221 | } 222 | } 223 | return 1; 224 | } 225 | 226 | // Mirror response from remote JVM to stdout 227 | static int read_response(int fd) { 228 | char buf[8192]; 229 | ssize_t bytes = read(fd, buf, sizeof(buf) - 1); 230 | if (bytes <= 0) { 231 | perror("Error reading response"); 232 | return 1; 233 | } 234 | 235 | // First line of response is the command result code 236 | buf[bytes] = 0; 237 | int result = atoi(buf); 238 | 239 | do { 240 | fwrite(buf, 1, bytes, stdout); 241 | bytes = read(fd, buf, sizeof(buf)); 242 | } while (bytes > 0); 243 | 244 | return result; 245 | } 246 | 247 | int main(int argc, char** argv) { 248 | if (argc < 3) { 249 | printf("Usage: jattach ...\n"); 250 | return 1; 251 | } 252 | 253 | int pid = atoi(argv[1]); 254 | if (pid == 0) { 255 | perror("Invalid pid provided"); 256 | return 1; 257 | } 258 | 259 | uid_t my_uid = geteuid(); 260 | gid_t my_gid = getegid(); 261 | uid_t target_uid = my_uid; 262 | gid_t target_gid = my_gid; 263 | int nspid = pid; 264 | if (!get_process_info(pid, &target_uid, &target_gid, &nspid)) { 265 | fprintf(stderr, "Process %d not found\n", pid); 266 | return 1; 267 | } 268 | 269 | // Make sure our /tmp and target /tmp is the same 270 | if (enter_mount_ns(pid) < 0) { 271 | fprintf(stderr, "WARNING: couldn't enter target process mnt namespace\n"); 272 | } 273 | 274 | // Dynamic attach is allowed only for the clients with the same euid/egid. 275 | // If we are running under root, switch to the required euid/egid automatically. 276 | if ((my_gid != target_gid && setegid(target_gid) != 0) || 277 | (my_uid != target_uid && seteuid(target_uid) != 0)) { 278 | perror("Failed to change credentials to match the target process"); 279 | return 1; 280 | } 281 | 282 | // Make write() return EPIPE instead of silent process termination 283 | signal(SIGPIPE, SIG_IGN); 284 | 285 | if (!check_socket(nspid) && !start_attach_mechanism(pid, nspid)) { 286 | perror("Could not start attach mechanism"); 287 | return 1; 288 | } 289 | 290 | int fd = connect_socket(nspid); 291 | if (fd == -1) { 292 | perror("Could not connect to socket"); 293 | return 1; 294 | } 295 | 296 | printf("Connected to remote JVM\n"); 297 | if (!write_command(fd, argc - 2, argv + 2)) { 298 | perror("Error writing to socket"); 299 | close(fd); 300 | return 1; 301 | } 302 | 303 | printf("Response code = "); 304 | fflush(stdout); 305 | 306 | int result = read_response(fd); 307 | printf("\n"); 308 | close(fd); 309 | 310 | return result; 311 | } -------------------------------------------------------------------------------- /src/utils/tokenizer.c: -------------------------------------------------------------------------------- 1 | // 2 | // Created by yche on 5/4/18. 3 | // 4 | #include "log.h" 5 | #include "tokenizer.h" 6 | 7 | #include 8 | 9 | #if defined(__BMI__) 10 | 11 | #include 12 | 13 | #elif defined(__SSE4_2__) 14 | 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include "uv.h" 21 | 22 | #include "../utils/utils.h" 23 | 24 | const char *const post_keys[] = {"interface", "method", "parameterTypesString", "parameter"}; 25 | const int post_keys_size[] = {9, 6, 20, 9}; 26 | // magic, magic, reg/2way/fastjson-serialization, status (4 bytes); request id (8 bytes) 27 | const char header_bytes[] = {(char) 0xda, (char) 0xbb, (char) (0x80 | 0x40 | 0x06), (char) 0x00, 28 | (char) 0x00, (char) 0x00, (char) 0x00, (char) 0x00, 29 | (char) 0x00, (char) 0x00, (char) 0x00, (char) 0x00}; 30 | 31 | const char *const hard_code_values[] = {"\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"\n", 32 | "\"hash\"\n", 33 | "\"Ljava/lang/String;\"\n", 34 | "\n{\"path\":\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"}\n"}; 35 | uv_mutex_t lock; 36 | 37 | int serial_split(const char *str, short len, short *off) { 38 | #ifdef DEBUG 39 | assert(len > 0 && len < 32767); 40 | #endif 41 | off[0] = -1; 42 | for (short i = 0, next = 1;;) { 43 | while (str[i] != '=') { i++; } 44 | off[next] = i; 45 | next++; 46 | 47 | while (i < len && str[i] != '&') { i++; } 48 | off[next] = i; 49 | next++; 50 | 51 | if (i == len) { return next; } 52 | } 53 | } 54 | 55 | #if defined(__SSE4_2__) 56 | 57 | inline int __tzcnt_u32_using_popcnt_cmpgt(unsigned int x) { 58 | int ret_cnt = 0; 59 | int half_bits = x & (0xff); 60 | if (half_bits == 0) { 61 | half_bits = (x >> 8) & (0xff); 62 | ret_cnt = 8; 63 | } 64 | 65 | __m128i pivot_u = _mm_set1_epi16(half_bits); 66 | __m128i inspected_ele = _mm_set_epi16(0xff, 0x7f, 0x3f, 0x1f, 0xf, 0x7, 0x3, 0x1); 67 | __m128i trunc_pivot_u = _mm_and_si128(pivot_u, inspected_ele); 68 | 69 | __m128i pivot_new = _mm_set1_epi16(0); 70 | __m128i cmp_res = _mm_cmpeq_epi16(trunc_pivot_u, pivot_new); 71 | 72 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 73 | ret_cnt += _mm_popcnt_u32(mask) >> 1; 74 | return ret_cnt; 75 | } 76 | 77 | inline int sse4_split_efficient(char *str, short len, short *off) { 78 | #ifdef DEBUG 79 | assert(len > 0 && len + 16 < 32767); 80 | #endif 81 | char restore_buf[16]; 82 | memcpy(restore_buf, str + len, sizeof(char) * 16); 83 | memset(str + len, 0, 16); 84 | off[0] = -1; 85 | for (short i = 0, next = 1;;) { 86 | // 1st: advance fo find first '=' 87 | while (true) { 88 | __m128i pivot_u = _mm_set1_epi8('='); 89 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 90 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 91 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 92 | 93 | int advance = (mask == 0 ? 16 : __tzcnt_u32_using_popcnt_cmpgt(mask)); 94 | i += advance; 95 | if (advance < 16) { break; } 96 | } 97 | off[next] = i; 98 | next++; 99 | 100 | // 2nd: advance to find first '&' 101 | while (true) { 102 | __m128i pivot_u = _mm_set1_epi8('&'); 103 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 104 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 105 | 106 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 107 | 108 | int advance = (mask == 0 ? 16 : __tzcnt_u32_using_popcnt_cmpgt(mask)); 109 | i += advance; 110 | if (advance < 16 || i >= len) { break; } 111 | } 112 | off[next] = i < len ? i : len; 113 | next++; 114 | 115 | if (i >= len) { 116 | memcpy(str + len, restore_buf, sizeof(char) * 16); 117 | return next; 118 | } 119 | } 120 | } 121 | 122 | #endif 123 | 124 | #if defined(__BMI__) 125 | 126 | inline int avx_split_efficient(char *str, short len, short *off) { 127 | #ifdef DEBUG 128 | assert(len > 0 && len + 16 < 32767); 129 | #endif 130 | memset(str + len, 0, 16); 131 | off[0] = -1; 132 | for (short i = 0, next = 1;;) { 133 | // 1st: advance fo find first '=' 134 | while (true) { 135 | __m128i pivot_u = _mm_set1_epi8('='); 136 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 137 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 138 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 139 | 140 | int advance = (mask == 0 ? 16 : __tzcnt_u32(mask)); 141 | i += advance; 142 | if (advance < 16) { break; } 143 | } 144 | off[next] = i; 145 | next++; 146 | 147 | // 2nd: advance to find first '&' 148 | while (true) { 149 | __m128i pivot_u = _mm_set1_epi8('&'); 150 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + i)); 151 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 152 | 153 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 154 | 155 | int advance = (mask == 0 ? 16 : __tzcnt_u32(mask)); 156 | i += advance; 157 | if (advance < 16 || i >= len) { break; } 158 | } 159 | off[next] = i < len ? i : len; 160 | next++; 161 | 162 | if (i >= len) { return next; } 163 | } 164 | } 165 | 166 | #endif 167 | 168 | int simd_split_efficient(char *str, short len, short *off) { 169 | #if defined(__BMI__) 170 | // printf("bmi"); 171 | return avx_split_efficient(str, len, off); 172 | #elif defined(__SSE4_2__) 173 | // printf("sse"); 174 | return sse4_split_efficient(str, len, off); 175 | #else 176 | return serial_split(str, len, off); 177 | #endif 178 | } 179 | 180 | inline int hex2int(char ch) { 181 | return ch < 'A' ? ch - '0' : 10 + (ch < 'a' ? ch - 'A' : ch - 'a'); 182 | } 183 | 184 | int serialize_str(char *str, const short *off, short len_off, int offset, char *res, int j) { 185 | for (int i = 0; i < len_off / 2; i++) { 186 | int real_i = i * 2; 187 | int key_off_beg = off[real_i] + 1; 188 | int key_off_end = off[real_i + 1]; 189 | //log_debug("key length: %d, key: %.*s", key_off_end - key_off_beg, key_off_end - key_off_beg, str+key_off_beg); 190 | // add post_keys[j][0] == str[key_off_beg] to reduce strncmp invocations 191 | if (post_keys[j][0] == str[key_off_beg] && key_off_end - key_off_beg == post_keys_size[j]) { 192 | if (strncmp(post_keys[j], str + key_off_beg, key_off_end - key_off_beg) == 0) { 193 | res[offset] = '"'; 194 | offset++; 195 | 196 | // copy and decoding url e.g. '%2F' to '/', wrap as a inline function later 197 | int val_off_beg = off[real_i + 1] + 1; 198 | int val_off_end = off[real_i + 2]; 199 | for (int last_seek, seek = val_off_beg; seek < val_off_end;) { 200 | last_seek = seek; 201 | 202 | // find first % or end 203 | #if defined(__BMI__) 204 | // printf("bmi..."); 205 | while (true) { 206 | __m128i pivot_u = _mm_set1_epi8('%'); 207 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + seek)); 208 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 209 | 210 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 211 | 212 | int advance = (mask == 0 ? 16 : __tzcnt_u32(mask)); 213 | seek += advance; 214 | if (advance < 16 || seek >= val_off_end) { break; } 215 | } 216 | if (seek > val_off_end) { seek = val_off_end; } 217 | 218 | #elif defined(__SSE4_2__) 219 | // printf("sse..."); 220 | while (true) { 221 | __m128i pivot_u = _mm_set1_epi8('%'); 222 | __m128i inspected_ele = _mm_loadu_si128((__m128i *) (str + seek)); 223 | __m128i cmp_res = _mm_cmpeq_epi8(pivot_u, inspected_ele); 224 | 225 | int mask = _mm_movemask_epi8(cmp_res); // 16 bits 226 | 227 | int advance = (mask == 0 ? 16 : __tzcnt_u32_using_popcnt_cmpgt(mask)); 228 | seek += advance; 229 | if (advance < 16 || seek >= val_off_end) { break; } 230 | } 231 | if (seek > val_off_end) { seek = val_off_end; } 232 | #else 233 | // printf("serial..."); 234 | while (seek < val_off_end && str[seek] != '%') { seek++; } 235 | #endif 236 | int advance = seek - last_seek; 237 | memcpy(res + offset, str + last_seek, advance); 238 | offset += advance; 239 | // decode %xx 240 | if (seek != val_off_end) { 241 | res[offset] = (char) (hex2int(str[seek + 1]) * 16 + hex2int(str[seek + 2])); 242 | offset++; 243 | seek += 3; //skip "%xx" 244 | } 245 | } 246 | 247 | memcpy(res + offset, "\"\n", 2); 248 | offset += 2; 249 | break; 250 | } 251 | } 252 | } 253 | return offset; 254 | } 255 | 256 | int serialize_path_dict(char *str, const short *off, short len_off, int offset, char *res) { 257 | for (int i = 0; i < len_off / 2; i++) { 258 | int real_i = i * 2; 259 | int beg = off[real_i] + 1; 260 | int end = off[real_i + 1]; 261 | if (end - beg == post_keys_size[0]) { 262 | if (strncmp(post_keys[0], str + beg, end - beg) == 0) { 263 | memcpy(res + offset, "{\"path\":\"", 9); 264 | offset += 9; 265 | 266 | int advance = off[real_i + 2] - off[real_i + 1] - 1; 267 | memcpy(res + offset, str + off[real_i + 1] + 1, advance); 268 | 269 | offset += advance; 270 | memcpy(res + offset, "\"}\n", 3); 271 | offset += 3; 272 | break; 273 | } 274 | } 275 | } 276 | return offset; 277 | } 278 | 279 | void generate_header(char *res, int body_len, int64_t request_id) { 280 | memcpy(res, header_bytes, 12); 281 | // int64_t request_id = get_and_incre_req_id(); 282 | long_to_8bytes(request_id, res + 4); 283 | 284 | res[12] = (char) ((body_len >> 24) & 0xFF); 285 | res[13] = (char) ((body_len >> 16) & 0xFF); 286 | res[14] = (char) ((body_len >> 8) & 0xFF); 287 | res[15] = (char) (body_len & 0xFF); 288 | } 289 | 290 | int generate_body(char *res, char *str_buffer, short *off, short len_off) { 291 | int offset = 16; 292 | memcpy(res + offset, "\"2.0.1\"\n", 8); 293 | offset += 8; 294 | offset = serialize_str(str_buffer, off, len_off, offset, res, 0); 295 | memcpy(res + offset, "null\n", 5); 296 | offset += 5; 297 | offset = serialize_str(str_buffer, off, len_off, offset, res, 1); 298 | offset = serialize_str(str_buffer, off, len_off, offset, res, 2); 299 | offset = serialize_str(str_buffer, off, len_off, offset, res, 3); 300 | offset = serialize_path_dict(str_buffer, off, len_off, offset, res); 301 | 302 | return offset; 303 | } 304 | 305 | // assume res allocated, return size 306 | int generate_res_in_place(char *res, char *str, short len, int64_t req_id) { 307 | short off[32]; // 32 for at most 16 (key, val) 308 | short len_off = (short) simd_split_efficient(str, len, off); 309 | 310 | int offset = generate_body(res, str, off, len_off); 311 | generate_header(res, offset - 16, req_id); 312 | return offset; 313 | } 314 | 315 | static const char *method_detail_values_onea = "\"2.0.1\"\n\"com.alibaba.dubbo.performance.demo.provider.IHelloService\"\nnull\n\"hash\"\n\"Ljava/lang/String;\"\n"; 316 | 317 | int generate_body_hard_code(char *res, char *parameter_str, int parameter_len) { 318 | 319 | memcpy(res + 16, method_detail_values_onea, 101 * sizeof(char)); 320 | int offset = 16 + 101; 321 | 322 | res[offset] = '\"'; 323 | offset++; 324 | memcpy(res + offset, parameter_str, parameter_len); 325 | offset += parameter_len; 326 | res[offset] = '\"'; 327 | offset++; 328 | 329 | const size_t path_len = strlen(hard_code_values[3]); 330 | memcpy(res + offset, hard_code_values[3], path_len); 331 | offset += path_len; 332 | return offset; 333 | } 334 | 335 | int generate_res_in_place_hard_code(char *res, char *str, short len, int64_t req_id) { 336 | int offset = generate_body_hard_code(res, str + 136, len - 136); 337 | generate_header(res, offset - 16, req_id); 338 | return offset; 339 | } -------------------------------------------------------------------------------- /src/provider_agent.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "provider_agent.h" 5 | #include "utils/log.h" 6 | #include "utils/etcd.h" 7 | #include "utils/utils.h" 8 | #include "config.h" 9 | #include "utils/tokenizer.h" 10 | 11 | #define P_DEFAULT_BACKLOG 256 // see https://linux.die.net/man/2/listen 12 | #define P_DEFAULT_PACKAGE_SIZE 4096 13 | #define P_DEFAULT_LEASE_TTL 600 14 | 15 | static const char *response_template = "HTTP/1.1 200 OK\r\nContent-Length: \r\n\r\n"; 16 | static const int response_template_len = 39; 17 | 18 | uv_loop_t *main_loop; 19 | struct sockaddr_in addr; 20 | struct sockaddr_in dubbo_addr; 21 | struct addrinfo hints; 22 | uv_getaddrinfo_t resolver; 23 | char provider_scale; 24 | 25 | int server_port; 26 | int dubbo_port; 27 | char *etcd_url; 28 | char *lease_id; 29 | 30 | uv_thread_t keep_alive_tid; 31 | p_tcp_server_t *p_tcp_servers[P_EVENT_LOOP_NUM]; 32 | 33 | void test_dubbo_connection(); 34 | 35 | void connect_to_dubbo_long_live(tcp_relay_t *tcp_relay); 36 | 37 | tcp_relay_t *init_tcp_relay_t(size_t size) { 38 | tcp_relay_t *relay = (tcp_relay_t *) malloc(sizeof(tcp_relay_t)); 39 | relay->tcp_conn_1 = NULL; 40 | relay->tcp_conn_2 = NULL; 41 | relay->dubbo_connection = NULL; 42 | relay->raw_buf1 = malloc(sizeof(char) * size); 43 | relay->raw_buf2 = malloc(sizeof(char) * size); 44 | relay->max_buffer_size = size; 45 | relay->raw_buf1_size = 0; 46 | relay->raw_buf2_size = 0; 47 | return relay; 48 | } 49 | 50 | p_tcp_server_t *init_p_tcp_server_t(int tid) { 51 | p_tcp_server_t *tcp_server = malloc(sizeof(p_tcp_server_t)); 52 | tcp_server->work_event_loop = uv_loop_new(); 53 | tcp_server->tid = tid; 54 | return tcp_server; 55 | } 56 | 57 | void free_tcp_relay_t(tcp_relay_t *tcp_relay) { 58 | free(tcp_relay->tcp_conn_1); 59 | free(tcp_relay->tcp_conn_2); 60 | free(tcp_relay->dubbo_connection); 61 | free(tcp_relay); 62 | } 63 | 64 | void keep_alive_thread_work(void *arg) { 65 | char *ID = (char *) arg; 66 | for (;;) { 67 | sleep((P_DEFAULT_LEASE_TTL - 10) > 3 ? (P_DEFAULT_LEASE_TTL - 10) 68 | : 3);//sleep for (TTL-10) to update lease time to etcd 69 | log_info("send heartbeat to etcd."); 70 | etcd_keep_alive(etcd_url, ID); 71 | } 72 | } 73 | 74 | void start_keep_alive_etcd(char *ID) { 75 | uv_thread_create(&keep_alive_tid, keep_alive_thread_work, (void *) ID); 76 | } 77 | 78 | void on_resolved(uv_getaddrinfo_t *resolver, int status, struct addrinfo *res) { 79 | if (status != 0) { 80 | log_fatal("error while resolving ip addr, reason: %s", uv_strerror(status)); 81 | exit(-1); 82 | } 83 | 84 | char addr[17] = {'\0'}; 85 | uv_ip4_name((struct sockaddr_in *) res->ai_addr, addr, 16); 86 | log_info("ipaddr is %s", addr); 87 | 88 | //register with etcd 89 | lease_id = etcd_grant(etcd_url, P_DEFAULT_LEASE_TTL);// request a 300s grantID 90 | char buff[2048]; 91 | sprintf(buff, "/dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/%c:%s:%d", provider_scale, addr, 92 | server_port); 93 | etcd_put(etcd_url, buff, lease_id); 94 | log_info("register endpoint: %s finished", buff); 95 | start_keep_alive_etcd(lease_id); 96 | 97 | test_dubbo_connection(); 98 | } 99 | 100 | void init_provider(char *provider_type, int server_p, int dubbo_p, char *etcd_u) { 101 | dubbo_port = dubbo_p; 102 | server_port = server_p; 103 | etcd_url = etcd_u; 104 | if (strcmp(provider_type, "provider-small") == 0) { 105 | provider_scale = '0'; 106 | } else if (strcmp(provider_type, "provider-medium") == 0) { 107 | provider_scale = '1'; 108 | } else if (strcmp(provider_type, "provider-large") == 0) { 109 | provider_scale = '2'; 110 | } else { 111 | log_fatal("unknown provider scale %s", provider_type); 112 | exit(-1); 113 | } 114 | 115 | uv_ip4_addr("127.0.0.1", dubbo_port, &dubbo_addr); 116 | uv_ip4_addr("0.0.0.0", server_port, &addr); 117 | hints.ai_family = PF_INET; 118 | hints.ai_socktype = SOCK_STREAM; 119 | hints.ai_protocol = IPPROTO_TCP; 120 | hints.ai_flags = 0; 121 | log_info("resolve %s ip addr...", provider_type); 122 | int r = uv_getaddrinfo(main_loop, &resolver, on_resolved, provider_type, NULL, &hints); 123 | 124 | if (r) { 125 | log_fatal("getaddrinfo call error"); 126 | exit(-1); 127 | } 128 | } 129 | 130 | void p_alloc_buffer_new(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 131 | buf->base = malloc(sizeof(char) * suggested_size); 132 | buf->len = suggested_size; 133 | } 134 | 135 | void p_alloc_buffer1(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 136 | tcp_relay_t *relay = handle->data; 137 | buf->base = relay->raw_buf1 + relay->raw_buf1_size; 138 | buf->len = relay->max_buffer_size - relay->raw_buf1_size; 139 | } 140 | 141 | void p_alloc_buffer2(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 142 | tcp_relay_t *relay = handle->data; 143 | buf->base = relay->raw_buf2 + relay->raw_buf2_size; 144 | buf->len = relay->max_buffer_size - relay->raw_buf2_size; 145 | } 146 | 147 | void p_close_tcp_relay_connection2(uv_handle_t *handle) { 148 | log_info("connection 2 close successful"); 149 | tcp_relay_t *tcp_relay = handle->data; 150 | free_tcp_relay_t(tcp_relay); 151 | } 152 | 153 | void p_close_tcp_relay_connection1(uv_handle_t *handle) { 154 | log_info("connection 1 close successful"); 155 | tcp_relay_t *tcp_relay = handle->data; 156 | uv_close((uv_handle_t *) tcp_relay->tcp_conn_2, p_close_tcp_relay_connection2); 157 | } 158 | 159 | void free_write_req_without_data(uv_write_t *req, int status) { 160 | free(req); 161 | } 162 | 163 | void free_write_req_with_data(uv_write_t *req, int status) { 164 | free(req->data); 165 | free(req); 166 | } 167 | 168 | void p_on_read_results(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 169 | //log_info("on p_on_read_results"); 170 | tcp_relay_t *tcp_relay = client->data; 171 | if (nread > 0) { 172 | tcp_relay->raw_buf2_size += nread; 173 | for (;;) { 174 | if (tcp_relay->raw_buf2_size < 16) 175 | return; 176 | int length = four_char_to_int(tcp_relay->raw_buf2 + 12); 177 | //log_info("length %d", length); 178 | int last_length = 16 + length; 179 | if (tcp_relay->raw_buf2_size < last_length) { return; } 180 | //int64_t req_id = bytes8_to_long(tcp_relay->raw_buf2 + 4); 181 | //log_info("req_id %lld", req_id); 182 | //log_info("dubbo res: %.*s", length, tcp_relay->raw_buf2 + 16); 183 | if (tcp_relay->raw_buf2[3] == 20) { 184 | char *response_all = malloc(sizeof(char) * 4096); 185 | char *response_body = response_all + 12; 186 | 187 | int hash_length = length - 3; 188 | char *hash_res = tcp_relay->raw_buf2 + 18; 189 | 190 | int res_current_length = 0; 191 | 192 | memcpy(response_body, response_template, response_template_len); 193 | res_current_length += response_template_len; 194 | int_to_string(response_body + 33, hash_length); 195 | 196 | memcpy(response_body + res_current_length, hash_res, hash_length); 197 | res_current_length += hash_length; 198 | int_to_four_char(res_current_length, response_all); 199 | memcpy(response_all + 4, tcp_relay->raw_buf2 + 4, 8); 200 | 201 | uv_write_t *write_req = malloc(sizeof(uv_write_t)); 202 | write_req->data = response_all; 203 | uv_buf_t local_buf = uv_buf_init(response_all, res_current_length + 12); 204 | //log_info("res: %.*s", res_current_length, response_body); 205 | uv_write(write_req, (uv_stream_t *) tcp_relay->tcp_conn_1, &local_buf, 1, free_write_req_with_data); 206 | } 207 | tcp_relay->raw_buf2_size -= last_length; 208 | memmove(tcp_relay->raw_buf2, tcp_relay->raw_buf2 + last_length, tcp_relay->raw_buf2_size); 209 | } 210 | } else if (nread < 0) { 211 | if (nread != UV_EOF) { 212 | log_error("Read error %s\n", uv_strerror(nread)); 213 | } 214 | uv_close((uv_handle_t *) tcp_relay->tcp_conn_1, p_close_tcp_relay_connection1); 215 | } 216 | } 217 | 218 | void p_read_connection_data_http_req(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 219 | //log_info("on p_read_connection_data"); 220 | tcp_relay_t *tcp_relay = client->data; 221 | if (nread > 0) { 222 | tcp_relay->raw_buf1_size += nread; 223 | for (;;) { 224 | if (tcp_relay->raw_buf1_size < 12) 225 | return; 226 | 227 | int package_length = four_char_to_int(tcp_relay->raw_buf1); 228 | //log_info("package size %d", package_length); 229 | int last_length = 12 + package_length; 230 | if (tcp_relay->raw_buf1_size < last_length) 231 | return; 232 | 233 | int64_t req_id = bytes8_to_long(tcp_relay->raw_buf1 + 4); 234 | //log_info("req_id %lld", req_id); 235 | char *dubbo_package = malloc(sizeof(char) * 4096); 236 | //log_info("length %d", package_length); 237 | int dubbo_package_size = generate_res_in_place(dubbo_package, tcp_relay->raw_buf1 + 12, 238 | package_length, req_id); 239 | 240 | uv_write_t *write_req = malloc(sizeof(uv_write_t)); 241 | write_req->data = dubbo_package; 242 | uv_buf_t local_buf = uv_buf_init(dubbo_package, dubbo_package_size); 243 | uv_write(write_req, (uv_stream_t *) tcp_relay->tcp_conn_2, &local_buf, 1, free_write_req_with_data); 244 | 245 | tcp_relay->raw_buf1_size -= last_length; 246 | memmove(tcp_relay->raw_buf1, tcp_relay->raw_buf1 + last_length, tcp_relay->raw_buf1_size); 247 | } 248 | } else if (nread < 0) { 249 | if (nread != UV_EOF) { 250 | log_error("Read error %s\n", uv_strerror(nread)); 251 | } 252 | //read from consumer agent, if fail or close, then we need to close the connection1 253 | uv_close((uv_handle_t *) tcp_relay->tcp_conn_1, p_close_tcp_relay_connection1); 254 | } 255 | } 256 | 257 | 258 | void p_on_duboo_connect(uv_connect_t *connection, int status) { 259 | tcp_relay_t *tcp_relay = connection->data; 260 | if (status == 0) { 261 | log_info("dubbo connect successful"); 262 | 263 | uv_stream_t *dubbo_stream = (uv_stream_t *) tcp_relay->tcp_conn_2; 264 | uv_read_start(dubbo_stream, p_alloc_buffer2, p_on_read_results);//start read from dubbo 265 | // if (provider_scale == '0') { 266 | // uv_stream_t *consumer_agent = (uv_stream_t *) tcp_relay->tcp_conn_1; 267 | // uv_read_start(consumer_agent, p_alloc_buffer_new, p_read_connection_data_dubbo); 268 | // } else { 269 | uv_stream_t *consumer_agent = (uv_stream_t *) tcp_relay->tcp_conn_1; 270 | uv_read_start(consumer_agent, p_alloc_buffer1, p_read_connection_data_http_req); 271 | //} 272 | } else { 273 | log_info("dubbo connect failed"); 274 | sleep(3);//sleep 3s 275 | log_info("retry to connection dubbo"); 276 | connect_to_dubbo_long_live(tcp_relay); 277 | } 278 | 279 | } 280 | 281 | void connect_to_dubbo_long_live(tcp_relay_t *tcp_relay) { 282 | log_info("init connection to dubbo"); 283 | p_tcp_server_t *p_tcp_server = tcp_relay->p_tcp_server; 284 | uv_tcp_t *dubbo_socket = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); 285 | uv_tcp_init(p_tcp_server->work_event_loop, dubbo_socket); 286 | uv_tcp_nodelay(dubbo_socket, 1); 287 | if (tcp_relay->tcp_conn_2 != NULL) { 288 | free(tcp_relay->tcp_conn_2); 289 | } 290 | tcp_relay->tcp_conn_2 = dubbo_socket; 291 | dubbo_socket->data = tcp_relay; 292 | 293 | log_info("init dubbo_connection"); 294 | uv_connect_t *dubbo_connection = malloc(sizeof(uv_connect_t)); 295 | 296 | if (tcp_relay->dubbo_connection != NULL) { 297 | free(tcp_relay->dubbo_connection); 298 | } 299 | tcp_relay->dubbo_connection = dubbo_connection; 300 | dubbo_connection->data = tcp_relay; 301 | //uv_tcp_keepalive(dubbo_socket, 1, 60); 302 | log_info("make dubbo_connection"); 303 | uv_tcp_connect(dubbo_connection, dubbo_socket, (const struct sockaddr *) &dubbo_addr, p_on_duboo_connect); 304 | } 305 | 306 | void p_on_new_connection(uv_stream_t *server, int status) { 307 | p_tcp_server_t *p_tcp_server = server->data; 308 | 309 | if (status != 0) { 310 | log_error("New connection error %s\n", uv_strerror(status)); 311 | return; 312 | } 313 | uv_tcp_t *client = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); 314 | uv_tcp_init(p_tcp_server->work_event_loop, client); 315 | uv_tcp_nodelay(client, 1); 316 | if (uv_accept(server, (uv_stream_t *) client) == 0) { 317 | log_info("tcp server %d accepted a connection", p_tcp_server->tid); 318 | tcp_relay_t *tcp_relay = init_tcp_relay_t(P_DEFAULT_PACKAGE_SIZE); 319 | tcp_relay->tcp_conn_1 = client; 320 | client->data = tcp_relay; 321 | tcp_relay->p_tcp_server = p_tcp_server; 322 | connect_to_dubbo_long_live(tcp_relay); 323 | 324 | } else { 325 | uv_close((uv_handle_t *) client, NULL); 326 | } 327 | } 328 | 329 | void p_start_listen(p_tcp_server_t *p_tcp_server) { 330 | log_info("start listen on %d port", server_port); 331 | uv_tcp_init_ex(p_tcp_server->work_event_loop, &(p_tcp_server->agent_server), AF_INET); 332 | uv_os_fd_t fd; 333 | uv_fileno((uv_handle_t *) &(p_tcp_server->agent_server), &fd); 334 | int optval = 1; 335 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); 336 | uv_tcp_nodelay(&(p_tcp_server->agent_server), 1); 337 | uv_tcp_bind(&(p_tcp_server->agent_server), (const struct sockaddr *) &addr, 0); 338 | p_tcp_server->agent_server.data = p_tcp_server; 339 | 340 | log_info("agent server bind successful"); 341 | int r = uv_listen((uv_stream_t *) &(p_tcp_server->agent_server), P_DEFAULT_BACKLOG, p_on_new_connection); 342 | if (r) { 343 | log_fatal("Listen error %s", uv_strerror(r)); 344 | exit(-1); 345 | } 346 | } 347 | 348 | void p_on_duboo_connect_test_cb(uv_connect_t *connection, int status) { 349 | if (status == 0) { 350 | log_info("dubbo connect successful"); 351 | uv_close((uv_handle_t *) connection->handle, NULL); 352 | } else { 353 | log_info("dubbo connect failed"); 354 | sleep(3);//sleep 3s 355 | log_info("retry to connection dubbo"); 356 | test_dubbo_connection(); 357 | } 358 | free(connection->handle); 359 | free(connection); 360 | } 361 | 362 | void test_dubbo_connection() { 363 | log_info("wait for dubbo connection..."); 364 | uv_connect_t *dubbo_connection = malloc(sizeof(uv_connect_t)); 365 | uv_tcp_t *dubbo_socket = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); 366 | uv_tcp_init(main_loop, dubbo_socket); 367 | uv_tcp_nodelay(dubbo_socket, 1); 368 | log_info("make dubbo_connection"); 369 | uv_tcp_connect(dubbo_connection, dubbo_socket, (const struct sockaddr *) &dubbo_addr, p_on_duboo_connect_test_cb); 370 | } 371 | 372 | void p_start_work_event_loop(void *args) { 373 | p_tcp_server_t *p_tcp_server = (p_tcp_server_t *) args; 374 | p_start_listen(p_tcp_server); 375 | 376 | uv_run(p_tcp_server->work_event_loop, UV_RUN_DEFAULT); 377 | } 378 | 379 | int provider(char *provider_type, int server_port, int dubbo_port, char *etcd_url) { 380 | log_info("provider_type: %s, server port: %d, dubbo port: %d, etcd_url: %s", provider_type, server_port, dubbo_port, 381 | etcd_url); 382 | main_loop = uv_default_loop(); 383 | get_cpu_info(); 384 | init_provider(provider_type, server_port, dubbo_port, etcd_url); 385 | uv_run(main_loop, UV_RUN_DEFAULT); 386 | log_info("start run workers"); 387 | int P_EVENT_LOOP_N = P_EVENT_LOOP_NUM; 388 | 389 | log_info("use %d event loops", P_EVENT_LOOP_N); 390 | 391 | for (int i = 0; i < P_EVENT_LOOP_N; i++) { 392 | p_tcp_servers[i] = init_p_tcp_server_t(i); 393 | } 394 | uv_thread_t work_tid[P_EVENT_LOOP_N]; 395 | 396 | for (int i = 0; i < P_EVENT_LOOP_N; i++) { 397 | uv_thread_create(&work_tid[i], p_start_work_event_loop, p_tcp_servers[i]); 398 | } 399 | 400 | //if main loop finish, wait for other event loops 401 | for (int i = 0; i < P_EVENT_LOOP_N; i++) { 402 | uv_thread_join(&work_tid[i]); 403 | } 404 | 405 | return 0; 406 | } -------------------------------------------------------------------------------- /src/consumer_agent.c: -------------------------------------------------------------------------------- 1 | #include "consumer_agent.h" 2 | 3 | #include 4 | 5 | #include "utils/log.h" 6 | #include "utils/utils.h" 7 | #include "utils/etcd.h" 8 | #include "../3rd-deps/picohttpparser/picohttpparser.h" 9 | 10 | #define likely(x) __builtin_expect((x),1) 11 | #define unlikely(x) __builtin_expect((x),0) 12 | 13 | #define DEFAULT_BACKLOG 256 14 | #define DEFAULT_PACKAGE_SIZE 4096 15 | #define DEFAULT_RESPONSE_SIZE 4096 16 | #define MAXIMUM_CONN_WARMUP_COUNT 256 17 | #define MAXIMUM_CONN_COUNT 1024 18 | #define MAXIMUM_ENDPOINT_SERVERS 16 19 | 20 | int64_t start_time; 21 | 22 | int num_endpoint_servers; 23 | struct sockaddr_in addr; 24 | int server_port; 25 | char **raw_server_string; 26 | char **endpoint_server_addr_str; 27 | struct sockaddr_in endpoint_sockaddr[MAXIMUM_ENDPOINT_SERVERS]; 28 | int *endpoint_server_port; 29 | int *endpoint_server_scale;//0->small, 1->medium, 2->large 30 | 31 | load_balancer_t *global_lb; 32 | uv_mutex_t lb_lock; 33 | 34 | int global_conn_remain_count; 35 | uv_mutex_t global_conn_count_mutex; 36 | 37 | uv_loop_t *main_loop; 38 | tcp_server_t *tcp_servers[EVENT_LOOP_NUM]; 39 | 40 | 41 | void make_connection_to_agent_(int which_endpoint, tcp_server_t *tcp_server); 42 | 43 | void connect_to_provider_agent_long_live(tcp_server_t *tcp_server); 44 | 45 | tcp_server_t *init_tcp_server_t(int tid) { 46 | tcp_server_t *tcp_server = malloc(sizeof(tcp_server_t)); 47 | tcp_server->work_event_loop = uv_loop_new(); 48 | tcp_server->req_id = tid; 49 | memset(tcp_server->http_req_dict, 0, sizeof(http_req_t *) * HASH_SLOT_NUM); 50 | return tcp_server; 51 | } 52 | 53 | void init_consumer(int server_p, char *etcd_url) { 54 | setenv("UV_THREADPOOL_SIZE", "4", 1); 55 | server_port = server_p; 56 | raw_server_string = (char **) malloc(sizeof(char *) * MAXIMUM_ENDPOINT_SERVERS); 57 | endpoint_server_addr_str = (char **) malloc(sizeof(char *) * MAXIMUM_ENDPOINT_SERVERS); 58 | endpoint_server_port = (int *) malloc(sizeof(int) * MAXIMUM_ENDPOINT_SERVERS); 59 | endpoint_server_scale = (int *) malloc(sizeof(int) * MAXIMUM_ENDPOINT_SERVERS); 60 | //dubbomesh/com.some.package.IHelloService/192.168.100.100:2000 61 | 62 | int r = etcd_get(etcd_url, "/dubbomesh/com.alibaba.dubbo.performance.demo.provider.IHelloService/", 63 | raw_server_string, 64 | &num_endpoint_servers); 65 | if (r != 0) { 66 | log_fatal("cannot connected to etcd server: %s", etcd_url); 67 | exit(-1); 68 | } 69 | if (num_endpoint_servers == 0) { 70 | log_fatal("cannot find any endpoint server, exiting"); 71 | exit(-1); 72 | } 73 | for (int i = 0; i < num_endpoint_servers; i++) { 74 | log_info("raw_server_str: %s", raw_server_string[i]); 75 | endpoint_server_addr_str[i] = malloc(sizeof(char) * 256); 76 | str_to_server_uri(raw_server_string[i], endpoint_server_addr_str[i], &endpoint_server_port[i], 77 | &endpoint_server_scale[i]); 78 | log_info("endpoint server addr: %s, port: %d, scale %d", endpoint_server_addr_str[i], endpoint_server_port[i], 79 | endpoint_server_scale[i]); 80 | uv_ip4_addr(endpoint_server_addr_str[i], endpoint_server_port[i], &endpoint_sockaddr[i]); 81 | } 82 | uv_ip4_addr("0.0.0.0", server_port, &addr); 83 | 84 | // init global load balancer 85 | global_lb = init_load_balancer(num_endpoint_servers); 86 | uv_mutex_init(&lb_lock); 87 | 88 | global_conn_remain_count = 0; 89 | uv_mutex_init(&global_conn_count_mutex); 90 | } 91 | 92 | http_req_t *init_http_req_t(size_t size) { 93 | http_req_t *req = malloc(sizeof(http_req_t)); 94 | req->client = NULL; 95 | req->raw_array = (char *) malloc(sizeof(char) * size); 96 | req->raw_array_size = 0; 97 | req->raw_array_max_size = size; 98 | req->request_length = -1; 99 | req->req_id = -1; 100 | req->response_package = (char *) malloc(sizeof(char) * DEFAULT_PACKAGE_SIZE); 101 | return req; 102 | } 103 | 104 | void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 105 | http_req_t *http_req = handle->data; 106 | buf->base = http_req->raw_array + http_req->raw_array_size; 107 | buf->len = http_req->raw_array_max_size - http_req->raw_array_size; 108 | } 109 | 110 | void alloc_buffer_new(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 111 | buf->base = malloc(sizeof(char) * suggested_size); 112 | buf->len = suggested_size; 113 | } 114 | 115 | void alloc_buffer_to_agent(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) { 116 | endpoint_server_t *endpoint_server = handle->data; 117 | buf->base = endpoint_server->raw_array + endpoint_server->size_of_raw_array; 118 | buf->len = endpoint_server->raw_array_max_length - endpoint_server->size_of_raw_array; 119 | } 120 | 121 | void free_http_req(http_req_t *http_req) { 122 | if (http_req != NULL) { 123 | if (http_req->client) 124 | free(http_req->client); 125 | free(http_req->raw_array); 126 | free(http_req->response_package); 127 | free(http_req); 128 | } 129 | } 130 | 131 | void close_cb(uv_handle_t *handle) { 132 | http_req_t *http_req = (http_req_t *) handle->data; 133 | free_http_req(http_req); 134 | 135 | } 136 | 137 | void after_write_to_provider_agent(uv_write_t *handle, int status) { 138 | free(handle); 139 | } 140 | 141 | void send_to_provider_agent(int which_endpoint, http_req_t *http_req, char *send_buffer, int send_buff_len) { 142 | tcp_server_t *tcp_server = http_req->tcp_server; 143 | endpoint_server_t *endpoint_server = tcp_server->endpoint_servers[which_endpoint]; 144 | 145 | uv_buf_t local_buf = uv_buf_init(send_buffer, send_buff_len); 146 | uv_write_t *write_req = malloc(sizeof(uv_write_t)); 147 | write_req->data = http_req; 148 | 149 | http_req->tcp_server->http_req_dict[http_req->req_id % HASH_SLOT_NUM] = http_req; 150 | uv_write(write_req, (uv_stream_t *) endpoint_server->tcp_stream, &local_buf, 1, after_write_to_provider_agent); 151 | } 152 | 153 | void c_read_connection_data(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 154 | http_req_t *http_req = client->data; 155 | tcp_server_t *tcp_server = http_req->tcp_server; 156 | 157 | if (nread > 0) { 158 | http_req->raw_array_size += nread; 159 | // 1st: parse header 160 | const char *method, *path; 161 | int minor_version; 162 | struct phr_header headers[16]; 163 | size_t method_len, path_len, num_headers; 164 | num_headers = 16; 165 | if (http_req->request_length < 0) { 166 | http_req->request_length = phr_parse_request(http_req->raw_array, http_req->raw_array_size, &method, 167 | &method_len, &path, &path_len, 168 | &minor_version, headers, &num_headers, 0); 169 | } 170 | if (http_req->request_length < 0) 171 | return; 172 | //log_info("%.*s", http_req->raw_array_size, http_req->raw_array); 173 | int content_length = atoi(headers[0].value);// this can be fixed according to a particular http client 174 | 175 | http_req->content_length = content_length; 176 | if (http_req->raw_array_size < content_length + http_req->request_length) 177 | return; 178 | 179 | uv_mutex_lock(&lb_lock); 180 | int which_endpoint = select_endpoint_time_based(global_lb, http_req); 181 | uv_mutex_unlock(&lb_lock); 182 | 183 | http_req->req_id = tcp_server->req_id; 184 | tcp_server->req_id += EVENT_LOOP_NUM; 185 | //4bytes package length, 8 bytes req_id, http body 186 | int raw_array_start_offset = http_req->request_length - 12; 187 | int_to_four_char(content_length, http_req->raw_array + raw_array_start_offset); 188 | long_to_8bytes(http_req->req_id, http_req->raw_array + raw_array_start_offset + 4); 189 | 190 | send_to_provider_agent(which_endpoint, http_req, http_req->raw_array + raw_array_start_offset, 191 | 12 + content_length); 192 | 193 | // 3rd: copy remain buff to the beginning of the buffer 194 | http_req->raw_array_size = 0; 195 | http_req->request_length = -1; 196 | } else if (nread < 0) { 197 | if (nread != UV_EOF) { log_error("Read error %s\n", uv_strerror(nread)); } 198 | log_info("err happens in http"); 199 | uv_close((uv_handle_t *) client, close_cb); 200 | 201 | uv_mutex_lock(&global_conn_count_mutex); 202 | global_conn_remain_count--; 203 | uv_mutex_unlock(&global_conn_count_mutex); 204 | } 205 | } 206 | 207 | void c_dummy_read_connection_data(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 208 | if (nread < 0) { 209 | uv_close((uv_handle_t *) client, NULL); 210 | } 211 | free(buf->base); 212 | } 213 | 214 | void on_new_connection(uv_stream_t *server, int status) { 215 | if (status != 0) { 216 | log_error("New connection error %s\n", uv_strerror(status)); 217 | return; 218 | } 219 | tcp_server_t *tcp_server = server->data; 220 | 221 | uv_tcp_t *client = (uv_tcp_t *) malloc(sizeof(uv_tcp_t)); 222 | uv_tcp_init(tcp_server->work_event_loop, client); 223 | uv_tcp_nodelay(client, 1); 224 | uv_tcp_keepalive(client, 1, 60); 225 | //log_info("start accept a connection"); 226 | int64_t cur_time = get_wall_time(); 227 | int conn_limit = (cur_time - start_time > 30000000 ? MAXIMUM_CONN_COUNT : MAXIMUM_CONN_WARMUP_COUNT); 228 | if (uv_accept(server, (uv_stream_t *) client) == 0) { 229 | int tmp; 230 | uv_mutex_lock(&global_conn_count_mutex); 231 | tmp = global_conn_remain_count; 232 | if (tmp < conn_limit) 233 | global_conn_remain_count++; 234 | uv_mutex_unlock(&global_conn_count_mutex); 235 | if (tmp < conn_limit) { 236 | // log_info("read start a conn, global conn remain count %d", tmp); 237 | http_req_t *req = init_http_req_t(DEFAULT_PACKAGE_SIZE * 2); 238 | req->tcp_server = tcp_server; 239 | req->client = client; 240 | client->data = (void *) req; 241 | uv_read_start((uv_stream_t *) client, alloc_buffer, c_read_connection_data); 242 | } else { 243 | uv_read_start((uv_stream_t *) client, alloc_buffer_new, c_dummy_read_connection_data); 244 | } 245 | } else { 246 | uv_close((uv_handle_t *) client, NULL); 247 | } 248 | } 249 | 250 | void after_write_cb(uv_write_t *write_req, int status) { 251 | free(write_req); 252 | } 253 | 254 | void reconnection_to_agent(uv_handle_t *handle) { 255 | endpoint_server_t *server = handle->data; 256 | tcp_server_t *tcp_server = server->tcp_server; 257 | log_info("retry to connect to endpoint server %d", server->which); 258 | make_connection_to_agent_(server->which, tcp_server); 259 | } 260 | 261 | void c_read_from_provide_agents(uv_stream_t *client, ssize_t nread, const uv_buf_t *buf) { 262 | endpoint_server_t *server = client->data; 263 | 264 | if (nread > 0) { 265 | server->size_of_raw_array += nread; 266 | tcp_server_t *tcp_server = server->tcp_server; 267 | for (;;) { 268 | if (server->size_of_raw_array < 12) { return; } 269 | int length_body = four_char_to_int(server->raw_array); 270 | int last_length = length_body + 12; 271 | if (server->size_of_raw_array < last_length) { return; } 272 | 273 | //log_info("res: %.*s", length_body, server->raw_array + 12); 274 | 275 | int64_t req_id = bytes8_to_long(server->raw_array + 4); 276 | //log_info("res req id: %lld", req_id); 277 | http_req_t *http_conn = tcp_server->http_req_dict[req_id % HASH_SLOT_NUM]; 278 | if (http_conn != NULL) { 279 | uv_mutex_lock(&lb_lock); 280 | finish_endpoint_task_time_based(global_lb, server->which, http_conn); 281 | uv_mutex_unlock(&lb_lock); 282 | 283 | memcpy(http_conn->response_package, server->raw_array + 12, length_body); 284 | 285 | uv_buf_t res_buf = uv_buf_init(http_conn->response_package, length_body); 286 | uv_write_t *write_req = (uv_write_t *) malloc(sizeof(uv_write_t)); 287 | write_req->data = http_conn; 288 | uv_write(write_req, (uv_stream_t *) http_conn->client, &res_buf, 1, after_write_cb); 289 | } else { 290 | // handle time-out influencing remain 291 | uv_mutex_lock(&lb_lock); 292 | global_lb->history_diff_time_lst[server->which]->remain--; 293 | uv_mutex_unlock(&lb_lock); 294 | } 295 | 296 | // 2nd: make the buffer correct, handling ticked packets 297 | server->size_of_raw_array = server->size_of_raw_array - last_length; 298 | memmove(server->raw_array, server->raw_array + last_length, server->size_of_raw_array); 299 | 300 | } 301 | } else if (nread < 0) { 302 | if (nread != UV_EOF) { 303 | log_error("Read error %s\n", uv_strerror(nread)); 304 | log_fatal("error while reading from provider agent %d", ((endpoint_server_t *) client->data)->which); 305 | exit(-1); 306 | } 307 | server->conn_status = -1; 308 | uv_close((uv_handle_t *) client, reconnection_to_agent); 309 | } 310 | } 311 | 312 | void start_listen(tcp_server_t *tcp_server) { 313 | log_info("start listening"); 314 | uv_tcp_init_ex(tcp_server->work_event_loop, &(tcp_server->server), AF_INET); 315 | uv_os_fd_t fd; 316 | uv_fileno((uv_handle_t *) &(tcp_server->server), &fd); 317 | int optval = 1; 318 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &optval, sizeof(optval)); 319 | uv_tcp_bind(&(tcp_server->server), (const struct sockaddr *) &addr, 0); 320 | uv_tcp_nodelay(&(tcp_server->server), 1); 321 | tcp_server->server.data = tcp_server; 322 | int r = uv_listen((uv_stream_t *) &(tcp_server->server), DEFAULT_BACKLOG, on_new_connection); 323 | 324 | if (r) { 325 | log_fatal("Listen error %s\n", uv_strerror(r)); 326 | exit(-1); 327 | } 328 | } 329 | 330 | void c_after_connect_to_a_agent(uv_connect_t *connection, int status) { 331 | endpoint_server_t *server = connection->data; 332 | tcp_server_t *tcp_server = server->tcp_server; 333 | if (status == 0) { 334 | log_info("connected to provider %d successful", server->which); 335 | server->conn_status = 0; 336 | server->tcp_stream->data = server; 337 | log_info("server pointer address: %x", server); 338 | log_info("c_after_connect_to_a_agent which server: %d", server->which); 339 | uv_read_start((uv_stream_t *) server->tcp_stream, alloc_buffer_to_agent, c_read_from_provide_agents); 340 | tcp_server->endpoint_connection_count++; 341 | if (tcp_server->endpoint_connection_count == num_endpoint_servers) { 342 | start_listen(tcp_server); 343 | } 344 | 345 | } else { 346 | log_info("connected to provider %d failed", server->which); 347 | sleep(3); 348 | log_info("retry to connect provider %d", server->which); 349 | make_connection_to_agent_(server->which, tcp_server); 350 | } 351 | } 352 | 353 | void make_connection_to_agent_(int which_endpoint, tcp_server_t *tcp_server) { 354 | int i = which_endpoint; 355 | if (tcp_server->endpoint_servers[i]->conn_status != 0) { 356 | log_info("init connection to provider %d", i); 357 | uv_connect_t *conn = malloc(sizeof(uv_connect_t)); 358 | if (tcp_server->endpoint_servers[i]->conn) 359 | free(tcp_server->endpoint_servers[i]->conn); 360 | tcp_server->endpoint_servers[i]->conn = conn; 361 | uv_tcp_t *client = malloc(sizeof(uv_tcp_t)); 362 | if (tcp_server->endpoint_servers[i]->tcp_stream) 363 | free(tcp_server->endpoint_servers[i]->tcp_stream); 364 | tcp_server->endpoint_servers[i]->tcp_stream = client; 365 | 366 | uv_tcp_init(tcp_server->work_event_loop, client); 367 | uv_tcp_nodelay(client, 1); 368 | conn->data = tcp_server->endpoint_servers[i]; 369 | uv_tcp_keepalive(client, 1, 60); 370 | log_info("start to connect endpoint %d", i); 371 | uv_tcp_connect(conn, client, 372 | (const struct sockaddr *) &endpoint_sockaddr[tcp_server->endpoint_servers[i]->which], 373 | c_after_connect_to_a_agent); 374 | } 375 | } 376 | 377 | void connect_to_provider_agent_long_live(tcp_server_t *tcp_server) { 378 | //connect to provider agents 379 | log_info("init connections to provider agents"); 380 | for (int i = 0; i < num_endpoint_servers; i++) { 381 | make_connection_to_agent_(i, tcp_server); 382 | } 383 | } 384 | 385 | void start_work_event_loop(void *args) { 386 | tcp_server_t *tcp_server = (tcp_server_t *) args; 387 | log_info("start work event loop"); 388 | 389 | for (int i = 0; i < num_endpoint_servers; i++) { 390 | endpoint_server_t *server = malloc(sizeof(endpoint_server_t)); 391 | server->which = i; 392 | server->conn_status = -1; 393 | server->raw_array = malloc(sizeof(char) * DEFAULT_RESPONSE_SIZE); 394 | server->size_of_raw_array = 0; 395 | server->raw_array_max_length = DEFAULT_RESPONSE_SIZE; 396 | server->tcp_server = tcp_server; 397 | server->conn = NULL; 398 | server->tcp_stream = NULL; 399 | log_info("start_event_loop, tcp_server addr: 0x%x", tcp_server); 400 | tcp_server->endpoint_servers[i] = server; 401 | } 402 | 403 | tcp_server->endpoint_connection_count = 0; 404 | connect_to_provider_agent_long_live(tcp_server); 405 | 406 | uv_run(tcp_server->work_event_loop, UV_RUN_DEFAULT); 407 | } 408 | 409 | int consumer(int server_port, char *etcd_url) { 410 | log_info("libuv version: %d.%d.%d%s", UV_VERSION_MAJOR, UV_VERSION_MINOR, UV_VERSION_PATCH, UV_VERSION_SUFFIX); 411 | get_cpu_info(); 412 | init_consumer(server_port, etcd_url); 413 | main_loop = uv_default_loop(); 414 | start_time = get_wall_time(); 415 | for (int i = 0; i < EVENT_LOOP_NUM; i++) { 416 | tcp_servers[i] = init_tcp_server_t(i); 417 | } 418 | uv_thread_t work_tid[EVENT_LOOP_NUM]; 419 | 420 | for (int i = 0; i < EVENT_LOOP_NUM; i++) { 421 | uv_thread_create(&work_tid[i], start_work_event_loop, tcp_servers[i]); 422 | } 423 | 424 | //if main loop finish, wait for other event loops 425 | for (int i = 0; i < EVENT_LOOP_NUM; i++) { 426 | uv_thread_join(&work_tid[i]); 427 | } 428 | return 0; 429 | } --------------------------------------------------------------------------------