You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by hu...@apache.org on 2019/07/29 08:02:33 UTC
[dubbo-php-framework] 02/28: submit code
This is an automated email from the ASF dual-hosted git repository.
huxing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/dubbo-php-framework.git
commit e8ad24725e6d8155b008208db3c8252712d41bec
Author: zackhu <za...@lexinfintech.com>
AuthorDate: Thu Jun 28 16:30:26 2018 +0800
submit code
---
README.md | 13 +
agent/src/c/Makefile | 8 +
agent/src/c/common/fsof_global.h | 40 +
agent/src/c/common/fsof_url.c | 97 +++
agent/src/c/common/fsof_url.h | 22 +
agent/src/c/common/fsof_util.c | 149 ++++
agent/src/c/common/fsof_util.h | 25 +
agent/src/c/common/strmap.c | 530 ++++++++++++++
agent/src/c/common/strmap.h | 366 ++++++++++
agent/src/c/include/hiredis/adapters/ae.h | 127 ++++
agent/src/c/include/hiredis/adapters/glib.h | 169 +++++
agent/src/c/include/hiredis/adapters/ivykis.h | 81 ++
agent/src/c/include/hiredis/adapters/libev.h | 147 ++++
agent/src/c/include/hiredis/adapters/libevent.h | 108 +++
agent/src/c/include/hiredis/adapters/libuv.h | 121 +++
agent/src/c/include/hiredis/adapters/macosx.h | 114 +++
agent/src/c/include/hiredis/adapters/qt.h | 135 ++++
agent/src/c/include/hiredis/async.h | 129 ++++
agent/src/c/include/hiredis/hiredis.h | 223 ++++++
agent/src/c/include/hiredis/read.h | 111 +++
agent/src/c/include/hiredis/sds.h | 273 +++++++
agent/src/c/include/zlog.h | 265 +++++++
agent/src/c/lib/libhiredis.a | Bin 0 -> 332078 bytes
agent/src/c/lib/libzlog.a | Bin 0 -> 536320 bytes
agent/src/c/release/agent_log.conf | 6 +
agent/src/c/release/protect_agent_server.sh | 7 +
agent/src/c/release/start_agent_server.sh | 18 +
agent/src/c/service/fsof_log.c | 79 ++
agent/src/c/service/fsof_log.h | 28 +
agent/src/c/service/fsof_mq.c | 155 ++++
agent/src/c/service/fsof_mq.h | 46 ++
agent/src/c/service/fsof_redis.c | 242 ++++++
agent/src/c/service/fsof_redis.h | 32 +
agent/src/c/service/fsof_zookeeper.c | 171 +++++
agent/src/c/service/fsof_zookeeper.h | 45 ++
agent/src/c/service/mt_array.c | 72 ++
agent/src/c/service/mt_array.h | 31 +
agent/src/c/src/fsof_main.c | 524 +++++++++++++
api/FSOFApi.php | 68 ++
arch.png | Bin 0 -> 45859 bytes
bin/app_admin.php | 584 +++++++++++++++
bin/restartall.sh | 17 +
common/BootStrap.php | 47 ++
common/FrameAutoLoader.php | 70 ++
common/config/FSOFCommonUtil.php | 75 ++
common/config/FSOFConfigManager.php | 228 ++++++
common/config/FSOFConfigUtil.php | 99 +++
common/config/FSOFConstants.php | 34 +
common/file/FSOFRedis.php | 182 +++++
common/file/FileSystemUtil.php | 132 ++++
common/log/FSOFSystemUtil.php | 66 ++
common/protocol/fsof/DubboParser.php | 305 ++++++++
common/protocol/fsof/DubboRequest.php | 341 +++++++++
common/protocol/fsof/DubboResponse.php | 193 +++++
common/url/FSOFUrl.php | 294 ++++++++
config/app/conf/dev/provider/demo-provider.deploy | 22 +
config/app/conf/gray/provider/example.deploy | 20 +
config/app/conf/pre/provider/example.deploy | 22 +
config/app/conf/pro/provider/example.deploy | 20 +
config/global/conf/fsof.ini | 34 +
consumer/FSOFConsumer.php | 113 +++
consumer/FrameAutoLoader.php | 70 ++
consumer/client/FSOFClient4Linux.php | 91 +++
consumer/client/IFSOFClient.php | 33 +
consumer/fsof/FSOFProcessor.php | 342 +++++++++
consumer/proxy/Proxy.php | 144 ++++
consumer/proxy/ProxyFactory.php | 229 ++++++
demo/demo-consumer/config/log4php.xml | 12 +
demo/demo-consumer/consumer/demo-consumer.consumer | 10 +
demo/demo-consumer/server/Consumer.php | 15 +
demo/demo-provider/BootStrap.php | 24 +
demo/demo-provider/config/log4php.xml | 12 +
demo/demo-provider/provider/demo-provider.provider | 9 +
demo/demo-provider/server/DemoServiceImpl.php | 18 +
provider/FSOFProvider.php | 86 +++
provider/FrameAutoLoader.php | 70 ++
provider/common/Console.php | 56 ++
provider/core/app/AppAutoLoader.php | 129 ++++
provider/core/app/AppContext.php | 84 +++
provider/core/app/AppLauncher.php | 40 +
provider/core/protocol/BaseProtocol.php | 116 +++
provider/core/protocol/BufferedProtocol.php | 64 ++
provider/core/protocol/IProtocol.php | 33 +
provider/core/server/BaseServer.php | 812 +++++++++++++++++++++
provider/core/server/FSOFRegistry.php | 376 ++++++++++
provider/core/server/IServer.php | 32 +
provider/core/server/TcpServer.php | 32 +
provider/fsof/FSOFProtocol.php | 480 ++++++++++++
provider/monitor/AppMonitor.php | 124 ++++
provider/monitor/OverloadMonitor.php | 91 +++
provider/monitor/ServiceMonitor.php | 207 ++++++
provider/shell/StartUp.php | 78 ++
registry/BootStrap.php | 28 +
registry/FrameAutoLoader.php | 70 ++
registry/automatic/ConsumerProxy.php | 112 +++
registry/automatic/RegistryService.php | 103 +++
registry/automatic/RegistryServiceFactory.php | 33 +
registry/automatic/ZookeeperClient.php | 299 ++++++++
98 files changed, 12239 insertions(+)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7e0c38e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# dubbo-php-framework
+dubbo-php-framework是一个用于php语言的RPC通讯框架,与dubbo在协议上完全兼容,可同时作为provider端和consumer端,使用zookeeper用于服务注册发现,序列化使用fastjson.
+
+
+
+## 说明
+* php provider以多进程方式运行,其中worker进程用于处理具体业务,manager进程控制worker进程的生命周期,master进程处理网络IO.
+* agent监听registry中provider地址信息的变更并同步到本地redis,供本机上所有php consumer共享.
+* php consumer、redis、angent三者部署在所有consumer机器上,并且彼此以unix socket通信.
+* provider_admin部署在所有provider机器上,用于控制该机器上所有php provider的生命周期.
+
+
+[Quick start](https://github.com/lexin-fintech/dubbo-php-framework/wiki/Quick-Start)
\ No newline at end of file
diff --git a/agent/src/c/Makefile b/agent/src/c/Makefile
new file mode 100644
index 0000000..a228884
--- /dev/null
+++ b/agent/src/c/Makefile
@@ -0,0 +1,8 @@
+TARGET = release/agent
+CFLAGS = -g -Wall -lpthread -lzookeeper_mt -L./lib -lhiredis -lzlog -I./include
+CC = gcc
+$(TARGET):service/fsof_mq.c src/fsof_main.c service/fsof_zookeeper.c common/strmap.c service/fsof_redis.c service/fsof_log.c common/fsof_url.c common/fsof_util.c service/mt_array.c
+ $(CC) $^ $(CFLAGS) -o $@
+.PHONY : clean
+clean:
+ rm -f $(TARGET)
diff --git a/agent/src/c/common/fsof_global.h b/agent/src/c/common/fsof_global.h
new file mode 100644
index 0000000..953c902
--- /dev/null
+++ b/agent/src/c/common/fsof_global.h
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_GLOBAL_H
+#define FSOF_GLOBAL_H
+
+#define FSOF_ROOT_NAME "/dubbo"
+#define PROVIDER_NAME "/providers"
+#define PROVIDER_NODE_NAME "providers"
+#define CONSUMER_NAME "/consumers"
+#define ROUTERS_NAME "/routers"
+#define CONFIGUREATORS_NAME "/configurators"
+#define CONFIGUREATORS_NODE_NAME "configurators"
+#define FSOF_REDIS_CONFIGURATOR_KEY_NAME "override"
+
+#define ZOOKEEPER_HOST "127.0.0.1:2181"
+#define DEFAULT_MAP_COUNT (10)
+#define REDIS_UNIX_SOCK "/var/fsof/redis.sock"
+
+
+
+struct list {
+ char *value;
+ struct list *next;
+};
+
+#endif
diff --git a/agent/src/c/common/fsof_url.c b/agent/src/c/common/fsof_url.c
new file mode 100644
index 0000000..0cc9887
--- /dev/null
+++ b/agent/src/c/common/fsof_url.c
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fsof_url.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+char * fsof_url_encode(char const *s, int len, int *new_length) {
+ unsigned char const *from, *end;
+ unsigned char c;
+ from = (unsigned char const*)s;
+ end = (unsigned char const*)s + len;
+ unsigned char *to = (unsigned char *) malloc(3 * len + 1);
+ unsigned char *start = to;
+
+ unsigned char hexchars[] = "0123456789ABCDEF";
+
+ while (from < end) {
+ c = *from++;
+
+ if (c == ' ') {
+ *to++ = '+';
+ } else if ((c < '0' && c != '-' && c != '.')
+ ||(c < 'A' && c > '9')
+ ||(c > 'Z' && c < 'a' && c != '_')
+ ||(c > 'z')) {
+ to[0] = '%';
+ to[1] = hexchars[c >> 4];
+ to[2] = hexchars[c & 15];
+ to += 3;
+ } else {
+ *to++ = c;
+ }
+ }
+
+ *to = 0;
+ if (new_length) {
+ *new_length = to - start;
+ }
+
+ return (char *) start;
+}
+
+int fsof_url_decode(char *str, int len)
+{
+ char *dest = str;
+ char *data = str;
+ int value;
+ int c;
+
+ while (len--) {
+ if (*data == '+') {
+ *dest = ' ';
+ }
+ else if (*data == '%' && len >= 2 && isxdigit((int) *(data + 1))
+ && isxdigit((int) *(data + 2))) {
+ c = ((unsigned char *)(data+1))[0];
+ if (isupper(c)) {
+ c = tolower(c);
+ }
+
+ value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
+ c = ((unsigned char *)(data+1))[1];
+ if (isupper(c)) {
+ c = tolower(c);
+ }
+
+ value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
+ *dest = (char)value ;
+ data += 2;
+ len -= 2;
+ } else {
+ *dest = *data;
+ }
+ data++;
+ dest++;
+ }
+
+ *dest = '\0';
+ return dest - str;
+
+}
+
+
diff --git a/agent/src/c/common/fsof_url.h b/agent/src/c/common/fsof_url.h
new file mode 100644
index 0000000..6530daa
--- /dev/null
+++ b/agent/src/c/common/fsof_url.h
@@ -0,0 +1,22 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_URL_H
+#define FSOF_URL_H
+#define URL_ENCODE_BUF_LEN (4 * 1024)
+int fsof_url_decode(char *str, int len);
+char * fsof_url_encode(char const *s, int len, int *new_length);
+#endif
diff --git a/agent/src/c/common/fsof_util.c b/agent/src/c/common/fsof_util.c
new file mode 100644
index 0000000..c1f8cff
--- /dev/null
+++ b/agent/src/c/common/fsof_util.c
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <fcntl.h>
+#include <unistd.h>
+#include "fsof_util.h"
+#include <string.h>
+#include <stdio.h>
+
+char * trim(char * src) {
+ int i = 0;
+ char *begin = src;
+ while(src[i] != '\0') {
+ if(src[i] != ' ') {
+ break;
+ }else {
+ begin++;
+ }
+ i++;
+ }
+
+ for(i = strlen(src)-1; i >= 0; i--){
+ if(src[i] != ' '){
+ break;
+ }else {
+ src[i] = '\0';
+ }
+ }
+
+ return begin;
+}
+
+char *get_ini_key_string(char *title,char *key,char *filename) {
+ FILE *fp;
+ char szLine[1024];
+ static char tmpstr[1024];
+ char *ret_str = NULL;
+ int rtnval;
+ int i = 0;
+ int flag = 0;
+ char *tmp;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ printf("have no such file \n");
+ return "";
+ }
+
+ while(!feof(fp))
+ {
+ rtnval = fgetc(fp);
+ if(rtnval == EOF)
+ {
+ break;
+ }
+ else
+ {
+ szLine[i++] = rtnval;
+ }
+ if(rtnval == '\n')
+ {
+ szLine[--i] = '\0';
+ i = 0;
+ tmp = strchr(szLine, '=');
+
+ if(( tmp != NULL )&&(flag == 1)) {
+ if(strstr(szLine,key)!=NULL)
+ {
+ if ('#' == szLine[0])
+ {
+ }
+ else if ( '/' == szLine[0] && '/' == szLine[1] )
+ {
+
+ }
+ else
+ {
+ strcpy(tmpstr,tmp+1);
+ fclose(fp);
+ ret_str = trim(tmpstr);
+ return ret_str;
+ }
+ }
+ }
+ else {
+ strcpy(tmpstr,"[");
+ strcat(tmpstr,title);
+ strcat(tmpstr,"]");
+ if( strncmp(tmpstr,szLine,strlen(tmpstr)) == 0 )
+ {
+ flag = 1;
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+ return "";
+
+}
+
+
+int get_current_path(char buf[],char *pFileName,int depth) {
+ char pidfile[64] = {0};
+ char *p = NULL;
+ int bytes = 0;
+ int fd = 0;
+ int cur_dp = 0;
+
+ sprintf(pidfile, "/proc/%d/cmdline", getpid());
+ fd = open(pidfile, O_RDONLY, 0);
+ bytes = read(fd, buf, 256);
+ close(fd);
+
+ p = &buf[strlen(buf)];
+_AGAIN:
+ while ('/' != *p) {
+ *p = '\0';
+ if ((p - buf) == 0) {
+ return 1; //path now null,can't get depth dir
+ }
+ p--;
+ }
+
+ cur_dp++;
+ if (cur_dp < depth) {
+ p--;
+ goto _AGAIN;
+ }
+ p++;
+
+ memcpy(p,pFileName,strlen(pFileName));
+ p += strlen(pFileName);
+ *p = '\0';
+ return 0;
+}
diff --git a/agent/src/c/common/fsof_util.h b/agent/src/c/common/fsof_util.h
new file mode 100644
index 0000000..136dfc8
--- /dev/null
+++ b/agent/src/c/common/fsof_util.h
@@ -0,0 +1,25 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_UTIL_H
+#define FSOF_UTIL_H
+
+#define MAX_CONFIG_PATH_LEN 260
+
+char *get_ini_key_string(char *title,char *key,char *filename);
+int get_current_path(char buf[],char *pFileName,int depth);
+
+#endif
diff --git a/agent/src/c/common/strmap.c b/agent/src/c/common/strmap.c
new file mode 100644
index 0000000..7716c19
--- /dev/null
+++ b/agent/src/c/common/strmap.c
@@ -0,0 +1,530 @@
+/*
+ * strmap version 2.0.1
+ *
+ * ANSI C hash table for strings.
+ *
+ * Version history:
+ * 1.0.0 - initial release
+ * 2.0.0 - changed function prefix from strmap to sm to ensure
+ * ANSI C compatibility
+ * 2.0.1 - improved documentation
+ *
+ * strmap.c
+ *
+ * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson.
+ *
+ * Per Ola Kristensson <po...@cam.ac.uk>
+ * Inference Group, Department of Physics
+ * University of Cambridge
+ * Cavendish Laboratory
+ * JJ Thomson Avenue
+ * CB3 0HE Cambridge
+ * United Kingdom
+ *
+ * strmap is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * strmap is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with strmap. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "strmap.h"
+
+typedef struct Pair Pair;
+
+typedef struct Bucket Bucket;
+
+struct Pair {
+ char *key;
+ struct map_object *value;
+};
+
+struct Bucket {
+ unsigned int count;
+ Pair *pairs;
+};
+
+struct StrMap {
+ unsigned int count;
+ Bucket *buckets;
+};
+
+static Pair * get_pair(Bucket *bucket, const char *key);
+static unsigned long hash(const char *str);
+
+StrMap * sm_new(unsigned int capacity)
+{
+ StrMap *map;
+
+ map = malloc(sizeof(StrMap));
+ if (map == NULL) {
+ return NULL;
+ }
+ map->count = capacity;
+ map->buckets = malloc(map->count * sizeof(Bucket));
+ if (map->buckets == NULL) {
+ free(map);
+ return NULL;
+ }
+ memset(map->buckets, 0, map->count * sizeof(Bucket));
+ return map;
+}
+
+void sm_delete(StrMap *map)
+{
+ unsigned int i, j, n, m;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return;
+ }
+ n = map->count;
+ bucket = map->buckets;
+ i = 0;
+ while (i < n) {
+ m = bucket->count;
+ pair = bucket->pairs;
+ j = 0;
+ while(j < m) {
+ free(pair->key);
+ free(pair->value);
+ pair++;
+ j++;
+ }
+ free(bucket->pairs);
+ bucket++;
+ i++;
+ }
+ free(map->buckets);
+ free(map);
+}
+
+int sm_del_pair(const StrMap *map, const char *key) {
+ unsigned int index;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return 0;
+ }
+ if (key == NULL) {
+ return 0;
+ }
+ index = hash(key) % map->count;
+ bucket = &(map->buckets[index]);
+ pair = get_pair(bucket, key);
+ if (pair == NULL) {
+ return 0;
+ }
+
+ return 0;
+}
+int sm_get(const StrMap *map, const char *key, struct map_object **out_buf)
+{
+ unsigned int index;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return 0;
+ }
+ if (key == NULL) {
+ return 0;
+ }
+ index = hash(key) % map->count;
+ bucket = &(map->buckets[index]);
+ pair = get_pair(bucket, key);
+ if (pair == NULL) {
+ return 0;
+ }
+ if (out_buf == NULL) {
+ return sizeof(*(pair->value));
+ }
+ if (out_buf == NULL) {
+ return 0;
+ }
+
+ //memcpy(out_buf, pair->value,sizeof(*out_buf));
+ *out_buf = pair->value;
+ return 1;
+}
+
+int sm_exists(const StrMap *map, const char *key)
+{
+ unsigned int index;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return 0;
+ }
+ if (key == NULL) {
+ return 0;
+ }
+ index = hash(key) % map->count;
+ bucket = &(map->buckets[index]);
+ pair = get_pair(bucket, key);
+ if (pair == NULL) {
+ return 0;
+ }
+ return 1;
+}
+
+int sm_put(StrMap *map, const char *key, struct map_object *value)
+{
+ unsigned int key_len, value_len, index;
+ Bucket *bucket;
+ Pair *tmp_pairs, *pair;
+ char *new_key;
+ struct map_object *new_value;
+
+ if (map == NULL) {
+ return 0;
+ }
+ if (key == NULL || value == NULL) {
+ return 0;
+ }
+ key_len = strlen(key);
+ value_len = sizeof(*value);
+ /* Get a pointer to the bucket the key string hashes to */
+ index = hash(key) % map->count;
+ bucket = &(map->buckets[index]);
+ /* Check if we can handle insertion by simply replacing
+ * an existing value in a key-value pair in the bucket.
+ */
+ if ((pair = get_pair(bucket, key)) != NULL) {
+ /* The bucket contains a pair that matches the provided key,
+ * change the value for that pair to the new value.
+ */
+ /* If the new value is larger than the old value, re-allocate
+ * space for the new larger value.
+ */
+ //memcpy(pair->value,value,sizeof(*value));
+ pair->value = value;//add by iceli resign the pointer to new value
+ return 1;
+ }
+ /* Allocate space for a new key and value */
+ new_key = malloc((key_len + 1) * sizeof(char));
+ if (new_key == NULL) {
+ return 0;
+ }
+ /*
+ new_value = malloc(value_len);
+ if (new_value == NULL) {
+ free(new_key);
+ return 0;
+ }
+ */
+ new_value = value;
+ /* Create a key-value pair */
+ if (bucket->count == 0) {
+ /* The bucket is empty, lazily allocate space for a single
+ * key-value pair.
+ */
+ bucket->pairs = malloc(sizeof(Pair));
+ if (bucket->pairs == NULL) {
+ free(new_key);
+ free(new_value);
+ return 0;
+ }
+ bucket->count = 1;
+ }
+ else {
+ /* The bucket wasn't empty but no pair existed that matches the provided
+ * key, so create a new key-value pair.
+ */
+ tmp_pairs = realloc(bucket->pairs, (bucket->count + 1) * sizeof(Pair));
+ if (tmp_pairs == NULL) {
+ free(new_key);
+ free(new_value);
+ return 0;
+ }
+ bucket->pairs = tmp_pairs;
+ bucket->count++;
+ }
+ /* Get the last pair in the chain for the bucket */
+ pair = &(bucket->pairs[bucket->count - 1]);
+ pair->key = new_key;
+ pair->value = new_value;
+ /* Copy the key and its value into the key-value pair */
+ strcpy(pair->key, key);
+ //memcpy(pair->value,value,value_len);//add by iceli don't copy the same pointer
+ return 1;
+}
+
+int sm_get_count(const StrMap *map)
+{
+ unsigned int i, j, n, m;
+ unsigned int count;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return 0;
+ }
+ bucket = map->buckets;
+ n = map->count;
+ i = 0;
+ count = 0;
+ while (i < n) {
+ pair = bucket->pairs;
+ m = bucket->count;
+ j = 0;
+ while (j < m) {
+ count++;
+ pair++;
+ j++;
+ }
+ bucket++;
+ i++;
+ }
+ return count;
+}
+
+int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj)
+{
+ unsigned int i, j, n, m;
+ Bucket *bucket;
+ Pair *pair;
+
+ if (map == NULL) {
+ return 0;
+ }
+ if (enum_func == NULL) {
+ return 0;
+ }
+ bucket = map->buckets;
+ n = map->count;
+ i = 0;
+ while (i < n) {
+ pair = bucket->pairs;
+ m = bucket->count;
+ j = 0;
+ while (j < m) {
+ enum_func(pair->key, pair->value, (struct map_object*)obj);
+ pair++;
+ j++;
+ }
+ bucket++;
+ i++;
+ }
+ return 1;
+}
+
+/*
+ * Returns a pair from the bucket that matches the provided key,
+ * or null if no such pair exist.
+ */
+static Pair * get_pair(Bucket *bucket, const char *key)
+{
+ unsigned int i, n;
+ Pair *pair;
+
+ n = bucket->count;
+ if (n == 0) {
+ return NULL;
+ }
+ pair = bucket->pairs;
+ i = 0;
+ while (i < n) {
+ if (pair->key != NULL && pair->value != NULL) {
+ if (strcmp(pair->key, key) == 0) {
+ return pair;
+ }
+ }
+ pair++;
+ i++;
+ }
+ return NULL;
+}
+
+/*
+ * Returns a hash code for the provided string.
+ */
+static unsigned long hash(const char *str)
+{
+ unsigned long hash = 5381;
+ int c;
+
+ while ((c = *str++)) {
+ hash = ((hash << 5) + hash) + c;
+ }
+ return hash;
+}
+
+/*
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
+*/
diff --git a/agent/src/c/common/strmap.h b/agent/src/c/common/strmap.h
new file mode 100644
index 0000000..9c45ddf
--- /dev/null
+++ b/agent/src/c/common/strmap.h
@@ -0,0 +1,366 @@
+/*
+ * strmap version 2.0.1
+ *
+ * ANSI C hash table for strings.
+ *
+ * Version history:
+ * 1.0.0 - initial release
+ * 2.0.0 - changed function prefix from strmap to sm to ensure
+ * ANSI C compatibility
+ * 2.0.1 - improved documentation
+ *
+ * strmap.h
+ *
+ * Copyright (c) 2009, 2011, 2013 Per Ola Kristensson.
+ *
+ * Per Ola Kristensson <po...@cam.ac.uk>
+ * Inference Group, Department of Physics
+ * University of Cambridge
+ * Cavendish Laboratory
+ * JJ Thomson Avenue
+ * CB3 0HE Cambridge
+ * United Kingdom
+ *
+ * strmap is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * strmap is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with strmap. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef _STRMAP_H_
+#define _STRMAP_H_
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#define MAP_OBJECT_CB_TYPE 1
+
+typedef struct StrMap StrMap;
+
+struct map_object {
+ unsigned char type;
+ unsigned char status;
+ void *ptr;
+};
+
+/*
+ * This callback function is called once per key-value when iterating over
+ * all keys associated to values.
+ *
+ * Parameters:
+ *
+ * key: A pointer to a null-terminated C string. The string must not
+ * be modified by the client.
+ *
+ * value: A pointer to a null-terminated C string. The string must
+ * not be modified by the client.
+ *
+ * obj: A pointer to a client-specific object. This parameter may be
+ * null.
+ *
+ * Return value: None.
+ */
+typedef void(*sm_enum_func)(const char *key, const struct map_object *value, const void *obj);
+
+/*
+ * Creates a string map.
+ *
+ * Parameters:
+ *
+ * capacity: The number of top-level slots this string map
+ * should allocate. This parameter must be > 0.
+ *
+ * Return value: A pointer to a string map object,
+ * or null if a new string map could not be allocated.
+ */
+StrMap * sm_new(unsigned int capacity);
+
+/*
+ * Releases all memory held by a string map object.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ * If the supplied string map has been previously released, the
+ * behaviour of this function is undefined.
+ *
+ * Return value: None.
+ */
+void sm_delete(StrMap *map);
+
+/*
+ * Returns the value associated with the supplied key.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ *
+ * key: A pointer to a null-terminated C string. This parameter cannot
+ * be null.
+ *
+ * out_buf: A pointer to an output buffer which will contain the value,
+ * if it exists and fits into the buffer.
+ *
+ * n_out_buf: The size of the output buffer in bytes.
+ *
+ * Return value: If out_buf is set to null and n_out_buf is set to 0 the return
+ * value will be the number of bytes required to store the value (if it exists)
+ * and its null-terminator. For all other parameter configurations the return value
+ * is 1 if an associated value was found and completely copied into the output buffer,
+ * 0 otherwise.
+ */
+int sm_get(const StrMap *map, const char *key, struct map_object **out_buf);
+
+
+int sm_del_pair(const StrMap *map, const char *key);
+
+/*
+ * Queries the existence of a key.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ *
+ * key: A pointer to a null-terminated C string. This parameter cannot
+ * be null.
+ *
+ * Return value: 1 if the key exists, 0 otherwise.
+ */
+int sm_exists(const StrMap *map, const char *key);
+
+/*
+ * Associates a value with the supplied key. If the key is already
+ * associated with a value, the previous value is replaced.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ *
+ * key: A pointer to a null-terminated C string. This parameter
+ * cannot be null. The string must have a string length > 0. The
+ * string will be copied.
+ *
+ * value: A pointer to a null-terminated C string. This parameter
+ * cannot be null. The string must have a string length > 0. The
+ * string will be copied.
+ *
+ * Return value: 1 if the association succeeded, 0 otherwise.
+ */
+int sm_put(StrMap *map, const char *key, struct map_object *value);
+
+/*
+ * Returns the number of associations between keys and values.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ *
+ * Return value: The number of associations between keys and values.
+ */
+int sm_get_count(const StrMap *map);
+
+/*
+ * An enumerator over all associations between keys and values.
+ *
+ * Parameters:
+ *
+ * map: A pointer to a string map. This parameter cannot be null.
+ *
+ * enum_func: A pointer to a callback function that will be
+ * called by this procedure once for every key associated
+ * with a value. This parameter cannot be null.
+ *
+ * obj: A pointer to a client-specific object. This parameter will be
+ * passed back to the client's callback function. This parameter can
+ * be null.
+ *
+ * Return value: 1 if enumeration completed, 0 otherwise.
+ */
+int sm_enum(const StrMap *map, sm_enum_func enum_func, const void *obj);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+/*
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
+*/
\ No newline at end of file
diff --git a/agent/src/c/include/hiredis/adapters/ae.h b/agent/src/c/include/hiredis/adapters/ae.h
new file mode 100644
index 0000000..5c551c2
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/ae.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_AE_H__
+#define __HIREDIS_AE_H__
+#include <sys/types.h>
+#include <ae.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisAeEvents {
+ redisAsyncContext *context;
+ aeEventLoop *loop;
+ int fd;
+ int reading, writing;
+} redisAeEvents;
+
+static void redisAeReadEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+ ((void)el); ((void)fd); ((void)mask);
+
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ redisAsyncHandleRead(e->context);
+}
+
+static void redisAeWriteEvent(aeEventLoop *el, int fd, void *privdata, int mask) {
+ ((void)el); ((void)fd); ((void)mask);
+
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ redisAsyncHandleWrite(e->context);
+}
+
+static void redisAeAddRead(void *privdata) {
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ aeEventLoop *loop = e->loop;
+ if (!e->reading) {
+ e->reading = 1;
+ aeCreateFileEvent(loop,e->fd,AE_READABLE,redisAeReadEvent,e);
+ }
+}
+
+static void redisAeDelRead(void *privdata) {
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ aeEventLoop *loop = e->loop;
+ if (e->reading) {
+ e->reading = 0;
+ aeDeleteFileEvent(loop,e->fd,AE_READABLE);
+ }
+}
+
+static void redisAeAddWrite(void *privdata) {
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ aeEventLoop *loop = e->loop;
+ if (!e->writing) {
+ e->writing = 1;
+ aeCreateFileEvent(loop,e->fd,AE_WRITABLE,redisAeWriteEvent,e);
+ }
+}
+
+static void redisAeDelWrite(void *privdata) {
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ aeEventLoop *loop = e->loop;
+ if (e->writing) {
+ e->writing = 0;
+ aeDeleteFileEvent(loop,e->fd,AE_WRITABLE);
+ }
+}
+
+static void redisAeCleanup(void *privdata) {
+ redisAeEvents *e = (redisAeEvents*)privdata;
+ redisAeDelRead(privdata);
+ redisAeDelWrite(privdata);
+ free(e);
+}
+
+static int redisAeAttach(aeEventLoop *loop, redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+ redisAeEvents *e;
+
+ /* Nothing should be attached when something is already attached */
+ if (ac->ev.data != NULL)
+ return REDIS_ERR;
+
+ /* Create container for context and r/w events */
+ e = (redisAeEvents*)malloc(sizeof(*e));
+ e->context = ac;
+ e->loop = loop;
+ e->fd = c->fd;
+ e->reading = e->writing = 0;
+
+ /* Register functions to start/stop listening for events */
+ ac->ev.addRead = redisAeAddRead;
+ ac->ev.delRead = redisAeDelRead;
+ ac->ev.addWrite = redisAeAddWrite;
+ ac->ev.delWrite = redisAeDelWrite;
+ ac->ev.cleanup = redisAeCleanup;
+ ac->ev.data = e;
+
+ return REDIS_OK;
+}
+#endif
diff --git a/agent/src/c/include/hiredis/adapters/glib.h b/agent/src/c/include/hiredis/adapters/glib.h
new file mode 100644
index 0000000..f61631b
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/glib.h
@@ -0,0 +1,169 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef __HIREDIS_GLIB_H__
+#define __HIREDIS_GLIB_H__
+
+#include <glib.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct
+{
+ GSource source;
+ redisAsyncContext *ac;
+ GPollFD poll_fd;
+} RedisSource;
+
+static void
+redis_source_add_read (gpointer data)
+{
+ RedisSource *source = (RedisSource *)data;
+ g_return_if_fail(source);
+ source->poll_fd.events |= G_IO_IN;
+ g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_read (gpointer data)
+{
+ RedisSource *source = (RedisSource *)data;
+ g_return_if_fail(source);
+ source->poll_fd.events &= ~G_IO_IN;
+ g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_add_write (gpointer data)
+{
+ RedisSource *source = (RedisSource *)data;
+ g_return_if_fail(source);
+ source->poll_fd.events |= G_IO_OUT;
+ g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_del_write (gpointer data)
+{
+ RedisSource *source = (RedisSource *)data;
+ g_return_if_fail(source);
+ source->poll_fd.events &= ~G_IO_OUT;
+ g_main_context_wakeup(g_source_get_context((GSource *)data));
+}
+
+static void
+redis_source_cleanup (gpointer data)
+{
+ RedisSource *source = (RedisSource *)data;
+
+ g_return_if_fail(source);
+
+ redis_source_del_read(source);
+ redis_source_del_write(source);
+ /*
+ * It is not our responsibility to remove ourself from the
+ * current main loop. However, we will remove the GPollFD.
+ */
+ if (source->poll_fd.fd >= 0) {
+ g_source_remove_poll((GSource *)data, &source->poll_fd);
+ source->poll_fd.fd = -1;
+ }
+}
+
+static gboolean
+redis_source_prepare (GSource *source,
+ gint *timeout_)
+{
+ RedisSource *redis = (RedisSource *)source;
+ *timeout_ = -1;
+ return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_check (GSource *source)
+{
+ RedisSource *redis = (RedisSource *)source;
+ return !!(redis->poll_fd.events & redis->poll_fd.revents);
+}
+
+static gboolean
+redis_source_dispatch (GSource *source,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ RedisSource *redis = (RedisSource *)source;
+
+ if ((redis->poll_fd.revents & G_IO_OUT)) {
+ redisAsyncHandleWrite(redis->ac);
+ redis->poll_fd.revents &= ~G_IO_OUT;
+ }
+
+ if ((redis->poll_fd.revents & G_IO_IN)) {
+ redisAsyncHandleRead(redis->ac);
+ redis->poll_fd.revents &= ~G_IO_IN;
+ }
+
+ if (callback) {
+ return callback(user_data);
+ }
+
+ return TRUE;
+}
+
+static void
+redis_source_finalize (GSource *source)
+{
+ RedisSource *redis = (RedisSource *)source;
+
+ if (redis->poll_fd.fd >= 0) {
+ g_source_remove_poll(source, &redis->poll_fd);
+ redis->poll_fd.fd = -1;
+ }
+}
+
+static GSource *
+redis_source_new (redisAsyncContext *ac)
+{
+ static GSourceFuncs source_funcs = {
+ .prepare = redis_source_prepare,
+ .check = redis_source_check,
+ .dispatch = redis_source_dispatch,
+ .finalize = redis_source_finalize,
+ };
+ redisContext *c = &ac->c;
+ RedisSource *source;
+
+ g_return_val_if_fail(ac != NULL, NULL);
+
+ source = (RedisSource *)g_source_new(&source_funcs, sizeof *source);
+ source->ac = ac;
+ source->poll_fd.fd = c->fd;
+ source->poll_fd.events = 0;
+ source->poll_fd.revents = 0;
+ g_source_add_poll((GSource *)source, &source->poll_fd);
+
+ ac->ev.addRead = redis_source_add_read;
+ ac->ev.delRead = redis_source_del_read;
+ ac->ev.addWrite = redis_source_add_write;
+ ac->ev.delWrite = redis_source_del_write;
+ ac->ev.cleanup = redis_source_cleanup;
+ ac->ev.data = source;
+
+ return (GSource *)source;
+}
+
+#endif /* __HIREDIS_GLIB_H__ */
diff --git a/agent/src/c/include/hiredis/adapters/ivykis.h b/agent/src/c/include/hiredis/adapters/ivykis.h
new file mode 100644
index 0000000..6a12a86
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/ivykis.h
@@ -0,0 +1,81 @@
+#ifndef __HIREDIS_IVYKIS_H__
+#define __HIREDIS_IVYKIS_H__
+#include <iv.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisIvykisEvents {
+ redisAsyncContext *context;
+ struct iv_fd fd;
+} redisIvykisEvents;
+
+static void redisIvykisReadEvent(void *arg) {
+ redisAsyncContext *context = (redisAsyncContext *)arg;
+ redisAsyncHandleRead(context);
+}
+
+static void redisIvykisWriteEvent(void *arg) {
+ redisAsyncContext *context = (redisAsyncContext *)arg;
+ redisAsyncHandleWrite(context);
+}
+
+static void redisIvykisAddRead(void *privdata) {
+ redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+ iv_fd_set_handler_in(&e->fd, redisIvykisReadEvent);
+}
+
+static void redisIvykisDelRead(void *privdata) {
+ redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+ iv_fd_set_handler_in(&e->fd, NULL);
+}
+
+static void redisIvykisAddWrite(void *privdata) {
+ redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+ iv_fd_set_handler_out(&e->fd, redisIvykisWriteEvent);
+}
+
+static void redisIvykisDelWrite(void *privdata) {
+ redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+ iv_fd_set_handler_out(&e->fd, NULL);
+}
+
+static void redisIvykisCleanup(void *privdata) {
+ redisIvykisEvents *e = (redisIvykisEvents*)privdata;
+
+ iv_fd_unregister(&e->fd);
+ free(e);
+}
+
+static int redisIvykisAttach(redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+ redisIvykisEvents *e;
+
+ /* Nothing should be attached when something is already attached */
+ if (ac->ev.data != NULL)
+ return REDIS_ERR;
+
+ /* Create container for context and r/w events */
+ e = (redisIvykisEvents*)malloc(sizeof(*e));
+ e->context = ac;
+
+ /* Register functions to start/stop listening for events */
+ ac->ev.addRead = redisIvykisAddRead;
+ ac->ev.delRead = redisIvykisDelRead;
+ ac->ev.addWrite = redisIvykisAddWrite;
+ ac->ev.delWrite = redisIvykisDelWrite;
+ ac->ev.cleanup = redisIvykisCleanup;
+ ac->ev.data = e;
+
+ /* Initialize and install read/write events */
+ IV_FD_INIT(&e->fd);
+ e->fd.fd = c->fd;
+ e->fd.handler_in = redisIvykisReadEvent;
+ e->fd.handler_out = redisIvykisWriteEvent;
+ e->fd.handler_err = NULL;
+ e->fd.cookie = e->context;
+
+ iv_fd_register(&e->fd);
+
+ return REDIS_OK;
+}
+#endif
diff --git a/agent/src/c/include/hiredis/adapters/libev.h b/agent/src/c/include/hiredis/adapters/libev.h
new file mode 100644
index 0000000..2bf8d52
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/libev.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEV_H__
+#define __HIREDIS_LIBEV_H__
+#include <stdlib.h>
+#include <sys/types.h>
+#include <ev.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibevEvents {
+ redisAsyncContext *context;
+ struct ev_loop *loop;
+ int reading, writing;
+ ev_io rev, wev;
+} redisLibevEvents;
+
+static void redisLibevReadEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+ ((void)loop);
+#endif
+ ((void)revents);
+
+ redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+ redisAsyncHandleRead(e->context);
+}
+
+static void redisLibevWriteEvent(EV_P_ ev_io *watcher, int revents) {
+#if EV_MULTIPLICITY
+ ((void)loop);
+#endif
+ ((void)revents);
+
+ redisLibevEvents *e = (redisLibevEvents*)watcher->data;
+ redisAsyncHandleWrite(e->context);
+}
+
+static void redisLibevAddRead(void *privdata) {
+ redisLibevEvents *e = (redisLibevEvents*)privdata;
+ struct ev_loop *loop = e->loop;
+ ((void)loop);
+ if (!e->reading) {
+ e->reading = 1;
+ ev_io_start(EV_A_ &e->rev);
+ }
+}
+
+static void redisLibevDelRead(void *privdata) {
+ redisLibevEvents *e = (redisLibevEvents*)privdata;
+ struct ev_loop *loop = e->loop;
+ ((void)loop);
+ if (e->reading) {
+ e->reading = 0;
+ ev_io_stop(EV_A_ &e->rev);
+ }
+}
+
+static void redisLibevAddWrite(void *privdata) {
+ redisLibevEvents *e = (redisLibevEvents*)privdata;
+ struct ev_loop *loop = e->loop;
+ ((void)loop);
+ if (!e->writing) {
+ e->writing = 1;
+ ev_io_start(EV_A_ &e->wev);
+ }
+}
+
+static void redisLibevDelWrite(void *privdata) {
+ redisLibevEvents *e = (redisLibevEvents*)privdata;
+ struct ev_loop *loop = e->loop;
+ ((void)loop);
+ if (e->writing) {
+ e->writing = 0;
+ ev_io_stop(EV_A_ &e->wev);
+ }
+}
+
+static void redisLibevCleanup(void *privdata) {
+ redisLibevEvents *e = (redisLibevEvents*)privdata;
+ redisLibevDelRead(privdata);
+ redisLibevDelWrite(privdata);
+ free(e);
+}
+
+static int redisLibevAttach(EV_P_ redisAsyncContext *ac) {
+ redisContext *c = &(ac->c);
+ redisLibevEvents *e;
+
+ /* Nothing should be attached when something is already attached */
+ if (ac->ev.data != NULL)
+ return REDIS_ERR;
+
+ /* Create container for context and r/w events */
+ e = (redisLibevEvents*)malloc(sizeof(*e));
+ e->context = ac;
+#if EV_MULTIPLICITY
+ e->loop = loop;
+#else
+ e->loop = NULL;
+#endif
+ e->reading = e->writing = 0;
+ e->rev.data = e;
+ e->wev.data = e;
+
+ /* Register functions to start/stop listening for events */
+ ac->ev.addRead = redisLibevAddRead;
+ ac->ev.delRead = redisLibevDelRead;
+ ac->ev.addWrite = redisLibevAddWrite;
+ ac->ev.delWrite = redisLibevDelWrite;
+ ac->ev.cleanup = redisLibevCleanup;
+ ac->ev.data = e;
+
+ /* Initialize read/write events */
+ ev_io_init(&e->rev,redisLibevReadEvent,c->fd,EV_READ);
+ ev_io_init(&e->wev,redisLibevWriteEvent,c->fd,EV_WRITE);
+ return REDIS_OK;
+}
+
+#endif
diff --git a/agent/src/c/include/hiredis/adapters/libevent.h b/agent/src/c/include/hiredis/adapters/libevent.h
new file mode 100644
index 0000000..1c2b271
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/libevent.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_LIBEVENT_H__
+#define __HIREDIS_LIBEVENT_H__
+#include <event.h>
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct redisLibeventEvents {
+ redisAsyncContext *context;
+ struct event rev, wev;
+} redisLibeventEvents;
+
+static void redisLibeventReadEvent(int fd, short event, void *arg) {
+ ((void)fd); ((void)event);
+ redisLibeventEvents *e = (redisLibeventEvents*)arg;
+ redisAsyncHandleRead(e->context);
+}
+
+static void redisLibeventWriteEvent(int fd, short event, void *arg) {
+ ((void)fd); ((void)event);
+ redisLibeventEvents *e = (redisLibeventEvents*)arg;
+ redisAsyncHandleWrite(e->context);
+}
+
+static void redisLibeventAddRead(void *privdata) {
+ redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ event_add(&e->rev,NULL);
+}
+
+static void redisLibeventDelRead(void *privdata) {
+ redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ event_del(&e->rev);
+}
+
+static void redisLibeventAddWrite(void *privdata) {
+ redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ event_add(&e->wev,NULL);
+}
+
+static void redisLibeventDelWrite(void *privdata) {
+ redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ event_del(&e->wev);
+}
+
+static void redisLibeventCleanup(void *privdata) {
+ redisLibeventEvents *e = (redisLibeventEvents*)privdata;
+ event_del(&e->rev);
+ event_del(&e->wev);
+ free(e);
+}
+
+static int redisLibeventAttach(redisAsyncContext *ac, struct event_base *base) {
+ redisContext *c = &(ac->c);
+ redisLibeventEvents *e;
+
+ /* Nothing should be attached when something is already attached */
+ if (ac->ev.data != NULL)
+ return REDIS_ERR;
+
+ /* Create container for context and r/w events */
+ e = (redisLibeventEvents*)malloc(sizeof(*e));
+ e->context = ac;
+
+ /* Register functions to start/stop listening for events */
+ ac->ev.addRead = redisLibeventAddRead;
+ ac->ev.delRead = redisLibeventDelRead;
+ ac->ev.addWrite = redisLibeventAddWrite;
+ ac->ev.delWrite = redisLibeventDelWrite;
+ ac->ev.cleanup = redisLibeventCleanup;
+ ac->ev.data = e;
+
+ /* Initialize and install read/write events */
+ event_set(&e->rev,c->fd,EV_READ,redisLibeventReadEvent,e);
+ event_set(&e->wev,c->fd,EV_WRITE,redisLibeventWriteEvent,e);
+ event_base_set(base,&e->rev);
+ event_base_set(base,&e->wev);
+ return REDIS_OK;
+}
+#endif
diff --git a/agent/src/c/include/hiredis/adapters/libuv.h b/agent/src/c/include/hiredis/adapters/libuv.h
new file mode 100644
index 0000000..3cdf3d3
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/libuv.h
@@ -0,0 +1,121 @@
+#ifndef __HIREDIS_LIBUV_H__
+#define __HIREDIS_LIBUV_H__
+#include <stdlib.h>
+#include <uv.h>
+#include "../hiredis.h"
+#include "../async.h"
+#include <string.h>
+
+typedef struct redisLibuvEvents {
+ redisAsyncContext* context;
+ uv_poll_t handle;
+ int events;
+} redisLibuvEvents;
+
+
+static void redisLibuvPoll(uv_poll_t* handle, int status, int events) {
+ redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+
+ if (status != 0) {
+ return;
+ }
+
+ if (events & UV_READABLE) {
+ redisAsyncHandleRead(p->context);
+ }
+ if (events & UV_WRITABLE) {
+ redisAsyncHandleWrite(p->context);
+ }
+}
+
+
+static void redisLibuvAddRead(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events |= UV_READABLE;
+
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelRead(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events &= ~UV_READABLE;
+
+ if (p->events) {
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+ } else {
+ uv_poll_stop(&p->handle);
+ }
+}
+
+
+static void redisLibuvAddWrite(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events |= UV_WRITABLE;
+
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+}
+
+
+static void redisLibuvDelWrite(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ p->events &= ~UV_WRITABLE;
+
+ if (p->events) {
+ uv_poll_start(&p->handle, p->events, redisLibuvPoll);
+ } else {
+ uv_poll_stop(&p->handle);
+ }
+}
+
+
+static void on_close(uv_handle_t* handle) {
+ redisLibuvEvents* p = (redisLibuvEvents*)handle->data;
+
+ free(p);
+}
+
+
+static void redisLibuvCleanup(void *privdata) {
+ redisLibuvEvents* p = (redisLibuvEvents*)privdata;
+
+ uv_close((uv_handle_t*)&p->handle, on_close);
+}
+
+
+static int redisLibuvAttach(redisAsyncContext* ac, uv_loop_t* loop) {
+ redisContext *c = &(ac->c);
+
+ if (ac->ev.data != NULL) {
+ return REDIS_ERR;
+ }
+
+ ac->ev.addRead = redisLibuvAddRead;
+ ac->ev.delRead = redisLibuvDelRead;
+ ac->ev.addWrite = redisLibuvAddWrite;
+ ac->ev.delWrite = redisLibuvDelWrite;
+ ac->ev.cleanup = redisLibuvCleanup;
+
+ redisLibuvEvents* p = (redisLibuvEvents*)malloc(sizeof(*p));
+
+ if (!p) {
+ return REDIS_ERR;
+ }
+
+ memset(p, 0, sizeof(*p));
+
+ if (uv_poll_init(loop, &p->handle, c->fd) != 0) {
+ return REDIS_ERR;
+ }
+
+ ac->ev.data = p;
+ p->handle.data = p;
+ p->context = ac;
+
+ return REDIS_OK;
+}
+#endif
diff --git a/agent/src/c/include/hiredis/adapters/macosx.h b/agent/src/c/include/hiredis/adapters/macosx.h
new file mode 100644
index 0000000..72121f6
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/macosx.h
@@ -0,0 +1,114 @@
+//
+// Created by Дмитрий Бахвалов on 13.07.15.
+// Copyright (c) 2015 Dmitry Bakhvalov. All rights reserved.
+//
+
+#ifndef __HIREDIS_MACOSX_H__
+#define __HIREDIS_MACOSX_H__
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "../hiredis.h"
+#include "../async.h"
+
+typedef struct {
+ redisAsyncContext *context;
+ CFSocketRef socketRef;
+ CFRunLoopSourceRef sourceRef;
+} RedisRunLoop;
+
+static int freeRedisRunLoop(RedisRunLoop* redisRunLoop) {
+ if( redisRunLoop != NULL ) {
+ if( redisRunLoop->sourceRef != NULL ) {
+ CFRunLoopSourceInvalidate(redisRunLoop->sourceRef);
+ CFRelease(redisRunLoop->sourceRef);
+ }
+ if( redisRunLoop->socketRef != NULL ) {
+ CFSocketInvalidate(redisRunLoop->socketRef);
+ CFRelease(redisRunLoop->socketRef);
+ }
+ free(redisRunLoop);
+ }
+ return REDIS_ERR;
+}
+
+static void redisMacOSAddRead(void *privdata) {
+ RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+ CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSDelRead(void *privdata) {
+ RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+ CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketReadCallBack);
+}
+
+static void redisMacOSAddWrite(void *privdata) {
+ RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+ CFSocketEnableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSDelWrite(void *privdata) {
+ RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+ CFSocketDisableCallBacks(redisRunLoop->socketRef, kCFSocketWriteCallBack);
+}
+
+static void redisMacOSCleanup(void *privdata) {
+ RedisRunLoop *redisRunLoop = (RedisRunLoop*)privdata;
+ freeRedisRunLoop(redisRunLoop);
+}
+
+static void redisMacOSAsyncCallback(CFSocketRef __unused s, CFSocketCallBackType callbackType, CFDataRef __unused address, const void __unused *data, void *info) {
+ redisAsyncContext* context = (redisAsyncContext*) info;
+
+ switch (callbackType) {
+ case kCFSocketReadCallBack:
+ redisAsyncHandleRead(context);
+ break;
+
+ case kCFSocketWriteCallBack:
+ redisAsyncHandleWrite(context);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static int redisMacOSAttach(redisAsyncContext *redisAsyncCtx, CFRunLoopRef runLoop) {
+ redisContext *redisCtx = &(redisAsyncCtx->c);
+
+ /* Nothing should be attached when something is already attached */
+ if( redisAsyncCtx->ev.data != NULL ) return REDIS_ERR;
+
+ RedisRunLoop* redisRunLoop = (RedisRunLoop*) calloc(1, sizeof(RedisRunLoop));
+ if( !redisRunLoop ) return REDIS_ERR;
+
+ /* Setup redis stuff */
+ redisRunLoop->context = redisAsyncCtx;
+
+ redisAsyncCtx->ev.addRead = redisMacOSAddRead;
+ redisAsyncCtx->ev.delRead = redisMacOSDelRead;
+ redisAsyncCtx->ev.addWrite = redisMacOSAddWrite;
+ redisAsyncCtx->ev.delWrite = redisMacOSDelWrite;
+ redisAsyncCtx->ev.cleanup = redisMacOSCleanup;
+ redisAsyncCtx->ev.data = redisRunLoop;
+
+ /* Initialize and install read/write events */
+ CFSocketContext socketCtx = { 0, redisAsyncCtx, NULL, NULL, NULL };
+
+ redisRunLoop->socketRef = CFSocketCreateWithNative(NULL, redisCtx->fd,
+ kCFSocketReadCallBack | kCFSocketWriteCallBack,
+ redisMacOSAsyncCallback,
+ &socketCtx);
+ if( !redisRunLoop->socketRef ) return freeRedisRunLoop(redisRunLoop);
+
+ redisRunLoop->sourceRef = CFSocketCreateRunLoopSource(NULL, redisRunLoop->socketRef, 0);
+ if( !redisRunLoop->sourceRef ) return freeRedisRunLoop(redisRunLoop);
+
+ CFRunLoopAddSource(runLoop, redisRunLoop->sourceRef, kCFRunLoopDefaultMode);
+
+ return REDIS_OK;
+}
+
+#endif
+
diff --git a/agent/src/c/include/hiredis/adapters/qt.h b/agent/src/c/include/hiredis/adapters/qt.h
new file mode 100644
index 0000000..5cc02e6
--- /dev/null
+++ b/agent/src/c/include/hiredis/adapters/qt.h
@@ -0,0 +1,135 @@
+/*-
+ * Copyright (C) 2014 Pietro Cerutti <ga...@gahr.ch>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_QT_H__
+#define __HIREDIS_QT_H__
+#include <QSocketNotifier>
+#include "../async.h"
+
+static void RedisQtAddRead(void *);
+static void RedisQtDelRead(void *);
+static void RedisQtAddWrite(void *);
+static void RedisQtDelWrite(void *);
+static void RedisQtCleanup(void *);
+
+class RedisQtAdapter : public QObject {
+
+ Q_OBJECT
+
+ friend
+ void RedisQtAddRead(void * adapter) {
+ RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+ a->addRead();
+ }
+
+ friend
+ void RedisQtDelRead(void * adapter) {
+ RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+ a->delRead();
+ }
+
+ friend
+ void RedisQtAddWrite(void * adapter) {
+ RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+ a->addWrite();
+ }
+
+ friend
+ void RedisQtDelWrite(void * adapter) {
+ RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+ a->delWrite();
+ }
+
+ friend
+ void RedisQtCleanup(void * adapter) {
+ RedisQtAdapter * a = static_cast<RedisQtAdapter *>(adapter);
+ a->cleanup();
+ }
+
+ public:
+ RedisQtAdapter(QObject * parent = 0)
+ : QObject(parent), m_ctx(0), m_read(0), m_write(0) { }
+
+ ~RedisQtAdapter() {
+ if (m_ctx != 0) {
+ m_ctx->ev.data = NULL;
+ }
+ }
+
+ int setContext(redisAsyncContext * ac) {
+ if (ac->ev.data != NULL) {
+ return REDIS_ERR;
+ }
+ m_ctx = ac;
+ m_ctx->ev.data = this;
+ m_ctx->ev.addRead = RedisQtAddRead;
+ m_ctx->ev.delRead = RedisQtDelRead;
+ m_ctx->ev.addWrite = RedisQtAddWrite;
+ m_ctx->ev.delWrite = RedisQtDelWrite;
+ m_ctx->ev.cleanup = RedisQtCleanup;
+ return REDIS_OK;
+ }
+
+ private:
+ void addRead() {
+ if (m_read) return;
+ m_read = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Read, 0);
+ connect(m_read, SIGNAL(activated(int)), this, SLOT(read()));
+ }
+
+ void delRead() {
+ if (!m_read) return;
+ delete m_read;
+ m_read = 0;
+ }
+
+ void addWrite() {
+ if (m_write) return;
+ m_write = new QSocketNotifier(m_ctx->c.fd, QSocketNotifier::Write, 0);
+ connect(m_write, SIGNAL(activated(int)), this, SLOT(write()));
+ }
+
+ void delWrite() {
+ if (!m_write) return;
+ delete m_write;
+ m_write = 0;
+ }
+
+ void cleanup() {
+ delRead();
+ delWrite();
+ }
+
+ private slots:
+ void read() { redisAsyncHandleRead(m_ctx); }
+ void write() { redisAsyncHandleWrite(m_ctx); }
+
+ private:
+ redisAsyncContext * m_ctx;
+ QSocketNotifier * m_read;
+ QSocketNotifier * m_write;
+};
+
+#endif /* !__HIREDIS_QT_H__ */
diff --git a/agent/src/c/include/hiredis/async.h b/agent/src/c/include/hiredis/async.h
new file mode 100644
index 0000000..59cbf46
--- /dev/null
+++ b/agent/src/c/include/hiredis/async.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_ASYNC_H
+#define __HIREDIS_ASYNC_H
+#include "hiredis.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct redisAsyncContext; /* need forward declaration of redisAsyncContext */
+struct dict; /* dictionary header is included in async.c */
+
+/* Reply callback prototype and container */
+typedef void (redisCallbackFn)(struct redisAsyncContext*, void*, void*);
+typedef struct redisCallback {
+ struct redisCallback *next; /* simple singly linked list */
+ redisCallbackFn *fn;
+ void *privdata;
+} redisCallback;
+
+/* List of callbacks for either regular replies or pub/sub */
+typedef struct redisCallbackList {
+ redisCallback *head, *tail;
+} redisCallbackList;
+
+/* Connection callback prototypes */
+typedef void (redisDisconnectCallback)(const struct redisAsyncContext*, int status);
+typedef void (redisConnectCallback)(const struct redisAsyncContext*, int status);
+
+/* Context for an async connection to Redis */
+typedef struct redisAsyncContext {
+ /* Hold the regular context, so it can be realloc'ed. */
+ redisContext c;
+
+ /* Setup error flags so they can be used directly. */
+ int err;
+ char *errstr;
+
+ /* Not used by hiredis */
+ void *data;
+
+ /* Event library data and hooks */
+ struct {
+ void *data;
+
+ /* Hooks that are called when the library expects to start
+ * reading/writing. These functions should be idempotent. */
+ void (*addRead)(void *privdata);
+ void (*delRead)(void *privdata);
+ void (*addWrite)(void *privdata);
+ void (*delWrite)(void *privdata);
+ void (*cleanup)(void *privdata);
+ } ev;
+
+ /* Called when either the connection is terminated due to an error or per
+ * user request. The status is set accordingly (REDIS_OK, REDIS_ERR). */
+ redisDisconnectCallback *onDisconnect;
+
+ /* Called when the first write event was received. */
+ redisConnectCallback *onConnect;
+
+ /* Regular command callbacks */
+ redisCallbackList replies;
+
+ /* Subscription callbacks */
+ struct {
+ redisCallbackList invalid;
+ struct dict *channels;
+ struct dict *patterns;
+ } sub;
+} redisAsyncContext;
+
+/* Functions that proxy to hiredis */
+redisAsyncContext *redisAsyncConnect(const char *ip, int port);
+redisAsyncContext *redisAsyncConnectBind(const char *ip, int port, const char *source_addr);
+redisAsyncContext *redisAsyncConnectBindWithReuse(const char *ip, int port,
+ const char *source_addr);
+redisAsyncContext *redisAsyncConnectUnix(const char *path);
+int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn);
+int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn);
+void redisAsyncDisconnect(redisAsyncContext *ac);
+void redisAsyncFree(redisAsyncContext *ac);
+
+/* Handle read/write events */
+void redisAsyncHandleRead(redisAsyncContext *ac);
+void redisAsyncHandleWrite(redisAsyncContext *ac);
+
+/* Command functions for an async context. Write the command to the
+ * output buffer and register the provided callback. */
+int redisvAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, va_list ap);
+int redisAsyncCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *format, ...);
+int redisAsyncCommandArgv(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, int argc, const char **argv, const size_t *argvlen);
+int redisAsyncFormattedCommand(redisAsyncContext *ac, redisCallbackFn *fn, void *privdata, const char *cmd, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agent/src/c/include/hiredis/hiredis.h b/agent/src/c/include/hiredis/hiredis.h
new file mode 100644
index 0000000..d2a6e90
--- /dev/null
+++ b/agent/src/c/include/hiredis/hiredis.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>,
+ * Jan-Erik Rediger <janerik at fnordig dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __HIREDIS_H
+#define __HIREDIS_H
+#include "read.h"
+#include <stdarg.h> /* for va_list */
+#include <sys/time.h> /* for struct timeval */
+#include <stdint.h> /* uintXX_t, etc */
+#include "sds.h" /* for sds */
+
+#define HIREDIS_MAJOR 0
+#define HIREDIS_MINOR 13
+#define HIREDIS_PATCH 3
+#define HIREDIS_SONAME 0.13
+
+/* Connection type can be blocking or non-blocking and is set in the
+ * least significant bit of the flags field in redisContext. */
+#define REDIS_BLOCK 0x1
+
+/* Connection may be disconnected before being free'd. The second bit
+ * in the flags field is set when the context is connected. */
+#define REDIS_CONNECTED 0x2
+
+/* The async API might try to disconnect cleanly and flush the output
+ * buffer and read all subsequent replies before disconnecting.
+ * This flag means no new commands can come in and the connection
+ * should be terminated once all replies have been read. */
+#define REDIS_DISCONNECTING 0x4
+
+/* Flag specific to the async API which means that the context should be clean
+ * up as soon as possible. */
+#define REDIS_FREEING 0x8
+
+/* Flag that is set when an async callback is executed. */
+#define REDIS_IN_CALLBACK 0x10
+
+/* Flag that is set when the async context has one or more subscriptions. */
+#define REDIS_SUBSCRIBED 0x20
+
+/* Flag that is set when monitor mode is active */
+#define REDIS_MONITORING 0x40
+
+/* Flag that is set when we should set SO_REUSEADDR before calling bind() */
+#define REDIS_REUSEADDR 0x80
+
+#define REDIS_KEEPALIVE_INTERVAL 15 /* seconds */
+
+/* number of times we retry to connect in the case of EADDRNOTAVAIL and
+ * SO_REUSEADDR is being used. */
+#define REDIS_CONNECT_RETRIES 10
+
+/* strerror_r has two completely different prototypes and behaviors
+ * depending on system issues, so we need to operate on the error buffer
+ * differently depending on which strerror_r we're using. */
+#ifndef _GNU_SOURCE
+/* "regular" POSIX strerror_r that does the right thing. */
+#define __redis_strerror_r(errno, buf, len) \
+ do { \
+ strerror_r((errno), (buf), (len)); \
+ } while (0)
+#else
+/* "bad" GNU strerror_r we need to clean up after. */
+#define __redis_strerror_r(errno, buf, len) \
+ do { \
+ char *err_str = strerror_r((errno), (buf), (len)); \
+ /* If return value _isn't_ the start of the buffer we passed in, \
+ * then GNU strerror_r returned an internal static buffer and we \
+ * need to copy the result into our private buffer. */ \
+ if (err_str != (buf)) { \
+ strncpy((buf), err_str, ((len) - 1)); \
+ buf[(len)-1] = '\0'; \
+ } \
+ } while (0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the reply object returned by redisCommand() */
+typedef struct redisReply {
+ int type; /* REDIS_REPLY_* */
+ long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
+ size_t len; /* Length of string */
+ char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING */
+ size_t elements; /* number of elements, for REDIS_REPLY_ARRAY */
+ struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
+} redisReply;
+
+redisReader *redisReaderCreate(void);
+
+/* Function to free the reply objects hiredis returns by default. */
+void freeReplyObject(void *reply);
+
+/* Functions to format a command according to the protocol. */
+int redisvFormatCommand(char **target, const char *format, va_list ap);
+int redisFormatCommand(char **target, const char *format, ...);
+int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
+int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
+void redisFreeCommand(char *cmd);
+void redisFreeSdsCommand(sds cmd);
+
+enum redisConnectionType {
+ REDIS_CONN_TCP,
+ REDIS_CONN_UNIX,
+};
+
+/* Context for a connection to Redis */
+typedef struct redisContext {
+ int err; /* Error flags, 0 when there is no error */
+ char errstr[128]; /* String representation of error when applicable */
+ int fd;
+ int flags;
+ char *obuf; /* Write buffer */
+ redisReader *reader; /* Protocol reader */
+
+ enum redisConnectionType connection_type;
+ struct timeval *timeout;
+
+ struct {
+ char *host;
+ char *source_addr;
+ int port;
+ } tcp;
+
+ struct {
+ char *path;
+ } unix_sock;
+
+} redisContext;
+
+redisContext *redisConnect(const char *ip, int port);
+redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
+redisContext *redisConnectNonBlock(const char *ip, int port);
+redisContext *redisConnectBindNonBlock(const char *ip, int port,
+ const char *source_addr);
+redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
+ const char *source_addr);
+redisContext *redisConnectUnix(const char *path);
+redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
+redisContext *redisConnectUnixNonBlock(const char *path);
+redisContext *redisConnectFd(int fd);
+
+/**
+ * Reconnect the given context using the saved information.
+ *
+ * This re-uses the exact same connect options as in the initial connection.
+ * host, ip (or path), timeout and bind address are reused,
+ * flags are used unmodified from the existing context.
+ *
+ * Returns REDIS_OK on successful connect or REDIS_ERR otherwise.
+ */
+int redisReconnect(redisContext *c);
+
+int redisSetTimeout(redisContext *c, const struct timeval tv);
+int redisEnableKeepAlive(redisContext *c);
+void redisFree(redisContext *c);
+int redisFreeKeepFd(redisContext *c);
+int redisBufferRead(redisContext *c);
+int redisBufferWrite(redisContext *c, int *done);
+
+/* In a blocking context, this function first checks if there are unconsumed
+ * replies to return and returns one if so. Otherwise, it flushes the output
+ * buffer to the socket and reads until it has a reply. In a non-blocking
+ * context, it will return unconsumed replies until there are no more. */
+int redisGetReply(redisContext *c, void **reply);
+int redisGetReplyFromReader(redisContext *c, void **reply);
+
+/* Write a formatted command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
+
+/* Write a command to the output buffer. Use these functions in blocking mode
+ * to get a pipeline of commands. */
+int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
+int redisAppendCommand(redisContext *c, const char *format, ...);
+int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+/* Issue a command to Redis. In a blocking context, it is identical to calling
+ * redisAppendCommand, followed by redisGetReply. The function will return
+ * NULL if there was an error in performing the request, otherwise it will
+ * return the reply. In a non-blocking context, it is identical to calling
+ * only redisAppendCommand and will always return NULL. */
+void *redisvCommand(redisContext *c, const char *format, va_list ap);
+void *redisCommand(redisContext *c, const char *format, ...);
+void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agent/src/c/include/hiredis/read.h b/agent/src/c/include/hiredis/read.h
new file mode 100644
index 0000000..2988aa4
--- /dev/null
+++ b/agent/src/c/include/hiredis/read.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+#ifndef __HIREDIS_READ_H
+#define __HIREDIS_READ_H
+#include <stdio.h> /* for size_t */
+
+#define REDIS_ERR -1
+#define REDIS_OK 0
+
+/* When an error occurs, the err flag in a context is set to hold the type of
+ * error that occurred. REDIS_ERR_IO means there was an I/O error and you
+ * should use the "errno" variable to find out what is wrong.
+ * For other values, the "errstr" field will hold a description. */
+#define REDIS_ERR_IO 1 /* Error in read or write */
+#define REDIS_ERR_EOF 3 /* End of file */
+#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
+#define REDIS_ERR_OOM 5 /* Out of memory */
+#define REDIS_ERR_OTHER 2 /* Everything else... */
+
+#define REDIS_REPLY_STRING 1
+#define REDIS_REPLY_ARRAY 2
+#define REDIS_REPLY_INTEGER 3
+#define REDIS_REPLY_NIL 4
+#define REDIS_REPLY_STATUS 5
+#define REDIS_REPLY_ERROR 6
+
+#define REDIS_READER_MAX_BUF (1024*16) /* Default max unused reader buffer. */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct redisReadTask {
+ int type;
+ int elements; /* number of elements in multibulk container */
+ int idx; /* index in parent (array) object */
+ void *obj; /* holds user-generated value for a read task */
+ struct redisReadTask *parent; /* parent task */
+ void *privdata; /* user-settable arbitrary field */
+} redisReadTask;
+
+typedef struct redisReplyObjectFunctions {
+ void *(*createString)(const redisReadTask*, char*, size_t);
+ void *(*createArray)(const redisReadTask*, int);
+ void *(*createInteger)(const redisReadTask*, long long);
+ void *(*createNil)(const redisReadTask*);
+ void (*freeObject)(void*);
+} redisReplyObjectFunctions;
+
+typedef struct redisReader {
+ int err; /* Error flags, 0 when there is no error */
+ char errstr[128]; /* String representation of error when applicable */
+
+ char *buf; /* Read buffer */
+ size_t pos; /* Buffer cursor */
+ size_t len; /* Buffer length */
+ size_t maxbuf; /* Max length of unused buffer */
+
+ redisReadTask rstack[9];
+ int ridx; /* Index of current read task */
+ void *reply; /* Temporary reply pointer */
+
+ redisReplyObjectFunctions *fn;
+ void *privdata;
+} redisReader;
+
+/* Public API for the protocol parser. */
+redisReader *redisReaderCreateWithFunctions(redisReplyObjectFunctions *fn);
+void redisReaderFree(redisReader *r);
+int redisReaderFeed(redisReader *r, const char *buf, size_t len);
+int redisReaderGetReply(redisReader *r, void **reply);
+
+#define redisReaderSetPrivdata(_r, _p) (int)(((redisReader*)(_r))->privdata = (_p))
+#define redisReaderGetObject(_r) (((redisReader*)(_r))->reply)
+#define redisReaderGetError(_r) (((redisReader*)(_r))->errstr)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agent/src/c/include/hiredis/sds.h b/agent/src/c/include/hiredis/sds.h
new file mode 100644
index 0000000..394f8b5
--- /dev/null
+++ b/agent/src/c/include/hiredis/sds.h
@@ -0,0 +1,273 @@
+/* SDSLib 2.0 -- A C dynamic strings library
+ *
+ * Copyright (c) 2006-2015, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2015, Oran Agra
+ * Copyright (c) 2015, Redis Labs, Inc
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Redis nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __SDS_H
+#define __SDS_H
+
+#define SDS_MAX_PREALLOC (1024*1024)
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+typedef char *sds;
+
+/* Note: sdshdr5 is never used, we just access the flags byte directly.
+ * However is here to document the layout of type 5 SDS strings. */
+struct __attribute__ ((__packed__)) sdshdr5 {
+ unsigned char flags; /* 3 lsb of type, and 5 msb of string length */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr8 {
+ uint8_t len; /* used */
+ uint8_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr16 {
+ uint16_t len; /* used */
+ uint16_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr32 {
+ uint32_t len; /* used */
+ uint32_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+struct __attribute__ ((__packed__)) sdshdr64 {
+ uint64_t len; /* used */
+ uint64_t alloc; /* excluding the header and null terminator */
+ unsigned char flags; /* 3 lsb of type, 5 unused bits */
+ char buf[];
+};
+
+#define SDS_TYPE_5 0
+#define SDS_TYPE_8 1
+#define SDS_TYPE_16 2
+#define SDS_TYPE_32 3
+#define SDS_TYPE_64 4
+#define SDS_TYPE_MASK 7
+#define SDS_TYPE_BITS 3
+#define SDS_HDR_VAR(T,s) struct sdshdr##T *sh = (void*)((s)-(sizeof(struct sdshdr##T)));
+#define SDS_HDR(T,s) ((struct sdshdr##T *)((s)-(sizeof(struct sdshdr##T))))
+#define SDS_TYPE_5_LEN(f) ((f)>>SDS_TYPE_BITS)
+
+static inline size_t sdslen(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ return SDS_TYPE_5_LEN(flags);
+ case SDS_TYPE_8:
+ return SDS_HDR(8,s)->len;
+ case SDS_TYPE_16:
+ return SDS_HDR(16,s)->len;
+ case SDS_TYPE_32:
+ return SDS_HDR(32,s)->len;
+ case SDS_TYPE_64:
+ return SDS_HDR(64,s)->len;
+ }
+ return 0;
+}
+
+static inline size_t sdsavail(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5: {
+ return 0;
+ }
+ case SDS_TYPE_8: {
+ SDS_HDR_VAR(8,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_16: {
+ SDS_HDR_VAR(16,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_32: {
+ SDS_HDR_VAR(32,s);
+ return sh->alloc - sh->len;
+ }
+ case SDS_TYPE_64: {
+ SDS_HDR_VAR(64,s);
+ return sh->alloc - sh->len;
+ }
+ }
+ return 0;
+}
+
+static inline void sdssetlen(sds s, size_t newlen) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ {
+ unsigned char *fp = ((unsigned char*)s)-1;
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+ }
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->len = newlen;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->len = newlen;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->len = newlen;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->len = newlen;
+ break;
+ }
+}
+
+static inline void sdsinclen(sds s, size_t inc) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ {
+ unsigned char *fp = ((unsigned char*)s)-1;
+ unsigned char newlen = SDS_TYPE_5_LEN(flags)+inc;
+ *fp = SDS_TYPE_5 | (newlen << SDS_TYPE_BITS);
+ }
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->len += inc;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->len += inc;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->len += inc;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->len += inc;
+ break;
+ }
+}
+
+/* sdsalloc() = sdsavail() + sdslen() */
+static inline size_t sdsalloc(const sds s) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ return SDS_TYPE_5_LEN(flags);
+ case SDS_TYPE_8:
+ return SDS_HDR(8,s)->alloc;
+ case SDS_TYPE_16:
+ return SDS_HDR(16,s)->alloc;
+ case SDS_TYPE_32:
+ return SDS_HDR(32,s)->alloc;
+ case SDS_TYPE_64:
+ return SDS_HDR(64,s)->alloc;
+ }
+ return 0;
+}
+
+static inline void sdssetalloc(sds s, size_t newlen) {
+ unsigned char flags = s[-1];
+ switch(flags&SDS_TYPE_MASK) {
+ case SDS_TYPE_5:
+ /* Nothing to do, this type has no total allocation info. */
+ break;
+ case SDS_TYPE_8:
+ SDS_HDR(8,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_16:
+ SDS_HDR(16,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_32:
+ SDS_HDR(32,s)->alloc = newlen;
+ break;
+ case SDS_TYPE_64:
+ SDS_HDR(64,s)->alloc = newlen;
+ break;
+ }
+}
+
+sds sdsnewlen(const void *init, size_t initlen);
+sds sdsnew(const char *init);
+sds sdsempty(void);
+sds sdsdup(const sds s);
+void sdsfree(sds s);
+sds sdsgrowzero(sds s, size_t len);
+sds sdscatlen(sds s, const void *t, size_t len);
+sds sdscat(sds s, const char *t);
+sds sdscatsds(sds s, const sds t);
+sds sdscpylen(sds s, const char *t, size_t len);
+sds sdscpy(sds s, const char *t);
+
+sds sdscatvprintf(sds s, const char *fmt, va_list ap);
+#ifdef __GNUC__
+sds sdscatprintf(sds s, const char *fmt, ...)
+ __attribute__((format(printf, 2, 3)));
+#else
+sds sdscatprintf(sds s, const char *fmt, ...);
+#endif
+
+sds sdscatfmt(sds s, char const *fmt, ...);
+sds sdstrim(sds s, const char *cset);
+void sdsrange(sds s, int start, int end);
+void sdsupdatelen(sds s);
+void sdsclear(sds s);
+int sdscmp(const sds s1, const sds s2);
+sds *sdssplitlen(const char *s, int len, const char *sep, int seplen, int *count);
+void sdsfreesplitres(sds *tokens, int count);
+void sdstolower(sds s);
+void sdstoupper(sds s);
+sds sdsfromlonglong(long long value);
+sds sdscatrepr(sds s, const char *p, size_t len);
+sds *sdssplitargs(const char *line, int *argc);
+sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen);
+sds sdsjoin(char **argv, int argc, char *sep);
+sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
+
+/* Low level functions exposed to the user API */
+sds sdsMakeRoomFor(sds s, size_t addlen);
+void sdsIncrLen(sds s, int incr);
+sds sdsRemoveFreeSpace(sds s);
+size_t sdsAllocSize(sds s);
+void *sdsAllocPtr(sds s);
+
+/* Export the allocator used by SDS to the program using SDS.
+ * Sometimes the program SDS is linked to, may use a different set of
+ * allocators, but may want to allocate or free things that SDS will
+ * respectively free or allocate. */
+void *sds_malloc(size_t size);
+void *sds_realloc(void *ptr, size_t size);
+void sds_free(void *ptr);
+
+#ifdef REDIS_TEST
+int sdsTest(int argc, char *argv[]);
+#endif
+
+#endif
diff --git a/agent/src/c/include/zlog.h b/agent/src/c/include/zlog.h
new file mode 100644
index 0000000..a123825
--- /dev/null
+++ b/agent/src/c/include/zlog.h
@@ -0,0 +1,265 @@
+/*
+ * This file is part of the zlog Library.
+ *
+ * Copyright (C) 2011 by Hardy Simpson <Ha...@gmail.com>
+ *
+ * Licensed under the LGPL v2.1, see the file COPYING in base directory.
+ */
+
+#ifndef __zlog_h
+#define __zlog_h
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdarg.h> /* for va_list */
+#include <stdio.h> /* for size_t */
+
+# if defined __GNUC__
+# define ZLOG_CHECK_PRINTF(m,n) __attribute__((format(printf,m,n)))
+# else
+# define ZLOG_CHECK_PRINTF(m,n)
+# endif
+
+typedef struct zlog_category_s zlog_category_t;
+
+int zlog_init(const char *confpath);
+int zlog_reload(const char *confpath);
+void zlog_fini(void);
+
+void zlog_profile(void);
+
+zlog_category_t *zlog_get_category(const char *cname);
+
+int zlog_put_mdc(const char *key, const char *value);
+char *zlog_get_mdc(const char *key);
+void zlog_remove_mdc(const char *key);
+void zlog_clean_mdc(void);
+
+void zlog(zlog_category_t * category,
+ const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const char *format, ...) ZLOG_CHECK_PRINTF(8,9);
+void vzlog(zlog_category_t * category,
+ const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const char *format, va_list args);
+void hzlog(zlog_category_t * category,
+ const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const void *buf, size_t buflen);
+
+int dzlog_init(const char *confpath, const char *cname);
+int dzlog_set_category(const char *cname);
+
+void dzlog(const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const char *format, ...) ZLOG_CHECK_PRINTF(7,8);
+void vdzlog(const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const char *format, va_list args);
+void hdzlog(const char *file, size_t filelen,
+ const char *func, size_t funclen,
+ long line, int level,
+ const void *buf, size_t buflen);
+
+typedef struct zlog_msg_s {
+ char *buf;
+ size_t len;
+ char *path;
+} zlog_msg_t;
+
+typedef int (*zlog_record_fn)(zlog_msg_t *msg);
+int zlog_set_record(const char *rname, zlog_record_fn record);
+
+/******* useful macros, can be redefined at user's h file **********/
+
+typedef enum {
+ ZLOG_LEVEL_DEBUG = 20,
+ ZLOG_LEVEL_INFO = 40,
+ ZLOG_LEVEL_NOTICE = 60,
+ ZLOG_LEVEL_WARN = 80,
+ ZLOG_LEVEL_ERROR = 100,
+ ZLOG_LEVEL_FATAL = 120
+} zlog_level;
+
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L
+# if defined __GNUC__ && __GNUC__ >= 2
+# define __func__ __FUNCTION__
+# else
+# define __func__ "<unknown>"
+# endif
+#endif
+
+#if defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L
+/* zlog macros */
+#define zlog_fatal(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, __VA_ARGS__)
+#define zlog_error(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, __VA_ARGS__)
+#define zlog_warn(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, __VA_ARGS__)
+#define zlog_notice(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, __VA_ARGS__)
+#define zlog_info(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, __VA_ARGS__)
+#define zlog_debug(cat, ...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, __VA_ARGS__)
+/* dzlog macros */
+#define dzlog_fatal(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, __VA_ARGS__)
+#define dzlog_error(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, __VA_ARGS__)
+#define dzlog_warn(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, __VA_ARGS__)
+#define dzlog_notice(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, __VA_ARGS__)
+#define dzlog_info(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, __VA_ARGS__)
+#define dzlog_debug(...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, __VA_ARGS__)
+#elif defined __GNUC__
+/* zlog macros */
+#define zlog_fatal(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, format, ##args)
+#define zlog_error(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, format, ##args)
+#define zlog_warn(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, format, ##args)
+#define zlog_notice(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, format, ##args)
+#define zlog_info(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, format, ##args)
+#define zlog_debug(cat, format, args...) \
+ zlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, format, ##args)
+/* dzlog macros */
+#define dzlog_fatal(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, format, ##args)
+#define dzlog_error(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, format, ##args)
+#define dzlog_warn(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, format, ##args)
+#define dzlog_notice(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, format, ##args)
+#define dzlog_info(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, format, ##args)
+#define dzlog_debug(format, args...) \
+ dzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, format, ##args)
+#endif
+
+/* vzlog macros */
+#define vzlog_fatal(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, format, args)
+#define vzlog_error(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, format, args)
+#define vzlog_warn(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, format, args)
+#define vzlog_notice(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, format, args)
+#define vzlog_info(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, format, args)
+#define vzlog_debug(cat, format, args) \
+ vzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, format, args)
+
+/* hzlog macros */
+#define hzlog_fatal(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, buf, buf_len)
+#define hzlog_error(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, buf, buf_len)
+#define hzlog_warn(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, buf, buf_len)
+#define hzlog_notice(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, buf, buf_len)
+#define hzlog_info(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, buf, buf_len)
+#define hzlog_debug(cat, buf, buf_len) \
+ hzlog(cat, __FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, buf, buf_len)
+
+
+/* vdzlog macros */
+#define vdzlog_fatal(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, format, args)
+#define vdzlog_error(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, format, args)
+#define vdzlog_warn(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, format, args)
+#define vdzlog_notice(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, format, args)
+#define vdzlog_info(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, format, args)
+#define vdzlog_debug(format, args) \
+ vdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, format, args)
+
+/* hdzlog macros */
+#define hdzlog_fatal(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_FATAL, buf, buf_len)
+#define hdzlog_error(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_ERROR, buf, buf_len)
+#define hdzlog_warn(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_WARN, buf, buf_len)
+#define hdzlog_notice(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_NOTICE, buf, buf_len)
+#define hdzlog_info(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_INFO, buf, buf_len)
+#define hdzlog_debug(buf, buf_len) \
+ hdzlog(__FILE__, sizeof(__FILE__)-1, __func__, sizeof(__func__)-1, __LINE__, \
+ ZLOG_LEVEL_DEBUG, buf, buf_len)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/agent/src/c/lib/libhiredis.a b/agent/src/c/lib/libhiredis.a
new file mode 100644
index 0000000..685a959
Binary files /dev/null and b/agent/src/c/lib/libhiredis.a differ
diff --git a/agent/src/c/lib/libzlog.a b/agent/src/c/lib/libzlog.a
new file mode 100644
index 0000000..e1f0c43
Binary files /dev/null and b/agent/src/c/lib/libzlog.a differ
diff --git a/agent/src/c/release/agent_log.conf b/agent/src/c/release/agent_log.conf
new file mode 100644
index 0000000..5496202
--- /dev/null
+++ b/agent/src/c/release/agent_log.conf
@@ -0,0 +1,6 @@
+[global]
+file perms = 644
+[formats]
+simple = "%d.%ms %m%n"
+[rules]
+fsof_agent.INFO "../../../../../dubbo-php-framework/agent/log/agent/agent.%d(%F).log", 100M; simple
diff --git a/agent/src/c/release/protect_agent_server.sh b/agent/src/c/release/protect_agent_server.sh
new file mode 100644
index 0000000..df69a9b
--- /dev/null
+++ b/agent/src/c/release/protect_agent_server.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+cd $(dirname $0)
+agent_server=$(ps aux | grep 'release/agent_server' | grep -v grep| wc -l)
+if [ "$agent_server" -eq "0" ]; then
+ /bin/bash start_agent_server.sh
+fi
diff --git a/agent/src/c/release/start_agent_server.sh b/agent/src/c/release/start_agent_server.sh
new file mode 100644
index 0000000..966fcdf
--- /dev/null
+++ b/agent/src/c/release/start_agent_server.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+log_dir="../../../../../dubbo-php-framework/agent/log/agent"
+
+if [ ! -d $log_dir ]; then
+ mkdir -p $log_dir
+fi
+
+process=$(ps aux | grep -v "grep" | grep "release/agent_server" | wc -l)
+if [ "$process" -ne "0" ]; then
+ ps aux |grep -v "grep" |grep "release/agent_server"|awk '{print $2}'|xargs kill -9
+fi
+
+cd ../../../../../dubbo-php-framework/agent/src/c/release
+cp agent agent_server
+chmod u+x agent_server
+cur_dir=$(pwd)
+$cur_dir/agent_server 600 ../../../../../dubbo-php-framework/config/global/conf/fsof.ini
diff --git a/agent/src/c/service/fsof_log.c b/agent/src/c/service/fsof_log.c
new file mode 100644
index 0000000..e79b7a3
--- /dev/null
+++ b/agent/src/c/service/fsof_log.c
@@ -0,0 +1,79 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fsof_log.h"
+#include <zlog.h>
+#include <time.h>
+#include <string.h>
+#include "../common/fsof_util.h"
+
+
+#define LOG_INFO_MAX_LEN (8 * 1024)
+#define LOG_DEBUG_INFO_NAME "fsof_agent_log"
+
+static zlog_category_t *g_zc;
+static char g_zlog_conf[MAX_CONFIG_PATH_LEN] = {0};
+
+
+void fsof_log_info(enum log_level level,const char *format,...) {
+ char log_info[LOG_INFO_MAX_LEN] = {0};
+ va_list v_log;
+
+ va_start(v_log,format);
+ vsnprintf(log_info,LOG_INFO_MAX_LEN,format,v_log);
+ va_end(v_log);
+
+ if (g_zc != NULL) {
+ switch (level) {
+ case INFO:
+ zlog_info(g_zc,log_info);
+ break;
+ case ERROR:
+ zlog_error(g_zc,log_info);
+ break;
+ case DEBUG:
+ zlog_debug(g_zc,log_info);
+ }
+ }
+}
+
+void fsof_log_close() {
+ if (g_zc != NULL) {
+ zlog_fini();
+ g_zc = NULL;
+ }
+}
+
+int fsof_log_init() {
+ int err = 0;
+
+ get_current_path(g_zlog_conf,"agent_log.conf",1);
+ err = zlog_init(g_zlog_conf);;
+ if (err > 0) {
+ return err;
+ }
+
+ g_zc = zlog_get_category("fsof_agent");
+ if (g_zc == NULL) {
+ zlog_fini();
+ err = 1;
+ return err;
+ }
+
+ fsof_log_info(INFO,"log init succeed--------------!!!\n");
+
+ return 0; //
+}
diff --git a/agent/src/c/service/fsof_log.h b/agent/src/c/service/fsof_log.h
new file mode 100644
index 0000000..50ea9c8
--- /dev/null
+++ b/agent/src/c/service/fsof_log.h
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_LOG_H
+#define FSOF_LOG_H
+
+enum log_level{
+ DEBUG = 1,
+ INFO,
+ ERROR
+};
+int fsof_log_init();
+void fsof_log_info(enum log_level level,const char *format,...);
+void fsof_log_close();
+#endif
diff --git a/agent/src/c/service/fsof_mq.c b/agent/src/c/service/fsof_mq.c
new file mode 100644
index 0000000..211dc87
--- /dev/null
+++ b/agent/src/c/service/fsof_mq.c
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fsof_mq.h"
+#include <string.h>
+#include <assert.h>
+
+#define DEFAULT_QUEUE_SIZE (1024)
+
+struct fsof_queue {
+ int cap;
+ int head;
+ int tail;
+ int lock;
+ struct fsof_message *queue;
+};
+
+struct fsof_queue* fsof_mq_create() {
+ struct fsof_queue *q = malloc(sizeof(struct fsof_queue));
+ if (q == NULL) {
+ //log info error malloc failed exit
+ exit(-1);
+ }
+
+ q->cap = DEFAULT_QUEUE_SIZE;
+ q->head = 0;
+ q->tail = 0;
+ q->lock = 0;
+ q->queue = malloc(sizeof(struct fsof_message) * q->cap);
+ if (q->queue == NULL) {
+ //log info error malloc failed exit
+ free(q);
+ exit(-1);
+ }
+
+ return q;
+}
+
+//expand queue
+static void fsof_expand_queue(struct fsof_queue *q) {
+ struct fsof_message *new_queue = malloc(sizeof(*new_queue) * q->cap * 2);
+
+ int i;
+ for (i = 0; i < q->cap; i++) {
+ new_queue[i] = q->queue[i];
+ }
+
+ q->head = 0;
+ q->tail = q->cap;
+ q->cap *= 2;
+ free(q->queue);
+ q->queue = new_queue;
+}
+
+void fsof_mq_push(struct fsof_queue *q,struct fsof_message *message) {
+ assert(message);
+ LOCK(q);
+
+ q->queue[q->tail++] = *message;
+ if (q->tail >= q->cap) {
+ q->tail = 0;
+ }
+
+ if (q->head == q->tail) {
+ fsof_expand_queue(q);
+ }
+
+ UNLOCK(q);
+}
+
+int fsof_mq_pop(struct fsof_queue *q,struct fsof_message *message) {
+ int ret = 0;
+ LOCK(q);
+
+ if (q->head != q->tail) {
+ *message = q->queue[q->head++];
+ ret = 1;
+
+ if (q->head >= q->cap) {
+ q->head = 0;
+ }
+ }
+
+ UNLOCK(q);
+ return ret;
+}
+
+int fsof_mq_len(struct fsof_queue *q) {
+ int len = 0;
+ LOCK(q);
+
+ if (q->head <= q->tail) {
+ len = q->tail - q->head;
+ }else {
+ len = q->tail + q->cap - q->head;
+ }
+
+ UNLOCK(q);
+ return len;
+}
+
+int fsof_mq_clear(struct fsof_queue *q) {
+ LOCK(q);
+ UNLOCK(q);
+ int i,j = 0;
+ for (i = 0; i < q->cap; i++) {
+ if (q->queue[i].key != NULL) {
+ free(q->queue[i].key);
+ }
+
+ for (j = 0; j < q->queue[i].count; j++) {
+ if (q->queue[i].value[j] != NULL) {
+ free(q->queue[i].value[j]);
+ }
+ }
+ }
+ memset(q->queue,0,sizeof(struct fsof_message) * q->cap);
+ return 0;
+}
+
+void fsof_mq_destroy(struct fsof_queue *q) {
+
+ if(NULL != q) {
+ if(NULL != q->queue) {
+ int i,j = 0;
+ for (i = 0; i < q->cap; i++) {
+ if (q->queue[i].key != NULL) {
+ free(q->queue[i].key);
+ }
+
+ for (j = 0; j < q->queue[i].count; j++) {
+ if (q->queue[i].value[j] != NULL) {
+ free(q->queue[i].value[j]);
+ }
+ }
+ }
+ free(q->queue);
+ }
+
+ free(q);
+ }
+}
diff --git a/agent/src/c/service/fsof_mq.h b/agent/src/c/service/fsof_mq.h
new file mode 100644
index 0000000..c31f4d1
--- /dev/null
+++ b/agent/src/c/service/fsof_mq.h
@@ -0,0 +1,46 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_MESSAGE_QUEUE_H
+#define FSOF_MESSAGE_QUEUE_H
+#include <stdlib.h>
+#include <stdint.h>
+
+#define LOCK(q) while (__sync_lock_test_and_set(&(q)->lock,1)) {}
+#define UNLOCK(q) __sync_lock_release(&(q)->lock);
+
+struct fsof_message {
+ char *key;
+ char **value;
+ int count; //total providers count
+};
+
+struct fsof_queue;
+
+//push data into message queue
+void fsof_mq_push(struct fsof_queue *q,struct fsof_message *message);
+//pop data from message queue
+int fsof_mq_pop(struct fsof_queue *q,struct fsof_message *message);
+//message queue len
+int fsof_mq_len(struct fsof_queue *q);
+//message queue struct create
+struct fsof_queue* fsof_mq_create();
+//clear message
+int fsof_mq_clear(struct fsof_queue *q);
+//message queue destroy
+void fsof_mq_destroy(struct fsof_queue *q);
+
+#endif
diff --git a/agent/src/c/service/fsof_redis.c b/agent/src/c/service/fsof_redis.c
new file mode 100644
index 0000000..919d160
--- /dev/null
+++ b/agent/src/c/service/fsof_redis.c
@@ -0,0 +1,242 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fsof_redis.h"
+#include "fsof_mq.h"
+#include "mt_array.h"
+#include "fsof_log.h"
+#include <hiredis/hiredis.h>
+#include <assert.h>
+#include <string.h>
+#include <unistd.h>
+#include <netinet/in.h>
+
+#define UNIX_PATH_LEN (100)
+
+struct array_object {
+ char *data_ptr;
+};
+
+extern char g_str_localip[INET_ADDRSTRLEN];
+static redisContext* g_redis_conn = NULL;
+static char unix_path[UNIX_PATH_LEN] = {0};
+
+/*
+* compare zookeeper provider list with redis provider list,then delete not exists list and add new provider list
+*/
+int compare_and_set_val(const struct fsof_message *message) {
+ struct redisReply *object = NULL;
+ struct redisReply *reply = fsof_redis_get(message->key);
+ if (reply != NULL && reply->elements > 0 && message->count > 0) {
+ //mt_array_t *same_list = mt_array_create(message->count,sizeof(struct array_object));
+ mt_array_t *new_list = mt_array_create(message->count,sizeof(struct array_object));
+ mt_array_t *delete_list = mt_array_create(message->count,sizeof(struct array_object));
+ //assert(same_list != NULL);
+ assert(new_list != NULL);
+ assert(delete_list != NULL);
+
+ struct array_object *tmp_object = NULL;
+ int i = 0;
+ int j = 0;
+ for (; i < reply->elements; i++) {
+ object = reply->element[i];
+ if (object != NULL) {
+ for (; j < message->count; j++) {
+ if (strcmp(object->str,message->value[j]) == 0) {//equal
+ break;
+ }
+ }
+
+ if (j == message->count) {//not found need delete
+ tmp_object = (struct array_object*)mt_array_push(delete_list);
+ assert(tmp_object != NULL);
+ if (tmp_object != NULL) {
+ tmp_object->data_ptr = object->str;
+ }
+ }
+
+ if (j < message->count) { //found same value
+ //nothing to do
+ }
+
+ j = 0;
+ }
+ }
+
+ i = j = 0;
+ for (; i < message->count; i++) {
+ for (; j < reply->elements; j++) {
+ object = reply->element[j];
+ if (strcmp(object->str,message->value[i]) == 0) {//equal
+ break;
+ }
+ }
+
+ if (j == reply->elements) { // not found new value
+ tmp_object = (struct array_object*)mt_array_push(new_list);
+ assert(tmp_object != NULL);
+ if (tmp_object != NULL) {
+ tmp_object->data_ptr = message->value[i];
+ }
+ }
+
+ j = 0;
+ }
+
+ if (delete_list->nelts > 0) {
+ struct array_object *ptr = NULL;
+ struct redisReply *tmp_reply = NULL;
+ i = 0;
+ for (; i < delete_list->nelts; i++) {
+ ptr = (struct array_object*)(delete_list->elts + i * delete_list->size);
+ fsof_log_info(INFO,"BEGIN Delete val is %s succeed,key is %s\n",ptr->data_ptr,message->key);
+__DELETE_AGAIN:
+ tmp_reply = redisCommand(g_redis_conn,"lrem %s 0 %s",message->key,ptr->data_ptr); //delete value
+ if (tmp_reply == NULL) {
+ fsof_redis_close();
+ fsof_redis_init(unix_path);
+ goto __DELETE_AGAIN;
+ }
+
+ fsof_log_info(INFO,"Delete val is %s succeed,key is %s\n",ptr->data_ptr,message->key);
+ freeReplyObject(tmp_reply);
+ }
+ }
+
+ if (new_list->nelts > 0) {
+ i = 0;
+ struct array_object *ptr = NULL;
+ struct redisReply *tmp_reply = NULL;
+ for (; i < new_list->nelts; i++) {
+ ptr = (struct array_object*)(new_list->elts + i * new_list->size);
+__NEW_AGAIN:
+ tmp_reply = redisCommand(g_redis_conn,"lpush %s %s",message->key,ptr->data_ptr);
+ if (tmp_reply == NULL) {
+ fsof_redis_close();
+ fsof_redis_init(unix_path);
+ goto __NEW_AGAIN;
+ }
+ fsof_log_info(INFO,"Insert val is %s succeed,key is %s\n",ptr->data_ptr,message->key);
+ freeReplyObject(tmp_reply);
+ }
+ }
+
+ mt_array_destroy(delete_list);
+ mt_array_destroy(new_list);
+ //mt_array_destroy(same_list);
+ freeReplyObject(reply);
+ } else {
+ return fsof_redis_setval(message); //directly set new value list
+ }
+ return 0;
+}
+
+//set list into redis
+int fsof_redis_setval(const struct fsof_message *message) {
+ assert(message);
+ int i = 0;
+ struct redisReply *reply = NULL;
+
+
+ if (message->count > 0) {
+ for (i = 0; i < message->count; i++) {
+ if (message->value[i] != NULL) {
+__SET_AGAIN:
+ reply = redisCommand(g_redis_conn,"lpush %s %s",message->key,message->value[i]);
+ if (reply == NULL) {
+ fsof_redis_close();
+ fsof_redis_init(unix_path);
+ goto __SET_AGAIN;
+ }
+
+ freeReplyObject(reply);
+ }
+ }
+ }
+
+ return 0;
+}
+
+struct redisReply* fsof_redis_get(const char *path) {
+ struct redisReply *reply = NULL;
+
+__GET_AGAIN:
+ reply = redisCommand(g_redis_conn,"lrange %s 0 -1",path);
+ if (reply == NULL) {
+ //occured an bad error ,need rejoin redis server
+ fsof_redis_close();
+ fsof_redis_init(unix_path);
+ goto __GET_AGAIN;
+ } else {
+ if (reply->type != REDIS_REPLY_ARRAY) { //value type must be array
+ freeReplyObject(reply);
+ return NULL;
+ }
+ }
+
+ return reply;
+}
+
+int fsof_redis_delete(const char *path) {
+ int err = 0;
+ struct redisReply *reply = NULL;
+
+__DELETE_AGAIN:
+ reply = redisCommand(g_redis_conn,"del %s",path);
+ if (reply == NULL) {
+ //occured an bad error ,need rejoin redis server
+ fsof_redis_close();
+ fsof_redis_init(unix_path);
+ goto __DELETE_AGAIN;
+ } else {
+ freeReplyObject(reply);
+ }
+
+ return err;
+}
+
+void fsof_redis_close() {
+ redisFree(g_redis_conn);
+}
+
+int fsof_redis_init(const char *path) {
+ int ret = 0;
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+ struct timeval time;
+
+ time.tv_sec = 0;
+ time.tv_usec = 60;
+
+ do {
+ gettimeofday(&start,NULL);
+ g_redis_conn = redisConnectUnixWithTimeout(path,time);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_sec - start.tv_sec); //calculate execute time
+ ret = g_redis_conn->err;
+
+ if (ret > 0) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|connect redis timeout!|connect_redis|%d",g_str_localip,ret,exec_time);
+ sleep(300);
+ }
+ }while(ret);
+
+ strcpy(unix_path,path);
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|connect redis succeed!|connect_redis|%d",g_str_localip,ret,exec_time);
+ return 0;
+}
+
diff --git a/agent/src/c/service/fsof_redis.h b/agent/src/c/service/fsof_redis.h
new file mode 100644
index 0000000..0fdaf5e
--- /dev/null
+++ b/agent/src/c/service/fsof_redis.h
@@ -0,0 +1,32 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_REDIS_H
+#define FSOF_REDIS_H
+
+struct fsof_message;
+
+int fsof_redis_init(const char *path);
+int fsof_redis_delete(const char *path);
+//compare redis exists provider list with zookeeper exists provider list add by iceli
+int compare_and_set_val(const struct fsof_message *message);
+//directly set zookeeper exists provider list into redis when redis can't find exists provider list add by iceli
+int fsof_redis_setval(const struct fsof_message *message);
+struct redisReply* fsof_redis_get(const char *path);
+void fsof_redis_close();
+
+
+#endif
diff --git a/agent/src/c/service/fsof_zookeeper.c b/agent/src/c/service/fsof_zookeeper.c
new file mode 100644
index 0000000..c5a2bd6
--- /dev/null
+++ b/agent/src/c/service/fsof_zookeeper.c
@@ -0,0 +1,171 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "fsof_zookeeper.h"
+#include "../common/strmap.h"
+#include "../common/fsof_global.h"
+#include <assert.h>
+#include <errno.h>
+
+#define TIME_OUT 30000
+#define ZOOKEEPER_VERSION -1
+
+extern int errno;
+
+struct provider_callback {
+ watcher_fn watcher_cb;
+};
+
+
+static zhandle_t* g_zkhandle = NULL;
+static watcher_fn g_watcher_fn;
+struct StrMap *g_provider_map;
+
+
+void fsof_zk_create(const char *path,bool ephemeral) {
+ //nothing to do
+}
+
+int fsof_zk_delete(const char *path) {
+ int ret;
+ ret = zoo_delete(g_zkhandle,path,-1);
+
+ return ret;
+}
+
+struct String_vector * fsof_zk_get_children(const char *path) {
+ int ret = -1;
+ struct map_object *object_val = NULL;
+ struct provider_callback *cb = NULL;
+ struct String_vector *str_list = NULL;
+
+ if (g_zkhandle == NULL) {
+ return NULL;
+ }
+
+ str_list = malloc(sizeof(*str_list));
+ if (str_list == NULL) {
+ //log error
+ exit(-1);
+ }
+
+ memset(str_list, 0, sizeof(*str_list));
+ sm_get(g_provider_map,path,&object_val);
+
+ if (object_val != NULL && object_val->status == 1) { //succeed
+ if (object_val->type == MAP_OBJECT_CB_TYPE) {
+ cb = object_val->ptr;
+ ret = zoo_wget_children(g_zkhandle,path,cb->watcher_cb,NULL,str_list);
+ }
+ } else { //0 error,can't get provider callback
+ ret = zoo_wget_children(g_zkhandle,path,NULL,NULL,str_list);
+ }
+
+ if (ret < 0) {
+ //log error
+ free(str_list);
+ return NULL;
+ }
+
+ return str_list;
+}
+
+void fsof_zk_add_listener(const char *path,watcher_fn watcher) {
+ struct provider_callback *cb = NULL;
+ struct map_object *object_val = NULL;
+
+ if (sm_exists(g_provider_map,path)) { //exist
+ sm_get(g_provider_map,path,&object_val);
+ cb = (struct provider_callback*)object_val->ptr;
+ if (cb != NULL) {
+ if (cb->watcher_cb != watcher) { //not same
+ cb->watcher_cb = watcher;
+ }
+ object_val->status = 1;
+ }
+
+ } else { //create new one
+ cb = malloc(sizeof(*cb));
+ assert(cb);
+ object_val = malloc(sizeof(*object_val));
+ assert(object_val);
+ cb->watcher_cb = watcher;
+ object_val->type = MAP_OBJECT_CB_TYPE;
+ object_val->ptr = cb;
+ object_val->status = 1;
+ sm_put(g_provider_map,path,object_val);
+ }
+}
+
+void fsof_zk_remove_listener(const char *path,watcher_fn watcher) {
+ struct provider_callback *cb = NULL;
+ struct map_object *object_val = NULL;
+
+ if (sm_exists(g_provider_map,path)) { //exist
+ sm_get(g_provider_map,path,&object_val);
+ if (object_val != NULL) {
+ cb = (struct provider_callback*)object_val->ptr;
+ if (cb != NULL && cb->watcher_cb == watcher) {
+ object_val->status = 0;
+ }
+ }
+ }
+}
+
+void fsof_zk_add_state_listener(watcher_fn watcher) {
+ g_watcher_fn = watcher;
+}
+
+void fsof_zk_remove_state_listener(watcher_fn watcher) {
+ g_watcher_fn = NULL;
+}
+
+int fsof_zk_init(const char *host) {
+ int err = 0;
+
+ if (g_watcher_fn == NULL) {
+ //log error
+ exit(-1);
+ }
+
+ g_zkhandle = zookeeper_init(host,g_watcher_fn,TIME_OUT, 0, "hello zookeeper.", 0);
+ if (g_zkhandle == NULL) {
+ //log error
+ err = errno; //failed
+ return err;
+ }
+
+ g_provider_map = sm_new(DEFAULT_MAP_COUNT);
+ if (g_provider_map == NULL) {
+ //log error malloc failed
+ exit(-1);
+ }
+
+ return err;
+}
+
+void fsof_zk_close() {
+ if (g_zkhandle != NULL) {
+ zookeeper_close(g_zkhandle);
+
+ if (g_provider_map != NULL) {
+ sm_delete(g_provider_map);
+ g_provider_map = NULL;
+ }
+
+ g_zkhandle = NULL;
+ }
+}
diff --git a/agent/src/c/service/fsof_zookeeper.h b/agent/src/c/service/fsof_zookeeper.h
new file mode 100644
index 0000000..c0e52ad
--- /dev/null
+++ b/agent/src/c/service/fsof_zookeeper.h
@@ -0,0 +1,45 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef FSOF_ZOOKEEPER_H
+#define FSOF_ZOOKEEPER_H
+#include "fsof_mq.h"
+#include <stdbool.h>
+#include <zookeeper/zookeeper.h>
+
+
+typedef void (*watcher_fn)(zhandle_t* zh, int type, int state,
+ const char* path, void* watcherCtx);
+void fsof_zk_create(const char *path,bool ephemeral);
+
+int fsof_zk_delete(const char *path);
+
+struct String_vector * fsof_zk_get_children(const char *path);
+
+void fsof_zk_add_listener(const char *path,watcher_fn watcher);
+
+void fsof_zk_remove_listener(const char *path,watcher_fn watcher);
+
+void fsof_zk_add_state_listener(watcher_fn watcher);
+
+void fsof_zk_remove_state_listener(watcher_fn watcher);
+
+int fsof_zk_init(const char *host);
+
+void fsof_zk_close();
+
+
+#endif
diff --git a/agent/src/c/service/mt_array.c b/agent/src/c/service/mt_array.c
new file mode 100644
index 0000000..5e525c0
--- /dev/null
+++ b/agent/src/c/service/mt_array.c
@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "mt_array.h"
+#include "string.h"
+
+static int mt_array_init(mt_array_t *m,int nalloc,int size);
+
+mt_array_t *mt_array_create(int nalloc,int size) {
+ mt_array_t *m = malloc(sizeof(mt_array_t));
+ if (m == NULL) { //failed
+ return NULL;
+ }
+ int ret = mt_array_init(m,nalloc,size);
+ if (ret == -1) {
+ return NULL;
+ }
+ return m;
+}
+
+void *mt_array_push(mt_array_t *m) {
+ size_t size;
+ void *new,*elt;
+ if (m->nalloc == m->nelts) {//full
+ size = m->size * m->nalloc;
+ new = malloc(size * 2);
+ if (new == NULL) {
+ return NULL;
+ }
+ memcpy(new,m->elts,size);
+ free(m->elts);
+ m->elts = new;
+ m->nalloc *= 2;
+ }
+
+ elt = (char*)m->elts + m->nelts * m->size;
+ m->nelts++;
+ return elt;
+
+}
+
+void mt_array_destroy(mt_array_t *m) {
+ if (m->elts != NULL) {
+ free(m->elts);
+ free(m);
+ }
+
+}
+
+static int mt_array_init(mt_array_t *m,int nalloc,int size) {
+ m->nelts = 0;
+ m->size = size;
+ m->nalloc = nalloc;
+ m->elts = malloc(nalloc * size);
+ if (m->elts == NULL) {
+ return -1;
+ }
+ return 0;
+}
diff --git a/agent/src/c/service/mt_array.h b/agent/src/c/service/mt_array.h
new file mode 100644
index 0000000..5af3bcd
--- /dev/null
+++ b/agent/src/c/service/mt_array.h
@@ -0,0 +1,31 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef MT_ARRAY_H
+#define MT_ARRAY_H
+#include <stdlib.h>
+
+typedef struct{
+ void *elts;
+ int nelts;
+ size_t size;
+ int nalloc;
+}mt_array_t;
+
+mt_array_t *mt_array_create(int nalloc,int size);
+void* mt_array_push(mt_array_t *m);
+void mt_array_destroy(mt_array_t *m);
+#endif
diff --git a/agent/src/c/src/fsof_main.c b/agent/src/c/src/fsof_main.c
new file mode 100644
index 0000000..8c71142
--- /dev/null
+++ b/agent/src/c/src/fsof_main.c
@@ -0,0 +1,524 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <assert.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ifaddrs.h>
+#include <hiredis/hiredis.h>
+#include "../service/fsof_mq.h"
+#include "../service/mt_array.h"
+#include "../service/fsof_zookeeper.h"
+#include "../common/fsof_global.h"
+#include "../common/fsof_util.h"
+#include "../service/fsof_redis.h"
+#include "../service/fsof_log.h"
+#include "../common/fsof_url.h"
+#include <sys/fcntl.h>
+#include <signal.h>
+
+//redis thread handle
+pthread_t g_redis_pid;
+//message queue ,store zookeeper node info
+struct fsof_queue *g_message_queue;
+//current zookeeper config path
+static char g_zookeeper_conf[MAX_CONFIG_PATH_LEN] = {0};
+//zookeeper url list
+static char g_zk_list[512] = {0};
+char g_str_localip[INET_ADDRSTRLEN] = {0};
+
+void init_zookeeper_env();
+static void set_all_providers_watcher(struct String_vector *list); //all providers add watcher
+static void enum_provider_list(struct fsof_message *message);
+static void get_zk_root_children();
+
+//deal with signo such as SIGALARM
+void sigroutine(int signo) {
+ //need to get zookeeper node info from zk server manually
+ if (signo == SIGALRM) {
+ fsof_log_info(ERROR,"Now recvied alarm signal\n");
+ get_zk_root_children();
+ }
+}
+
+//create thread
+static void create_thread(pthread_t *thread,void *(*start_routine) (void *),void *arg) {
+ if (pthread_create(thread,NULL,start_routine,arg)) {
+ //thread create failed
+ fsof_log_info(INFO,"create thread function called failed!\n");
+ exit(-1);
+ }
+}
+
+//redis worker thread ,main job is getting message from g_message_queue ,then write into redis
+static void * thread_redis_work(void *p) {
+ int ret = 0;
+ int i = 0;
+ struct fsof_message message = {0};
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+
+ while(true) {
+ //pop message from g_message_queue
+ ret = fsof_mq_pop(g_message_queue,&message);
+ if (ret) {
+ //deal with message
+ if (message.count > 0) {
+ gettimeofday(&start,NULL);
+ ret = compare_and_set_val(&message);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+ if (ret == 0) {
+ //insert succeed
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|set redis data succeed!|set_redis|%d",g_str_localip,0,exec_time);
+ }
+ } else {
+ gettimeofday(&start,NULL);
+ fsof_redis_delete(message.key);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+ fsof_log_info(DEBUG,"%s|FSOF_AGENT|%d|delete redis data key succeed!|delete_redis|%d",g_str_localip,0,exec_time);
+ }
+ //enum every provider value and key
+ enum_provider_list(&message);
+ //free message content, message itself free by g_message_queue
+ for (i = 0; i < message.count; i++) {
+ if (message.value[i] != NULL) {
+ free(message.value[i]);
+ }
+ }
+
+ free(message.key);
+ if (message.value != NULL) {
+ free(message.value);
+ }
+ } else {
+ //0.1 seconds
+ usleep(100000);
+ }
+
+ }
+
+ return NULL;
+}
+
+static void parse_provider_key(struct String_vector *list,const char *path) {
+ int i ;
+ struct fsof_message message = {0};
+ char url_encode_data[URL_ENCODE_BUF_LEN] = {0};
+ int nlen = 0;
+ int service_len = 0;
+ int provider_index = 0;
+ char *provider_str = NULL;
+
+ //get service key
+ provider_str = strstr(path,PROVIDER_NAME);
+ provider_index = provider_str - path;
+ service_len = provider_index - sizeof(FSOF_ROOT_NAME);
+ if (service_len <= 0) {
+ return;
+ }
+
+ message.key = malloc(service_len + 1);
+ assert(message.key);
+ memcpy(message.key,path + sizeof(FSOF_ROOT_NAME),service_len);
+ message.key[service_len] = '\0';
+
+ if (list->count > 0) {
+ message.value = malloc(sizeof(char*) * list->count);
+ assert(message.value);
+ memset(message.value,0,sizeof(char*) * list->count);
+
+ int val_index = 0;
+ for (i = 0; i < list->count; i++) {
+ memcpy(url_encode_data,list->data[i],strlen(list->data[i]));
+ nlen = fsof_url_decode(url_encode_data,strlen(url_encode_data));
+ fsof_log_info(INFO,"key: %s,value: %s,nlen is %d\n",message.key,url_encode_data,nlen);
+ message.value[val_index] = malloc(nlen + 1);
+ assert(message.value[val_index]);
+ strcpy(message.value[val_index],url_encode_data);
+ message.value[val_index][nlen] = '\0';
+ val_index++;
+ memset(url_encode_data,0,URL_ENCODE_BUF_LEN);
+ }
+
+ message.count = val_index;
+
+ }else {
+ fsof_log_info(INFO,"parse key child is zero,need to delete redis key %s\n",path);
+ //means delete provider key
+ message.count = 0;
+ }
+
+ fsof_mq_push(g_message_queue,&message);
+
+}
+
+//enum provider list
+static void enum_provider_list(struct fsof_message *message) {
+ int i ;
+
+ for (i = 0; i < message->count; i++) {
+ fsof_log_info(INFO,"Enum provider key value is %s,path is %s\n",message->value[i],message->key);
+ }
+}
+
+static void fsof_provider_node_watcher(zhandle_t* zh, int type, int state,
+ const char* path, void* watcherCtx) {
+ struct String_vector *str_list = NULL;
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+
+ if (type == ZOO_CHILD_EVENT) {
+
+ fsof_log_info(INFO,"%s children node has been changed ,need to get new children list\n",path);
+ if (strcmp(path,FSOF_ROOT_NAME) == 0) {
+ gettimeofday(&start,NULL);
+ str_list = fsof_zk_get_children(FSOF_ROOT_NAME);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (str_list != NULL) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ set_all_providers_watcher(str_list);
+ } else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ }
+ } else {
+ gettimeofday(&start,NULL);
+ str_list = fsof_zk_get_children(path);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (str_list != NULL) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ parse_provider_key(str_list,path);
+ } else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ }
+ }
+
+ if (str_list != NULL) {
+ deallocate_String_vector(str_list);
+ free(str_list);
+ }
+ }
+}
+
+//watcher service node ,when the service node children has been changed ,need to set provider watcher
+static void fsof_service_node_watcher(zhandle_t* zh, int type, int state,
+ const char* path, void* watcherCtx) {
+ struct String_vector *str_list = NULL;
+ struct String_vector *provider_list = NULL;
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+ char provider_key[512] = {0};
+ int i;
+
+ if (type == ZOO_CHILD_EVENT) {
+ fsof_log_info(INFO,"service node watcher entered %s\n",path);
+ gettimeofday(&start,NULL);
+ str_list = fsof_zk_get_children(path);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (str_list != NULL) {
+ sprintf(provider_key,"%s%s",path,PROVIDER_NAME);
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+
+ for (i = 0; i < str_list->count; i++) {
+ if (strcmp(str_list->data[i],PROVIDER_NODE_NAME) == 0) {
+ fsof_zk_add_listener(provider_key,fsof_provider_node_watcher);
+ gettimeofday(&start,NULL);
+ provider_list = fsof_zk_get_children(provider_key);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (provider_list != NULL) { //deal
+ //enum_provider_list(provider_list,provider_key);
+ parse_provider_key(provider_list,provider_key);
+ deallocate_String_vector(provider_list);
+ free(provider_list);
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ } else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ }
+ break;
+ }
+ }
+
+ deallocate_String_vector(str_list);
+ free(str_list);
+ } else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ }
+ }
+
+}
+
+static void set_all_providers_watcher(struct String_vector *list) {
+ struct String_vector *provider_list = NULL;
+ char provider_key[512] = {0};
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+ int i;
+
+ for (i = 0; i < list->count; i++) {
+ sprintf(provider_key,"%s/%s%s",FSOF_ROOT_NAME,list->data[i],PROVIDER_NAME);
+ fsof_zk_add_listener(provider_key,fsof_provider_node_watcher);
+ gettimeofday(&start,NULL);
+ provider_list = fsof_zk_get_children(provider_key);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (provider_list == NULL) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ memset(provider_key,0,sizeof(provider_key));
+ sprintf(provider_key,"%s/%s",FSOF_ROOT_NAME,list->data[i]);
+ fsof_zk_add_listener(provider_key,fsof_service_node_watcher);
+ gettimeofday(&start,NULL);
+ provider_list = fsof_zk_get_children(provider_key);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (provider_list == NULL) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ } else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ }
+ }else {
+ //enum_provider_list(provider_list,provider_key);
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ parse_provider_key(provider_list,provider_key);
+ }
+
+ memset(provider_key,0,sizeof(provider_key));
+ if (provider_list != NULL) {
+ deallocate_String_vector(provider_list);
+ free(provider_list);
+ }
+ }
+}
+static void get_zk_root_children() {
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+ struct String_vector *str_list = NULL;
+
+ fsof_zk_add_listener(FSOF_ROOT_NAME,fsof_provider_node_watcher);
+ gettimeofday(&start,NULL);
+ str_list = fsof_zk_get_children(FSOF_ROOT_NAME);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec); //calculate execute time
+
+ if (str_list != NULL) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data succeed!|get_zookeeper|%d",g_str_localip,0,exec_time);
+ set_all_providers_watcher(str_list);
+ }else {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|get zookeeper data failed!|get_zookeeper|%d",g_str_localip,-1,exec_time);
+ fsof_log_info(INFO,"can't get children of key %s\n",FSOF_ROOT_NAME);
+ }
+
+ if (str_list != NULL) {
+ deallocate_String_vector(str_list);
+ free(str_list);
+ }
+}
+
+//watcher fsof root node main event :ZOO_CONNECTED_STATE ZOO_EXPIRED_SESSION_STATE
+static void fsof_root_node_watcher(zhandle_t* zh, int type, int state,
+ const char* path, void* watcherCtx) {
+ if (type == ZOO_SESSION_EVENT) {
+ if(state == ZOO_CONNECTED_STATE) {
+ //begin get children from root node
+ get_zk_root_children();
+ } else if (state == ZOO_EXPIRED_SESSION_STATE || state == ZOO_CONNECTING_STATE || state == ZOO_AUTH_FAILED_STATE) {
+ //log info sesstion expired need rejoin
+ fsof_log_info(ERROR,"zookeeper session expired,need rejoin zookeeper\n");
+ fsof_zk_close();
+ init_zookeeper_env();
+ }
+ }
+}
+
+void parse_zk_config(char *src,char *dest) {
+ assert(src);
+ assert(dest);
+ char *ptr = NULL;
+ char *tail_ptr = src + strlen(src);
+
+ while ((ptr = strstr(src,"http://")) != 0) {
+ if ((ptr - src) == 0) {
+ src += strlen("http://");
+ }
+ else if ((ptr - src) > 0) {
+ memcpy(dest,src,ptr - src);
+ dest += ptr - src;
+ src += ptr - src + strlen("http://");
+ }
+ }
+
+ if (ptr == NULL && src != tail_ptr) {
+ memcpy(dest,src,tail_ptr - src);
+ }
+}
+
+void init_zookeeper_env() {
+ int ret = 0;
+ char *zk_str = NULL;
+ struct timeval start = {0};
+ struct timeval end = {0};
+ int exec_time = 0;
+
+ if (*g_zookeeper_conf != '\0') {
+ //parse zookeeper config
+ fsof_log_info(INFO,"Now begin start parse zookeeper config file %s!\n",g_zookeeper_conf);
+ zk_str = get_ini_key_string("fsof_setting","zk_url_list",g_zookeeper_conf);
+ parse_zk_config(zk_str,g_zk_list);
+ fsof_log_info(INFO,"Now end start parse zookeeper config file %s,zk list str is %s!\n",g_zookeeper_conf,g_zk_list);
+ }
+ //set root node watcher
+ fsof_zk_add_state_listener(fsof_root_node_watcher);
+
+ do{
+ gettimeofday(&start,NULL);
+ ret = fsof_zk_init(g_zk_list);
+ gettimeofday(&end,NULL);
+ exec_time = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_sec - start.tv_sec); //calculate execute time
+
+ if (ret > 0) {
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|connect zookeeper timeout!|connect_zookeeper|%d",g_str_localip,ret,exec_time);
+ sleep(300);
+ }
+ }while(ret);
+
+ fsof_log_info(INFO,"%s|FSOF_AGENT|%d|connect zookeeper succeed!|connect_zookeeper|%d",g_str_localip,ret,exec_time);
+}
+
+static void get_local_ip() {
+ struct ifaddrs * ifAddrStruct = NULL;
+ void * tmpAddrPtr = NULL;
+
+ getifaddrs(&ifAddrStruct);
+ while (ifAddrStruct != NULL) {
+
+ if (ifAddrStruct->ifa_addr->sa_family == AF_INET) {
+ tmpAddrPtr = &((struct sockaddr_in *)ifAddrStruct->ifa_addr)->sin_addr;
+ inet_ntop(AF_INET, tmpAddrPtr, g_str_localip, INET_ADDRSTRLEN);
+ }
+
+ ifAddrStruct = ifAddrStruct->ifa_next;
+ memset(g_str_localip,0,INET_ADDRSTRLEN);
+ }
+}
+
+void cleanup_env() {
+ fsof_zk_close();
+ fsof_redis_close();
+ fsof_log_close();
+}
+
+//set process to daemon
+void daemonize(void) {
+ int fd;
+
+ /* parent exits */
+ if (fork() != 0) {
+ exit(0);
+ }
+
+ /* create a new session */
+ setsid();
+
+ /* Every output goes to /dev/null. If Redis is daemonized but
+ * the 'logfile' is set to 'stdout' in the configuration file
+ * it will not log at all. */
+ if ((fd = open("/dev/null", O_RDWR, 0)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd , STDERR_FILENO);
+
+ close(fd);
+ }
+}
+
+int main(int argc,char **argv) {
+ int err = 0;
+ struct itimerval timer_setting,old_val;
+ int timer_val = 0;
+
+ if (argc < 2){
+ fprintf(stderr,"parameters contains the timer value of zookeeper unit:second and the URL config of zookeeper!\n");
+ fprintf(stderr,"agent time conf/fsof.ini\n");
+ exit(0);
+ }
+
+ //parameter contains the timer value of zookeeper unit:second
+ timer_val = atoi(argv[1]);
+
+ //dubbo-php-framework/config/global/conf/fsof.ini
+ strncpy(g_zookeeper_conf, argv[2], sizeof(g_zookeeper_conf) - 1 > strlen(argv[2]) ? strlen(argv[2]) : sizeof(g_zookeeper_conf) - 1);
+ if (access(g_zookeeper_conf, F_OK) == -1){
+ fprintf(stderr,"can not find conf/fsof.ini\n");
+ exit(0);
+ }
+
+ //set process to daemon ,if failed ,directly exit
+ daemonize();
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+ signal(SIGALRM, sigroutine);
+
+ if (timer_val > 0) {
+ timer_setting.it_value.tv_sec = timer_val;
+ timer_setting.it_value.tv_usec = 0;
+ timer_setting.it_interval.tv_sec = timer_val;
+ timer_setting.it_interval.tv_usec = 0;
+ setitimer(ITIMER_REAL, &timer_setting, &old_val);
+ }
+
+ err = fsof_log_init();
+ if (err > 0) {
+ fprintf(stderr,"agent log init failed,exit!\n");
+ exit(-1);
+ }
+
+ get_local_ip();
+ g_message_queue = fsof_mq_create();
+ create_thread(&g_redis_pid,thread_redis_work,NULL);
+ fsof_redis_init(REDIS_UNIX_SOCK);
+ init_zookeeper_env();
+
+ pthread_join(g_redis_pid,NULL);
+ fsof_mq_destroy(g_message_queue);
+ fsof_log_info(INFO,"fsof agent redis thread exit,need cleanup resource!\n");
+
+ //cleanup
+ cleanup_env();
+
+ return 0;
+}
+
diff --git a/api/FSOFApi.php b/api/FSOFApi.php
new file mode 100644
index 0000000..278674c
--- /dev/null
+++ b/api/FSOFApi.php
@@ -0,0 +1,68 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+use com\fenqile\fsof\consumer\FSOFConsumer;
+use com\fenqile\fsof\consumer\proxy\ProxyFactory;
+
+class FSOFApi
+{
+ private static $__FSOF_CONSUMER_APP_NAME__ = NULL;
+ private static $__FSOF_CONSUMER_APP_SRC_PATH__ = NULL;
+
+ /**
+ * 设置Consumer运行上下文环境,在使用fsof_consumer时,该函数要最先调用
+ * @param $appName consumer app的名字
+ * @param $appSrcPath consumer app src源码的绝对路径
+ */
+ public static function configure($appName, $appSrcPath)
+ {
+ self::$__FSOF_CONSUMER_APP_NAME__ = $appName;
+ self::$__FSOF_CONSUMER_APP_SRC_PATH__ = $appSrcPath;
+ }
+
+ /**
+ *
+ * 获取指定provider服务的proxy对象
+ * @param $service service配置文件中的key 例:com.alibaba.test.TestService 必选
+ * @param int $ioTimeout 连接超时时间和接收数据超时时间 单位 s;支持小数0.5 500ms
+ * @return \com\fenqile\fsof\consumer\proxy\Proxy|null
+ */
+ public static function newProxy($service ,$ioTimeout = 3)
+ {
+ $s = NULL;
+ if(empty(self::$__FSOF_CONSUMER_APP_NAME__) || empty(self::$__FSOF_CONSUMER_APP_SRC_PATH__))
+ {
+ \Logger::getLogger(__CLASS__)->error("appName or appSrcPath not set,please use FSOFApi::configure() for set.");
+ return null;
+ }
+ else
+ {
+ if(!is_numeric($ioTimeout) || $ioTimeout <= 0)
+ {
+ $ioTimeout = 3;
+ }
+ $consumerFile = dirname(__DIR__).DIRECTORY_SEPARATOR.'consumer'.DIRECTORY_SEPARATOR.'FSOFConsumer.php';
+ //加载FSOFConsumer
+ require_once($consumerFile);
+ $app_setting = array('app_name' => self::$__FSOF_CONSUMER_APP_NAME__, 'app_src' => self::$__FSOF_CONSUMER_APP_SRC_PATH__);
+ FSOFConsumer::init($app_setting);
+ $s = ProxyFactory::getInstance($service, $ioTimeout);
+ }
+ return $s;
+ }
+}
\ No newline at end of file
diff --git a/arch.png b/arch.png
new file mode 100644
index 0000000..05b60e1
Binary files /dev/null and b/arch.png differ
diff --git a/bin/app_admin.php b/bin/app_admin.php
new file mode 100644
index 0000000..13e6dc2
--- /dev/null
+++ b/bin/app_admin.php
@@ -0,0 +1,584 @@
+#!/usr/bin/php
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+require_once('log4php/Logger.php');
+
+use com\fenqile\fsof\common\config\FSOFConfigManager;
+
+define('FSOF_ROOT_PATH', __DIR__);
+define('FSOF_PROVIDER_PID_PATH', '/var/fsof/provider/');
+
+if(!defined("SIG_DFL")) define("SIG_DFL", 0);
+
+//加载commom
+$fsofCommonPath = dirname(FSOF_ROOT_PATH).DIRECTORY_SEPARATOR.'common';
+require_once($fsofCommonPath.DIRECTORY_SEPARATOR.'BootStrap.php');
+
+if(!defined("MASTER_PID_FILE_FORMAT")) define("MASTER_PID_FILE_FORMAT",'.master.pid');
+if(!defined("MANAGER_PID_FILE_FORMAT")) define("MANAGER_PID_FILE_FORMAT",'.manager.pid');
+
+if(!defined("FILTER_PROCESS_STATUS")) define("FILTER_PROCESS_STATUS",1);
+if(!defined("PS_CMD_WORK_PROCESS")) define("PS_CMD_WORK_PROCESS",2);
+
+if(!defined("PROVIDER_STATE_SUCCESS")) define("PROVIDER_STATE_SUCCESS",0);
+if(!defined("PROVIDER_STATE_FAILED")) define("PROVIDER_STATE_FAILED",1);
+if(!defined("PROVIDER_STATE_RUNNING")) define("PROVIDER_STATE_RUNNING",2);
+
+//检查swoole及zookeeper、redis是否安装
+if(!extension_loaded('swoole'))
+{
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m no swoole extension.\n".PHP_EOL);
+ exit(1);
+}
+if (!extension_loaded('zookeeper'))
+{
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m no zookeeper extension.\n".PHP_EOL);
+ exit(1);
+}
+if (!extension_loaded('redis'))
+{
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m no redis extension.\n".PHP_EOL);
+ exit(1);
+}
+
+//php app_admin.php ServerName start
+if($argc == 3)
+{
+ $name = $argv[1];
+ $cmd = $argv[2];
+}
+else if (2 == $argc)
+{
+ $cmd = $argv[1];
+}
+else
+{
+ printInfo();
+ exit(1);
+}
+
+//新命令格式:app_admin cmd appname
+$cmds = array('start','stop','reload','status','restart','list','startall','shutdown','reloadall','extstart','extrestart');
+$one_cmds = array('start','stop','reload','restart','extstart','extrestart');
+$all_cmds = array('list','startall','shutdown','reloadall');
+if (!in_array($cmd,$cmds ) || (empty($name)&& in_array($cmd,$one_cmds)) || (!empty($name) && in_array($cmd,$all_cmds)))
+{
+ printInfo();
+ exit(1);
+}
+
+if(3 == $argc)
+{
+ echo "--------app_admin.php $name $cmd--------".PHP_EOL;
+}
+else
+{
+ echo "--------app_admin.php $cmd--------".PHP_EOL;
+}
+
+$config = FSOFConfigManager::getProviderAppDeploy($name);
+if(isset($config['server']['log_cfg_file_path']) && !empty($config['server']['log_cfg_file_path']))
+{
+ \Logger::configure($config['server']['log_cfg_file_path']);
+ date_default_timezone_set('PRC');
+}
+
+//执行所有的控制命令
+if($cmd == 'list')
+{
+ $configArr = FSOFConfigManager::getProviderAppList();
+ echo "---------your server list:---------".PHP_EOL;
+ foreach($configArr as $k => $v)
+ {
+ echo basename($v, '.deploy').PHP_EOL;
+ }
+ echo '----------------------------'.PHP_EOL;
+ exit(1);
+}
+else if ("startall" == $cmd)
+{
+ $providerList = FSOFConfigManager::getProviderAppList();
+ foreach($providerList as $k => $v)
+ {
+ $name = basename($v, '.deploy');
+ startProvider($name, "start");
+ }
+}
+else if ("reloadall" == $cmd || "shutdown" == $cmd)
+{
+ $providerList = FSOFConfigManager::getRunProviderList();
+ foreach($providerList as $k => $v)
+ {
+ $name = basename($v, MASTER_PID_FILE_FORMAT);
+ if ("reloadall" == $cmd)
+ {
+ reloadProvider($name);
+ }
+ else
+ {
+ stopProvider($name);
+ echo "server {$name} stop \033[32;40m [SUCCESS] \033[0m".PHP_EOL;
+ }
+ }
+}
+else if ("reload" == $cmd)
+{
+ reloadProvider($name);
+}
+else if ("start" == $cmd || "extstart" == $cmd)
+{
+ $ret = startProvider($name, $cmd);
+ if (!$ret)
+ {
+ exit(1);
+ }
+}
+else if ("restart" == $cmd || "extrestart" == $cmd)
+{
+ $ret = restartProvider($name, $cmd);
+ if (!$ret)
+ {
+ exit(1);
+ }
+}
+else if ("stop" == $cmd )
+{
+ stopProvider($name);
+ echo "server {$name} stop \033[32;40m [SUCCESS] \033[0m".PHP_EOL;
+}
+else if("status" == $cmd)
+{
+ if(empty($name))
+ {
+ $providerList = FSOFConfigManager::getProviderAppList();
+ foreach($providerList as $k => $v)
+ {
+ $name = basename($v, '.deploy');
+ getProviderStatus($name);
+ }
+ }
+ else
+ {
+ getProviderStatus($name);
+ }
+}
+
+function startServ($cmd, $name)
+{
+ $startUpPath = dirname(FSOF_ROOT_PATH)."/provider/shell/StartUp.php";
+ if(!file_exists($startUpPath))
+ {
+ echo "{$cmd} server {$name} \033[31;40m [FAIL] \033[0m; no find:{$startUpPath}".PHP_EOL;
+ return false;
+ }
+
+ initByFSOFConfig();;
+ $process = new \swoole_process(function(\swoole_process $worker) use($startUpPath, $cmd, $name)
+ {
+ $worker->exec(PHP_BIN_PATH, array($startUpPath, $name, $cmd));
+ }, false);
+ $process->start();
+ $exeRet = \swoole_process::wait();
+ if($exeRet['code'])
+ {
+ //创建失败
+ echo "{$cmd} server {$name} \033[31;40m [FAILED] \033[0m".PHP_EOL;
+ return false;
+ }
+ else
+ {
+ echo "{$cmd} server {$name} \033[32;40m [SUCCESS] \033[0m".PHP_EOL;
+ return true;
+ }
+}
+
+function getProviderMasterPid($name)
+{
+ $providerFile = FSOF_PROVIDER_PID_PATH.$name.MASTER_PID_FILE_FORMAT;
+ if (file_exists($providerFile))
+ {
+ $pid = file_get_contents($providerFile);
+ if (!empty($pid))
+ {
+ if (posix_kill(intval($pid), SIG_DFL))
+ {
+ return $pid;
+ }
+ }
+ else
+ {
+ $master_name = "{$name}_master_process";
+ return filterProcess($master_name, PS_CMD_WORK_PROCESS);
+ }
+ }
+ return null;
+}
+
+function getProviderManagerPid($name)
+{
+ $providerFile = FSOF_PROVIDER_PID_PATH.$name.MANAGER_PID_FILE_FORMAT;
+ if (file_exists($providerFile))
+ {
+ $pid = file_get_contents($providerFile);
+ if (!empty($pid))
+ {
+ if (posix_kill(intval($pid), SIG_DFL))
+ {
+ return $pid;
+ }
+ }
+ else
+ {
+ $manager_name = "{$name}_manager_process";
+ return filterProcess($manager_name, PS_CMD_WORK_PROCESS);
+ }
+ }
+ return null;
+}
+
+function checkMasterProcessExist($name)
+{
+ $pid = getProviderMasterPid($name);
+ if (empty($pid))
+ {
+ return false;
+ }
+ else
+ {
+ echo "the pid of {$name} is :".$pid.PHP_EOL;
+ return true;
+ }
+}
+
+function checkProviderInfo($name)
+{
+ $providerDeploy = FSOFConfigManager::isExistProviderDeploy($name);
+ if (empty($providerDeploy))
+ {
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m your server {$name} not deploy".PHP_EOL);
+ return false;
+ }
+
+ $phpStart = FSOFConfigManager::isExistProviderFile($name);
+ if(!$phpStart)
+ {
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m {$name} root path not exist".PHP_EOL);
+ return false;
+ }
+ return true;
+}
+
+function start( $name, $cmd)
+{
+ $isRun = checkMasterProcessExist($name);
+ if ($isRun)
+ {
+ return PROVIDER_STATE_RUNNING;
+ }
+ else
+ {
+ if(startServ($cmd,$name))
+ {
+ return PROVIDER_STATE_SUCCESS;
+ }
+ else
+ {
+ return PROVIDER_STATE_FAILED;
+ }
+ }
+}
+
+function startProvider($name, $cmd)
+{
+ if (!checkProviderInfo($name))
+ {
+ return false;
+ }
+
+ $ret = start($name, $cmd);
+ if ( PROVIDER_STATE_SUCCESS == $ret)
+ {
+ echo "server {$name} start \033[32;40m [SUCCESS] \033[0m".PHP_EOL;
+ }
+ else if (PROVIDER_STATE_RUNNING == $ret)
+ {
+ echo "{$name} is already running".PHP_EOL;
+ }
+ else
+ {
+ fwrite(STDERR, "server {$name} start \033[31;40m [FAIL] \033[0m".PHP_EOL);
+ return false;
+ }
+ return true;
+}
+
+function restartProvider($name, $cmd)
+{
+ if (!checkProviderInfo($name))
+ {
+ return false;
+ }
+ stopProvider($name);
+ $startCmd = "start";
+ if ("extrestart" == $cmd)
+ {
+ $startCmd = "extstart";
+ }
+ $ret = start($name, $startCmd);
+ if (PROVIDER_STATE_FAILED != $ret)
+ {
+ echo "restart server {$name} \033[32;40m [SUCCESS] \033[0m" . PHP_EOL;
+ }
+ else
+ {
+ fwrite(STDERR, "restart server {$name} \033[31;40m [FAIL] \033[0m" . PHP_EOL);
+ return false;
+ }
+ return true;
+}
+
+function reloadProvider($name)
+{
+ $pid = getProviderMasterPid($name);
+ if (!posix_kill(intval($pid), SIGUSR1))
+ {
+ echo $name."server {$name} reload \033[31;40m [FAIL] \033[0m".PHP_EOL;
+ }
+ else
+ {
+ fwrite(STDERR, "server {$name} reload \033[32;40m [SUCCESS] \033[0m".PHP_EOL);
+ }
+}
+
+function waitProcessEnd($name)
+{
+ /*
+ * 先检测该master进程是否存在,检测时间为10s,不存在则退出,如一直存在,则强制杀掉子进程(发送SIGKILL信号),目的为了manager进程能正常退出, 从zookeeper注销服务
+ * ,等待2s后重新检测,如仍存在,则强制杀除master与manager进程
+ */
+ $work_name= "{$name}_event_worker_process";
+ $stime = time();
+
+ while(time() - $stime < 10)
+ {
+ $pid = getProviderMasterPid($name);
+ if (empty($pid))
+ {
+ break;
+ }
+ sleep(1);
+ }
+
+ $workPid = trim(filterProcess($work_name, PS_CMD_WORK_PROCESS));
+ if (!empty($workPid))
+ {
+ $pids = explode("\n", $workPid);
+ foreach ($pids as $key => $wPid)
+ {
+ posix_kill(intval($wPid), SIGKILL);
+ usleep(10000);
+ }
+ }
+
+ sleep(1);
+ $pid = getProviderMasterPid($name);
+ if (!empty($pid))
+ {
+ $managerPid = getProviderManagerPid($name);
+ if(!empty($managerPid))
+ {
+ posix_kill(intval($managerPid), SIGKILL);
+ }
+ posix_kill(intval($pid), SIGKILL);
+ }
+}
+
+function stopProvider($name)
+{
+ $masterPid = getProviderMasterPid($name);
+ if (empty($masterPid))
+ {
+ waitProcessEnd($name);
+ }
+ else
+ {
+ if (!posix_kill(intval($masterPid), SIGTERM))
+ {
+ fwrite(STDERR, "end signal to {$name}: {$masterPid} failed" );
+ }
+ waitProcessEnd($name);
+ }
+
+ $masterFile = FSOF_PROVIDER_PID_PATH.$name.MASTER_PID_FILE_FORMAT;
+ $managerFile = FSOF_PROVIDER_PID_PATH.$name.MANAGER_PID_FILE_FORMAT;
+ if (file_exists($masterFile))
+ {
+ unlink($masterFile);
+ }
+ if (file_exists($managerFile))
+ {
+ unlink($managerFile);
+ }
+ echo "stop server {$name} \033[32;40m [SUCCESS] \033[0m".PHP_EOL;
+}
+
+function getProviderStatus($name)
+{
+ echo "######################\033[32;40m {$name} server status\033[0m##################" . PHP_EOL;
+ if(getProviderMasterPid($name))
+ {
+ $psRet = filterProcess($name, FILTER_PROCESS_STATUS);
+ if (empty($psRet))
+ {
+ echo PHP_EOL . "\033[32;40m {$name} is [NO RUNNING] \033[0m-------------" . PHP_EOL;
+ }
+ else
+ {
+ print_r($psRet);
+ echo PHP_EOL . "\033[32;40m {$name} is [RUNNING] \033[0m-------------" . PHP_EOL;
+ }
+ }
+ else
+ {
+ echo PHP_EOL . "\033[32;40m {$name} is [NO RUNNING] \033[0m-------------" . PHP_EOL;
+ }
+ echo "-------------------------------------------------------------------------------------" . PHP_EOL;
+}
+
+function filterProcess($process_name,$type)
+{
+ if(FILTER_PROCESS_STATUS == $type)
+ {
+ $shell_cmd = "ps auxf | grep {$process_name} | grep -v grep | grep -v app_admin";
+ }
+ elseif(PS_CMD_WORK_PROCESS == $type)
+ {
+ $shell_cmd = "ps auxf | grep -w {$process_name} | grep -v grep | awk '{print $2}'";
+ }
+ else
+ {
+ return "usage: type(1/2) name";
+ }
+
+ $file = popen($shell_cmd, 'r');
+ if(empty($file))
+ {
+ //echo "$shell_cmd.', return null'" . PHP_EOL;
+ return null;
+ }
+
+ $ret = fread($file,10240);
+ pclose($file);
+
+ $ret = trim(print_r($ret,TRUE));
+ //echo $shell_cmd.",return ".$ret. PHP_EOL;
+ return $ret;
+}
+
+function analyseFSOFConfig()
+{
+ $fsofConfigPath = FSOF_INI_CONFIG_FILE_PATH.DIRECTORY_SEPARATOR.'fsof.ini';
+ if (!file_exists($fsofConfigPath))
+ {
+ fwrite(STDERR,"\033[31;40m [ERROR] \033[0m " . $fsofConfigPath . " can not be loaded");
+ return null;
+ }
+ return parse_ini_file($fsofConfigPath, true);
+}
+
+function initByFSOFConfig()
+{
+ $globalFSOFConfig = analyseFSOFConfig();
+ if(!empty($globalFSOFConfig))
+ {
+ if(isset($globalFSOFConfig['fsof_container_setting']))
+ {
+ $globalConfig = $globalFSOFConfig['fsof_container_setting'];
+ }
+ }
+
+ if(strlen(PHP_BINDIR) > 0)
+ {
+ if(!defined("PHP_BIN_PATH"))
+ {
+ define('PHP_BIN_PATH', PHP_BINDIR.DIRECTORY_SEPARATOR.'php');
+ }
+ }
+ else
+ {
+ if(!defined("PHP_BIN_PATH"))
+ {
+ if (isset($globalConfig['php']))
+ {
+ define('PHP_BIN_PATH',$globalConfig['php']);
+ }
+ else
+ {
+ define('PHP_BIN_PATH','/usr/bin/php');
+ }
+ }
+ }
+
+ // set user
+ if (isset($globalConfig['user']))
+ {
+ $user = $globalConfig['user'];
+ }
+ else
+ {
+ $user = 'root';
+ }
+
+ changeConsoleUser($user);
+}
+
+function changeConsoleUser($user)
+{
+ if (!function_exists('posix_getpwnam'))
+ {
+ trigger_error(__METHOD__.": require posix extension.");
+ return;
+ }
+ $user = posix_getpwnam($user);
+ if($user)
+ {
+ posix_setuid($user['uid']);
+ posix_setgid($user['gid']);
+ }
+}
+
+//用于和守护进程进行通信
+function printInfo()
+{
+ echo "usage: app_admin.php cmd || app_admin.php app_name cmd".PHP_EOL;
+ echo "example: app_admin startall\t\t\t[\033[32;40m启动所有服务\033[0m]".PHP_EOL;
+ echo " app_admin shutdown\t\t\t[\033[32;40m关闭所有服务\033[0m]".PHP_EOL;
+ echo " app_admin reloadall\t\t\t[\033[32;40m热更新所有服务\033[0m]".PHP_EOL;
+ echo " app_admin list\t\t\t\t[\033[32;40m查看服务\033[0m]".PHP_EOL;
+ echo " app_admin status\t\t\t[\033[32;40m查看所有服务运行状态\033[0m]".PHP_EOL;
+ echo " app_admin app_name status\t\t[\033[32;40m查看app服务运行状态\033[0m]".PHP_EOL;
+ echo " app_admin app_name start\t\t[\033[32;40m启动app服务\033[0m]".PHP_EOL;
+ echo " app_admin app_name restart\t\t[\033[32;40m重启app服务\033[0m]".PHP_EOL;
+ echo " app_admin app_name reload\t\t[\033[32;40m热更新app服务\033[0m]".PHP_EOL;
+ echo " app_admin app_name stop\t\t[\033[32;40m停止app服务\033[0m]".PHP_EOL;
+ echo " app_admin app_name extstart\t\t[\033[32;40m启动扩展的app服务,不注册配置中心\033[0m]".PHP_EOL;
+ echo " app_admin app_name extrestart\t\t[\033[32;40m重启扩展的app服务,不注册配置中心\033[0m]".PHP_EOL;
+ exit(1);
+}
+
diff --git a/bin/restartall.sh b/bin/restartall.sh
new file mode 100644
index 0000000..ca205fa
--- /dev/null
+++ b/bin/restartall.sh
@@ -0,0 +1,17 @@
+#/bin/bash
+
+FSOF_BIN=`pwd`;
+DIRECTORY_SEPARATOR="/";
+FILE_NAME="servicename_master_process";
+
+#记录启动的服务
+ps -ef| grep master_process| grep -v grep > $FSOF_BIN$DIRECTORY_SEPARATOR$FILE_NAME;
+
+#启动服务进程
+while read LINE
+do
+ SER_NAME=`echo $LINE| awk '{print $NF}'|sed 's/_master_process//g'`;
+ echo "********************Beganing Start" $SER_NAME;
+ php $FSOF_BIN$DIRECTORY_SEPARATOR"app_admin.php" $SER_NAME restart;
+ echo "********************Ending Start" $SER_NAME;
+done < $FSOF_BIN$DIRECTORY_SEPARATOR$FILE_NAME
diff --git a/common/BootStrap.php b/common/BootStrap.php
new file mode 100644
index 0000000..e498469
--- /dev/null
+++ b/common/BootStrap.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common;
+
+//定义common根目录
+$fsof_common_root_path = __DIR__;
+require_once($fsof_common_root_path.DIRECTORY_SEPARATOR.'FrameAutoLoader.php');
+
+// 定义config模块的根路径
+if(!defined('FSOF_CONFIG_ROOT_PATH')) define('FSOF_CONFIG_ROOT_PATH', dirname($fsof_common_root_path));
+
+//fsof.ini文件路径
+$fsofIniConfigFilePath = FSOF_CONFIG_ROOT_PATH.DIRECTORY_SEPARATOR.'config'.DIRECTORY_SEPARATOR.'global'.DIRECTORY_SEPARATOR.'conf';
+if(!defined('FSOF_INI_CONFIG_FILE_PATH')) define('FSOF_INI_CONFIG_FILE_PATH', $fsofIniConfigFilePath);
+
+//app.deploy文件路径
+$fsofPhpConfigPath = FSOF_CONFIG_ROOT_PATH.DIRECTORY_SEPARATOR.'config';
+if(!defined('FSOF_PHP_CONFIG_ROOT_PATH')) define('FSOF_PHP_CONFIG_ROOT_PATH', $fsofPhpConfigPath);
+
+//注册顶层命名空间到自动载入器
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common', $fsof_common_root_path);
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\log', $fsof_common_root_path.DIRECTORY_SEPARATOR.'log');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\url', $fsof_common_root_path.DIRECTORY_SEPARATOR.'url');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\file', $fsof_common_root_path.DIRECTORY_SEPARATOR.'file');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\config', $fsof_common_root_path.DIRECTORY_SEPARATOR.'config');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\context', $fsof_common_root_path.DIRECTORY_SEPARATOR.'context');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\protocol', $fsof_common_root_path.DIRECTORY_SEPARATOR.'protocol');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\fuse', $fsof_common_root_path.DIRECTORY_SEPARATOR.'fuse');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\common\report', $fsof_common_root_path.DIRECTORY_SEPARATOR.'report');
+
+spl_autoload_register(__NAMESPACE__.'\FrameAutoLoader::autoload');
+
diff --git a/common/FrameAutoLoader.php b/common/FrameAutoLoader.php
new file mode 100644
index 0000000..9ff2d17
--- /dev/null
+++ b/common/FrameAutoLoader.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common;
+
+class FrameAutoLoader
+{
+ /**
+ * 命名空间的路径
+ */
+ private static $nsPath = Array();
+
+ /**
+ * 自动载入类
+ * @param $class
+ */
+ public static function autoload($class)
+ {
+ $frameRootPathDepth = count(explode('\\', __NAMESPACE__));
+ $root = explode('\\', trim($class, '\\'));
+ $pathDepth = count($root);
+ if($pathDepth > $frameRootPathDepth)
+ {
+ $key = $className = $class;
+ if($pathDepth > $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 2);
+ $className = $root[$frameRootPathDepth + 1];
+ unset($root[$frameRootPathDepth + 1]);
+ $key = implode($root, '\\');
+ }
+ else if($pathDepth == $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 1);
+ $className = $root[$frameRootPathDepth];
+ unset($root[$frameRootPathDepth]);
+ $key = implode($root, '\\');
+ }
+
+ if(isset(self::$nsPath[$key]))
+ {
+ include_once self::$nsPath[$key] . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
+ }
+ }
+ }
+
+ /**
+ * 设置根命名空间
+ * @param $root
+ * @param $path
+ */
+ public static function setRootNS($root, $path)
+ {
+ self::$nsPath[$root] = $path;
+ }
+}
\ No newline at end of file
diff --git a/common/config/FSOFCommonUtil.php b/common/config/FSOFCommonUtil.php
new file mode 100644
index 0000000..6230ad0
--- /dev/null
+++ b/common/config/FSOFCommonUtil.php
@@ -0,0 +1,75 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\config;
+
+class FSOFCommonUtil
+{
+ public static function parseWeightInfo($versionWeightList)
+ {
+ $versionLists = array();
+ $versionWeight = explode(",", $versionWeightList);
+ foreach ($versionWeight as $index => $value)
+ {
+ $versionWeight = explode(":" ,$value);
+ if (count($versionWeight) == 1)
+ {
+ $versionLists[$value] = 0;
+ }
+ else
+ {
+ $versionLists[$versionWeight[0]] = $versionWeight[1];
+ }
+ }
+ return $versionLists;
+ }
+
+ public static function getWeightSum($versionLists)
+ {
+ $weightSum = 0;
+ foreach ($versionLists as $key => $value)
+ {
+ $weightSum += $value;
+ }
+ return $weightSum;
+ }
+
+ public static function getVersionByWeight($versionWeightList)
+ {
+ $version = $versionWeightList;
+ $versionLists = self::parseWeightInfo($versionWeightList);
+ $weightSum = self::getWeightSum($versionLists);
+ if ($weightSum > 1)
+ {
+ $startValue = 0;
+ $dstValue = mt_rand(0, $weightSum-1);
+ foreach ($versionLists as $key => $value)
+ {
+ if($dstValue >= $startValue && $dstValue < $value + $startValue)
+ {
+ $version = $key;
+ break;
+ }
+ else
+ {
+ $startValue += $value;
+ }
+ }
+ }
+ return $version;
+ }
+}
diff --git a/common/config/FSOFConfigManager.php b/common/config/FSOFConfigManager.php
new file mode 100644
index 0000000..b7bb728
--- /dev/null
+++ b/common/config/FSOFConfigManager.php
@@ -0,0 +1,228 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\config;
+
+class FSOFConfigManager
+{
+
+ public static function getFSOFIni()
+ {
+ $fsof_config_file = FSOF_INI_CONFIG_FILE_PATH.DIRECTORY_SEPARATOR.'fsof.ini';
+ $fsof_config_data = FSOFConfigUtil::loadConfigFile($fsof_config_file);
+ return $fsof_config_data;
+ }
+
+ public static function getDeployFile($name)
+ {
+ $env = self::getCurrentEnvironment();
+ return FSOF_PHP_CONFIG_ROOT_PATH.DIRECTORY_SEPARATOR.'app'.DIRECTORY_SEPARATOR.'conf'.DIRECTORY_SEPARATOR.$env.DIRECTORY_SEPARATOR.'provider'.DIRECTORY_SEPARATOR.$name.'.deploy';
+ }
+
+ public static function getCurrentEnvironment()
+ {
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
+ {
+ $currentEnv = 'dev';
+ return $currentEnv;
+ }
+ $currentEnv = 'pro';
+ $fsof_config_data = self::getFSOFIni();
+ if(isset($fsof_config_data['fsof_setting']['environment']))
+ {
+ $currentEnv = $fsof_config_data['fsof_setting']['environment'];
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->error('fsof.ini is not set environment, default is pro');
+ }
+ return $currentEnv;
+ }
+
+ public static function getKeepConnect()
+ {
+ $keep_connect = false;
+ $fsof_config_data = self::getFSOFIni();
+ if(isset($fsof_config_data['fsof_setting']['keep_connect']))
+ {
+ $keep_connect = $fsof_config_data['fsof_setting']['keep_connect'];
+ }
+ return $keep_connect;
+ }
+
+ public static function isExistProviderDeploy($appName)
+ {
+ $deployFile = self::getDeployFile($appName);
+ if(file_exists($deployFile))
+ {
+ return $deployFile;
+ }
+
+ \Logger::getLogger(__CLASS__)->error('not deploy '.$appName.":".$deployFile);
+ return null;
+ }
+
+ public static function isExistProviderFile($name)
+ {
+ $appBootFile = self::getProviderAppRoot($name);
+ if (isset($appBootFile) && file_exists($appBootFile) && is_file($appBootFile))
+ {
+ return true;
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->error('not process ' . $name . ":" . $appBootFile);
+ }
+ return false;
+ }
+
+ public static function isProviderAppDeploy($appName)
+ {
+ $deployFile = self::getDeployFile($appName);
+ if(file_exists($deployFile))
+ {
+ $appBootFile = self::getProviderAppRoot($appName);
+ if(isset($appBootFile) && file_exists($appBootFile) && is_file($appBootFile))
+ {
+ return TRUE;
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->error("{$appName} bootstrap not exist:".$appBootFile);
+ }
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->error("{$appName} deploy file not exist:".$deployFile);
+ }
+ return FALSE;
+ }
+
+ public static function getProviderAppDeploy($appName)
+ {
+ $deployFile = self::getDeployFile($appName);
+ $deployFileData = parse_ini_file($deployFile, TRUE);
+ return $deployFileData;
+ }
+
+ public static function getProviderAppRoot($appName)
+ {
+ $deployFile = self::getDeployFile($appName);
+ if(file_exists($deployFile))
+ {
+ $config = parse_ini_file($deployFile, TRUE);
+ if(isset($config['server']['root']))
+ {
+ $appBootFile = $config['server']['root'];
+ if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN')
+ {
+ $appBootFile = str_replace('/', '\\', $appBootFile);
+ }
+ if(strpos($appBootFile, DIRECTORY_SEPARATOR) > 0)
+ {
+ //对于相对路径,需要拼接成完整路径
+ $appBootFile = FSOF_PHP_CONFIG_ROOT_PATH.DIRECTORY_SEPARATOR.$appBootFile;
+ }
+ return $appBootFile;
+ }
+ }
+ return NULL;
+ }
+
+ public static function trimall($str)
+ {
+ //删除空格,换行符及其它
+ $qian=array(" "," ","\t","\n","\r");
+ $hou=array("","","","","");
+ return str_replace($qian,$hou,$str);
+ }
+
+ public static function isVersion($version)
+ {
+ if(empty($version))
+ {
+ return FALSE;
+ }
+ $match_times = preg_match('/^(dev\.|prod\.|)\d+\.\d+\.\d+/', $version);
+ if($match_times > 0)
+ {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ public static function getDeployVersion($rootPath)
+ {
+ $ret = NULL;
+ $versionFilePath = dirname(dirname($rootPath)).DIRECTORY_SEPARATOR.'version.config';
+ if (file_exists($versionFilePath))
+ {
+ $ret = self::trimall(file_get_contents($versionFilePath));
+ if(!self::isVersion($ret))
+ {
+ $ret = NULL;
+ \Logger::getLogger(__CLASS__)->error("version.config: version is error:".$ret);
+ }
+ }
+ return $ret;
+ }
+
+ public static function selectDeployVersion($bootFile, $version)
+ {
+ //插入发布版本信息
+ if(empty($version) || empty($bootFile))
+ {
+ return $bootFile;
+ }
+ $rootPath = explode(DIRECTORY_SEPARATOR, $bootFile);
+ $num = count($rootPath);
+ $tmp = array($num+1);
+ for($i = 0; $i < $num; $i++)
+ {
+ if($i < $num-2)
+ {
+ $tmp[$i] = $rootPath[$i];
+ }
+ else if($i == $num-2)
+ {
+ $tmp[$i] = $version;
+ $tmp[$i + 1] = $rootPath[$i];
+ }
+ else
+ {
+ $tmp[$i + 1] = $rootPath[$i];
+ }
+ }
+ $bootFile = implode(DIRECTORY_SEPARATOR, $tmp);
+ return $bootFile;
+ }
+
+ public static function getProviderAppList()
+ {
+ //遍历所有的deploy文件,所有appname采用'*'
+ $deployDir = self::getDeployFile('*');
+ $deploylist = glob($deployDir);
+ return $deploylist;
+ }
+
+ public static function getRunProviderList()
+ {
+ $pid = FSOF_PROVIDER_PID_PATH.'*.master.pid';
+ $pidList = glob($pid);
+ return $pidList;
+ }
+}
diff --git a/common/config/FSOFConfigUtil.php b/common/config/FSOFConfigUtil.php
new file mode 100644
index 0000000..6aa315d
--- /dev/null
+++ b/common/config/FSOFConfigUtil.php
@@ -0,0 +1,99 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\config;
+
+class FSOFConfigUtil
+{
+ public static function loadConfigFile($configFile)
+ {
+ if (! file_exists($configFile))
+ {
+ \Logger::getLogger(__CLASS__)->error($configFile." can not be loaded");
+ return array();
+ }
+ $config = parse_ini_file($configFile, true);
+ return $config;
+ }
+
+ public static function get($configFile, $key, $default = NULL)
+ {
+ $config = self::loadConfigFile($configFile);
+ $result = isset($config[$key]) ? $config[$key] : $default;
+ return $result;
+ }
+
+ public static function getField($configFile, $key, $filed, $default = NULL)
+ {
+ $config = self::loadConfigFile($configFile);
+ $result = isset($config[$key][$filed]) ? $config[$key][$filed] : $default;
+ return $result;
+ }
+
+ public static function parse_ini_file_multi($file, $process_sections = false, $scanner_mode = INI_SCANNER_NORMAL)
+ {
+ $explode_str = '.';
+ $escape_char = "'";
+ // load ini file the normal way
+ $data = parse_ini_file($file, $process_sections, $scanner_mode);
+ if (!$process_sections)
+ {
+ $data = array($data);
+ }
+ foreach ($data as $section_key => $section)
+ {
+ // loop inside the section
+ foreach ($section as $key => $value)
+ {
+ if (strpos($key, $explode_str))
+ {
+ if (substr($key, 0, 1) !== $escape_char)
+ {
+ // key has a dot. Explode on it, then parse each subkeys
+ // and set value at the right place thanks to references
+ $sub_keys = explode($explode_str, $key);
+ $subs =& $data[$section_key];
+ foreach ($sub_keys as $sub_key)
+ {
+ if (!isset($subs[$sub_key]))
+ {
+ $subs[$sub_key] = array();
+ }
+ $subs =& $subs[$sub_key];
+ }
+ // set the value at the right place
+ $subs = $value;
+ // unset the dotted key, we don't need it anymore
+ unset($data[$section_key][$key]);
+ }
+ else
+ {
+ // we have escaped the key, so we keep dots as they are
+ $new_key = trim($key, $escape_char);
+ $data[$section_key][$new_key] = $value;
+ unset($data[$section_key][$key]);
+ }
+ }
+ }
+ }
+ if (!$process_sections)
+ {
+ $data = $data[0];
+ }
+ return $data;
+ }
+}
\ No newline at end of file
diff --git a/common/config/FSOFConstants.php b/common/config/FSOFConstants.php
new file mode 100644
index 0000000..0b38d0c
--- /dev/null
+++ b/common/config/FSOFConstants.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\config;
+
+class FSOFConstants
+{
+ //任何服务分组
+ const FSOF_SERVICE_GROUP_ANY = '*';
+ //默认服务分组
+ const FSOF_SERVICE_GROUP_DEFAULT = 'default';
+ //默认服务版本
+ const FSOF_SERVICE_VERSION_DEFAULT = '1.0.0';
+
+ //monitor定时监控时间,暂定5分钟
+ const FSOF_MONITOR_TIMER = 300000;
+
+ //所使用的redis端口号
+ const FSOF_SERVICE_REDIS_PORT =6379;
+}
\ No newline at end of file
diff --git a/common/file/FSOFRedis.php b/common/file/FSOFRedis.php
new file mode 100644
index 0000000..9ec56a1
--- /dev/null
+++ b/common/file/FSOFRedis.php
@@ -0,0 +1,182 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\file;
+
+use com\fenqile\fsof\common\config\FSOFConstants;
+
+
+class FSOFRedis
+{
+ const REDIS_TIME_OUT = 1;
+
+ private static $_instance;
+
+ private $m_redis = null;
+
+ private $logger;
+
+ public static function instance()
+ {
+ if (extension_loaded('redis'))
+ {
+ if (!isset(FSOFRedis::$_instance))
+ {
+ FSOFRedis::$_instance = new FSOFRedis();
+ }
+ return FSOFRedis::$_instance;
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->error("not installed redis extension");
+ }
+ return NULL;
+ }
+
+ public function __construct()
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ $this->get_redis();
+ }
+
+ public function get_redis()
+ {
+ if (!isset($this->m_redis))
+ {
+ try
+ {
+ $redis_cli = new \Redis();
+ $ret = $redis_cli->connect("127.0.0.1",FSOFConstants::FSOF_SERVICE_REDIS_PORT,self::REDIS_TIME_OUT);
+ if (!$ret)
+ {
+ $this->logger->warn("connect redis failed[127.0.0.1:6379]");
+ $ret = $redis_cli->connect("/var/fsof/redis.sock",-1,self::REDIS_TIME_OUT);
+ }
+ }
+ catch (\Exception $e)
+ {
+ $ret = false;
+ $this->logger->error('connect redis excepiton:'.$e->getMessage().', errno:' . $e->getCode());
+ throw new \Exception($e->getMessage());
+ }
+
+ if($ret)
+ {
+ $this->m_redis = $redis_cli;
+ }
+ else
+ {
+ $this->logger->error('connect redis failed:|errno:' . $redis_cli->getLastError());
+ throw new \Exception("连接本地redis异常");
+ }
+ }
+
+ return $this->m_redis;
+ }
+
+ public function getProviderInfo($key)
+ {
+ return $this->getlist($key);
+ }
+
+ public function get($key)
+ {
+ if (!empty($key) && isset($this->m_redis))
+ {
+ return $this->m_redis->get($key);
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ public function getlRange($key)
+ {
+ if (!empty($key) && isset($this->m_redis))
+ {
+ return $this->m_redis->lRange($key, 0, -1);
+ }
+ else
+ {
+ $this->logger->warn('not object of redis:'.$key);
+ return NULL;
+ }
+ }
+
+ public function getlist($key)
+ {
+ $ret = NULL;
+ if (!empty($key))
+ {
+ try
+ {
+ if(!isset($this->m_redis))
+ {
+ $this->get_redis();
+ }
+ $ret = $this->getlRange($key);
+ }
+ catch (\Exception $e)
+ {
+ $this->logger->warn('redis current connect excepiton'.' |errcode:'.$e->getCode().' |errmsg:'.$e->getMessage());
+ $this->close();
+ //重试一次
+ $this->get_redis();
+ $ret = $this->getlRange($key);
+ }
+ }
+ return $ret;
+ }
+
+ public function set($key, $value)
+ {
+ if (!empty($key) && isset($this->m_redis))
+ {
+ return $this->m_redis->set($key, $value);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ /**
+ * Description: 关闭redis连接
+ */
+ public function close()
+ {
+ try
+ {
+ if (isset($this->m_redis))
+ {
+ $this->m_redis->close();
+ unset($this->m_redis);
+ }
+ }
+ catch (\Exception $e)
+ {
+ unset($this->m_redis);
+ $this->logger->error('close redis error:'.$e->getMessage(),$e);
+ }
+ }
+
+ public function __destruct()
+ {
+ $this->close();
+ }
+}
diff --git a/common/file/FileSystemUtil.php b/common/file/FileSystemUtil.php
new file mode 100644
index 0000000..5cdef35
--- /dev/null
+++ b/common/file/FileSystemUtil.php
@@ -0,0 +1,132 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\file;
+
+class FileSystemUtil
+{
+ /**
+ * 递归创建目录
+ * @param $dir
+ * @param int $mode
+ * @return bool
+ */
+ public static function makeDir($dir, $mode = 0777)
+ {
+ if (is_dir($dir) || mkdir($dir, $mode, true))
+ {
+ return true;
+ }
+
+ if (!self::makeDir(dirname($dir), $mode))
+ {
+ return false;
+ }
+ return mkdir($dir, $mode);
+ }
+
+ /**
+ * 递归获取目录下的文件
+ * @param $dir
+ * @param string $filter
+ * @param array $result
+ * @param bool $deep
+ * @return array
+ */
+ public static function treeDir($dir, $filter = '', &$result = array(), $deep = false)
+ {
+ $files = new \DirectoryIterator($dir);
+ foreach ($files as $file)
+ {
+ if ($file->isDot())
+ {
+ continue;
+ }
+
+ $filename = $file->getFilename();
+
+ if ($file->isDir())
+ {
+ self::treeDir($dir . DIRECTORY_SEPARATOR . $filename, $filter, $result, $deep);
+ }
+ else
+ {
+ if(!empty($filter) && !preg_match($filter, $filename))
+ {
+ continue;
+ }
+
+ if ($deep)
+ {
+ $result[$dir][] = $filename;
+ }
+ else
+ {
+ $result[] = $dir . DIRECTORY_SEPARATOR . $filename;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ /**
+ * 递归删除目录
+ * @param $dir
+ * @param $filter
+ * @return bool
+ */
+ public static function deleteDir($dir, $filter = '')
+ {
+ $files = new \DirectoryIterator($dir);
+ foreach ($files as $file)
+ {
+ if ($file->isDot())
+ {
+ continue;
+ }
+
+ $filename = $file->getFilename();
+
+ if ($file->isDir())
+ {
+ self::deleteDir($dir . DIRECTORY_SEPARATOR . $filename);
+ }
+ else
+ {
+ if (!empty($filter) && !preg_match($filter, $filename))
+ {
+ continue;
+ }
+
+ unlink($dir . DIRECTORY_SEPARATOR . $filename);
+ }
+ }
+ return rmdir($dir);
+ }
+
+ public static function require_file($path)
+ {
+ $bRet = false;
+ if(is_file($path)) {
+ require_once($path);
+ $bRet = true;
+ }
+
+ return $bRet;
+ }
+}
diff --git a/common/log/FSOFSystemUtil.php b/common/log/FSOFSystemUtil.php
new file mode 100644
index 0000000..652c613
--- /dev/null
+++ b/common/log/FSOFSystemUtil.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace com\fenqile\fsof\common\log;
+
+class FSOFSystemUtil
+{
+ private static $localHost = null;
+ private static $defaultHost = '127.0.0.1';
+
+ public static function getLocalIP()
+ {
+ if (!isset(FSOFSystemUtil::$localHost))
+ {
+ if(extension_loaded('swoole'))
+ {
+ $ipList = swoole_get_local_ip();
+ foreach($ipList as $key => $local)
+ {
+ if ($local != self::$defaultHost)
+ {
+ FSOFSystemUtil::$localHost = $local;
+ break;
+ }
+ }
+
+ if (empty(FSOFSystemUtil::$localHost))
+ {
+ FSOFSystemUtil::$localHost = self::$defaultHost;
+ }
+ }
+ else
+ {
+ FSOFSystemUtil::$localHost = self::$defaultHost;
+ }
+ }
+ return FSOFSystemUtil::$localHost;
+ }
+
+ public static function getServiceIP()
+ {
+ $result = self::getLocalIP();
+
+ if ($result == self::$defaultHost)
+ {
+ throw new \Exception("本服务器网络地址错误出错");
+ }
+
+ return $result;
+ }
+}
diff --git a/common/protocol/fsof/DubboParser.php b/common/protocol/fsof/DubboParser.php
new file mode 100644
index 0000000..0aee201
--- /dev/null
+++ b/common/protocol/fsof/DubboParser.php
@@ -0,0 +1,305 @@
+<?php
+namespace com\fenqile\fsof\common\protocol\fsof;
+
+/**
+ *
+ * Dubbo网络协议
+ * +------------------------------------------------------------------------------------------+
+ * | 包头(二进制数据 16bit) | 包体 |
+ * +-----------------------------------------------------------------------------------------+
+ * | 版本号 | 命令&serialize | 空白 | 包序号| 长度 | 数据 |
+ * +-----------------------------------------------------------------------------------------+
+ * | magic(2) | cmd&serialize(1)|(1) |sn(8) | len(4) | data(N)|
+ * +-----------------------------------------------------------------------------------------+
+ *
+ * magic:协议包起始标识, 0xdabb
+ * --------------------------------------------------------------------------------------------
+ * cmd:命令类型:FLAG_REQUEST为0x80, FLAG_TWOWAY为0x40, FLAG_EVENT为0x20
+ * serialize:序列化方案编号:与cmd共用一个字节,采用json,对应dubbo中编号为6
+ * --------------------------------------------------------------------------------------------
+ * sn:请求序号,consumer会为每个请求编制一个进程内唯一序号
+ * ,provider处理完请求后在返回的数据包中会携带该sn号,供consumer判断当前的数据是对应哪个请求
+ * --------------------------------------------------------------------------------------------*
+ * len:数据报文长度
+ * --------------------------------------------------------------------------------------------
+ * data:数据报文,目前采用json进行序列化
+ * --------------------------------------------------------------------------------------------
+ */
+class DubboParser
+{
+ //dubbo协议基本信息
+ const PACKAGE_HEDA_LEN = 16;
+ const MAX_RECV_LEN = 1048576;//1024*1024;
+ const RESPONSE_TCP_SEGMENT_LEN = 1048576;//1*1024*1024;
+
+ //fsof协议ver字段,ver字段既指示协议版本信息,也作为magic使用
+ const DUBBO_PROTOCOL_MAGIC = 0xdabb;
+
+ //serialize 方案编号
+ const DUBBO_PROTOCOL_SERIALIZE_FAST_JSON = 6; //serialization code
+
+
+ //fsof协议包头cmd字段含义
+ const FLAG_REQUEST = 0x80; //request
+ const FLAG_TWOWAY = 0x40; //two_way
+ const FLAG_HEARTBEAT_EVENT = 0x20; //heart_event
+ const SERIALIZATION_MASK = 0x1f; //serialization_mask
+
+ const UPPER_MASK = 0xffffffff00000000;
+ const LOWER_MASK = 0x00000000ffffffff;
+
+ const RESPONSE_WITH_EXCEPTION = 0;
+ const RESPONSE_VALUE = 1;
+ const RESPONSE_NULL_VALUE = 2;
+
+ private $logger;
+
+ public function __construct()
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ }
+
+ public function packRequest(DubboRequest &$request)
+ {
+ $reqData = json_encode($request->getDubboVersion()) . PHP_EOL .
+ json_encode($request->getService()) . PHP_EOL .
+ json_encode($request->getVersion()) . PHP_EOL .
+ json_encode($request->getMethod()) . PHP_EOL ;
+ $typeStr = "";
+ for($i=0;$i < count($request->getTypes());$i++){
+ $typeStr = $typeStr.$request->getTypes()[$i];
+ }
+ $reqData = $reqData.json_encode($typeStr) . PHP_EOL;
+ for ($x = 0; $x < count($request->getParams()); $x++) {
+ $reqData = $reqData . json_encode($request->getParams()[$x]) . PHP_EOL;
+ }
+ $attach = array();
+ $attach['path'] = $request->getService();
+ $attach['interface'] = $request->getService();
+ $attach['group'] = $request->getGroup();
+ $attach['timeout'] = $request->getTimeout();
+ $attach['version'] = $request->getVersion();
+ $request->setAttach($attach);
+ $reqData = $reqData . json_encode($request->getAttach());
+
+ $upper = ($request->getSn() & self::UPPER_MASK) >> 32;
+ $lower = $request->getSn() & self::LOWER_MASK;
+ $flag = (self::FLAG_REQUEST | self::DUBBO_PROTOCOL_SERIALIZE_FAST_JSON);
+ if ($request->isTwoWay()) $flag |= self::FLAG_TWOWAY;
+ if ($request->isHeartbeatEvent()) $flag |= self::FLAG_HEARTBEAT_EVENT;
+ $out = pack("n1C1a1N1N1N1",
+ self::DUBBO_PROTOCOL_MAGIC,
+ $flag,
+ "",
+ $upper,
+ $lower,
+ strlen($reqData));
+ return $out . $reqData;
+ }
+
+
+ public function parseResponseHeader(DubboResponse &$response)
+ {
+ $res_header = substr($response->getFullData(), 0, self::PACKAGE_HEDA_LEN);
+ $format = 'n1magic/C1flag/C1status/N1upper/N1lower/N1len';
+ $_arr = unpack($format, $res_header);
+ $response->setStatus($_arr['status']);
+ $response->setSn($_arr["upper"] << 32 | $_arr["lower"]);
+ $flag = $_arr["flag"];
+ if (($flag & self::FLAG_HEARTBEAT_EVENT) != 0)
+ {
+ $response->setHeartbeatEvent(true);
+ }
+ $response->setSerialization($flag & self::DUBBO_PROTOCOL_SERIALIZE_FAST_JSON);
+ $response->setLen($_arr["len"]);
+ return $response;
+ }
+
+ public function parseResponseBody(DubboResponse &$response)
+ {
+ $_data = substr($response->getFullData(), self::PACKAGE_HEDA_LEN);
+ $response->setResponseBody($_data);
+ $dataArr = array();
+ if ($_data){
+ $dataArr = explode(PHP_EOL,$_data);
+ }
+ if ($response->getStatus() == DubboResponse::OK)
+ {
+ if ($response->isHeartbeatEvent())
+ {
+ $response->setResult(json_decode($dataArr[0], true));
+ } else
+ {
+ switch ($dataArr[0])
+ {
+ case self::RESPONSE_NULL_VALUE:
+ break;
+ case self::RESPONSE_VALUE:
+ $response->setResult(json_decode($dataArr[1], true));
+ break;
+ case self::RESPONSE_WITH_EXCEPTION:
+ $exception = json_decode($dataArr[1], true);
+ if(is_array($exception) && array_key_exists('message', $exception)){
+ throw new \Exception($exception['message']);
+ }else if(is_string($exception)){
+ throw new \Exception($exception);
+ }else{
+ throw new \Exception("provider occur error");
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ return $response;
+ } else {
+ throw new \Exception(json_decode($dataArr[0], true));
+ }
+ }
+
+
+ public function parseRequestHeader(DubboRequest &$request)
+ {
+ $_data = substr($request->getFullData(), 0, self::PACKAGE_HEDA_LEN);
+ $format = 'n1magic/C1flag/C1blank/N1upper/N1lower/N1len';
+ $_arr = unpack($format, $_data);
+ $flag = $_arr['flag'];
+ $request->setTwoWay(($flag & self::FLAG_TWOWAY) != 0);
+ if (($flag & self::FLAG_HEARTBEAT_EVENT) != 0)
+ {
+ $request->setHeartbeatEvent(true);
+ }
+ $request->setSerialization($flag & self::DUBBO_PROTOCOL_SERIALIZE_FAST_JSON);
+ $request->setSn($_arr['upper'] << 32 | $_arr['lower']);
+ $request->setDataLen($_arr['len']);
+ $request->setRequestLen($request->getDataLen() + self::PACKAGE_HEDA_LEN);
+ return $request;
+ }
+
+ public function parseRequestBody(DubboRequest &$request)
+ {
+ if ($request->getSerialization() != self::DUBBO_PROTOCOL_SERIALIZE_FAST_JSON)
+ {
+ $this->logger->error("unknown serialization type :" . $request->getSerialization());
+ return false;
+ }
+ $cliData = substr($request->getFullData(), self::PACKAGE_HEDA_LEN);
+ if ($cliData)
+ {
+ if ($request->isHeartbeatEvent())
+ {
+ //心跳请求,不需要数据回送
+ } else
+ {
+ $dataArr = explode(PHP_EOL, $cliData);
+ $request->setDubboVersion(json_decode($dataArr[0], true));
+ $request->setService(json_decode($dataArr[1], true));
+ $request->setVersion(json_decode($dataArr[2], true));
+ $methodName = json_decode($dataArr[3],true);
+ if ($methodName == "\$invoke")
+ {
+ //泛化调用
+ $request->setMethod(json_decode($dataArr[5],true));
+ $request->setParams(json_decode($dataArr[7],true));
+ $attach = json_decode($dataArr[8],true);
+ } else
+ {
+ //非泛化调用
+ $request->setMethod($methodName);
+ $paramTypes = json_decode($dataArr[4], true);
+ if ($paramTypes == "")
+ {
+ //调用没有参数的方法
+ $request->setTypes(NULL);
+ $request->setParams(NULL);
+ $attach = json_decode($dataArr[5],true);
+ } else
+ {
+ $typeArr = explode(";", $paramTypes);
+ $typeArrLen = count($typeArr);
+ $request->setParamNum($typeArrLen - 1);
+ $params = array();
+ for ($i = 0; $i < $typeArrLen - 1; $i++) {
+ $params[$i] = json_decode($dataArr[5 + $i],true);
+ }
+ $request->setParams($params);
+ $attach = json_decode($dataArr[5 + ($typeArrLen - 1)],true);
+ }
+ }
+ $request->setAttach($attach);
+ if (array_key_exists('group', $attach))
+ {
+ $request->setGroup($attach['group']);
+ }
+ return $request;
+ }
+ }
+ return false;
+ }
+
+
+ public function packResponse(DubboResponse &$response)
+ {
+ if ($response->getStatus() != DubboResponse::OK) {
+ $resData = json_encode($response->getErrorMsg()) ;
+ } else {
+ if($response->getErrorMsg() != NULL && $response->getErrorMsg() != ""){
+ $resData = json_encode(self::RESPONSE_WITH_EXCEPTION) . PHP_EOL . json_encode($response->getErrorMsg());
+ }else if ($response->getResult() == NULL) {
+ $resData = json_encode(self::RESPONSE_NULL_VALUE);
+ } else {
+ $resData = json_encode(self::RESPONSE_VALUE) . PHP_EOL . json_encode($response->getResult());
+ }
+ }
+ $resData = $resData.PHP_EOL;
+ $upper = ($response->getSn() & self::UPPER_MASK) >> 32;
+ $lower = $response->getSn() & self::LOWER_MASK;
+ $flag = self::DUBBO_PROTOCOL_SERIALIZE_FAST_JSON;
+ if ($response->isHeartbeatEvent()) {
+ $flag |= self::FLAG_HEARTBEAT_EVENT;
+ }
+ $out = pack("n1C1C1N1N1N1",
+ self::DUBBO_PROTOCOL_MAGIC,
+ $flag,
+ $response->getStatus(),
+ $upper,
+ $lower,
+ strlen($resData));
+
+ return $out . $resData;
+ }
+
+ public function isNormalResponse(DubboResponse $response)
+ {
+ return !($response->isHeartbeatEvent());
+ }
+
+ public function isNormalRequest(DubboRequest $request)
+ {
+ return !($request->isHeartbeatEvent());
+ }
+
+ public function isOneWayRequest(DubboRequest $request)
+ {
+ return !($request->isTwoWay());
+ }
+
+ public function isHearBeatRequest(DubboRequest $request)
+ {
+ return $request->isHeartbeatEvent();
+ }
+
+ public function isHearBeatResponse(DubboResponse $response)
+ {
+ return $response->isHeartbeatEvent();
+ }
+
+ public static function getReqInQueueTime(DubboRequest $request)
+ {
+ $ret = 0;
+ if (!empty($request->reqInfo)) {
+ $ret = isset($request->reqInfo['inqueue_time']) ? $request->reqInfo['inqueue_time'] : 0;
+ }
+ return $ret;
+ }
+}
\ No newline at end of file
diff --git a/common/protocol/fsof/DubboRequest.php b/common/protocol/fsof/DubboRequest.php
new file mode 100644
index 0000000..cb30cb7
--- /dev/null
+++ b/common/protocol/fsof/DubboRequest.php
@@ -0,0 +1,341 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\protocol\fsof;
+
+class DubboRequest
+{
+ //包头字段
+ private $twoWay = true;
+ private $heartbeatEvent = false;
+ private $sn; //请求序号
+ private $serialization;
+ private $fullData; //报文(包含消息头)
+ private $dataLen; //数据报文长度
+ private $requestLen; //请求报文总长度(即消息头+消息体的长度)
+
+ //包体字段
+ private $dubboVersion = "2.0.0"; //dubbo 版本
+ private $service; //服务名称
+ private $version; //服务版本
+ private $group; //服务group
+ private $method; //方法名
+ private $timeout; //超时时间(ms)
+ private $types; //参数类型
+ private $paramNum = 0; //参数个数
+ private $params = array(); //调用$method时的参数
+ private $attach = array();
+
+ /**
+ * @return boolean
+ */
+ public function isTwoWay()
+ {
+ return $this->twoWay;
+ }
+
+ /**
+ * @param boolean $twoWay
+ */
+ public function setTwoWay($twoWay)
+ {
+ $this->twoWay = $twoWay;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getDataLen()
+ {
+ return $this->dataLen;
+ }
+
+ /**
+ * @param mixed $dataLen
+ */
+ public function setDataLen($dataLen)
+ {
+ $this->dataLen = $dataLen;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getRequestLen()
+ {
+ return $this->requestLen;
+ }
+
+ /**
+ * @param mixed $requestLen
+ */
+ public function setRequestLen($requestLen)
+ {
+ $this->requestLen = $requestLen;
+ }
+
+
+
+ /**
+ * @return boolean
+ */
+ public function isHeartbeatEvent()
+ {
+ return $this->heartbeatEvent;
+ }
+
+ /**
+ * @param boolean $heartbeatEvent
+ */
+ public function setHeartbeatEvent($heartbeatEvent)
+ {
+ $this->heartbeatEvent = $heartbeatEvent;
+ }
+
+
+
+
+ /**
+ * @return mixed
+ */
+ public function getSn()
+ {
+ return $this->sn;
+ }
+
+ /**
+ * @param mixed $sn
+ */
+ public function setSn($sn)
+ {
+ $this->sn = $sn;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTimeout()
+ {
+ return $this->timeout;
+ }
+
+ /**
+ * @param mixed $timeout
+ */
+ public function setTimeout($timeout)
+ {
+ $this->timeout = $timeout;
+ }
+
+
+
+ /**
+ * @return mixed
+ */
+ public function getFullData()
+ {
+ return $this->fullData;
+ }
+
+ /**
+ * @param mixed $fullData
+ */
+ public function setFullData($fullData)
+ {
+ $this->fullData = $fullData;
+ }
+
+ /**
+ * @return string
+ */
+ public function getDubboVersion()
+ {
+ return $this->dubboVersion;
+ }
+
+ /**
+ * @param string $dubboVersion
+ */
+ public function setDubboVersion($dubboVersion)
+ {
+ $this->dubboVersion = $dubboVersion;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getService()
+ {
+ return $this->service;
+ }
+
+ /**
+ * @param mixed $service
+ */
+ public function setService($service)
+ {
+ $this->service = $service;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getVersion()
+ {
+ return $this->version;
+ }
+
+ /**
+ * @param mixed $version
+ */
+ public function setVersion($version)
+ {
+ $this->version = $version;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getGroup()
+ {
+ return $this->group;
+ }
+
+ /**
+ * @param mixed $group
+ */
+ public function setGroup($group)
+ {
+ $this->group = $group;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getMethod()
+ {
+ return $this->method;
+ }
+
+ /**
+ * @param mixed $method
+ */
+ public function setMethod($method)
+ {
+ $this->method = $method;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getTypes()
+ {
+ return $this->types;
+ }
+
+ /**
+ * @param mixed $types
+ */
+ public function setTypes($types)
+ {
+ $this->types = $types;
+ }
+
+ /**
+ * @return array
+ */
+ public function getParams()
+ {
+ return $this->params;
+ }
+
+ /**
+ * @param array $params
+ */
+ public function setParams($params)
+ {
+ $this->params = $params;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAttach()
+ {
+ return $this->attach;
+ }
+
+ /**
+ * @param array $attach
+ */
+ public function setAttach($attach)
+ {
+ $this->attach = $attach;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSerialization()
+ {
+ return $this->serialization;
+ }
+
+ /**
+ * @param mixed $serialization
+ */
+ public function setSerialization($serialization)
+ {
+ $this->serialization = $serialization;
+ }
+
+ /**
+ * @return int
+ */
+ public function getParamNum()
+ {
+ return $this->paramNum;
+ }
+
+ /**
+ * @param int $paramNum
+ */
+ public function setParamNum($paramNum)
+ {
+ $this->paramNum = $paramNum;
+ }
+
+
+
+ //用于监控service中各方法的性能
+ public $startTime; //开始处理请求的时间
+ public $endTime; //请求处理结束的时间
+
+ public $host; //记录acc日志中的目标IP
+ public $port; //记录acc日志中的目标端口
+
+ public $reqInfo = null; //swoole框架记录的过载信息
+
+ public $environment = "prod";
+
+
+ public function __toString()
+ {
+ $ret = sprintf("%s:%s:%s->%s", $this->service, $this->version, $this->group, $this->method);
+ return $ret;
+ }
+}
\ No newline at end of file
diff --git a/common/protocol/fsof/DubboResponse.php b/common/protocol/fsof/DubboResponse.php
new file mode 100644
index 0000000..9e3554e
--- /dev/null
+++ b/common/protocol/fsof/DubboResponse.php
@@ -0,0 +1,193 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\common\protocol\fsof;
+
+class DubboResponse
+{
+ const OK = 20;
+ const SERVICE_ERROR = 70; //服务端框架层有异常
+
+ private $fullData; //返回报文(包含消息头)
+
+ //包头字段
+ private $sn; //请求序号
+ private $status = self::OK;
+ private $heartbeatEvent = false;
+ private $serialization;
+ private $len; //数据部分长度
+
+ //包体字段
+ private $responseBody; //包体数据
+ private $result; //调用$method的结果
+ private $errorMsg;
+
+ /**
+ * @return mixed
+ */
+ public function getFullData()
+ {
+ return $this->fullData;
+ }
+
+ /**
+ * @param mixed $fullData
+ */
+ public function setFullData($fullData)
+ {
+ $this->fullData = $fullData;
+ }
+
+ /**
+ * @return int
+ */
+ public function getStatus()
+ {
+ return $this->status;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getLen()
+ {
+ return $this->len;
+ }
+
+ /**
+ * @param mixed $len
+ */
+ public function setLen($len)
+ {
+ $this->len = $len;
+ }
+
+ /**
+ * @param int $status
+ */
+ public function setStatus($status)
+ {
+ $this->status = $status;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isHeartbeatEvent()
+ {
+ return $this->heartbeatEvent;
+ }
+
+ /**
+ * @param boolean $heartbeatEvent
+ */
+ public function setHeartbeatEvent($heartbeatEvent)
+ {
+ $this->heartbeatEvent = $heartbeatEvent;
+ }
+
+
+
+
+ /**
+ * @return mixed
+ */
+ public function getErrorMsg()
+ {
+ return $this->errorMsg;
+ }
+
+ /**
+ * @param mixed $errorMsg
+ */
+ public function setErrorMsg($errorMsg)
+ {
+ $this->errorMsg = $errorMsg;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSn()
+ {
+ return $this->sn;
+ }
+
+ /**
+ * @param mixed $sn
+ */
+ public function setSn($sn)
+ {
+ $this->sn = $sn;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getResult()
+ {
+ return $this->result;
+ }
+
+ /**
+ * @param mixed $result
+ */
+ public function setResult($result)
+ {
+ $this->result = $result;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getSerialization()
+ {
+ return $this->serialization;
+ }
+
+ /**
+ * @param mixed $serialization
+ */
+ public function setSerialization($serialization)
+ {
+ $this->serialization = $serialization;
+ }
+
+ /**
+ * @return mixed
+ */
+ public function getResponseBody()
+ {
+ return $this->responseBody;
+ }
+
+ /**
+ * @param mixed $responseBody
+ */
+ public function setResponseBody($responseBody)
+ {
+ $this->responseBody = $responseBody;
+ }
+
+
+
+ public function __toString()
+ {
+ $ret = sprintf("%s->%s", $this->sn,$this->responseBody);
+ return $ret;
+ }
+}
\ No newline at end of file
diff --git a/common/url/FSOFUrl.php b/common/url/FSOFUrl.php
new file mode 100644
index 0000000..7b5637a
--- /dev/null
+++ b/common/url/FSOFUrl.php
@@ -0,0 +1,294 @@
+<?php
+namespace com\fenqile\fsof\common\url;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class FSOFUrl
+{
+ const URL_SCHEME = 'scheme';
+ const URL_HOST = 'host';
+ const URL_PORT = 'port';
+ const URL_QUERY = 'query';
+ const URL_PATH = 'path';
+
+ const URL_VERSION = 'version';
+ const URL_GROUP = 'group';
+ const URL_SET = 'set';
+ const URL_SERVICE = 'service';
+
+ const URL_APPLICATION = 'application';
+ const URL_CATEGORY = 'category';
+
+ const URL_WEIGHT = 'weight';
+
+ private $originUrl = NULL;
+ private $encodedUrl = NULL;
+ private $zkPath = NULL;
+
+ private $query = NULL;
+ private $schema = NULL;
+ private $host = NULL;
+ private $port = NULL;
+ private $group = NULL;
+ private $set = NULL;
+ private $service = NULL;
+ private $version = NULL;
+ private $params = NULL;
+ private $weight = NULL;
+
+ private $logger;
+
+ public function __construct($urlPara)
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ if(is_string($urlPara))
+ {
+ if(!$this->initByStr($urlPara))
+ {
+ $this->logger->error('FSOFUrl initByStr failed|urlPara:'.$urlPara);
+ throw new \Exception("传入URL非法, FSOFUrl初始化失败");
+ }
+ }
+ else if(is_array($urlPara))
+ {
+ if(!$this->initByArr($urlPara))
+ {
+ $this->logger->error('FSOFUrl initByArr failed|urlPara:'.json_encode($urlPara));
+ throw new \Exception("传入URL非法, FSOFUrl初始化失败");
+ }
+ }
+ else
+ {
+ throw new \Exception("传入URL非法, FSOFUrl初始化失败");
+ }
+ }
+
+ private function initByStr($urlPara)
+ {
+ $this->originUrl = $urlPara;
+
+ $ret = $this->parse($this->originUrl);
+ if(!$ret)
+ {
+ return FALSE;
+ }
+
+ return $this->initByArr($ret);
+ }
+
+ private function initByArr($urlPara)
+ {
+ if( !isset($urlPara[self::URL_SCHEME]) ||
+ !isset($urlPara[self::URL_HOST]) ||
+ !isset($urlPara[self::URL_PORT]))
+ {
+ return FALSE;
+ }
+
+ $this->params = $urlPara;
+ $this->schema = $urlPara[self::URL_SCHEME];
+ $this->host = $urlPara[self::URL_HOST];
+ $this->port = $urlPara[self::URL_PORT];
+ $getArgs = null;
+
+ if(isset($urlPara[self::URL_QUERY]))
+ {
+ $this->query = $urlPara[self::URL_QUERY];
+ parse_str($this->query,$getArgs);
+ $this->params = array_merge($this->params,$getArgs);
+ }
+
+ if (isset ($getArgs))
+ {
+ if(isset($getArgs[self::URL_VERSION]))
+ {
+ $this->version = $getArgs[self::URL_VERSION];
+ }
+
+ if(isset($getArgs[self::URL_GROUP]))
+ {
+ $this->group = $getArgs[self::URL_GROUP];
+ }
+
+ if (array_key_exists(self::URL_WEIGHT,$getArgs))
+ {
+ $this->weight = intval($getArgs[self::URL_WEIGHT]);
+ }
+ else
+ {
+ $this->weight = null;
+ }
+
+ if(isset($getArgs[self::URL_SET]))
+ {
+ $this->set = $getArgs[self::URL_SET];
+ }
+
+ if(isset($urlPara[self::URL_PATH]))
+ {
+ $this->service = ltrim($urlPara[self::URL_PATH], '/');
+ }
+ else if(isset($getArgs[self::URL_SERVICE]))
+ {
+ $this->service = $getArgs[self::URL_SERVICE];
+ }
+ }
+
+ $this->joinUrlStr();
+
+ return true;
+ }
+
+ private function joinUrlStr()
+ {
+ if(empty($this->originUrl))
+ {
+ $this->originUrl = $this->schema.'://'.$this->host.':'.$this->port.'/'.$this->service.'?'.$this->query;
+ $this->encodedUrl = urlencode($this->originUrl);
+ }
+ else
+ {
+ $this->encodedUrl = urlencode($this->originUrl);
+ }
+
+ return true;
+ }
+
+ public function getOriginUrl()
+ {
+ return $this->originUrl;
+ }
+
+ public function getEncodedUrl()
+ {
+ return $this->encodedUrl;
+ }
+
+ public function getHost()
+ {
+ return $this->host;
+ }
+
+ public function getPort()
+ {
+ return $this->port;
+ }
+
+ public function getGroup($defaultValue = NULL)
+ {
+ if(empty($this->group))
+ {
+ return $defaultValue;
+ }
+ else
+ {
+ return $this->group;
+ }
+ }
+
+ public function getSet($defaultValue = NULL)
+ {
+ if(empty($this->set))
+ {
+ return $defaultValue;
+ }
+ else
+ {
+ return $this->set;
+ }
+ }
+
+ public function getVersion($defaultValue = NULL)
+ {
+ if(empty($this->version))
+ {
+ return $defaultValue;
+ }
+ else
+ {
+ return $this->version;
+ }
+ }
+
+ public function getService()
+ {
+ return $this->service;
+ }
+
+ public function getZookeeperPath()
+ {
+ $root = '/dubbo';
+ $providers = 'providers';
+ $this->zkPath = $root.'/'.$this->getService().'/'.$providers.'/'.$this->getEncodedUrl();
+ return $this->zkPath;
+ }
+
+ public function getApplication()
+ {
+ return $this->getparam(self::URL_APPLICATION);
+ }
+
+ public function getCategory()
+ {
+ return $this->getparam(self::URL_CATEGORY);
+ }
+
+ public function getDefaultWeight($weight)
+ {
+ if(null === $this->weight)
+ {
+ return $weight;
+ }
+ else
+ {
+ return $this->weight;
+ }
+ }
+
+ public function getWeight()
+ {
+ return $this->weight;
+ }
+
+ public function setWeight($weight)
+ {
+ $this->weight = $weight;
+ }
+
+ public function getParams($key)
+ {
+ return $this->getparam($key);
+ }
+
+ private function parse($url)
+ {
+ return parse_url($url);
+ }
+
+ private function getparam($key)
+ {
+ if(isset($this->params[$key]))
+ {
+ return $this->params[$key];
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/app/conf/dev/provider/demo-provider.deploy b/config/app/conf/dev/provider/demo-provider.deploy
new file mode 100644
index 0000000..b1f9ded
--- /dev/null
+++ b/config/app/conf/dev/provider/demo-provider.deploy
@@ -0,0 +1,22 @@
+[server]
+; port
+listen[] = 10000
+
+;app log config file path
+log_cfg_file_path = '/root/fsof_php_core/dubbo-php-framework/demo/demo-provider/config/log4php.xml'
+
+;swoole log path
+swoole_log_path = '/var/fsof/provider/example_swoole.log'
+
+; point to app root path's bootstrap.php
+root = '/root/fsof_php_core/dubbo-php-framework/demo/demo-provider/BootStrap.php'
+
+[setting]
+; worker num
+worker_num = 2
+
+; daemonize
+daemonize = true
+
+;max_request
+max_request = 1
diff --git a/config/app/conf/gray/provider/example.deploy b/config/app/conf/gray/provider/example.deploy
new file mode 100644
index 0000000..a502d5e
--- /dev/null
+++ b/config/app/conf/gray/provider/example.deploy
@@ -0,0 +1,20 @@
+[server]
+; port
+listen[] = 10000
+
+;Provider app log level INFO WARN ERROR
+log_level = INFO
+
+;base service log level INFO WARN ERROR
+bslog_level = INFO
+
+; point to app root path's bootstrap.php
+root = '../example/provider/src/BootStrap.php'
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+[setting]
+; worker num
+worker_num = 2
+
+; daemonize
+daemonize = true
\ No newline at end of file
diff --git a/config/app/conf/pre/provider/example.deploy b/config/app/conf/pre/provider/example.deploy
new file mode 100644
index 0000000..c87f999
--- /dev/null
+++ b/config/app/conf/pre/provider/example.deploy
@@ -0,0 +1,22 @@
+[server]
+; port
+listen[] = 10000
+
+;Provider app log level INFO WARN ERROR
+log_level = INFO
+
+;base service log level INFO WARN ERROR
+bslog_level = INFO
+
+; point to app root path's bootstrap.php
+root = '../example/provider/src/BootStrap.php'
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+[setting]
+; worker num
+worker_num = 2
+
+; daemonize
+daemonize = true
+
+
diff --git a/config/app/conf/pro/provider/example.deploy b/config/app/conf/pro/provider/example.deploy
new file mode 100644
index 0000000..a502d5e
--- /dev/null
+++ b/config/app/conf/pro/provider/example.deploy
@@ -0,0 +1,20 @@
+[server]
+; port
+listen[] = 10000
+
+;Provider app log level INFO WARN ERROR
+log_level = INFO
+
+;base service log level INFO WARN ERROR
+bslog_level = INFO
+
+; point to app root path's bootstrap.php
+root = '../example/provider/src/BootStrap.php'
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+[setting]
+; worker num
+worker_num = 2
+
+; daemonize
+daemonize = true
\ No newline at end of file
diff --git a/config/global/conf/fsof.ini b/config/global/conf/fsof.ini
new file mode 100644
index 0000000..aa92cfc
--- /dev/null
+++ b/config/global/conf/fsof.ini
@@ -0,0 +1,34 @@
+[fsof_container_setting]
+;php path
+php = '/usr/bin/php'
+
+;app's user
+user = root
+
+[fsof_setting]
+;machine enviroment:[dev|pre|gray|pro]
+environment = dev
+
+;in p2p mode, provider not register to registry
+p2p_mode = false
+
+;zookeeper log switch: 0.close; 1.error; 2.warn; 3.info; 4.debug
+zklog_level = 0
+
+;zookeepr log path
+zklog_path = '/var/fsof/provider/zookeeper.log'
+
+;zookeeper url list
+zk_url_list = http://127.0.0.1:2181
+
+;provider overload mode switch
+overload_mode = true
+
+;if request wait more than waiting_time before processed, we will lost this quest, unit is micro-second
+waiting_time = 2000
+
+;if overload_number requests trigger overload rule continuous, we will open loss request mode
+overload_number = 5
+
+;how many quest is lost before lost mode is close
+loss_number = 20
\ No newline at end of file
diff --git a/consumer/FSOFConsumer.php b/consumer/FSOFConsumer.php
new file mode 100644
index 0000000..58f06da
--- /dev/null
+++ b/consumer/FSOFConsumer.php
@@ -0,0 +1,113 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer;
+
+use com\fenqile\fsof\consumer\proxy\ProxyFactory;
+
+
+class FSOFConsumer
+{
+ /**
+ * @var boolean Has [FSOFConsumer::init] been called?
+ */
+ protected static $_init = FALSE;
+
+ protected static $_initSetting;
+
+ public static function init(array $settings = NULL)
+ {
+ if (self::isConsumerInit())
+ {
+ // Do not allow execution twice
+ return;
+ }
+ \Logger::getLogger(__CLASS__)->info("consumer cfg:".json_encode($settings, true));
+
+ $consumerRoot = __DIR__;
+ $fsofBootPath = dirname($consumerRoot);
+
+ //加载commom
+ $fsofCommonPath = $fsofBootPath.DIRECTORY_SEPARATOR.'common';
+ require_once($fsofCommonPath.DIRECTORY_SEPARATOR.'BootStrap.php');
+
+ //加载registry
+ $fsofRegistryPath = $fsofBootPath.DIRECTORY_SEPARATOR.'registry';
+ require_once($fsofRegistryPath.DIRECTORY_SEPARATOR.'BootStrap.php');
+
+ //检查输入参数app_src
+ if ((!isset($settings['app_src'])) || (!isset($settings['app_name'])))
+ {
+ throw new \Exception("FSOFConsumer::init传入的app的src路径参数不准确");
+ }
+ $consumerConfigFile = $settings['app_src'];
+ $consumerConfigFile = rtrim($consumerConfigFile, DIRECTORY_SEPARATOR);
+ $consumerConfigFile = $consumerConfigFile.DIRECTORY_SEPARATOR.'consumer'.DIRECTORY_SEPARATOR.$settings['app_name'].'.consumer';
+ if (file_exists($consumerConfigFile))
+ {
+ try
+ {
+ $consumerConfig = parse_ini_file($consumerConfigFile, true);
+ }
+ catch (\Exception $e)
+ {
+ throw new \Exception("consumer配置文件有误[".$consumerConfigFile."]");
+ }
+ }
+ else
+ {
+ $consumerConfig = array();
+ }
+
+ self::$_initSetting = $settings;
+
+ //注册consumer框架的autoLoader
+ self::registerConsumerFrameAutoLoader($consumerRoot);
+
+ //注册consumer的动态代理工厂
+ ProxyFactory::setConsumerConfig($consumerConfig, $consumerConfigFile, $settings);
+
+ // FSOFConsumer is now initialized
+ self::$_init = TRUE;
+ }
+
+ private static function registerConsumerFrameAutoLoader($consumerRoot)
+ {
+ if (!self::isConsumerInit())
+ {
+ //注册框架顶层命名空间到自动加载器
+ require_once $consumerRoot.DIRECTORY_SEPARATOR.'FrameAutoLoader.php';
+ FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer', $consumerRoot);
+ FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer\app', $consumerRoot.DIRECTORY_SEPARATOR.'app');
+ FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer\fsof', $consumerRoot.DIRECTORY_SEPARATOR.'fsof');
+ FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer\proxy', $consumerRoot.DIRECTORY_SEPARATOR.'proxy');
+ FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer\client', $consumerRoot.DIRECTORY_SEPARATOR.'client');
+ spl_autoload_register(__NAMESPACE__.'\FrameAutoLoader::autoload');
+ }
+ }
+
+ public static function getInitSetting()
+ {
+ return self::$_initSetting;
+ }
+
+ public static function isConsumerInit()
+ {
+ return self::$_init;
+ }
+
+}
\ No newline at end of file
diff --git a/consumer/FrameAutoLoader.php b/consumer/FrameAutoLoader.php
new file mode 100644
index 0000000..c87b096
--- /dev/null
+++ b/consumer/FrameAutoLoader.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer;
+
+class FrameAutoLoader
+{
+ /**
+ * 命名空间的路径
+ */
+ private static $nsPath = Array();
+
+ /**
+ * 自动载入类
+ * @param $class
+ */
+ public static function autoload($class)
+ {
+ $frameRootPathDepth = count(explode('\\', __NAMESPACE__));
+ $root = explode('\\', trim($class, '\\'));
+ $pathDepth = count($root);
+ if($pathDepth > $frameRootPathDepth)
+ {
+ $key = $className = $class;
+ if($pathDepth > $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 2);
+ $className = $root[$frameRootPathDepth + 1];
+ unset($root[$frameRootPathDepth + 1]);
+ $key = implode($root, '\\');
+ }
+ else if($pathDepth == $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 1);
+ $className = $root[$frameRootPathDepth];
+ unset($root[$frameRootPathDepth]);
+ $key = implode($root, '\\');
+ }
+
+ if(isset(self::$nsPath[$key]))
+ {
+ include_once self::$nsPath[$key] . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
+ }
+ }
+ }
+
+ /**
+ * 设置根命名空间
+ * @param $root
+ * @param $path
+ */
+ public static function setRootNS($root, $path)
+ {
+ self::$nsPath[$root] = $path;
+ }
+}
\ No newline at end of file
diff --git a/consumer/client/FSOFClient4Linux.php b/consumer/client/FSOFClient4Linux.php
new file mode 100644
index 0000000..8416989
--- /dev/null
+++ b/consumer/client/FSOFClient4Linux.php
@@ -0,0 +1,91 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer\client;
+
+class FSOFClient4Linux extends IFSOFClient
+{
+ private $logger;
+ public function __construct()
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ if (extension_loaded('swoole'))
+ {
+ try
+ {
+ $this->client = new \swoole_client(SWOOLE_SOCK_TCP);
+ $this->client->set(array(
+ 'open_length_check' => TRUE,
+ 'package_length_offset' => 12, //第N个字节是包长度的值
+ 'package_body_offset' => 16, //第几个字节开始计算长度
+ 'package_length_type' => 'N',
+ 'package_max_length' => 1024 * 1024 * 5, //TCP协议最大长度为2M,暂定5M
+ ));
+ }
+ catch (\Exception $e)
+ {
+ $this->logger->error("not installed swoole extension", $e);
+ throw new \Exception("该环境未安装swoole扩展");
+ }
+ }
+ else
+ {
+ $this->logger->error("not installed swoole extension");
+ throw new \Exception("该环境未安装swoole扩展");
+ }
+ }
+
+ public function connect($ipAddr, $port, $ioTimeOut)
+ {
+ try
+ {
+ return $this->client->connect($ipAddr, $port, $ioTimeOut);
+ }
+ catch (\Exception $e)
+ {
+ $this->logger->error("connect FSOF server[".$ipAddr.":".$port ."] failed", $e);
+ return false;
+ }
+ }
+
+ public function send($data, $len)
+ {
+ return $this->client->send($data);
+ }
+
+ public function recv($len = 65535)
+ {
+ return $this->client->recv($len, true);
+ }
+
+ public function close($force = false)
+ {
+ try
+ {
+ $this->client->close($force);
+ }
+ catch (\Exception $e)
+ {
+ $this->logger->error("close exception", $e);
+ }
+ }
+
+ public function getlasterror()
+ {
+ return $this->client->errCode;
+ }
+}
\ No newline at end of file
diff --git a/consumer/client/IFSOFClient.php b/consumer/client/IFSOFClient.php
new file mode 100644
index 0000000..3109dfb
--- /dev/null
+++ b/consumer/client/IFSOFClient.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer\client;
+
+abstract class IFSOFClient
+{
+ protected $client;
+
+ abstract public function connect($ipAddr, $port, $ioTimeOut);
+
+ abstract public function send($data, $len);
+
+ abstract public function recv($len = 65535);
+
+ abstract public function close($force = false);
+
+ abstract public function getlasterror();
+}
\ No newline at end of file
diff --git a/consumer/fsof/FSOFProcessor.php b/consumer/fsof/FSOFProcessor.php
new file mode 100644
index 0000000..1c1cb87
--- /dev/null
+++ b/consumer/fsof/FSOFProcessor.php
@@ -0,0 +1,342 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer\fsof;
+
+use com\fenqile\fsof\common\config\FSOFConstants;
+use com\fenqile\fsof\common\protocol\fsof\DubboParser;
+use com\fenqile\fsof\common\protocol\fsof\DubboRequest;
+use com\fenqile\fsof\common\protocol\fsof\DubboResponse;
+use com\fenqile\fsof\consumer\client\FSOFClient4Linux;
+
+class FSOFProcessor
+{
+ const FSOF_CONNECTION_RESET = 104;
+
+ const FSOF_ETIMEOUT = 110;
+ const FSOF_EINPROGRESS = 115;
+ const FSOF_ECONNREFUSED = 111;
+
+ protected $parser;
+
+ private $logger;
+
+ public function __construct()
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ $this->parser = new DubboParser();
+ }
+
+ public function executeRequest(DubboRequest $request, $svrAddr, $ioTimeOut, &$providerAddr)
+ {
+ //计算服务端个数
+ $svrNum = count($svrAddr);
+ //连接异常重试次数最多2次
+ $connect_try_times = ($svrNum > 2) ? 2 : $svrNum;
+ $client = NULL;
+ for ($i = 0; $i < $connect_try_times; $i++)
+ {
+ try
+ {
+ //获取路由下标
+ $col = mt_rand(0, $svrNum-1);
+ $svrUrl = $svrAddr[$col];
+ $host = $svrUrl->getHost();
+ $port = $svrUrl->getPort();
+
+ //记录路由信息
+ $providerAddr = $host.':'.$port;
+
+ //透传到服务端字段
+ $request->host = $host;
+ $request->port = $port;
+ $request->setGroup($svrUrl->getGroup(FSOFConstants::FSOF_SERVICE_GROUP_ANY));
+ $request->setVersion( $svrUrl->getVersion(FSOFConstants::FSOF_SERVICE_VERSION_DEFAULT));
+ $request->setTimeout($ioTimeOut * 1000);
+
+ $client = $this->connectProvider($host, $port, $ioTimeOut);
+ if(empty($client))
+ {
+ //记录连接错误日志
+ $this->logger->error("connect FSOF server[".$host.":".$port ."] failed");
+ //删除无用地址信息
+ $svrAddr[$col] = NULL;
+ $svrAddr = array_filter($svrAddr);
+ if(self::FSOF_ECONNREFUSED == $this->lastErrorNo)
+ {
+ //连接拒绝
+ continue;
+ }
+ else if(self::FSOF_ETIMEOUT == $this->lastErrorNo || self::FSOF_EINPROGRESS == $this->lastErrorNo)
+ {
+ //连接超时
+ break;
+ }
+ else
+ {
+ //其他错误
+ continue;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ catch (\Exception $e)
+ {
+ if (!empty($client))
+ {
+ unset($client);
+ }
+ $this->logger->error($e->getMessage(), $e);
+ }
+ }
+
+ //与服务端进行交互
+ $ret = NULL;
+ if(isset($client))
+ {
+ try
+ {
+ $data = $this->parser->packRequest($request);
+ $dataLen = strlen($data);
+ if(!$client->send($data, $dataLen))
+ {
+ $client->close(true);
+ unset($client);
+ $msg = json_encode($request->__toString(), JSON_UNESCAPED_UNICODE);
+ if (mb_strlen($msg, 'UTF-8') >= 512)
+ {
+ $msg = mb_substr($msg, 0, 512, 'UTF-8').' ...(len:'.strlen($msg).")";
+ }
+ $this->logger->error("send date failed:" . $msg);
+ throw new \Exception("发送请求数据失败");
+ }
+ }
+ catch (\Exception $e)
+ {
+ $client->close(true);
+ unset($client);
+ $msg = json_encode($request->__toString(), JSON_UNESCAPED_UNICODE);
+ if (mb_strlen($msg, 'UTF-8') >= 512)
+ {
+ $msg = mb_substr($msg, 0, 512, 'UTF-8').' ...(len:'.strlen($msg).")";
+ }
+ $this->logger->error("send date failed:" . $msg, $e);
+ throw new \Exception("发送请求数据失败");
+ }
+
+ try
+ {
+ $ret = $this->recvDataFromProvider($client, $request);
+ $client->close();
+ unset($client);
+ }
+ catch (\Exception $e)
+ {
+ $client->close(true);
+ unset($client);
+ throw $e;
+ }
+ }
+ else
+ {
+ throw new \Exception("与服务器建立连接失败");
+ }
+ return $ret;
+ }
+
+ protected function connectProvider($host, $port, $iotimeout)
+ {
+ try
+ {
+ $start_time = microtime(true);//取到微秒
+
+ $client = new FSOFClient4Linux();
+
+ if ($client->connect($host,$port,$iotimeout))
+ {
+ $this->logger->debug('connect to server['.$host.":".$port."] success,timeout:".$iotimeout);
+ }
+ else
+ {
+ //记录错误码
+ $this->lastErrorNo = $client->getlasterror();
+ $cost_time = (int)((microtime(true) - $start_time) * 1000000);
+ $this->logger->error('connect to server['.$host.":".$port."] failed,timeout:".$iotimeout."|".$cost_time."us".'|errcode:'.$this->lastErrorNo);
+ unset($client);
+ }
+ }
+ catch (\Exception $e)
+ {
+ unset($client);
+ $this->logger->error("Connect provider exception:",$e);
+ }
+
+ if (isset($client))
+ {
+ return $client;
+ }
+ else
+ {
+ return NULL;
+ }
+ }
+
+ protected function recvDataFromProvider($socket, DubboRequest $request)
+ {
+ $fsof_data = $this->Recv($socket, DubboParser::PACKAGE_HEDA_LEN);
+ if (!$fsof_data)
+ {
+ if (0 == $socket->getlasterror())
+ {
+ throw new \Exception("provider端己关闭网络连接");
+ }
+ else
+ {
+ throw new \Exception("接收应答数据超时");
+ }
+ }
+
+ //解析头
+ $response = new DubboResponse();
+ $response->setFullData($fsof_data);
+ $response = $this->parser->parseResponseHeader($response);
+ if (($response) && ($response->getSn() != $request->getSn()))
+ {
+ $this->logger->error("response sn[{$response->getSn()}] != request sn[{$request->getSn()}]");
+ throw new \Exception("请求包中的sn非法");
+ }
+
+ //接收消息体
+ $resData = substr($response->getFullData(), DubboParser::PACKAGE_HEDA_LEN);
+ if ($resData)
+ {
+ $resDataLen = strlen($resData);
+ }
+ else
+ {
+ $resDataLen = 0;
+ }
+
+ if ($resDataLen < $response->getLen())
+ {
+ //取到微秒
+ $start_time = microtime(true);
+ //如果长度超过1M,则分包处理,以1M为单位分包
+ $resv_len = $response->getLen() - $resDataLen;
+ $cur_len = 0;
+ $recv_data = '';
+ do
+ {
+ if (DubboParser::MAX_RECV_LEN > $resv_len)
+ {
+ $cur_len = $resv_len;
+ }
+ else
+ {
+ $cur_len = DubboParser::MAX_RECV_LEN;
+ }
+ $tmpdata = $this->Recv($socket, $cur_len);
+ if ($tmpdata)
+ {
+ $recv_data = $recv_data . $tmpdata;
+ $resv_len -= $cur_len;
+ }
+ else
+ {
+ if (0 == $socket->getlasterror())
+ {
+ throw new \Exception("provider端己关闭网络连接");
+ }
+ else
+ {
+ throw new \Exception("接收应答数据超时");
+ }
+ }
+ //如果超过15秒就当超时处理
+ if ((microtime(true) - $start_time) > 15)
+ {
+ $this->logger->error("Multi recv {$resv_len} bytes data timeout");
+ throw new \Exception("接收应答数据超时");
+ }
+ } while ($resv_len > 0);
+
+ $response->setFullData($response->getFullData() . $recv_data);
+ }
+
+ if ($this->parser->parseResponseBody($response))
+ {
+ if(DubboResponse::OK != $response->getStatus())
+ {
+ throw new \Exception($response->getErrorMsg());
+ }
+ else
+ {
+ return $response->getResult();
+ }
+ }
+ else
+ {
+ $this->logger->error("parse response body err:".$response->__toString());
+ throw new \Exception("未知异常");
+ }
+ }
+
+ protected function Recv($socket, $len)
+ {
+ try
+ {
+ $resv_len = $len;
+ $_data = '';
+ $cnt = 20;//最多循环20次,防止provider端挂掉时,consumer陷入死循环
+ do
+ {
+ $cnt--;
+ $tmp_data = $socket->recv($resv_len);
+ if (!$tmp_data)
+ {
+ $this->logger->warn("socket->recv faile:$resv_len");
+ break;
+ }
+ $_data = $_data . $tmp_data;
+ $resv_len -= strlen($tmp_data);
+ } while (($resv_len > 0) && ($cnt > 0));
+
+ if ($resv_len > 0)
+ {
+ $this->logger->error("Recv $len data fail!");
+ return FALSE;
+ }
+
+ return $_data;
+ }
+ catch (\Exception $e)
+ {
+ $this->logger->error('recv data exception',$e);
+ if(self::FSOF_CONNECTION_RESET == $e->getCode())
+ {
+ throw new \Exception("未知异常");
+ }
+ else
+ {
+ throw new \Exception("接收应答数据超时");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/consumer/proxy/Proxy.php b/consumer/proxy/Proxy.php
new file mode 100644
index 0000000..cecadce
--- /dev/null
+++ b/consumer/proxy/Proxy.php
@@ -0,0 +1,144 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer\proxy;
+
+use com\fenqile\fsof\common\protocol\fsof\DubboRequest;
+use com\fenqile\fsof\consumer\fsof\FSOFProcessor;
+
+
+final class Proxy
+{
+ private $appName;
+
+ private $group;
+
+ private $serviceInterface;
+
+ private $serviceAddress = array();
+
+ private $fsofProcessor;
+
+ //默认超时时间为3s
+ private $ioTimeOut = 3;
+
+ private $logger;
+
+ public static function newProxyInstance($serviceInterfaces, $appName, $group)
+ {
+ return new Proxy($serviceInterfaces, $appName, $group);
+ }
+
+ public function __construct($serviceInterfaces, $appName, $group)
+ {
+ $this->logger = \Logger::getLogger(__CLASS__);
+ $this->serviceInterface = $serviceInterfaces;
+ $this->appName = $appName;
+ $this->group = $group;
+ $this->fsofProcessor = new FSOFProcessor();
+ }
+
+ public function setIOTimeOut($ioTimeOut)
+ {
+ $this->ioTimeOut = $ioTimeOut;
+ }
+
+ public function setAddress($serviceAddr = array())
+ {
+ //删除旧的地址
+ unset($this->serviceAddress);
+ $this->serviceAddress = $serviceAddr;
+ }
+
+ public function getAddressStr()
+ {
+ $ret = "";
+ foreach ($this->serviceAddress as $index => $url)
+ {
+ $ret .= $url->getHost().':'.$url->getPort().';';
+ }
+ return $ret;
+ }
+
+ protected function generatePackageSN()
+ {
+ srand((double)microtime() * 1000000);
+ $rand_number = rand();
+ return $rand_number;
+ }
+
+ protected function generateParamType($num)
+ {
+ $types = array();
+ for($i = 0; $i < $num; $i++)
+ {
+ $types[] = 'Ljava/lang/Object;';
+ }
+ return $types;
+ }
+
+ protected function trimParams($params)
+ {
+ if(count($params) == 1 && empty($params[0]))
+ {
+ return null;
+ }
+ return $params;
+ }
+
+ public function __call($name, $args)
+ {
+ $result = NULL;
+ $method = null;
+ $providerAddress = NULL;
+ $request = new DubboRequest();
+ //取到微秒
+ $begin_time = microtime(true);
+ $this->logger->debug("in|consumer_app:{$this->appName}|service:{$this->serviceInterface}|timout:{$this->ioTimeOut}|name:{$name}");
+ try {
+ $request->setSn($this->generatePackageSN());
+ $request->setService($this->serviceInterface);
+ $request->setMethod($args[0]);
+ array_shift($args);
+ $request->setParams($args);
+ $request->setTypes($this->generateParamType(count($request->getParams())));
+ $result = $this->fsofProcessor->executeRequest($request, $this->serviceAddress, $this->ioTimeOut, $providerAddress);
+ }catch (\Exception $e) {
+ $cost_time = (int)((microtime(true) - $begin_time) * 1000000);
+ //记录consumer接口告警日志
+ $this->setAccLog($request, $cost_time, $e->getMessage());
+ throw $e;
+ }
+ $cost_time = (int)((microtime(true) - $begin_time) * 1000000);
+ //记录consumer接口告警日志
+ $this->setAccLog($request, $cost_time, "ok");
+ return $result;
+ }
+
+ protected function setAccLog($request, $costTime, $errMsg='ok')
+ {
+ //时间|服务名|耗时(us)|返回码|应用名|方法名|目标服务group|目标服务version|目标机器ip:port|备注
+ $accLog = sprintf("%s|%d|%d|%s|%s|%s|%s|%s", $request->getService(), $costTime,
+ $this->appName,
+ $request->getMethod(),
+ $request->getGroup(),
+ $request->getVersion(),
+ $request->host . ':' . $request->port,
+ $errMsg);
+ $this->logger->debug($accLog);
+ }
+}
\ No newline at end of file
diff --git a/consumer/proxy/ProxyFactory.php b/consumer/proxy/ProxyFactory.php
new file mode 100644
index 0000000..d1c3c4e
--- /dev/null
+++ b/consumer/proxy/ProxyFactory.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\consumer\proxy;
+
+use com\fenqile\fsof\common\url\FSOFUrl;
+use com\fenqile\fsof\common\log\FSOFSystemUtil;
+use com\fenqile\fsof\common\config\FSOFConstants;
+use com\fenqile\fsof\common\config\FSOFCommonUtil;
+use com\fenqile\fsof\registry\automatic\ConsumerProxy;
+
+
+final class ProxyFactory
+{
+ /**
+ * @var App加载的配置文件路径
+ */
+ protected static $appConfigFile = '';
+
+ /**
+ * @var p2p模式开关
+ */
+ protected static $p2pMode = FALSE;
+
+ /**
+ * @var app所属的group
+ */
+ protected static $appGroup = FSOFConstants::FSOF_SERVICE_GROUP_ANY;
+
+ /**
+ * @var app所属的version
+ */
+ protected static $appVersion = FSOFConstants::FSOF_SERVICE_VERSION_DEFAULT;
+
+ /**
+ * @var appname
+ */
+ protected static $appName = 'consumer';
+
+ /**
+ * @var instance array
+ */
+ protected static $serviceInstances = array();
+
+ /**
+ * @var *.consumer中配置的service信息
+ */
+ protected static $serviceConsumers = array();
+
+ private static $logger;
+
+ public static function setConsumerConfig($configData, $consumerConfigFile, $initSettings)
+ {
+ self::$logger = \Logger::getLogger(__CLASS__);
+
+ //App名字
+ self::$appName = $initSettings['app_name'];
+
+ //获取app加载配置文件;输出日志方便问题定位
+ self::$appConfigFile = $consumerConfigFile;
+
+ //是否开启p2p模式, p2p模式下, 将使用[consumer_services]下的信息进行路由
+ if(isset($configData['consumer_config']['p2p_mode']) && $configData['consumer_config']['p2p_mode'])
+ {
+ self::$p2pMode = $configData['consumer_config']['p2p_mode'];
+ }
+
+ if (isset($configData['consumer_config']['group']))
+ {
+ self::$appGroup = $configData['consumer_config']['group'];
+ }
+
+ if(isset($configData['consumer_config']['version']))
+ {
+ self::$appVersion = $configData['consumer_config']['version'];
+ }
+
+ if(isset($configData['consumer_services']))
+ {
+ self::$serviceConsumers = $configData['consumer_services'];
+ }
+ }
+
+ //use automatic registry
+ private static function getInstancByRedis($service, $ioTimeOut, $version, $group)
+ {
+ $ret = NULL;
+ $providerInfo = ConsumerProxy::instance()->getProviders($service, $version, $group);
+ if(!empty($providerInfo))
+ {
+ $cacheKey = $service.':'.$version.':'.$group;
+ if(empty(self::$serviceInstances[$cacheKey]))
+ {
+ $ret = Proxy::newProxyInstance($service, self::$appName, $group);
+ self::$serviceInstances[$cacheKey] = $ret;
+ }
+ else
+ {
+ $ret = self::$serviceInstances[$cacheKey];
+ }
+
+ //设置io超时时间
+ $ret->setIOTimeOut($ioTimeOut);
+
+ //设置地址列表
+ $ret->setAddress($providerInfo);
+ }
+ else
+ {
+ self::$logger->error("not find providers form redis for $service:$version:$group");
+ }
+
+ return $ret;
+ }
+
+ //use p2p mode
+ private static function getInstanceByP2P($service, $ioTimeOut, $version, $group)
+ {
+ $ret = NULL;
+ if(array_key_exists($service, self::$serviceConsumers))
+ {
+ $serviceProperty = self::$serviceConsumers[$service];
+ if (isset($serviceProperty['url']))
+ {
+ $ret = Proxy::newProxyInstance($service, self::$appName, $group);
+
+ //设置io超时时间
+ $ret->setIOTimeOut($ioTimeOut);
+
+ //设置所用服务的ip地址列表
+ $serviceAddr = explode(",",$serviceProperty['url']);
+ $serviceUrls = array();
+ foreach ($serviceAddr as $index => $addr)
+ {
+ $tmpUrl = $addr.'/'."{$service}?version={$version}&group={$group}";
+ $serviceUrls[] = new FSOFUrl($tmpUrl);
+ }
+ $ret->setAddress($serviceUrls);
+ }
+ else
+ {
+ self::$logger->warn(self::$appName.'.consumer not exist url');
+ }
+ }
+ else
+ {
+ self::$logger->warn('service not found on p2p|consumer_app:'.self::$appName.'|provider_service:'.$service);
+ }
+
+ return $ret;
+ }
+
+ public static function getInstance($consumerInterface, $ioTimeOut = 3)
+ {
+ $ret = NULL;
+ $route = '';
+ $addressList = 'null';
+
+ //app级组和版本信息
+ $group = self::$appGroup;
+ $versionList = self::$appVersion;
+
+ if (array_key_exists($consumerInterface, self::$serviceConsumers))
+ {
+ $serviceProperty = self::$serviceConsumers[$consumerInterface];
+ if(isset($serviceProperty['group']))
+ {
+ $group = $serviceProperty['group'];
+ }
+ if(isset($serviceProperty['version']))
+ {
+ $versionList = $serviceProperty['version'];
+ }
+ }
+
+ try
+ {
+
+ //依据配置权重选取版本号
+ $version = FSOFCommonUtil::getVersionByWeight($versionList);
+
+ //p2p模式
+ if (self::$p2pMode)
+ {
+ $ret = self::getInstanceByP2P($consumerInterface, $ioTimeOut, $version, $group);
+ $route = 'p2p';
+ }
+
+ if (empty($ret))
+ {
+ //registry 模式
+ $ret = self::getInstancByRedis($consumerInterface, $ioTimeOut, $version, $group);
+ $route = 'auto registry';
+ }
+
+ if (empty($ret))
+ {
+ $errMsg = "current_address:".FSOFSystemUtil::getLocalIP()."|".$consumerInterface;
+ throw new \Exception($errMsg);
+ }
+ else
+ {
+ $addressList = $ret->getAddressStr();
+ }
+ self::$logger->debug('consumer_app:'.self::$appName.'|app_config_file:'.self::$appConfigFile.
+ '|version:'.$version.'|group:'.$group.'|provider_service:'.$consumerInterface.'|route:'.$route.'|addr_list:'.$addressList.'|timeout:'.$ioTimeOut);
+ }
+ catch (\Exception $e)
+ {
+ self::$logger->error('consumer_app:'.self::$appName.'|app_config_file:'.self::$appConfigFile.
+ '|version:'.$version.'|group:'.$group.'|provider_service:'.$consumerInterface.'|errmsg:'. $e->getMessage().'|exceptionmsg:'.$e);
+ }
+ return $ret;
+ }
+}
\ No newline at end of file
diff --git a/demo/demo-consumer/config/log4php.xml b/demo/demo-consumer/config/log4php.xml
new file mode 100644
index 0000000..7e89f2f
--- /dev/null
+++ b/demo/demo-consumer/config/log4php.xml
@@ -0,0 +1,12 @@
+<configuration xmlns="http://logging.apache.org/log4php/">
+ <appender name="myAppender" class="LoggerAppenderFile">
+ <param name="file" value="/tmp/consumer.log" />
+ <layout class="LoggerLayoutPattern">
+ <param name="conversionPattern" value="[%date{Y-m-d H:i:s,u}][%level][%logger][%line] %message%newline" />
+ </layout>
+ </appender>
+ <root>
+ <level value="DEBUG" />
+ <appender_ref ref="myAppender" />
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/demo/demo-consumer/consumer/demo-consumer.consumer b/demo/demo-consumer/consumer/demo-consumer.consumer
new file mode 100644
index 0000000..8e007d8
--- /dev/null
+++ b/demo/demo-consumer/consumer/demo-consumer.consumer
@@ -0,0 +1,10 @@
+[consumer_config]
+p2p_mode = false
+
+[consumer_services]
+
+com.fenqile.example.DemoService[group] = *
+com.fenqile.example.DemoService[version] = 1.0.0
+
+com.fenqile.arch.dubbo.service.DemoPhpService[group] = *
+com.fenqile.arch.dubbo.service.DemoPhpService[version] = 1.0.0
\ No newline at end of file
diff --git a/demo/demo-consumer/server/Consumer.php b/demo/demo-consumer/server/Consumer.php
new file mode 100644
index 0000000..fa895d0
--- /dev/null
+++ b/demo/demo-consumer/server/Consumer.php
@@ -0,0 +1,15 @@
+<?php
+$APP_SRC_PATH = __DIR__;
+$fsofApiPath = dirname(dirname(dirname($APP_SRC_PATH))).DIRECTORY_SEPARATOR.'api'.DIRECTORY_SEPARATOR.'FSOFApi.php';
+require_once($fsofApiPath);
+FSOFApi::configure('demo-consumer', $APP_SRC_PATH);
+
+require_once('log4php/Logger.php');
+Logger::configure(dirname(dirname(__FILE__)).'/config/log4php.xml');
+date_default_timezone_set('PRC');
+
+//php框架调用php提供的服务
+$service = 'com.fenqile.example.DemoService';
+$proxy = FSOFApi::newProxy($service, 3);
+$ret = $proxy->invoke("sayHello","zhangsan");
+echo "ret:$ret".PHP_EOL;
diff --git a/demo/demo-provider/BootStrap.php b/demo/demo-provider/BootStrap.php
new file mode 100644
index 0000000..a38b939
--- /dev/null
+++ b/demo/demo-provider/BootStrap.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This file must exist and put in the project root directory,
+ * The framework automatically include files and folders at its level.
+ * If you need to include the files of other directories, please include in this file
+ */
+//require_once 'other dirname file';
\ No newline at end of file
diff --git a/demo/demo-provider/config/log4php.xml b/demo/demo-provider/config/log4php.xml
new file mode 100644
index 0000000..4916ea7
--- /dev/null
+++ b/demo/demo-provider/config/log4php.xml
@@ -0,0 +1,12 @@
+<configuration xmlns="http://logging.apache.org/log4php/">
+ <appender name="myAppender" class="LoggerAppenderFile">
+ <param name="file" value="/tmp/provider.log" />
+ <layout class="LoggerLayoutPattern">
+ <param name="conversionPattern" value="[%date{Y-m-d H:i:s,u}][%level][%logger][%line] %message%newline" />
+ </layout>
+ </appender>
+ <root>
+ <level value="INFO" />
+ <appender_ref ref="myAppender" />
+ </root>
+</configuration>
\ No newline at end of file
diff --git a/demo/demo-provider/provider/demo-provider.provider b/demo/demo-provider/provider/demo-provider.provider
new file mode 100644
index 0000000..1ed5f94
--- /dev/null
+++ b/demo/demo-provider/provider/demo-provider.provider
@@ -0,0 +1,9 @@
+[service_properties]
+owner = author
+version = 1.0.0
+group = default
+
+[service_providers]
+com.fenqile.example.DemoService[service] = DemoServiceImpl
+com.fenqile.example.DemoService[version] = 1.0.0
+com.fenqile.example.DemoService[group] = default
\ No newline at end of file
diff --git a/demo/demo-provider/server/DemoServiceImpl.php b/demo/demo-provider/server/DemoServiceImpl.php
new file mode 100644
index 0000000..77930d0
--- /dev/null
+++ b/demo/demo-provider/server/DemoServiceImpl.php
@@ -0,0 +1,18 @@
+<?php
+/**
+ * Version: 1.0.0
+ * Description: 示例程序,模拟server工程下的各个service
+ */
+
+require_once('log4php/Logger.php');
+Logger::configure(dirname(dirname(__FILE__)).'/config/log4php.xml');
+date_default_timezone_set('PRC');
+
+class DemoServiceImpl
+{
+ public function sayHello($name)
+ {
+ \Logger::getLogger(__CLASS__)->info("Hello $name");
+ return "Hello $name";
+ }
+}
diff --git a/provider/FSOFProvider.php b/provider/FSOFProvider.php
new file mode 100644
index 0000000..7eab85a
--- /dev/null
+++ b/provider/FSOFProvider.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider;
+
+require_once(FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'FrameAutoLoader.php');
+
+//加载commom
+$fsofCommonPath = FSOF_FRAMEWORK_ROOT_PATH.DIRECTORY_SEPARATOR.'common';
+require_once($fsofCommonPath.DIRECTORY_SEPARATOR.'BootStrap.php');
+
+//加载registry
+$fsofRegistryPath = FSOF_FRAMEWORK_ROOT_PATH.DIRECTORY_SEPARATOR.'registry';
+require_once $fsofRegistryPath.DIRECTORY_SEPARATOR.'BootStrap.php';
+
+//注册顶层命名空间到自动载入器
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider', FSOF_PROVIDER_ROOT_PATH);
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider\core', FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'core');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider\fsof', FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'fsof');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider\shell', FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'shell');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider\common', FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'common');
+FrameAutoLoader::setRootNS('com\fenqile\fsof\provider\monitor', FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'monitor');
+//provider启动时初始化consumer
+FrameAutoLoader::setRootNS('com\fenqile\fsof\consumer', FSOF_FRAMEWORK_ROOT_PATH.DIRECTORY_SEPARATOR.'consumer');
+spl_autoload_register(__NAMESPACE__.'\FrameAutoLoader::autoload');
+
+//注册全局异常捕获器
+function exceptionHandler($exception)
+{
+ $exceptionHash = array(
+ 'className' => 'Exception',
+ 'message' => $exception->getMessage(),
+ 'code' => $exception->getCode(),
+ 'file' => $exception->getFile(),
+ 'line' => $exception->getLine(),
+ 'trace' => array(),
+ );
+
+ $traceItems = $exception->getTrace();
+ foreach ($traceItems as $traceItem)
+ {
+ $traceHash = array(
+ 'file' => isset($traceItem['file']) ? $traceItem['file'] : 'null',
+ 'line' => isset($traceItem['line']) ? $traceItem['line'] : 'null',
+ 'function' => isset($traceItem['function']) ? $traceItem['function'] : 'null',
+ 'args' => array(),
+ );
+
+ if (!empty($traceItem['class']))
+ {
+ $traceHash['class'] = $traceItem['class'];
+ }
+
+ if (!empty($traceItem['type']))
+ {
+ $traceHash['type'] = $traceItem['type'];
+ }
+
+ if (!empty($traceItem['args']))
+ {
+ foreach ($traceItem['args'] as $argsItem)
+ {
+ $traceHash['args'][] = \var_export($argsItem, true);
+ }
+ }
+
+ $exceptionHash['trace'][] = $traceHash;
+ }
+
+ \Logger::getLogger(__CLASS__)->error(print_r($exceptionHash, true));
+}
+set_exception_handler(__NAMESPACE__ . '\exceptionHandler');
\ No newline at end of file
diff --git a/provider/FrameAutoLoader.php b/provider/FrameAutoLoader.php
new file mode 100644
index 0000000..b5c5c48
--- /dev/null
+++ b/provider/FrameAutoLoader.php
@@ -0,0 +1,70 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider;
+
+class FrameAutoLoader
+{
+ /**
+ * 命名空间的路径
+ */
+ private static $nsPath = Array();
+
+ /**
+ * 自动载入类
+ * @param $class
+ */
+ public static function autoload($class)
+ {
+ $frameRootPathDepth = count(explode('\\', __NAMESPACE__));
+ $root = explode('\\', trim($class, '\\'));
+ $pathDepth = count($root);
+ if($pathDepth > $frameRootPathDepth)
+ {
+ $key = $className = $class;
+ if($pathDepth > $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 2);
+ $className = $root[$frameRootPathDepth + 1];
+ unset($root[$frameRootPathDepth + 1]);
+ $key = implode($root, '\\');
+ }
+ else if($pathDepth == $frameRootPathDepth + 1)
+ {
+ $root = explode('\\', trim($class, '\\'), $frameRootPathDepth + 1);
+ $className = $root[$frameRootPathDepth];
+ unset($root[$frameRootPathDepth]);
+ $key = implode($root, '\\');
+ }
+
+ if(isset(self::$nsPath[$key]))
+ {
+ include_once self::$nsPath[$key] . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $className) . '.php';
+ }
+ }
+ }
+
+ /**
+ * 设置根命名空间
+ * @param $root
+ * @param $path
+ */
+ public static function setRootNS($root, $path)
+ {
+ self::$nsPath[$root] = $path;
+ }
+}
\ No newline at end of file
diff --git a/provider/common/Console.php b/provider/common/Console.php
new file mode 100644
index 0000000..841f6fe
--- /dev/null
+++ b/provider/common/Console.php
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\common;
+
+class Console
+{
+ /**
+ * 改变进程的用户ID
+ * @param $user
+ */
+ public static function changeUser($user)
+ {
+ if (!function_exists('posix_getpwnam'))
+ {
+ trigger_error(__METHOD__.": require posix extension.");
+ return;
+ }
+ $user = posix_getpwnam($user);
+ if($user)
+ {
+ posix_setuid($user['uid']);
+ posix_setgid($user['gid']);
+ }
+ }
+
+ public static function setProcessName($name)
+ {
+ if (function_exists('cli_set_process_title'))
+ {
+ cli_set_process_title($name);
+ }
+ else if(function_exists('swoole_set_process_name'))
+ {
+ swoole_set_process_name($name);
+ }
+ else
+ {
+ trigger_error(__METHOD__." failed. require cli_set_process_title or swoole_set_process_name.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/provider/core/app/AppAutoLoader.php b/provider/core/app/AppAutoLoader.php
new file mode 100644
index 0000000..14a983a
--- /dev/null
+++ b/provider/core/app/AppAutoLoader.php
@@ -0,0 +1,129 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\app;
+
+class AppAutoLoader
+{
+ private static $root_path = array();
+ private static $class_autoload_path = array();
+
+ /**
+ * [set_include_path 完成根目录下目录的扫描,支持多层目录嵌套]
+ * @param [type] $dir [路径]
+ * @return array
+ */
+ private static function set_include_path($dir)
+ {
+ $include_paths = array();
+ $include_paths[] = $dir;
+
+ //读取所有的文件夹目录
+ $arr = scandir($dir);
+ $len = count($arr);
+ for ($i=0; $i < $len; $i++)
+ {
+ //.和..去掉
+ if (('.' == $arr[$i]) || ('..' == $arr[$i]))
+ {
+ continue;
+ }
+ if(is_dir($dir.DIRECTORY_SEPARATOR.$arr[$i]))
+ {
+ $include_paths = array_merge($include_paths, self::set_include_path($dir.DIRECTORY_SEPARATOR.$arr[$i]));
+ }
+ }
+
+ return array_unique($include_paths);
+ }
+
+ /**
+ * [auto_load]
+ * @param [type] $className [文件名]
+ * @return [type] [description]
+ */
+ public static function auto_load($className)
+ {
+ $newClassName = str_replace('\\', DIRECTORY_SEPARATOR, trim($className, '\\'));
+ foreach (self::$class_autoload_path as $key => $path)
+ {
+ $class_file = $path.DIRECTORY_SEPARATOR.$newClassName.".php";
+ if (is_file($class_file))
+ {
+ require_once($class_file);
+ break;
+ }
+ }
+ }
+
+ /**
+ * [setRoot 设置root根目录,可以同时添加多个]
+ * @param array $root [array]
+ */
+ public static function setRoot($rootArr = array())
+ {
+ if(is_array($rootArr))
+ {
+ self::$root_path = array_merge(self::$root_path, $rootArr);
+
+ foreach (self::$root_path as $key => $value)
+ {
+ self::$class_autoload_path = array_merge(self::$class_autoload_path,self::set_include_path($value));
+ }
+ }
+ }
+
+ /**
+ * [addRoot 添加root节点,可以多节点实现auto_load]
+ * @param [type] $root [description]
+ */
+ public static function addRoot($root)
+ {
+ \Logger::getLogger(__CLASS__)->debug('addRoot() in '.$root);
+ if(isset($root))
+ {
+ self::$root_path[] = $root;
+ foreach (self::$root_path as $key => $value)
+ {
+ self::$class_autoload_path = array_merge(self::$class_autoload_path,self::set_include_path($value));
+ }
+ }
+ \Logger::getLogger(__CLASS__)->debug('addRoot() out '.print_r(self::$class_autoload_path, true));
+ }
+
+ /**
+ * [getFatherPath 获取父级目录路径]
+ * @param [type] $path [description]
+ * @param integer $num [父级的级数,默认是当前目录的上一级目录]
+ * @return [type] [路径字符串]
+ */
+ public static function getFatherPath($path, $num = 1)
+ {
+ if (empty($path))
+ {
+ return "";
+ }
+
+ for ($i = 0; $i < $num; $i++)
+ {
+ $path = substr($path,0,strrpos($path ,DIRECTORY_SEPARATOR));
+ }
+ return $path;
+ }
+}
+
+spl_autoload_register(array(__NAMESPACE__ . '\AppAutoLoader','auto_load'));
\ No newline at end of file
diff --git a/provider/core/app/AppContext.php b/provider/core/app/AppContext.php
new file mode 100644
index 0000000..12f3e3b
--- /dev/null
+++ b/provider/core/app/AppContext.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\app;
+
+
+final class AppContext
+{
+ private $instances = array();
+
+ private $stateless = FALSE;//默认所有服务都是有状态的,每次使用都重新new
+
+ private $server;
+
+ public function setStateless($stateless, $server)
+ {
+ $this->stateless = $stateless;
+ $this->server = $server;
+ }
+
+ public function isStateless()
+ {
+ return $this->stateless;
+ }
+
+ public function getInstance($className, $params = null)
+ {
+ if($this->stateless)
+ {
+ \Logger::getLogger(__CLASS__)->debug("get stateless instance for $className");
+ if (isset($this->instances[$className]))
+ {
+ return $this->instances[$className];
+ }
+
+ if (!class_exists($className,true))
+ {
+ throw new \Exception("no class {$className}");
+ }
+
+ if (empty($params))
+ {
+ $this->instances[$className] = new $className();
+ }
+ else
+ {
+ $this->instances[$className] = new $className($params);
+ }
+
+ return $this->instances[$className];
+ }
+ else
+ {
+ \Logger::getLogger(__CLASS__)->debug("get new instance for $className");
+ if (!class_exists($className,true))
+ {
+ throw new \Exception("no class {$className}");
+ }
+
+ if (empty($params))
+ {
+ return new $className();
+ }
+ else
+ {
+ return new $className($params);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/provider/core/app/AppLauncher.php b/provider/core/app/AppLauncher.php
new file mode 100644
index 0000000..f2e7257
--- /dev/null
+++ b/provider/core/app/AppLauncher.php
@@ -0,0 +1,40 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\app;
+
+use com\fenqile\fsof\provider\fsof\FSOFProtocol;
+
+//加载Provider框架代码
+require_once(FSOF_PROVIDER_ROOT_PATH.DIRECTORY_SEPARATOR.'FSOFProvider.php');
+
+class AppLauncher
+{
+ public static function createApplication($appBootLoader)
+ {
+ /**
+ * 执行app的初始化操作,所有初始化逻辑都在$appBootLoader中定义
+ * 先加载app的初始化文件,这样app的自定义autoload机制优先级高于AppLauncher
+ **/
+ require_once $appBootLoader;
+
+ //autoload 用户的所有代码
+ $appRoot = AppAutoLoader::getFatherPath($appBootLoader, 1);
+ AppAutoLoader::addRoot($appRoot);
+ return new FSOFProtocol();
+ }
+}
\ No newline at end of file
diff --git a/provider/core/protocol/BaseProtocol.php b/provider/core/protocol/BaseProtocol.php
new file mode 100644
index 0000000..56168eb
--- /dev/null
+++ b/provider/core/protocol/BaseProtocol.php
@@ -0,0 +1,116 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\protocol;
+
+use com\fenqile\fsof\common\config\FSOFConstants;
+
+abstract class BaseProtocol implements IProtocol
+{
+ protected $server;
+ protected $swoole_server;
+
+ function __construct()
+ {
+ $this->init();
+ }
+
+ abstract public function init();
+
+ public function setServer($server)
+ {
+ $this->server = $server;
+ }
+
+ public function getAppConfig()
+ {
+ return $this->server->getAppConfig();
+ }
+
+ public function getAppName()
+ {
+ return $this->server->getAppName();
+ }
+
+ public function getAppRunTimeEnv()
+ {
+ return $this->server->getAppRunTimeEnv();
+ }
+
+ public function onStart($server, $workerId)
+ {
+ $this->swoole_server = $server;
+
+ //监控app重加载时间
+ $this->server->getAppMonitor()->onAppReload();
+
+ //当worker_id为0时添加定时器,驱动app监控信息上报
+// if($workerId == 0)
+// {
+// $this->swoole_server->addtimer(FSOFConstants::FSOF_MONITOR_TIMER); //5分钟监控一次数据5*60*1000
+// }
+
+ \Logger::getLogger(__CLASS__)->debug("protocol onStart():{$workerId}");
+ }
+
+ public function onConnect($server, $fd, $fromId)
+ {
+
+ }
+
+ public function onReceive($server,$clientId, $fromId, $data, $reqInfo = null)
+ {
+
+ }
+
+ public function onClose($server, $fd, $fromId)
+ {
+
+ }
+
+ public function onShutdown($serv, $workerId)
+ {
+
+ }
+
+ public function onTask($serv, $taskId, $fromId, $data)
+ {
+
+ }
+
+ public function onFinish($serv, $taskId, $data)
+ {
+
+ }
+
+ public function onTimer($serv, $interval)
+ {
+ switch( $interval )
+ {
+ case FSOFConstants::FSOF_MONITOR_TIMER:
+ {
+ $this->server->getAppMonitor()->uploadMonitorData();
+ break;
+ }
+ }
+ }
+
+ public function onRequest($request, $response)
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/provider/core/protocol/BufferedProtocol.php b/provider/core/protocol/BufferedProtocol.php
new file mode 100644
index 0000000..12fd7b5
--- /dev/null
+++ b/provider/core/protocol/BufferedProtocol.php
@@ -0,0 +1,64 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\protocol;
+
+
+abstract class BufferedProtocol extends BaseProtocol
+{
+ protected $requests;
+
+ const STATUS_FINISH = 1; //完成,进入处理流程
+ const STATUS_WAIT = 2; //等待数据
+ const STATUS_ERROR = 3; //错误,丢弃此包
+
+ public function onReceive($server, $clientId, $fromId, $data, $reqInfo = null)
+ {
+ // 检查buffer
+ $ret = $this->checkBuffer($clientId, $data);
+ \Logger::getLogger(__CLASS__)->debug("ret = ${ret}");
+
+ switch($ret)
+ {
+ case self::STATUS_ERROR:
+ unset($this->requests[$clientId]);
+ return true; // 错误的请求
+ case self::STATUS_WAIT:
+ return true; //数据不完整,继续等待
+ default:
+ break; // 完整数据
+ }
+
+ $request = $this->requests[$clientId];
+ if (!empty($reqInfo))
+ {
+ $request->reqInfo = $reqInfo;
+ }
+ $this->server->setRequest($request);
+ $this->onOneRequest($clientId, $request);
+ unset($this->requests[$clientId]);
+ }
+
+ abstract public function checkBuffer($client_id, $data);
+
+ abstract public function onOneRequest($client_id, $request);
+
+ public function onClose($server, $fd, $fromId)
+ {
+ unset($this->requests[$fd]);
+ }
+}
\ No newline at end of file
diff --git a/provider/core/protocol/IProtocol.php b/provider/core/protocol/IProtocol.php
new file mode 100644
index 0000000..187f1bf
--- /dev/null
+++ b/provider/core/protocol/IProtocol.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+namespace com\fenqile\fsof\provider\core\protocol;
+
+interface IProtocol
+{
+ function setServer($server);
+ function getAppConfig();
+ function onStart($server, $workerId);
+ function onConnect($server, $client_id, $from_id);
+ function onReceive($server,$client_id, $from_id, $data, $reqInfo = null);
+ function onClose($server, $client_id, $from_id);
+ function onShutdown($server, $worker_id);
+ function onTask($serv, $task_id, $from_id, $data);
+ function onFinish($serv, $task_id, $data);
+ function onTimer($serv, $interval);
+ function onRequest($request, $response);
+}
\ No newline at end of file
diff --git a/provider/core/server/BaseServer.php b/provider/core/server/BaseServer.php
new file mode 100644
index 0000000..e66c04e
--- /dev/null
+++ b/provider/core/server/BaseServer.php
@@ -0,0 +1,812 @@
... 2974 lines suppressed ...