Browse Source

add module: consul

hewei 6 years ago
parent
commit
61dbcafc66
10 changed files with 413 additions and 17 deletions
  1. 1 0
      .gitignore
  2. 7 2
      Makefile
  3. 159 0
      consul/consul.cpp
  4. 52 0
      consul/consul.h
  5. 67 0
      examples/consul_cli.cpp
  6. 89 0
      scripts/consul.py
  7. 25 0
      scripts/consul_agent.sh
  8. 0 15
      scripts/create_pro.sh
  9. 13 0
      scripts/create_repo.sh
  10. 0 0
      scripts/libhv.cmake

+ 1 - 0
.gitignore

@@ -17,6 +17,7 @@ cscope*
 # output
 *.o
 *.obj
+*.out
 
 *.pid
 *.log

+ 7 - 2
Makefile

@@ -3,7 +3,7 @@ TMPDIR=tmp
 
 default: all
 
-all: libhv test timer loop tcp udp nc nmap httpd curl
+all: libhv test timer loop tcp udp nc nmap httpd curl consul_cli
 
 clean:
 	$(MAKEF) clean SRCDIRS=". base utils event http http/client http/server protocol examples $(TMPDIR)"
@@ -67,6 +67,11 @@ curl: prepare
 	$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" SRCDIRS=". base utils http http/client $(TMPDIR)"
 	#$(MAKEF) TARGET=$@ SRCDIRS="$(CURL_SRCDIRS)" SRCDIRS=". base utils http http/client $(TMPDIR)" DEFINES="$(DEFINES) WITH_CURL CURL_STATICLIB"
 
