├── .gitignore ├── Consul ├── Agent.php ├── ConsulClient.php ├── ConsulResponse.php ├── Discovery.php ├── Http.php ├── Service.php └── Service │ ├── Agent.php │ ├── Catalog.php │ ├── Health.php │ ├── Kv.php │ └── Service.php ├── README.md ├── clearCache.php ├── deregister.php ├── getService.php ├── health.php ├── maintenance.php └── register.php /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /Consul/Agent.php: -------------------------------------------------------------------------------- 1 | options = [ 27 | "host" => "http://127.0.0.1:8500" 28 | ]; 29 | if (isset($options["host"])) { 30 | $this->options["host"] = $options["host"]; 31 | } 32 | $sf = new ConsulClient(['host' => $this->options["host"]]); 33 | $this->apiClient = $sf->agent; 34 | } 35 | 36 | /** 37 | * 服务注册,使用前请看参数类型,按类型传参 38 | * @param string $id //服务id 39 | * @param string $name //服务名称 40 | * @param string $ip //服务注册到consul的IP,服务发现,发现的就是这个IP 41 | * @param array $tags //服务tag,数组,自定义,可以根据这个tag来区分同一个服务名的服务,array('secure=false')代表是当前http协议,array('secure=true')代表当前https,数组内容自行增减 42 | * @param integer $port //服务IP对应端口号, 43 | * @param string $healthCheckIp //健康检查ip,一般与注册ip相同,需拼接协议 44 | * @param integer $healthCheckPort //健康检查ip对应端口 45 | * @param string $healthCheckPath //与IP和port拼接作为健康检查接口,对应的path,如:consul/health 46 | * @param string $interval //健康检查间隔时间,默认每隔10s,调用一次拼接好的健康检查地址URL 47 | * @return array array('code' => 为零时正常返回, 'msg' => 提示, 'data' => 对象类型) 48 | */ 49 | public function registerService($id, $name, $ip, $tags = ['secure=false'], $port = 80, $healthCheckIp = '', $healthCheckPort = 80, $healthCheckPath = '', $interval = '10s') 50 | { 51 | if (!$name) { 52 | return array('code' => 101, 'msg' => '服务名称不能为空', 'data' => (object)array()); 53 | } 54 | 55 | $healthCheckIp = $healthCheckIp ? $healthCheckIp : $ip; 56 | 57 | $data = array( 58 | 'id' => $id, 59 | 'name' => $name, 60 | 'tags' => $tags, 61 | 'address' => $ip, 62 | 'port' => (int)$port, 63 | 'enabletagoverride' => false, 64 | 'check' => array( 65 | 'deregistercriticalserviceafter' => '90m', 66 | 'http' => $healthCheckIp.':'.$healthCheckPort.'/'.$healthCheckPath, //指定健康检查的URL,调用后只要返回20X,consul都认为是健康的(我们是返回SUACCESS) 67 | 'interval' => $interval, 68 | ), //健康检查部分 69 | ); 70 | 71 | $res = $this->apiClient->put('service','register', $data); 72 | if ($res === NULL) { 73 | return array('code' => 103, 'msg' => '与Consul通信时出错', 'data' => (object)array()); 74 | } 75 | return array('code' => 0, 'msg' => 'ok', 'data' => $res); 76 | } 77 | 78 | /** 79 | * 服务维护状态更改 80 | * @param string $service_id //服务id,必填 81 | * @param boolean $enable //true启用维护模式,false禁用维护模式,必填 82 | * @param string $reason //更改原因,选填 83 | * @return array array('code' => 为零时正常返回, 'msg' => 提示, 'data' => 对象类型) 84 | */ 85 | public function maintenance($service_id, $enable, $reason = '') 86 | { 87 | if (!$service_id) { 88 | return array('code' => 101, 'msg' => '服务id不能为空', 'data' => (object)array()); 89 | } 90 | if (!is_bool($enable)) { 91 | return array('code' => 102, 'msg' => '维护状态必须是bool类型', 'data' => (object)array()); 92 | } 93 | $data = array( 94 | 'service_id' => $service_id,//required 95 | 'enable' => $enable,//required 96 | 'reason' => $reason, 97 | ); 98 | $response = $this->apiClient->put('service', 'maintenance', $service_id, $data, 'build_url'); 99 | if ($response === NULL) { 100 | return array('code' => 103, 'msg' => '服务不存在或通信出错', 'data' => (object)array()); 101 | } 102 | 103 | return array('code' => 0, 'msg' => 'ok', 'data' => $response); 104 | } 105 | 106 | /** 107 | * 注销服务 108 | * @param string $service_id //服务id,必填 109 | * @return array array('code' => 为零时正常返回, 'msg' => 提示, 'data' => 对象类型) 110 | */ 111 | public function deregisterService($service_id) 112 | { 113 | if (!$service_id) { 114 | return array('code' => 101, 'msg' => '服务id不能为空', 'data' => (object)array()); 115 | } 116 | $response = $this->apiClient->put('service', 'deregister', $service_id); 117 | 118 | return array('code' => 0, 'msg' => 'ok', 'data' => $response); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Consul/ConsulClient.php: -------------------------------------------------------------------------------- 1 | '127.0.0.1:8500', 'url' => '/v1/'); 16 | $default = array_replace($default, $option); 17 | $this->base_url = $default['host'] . $default['url']; 18 | $this->http = new Http(); 19 | if (!empty($default['token'])) { 20 | $this->http->query['token'] = $default['token']; 21 | } 22 | } 23 | 24 | /** 25 | * 用魔术属性获取服务 26 | * 27 | * @param string $name 服务名 28 | * 29 | * @return Service 服务 30 | */ 31 | public function __get($name) 32 | { 33 | return $this->get($name); 34 | } 35 | 36 | /** 37 | * 获取服务 38 | * 39 | * @param string $name 服务名 40 | * 41 | * @return Service 服务 42 | */ 43 | public function get($name) 44 | { 45 | $name = ucfirst($name); 46 | if (empty($this->service[$name])) { 47 | $class_name = "Consul\\Service\\$name"; 48 | $this->service[$name] = new $class_name($this->base_url, $this->http); 49 | } 50 | return $this->service[$name]; 51 | } 52 | 53 | } 54 | 55 | /** 56 | * 自动注册服务类 57 | */ 58 | spl_autoload_register(function ($class) { 59 | $file = dirname(__DIR__) . DIRECTORY_SEPARATOR.str_replace("\\", DIRECTORY_SEPARATOR, $class) . '.php'; 60 | if (is_file($file)) { 61 | require($file); 62 | } 63 | } 64 | ); -------------------------------------------------------------------------------- /Consul/ConsulResponse.php: -------------------------------------------------------------------------------- 1 | headers = $headers; 14 | $this->body = $body; 15 | $this->status = $status; 16 | } 17 | 18 | public function getHeaders() 19 | { 20 | return $this->headers; 21 | } 22 | 23 | public function getBody() 24 | { 25 | return $this->getArray(); 26 | } 27 | 28 | public function getStatusCode() 29 | { 30 | return $this->status; 31 | } 32 | 33 | public function getJson() 34 | { 35 | return $this->body; 36 | } 37 | 38 | public function getArray() 39 | { 40 | return json_decode($this->body, true); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Consul/Discovery.php: -------------------------------------------------------------------------------- 1 | options = [ 29 | "host" => "http://127.0.0.1:8500" 30 | ]; 31 | if (isset($options["host"])) { 32 | $this->options["host"] = $options["host"]; 33 | } 34 | $sf = new ConsulClient(['host' => $this->options["host"]]); 35 | $this->apiClient = $sf->health; 36 | 37 | //TODO 线上的话,修改为框架内调用方式 38 | // $this->cacheClient = new \Redis(); 39 | // $this->cacheClient->connect("127.0.0.1","6379"); 40 | } 41 | 42 | /** 43 | * 服务发现 44 | * @param string $serviceName //服务名,必填 45 | * @param boolean $cache //是否使用缓存 46 | * @param array $tags 选填 注册服务的时候传的tags,可以根据这个tags来区分同一个服务名的服务 47 | * @return array array('code' => 为零时正常返回, 'msg' => 提示, 'data' => 对象类型); 48 | */ 49 | public function getService($serviceName, $cache = true, $tags = array()) { 50 | if (!$serviceName) { 51 | return array('code' => 101, 'msg' => '服务名不能为空', 'data' => (object)array()); 52 | } 53 | 54 | // if ($cache) { 55 | // //获取当前服务名的缓存 56 | // $value = $this->cacheClient->get($keyProfix.$serviceName); 57 | // if ($value) { 58 | // $return = @unserialize($value); 59 | // shuffle($return);//随机取出一个 60 | // $return = new Service($return[0]["Service"]); 61 | // return array('code' => 0, 'msg' => 'ok', 'data' => $return); 62 | // } 63 | // } 64 | 65 | //发现服务 66 | $query = array( 67 | 'service' => $serviceName, 68 | 'near' => '_agent', 69 | 'passing' => true, 70 | ); 71 | if ($tags) { 72 | $query['tags'] = $tags; 73 | } 74 | $discoveryResponse = $this->apiClient->service($serviceName, $query);//return $discoveryResponse; 75 | if ($discoveryResponse === null) { 76 | array('code' => 102, 'msg' => '与Consul通信时出错', 'data' => (object)array()); 77 | } 78 | if (empty($discoveryResponse)) { 79 | return array('code' => 104, 'msg' => sprintf("未找到服务: %s ", $serviceName), 'data' => (object)array()); 80 | } 81 | 82 | // if ($cache) { 83 | // //缓存 84 | // $service = serialize($discoveryResponse); 85 | // $this->cacheClient->setex($keyProfix.$serviceName, 600, $service);//10min缓存失效时间 86 | // } 87 | 88 | shuffle($discoveryResponse);//随机取出一个 89 | $return = new Service($discoveryResponse[0]["Service"]); 90 | 91 | return array('code' => 0, 'msg' => 'ok', 'data' => $return); 92 | } 93 | 94 | /** 95 | * 删除一个或多个服务缓存 96 | * @param $serviceNames array(key1, key2, ...) 缓存服务的名字组成的数组 97 | * @return int 返回清除了几个键的缓存 98 | */ 99 | public function clearCache($serviceNames, $all = false) 100 | { 101 | if (!$serviceNames) { 102 | return 0; 103 | } 104 | //redis 105 | if ($all) { 106 | $res = $this->cacheClient->del($keyProfix.'*'); 107 | } else { 108 | $res = $this->cacheClient->del($serviceNames); 109 | } 110 | return $res; 111 | } 112 | } -------------------------------------------------------------------------------- /Consul/Http.php: -------------------------------------------------------------------------------- 1 | 'php-consul-sdk', 10 | 'Content-Type' => 'application/x-www-form-urlencoded', 11 | 'Cache-Control' => 'max-age=0', 12 | 'Accept' => '*/*', 13 | 'Accept-Language' => 'zh-CN,zh;q=0.8', 14 | ); 15 | 16 | /** 17 | * @var array 公用的URL参数 18 | */ 19 | public $query = array(); 20 | public $http_prefix = 'http://'; 21 | 22 | /** 23 | * http的语法 24 | * 25 | * @param string $url 地址 26 | * @param array $param 参数 27 | * @param string $method 请求类型 28 | * @param array $header 自定义头 29 | * 30 | * @return string 31 | */ 32 | public function request($url, array $param = array(), $method = 'GET', $header = array()) 33 | { 34 | $query = array(); 35 | $header = array_merge($header, $this->headers); 36 | $body = ''; 37 | $to_query = array(); 38 | if (!empty($param['__body'])) { 39 | $body = $param['__body']; 40 | unset($param['__body']); 41 | if (isset($param['build_url']) && $param['build_url']) { 42 | $to_query = json_decode($body, true); 43 | unset($param['build_url']); 44 | } 45 | } 46 | if (strcasecmp($method, 'get') === 0) { 47 | $query = array_merge($this->query, $param); 48 | } else { 49 | if (empty($body)) { 50 | $body = http_build_query($param); 51 | $query = array_merge($this->query); 52 | } else { 53 | $query = array_merge($this->query, $param); 54 | if ($to_query) { 55 | $query = array_merge($this->query, $to_query); 56 | } 57 | } 58 | } 59 | if (strpos($url, '?') === false) { 60 | $url .= '?' . http_build_query($query); 61 | } else { 62 | $url .= '&' . http_build_query($query); 63 | } 64 | $ch = curl_init(); 65 | if ($this->http_prefix == 'https://') { 66 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 67 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 68 | } 69 | curl_setopt($ch, CURLOPT_URL, $this->http_prefix . $url); 70 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式 71 | curl_setopt($ch, CURLOPT_HEADER, false); 72 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 73 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 74 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body); 75 | $content = curl_exec($ch); 76 | $status = curl_errno($ch); 77 | if ($status === 0) { 78 | return $content; 79 | } else { 80 | return false; 81 | } 82 | } 83 | 84 | public function header($url, array $param = array(), $method = 'GET', $header = array()) 85 | { 86 | $query = array(); 87 | $header = array_merge($header, $this->headers); 88 | $body = ''; 89 | if (!empty($param['__body'])) { 90 | $body = $param['__body']; 91 | unset($param['__body']); 92 | } 93 | if (strcasecmp($method, 'get') === 0) { 94 | $query = array_merge($this->query, $param); 95 | } else { 96 | if (empty($body)) { 97 | $body = http_build_query($param); 98 | $query = array_merge($this->query); 99 | } else { 100 | $query = array_merge($this->query, $param); 101 | } 102 | } 103 | if (strpos($url, '?') === false) { 104 | $url .= '?' . http_build_query($query); 105 | } else { 106 | $url .= '&' . http_build_query($query); 107 | } 108 | 109 | $ch = curl_init(); 110 | if ($this->http_prefix == 'https://') { 111 | curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 112 | curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 113 | } 114 | curl_setopt($ch, CURLOPT_URL, $this->http_prefix . $url); 115 | curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); //设置请求方式 116 | curl_setopt($ch, CURLOPT_HEADER, true); 117 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); 118 | curl_setopt($ch, CURLOPT_HTTPHEADER, $header); 119 | curl_setopt($ch, CURLOPT_POSTFIELDS, $body); 120 | $content = curl_exec($ch); 121 | $status = curl_errno($ch); 122 | curl_close($ch); 123 | $header = array(); 124 | if ($status === 0) { 125 | if (($index = strpos($content, "\r\n\r\n")) !== false) { 126 | $header_body = substr($content, 0, $index); 127 | $header_body = explode("\r\n", $header_body); 128 | foreach ($header_body as $r) { 129 | $index = strpos($r, ':'); 130 | if ($index !== false) { 131 | $header[trim(substr($r, 0, $index))] = trim(substr($r, $index + 1)); 132 | } 133 | } 134 | } 135 | } 136 | return $header; 137 | } 138 | } -------------------------------------------------------------------------------- /Consul/Service.php: -------------------------------------------------------------------------------- 1 | serviceID = $serviceData["ID"]; 23 | $this->name = $serviceData["Service"]; 24 | $this->address = $serviceData["Address"]; 25 | $this->port = $serviceData["Port"]; 26 | $this->tags = $serviceData["Tags"]; 27 | } 28 | } 29 | 30 | /** 31 | * @return mixed 32 | */ 33 | public function getID() { 34 | return $this->serviceID; 35 | } 36 | 37 | /** 38 | * @param mixed $serviceID 39 | */ 40 | public function setID($serviceID) { 41 | $this->serviceID = $serviceID; 42 | } 43 | 44 | /** 45 | * @return mixed 46 | */ 47 | public function getPort() { 48 | return $this->port; 49 | } 50 | 51 | /** 52 | * @param mixed $port 53 | */ 54 | public function setPort($port) { 55 | $this->port = $port; 56 | } 57 | 58 | /** 59 | * @return mixed 60 | */ 61 | public function getAddress() { 62 | return $this->address; 63 | } 64 | 65 | /** 66 | * @param mixed $address 67 | */ 68 | public function setAddress($address) { 69 | $this->address = $address; 70 | } 71 | 72 | /** 73 | * @return mixed 74 | */ 75 | public function getTags() { 76 | return $this->tags; 77 | } 78 | 79 | /** 80 | * @param mixed $tags 81 | */ 82 | public function setTags($tags) { 83 | $this->tags = $tags; 84 | } 85 | 86 | /** 87 | * @return mixed 88 | */ 89 | public function getName() { 90 | return $this->name; 91 | } 92 | 93 | /** 94 | * @param mixed $name 95 | */ 96 | public function setName($name) { 97 | $this->name = $name; 98 | } 99 | } -------------------------------------------------------------------------------- /Consul/Service/Agent.php: -------------------------------------------------------------------------------- 1 | name = 'agent'; 9 | } 10 | } -------------------------------------------------------------------------------- /Consul/Service/Catalog.php: -------------------------------------------------------------------------------- 1 | name = 'catalog'; 9 | } 10 | } -------------------------------------------------------------------------------- /Consul/Service/Health.php: -------------------------------------------------------------------------------- 1 | name = 'health'; 9 | } 10 | } -------------------------------------------------------------------------------- /Consul/Service/Kv.php: -------------------------------------------------------------------------------- 1 | name = 'kv'; 9 | } 10 | 11 | public function get($key, array $param = array()) 12 | { 13 | $url = $this->name . '/' . $key; 14 | $resp = $this->http->request($this->base_url . $url, $param); 15 | return $this->response($resp, empty($param['recurse'])?false:$param['recurse']); 16 | } 17 | 18 | public function delete($key, array $param = array()) 19 | { 20 | $url = $this->name . '/' . $key; 21 | $resp = $this->http->request($this->base_url . $url, $param, 'DELETE'); 22 | return $resp == 'true'; 23 | } 24 | 25 | public function set($key, $value, array $param = array()) 26 | { 27 | $url = $this->name . '/' . $key; 28 | if (is_array($value) || is_object($value)) { 29 | $value = json_encode($value); 30 | } 31 | $param['__body'] = $value; 32 | $resp = $this->http->request($this->base_url . $url, $param, 'PUT'); 33 | return $resp == 'true'; 34 | } 35 | 36 | public function response($str, $isArray = false) 37 | { 38 | $json = parent::response($str); 39 | if (is_array($json)) { 40 | if (count($json) == 1 && $isArray == false) { 41 | return base64_decode($json[0]['Value']); 42 | } else { 43 | $re = array(); 44 | foreach ($json as $v) { 45 | $re[$v['Key']] = base64_decode($v['Value']); 46 | } 47 | return $re; 48 | } 49 | } else { 50 | return $json; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Consul/Service/Service.php: -------------------------------------------------------------------------------- 1 | base_url = $base_url; 29 | $this->http = $http; 30 | } 31 | 32 | public function header() 33 | { 34 | $func_args = func_get_args(); 35 | if (count($func_args) == 0) { 36 | return false; 37 | } 38 | $method_name = $func_args[0]; 39 | $param = array(); 40 | $url = $this->name . '/' . $method_name; 41 | for ($i = 1; $i < count($func_args); $i++) { 42 | if (is_array($func_args[$i])) { 43 | $param = array_merge($param, $func_args[$i]); 44 | } else { 45 | $url .= '/' . $func_args[$i]; 46 | } 47 | } 48 | $resp = $this->http->header($this->base_url . $url, $param); 49 | return $resp; 50 | } 51 | 52 | public function __call($method_name, $args) 53 | { 54 | $url = $this->name . '/' . $method_name; 55 | if (empty($args)) { 56 | $resp = $this->http->request($this->base_url . $url); 57 | } else { 58 | $param = array(); 59 | foreach ($args as $arg) { 60 | if (is_array($arg)) { 61 | $param = array_merge($param, $arg); 62 | } else { 63 | $url .= '/' . $arg; 64 | } 65 | } 66 | $resp = $this->http->request($this->base_url . $url, $param); 67 | } 68 | return $this->response($resp); 69 | } 70 | 71 | public function put() 72 | { 73 | $args=func_get_args(); 74 | $url = $this->name ; 75 | if (empty($args)) { 76 | $resp = $this->http->request($this->base_url . $url); 77 | } else { 78 | $param = array(); 79 | $build_url = false; 80 | foreach ($args as $arg) { 81 | if (is_array($arg)) { 82 | $param = array_merge($param, $arg); 83 | } else { 84 | if ($arg == 'build_url') { 85 | $build_url = $arg; 86 | continue; 87 | } 88 | $url .= '/' . $arg; 89 | } 90 | } 91 | if (empty($param)) { 92 | $resp = $this->http->request($this->base_url . $url, array(), 'PUT'); 93 | } else { 94 | if ($build_url) { 95 | $resp = $this->http->request($this->base_url . $url, array('__body' => json_encode($param), 'build_url' => true), 'PUT'); 96 | } else { 97 | $resp = $this->http->request($this->base_url . $url, array('__body' => json_encode($param)), 'PUT'); 98 | } 99 | } 100 | } 101 | return $this->response($resp); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-consul 2 | 3 | ## 使用方法 4 | 5 | #### 仔细的看consul官方接口文档 6 | 使用sdk需要你仔细的了解consul的http的API。 7 | 8 | 官方HTTP API接口文档:https://www.consul.io/api/index.html 9 | 10 | #### 内代码有实现怎么调用的~ 11 | 代码例子中已实现:服务注册,服务发现,服务注销,服务维护 12 | 13 | 例如:getService.php,中示例了服务发现的使用方法 14 | ``` 15 | $serviceName = 'win2';//注册时的name 16 | $cache = false;//是否使用缓存,默认false 17 | 18 | $discovery = new Consul\Discovery(array( 19 | 'host' => 'http://127.0.0.1:8500' 20 | )); 21 | $service = $discovery->getService($serviceName, $cache); 22 | ``` 23 | -------------------------------------------------------------------------------- /clearCache.php: -------------------------------------------------------------------------------- 1 | 'http://127.0.0.1:8500' 17 | )); 18 | $res = $discovery->clearCache($serviceNames, $all); 19 | 20 | var_dump($res); -------------------------------------------------------------------------------- /deregister.php: -------------------------------------------------------------------------------- 1 | 'http://127.0.0.1:8500' 15 | )); 16 | $res = $agent->deregisterService($service_id); 17 | 18 | echo "
"; 19 | var_dump($res); -------------------------------------------------------------------------------- /getService.php: -------------------------------------------------------------------------------- 1 | 'http://127.0.0.1:8500' 16 | )); 17 | $service = $discovery->getService($serviceName, $cache); 18 | 19 | echo ""; 20 | var_dump($service); 21 | //var_dump($service['data']->getID()); 22 | //var_dump($service['data']->getPort()); 23 | //var_dump($service['data']->getAddress()); 24 | //var_dump($service['data']->getName()); 25 | //var_dump($service['data']->getTags()); -------------------------------------------------------------------------------- /health.php: -------------------------------------------------------------------------------- 1 | 'http://127.0.0.1:8500' 19 | )); 20 | 21 | $res = $agent->maintenance($service_id, $enable, $reason); 22 | 23 | echo ""; 24 | var_dump($res); -------------------------------------------------------------------------------- /register.php: -------------------------------------------------------------------------------- 1 | 'http://127.0.0.1:8500' 34 | )); 35 | 36 | $res = $agent->registerService($id, $name, $ip, $tags, $port, $healthCheckIp, $healthCheckPort, $healthCheckPath, $interval); 37 | 38 | echo ""; 39 | var_dump($res); 40 | 41 | --------------------------------------------------------------------------------