{"action":"created","comment":{"url":"https://api.github.com/repos/brpc/brpc/pulls/comments/217982391","pull_request_review_id":155798654,"id":217982391,"node_id":"MDI0OlB1bGxSZXF1ZXN0UmV2aWV3Q29tbWVudDIxNzk4MjM5MQ==","diff_hunk":"@@ -0,0 +1,380 @@\n+// Copyright (c) 2018 Iqiyi, Inc.\n+// \n+// Licensed under the Apache License, Version 2.0 (the \"License\");\n+// you may not use this file except in compliance with the License.\n+// You may obtain a copy of the License at\n+// \n+//     http://www.apache.org/licenses/LICENSE-2.0\n+// \n+// Unless required by applicable law or agreed to in writing, software\n+// distributed under the License is distributed on an \"AS IS\" BASIS,\n+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n+// See the License for the specific language governing permissions and\n+// limitations under the License.\n+\n+// Authors: Cai,Daojin (caidaojin@qiyi.com)\n+\n+\n+#include \"brpc/channel.h\"\n+#include \"brpc/log.h\"\n+#include \"brpc/policy/couchbase_listener_naming_service.h\"\n+#include \"brpc/policy/couchbase_naming_service.h\"\n+#include \"brpc/progressive_reader.h\"\n+#include \"bthread/bthread.h\"\n+#include \"butil/status.h\"\n+#include \"butil/string_splitter.h\"\n+#include \"butil/strings/string_number_conversions.h\"\n+#include \"butil/third_party/libvbucket/vbucket.h\"\n+\n+namespace brpc {\n+namespace policy {\n+\n+DEFINE_int32(couchbase_listen_retry_times, 5,\n+             \"Retry times to create couchbase vbucket map monitoring connection.\"\n+             \"Listen thread will sleep a while when reach this times.\");\n+DEFINE_int32(couchbase_listen_interval_ms, 1000, \n+             \"Listen thread sleep for the number of milliseconds after creating\"\n+             \"vbucket map monitoring connection failure.\");\n+\n+namespace {\n+\n+// Each vbucket map json string is spiltted by \"\\n\\n\\n\\n\".\n+const std::string kSeparator(\"\\n\\n\\n\\n\");\n+\n+}\n+\n+class CouchbaseServerListener;\n+\n+class VBucketMapReader : public brpc::ProgressiveReader {\n+public:\n+    VBucketMapReader(CouchbaseServerListener* listener) : _listener(listener) {}\n+    ~VBucketMapReader() = default;\n+\n+    virtual butil::Status OnReadOnePart(const void* data, size_t length);\n+    \n+    virtual void OnEndOfMessage(const butil::Status& status);\n+\n+    void Attach() { _attach = true; }\n+    void Detach() { _attach = false; }\n+    bool IsAttached() { return _attach; }\n+    // The couchbase channel has been distructed.\n+    void Destroy() { _listener = nullptr; }\n+\n+public:\n+    VBucketMapReader(const VBucketMapReader&) = delete;\n+    VBucketMapReader& operator=(const VBucketMapReader&) = delete;    \n+    \n+    // It is monitoring vbucket map if it is true.\n+    bool _attach = false;\n+    CouchbaseServerListener* _listener;\n+    std::string _buf;\n+    butil::Mutex _mutex;\n+};\n+\n+class CouchbaseServerListener {\n+public:\n+    CouchbaseServerListener(\n+        const butil::StringPiece& server_list, const butil::StringPiece& streaming_url, \n+        const butil::StringPiece& init_url, const butil::StringPiece& auth, CouchbaseNamingService* ns)\n+        : _streaming_url(streaming_url.data(), streaming_url.length()),\n+          _auth(auth.data(), auth.length()),\n+\t        _ns(ns),\n+          _reader(new VBucketMapReader(this)) {\n+        Init(server_list, init_url);\n+    }\n+    ~CouchbaseServerListener();\n+   \n+    void UpdateVBucketMap(std::string&& vb_map);\n+    \n+    void CreateListener();\n+    \n+private:\n+    CouchbaseServerListener(const CouchbaseServerListener&) = delete;\n+    CouchbaseServerListener& operator=(const CouchbaseServerListener&) = delete;\n+\n+    void Init(const butil::StringPiece& server_list, const  butil::StringPiece& init_url);\n+\n+    bool InitVBucketMap(const std::string& url);\n+\n+    static void* ListenThread(void* arg);\n+\n+    bthread_t _listen_bth;\n+    // REST/JSON url to monitor vbucket map.\n+    std::string _streaming_url;\n+    std::string _auth;\n+    std::string _listen_port;\n+    std::string _service_name;\n+    CouchbaseNamingService* _ns;\n+    // Monitor couchbase vbuckets map on this channel. \n+    brpc::Channel _listen_channel;\n+    // If _reader is not attached to listen socket, it will be released in\n+    // CouchbaseServerListener desconstruction. Otherwise, it will be released\n+    // by itself.\n+    VBucketMapReader* _reader;\n+};\n+\n+butil::Status VBucketMapReader::OnReadOnePart(const void* data, size_t length) {\n+    BAIDU_SCOPED_LOCK(_mutex);\n+    // If '_listener' is desconstructed, return error status directly.    \n+    if (_listener == nullptr) {\n+        return butil::Status(-1, \"Couchbase channel is destroyed\");\n+    }\n+\n+    _buf.append(static_cast<const char*>(data), length);\n+    const size_t end_pos = _buf.rfind(kSeparator);\n+    if (end_pos != std::string::npos) {\n+        size_t begin_pos = _buf.rfind(kSeparator, end_pos - 1);\n+        if (begin_pos == std::string::npos) {\n+            begin_pos = 0;\n+        } else {\n+            begin_pos += kSeparator.size();\n+        }\n+        std::string vb_map = _buf.substr(begin_pos, end_pos);\n+        _buf = _buf.substr(end_pos + kSeparator.size());\n+        _listener->UpdateVBucketMap(std::move(vb_map));\n+    } \n+    return butil::Status::OK();\n+}\n+\n+void VBucketMapReader::OnEndOfMessage(const butil::Status& status) {\n+    {    \n+        BAIDU_SCOPED_LOCK(_mutex);\n+        if (_listener != nullptr) {\n+            _buf.clear();\n+            Detach();\n+            _listener->CreateListener();\n+            return;\n+        }\n+    }\n+    // If '_listener' is desconstructed, release this object.    \n+    std::unique_ptr<VBucketMapReader> release(this);\n+}\n+\n+CouchbaseServerListener::~CouchbaseServerListener() {\n+    std::unique_lock<butil::Mutex> mu(_reader->_mutex);\n+    bthread_stop(_listen_bth);\n+    bthread_join(_listen_bth, nullptr);\n+    if (!_reader->IsAttached()) {\n+        mu.unlock();\n+        std::unique_ptr<VBucketMapReader> p(_reader);\n+    } else {\n+        _reader->Destroy();\n+    }\n+}\n+\n+void CouchbaseServerListener::Init(const butil::StringPiece& server_list, \n+                                   const butil::StringPiece& init_url) {\n+    brpc::ChannelOptions options;\n+    options.protocol = PROTOCOL_HTTP;\n+    options.max_retry = FLAGS_couchbase_listen_retry_times;\n+    std::string ns_url(\"couchbase_listener://\");\n+    ns_url.append(server_list.data(), server_list.length());\n+    std::string unique_id = \"_\" + butil::Uint64ToString(reinterpret_cast<uint64_t>(this));\n+    ns_url += unique_id;\n+    _service_name.append(server_list.data(), server_list.length());\n+    _service_name += unique_id;\n+    CHECK(_listen_channel.Init(ns_url.c_str(), \"rr\", &options) == 0) \n+        << \"Failed to init listen channel.\";\n+    const std::string init_url_str(init_url.data(), init_url.length());\n+    InitVBucketMap(init_url_str);\n+    CreateListener();\n+}\n+ \n+bool CouchbaseServerListener::InitVBucketMap(const std::string& uri) {\n+    Controller cntl;\n+    if (!_auth.empty()) {\n+        cntl.http_request().SetHeader(\"Authorization\", _auth);\n+    }\n+    cntl.http_request().uri() = uri;\n+    _listen_channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr);\n+    if (!cntl.Failed()) {\n+        std::string vb_map = cntl.response_attachment().to_string();\n+        if (!vb_map.empty()) {\n+            _listen_port = butil::IntToString(cntl.remote_side().port);\n+            UpdateVBucketMap(std::move(vb_map));\n+            return true;\n+        }\n+    }\n+    LOG(ERROR) << \"Failed to init vbucket map: \" << cntl.ErrorText();\n+    // Set empty for first batch of server.\n+    std::vector<std::string> servers;\n+    std::string vb_map;\n+    _ns->ResetServers(servers, vb_map);\n+    return false;\n+}\n+\n+void* CouchbaseServerListener::ListenThread(void* arg) {\n+    CouchbaseServerListener* listener = \n+        static_cast<CouchbaseServerListener*>(arg);\n+    while (true) {\n+        listener->_reader->Detach();  \n+        Controller cntl;\n+        if (!listener->_auth.empty()) {\n+            cntl.http_request().SetHeader(\"Authorization\", listener->_auth);\n+        }\n+        cntl.http_request().uri() = listener->_streaming_url;\n+        cntl.response_will_be_read_progressively();","path":"src/brpc/policy/couchbase_naming_service.cpp","position":216,"original_position":216,"commit_id":"8451a663b2a943b42a41031b9d2885adf87f68fc","original_commit_id":"8451a663b2a943b42a41031b9d2885adf87f68fc","user":{"login":"jamesge","id":6566535,"node_id":"MDQ6VXNlcjY1NjY1MzU=","avatar_url":"https://avatars0.githubusercontent.com/u/6566535?v=4","gravatar_id":"","url":"https://api.github.com/users/jamesge","html_url":"https://github.com/jamesge","followers_url":"https://api.github.com/users/jamesge/followers","following_url":"https://api.github.com/users/jamesge/following{/other_user}","gists_url":"https://api.github.com/users/jamesge/gists{/gist_id}","starred_url":"https://api.github.com/users/jamesge/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jamesge/subscriptions","organizations_url":"https://api.github.com/users/jamesge/orgs","repos_url":"https://api.github.com/users/jamesge/repos","events_url":"https://api.github.com/users/jamesge/events{/privacy}","received_events_url":"https://api.github.com/users/jamesge/received_events","type":"User","site_admin":false},"body":"如果body不是特别大（上MB），就不要用ProgressiveReader了，还是会增加不少复杂度的。况且这里也并没有显著提高获得vbmap的时效性？","created_at":"2018-09-17T08:26:36Z","updated_at":"2018-09-17T08:54:50Z","html_url":"https://github.com/brpc/brpc/pull/465#discussion_r217982391","pull_request_url":"https://api.github.com/repos/brpc/brpc/pulls/465","author_association":"MEMBER","_links":{"self":{"href":"https://api.github.com/repos/brpc/brpc/pulls/comments/217982391"},"html":{"href":"https://github.com/brpc/brpc/pull/465#discussion_r217982391"},"pull_request":{"href":"https://api.github.com/repos/brpc/brpc/pulls/465"}}},"pull_request":{"url":"https://api.github.com/repos/brpc/brpc/pulls/465","id":210897875,"node_id":"MDExOlB1bGxSZXF1ZXN0MjEwODk3ODc1","html_url":"https://github.com/brpc/brpc/pull/465","diff_url":"https://github.com/brpc/brpc/pull/465.diff","patch_url":"https://github.com/brpc/brpc/pull/465.patch","issue_url":"https://api.github.com/repos/brpc/brpc/issues/465","number":465,"state":"open","locked":false,"title":"Brpc couchbase","user":{"login":"cdjingit","id":31362185,"node_id":"MDQ6VXNlcjMxMzYyMTg1","avatar_url":"https://avatars1.githubusercontent.com/u/31362185?v=4","gravatar_id":"","url":"https://api.github.com/users/cdjingit","html_url":"https://github.com/cdjingit","followers_url":"https://api.github.com/users/cdjingit/followers","following_url":"https://api.github.com/users/cdjingit/following{/other_user}","gists_url":"https://api.github.com/users/cdjingit/gists{/gist_id}","starred_url":"https://api.github.com/users/cdjingit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdjingit/subscriptions","organizations_url":"https://api.github.com/users/cdjingit/orgs","repos_url":"https://api.github.com/users/cdjingit/repos","events_url":"https://api.github.com/users/cdjingit/events{/privacy}","received_events_url":"https://api.github.com/users/cdjingit/received_events","type":"User","site_admin":false},"body":"方案简述：\r\n1. CouchbaseChannel用来发送couchbase 请求，主要功能：为一个bucket所包含的每个server建立\r\n   memcache channel,  根据每个请求的key值，得到该请求应该在哪个memcache channel上发送。\r\n2. CouchbaseServerListener 用来监听vbucket mapping的变化，获取最新的vbucket mapping ，\r\n    并更新CouchbaseChannel中相关数据结构。\r\n3. CouchbaseNamingService 用来跟新CouchbaseServerListener中的监听的couchbase server list。\r\n4. src/butil/libvbucket/ 开源库， 用来解析couchbase server推送的vbucket map的json消息。","created_at":"2018-08-25T11:02:10Z","updated_at":"2018-09-17T08:54:50Z","closed_at":null,"merged_at":null,"merge_commit_sha":"f13bf5eaf705d0ab3cdd40b452e6ad16c93382d0","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[],"milestone":null,"commits_url":"https://api.github.com/repos/brpc/brpc/pulls/465/commits","review_comments_url":"https://api.github.com/repos/brpc/brpc/pulls/465/comments","review_comment_url":"https://api.github.com/repos/brpc/brpc/pulls/comments{/number}","comments_url":"https://api.github.com/repos/brpc/brpc/issues/465/comments","statuses_url":"https://api.github.com/repos/brpc/brpc/statuses/8451a663b2a943b42a41031b9d2885adf87f68fc","head":{"label":"cdjingit:brpc_couchbase_caidaojin","ref":"brpc_couchbase_caidaojin","sha":"8451a663b2a943b42a41031b9d2885adf87f68fc","user":{"login":"cdjingit","id":31362185,"node_id":"MDQ6VXNlcjMxMzYyMTg1","avatar_url":"https://avatars1.githubusercontent.com/u/31362185?v=4","gravatar_id":"","url":"https://api.github.com/users/cdjingit","html_url":"https://github.com/cdjingit","followers_url":"https://api.github.com/users/cdjingit/followers","following_url":"https://api.github.com/users/cdjingit/following{/other_user}","gists_url":"https://api.github.com/users/cdjingit/gists{/gist_id}","starred_url":"https://api.github.com/users/cdjingit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdjingit/subscriptions","organizations_url":"https://api.github.com/users/cdjingit/orgs","repos_url":"https://api.github.com/users/cdjingit/repos","events_url":"https://api.github.com/users/cdjingit/events{/privacy}","received_events_url":"https://api.github.com/users/cdjingit/received_events","type":"User","site_admin":false},"repo":{"id":120751636,"node_id":"MDEwOlJlcG9zaXRvcnkxMjA3NTE2MzY=","name":"brpc","full_name":"cdjingit/brpc","private":false,"owner":{"login":"cdjingit","id":31362185,"node_id":"MDQ6VXNlcjMxMzYyMTg1","avatar_url":"https://avatars1.githubusercontent.com/u/31362185?v=4","gravatar_id":"","url":"https://api.github.com/users/cdjingit","html_url":"https://github.com/cdjingit","followers_url":"https://api.github.com/users/cdjingit/followers","following_url":"https://api.github.com/users/cdjingit/following{/other_user}","gists_url":"https://api.github.com/users/cdjingit/gists{/gist_id}","starred_url":"https://api.github.com/users/cdjingit/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/cdjingit/subscriptions","organizations_url":"https://api.github.com/users/cdjingit/orgs","repos_url":"https://api.github.com/users/cdjingit/repos","events_url":"https://api.github.com/users/cdjingit/events{/privacy}","received_events_url":"https://api.github.com/users/cdjingit/received_events","type":"User","site_admin":false},"html_url":"https://github.com/cdjingit/brpc","description":"Industrial-grade RPC framework used throughout Baidu, with 1,000,000+ instances and thousands kinds of services, called \"baidu-rpc\" inside Baidu.","fork":true,"url":"https://api.github.com/repos/cdjingit/brpc","forks_url":"https://api.github.com/repos/cdjingit/brpc/forks","keys_url":"https://api.github.com/repos/cdjingit/brpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/cdjingit/brpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/cdjingit/brpc/teams","hooks_url":"https://api.github.com/repos/cdjingit/brpc/hooks","issue_events_url":"https://api.github.com/repos/cdjingit/brpc/issues/events{/number}","events_url":"https://api.github.com/repos/cdjingit/brpc/events","assignees_url":"https://api.github.com/repos/cdjingit/brpc/assignees{/user}","branches_url":"https://api.github.com/repos/cdjingit/brpc/branches{/branch}","tags_url":"https://api.github.com/repos/cdjingit/brpc/tags","blobs_url":"https://api.github.com/repos/cdjingit/brpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/cdjingit/brpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/cdjingit/brpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/cdjingit/brpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/cdjingit/brpc/statuses/{sha}","languages_url":"https://api.github.com/repos/cdjingit/brpc/languages","stargazers_url":"https://api.github.com/repos/cdjingit/brpc/stargazers","contributors_url":"https://api.github.com/repos/cdjingit/brpc/contributors","subscribers_url":"https://api.github.com/repos/cdjingit/brpc/subscribers","subscription_url":"https://api.github.com/repos/cdjingit/brpc/subscription","commits_url":"https://api.github.com/repos/cdjingit/brpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/cdjingit/brpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/cdjingit/brpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/cdjingit/brpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/cdjingit/brpc/contents/{+path}","compare_url":"https://api.github.com/repos/cdjingit/brpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/cdjingit/brpc/merges","archive_url":"https://api.github.com/repos/cdjingit/brpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/cdjingit/brpc/downloads","issues_url":"https://api.github.com/repos/cdjingit/brpc/issues{/number}","pulls_url":"https://api.github.com/repos/cdjingit/brpc/pulls{/number}","milestones_url":"https://api.github.com/repos/cdjingit/brpc/milestones{/number}","notifications_url":"https://api.github.com/repos/cdjingit/brpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/cdjingit/brpc/labels{/name}","releases_url":"https://api.github.com/repos/cdjingit/brpc/releases{/id}","deployments_url":"https://api.github.com/repos/cdjingit/brpc/deployments","created_at":"2018-02-08T11:16:19Z","updated_at":"2018-09-06T03:55:23Z","pushed_at":"2018-09-17T04:20:43Z","git_url":"git://github.com/cdjingit/brpc.git","ssh_url":"git@github.com:cdjingit/brpc.git","clone_url":"https://github.com/cdjingit/brpc.git","svn_url":"https://github.com/cdjingit/brpc","homepage":"","size":25808,"stargazers_count":0,"watchers_count":0,"language":"C++","has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"brpc:master","ref":"master","sha":"0f621581b856f03136e29cb801ce0f555820ee8f","user":{"login":"brpc","id":31610676,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNjEwNjc2","avatar_url":"https://avatars2.githubusercontent.com/u/31610676?v=4","gravatar_id":"","url":"https://api.github.com/users/brpc","html_url":"https://github.com/brpc","followers_url":"https://api.github.com/users/brpc/followers","following_url":"https://api.github.com/users/brpc/following{/other_user}","gists_url":"https://api.github.com/users/brpc/gists{/gist_id}","starred_url":"https://api.github.com/users/brpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brpc/subscriptions","organizations_url":"https://api.github.com/users/brpc/orgs","repos_url":"https://api.github.com/users/brpc/repos","events_url":"https://api.github.com/users/brpc/events{/privacy}","received_events_url":"https://api.github.com/users/brpc/received_events","type":"Organization","site_admin":false},"repo":{"id":102343794,"node_id":"MDEwOlJlcG9zaXRvcnkxMDIzNDM3OTQ=","name":"brpc","full_name":"brpc/brpc","private":false,"owner":{"login":"brpc","id":31610676,"node_id":"MDEyOk9yZ2FuaXphdGlvbjMxNjEwNjc2","avatar_url":"https://avatars2.githubusercontent.com/u/31610676?v=4","gravatar_id":"","url":"https://api.github.com/users/brpc","html_url":"https://github.com/brpc","followers_url":"https://api.github.com/users/brpc/followers","following_url":"https://api.github.com/users/brpc/following{/other_user}","gists_url":"https://api.github.com/users/brpc/gists{/gist_id}","starred_url":"https://api.github.com/users/brpc/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/brpc/subscriptions","organizations_url":"https://api.github.com/users/brpc/orgs","repos_url":"https://api.github.com/users/brpc/repos","events_url":"https://api.github.com/users/brpc/events{/privacy}","received_events_url":"https://api.github.com/users/brpc/received_events","type":"Organization","site_admin":false},"html_url":"https://github.com/brpc/brpc","description":"Industrial-grade RPC framework used throughout Baidu, with 1,000,000+ instances and thousands kinds of services, called \"baidu-rpc\" inside Baidu.","fork":false,"url":"https://api.github.com/repos/brpc/brpc","forks_url":"https://api.github.com/repos/brpc/brpc/forks","keys_url":"https://api.github.com/repos/brpc/brpc/keys{/key_id}","collaborators_url":"https://api.github.com/repos/brpc/brpc/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/brpc/brpc/teams","hooks_url":"https://api.github.com/repos/brpc/brpc/hooks","issue_events_url":"https://api.github.com/repos/brpc/brpc/issues/events{/number}","events_url":"https://api.github.com/repos/brpc/brpc/events","assignees_url":"https://api.github.com/repos/brpc/brpc/assignees{/user}","branches_url":"https://api.github.com/repos/brpc/brpc/branches{/branch}","tags_url":"https://api.github.com/repos/brpc/brpc/tags","blobs_url":"https://api.github.com/repos/brpc/brpc/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/brpc/brpc/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/brpc/brpc/git/refs{/sha}","trees_url":"https://api.github.com/repos/brpc/brpc/git/trees{/sha}","statuses_url":"https://api.github.com/repos/brpc/brpc/statuses/{sha}","languages_url":"https://api.github.com/repos/brpc/brpc/languages","stargazers_url":"https://api.github.com/repos/brpc/brpc/stargazers","contributors_url":"https://api.github.com/repos/brpc/brpc/contributors","subscribers_url":"https://api.github.com/repos/brpc/brpc/subscribers","subscription_url":"https://api.github.com/repos/brpc/brpc/subscription","commits_url":"https://api.github.com/repos/brpc/brpc/commits{/sha}","git_commits_url":"https://api.github.com/repos/brpc/brpc/git/commits{/sha}","comments_url":"https://api.github.com/repos/brpc/brpc/comments{/number}","issue_comment_url":"https://api.github.com/repos/brpc/brpc/issues/comments{/number}","contents_url":"https://api.github.com/repos/brpc/brpc/contents/{+path}","compare_url":"https://api.github.com/repos/brpc/brpc/compare/{base}...{head}","merges_url":"https://api.github.com/repos/brpc/brpc/merges","archive_url":"https://api.github.com/repos/brpc/brpc/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/brpc/brpc/downloads","issues_url":"https://api.github.com/repos/brpc/brpc/issues{/number}","pulls_url":"https://api.github.com/repos/brpc/brpc/pulls{/number}","milestones_url":"https://api.github.com/repos/brpc/brpc/milestones{/number}","notifications_url":"https://api.github.com/repos/brpc/brpc/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/brpc/brpc/labels{/name}","releases_url":"https://api.github.com/repos/brpc/brpc/releases{/id}","deployments_url":"https://api.github.com/repos/brpc/brpc/deployments","created_at":"2017-09-04T09:28:47Z","updated_at":"2018-09-17T08:38:34Z","pushed_at":"2018-09-17T08:06:35Z","git_url":"git://github.com/brpc/brpc.git","ssh_url":"git@github.com:brpc/brpc.git","clone_url":"https://github.com/brpc/brpc.git","svn_url":"https://github.com/brpc/brpc","homepage":"","size":25858,"stargazers_count":7446,"watchers_count":7446,"language":"C++","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":1692,"mirror_url":null,"archived":false,"open_issues_count":71,"license":{"key":"apache-2.0","name":"Apache License 2.0","spdx_id":"Apache-2.0","url":"https://api.github.com/licenses/apache-2.0","node_id":"MDc6TGljZW5zZTI="},"forks":1692,"open_issues":71,"watchers":7446,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/brpc/brpc/pulls/465"},"html":{"href":"https://github.com/brpc/brpc/pull/465"},"issue":{"href":"https://api.github.com/repos/brpc/brpc/issues/465"},"comments":{"href":"https://api.github.com/repos/brpc/brpc/issues/465/comments"},"review_comments":{"href":"https://api.github.com/repos/brpc/brpc/pulls/465/comments"},"review_comment":{"href":"https://api.github.com/repos/brpc/brpc/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/brpc/brpc/pulls/465/commits"},"statuses":{"href":"https://api.github.com/repos/brpc/brpc/statuses/8451a663b2a943b42a41031b9d2885adf87f68fc"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"jamesge"}}