+consul_cli: prepare
+	-rm $(TMPDIR)/*.o $(TMPDIR)/*.h $(TMPDIR)/*.c $(TMPDIR)/*.cpp
+	cp examples/consul_cli.cpp examples/http_api_test.h $(TMPDIR)
+	$(MAKEF) TARGET=$@ SRCDIRS=". base utils event http http/client consul $(TMPDIR)" DEFINES="PRINT_DEBUG"
+
 unittest: prepare
 	$(CC)  -g -Wall -std=c99   -I. -Ibase            -o bin/hmutex     unittest/hmutex_test.c        -pthread
 	$(CC)  -g -Wall -std=c99   -I. -Ibase            -o bin/connect    unittest/connect_test.c       base/hsocket.c
@@ -146,4 +151,4 @@ install:
 webbench: prepare
 	$(CC) -o bin/webbench unittest/webbench.c
 
-.PHONY: clean prepare libhv test timer loop tcp udp nc nmap httpd curl unittest webbench install
+.PHONY: clean prepare libhv test timer loop tcp udp nc nmap httpd curl consul_cli unittest webbench install

+ 159 - 0
consul/consul.cpp

@@ -0,0 +1,159 @@
+#include "consul.h"
+
+#include "http_client.h"
+
+#include "json.hpp"
+using json = nlohmann::json;
+
+#include "hstring.h"
+#include "herr.h"
+
+#define PROTOCOL    "http://"
+#define API_VERSION "v1"
+
+const char url_register[] = "/agent/service/register";
+const char url_deregister[] = "/agent/service/deregister";
+const char url_discover[] = "/catalog/service";
+
+string make_url(const char* ip, int port, const char* url) {
+    return asprintf(PROTOCOL "%s:%d/" API_VERSION "%s",
+            ip, port,
+            url);
+}
+
+string make_ServiceID(consul_service_t* service) {
+    return asprintf("%s-%s:%d", service->name, service->ip, service->port);
+}
+
+/*
+{
+  "ID": "redis1",
+  "Name": "redis",
+  "Tags": [
+    "primary",
+    "v1"
+  ],
+  "Address": "127.0.0.1",
+  "Port": 8000,
+  "Meta": {
+    "redis_version": "4.0"
+  },
+  "EnableTagOverride": false,
+  "Check": {
+    "DeregisterCriticalServiceAfter": "90m",
+    "Args": ["/usr/local/bin/check_redis.py"],
+    "HTTP": "http://localhost:5000/health",
+    "Interval": "10s",
+    "TTL": "15s"
+  },
+  "Weights": {
+    "Passing": 10,
+    "Warning": 1
+  }
+}
+ */
+int register_service(consul_node_t* node, consul_service_t* service, consul_health_t* health) {
+    HttpRequest req;
+    req.method = HTTP_PUT;
+    req.url = make_url(node->ip, node->port, url_register);
+    req.content_type = APPLICATION_JSON;
+
+    json jservice;
+    jservice["Name"] = service->name;
+    if (strlen(service->ip) != 0) {
+        jservice["Address"] = service->ip;
+    }
+    jservice["Port"] = service->port;
+    jservice["ID"] = make_ServiceID(service);
+
+    json jcheck;
+    if (*health->url == '\0') {
+        snprintf(health->url, sizeof(health->url), "%s:%d", service->ip, service->port);
+    }
+    jcheck[health->protocol] = health->url;
+    jcheck["Interval"] = asprintf("%dms", health->interval);
+    jcheck["DeregisterCriticalServiceAfter"] = asprintf("%dms", health->interval * 3);
+    jservice["Check"] = jcheck;
+
+    req.body = jservice.dump();
+    printd("%s\n", req.body.c_str());
+
+    HttpResponse res;
+    int ret = http_client_send(&req, &res);
+    printd("%s\n", res.body.c_str());
+    return ret;
+}
+
+int deregister_service(consul_node_t* node, consul_service_t* service) {
+    string url = make_url(node->ip, node->port, url_deregister);
+    url += '/';
+    url += make_ServiceID(service);
+
+    HttpRequest req;
+    req.method = HTTP_PUT;
+    req.url = url;
+    req.content_type = APPLICATION_JSON;
+
+    HttpResponse res;
+    int ret = http_client_send(&req, &res);
+    printd("%s\n", res.body.c_str());
+    return ret;
+}
+
+int discover_services(consul_node_t* node, const char* service_name, std::vector<consul_service_t>& services) {
+    string url = make_url(node->ip, node->port, url_discover);
+    url += '/';
+    url += service_name;
+
+    HttpRequest req;
+    req.method = HTTP_GET;
+    req.url = url;
+
+    HttpResponse res;
+
+    int ret = http_client_send(&req, &res);
+    if (ret != 0) {
+        return ret;
+    }
+    printd("%s\n", res.body.c_str());
+
+    json jroot = json::parse(res.body.c_str(), NULL, false);
+    if (!jroot.is_array()) {
+        return ERR_INVALID_JSON;
+    }
+    if (jroot.size() == 0) {
+        return 0;
+    }
+
+    consul_service_t service;
+    services.clear();
+    for (size_t i = 0; i < jroot.size(); ++i) {
+        auto jservice = jroot[i];
+        if (!jservice.is_object()) {
+            continue;
+        }
+        auto jname = jservice["ServiceName"];
+        if (!jname.is_string()) {
+            continue;
+        }
+        auto jip = jservice["ServiceAddress"];
+        if (!jip.is_string()) {
+            continue;
+        }
+        auto jport = jservice["ServicePort"];
+        if (!jport.is_number_integer()) {
+            continue;
+        }
+
+        string name = jname;
+        string ip = jip;
+        int    port = jport;
+
+        strncpy(service.name, name.c_str(), sizeof(service.name));
+        strncpy(service.ip, ip.c_str(), sizeof(service.ip));
+        service.port = port;
+        services.emplace_back(service);
+    }
+
+    return 0;
+}

+ 52 - 0
consul/consul.h

@@ -0,0 +1,52 @@
+#ifndef CONSUL_H_
+#define CONSUL_H_
+
+#include <vector>
+#include <string.h>
+
+typedef struct consul_node_s {
+    // node
+    char ip[32];
+    int  port;
+
+    consul_node_s() {
+        strcpy(ip, "127.0.0.1");
+        port = 8500;
+    }
+} consul_node_t;
+
+typedef struct consul_service_s {
+    // service
+    char name[64];
+    char ip[32];
+    int  port;
+
+    consul_service_s() {
+        memset(this, 0, sizeof(consul_service_s));
+        strcpy(ip, "127.0.0.1");
+    }
+} consul_service_t;
+
+typedef struct consul_health_s {
+    // check
+    char protocol[32]; // TCP,HTTP
+    char url[256];
+    char status[32]; // any,passing,warning,critical
+
+    int interval; // ms
+    int timeout;  // ms
+
+    consul_health_s() {
+        memset(this, 0, sizeof(consul_health_s));
+        strcpy(protocol, "TCP");
+        strcpy(status, "passing");
+        interval = 10000;
+        timeout = 3000;
+    }
+} consul_health_t;
+
+int register_service(consul_node_t* node, consul_service_t* service, consul_health_t* health);
+int deregister_service(consul_node_t* node, consul_service_t* service);
+int discover_services(consul_node_t* node, const char* service_name, std::vector<consul_service_t>& services);
+
+#endif // CONSUL_H_

+ 67 - 0
examples/consul_cli.cpp

@@ -0,0 +1,67 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "consul.h"
+
+int main(int argc, char* argv[]) {
+    if (argc < 3) {
+        printf("Usage: cmd subcmd ServiceName [ServiceAddress ServicePort] [NodeIP NodePort]\n");
+        printf("subcmd=[register,deregister,discover]\n");
+        return -10;
+    }
+    const char* subcmd = argv[1];
+    const char* ServiceName = argv[2];
+    const char* ServiceAddress = "127.0.0.1";
+    int ServicePort = 0;
+    const char* NodeIP = "127.0.0.1";
+    int NodePort = 8500;
+    if (argc > 3) {
+        ServiceAddress = argv[3];
+    }
+    if (argc > 4) {
+        ServicePort = atoi(argv[4]);
+    }
+    if (argc > 5) {
+        NodeIP = argv[5];
+    }
+    if (argc > 6) {
+        NodePort = atoi(argv[6]);
+    }
+
+    consul_node_t node;
+    strncpy(node.ip, NodeIP, sizeof(node.ip));
+    node.port = NodePort;
+
+    consul_service_t service;
+    strncpy(service.name, ServiceName, sizeof(service.name));
+    strncpy(service.ip, ServiceAddress, sizeof(service.ip));
+    service.port = ServicePort;
+
+    consul_health_t health;
+
+    if (strcmp(subcmd, "register") == 0) {
+        int ret = register_service(&node, &service, &health);
+        printf("register_service retval=%d\n", ret);
+        goto discover;
+    }
+    else if (strcmp(subcmd, "deregister") == 0) {
+        int ret = deregister_service(&node, &service);
+        printf("deregister_service retval=%d\n", ret);
+        goto discover;
+    }
+    else if (strcmp(subcmd, "discover") == 0) {
+discover:
+        std::vector<consul_service_t> services;
+        discover_services(&node, ServiceName, services);
+        for (auto& service : services) {
+            printf("name=%s ip=%s port=%d\n", service.name, service.ip, service.port);
+        }
+    }
+    else {
+        printf("subcmd error!\n");
+        return -20;
+    }
+
+    return 0;
+}

+ 89 - 0
scripts/consul.py

@@ -0,0 +1,89 @@
+#!/usr/bin/python3
+
+import sys
+import json
+import requests
+
+headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36',
+            'Cache-Control': 'no-cache',
+            'Accpet': '*/*',
+            'Content-Type': 'application/json'}
+
+_node = {'ip': '127.0.0.1', 'port': 8500}
+
+def gen_ServiceID(service_name, service_ip, service_port):
+    return service_name + '-' + service_ip + ':' + str(service_port)
+
+def register_service(service_name, service_ip, service_port , node=_node):
+    service = {}
+    service['Name'] = service_name
+    service['Address'] = service_ip
+    service['Port'] = service_port
+    service['ID'] = gen_ServiceID(service_name, service_ip, service_port)
+    check = {}
+    check['TCP'] = service['Address'] + ':' + str(service['Port'])
+    check['Interval'] = '10s'
+    check['DeregisterCriticalServiceAfter'] = '30s'
+    service['Check'] = check
+    data = json.dumps(service)
+    url = 'http://' + node['ip'] + ':' + str(node['port'])
+    url += '/v1/agent/service/register'
+    try:
+        res = requests.put(url=url, headers=headers, data=data)
+        return res.status_code == 200 and len(res.content) == 0
+    except Exception as e:
+        print(e)
+        return False
+
+def deregister_service(ServiceID, node=_node):
+    url = 'http://' + node['ip'] + ':' + str(node['port'])
+    url += '/v1/agent/service/deregister/'
+    url += ServiceID
+    try:
+        res = requests.put(url=url, headers=headers)
+        return res.status_code == 200 and len(res.content) == 0
+    except Exception as e:
+        print(e)
+        return False
+
+def discover_service(service_name, node=_node):
+    url = 'http://' + node['ip'] + ':' + str(node['port'])
+    url += '/v1/catalog/service/'
+    url += service_name
+    try:
+        res = requests.get(url=url, headers=headers)
+        return res.content
+    except Exception as e:
+        print(e)
+        return 'false'
+
+def main():
+    print(sys.argv)
+    if len(sys.argv) < 4:
+        print('Usage: cmd service_name service_ip service_port node_ip node_port')
+        return
+    service_name = sys.argv[1]
+    service_ip = sys.argv[2]
+    service_port = int(sys.argv[3])
+    if len(sys.argv) >= 5:
+        _node['ip'] = sys.argv[4]
+        _node['port'] = int(sys.argv[5])
+    ServiceID = gen_ServiceID(service_name, service_ip, service_port)
+    if register_service(service_name, service_ip, service_port):
+        print('register_service [%s] succeed!' % (ServiceID))
+    else:
+        print('register_service failed!')
+
+    print(discover_service(service_name))
+
+    '''
+    if deregister_service(ServiceID):
+        print('deregister_service [%s] succeed!' % (ServiceID))
+    else:
+        print('deregister_service failed')
+
+    print(discover_service(service_name))
+    '''
+
+if __name__ == '__main__':
+    main()

+ 25 - 0
scripts/consul_agent.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+
+rm -r consul
+rm nohup.out
+mkdir consul
+
+print_help() {
+cat <<EOF
+Usage:cmd bind_ip
+
+example:
+    ./consul_agent.sh 192.168.1.123
+EOF
+}
+
+main() {
+    if [ $# -lt 1 ]; then
+        print_help
+        return
+    fi
+    bind_ip=$1
+    nohup consul agent -server -ui -bootstrap-expect=1 -node=s1 -bind=${bind_ip} -client=0.0.0.0 -data-dir=/var/lib/consul -pid-file=consul/consul.pid -log-file=consul/consul.log &
+}
+
+main $@

+ 0 - 15
scripts/create_pro.sh

@@ -1,15 +0,0 @@
-#!/bin/bash
-
-mkdir -p include lib src bin doc etc 3rd/include 3rd/lib dist
-touch README.md BUILD.md RELEASE.md CHANGELOG.md Makefile .gitignore
-git init
-
-# personal
-git submodule add https://github.com/ithewei/hw.git src/hw
-cp src/hw/.gitignore .
-cp src/hw/.clang-format .
-cp src/hw/Makefile .
-cp -r src/hw/etc/* etc
-cp src/hw/main.cpp.tmpl src/main.cpp
-
-make

+ 13 - 0
scripts/create_repo.sh

@@ -0,0 +1,13 @@
+#!/bin/bash
+
+mkdir -p include lib src bin doc etc 3rd/include 3rd/lib dist
+touch README.md BUILD.md RELEASE.md CHANGELOG.md Makefile .gitignore
+git init
+
+# personal
+git submodule add https://github.com/ithewei/libhv.git src/hv
+cp src/hv/.gitignore .
+cp src/hv/.clang-format .
+cp src/hv/Makefile .
+cp -r src/hv/etc/* etc
+cp src/hv/main.cpp.tmpl src/main.cpp

+ 0 - 0
scripts/my.cmake → scripts/libhv.cmake