diff --git a/.travis.yml b/.travis.yml index 07e8536c..3288f12c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -42,7 +42,7 @@ before_script: before_install: - wget --no-clobber https://github.com/bazelbuild/bazel/releases/download/0.25.1/bazel_0.25.1-linux-x86_64.deb && sudo dpkg -i bazel_0.25.1-linux-x86_64.deb - sudo apt-get install automake bison flex g++ git libboost-all-dev libevent-dev libssl-dev libtool make pkg-config # thrift dependencies -- wget http://www.apache.org/dist/thrift/0.11.0/thrift-0.11.0.tar.gz && tar -xf thrift-0.11.0.tar.gz && cd thrift-0.11.0/ && ./configure --prefix=/usr --with-rs=no --with-ruby=no --with-python=no --with-java=no --with-go=no --with-perl=no --with-php=no --with-csharp=no --with-erlang=no --with-lua=no --with-nodejs=no CXXFLAGS="-Wno-unused-variable" && make -j4 && sudo make install && cd - +- wget https://archive.apache.org/dist/thrift/0.11.0/thrift-0.11.0.tar.gz && tar -xf thrift-0.11.0.tar.gz && cd thrift-0.11.0/ && ./configure --prefix=/usr --with-rs=no --with-ruby=no --with-python=no --with-java=no --with-go=no --with-perl=no --with-php=no --with-csharp=no --with-erlang=no --with-lua=no --with-nodejs=no CXXFLAGS="-Wno-unused-variable" && make -j4 && sudo make install && cd - install: - sudo apt-get install -qq realpath libgflags-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev libgoogle-perftools-dev libboost-dev libssl-dev libevent-dev libboost-test-dev libgoogle-glog-dev diff --git a/Dockerfile b/Dockerfile index 6474a7d1..253e43f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ # limitations under the License. # A image for building/testing brpc -FROM ubuntu:16.04 +FROM ubuntu:20.04 # prepare env RUN apt-get update && apt-get install -y --no-install-recommends \ @@ -37,6 +37,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ libsnappy-dev && \ apt-get clean -y -RUN git clone https://github.com/brpc/brpc.git brpc -RUN cd /brpc && sh config_brpc.sh --headers=/usr/include --libs=/usr/lib && \ +RUN git clone https://github.com/apache/incubator-brpc.git +RUN cd incubator-brpc && sh config_brpc.sh --headers=/usr/include --libs=/usr/lib && \ make -j "$(nproc)" diff --git a/LICENSE b/LICENSE index 1ce506fb..0d7ec6a3 100644 --- a/LICENSE +++ b/LICENSE @@ -476,7 +476,7 @@ src/butil/third_party/valgrind/valgrind.h: licensed under the following terms: -------------------------------------------------------------------------------- -src/butil/crc32c.h, src/butil/crc32c.cc: licensed under the following terms: +src/butil/crc32c.h, src/butil/crc32c.cc, test/crc32c_unittest.cc: licensed under the following terms: Copyright (c) 2011-present, Facebook, Inc. All rights reserved. This source code is licensed under the BSD-style license found in the @@ -663,7 +663,7 @@ src/brpc/ts.cpp: licensed under the following terms: -------------------------------------------------------------------------------- -src/brpc/builtin/pprof_perl.cpp: licensed under the following terms: +src/brpc/builtin/pprof_perl.cpp, tools/pprof: licensed under the following terms: Copyright (c) 1998-2007, Google Inc. All rights reserved. diff --git a/NOTICE b/NOTICE index e26721e3..ac6790ec 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ Apache brpc (incubating) -Copyright 2018-2020 The Apache Software Foundation +Copyright 2018-2021 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (http://www.apache.org/). diff --git a/RELEASE_VERSION b/RELEASE_VERSION index c81aa44a..3eefcb9d 100644 --- a/RELEASE_VERSION +++ b/RELEASE_VERSION @@ -1 +1 @@ -0.9.7 +1.0.0 diff --git a/community/cases.md b/community/cases.md new file mode 100644 index 00000000..d4823ac2 --- /dev/null +++ b/community/cases.md @@ -0,0 +1,43 @@ +# brpc的应用案例集合 + +## what is this +这里列出brpc在各个企业中的落地场景,包括企业名称,应用项目作用,集群规模和QPS统计,使用的版本信息等 + +## Why this +列出各个案例,一来方便用户进行参考,了解brpc可以用在哪些场景下; +二来方便社区开发者统计brpc的版本,规模等情况 + +# Case List +## 落地case的sample (如果有多个场景,建议分开) +* 公司名称: xxx公司 +* 落地项目: 例如app的个性化推荐系统的预测服务 +* 集群规模: 例如100台 +* QPS: 例如峰值1000万, 均值100万 +* 使用版本: 例如社区版本0.9.7 +* 信息提供者:某某 + +## brpc在 百度的落地情况 +* 公司名称: 百度 +* 落地项目: 基础架构(分布式计算、存储、数据库等),业务系统(Feed、凤巢、地图等) +* 集群规模: 2000多个活跃模块,100w以上实例 +* 使用版本: baidu内部版本 +* 信息提供者:wwbmmm + +## brpc在维沃的落地情况 +* 公司名称: 维沃(vivo) +* 落地项目: 在线推荐系统 +* 使用版本: 社区版本0.9.7 +* 信息提供者:guodongxiaren + +## brpc在爱奇艺的落地情况 +* 公司名称: 爱奇艺(iqiyi) +* 落地项目: 广告、推荐、搜索 +* 使用版本: 基于社区版本定制 +* 集群规模: 3000+台机器(广告) +* 信息提供者:cdjingit + +## brpc在第四范式的落地情况 +* 公司名称: 第四范式(4paradigm) +* 落地项目: 风控、推荐、智能运维等 +* 使用版本: 基于社区版本定制 +* 信息提供者:dl239 diff --git a/community/logo-bRPC.svg b/community/logo-bRPC.svg new file mode 100644 index 00000000..bd7c97d8 --- /dev/null +++ b/community/logo-bRPC.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/community/newcommitter.md b/community/newcommitter.md new file mode 100644 index 00000000..ed776f9d --- /dev/null +++ b/community/newcommitter.md @@ -0,0 +1,27 @@ +# 这里记录new committer相关的流程和网站 + + +## 成为committer的路程 +1. 在private邮件中发起讨论和投票 +2. 投票通过即OK +3. 让他填写CLA并发给secretary@apache.org + + +## 如何赋予committer在github上的权限 + +1. 加为committer +https://whimsy.apache.org/roster/ppmc/brpc + +2. 让他设置github id +https://id.apache.org/ + +3. 让他访问该网址,获得github的权限 +https://gitbox.apache.org/setup/ + + +## apache 官网new committer相关的文档 + +https://community.apache.org/newcommitter.html + +https://infra.apache.org/new-committers-guide.html + diff --git a/community/oncall.md b/community/oncall.md new file mode 100644 index 00000000..e762e269 --- /dev/null +++ b/community/oncall.md @@ -0,0 +1,38 @@ +# 值周工程师的职责如下 + +## 1. 每天查看github上brpc项目待处理的Pull Request和Issue列表,负责问题的处理 + * 包括标记issue,回复issue,关闭issue等; + * 判断issue是否是长期Issue,如果是则标记为Pending + * 判断Issue的类型,例如bug,enhancement, discussion等 + * 把issue分配到熟悉该模块的贡献者(可在微信群里询问谁来负责) + +## 2. 轮值时间为一周 + * 即从周日早上到下周六晚上 + +## 3. 轮值结束需要 + * 编写值周report,并发送到dev@brpc.apache.org邮件群中 + * 提醒下一位轮值同学 + +## 4. 值周顺序如下 + * 朱佳顺 @zyearn + * 李磊 @lorinlee + * 王伟冰 @wwbmmm + * 蔡道进 @cdjingit + * 何磊 @TousakaRin + +## 5. 值周记录如下 + +| 时间(月/日/年) | 值周人 | 值周report| +| ---- | ---- | --- | +| | 朱佳顺 | +| 06/21/2021 to 06/27/2021 | 李磊 | https://lists.apache.org/thread.html/rc9eb911bc6465c16bd8bb14f9d1f7c9548e13759101f43cf0300908a%40%3Cdev.brpc.apache.org%3E +| 06/28/2021 to 07/04/2021 | 蔡道进 | https://lists.apache.org/thread.html/r23992a4f958bea5b28b9b87a09c60790dae8d5d28f2c49aded3f1760%40%3Cdev.brpc.apache.org%3E +| 07/05/2021 - 07/12/2021 | 何磊 | https://lists.apache.org/thread.html/r3f67bc42ecdc07b25c39aedaf30aeae79039aa1cc3e6ad99364c8178%40%3Cdev.brpc.apache.org%3E +| 08/16/2021 - 08/22/2021 | 朱佳顺 | https://lists.apache.org/thread.html/rc03364574b40e4e9bb8112eae0500589d632011f535a4f0747c4ce5f%40%3Cdev.brpc.apache.org%3E +| 08/23/2021 - 08/29/2021 | 李磊 | https://lists.apache.org/thread.html/r10522af87df62aa0aa48f5a279cef219db1917048108cb744e53772e%40%3Cdev.brpc.apache.org%3E +| 08/30/2021 - 09/05/2021 | 蔡道进 |https://lists.apache.org/thread.html/r6e9846a17464581528280b43d835000da78ee700d2fa195cb92d2353%40%3Cdev.brpc.apache.org%3E +| 09/06/2021 - 09/12/2021 | 何磊 | https://lists.apache.org/thread.html/ra10e10abca213e0e5e797a74af8a9c8810e54fd1543c108bf03a56e5%40%3Cdev.brpc.apache.org%3E +| 09/20/2021 - 09/26/2021 | 李磊 | https://lists.apache.org/thread.html/r19c08874ad681edbc82526a09b34d3ccabe7cf168ed91935575ff557%40%3Cdev.brpc.apache.org%3E +| 09/27/2021 - 10/03/2021 | 蔡道进 | https://lists.apache.org/thread.html/re3391d87f2e2aba55986b96b18ca375f07840bac67249fced2f851f5%40%3Cdev.brpc.apache.org%3E +| 13/12/2021 - 19/12/2021 | 蔡道进 | https://lists.apache.org/thread/mvclsy79859mrbdso1xzm6y7yz3lg6w0 +| 01/24/2022 - 02/06/2022 | 王伟冰 | https://lists.apache.org/thread/ttgqnw4hfw0qnb7swvnn2kxb5b9hkdbo diff --git a/community/release_cn.md b/community/release_cn.md new file mode 100644 index 00000000..6df88234 --- /dev/null +++ b/community/release_cn.md @@ -0,0 +1,558 @@ +brpc 发布apache release 版本流程step by step +=== +概述:分为如下几个步骤 +1. 事前准备:包括生成签名需要的key,github上打标签,修改version文件等 +2. 发布软件包:包括制作source tarball,签名,上传到制定地点并验证 +3. 第一次投票:在dev@brpc邮件群里投票 +4. 第二次投票:在general@brpc邮件群里投票 +5. 发版通告:包括发邮件,更新brpc网站 + + +# 签名准备 + +## 1. 安装 GPG +在[GnuPG官网](https://www.gnupg.org/download/index.html)下载安装包。 GnuPG的1.x版本和2.x版本的命令有细微差别,下列说明以`GnuPG-2.3.1`版本(OSX)为例。 + +安装完成后,执行以下命令查看版本号。 +```bash +gpg --version +``` + +## 2. 创建 key + +安装完成后,执行以下命令创建key。 + +```bash +gpg --full-gen-key +``` + +根据提示完成创建key,注意邮箱要使用Apache邮件地址: +``` +gpg (GnuPG) 2.3.1; Copyright (C) 2021 Free Software Foundation, Inc. +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +Please select what kind of key you want: + (1) RSA and RSA + (2) DSA and Elgamal + (3) DSA (sign only) + (4) RSA (sign only) + (9) ECC (sign and encrypt) *default* + (10) ECC (sign only) + (14) Existing key from card +Your selection? 1 +RSA keys may be between 1024 and 4096 bits long. +What keysize do you want? (3072) 4096 +Requested keysize is 4096 bits +Please specify how long the key should be valid. + 0 = key does not expire + = key expires in n days + w = key expires in n weeks + m = key expires in n months + y = key expires in n years +Key is valid for? (0) 0 +Key does not expire at all +Is this correct? (y/N) y + +GnuPG needs to construct a user ID to identify your key. + +Real name: LorinLee +Email address: lorinlee@apache.org +Comment: lorinlee's key +You selected this USER-ID: + "LorinLee (lorinlee's key) " + +Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O +You need a Passphrase to protect your secret key. # 输入密码 + +We need to generate a lot of random bytes. It is a good idea to perform +some other action (type on the keyboard, move the mouse, utilize the +disks) during the prime generation; this gives the random number +generator a better chance to gain enough entropy. +gpg: key 92E18A11B6585834 marked as ultimately trusted +gpg: revocation certificate stored as '/Users/lilei/.gnupg/openpgp-revocs.d/C30F211F071894258497F46392E18A11B6585834.rev' +public and secret key created and signed. + +pub rsa4096 2021-10-17 [SC] + C30F211F071894258497F46392E18A11B6585834 +uid LorinLee (lorinlee's key) +sub rsa4096 2021-10-17 [E] +``` + +## 3. 查看生成的key + +```bash +gpg --list-keys +``` + +执行结果: + +``` +gpg: checking the trustdb +gpg: marginals needed: 3 completes needed: 1 trust model: pgp +gpg: depth: 0 valid: 2 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 2u +/Users/lilei/.gnupg/pubring.kbx +---------------------------------- +pub rsa4096 2021-10-17 [SC] + C30F211F071894258497F46392E18A11B6585834 +uid [ultimate] LorinLee (lorinlee's key) +sub rsa4096 2021-10-17 [E] +``` + +其中 `C30F211F071894258497F46392E18A11B6585834` 为公钥ID。 + +## 4. 将公钥公布到服务器 + +命令如下: + +```bash +gpg --keyserver hkp://pgp.mit.edu --send-key C30F211F071894258497F46392E18A11B6585834 +``` + +## 5. 生成 fingerprint 并上传到 apache 用户信息中 +由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。通常,你可以在⽹站上公布一个公钥指纹,让其他⼈核对下载到的公钥是否为真。fingerprint参数生成公钥指纹。 + +执行如下命令查看 fingerprint: +``` +gpg --fingerprint lorinlee(用户ID) +``` + +输出如下: +``` +/Users/lilei/.gnupg/pubring.kbx +---------------------------------- +pub rsa4096 2021-10-17 [SC] + C30F 211F 0718 9425 8497 F463 92E1 8A11 B658 5834 +uid [ultimate] LorinLee (lorinlee's key) +sub rsa4096 2021-10-17 [E] +``` + +将上面的 fingerprint 粘贴到⾃己的⽤户信息中: https://id.apache.org + +# 发布包准备 + +## 1. 编辑 RELEASE_VERSION 文件 + +编辑项目根目录下`RELEASE_VERSION`文件,更新版本号,并提交至代码仓库,本文以`1.0.0`版本为例,文件内容为: + +``` +1.0.0 +``` + +## 2. 创建发布 tag +拉取发布分支,并推送tag +```bash +git clone -b ${branch} git@github.com:apache/incubator-brpc.git ~/incubator-brpc + +cd ~/incubator-brpc + +git tag -a 1.0.0 -m "release 1.0.0" + +git push origin --tags +``` + +## 3. 打包发布包 + +```bash +git archive --format=tar 1.0.0 --prefix=apache-brpc-1.0.0-incubating-src/ | gzip > apache-brpc-1.0.0-incubating-src.tar.gz +``` + +## 4. 生成签名文件 + +```bash +gpg -u lorinlee@apache.org --armor --output apache-brpc-1.0.0-incubating-src.tar.gz.asc --detach-sign apache-brpc-1.0.0-incubating-src.tar.gz + +gpg --verify apache-brpc-1.0.0-incubating-src.tar.gz.asc apache-brpc-1.0.0-incubating-src.tar.gz + +``` + +## 5. 生成哈希文件 + +```bash +sha512sum apache-brpc-1.0.0-incubating-src.tar.gz > apache-brpc-1.0.0-incubating-src.tar.gz.sha512 + +sha512sum --check apache-brpc-1.0.0-incubating-src.tar.gz.sha512 +``` + +# 发布至Apache SVN仓库 + +## 1. 检出 dist/dev 下的 brpc 仓库目录 + +如无本地工作目录,则先创建本地工作目录。将Apache SVN仓库克隆下来,username需要使用自己的Apache LDAP用户名 + +```bash +mkdir -p ~/brpc_svn/dev/ + +cd ~/brpc_svn/dev/ + +svn --username=lorinlee co https://dist.apache.org/repos/dist/dev/incubator/brpc/ + +cd ~/brpc_svn/dev/brpc +``` + +## 2. 添加GPG公钥 + +仅第一次部署的账号需要添加,只要KEYS中包含已经部署过的账户的公钥即可。 + +``` +gpg -a --export lorinlee >> KEYS +``` + +## 3. 将待发布的代码包添加至SVN目录 + +```bash +mkdir -p ~/brpc_svn/dev/brpc/1.0.0 + +cd ~/brpc_svn/dev/brpc/1.0.0 + +cp ~/incubator-brpc/apache-brpc-1.0.0-incubating-src.tar.gz ~/brpc_svn/dev/brpc/1.0.0 + +cp ~/incubator-brpc/apache-brpc-1.0.0-incubating-src.tar.gz.asc ~/brpc_svn/dev/brpc/1.0.0 + +cp ~/incubator-brpc/apache-brpc-1.0.0-incubating-src.tar.gz.sha512 ~/brpc_svn/dev/brpc/1.0.0 +``` + +## 4. 提交SVN + +使用Apache LDAP账号提交SVN + +```bash +svn add * + +svn --username=lorinlee commit -m "release 1.0.0" +``` + +# 检查发布结果 + +## 1. 检查sha512哈希 + +```bash +sha512sum --check apache-brpc-1.0.0-incubating-src.tar.gz.sha512 +``` + +## 2. 检查GPG签名 +首先导入发布人公钥。从svn仓库导入KEYS到本地环境。(发布版本的人不需要再导入,帮助做验证的人需要导入,用户名填发版人的即可) + +```bash +curl https://dist.apache.org/repos/dist/dev/incubator/brpc/KEYS >> KEYS + +gpg --import KEYS +``` + +设置信任该用户的签名,执行以下命令,填写发布人的用户名 +```bash +gpg --edit-key lorinlee +``` + +输出为 +``` +gpg (GnuPG) 2.3.1; Copyright (C) 2021 Free Software Foundation, Inc. +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +Secret key is available. + +gpg> trust + +Please decide how far you trust this user to correctly verify other users' keys +(by looking at passports, checking fingerprints from different sources, etc.) + + 1 = I don't know or won't say + 2 = I do NOT trust + 3 = I trust marginally + 4 = I trust fully + 5 = I trust ultimately + m = back to the main menu + +Your decision? 5 +Do you really want to set this key to ultimate trust? (y/N) y + +gpg> save +``` + +然后进行gpg签名检查。 +``` +gpg --verify apache-brpc-1.0.0-incubating-src.tar.gz.asc apache-brpc-1.0.0-incubating-src.tar.gz +``` + +## 3. 检查发布内容 + +### 1. 对比源码包与github上的tag内容差异 + +```bash +curl -Lo tag-1.0.0.tar.gz https://github.com/apache/incubator-brpc/archive/refs/tags/1.0.0.tar.gz + +tar xvzf tag-1.0.0.tar.gz + +tar xvzf apache-brpc-1.0.0-incubating-src.tar.gz + +diff -r brpc-1.0.0 apache-brpc-1.0.0-incubating-src +``` + +### 2. 检查源码包的文件内容 + +- 检查源码包是否包含由于包含不必要文件,致使tarball过于庞大 +- 存在LICENSE和NOTICE文件 +- NOTICE文件中的年份正确 +- 只存在文本文件,不存在二进制文件 +- 所有文件的开头都有ASF许可证 +- 能够正确编译,单元测试可以通过 +- 检查是否有多余文件或文件夹,例如空文件夹等 +- 检查第三方依赖许可证: + - 第三方依赖的许可证兼容 + - 所有第三方依赖的许可证都在LICENSE文件中声名 + - 依赖许可证的完整版全部在license目录 + - 如果依赖的是Apache许可证并且存在NOTICE文件,那么这些NOTICE文件也需要加入到版本的NOTICE文件中 + +# 在Apache brpc社区发起投票 + +## 1. 投票阶段 + +1. brpc社区投票,发起投票邮件到dev@brpc.apache.org。PPMC需要先按文档检查版本的正确性,然后再进行投票。经过至少72小时并统计到3个+1 PPMC member票后,即可进入下一阶段。 +2. 宣布投票结果,发起投票结果邮件到dev@brpc.apache.org。 + +## 2. 投票邮件模板 + +1. Apache brpc 社区投票邮件模板 + +标题: +``` +[VOTE] Release Apache brpc (Incubating) 1.0.0 +``` + +正文: +``` +Hello Apache brpc (Incubating) Community, + +This is a call for vote to release Apache brpc (Incubating) version +1.0.0 + +[Release Note] + - xxx + +The release candidates: +https://dist.apache.org/repos/dist/dev/incubator/brpc/1.0.0/ + +Git tag for the release: +https://github.com/apache/incubator-brpc/releases/tag/1.0.0 + +Release Commit ID: +https://github.com/apache/incubator-brpc/commit/xxx + +Keys to verify the Release Candidate: +https://dist.apache.org/repos/dist/dev/incubator/brpc/KEYS + +The vote will be open for at least 72 hours or until necessary number of +votes are reached. + +Please vote accordingly: +[ ] +1 approve +[ ] +0 no opinion +[ ] -1 disapprove with the reason + +PMC vote is +1 binding, all others is +1 non-binding. + +Checklist for reference: +[ ] Download links are valid. +[ ] Checksums and PGP signatures are valid. +[ ] Source code distributions have correct names matching the current +release. +[ ] LICENSE and NOTICE files are correct for each brpc repo. +[ ] All files have license headers if necessary. +[ ] No compiled archives bundled in source archive. + +Regards, +LorinLee +``` + +2. Apache brpc 社区宣布结果邮件模板 + +标题: +``` +[Result] [VOTE] Release Apache brpc (Incubating) 1.0.0 +``` + +正文: +``` +Hi all, + +The vote to release Apache brpc (Incubating) 1.0.0 has passed. + +The vote PASSED with 3 binding +1, 1 non binding +1 and 0 -1 votes: + +Binding votes: + - xxx + - yyy + - zzz + +Non-binding votes: + - aaa + +Vote thread: xxx (vote email link in https://lists.apache.org/) + +Thank you to all the above members to help us to verify and vote for the 1.0.0 release. We will move to IPMC voting shortly. + +Regards, +LorinLee +``` + +## 3. 投票未通过 + +若社区投票未通过,则对代码仓库进行修改,重新打包,发起投票。 + +# 在Apache Incubator社区发起投票 + +## 1. 投票阶段 + +1. Incubator社区投票,发起投票邮件到general@incubator.apache.org。IPMC会进行投票。经过至少72小时并统计到3个+1 IPMC member票后,即可进入下一阶段。 +2. 宣布投票结果,发起投票结果邮件到general@incubator.apache.org。 + +## 2. 投票邮件模板 + +1. Apache Incubator 社区投票邮件模板 + +标题: +``` +[VOTE] Release Apache brpc (Incubating) 1.0.0 +``` + +正文: +``` +Hello IPMC, + +The Apache brpc community has voted and approved the release of Apache +brpc (Incubating) 1.0.0. We now kindly request the IPMC members +review and vote for this release. + +brpc is an industrial-grade RPC framework with extremely high performance, +and it supports multiple protocols, full rpc features, and has many +convenient tools. The current release provides the first official and +stable package, resolves all of the license issues, and fixes many bugs. + +brpc community vote thread: xxx + +Vote result thread: xxx + +The release candidate: +https://dist.apache.org/repos/dist/dev/incubator/brpc/1.0.0/ + +This release has been signed with a PGP available here: +https://dist.apache.org/repos/dist/dev/incubator/brpc/KEYS + +Git tag for the release: +https://github.com/apache/incubator-brpc/releases/tag/1.0.0 + +Build guide and get started instructions can be found at: +https://github.com/apache/incubator-brpc/blob/master/docs/en/getting_started.md + +The vote will be open for at least 72 hours or until the necessary number +of votes is reached. + +Please vote accordingly: +[ ] +1 Approve the release of Apache brpc (Incubating) 1.0.0 +[ ] +0 +[ ] -1 Do not approve (please specify the reason) + +Regards, +Lorin Lee +Apache brpc (Incubating) +``` + +2. Apache Incubator 社区宣布结果邮件模板 + +标题: +``` +[Result] [VOTE] Release Apache brpc (Incubating) 1.0.0 +``` + +正文: +``` +Hello Incubator Community, + +Thanks to everyone that participated. The vote to release Apache +brpc (Incubating) version 1.0.0 in general@incuabator +is now closed. + +Vote thread: xxx (vote email link in https://lists.apache.org/) + +The vote PASSED with 3 binding +1, 1 non binding +1 and 0 -1 votes: + +Binding votes: + - xxx + - yyy + - zzz + +Non-binding votes: + - aaa + +I will be working on publishing the artifacts of Apache brpc +(Incubating) 1.0.0 and post an announcement. Thanks! + +Regards, +Lorin Lee +Apache brpc (Incubating) +``` + +# 完成发布 + +## 1. 将发布包从Apache SVN仓库 dist/dev 移动至 dist/release + +``` +svn mv https://dist.apache.org/repos/dist/dev/incubator/brpc/1.0.0 https://dist.apache.org/repos/dist/release/incubator/brpc/1.0.0 -m "release brpc 1.0.0" + +svn delete https://dist.apache.org/repos/dist/release/incubator/brpc/KEYS -m "delete KEYS" + +svn cp https://dist.apache.org/repos/dist/dev/incubator/brpc/KEYS https://dist.apache.org/repos/dist/release/incubator/brpc/KEYS -m "update brpc KEYS" +``` + +## 2. Github版本发布 + +在 GitHub Releases 页面的对应版本上点击 Edit +编辑版本号及版本说明,并点击 Publish release + +## 3. 更新下载页面 + +等待并确认新的发布版本同步至 Apache 镜像后,更新如下页面: +`https://brpc.apache.org/download/`,更新方式在 `https://github.com/apache/incubator-brpc-website/` 仓库中。 + +GPG签名文件和哈希校验文件的下载链接应该使用这个前缀:https://downloads.apache.org/incubator/brpc/ + +代码包的下载链接应该使用这个前缀:https://www.apache.org/dyn/closer.lua/incubator/brpc/ + +## 4. 发送邮件通知发布完成 + +发送邮件到dev@brpc.apache.org和announce@apache.org通知完成版本发布,通知邮件模板如下: + +标题: +``` +[ANNOUNCE] Apache brpc 1.0.0 +``` + +正文 +``` +Hi all, + +The Apache brpc (incubating) community is pleased to announce that Apache +brpc (incubating) 1.0.0 has been released! + +brpc is an industrial-grade RPC framework with extremely high performance, +and it supports multiple protocols, full rpc features, and has many +convenient tools. + +Brief notes of this release: + - xxx + - yyy + +More details regarding Apache brpc can be found at: +http://brpc.apache.org/ + +The release artifacts can be downloaded here: +https://brpc.apache.org/download/ + +The release notes can be found here: +https://github.com/apache/incubator-brpc/releases/tag/1.0.0 + +Thanks, +Lorin Lee +Apache brpc (Incubating) +``` diff --git a/community/releasecheck.md b/community/releasecheck.md new file mode 100644 index 00000000..2bb1bd3b --- /dev/null +++ b/community/releasecheck.md @@ -0,0 +1,46 @@ +# brpc 发版时候的Check List + +## 文档背景: +当Release Manager在dev邮件群中发起发布投票的时候,需要PMC成员对发版相关信息进行检查,如果检查通过则在邮件群中回复通过并附上检查结果。本文档就是各个检查项。 + +## Checklist 资料来源 +根据Incubator PMC Chair Justin在ApacheCon North America 2019的分享 +https://training.apache.org/topics/ApacheWay/NavigatingASFIncubator/index.html + +![image](./releasecheck.png) + + +## 常见的问题导致-1 +![image](./releasefail.png) + +## Vote时候的检查项 +1. Incubating in name 即下载包的文件名是否带上了incubating +2. LICENSE and NOTICE are good + - LICENSE是否符合Apache的要求 + - ASF允许的LICENSE:Category A(Apache V2,BSD 3,MIT etc) + - ASF建议不放到源码中的LICENSE:Category B(EPL,MPL,CDDL,etc) + - ASF不允许的LICENSE:Category X(GPL,LGPL,CC Non commercial,etc) + - NOTICE是否符合Apache的要求 +3. Signatures and hashes correct +4. All ASF files have ASF headers +5. No unexpected binary files +6. Must have an incubating disclaimer + - Repo根目录下应该有DISCLAIMER文件 + - 内容是Apache统一的内容 + + +## Vote时候的常用回复 + +“+1 (binding) + + I checked: + - incubating in name + - LICENSE and NOTICE are good + - signatures and hashes correct + - All ASF files have ASF headers + - no unexpected binary files + +## 注意: +1. 不要回复简单的+1,需要加上执行的几个检查项和检查结果 +2. 给出-1的时候,同样需要给出明确的理由 + diff --git a/community/releasecheck.png b/community/releasecheck.png new file mode 100644 index 00000000..9bc1141d Binary files /dev/null and b/community/releasecheck.png differ diff --git a/community/releasefail.png b/community/releasefail.png new file mode 100644 index 00000000..580f7d80 Binary files /dev/null and b/community/releasefail.png differ diff --git a/config_brpc.sh b/config_brpc.sh index 1720031f..f52911db 100755 --- a/config_brpc.sh +++ b/config_brpc.sh @@ -161,7 +161,7 @@ else OPENSSL_LIB=$(find_dir_of_lib ssl) # Inconvenient to check these headers in baidu-internal #PTHREAD_HDR=$(find_dir_of_header_or_die pthread.h) - OPENSSL_HDR=$(find_dir_of_header_or_die openssl/ssl.h) + OPENSSL_HDR=$(find_dir_of_header_or_die openssl/ssl.h mesalink/openssl/ssl.h) fi if [ $WITH_MESALINK != 0 ]; then diff --git a/docs/cn/atomic_instructions.md b/docs/cn/atomic_instructions.md index 1d92ab15..e5a14cb8 100644 --- a/docs/cn/atomic_instructions.md +++ b/docs/cn/atomic_instructions.md @@ -39,7 +39,7 @@ ```c++ // Thread 1 -// ready was initialized to false +// bool ready was initialized to false p.init(); ready = true; ``` @@ -72,7 +72,7 @@ if (ready) { ```c++ // Thread1 -// ready was initialized to false +// std::atomic ready was initialized to false p.init(); ready.store(true, std::memory_order_release); ``` diff --git a/docs/cn/bthread_id.md b/docs/cn/bthread_id.md index 385b65b7..d6ba8a2c 100644 --- a/docs/cn/bthread_id.md +++ b/docs/cn/bthread_id.md @@ -23,5 +23,14 @@ bthread_id的接口不太简洁,有不少API: 这么多接口是为了满足不同的使用流程。 -- 发送request的流程:create -> lock -> ... register timer and send RPC ... -> unlock -- 接收response的流程:lock -> ..process response -> call done +- 发送request的流程:bthread_id_create -> bthread_id_lock -> ... register timer and send RPC ... -> bthread_id_unlock +- 接收response的流程:bthread_id_lock -> ..process response -> bthread_id_unlock_and_destroy +- 异常处理流程:timeout/socket fail -> bthread_id_error -> 执行on_error回调(这里会加锁),分两种情况 + - 请求重试/backup request: 重新register timer and send RPC -> bthread_id_unlock + - 无法重试,最终失败:bthread_id_unlock_and_destroy +- 同步等待RPC结束:bthread_id_join + +为了减少等待,bthread_id做了一些优化的机制: + +- error发生的时候,如果bthread_id已经被锁住,会把error信息放到一个pending queue中,bthread_id_error函数立即返回。当bthread_id_unlock的时候,如果pending queue里面有任务就取出来执行。 +- RPC结束的时候,如果存在用户回调,先执行一个bthread_id_about_to_destroy,让正在等待的bthread_id_lock操作立即失败,再执行用户回调(这个可能耗时较长,不可控),最后再执行bthread_id_unlock_and_destroy diff --git a/docs/cn/bvar_c++.md b/docs/cn/bvar_c++.md index 7888b39a..60034912 100644 --- a/docs/cn/bvar_c++.md +++ b/docs/cn/bvar_c++.md @@ -54,7 +54,7 @@ foo::bar::g_task_pushed << 1; - `bvar::PerSecond` : 获得某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量。 - `bvar::LatencyRecorder` : 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了。 -**确认变量名是全局唯一的!**否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。 +**确认变量名是全局唯一的!** 否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。 程序中有来自各种模块不同的bvar,为避免重名,建议如此命名:**模块_类名_指标** @@ -87,7 +87,7 @@ rpc_socket_count : 6 # 模块=rpc 类名=socket ```c++ namespace foo { namespace bar { -// 注意g_read_error_minute和g_task_pushed_per_second都是衍生的bvar,会自动更新,不要声明。 +// 注意g_read_error_minute和g_task_pushed_second都是衍生的bvar,会自动更新,不要声明。 extern bvar::Adder g_read_error; extern bvar::LatencyRecorder g_write_latency; extern bvar::Adder g_task_pushed; @@ -148,7 +148,7 @@ int expose_as(const butil::StringPiece& prefix, const butil::StringPiece& name); ``` 全局曝光后的bvar名字便为name或prefix + name,可通过以_exposed为后缀的static函数查询。比如Variable::describe_exposed(name)会返回名为name的bvar的描述。 -当相同名字的bvar已存在时,expose会打印FATAL日志并返回-1。如果选项**--bvar_abort_on_same_name**设为true (默认是false),程序会直接abort。 +当相同名字的bvar已存在时,expose会打印FATAL日志并返回-1。如果选项 **-bvar_abort_on_same_name**设为true (默认是false),程序会直接abort。 下面是一些曝光bvar的例子: ```c++ @@ -221,7 +221,7 @@ dump功能由如下gflags控制: | ------------------ | ----------------------- | ---------------------------------------- | | bvar_dump | false | Create a background thread dumping all bvar periodically, all bvar_dump_* flags are not effective when this flag is off | | bvar_dump_exclude | "" | Dump bvar excluded from these wildcards(separated by comma), empty means no exclusion | -| bvar_dump_file | monitor/bvar..data | Dump bvar into this file | +| bvar_dump_file | monitor/bvar.\.data | Dump bvar into this file | | bvar_dump_include | "" | Dump bvar matching these wildcards(separated by comma), empty means including all | | bvar_dump_interval | 10 | Seconds between consecutive dump | | bvar_dump_prefix | \ | Every dumped name starts with this prefix | diff --git a/docs/cn/circuit_breaker.md b/docs/cn/circuit_breaker.md index 63aaed1b..bf7b4803 100644 --- a/docs/cn/circuit_breaker.md +++ b/docs/cn/circuit_breaker.md @@ -22,11 +22,11 @@ option.enable_circuit_breaker = true; 可选的熔断由CircuitBreaker实现,在开启了熔断之后,CircuitBreaker会记录每一个请求的处理结果,并维护一个累计出错时长,记为acc_error_cost,当acc_error_cost > max_error_cost时,熔断该节点。 **每次请求返回成功之后,更新max_error_cost:** -1. 首先需要更新latency的EMA值,记为ema_latency: ema_latency = ema_latency * alpha + (1 - alpha) * latency。 +1. 首先需要更新latency的[EMA](https://en.wikipedia.org/wiki/Moving_average)值,记为ema_latency: ema_latency = ema_latency * alpha + (1 - alpha) * latency。 2. 之后根据ema_latency更新max_error_cost: max_error_cost = window_size * max_error_rate * ema_latency。 -上面的window_size和max_error_rate均为gflag所指定的常量, alpha则是一个略小于1的常量,其值由window_size和下面提到的circuit_breaker_epsilon_value决定。latency则指该次请求所的耗时。 +上面的window_size和max_error_rate均为gflag所指定的常量, alpha则是一个略小于1的常量,其值由window_size和下面提到的circuit_breaker_epsilon_value决定。latency则指该次请求的耗时。 **每次请求返回之后,都会更新acc_error_cost:** 1. 如果请求处理成功,则令 acc_error_cost = alpha * acc_error_cost @@ -40,7 +40,7 @@ option.enable_circuit_breaker = true; 为了允许某个节点在短时间内抖动,同时又能够剔除长期错误率较高的节点,CircuitBreaker同时维护了长短两个窗口,长窗口阈值较低,短窗口阈值较高。长窗口的主要作用是剔除那些长期错误率较高的服务。我们可以根据实际的qps及对于错误的容忍程度来调整circuit_breaker_long_window_size及circuit_breaker_long_window_error_percent。 -短窗口则允许我们更加精细的控制熔断的灵敏度,在一些对抖动很敏感的场景,可以通过调整circuit_breaker_short_window_size和circuit_breaker_long_window_short_percent来缩短短窗口的长度、降低短窗口对于错误的容忍程度,使得出现抖动时能够快速对故障节点进行熔断。 +短窗口则允许我们更加精细的控制熔断的灵敏度,在一些对抖动很敏感的场景,可以通过调整circuit_breaker_short_window_size和circuit_breaker_short_window_error_percent来缩短短窗口的长度、降低短窗口对于错误的容忍程度,使得出现抖动时能够快速对故障节点进行熔断。 此外,circuit_breaker_epsilon_value可以调整窗口对于**连续抖动的容忍程度**,circuit_breaker_epsilon_value的值越低,计算公式中的alpha越小,acc_error_cost下降的速度就越快,当circuit_breaker_epsilon_value的值达到0.001时,若一整个窗口的请求都没有出错,那么正好可以把acc_error_cost降低到0。 diff --git a/docs/cn/client.md b/docs/cn/client.md index c49b5e3d..58fd7de4 100755 --- a/docs/cn/client.md +++ b/docs/cn/client.md @@ -48,11 +48,15 @@ int Init(const char* server_addr, int port, const ChannelOptions* options); - 127.0.0.1:80 - www.foo.com:8765 - localhost:9000 +- [::1]:8080 # IPV6 +- unix:path.sock # Unix domain socket 不合法的"server_addr_and_port": - 127.0.0.1:90000 # 端口过大 - 10.39.2.300:8000 # 非法的ip +关于IPV6和Unix domain socket的使用,详见 [EndPoint](endpoint.md)。 + # 连接服务集群 ```c++ @@ -214,6 +218,8 @@ int main() { 即weighted round robin, 根据服务器列表配置的权重值来选择服务器。服务器被选到的机会正比于其权重值,并且该算法能保证同一服务器被选到的结果较均衡的散开。 +实例的tag需要是表示权值的int32数字,如tag="50"。 + ### random 随机从列表中选择一台服务器,无需其他设置。和round robin类似,这个算法的前提也是服务器都是类似的。 @@ -222,6 +228,8 @@ int main() { 即weighted random, 根据服务器列表配置的权重值来选择服务器,服务器被选到的机会正比于其权重值。 +实例tag的要求同wrr。 + ### la locality-aware,优先选择延时低的下游,直到其延时高于其他机器,无需其他设置。实现原理请查看[Locality-aware load balancing](lalb.md)。 @@ -238,6 +246,8 @@ locality-aware,优先选择延时低的下游,直到其延时高于其他机 实现原理请查看[Consistent Hashing](consistent_hashing.md)。 +其他lb不需要设置Controller.set_request_code(),如果调用了request_code也不会被lb使用,例如:lb=rr调用了Controller.set_request_code(),即使所有RPC的request_code都相同,也依然是rr。 + ### 从集群宕机后恢复时的客户端限流 集群宕机指的是集群中所有server都处于不可用的状态。由于健康检查机制,当集群恢复正常后,server会间隔性地上线。当某一个server上线后,所有的流量会发送过去,可能导致服务再次过载。若熔断开启,则可能导致其它server上线前该server再次熔断,集群永远无法恢复。作为解决方案,brpc提供了在集群宕机后恢复时的限流机制:当集群中没有可用server时,集群进入恢复状态,假设正好能服务所有请求的server数量为min_working_instances,当前集群可用的server数量为q,则在恢复状态时,client接受请求的概率为q/min_working_instances,否则丢弃;若一段时间hold_seconds内q保持不变,则把流量重新发送全部可用的server上,并离开恢复状态。在恢复阶段时,可以通过判断controller.ErrorCode()是否等于brpc::ERJECT来判断该次请求是否被拒绝,被拒绝的请求不会被框架重试。 @@ -291,6 +301,12 @@ if (cntl->Failed()) { } ``` +> 警告: 请勿在持有pthread锁的情况下,调用brpc的同步CallMethod!否则很容易导致死锁。 +> +> 解决方案(二选一): +> 1. 将pthread锁换成bthread锁(bthread_mutex_t) +> 1. 在CallMethod之前将锁释放 + ## 异步访问 指的是:给CallMethod传递一个额外的回调对象done,CallMethod在发出request后就结束了,而不是在RPC结束后。当server端返回response或发生错误(包括超时)时,done->Run()会被调用。对RPC的后续处理应该写在done->Run()里,而不是CallMethod后。 @@ -299,7 +315,11 @@ if (cntl->Failed()) { 你可以独立地创建这些对象,并使用[NewCallback](#使用NewCallback)生成done,也可以把Response和Controller作为done的成员变量,[一起new出来](#继承google::protobuf::Closure),一般使用前一种方法。 -**发起异步请求后Request和Channel也可以立刻析构**。这两样和response/controller是不同的。注意:这是说Channel的析构可以立刻发生在CallMethod**之后**,并不是说析构可以和CallMethod同时发生,删除正被另一个线程使用的Channel是未定义行为(很可能crash)。 +发起异步请求后Request可以立刻析构。(SelectiveChannel是个例外,SelectiveChannel情况下必须在请求处理完成后再释放request对象) + +发起异步请求后Channel可以立刻析构。 + +注意:这是说Request/Channel的析构可以立刻发生在CallMethod**之后**,并不是说析构可以和CallMethod同时发生,删除正被另一个线程使用的Channel是未定义行为(很可能crash)。 ### 使用NewCallback ```c++ @@ -322,7 +342,7 @@ MyService_Stub stub(&channel); MyRequest request; // 你不用new request,即使在异步访问中. request.set_foo(...); cntl->set_timeout_ms(...); -stub.some_method(cntl, &request, response, google::protobuf::NewCallback(OnRPCDone, response, cntl)); +stub.some_method(cntl, &request, response, brpc::NewCallback(OnRPCDone, response, cntl)); ``` 由于protobuf 3把NewCallback设置为私有,r32035后brpc把NewCallback独立于[src/brpc/callback.h](https://github.com/brpc/brpc/blob/master/src/brpc/callback.h)(并增加了一些重载)。如果你的程序出现NewCallback相关的编译错误,把google::protobuf::NewCallback替换为brpc::NewCallback就行了。 @@ -505,7 +525,7 @@ Controller的特点: ## 线程数 -和大部分的RPC框架不同,brpc中并没有独立的Client线程池。所有Channel和Server通过[bthread](http://wiki.baidu.com/display/RPC/bthread)共享相同的线程池. 如果你的程序同样使用了brpc的server, 仅仅需要设置Server的线程数。 或者可以通过[gflags](http://wiki.baidu.com/display/RPC/flags)设置[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency)来设置全局的线程数. +和大部分的RPC框架不同,brpc中并没有独立的Client线程池。所有Channel和Server通过[bthread](bthread.md)共享相同的线程池. 如果你的程序同样使用了brpc的server, 仅仅需要设置Server的线程数。 或者可以通过[gflags](flags.md)设置[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency)来设置全局的线程数. ## 超时 @@ -519,7 +539,7 @@ Controller的特点: ## 重试 -ChannelOptions.max_retry是该Channel上所有RPC的默认最大重试次数,Controller.set_max_retry()可修改某次RPC的值,默认值3,0表示不重试。 +ChannelOptions.max_retry是该Channel上所有RPC的默认最大重试次数,默认值3,0表示不重试。Controller.set_max_retry()可修改某次RPC的值。 r32111后Controller.retried_count()返回重试次数。 @@ -751,11 +771,11 @@ set_request_compress_type()设置request的压缩方式,默认不压缩。 注意:附件不会被压缩。 -http/h2 body的压缩方法见[client压缩request body](http_client#压缩request-body)。 +http/h2 body的压缩方法见[client压缩request body](http_client.md#压缩request-body)。 支持的压缩方法有: -- brpc::CompressTypeSnappy : [snanpy压缩](http://google.github.io/snappy/),压缩和解压显著快于其他压缩方法,但压缩率最低。 +- brpc::CompressTypeSnappy : [snappy压缩](http://google.github.io/snappy/),压缩和解压显著快于其他压缩方法,但压缩率最低。 - brpc::CompressTypeGzip : [gzip压缩](http://en.wikipedia.org/wiki/Gzip),显著慢于snappy,但压缩率高 - brpc::CompressTypeZlib : [zlib压缩](http://en.wikipedia.org/wiki/Zlib),比gzip快10%~20%,压缩率略好于gzip,但速度仍明显慢于snappy。 @@ -797,7 +817,7 @@ http/h2 body的压缩方法见[client压缩request body](http_client#压缩reque ### Q: brpc能用unix domain socket吗 -不能。同机TCP socket并不走网络,相比unix domain socket性能只会略微下降。一些不能用TCP socket的特殊场景可能会需要,以后可能会扩展支持。 +支持,参考 [EndPoint](endpoint.md). ### Q: Fail to connect to xx.xx.xx.xx:xxxx, Connection refused diff --git a/docs/cn/cpu_profiler.md b/docs/cn/cpu_profiler.md index 3a3aa71b..5dfa3960 100644 --- a/docs/cn/cpu_profiler.md +++ b/docs/cn/cpu_profiler.md @@ -5,7 +5,7 @@ brpc可以分析程序中的热点函数。 1. 链接`libtcmalloc_and_profiler.a` 1. 这么写也开启了tcmalloc,不建议单独链接cpu profiler而不链接tcmalloc,可能越界访问导致[crash](https://github.com/gperftools/gperftools/blob/master/README#L226).可能由于tcmalloc不及时归还内存,越界访问不会crash。 2. 如果tcmalloc使用frame pointer而不是libunwind回溯栈,请确保在CXXFLAGS或CFLAGS中加上`-fno-omit-frame-pointer`,否则函数间的调用关系会丢失,最后产生的图片中都是彼此独立的函数方框。 -2. 定义宏BRPC_ENABLE_CPU_PROFILER, 一般加入编译参数-DBRPC_ENABLE_CPU_PROFILER。 +2. 定义宏BRPC_ENABLE_CPU_PROFILER, 一般加入编译参数-DBRPC_ENABLE_CPU_PROFILER。注意:BRPC_ENABLE_CPU_PROFILER宏需要定义在引用到brpc头文件(channel.h或server.h)的代码里。比如A模块引用B模块,B模块在实现中引用brpc头文件,必须在B模块的编译参数加上BRPC_ENABLE_CPU_PROFILER宏,在A模块加是没用的。 3. 如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)。 注意要关闭Server端的认证,否则可能会看到这个: @@ -23,6 +23,21 @@ FATAL: 12-26 10:01:25: * 0 [src/brpc/policy/giano_authenticator.cpp:65][429496 WARNING: 12-26 10:01:25: * 0 [src/brpc/input_messenger.cpp:132][4294969345] Authentication failed, remote side(127.0.0.1:22989) of sockfd=5, close it ``` +# 查看方法 + +1. 通过builtin service的 /hotspots/cpu 页面查看 +1. 通过pprof 工具查看,如 tools/pprof --text localhost:9002/pprof/profile + +# 控制采样频率 + +启动前设置环境变量:export CPUPROFILE_FREQUENCY=xxx + +默认值为: 100 + +# 控制采样时间 + +url加上?seconds=秒数,如/hotspots/cpu?seconds=5 + # 图示 下图是一次运行cpu profiler后的结果: diff --git a/docs/cn/endpoint.md b/docs/cn/endpoint.md new file mode 100644 index 00000000..10a0ea3b --- /dev/null +++ b/docs/cn/endpoint.md @@ -0,0 +1,61 @@ +# UDS及IPV6支持 + +butil::EndPoint已经支持UDS(Unix Domain Socket)及IPV6。 + +## 基本用法 +代码用法: + +```cpp +EndPoint ep; +str2endpoint("unix:path.sock", &ep); // 初始化一个UDS的EndPoint +str2endpoint("[::1]:8086", &ep); // 初始化一个IPV6的EndPoint +str2endpoint("[::1]", 8086, &ep); // 初始化一个IPV6的EndPoint + +// 获取EndPoint的类型 +sa_family_t type = get_endpoint_type(ep); // 可能为AF_INET、AF_INET6或AF_UNIX + +// 使用EndPoint,和原来的方式一样 +LOG(DEBUG) << ep; // 打印EndPoint +std::string ep_str = endpoint2str(ep).c_str(); // EndPoint转str +tcp_listen(ep); // 用监听EndPoint表示的tcp端口 +tcp_connect(ep, NULL); // 用连接EndPoint表示的tcp端口 + +sockaddr_storage ss; +socklen_t socklen = 0; +endpoint2sockaddr(ep, &ss, &socklen); // 将EndPoint转为sockaddr结构,以便调用系统函数 +``` + +## 在brpc中使用UDS或IPV6 + +只需要在原来输入IPV4字符串的时候,填写UDS路径或IPV6地址即可,如: + +```cpp +server.Start("unix:path.sock", options); // 启动server监听UDS地址 +server.Start("[::0]:8086", options); // 启动server监听IPV6地址 + +channel.Init("unix:path.sock", options); // 初始化single server的Channel,访问UDS地址 +channel.Init("list://[::1]:8086,[::1]:8087", "rr", options); // 初始化带LB的Channel,访问IPV6地址 +``` + +通过 example/echo_c++ ,展示了如何使用UDS或IPV6: + +```bash +./echo_server -listen_addr='unix:path.sock' & # 启动Server监听UDS地址 +./echo_server -listen_addr='[::0]:8080' & # 启动Server监听IPV6端口 + +./echo_client -server='unix:path.sock' # 启动Client访问UDS地址 +./echo_client -server='[::1]:8080' # 启动Client访问IPV6端口 +``` + +## 限制 + +由于EndPoint结构被广泛地使用,为了保证对存量代码的兼容性(包括ABI兼容性),目前采用的实现方式是不修改EndPoint的ABI定义,使用原来的ip字段作为id,port字段来做为扩展标记,把真正的信息存在一个外部的数据结构中。 + +这种实现方式对于现存的仅使用IPV4的代码是完全兼容的,但对于使用UDS或IPV6的用户,有些代码是不兼容的,比如直接访问EndPoint的ip和port成员的代码。 + +关于UDS和IPV6,目前已知的一些限制: + +- 不兼容rpcz +- 不支持使用PortRange方式启动server +- 不支持在ServerOption中指定internal_port +- IPV6不支持link local地址(fe80::开头的地址) \ No newline at end of file diff --git a/docs/cn/error_code.md b/docs/cn/error_code.md index 52a4a4d9..7bb9d6cf 100644 --- a/docs/cn/error_code.md +++ b/docs/cn/error_code.md @@ -26,8 +26,9 @@ brpc中常见错误的打印内容列表如下: | 错误码 | 数值 | 重试 | 说明 | 日志 | | -------------- | ---- | ---- | ---------------------------------------- | ---------------------------------------- | | EAGAIN | 11 | 是 | 同时发送的请求过多。软限,很少出现。 | Resource temporarily unavailable | +| ENODATA | 61 | 是 | 1. Naming Service返回的server列表为空 2. Naming Service某次变更时,所有实例都发生了修改,Naming Service更新LB的逻辑是先Remove再Add,会存在很短时间内LB实例列表为空的情况 | Fail to select server from xxx | | ETIMEDOUT | 110 | 是 | 连接超时。 | Connection timed out | -| EHOSTDOWN | 112 | 是 | 找不到可用的server。server可能停止服务了,也可能正在退出中(返回了ELOGOFF)。 | "Fail to select server from …" "Not connected to … yet" | +| EHOSTDOWN | 112 | 是 | 可能原因:一、Naming Server返回的列表不为空,但LB选不出可用的server,LB返回了EHOSTDOWN错误。具体可能原因:a.Server正在退出中(返回了ELOGOFF) b. Server因为之前的某种失败而被封禁,封禁的具体逻辑:1. 对于单连接,唯一的连接socket被SetFail即封禁,SetFail在代码里出现非常多,有很多种可能性触发 2. 对于连接池/短连接,只有错误号满足does_error_affect_main_socket时(ECONNREFUSED,ENETUNREACH,EHOSTUNREACH或EINVAL)才会封禁 3. 封禁之后,有CheckHealth线程健康检查,就是尝试去连接一下,检查间隔由SocketOptions的health_check_interval_s控制,检查正常会解封。二、使用SingleServer方式初始化Channel(没有LB),唯一的一个连接为LOGOFF或者封禁状态(同上) | "Fail to select server from …" "Not connected to … yet" | | ENOSERVICE | 1001 | 否 | 找不到服务,不太出现,一般会返回ENOMETHOD。 | | | ENOMETHOD | 1002 | 否 | 找不到方法。 | 形式广泛,常见如"Fail to find method=..." | | EREQUEST | 1003 | 否 | request序列化错误,client端和server端都可能设置 | 形式广泛:"Missing required fields in request: …" "Fail to parse request message, …" "Bad request" | diff --git a/docs/cn/execution_queue.md b/docs/cn/execution_queue.md index 21e40dbe..6e95ba29 100644 --- a/docs/cn/execution_queue.md +++ b/docs/cn/execution_queue.md @@ -112,7 +112,7 @@ template int execution_queue_join(ExecutionQueueId id); ``` -stop和join都可以多次调用, 都会又合理的行为。stop可以随时调用而不用当心线程安全性问题。 +stop和join都可以多次调用, 都会有合理的行为。stop可以随时调用而不用当心线程安全性问题。 和fd的close类似,如果stop不被调用, 相应的资源会永久泄露。 diff --git a/docs/cn/flags.md b/docs/cn/flags.md index 74da50cb..fa8eac82 100644 --- a/docs/cn/flags.md +++ b/docs/cn/flags.md @@ -6,7 +6,7 @@ brpc使用gflags管理配置。如果你的程序也使用gflags,那么你应 # Usage of gflags -gflags一般定义在需要它的源文件中。#include 后在全局scope加入DEFINE_**(**, **, **); 比如: +gflags一般定义在需要它的源文件中。#include 后在全局scope加入DEFINE_*\*(*\*, *\*, *\*); 比如: ```c++ #include @@ -43,9 +43,9 @@ conf/gflags.conf: No such file or directory # flagfile -在命令行中参数和值之间可不加等号,而在flagfile中一定要加。比如`./myapp -param 7`是ok的,但在`./myapp -flagfile=./gflags.conf`对应的gflags.conf中一定要写成**-param=7**或**--param=7**,否则就不正确且不会报错。 +在命令行中参数和值之间可不加等号,而在flagfile中一定要加。比如`./myapp -param 7`是ok的,但在`./myapp -flagfile=./gflags.conf`对应的gflags.conf中一定要写成 **-param=7** 或 **--param=7**,否则就不正确且不会报错。 -在命令行中字符串可用单引号或双引号包围,而在flagfile中不能加。比如`./myapp -name="tom"`或`./myapp -name='tom'`都是ok的,但在`./myapp -flagfile=./gflags.conf`对应的gflags.conf中一定要写成**-name=tom**或**--name=tom**,如果写成-name="tom"的话,引号也会作为值的一部分。配置文件中的值可以有空格,比如gflags.conf中写成-name=value with spaces是ok的,参数name的值就是value with spaces,而在命令行中要用引号括起来。 +在命令行中字符串可用单引号或双引号包围,而在flagfile中不能加。比如`./myapp -name="tom"`或`./myapp -name='tom'`都是ok的,但在`./myapp -flagfile=./gflags.conf`对应的gflags.conf中一定要写成 **-name=tom** 或 **--name=tom**,如果写成-name="tom"的话,引号也会作为值的一部分。配置文件中的值可以有空格,比如gflags.conf中写成-name=value with spaces是ok的,参数name的值就是value with spaces,而在命令行中要用引号括起来。 flagfile中参数可由单横线(如-foo)或双横线(如--foo)打头,但不能以三横线或更多横线打头,否则的话是无效参数且不会报错! diff --git a/docs/cn/getting_started.md b/docs/cn/getting_started.md index 4d10ef66..53ef0f50 100644 --- a/docs/cn/getting_started.md +++ b/docs/cn/getting_started.md @@ -1,59 +1,61 @@ -# BUILD +[English version](../en/getting_started.md) -brpc prefers static linkages of deps, so that they don't have to be installed on every machine running the app. +# 构建 -brpc depends on following packages: +brpc鼓励静态链接依赖,以便于每个运行brpc服务的机器不必再安装依赖。 + +brpc有如下依赖: * [gflags](https://github.com/gflags/gflags): Extensively used to define global options. * [protobuf](https://github.com/google/protobuf): Serializations of messages, interfaces of services. -* [leveldb](https://github.com/google/leveldb): Required by [/rpcz](rpcz.md) to record RPCs for tracing. +* [leveldb](https://github.com/google/leveldb): Required by [rpcz](rpcz.md) to record RPCs for tracing. -# Supported Environment +# 支持的环境 * [Ubuntu/LinuxMint/WSL](#ubuntulinuxmintwsl) * [Fedora/CentOS](#fedoracentos) -* [Linux with self-built deps](#linux-with-self-built-deps) +* [自己构建依赖的Linux](#自己构建依赖的Linux) * [MacOS](#macos) ## Ubuntu/LinuxMint/WSL -### Prepare deps +### 依赖准备 -Install common deps, [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +安装通用依赖,[gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): ```shell sudo apt-get install -y git g++ make libssl-dev libgflags-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev ``` -If you need to statically link leveldb: +如果你需要静态链接leveldb: ```shell sudo apt-get install -y libsnappy-dev ``` -If you need to enable cpu/heap profilers in examples: +如果你要在样例中启用cpu/heap的profiler: ```shell sudo apt-get install -y libgoogle-perftools-dev ``` -If you need to run tests, install and compile libgtest-dev (which is not compiled yet): +如果你要运行测试,那么要安装并编译libgtest-dev(它没有被默认编译): ```shell sudo apt-get install -y cmake libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libgtest* /usr/lib/ && cd - ``` -The directory of gtest source code may be changed, try `/usr/src/googletest/googletest` if `/usr/src/gtest` is not there. +gtest源码目录可能变动,如果`/usr/src/gtest`不存在,请尝试`/usr/src/googletest/googletest`。 -### Compile brpc with config_brpc.sh -git clone brpc, cd into the repo and run +### 使用config_brpc.sh编译brpc +git克隆brpc,进入到项目目录,然后运行 ```shell $ sh config_brpc.sh --headers=/usr/include --libs=/usr/lib $ make ``` -To change compiler to clang, add `--cxx=clang++ --cc=clang`. +修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。 -To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. +不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。 -To use brpc with glog, add `--with-glog`. +使用glog版的brpc,添加选项`--with-glog`。 -To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. +要启用 [thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。 -**Run example** +**运行样例** ```shell $ cd example/echo_c++ @@ -62,30 +64,30 @@ $ ./echo_server & $ ./echo_client ``` -Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` +上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make` -**Run tests** +**运行测试** ```shell $ cd test $ make $ sh run_tests.sh ``` -### Compile brpc with cmake +### 使用cmake编译brpc ```shell cmake -B build && cmake --build build -j6 ``` -To help VSCode or Emacs(LSP) to understand code correctly, add `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate `compile_commands.json` +要帮助VSCode或Emacs(LSP)去正确地理解代码,添加`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`选项去生成`compile_commands.json`。 -To change compiler to clang, overwrite environment variable `CC` and `CXX` to `clang` and `clang++` respectively. +要修改编译器为clang,请修改环境变量`CC`和`CXX`为`clang`和`clang++`。 -To not link debugging symbols, remove `build/CMakeCache.txt` and cmake with `-DWITH_DEBUG_SYMBOLS=OFF` +不想链接调试符号,请移除`build/CMakeCache.txt`,然后用`-DWITH_DEBUG_SYMBOLS=OFF`选项执行cmake。 -To use brpc with glog, cmake with `-DWITH_GLOG=ON`. +想要让brpc使用glog,用`-DWITH_GLOG=ON`选项执行cmake。 -To enable [thrift support](../en/thrift.md), install thrift first and cmake with `-DWITH_THRIFT=ON`. +要启用 [thrift 支持](../en/thrift.md),先安装thrift,然后用`-DWITH_THRIFT=ON`选项执行cmake。 -**Run example with cmake** +**用cmake运行样例** ```shell $ cd example/echo_c++ @@ -93,9 +95,10 @@ $ cmake -B build && cmake --build build -j4 $ ./echo_server & $ ./echo_client ``` -Examples link brpc statically, if you need to link the shared version, remove `CMakeCache.txt` and cmake with `-DLINK_SO=ON` -**Run tests** +上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请先移除`CMakeCache.txt`,然后用`-DLINK_SO=ON`选项重新执行cmake。 + +**运行测试** ```shell $ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test @@ -103,50 +106,50 @@ $ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test ## Fedora/CentOS -### Prepare deps +### 依赖准备 -CentOS needs to install EPEL generally otherwise many packages are not available by default. +CentOS一般需要安装EPEL,否则很多包都默认不可用。 ```shell sudo yum install epel-release ``` -Install common deps: +安装通用依赖: ```shell sudo yum install git gcc-c++ make openssl-devel ``` -Install [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +安装 [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): ```shell sudo yum install gflags-devel protobuf-devel protobuf-compiler leveldb-devel ``` -If you need to enable cpu/heap profilers in examples: +如果你要在样例中启用cpu/heap的profiler: ```shell sudo yum install gperftools-devel ``` -If you need to run tests, install and compile gtest-devel (which is not compiled yet): +如果你要运行测试,那么要安装ligtest-dev: ```shell sudo yum install gtest-devel ``` -### Compile brpc with config_brpc.sh +### 使用config_brpc.sh编译brpc -git clone brpc, cd into the repo and run +git克隆brpc,进入项目目录然后执行: ```shell -$ sh config_brpc.sh --headers=/usr/include --libs=/usr/lib64 +$ sh config_brpc.sh --headers="/usr/include" --libs="/usr/lib64 /usr/bin" $ make ``` -To change compiler to clang, add `--cxx=clang++ --cc=clang`. +修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。 -To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. +不想链接调试符号,添加选项`--nodebugsymbols` 然后编译将会得到更轻量的二进制文件。 -To use brpc with glog, add `--with-glog`. +想要让brpc使用glog,添加选项:`--with-glog`。 -To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. +要启用 [thrift 支持](../en/thrift.md),先安装thrift,然后添加选项:`--with-thrift`。 -**Run example** +**运行样例** ```shell $ cd example/echo_c++ @@ -155,50 +158,50 @@ $ ./echo_server & $ ./echo_client ``` -Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` +上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make` -**Run tests** +**运行测试** ```shell $ cd test $ make $ sh run_tests.sh ``` -### Compile brpc with cmake -Same with [here](#compile-brpc-with-cmake) +### 使用cmake编译brpc +参考[这里](#使用cmake编译brpc) -## Linux with self-built deps +## 自己构建依赖的Linux -### Prepare deps +### 依赖准备 -brpc builds itself to both static and shared libs by default, so it needs static and shared libs of deps to be built as well. +brpc默认会构建出静态库和共享库,因此它也需要依赖有静态库和共享库两个版本。 -Take [gflags](https://github.com/gflags/gflags) as example, which does not build shared lib by default, you need to pass options to `cmake` to change the behavior: +以[gflags](https://github.com/gflags/gflags)为例,它默认不构建共享库,你需要给`cmake`指定选项去改变这一行为: ```shell $ cmake . -DBUILD_SHARED_LIBS=1 -DBUILD_STATIC_LIBS=1 $ make ``` -### Compile brpc +### 编译brpc -Keep on with the gflags example, let `../gflags_dev` be where gflags is cloned. +还以gflags为例,`../gflags_dev`表示gflags被克隆的位置。 -git clone brpc. cd into the repo and run +git克隆brpc。进入到项目目录然后运行: ```shell $ sh config_brpc.sh --headers="../gflags_dev /usr/include" --libs="../gflags_dev /usr/lib64" $ make ``` -Here we pass multiple paths to `--headers` and `--libs` to make the script search for multiple places. You can also group all deps and brpc into one directory, then pass the directory to --headers/--libs which actually search all subdirectories recursively and will find necessary files. +这里我们给`--headers`和`--libs`传递多个路径使得脚本能够在多个地方进行检索。你也可以打包所有依赖和brpc一起放到一个目录中,然后把目录传递给 --headers/--libs选项,它会递归搜索所有子目录直到找到必须的文件。 -To change compiler to clang, add `--cxx=clang++ --cc=clang`. +修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。 -To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. +不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。 -To use brpc with glog, add `--with-glog`. +使用glog版的brpc,添加选项`--with-glog`。 -To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. +要启用[thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。 ```shell $ ls my_dev @@ -208,49 +211,49 @@ $ sh config_brpc.sh --headers=.. --libs=.. $ make ``` -### Compile brpc with cmake -Same with [here](#compile-brpc-with-cmake) +### 使用cmake编译brpc +参考[这里](#使用cmake编译brpc) ## MacOS -Note: In the same running environment, the performance of the current Mac version is about 2.5 times worse than the Linux version. If your service is performance-critical, do not use MacOS as your production environment. +注意:在相同运行环境下,当前Mac版brpc的性能比Linux版差2.5倍。如果你的服务是性能敏感的,请不要使用MacOs作为你的生产环境。 -### Prepare deps +### 依赖准备 -Install common deps: +安装通用依赖: ```shell brew install openssl git gnu-getopt coreutils ``` -Install [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +安装[gflags](https://github.com/gflags/gflags),[protobuf](https://github.com/google/protobuf),[leveldb](https://github.com/google/leveldb): ```shell brew install gflags protobuf leveldb ``` -If you need to enable cpu/heap profilers in examples: +如果你要在样例中启用cpu/heap的profiler: ```shell brew install gperftools ``` -If you need to run tests, download and compile googletest (which is not compiled yet): +如果你要运行测试,那么要安装并编译googletest(它没有被默认编译): ```shell git clone https://github.com/google/googletest -b release-1.10.0 && cd googletest/googletest && mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS="-std=c++11" .. && make ``` -After the compilation, copy include/ and lib/ into /usr/local/include and /usr/local/lib respectively to expose gtest to all apps +在编译完成后,复制include/和lib/目录到/usr/local/include和/usr/local/lib目录中,以便于让所有应用都能使用gtest。 -### Compile brpc with config_brpc.sh -git clone brpc, cd into the repo and run +### 使用config_brpc.sh编译brpc +git克隆brpc,进入到项目目录然后运行: ```shell $ sh config_brpc.sh --headers=/usr/local/include --libs=/usr/local/lib --cc=clang --cxx=clang++ $ make ``` -To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. +不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。 -To use brpc with glog, add `--with-glog`. +使用glog版的brpc,添加选项`--with-glog`。 -To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. +要启用[thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。 -**Run example** +**运行样例** ```shell $ cd example/echo_c++ @@ -258,85 +261,84 @@ $ make $ ./echo_server & $ ./echo_client ``` +上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make` -Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` - -**Run tests** +**运行测试** ```shell $ cd test $ make $ sh run_tests.sh ``` -### Compile brpc with cmake -Same with [here](#compile-brpc-with-cmake) +### 使用cmake编译brpc +参考[这里](#使用cmake编译brpc) -# Supported deps +# 支持的依赖 ## GCC: 4.8-7.1 -c++11 is turned on by default to remove dependencies on boost (atomic). +c++11被默认启用,以去除去boost的依赖(比如atomic)。 -The over-aligned issues in GCC7 is suppressed temporarily now. +GCC7中over-aligned的问题暂时被禁止。 -Using other versions of gcc may generate warnings, contact us to fix. +使用其他版本的gcc可能会产生编译警告,请联系我们予以修复。 -Adding `-D__const__=` to cxxflags in your makefiles is a must to avoid [errno issue in gcc4+](thread_local.md). +请在makefile中给cxxflags增加`-D__const__=`选项以避免[gcc4+中的errno问题](thread_local.md). ## Clang: 3.5-4.0 -no known issues. +无已知问题。 ## glibc: 2.12-2.25 -no known issues. +无已知问题。 ## protobuf: 2.4+ -Be compatible with pb 3.x and pb 2.x with the same file: -Don't use new types in proto3 and start the proto file with `syntax="proto2";` -[tools/add_syntax_equal_proto2_to_all.sh](https://github.com/brpc/brpc/blob/master/tools/add_syntax_equal_proto2_to_all.sh)can add `syntax="proto2"` to all proto files without it. +同一个文件兼容pb 3.x版本和pb 2.x版本: +不要使用proto3新增的类型,并且在proto文件的起始位置添加`syntax=proto2;`声明。 +[tools/add_syntax_equal_proto2_to_all.sh](https://github.com/brpc/brpc/blob/master/tools/add_syntax_equal_proto2_to_all.sh)这个脚本可以给所有没有这行声明的proto文件添加`syntax="proto2"`声明。 -Arena in pb 3.x is not supported yet. +pb 3.x中的Arena至今没被支持。 ## gflags: 2.0-2.2.1 -no known issues. +无已知问题。 ## openssl: 0.97-1.1 -required by https. +被https功能需要。 ## tcmalloc: 1.7-2.5 -brpc does **not** link [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html) by default. Users link tcmalloc on-demand. +brpc默认**不**链接 [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html)。用户按需要链接tcmalloc。 -Comparing to ptmalloc embedded in glibc, tcmalloc often improves performance. However different versions of tcmalloc may behave really differently. For example, tcmalloc 2.1 may make multi-threaded examples in brpc perform significantly worse(due to a spinlock in tcmalloc) than the one using tcmalloc 1.7 and 2.5. Even different minor versions may differ. When you program behave unexpectedly, remove tcmalloc or try another version. +和glibc内置的ptmalloc相比,tcmalloc通常能提升性能。然而不同版本的tcmalloc可能表现迥异。例如:tcmalloc 2.1与 tcmalloc 1.7和2.5相比,可能会让brpc的多线程样例性能显著恶化(tcmalloc中的一个自旋锁导致的)。甚至不同的小版本号之间变现也可能不同。当你的程序表现不符合预期的时候,移除tcmalloc然后尝试其他版本。 -Code compiled with gcc 4.8.2 and linked to a tcmalloc compiled with earlier GCC may crash or deadlock before main(), E.g: +用gcc4.8.2编译然后链接更早版本GCC编译的tcmalloc,可能会让程序中main()函数之前挂掉或者死锁,例如: ![img](../images/tcmalloc_stuck.png) -When you meet the issue, compile tcmalloc with the same GCC. +当你遇到这个问题的时候,请用同一个GCC重新编译tcmalloc。 -Another common issue with tcmalloc is that it does not return memory to system as early as ptmalloc. So when there's an invalid memory access, the program may not crash directly, instead it crashes at a unrelated place, or even not crash. When you program has weird memory issues, try removing tcmalloc. +另外一个使用tcmalloc的常见问题是,它不会像 ptmalloc一样及时地归还内存给系统。因此当有一个无效的内存访问的时候,程序可能不会直接挂掉,取而代之的是它可能在一个不相关的地方挂掉,或者甚至一直不挂掉。当你的程序出现怪异的内存问题的时候,尝试移除tcmalloc。 -If you want to use [cpu profiler](cpu_profiler.md) or [heap profiler](heap_profiler.md), do link `libtcmalloc_and_profiler.a`. These two profilers are based on tcmalloc.[contention profiler](contention_profiler.md) does not require tcmalloc. +如果你要使用[cpu profiler](cpu_profiler.md)或[heap profiler](heap_profiler.md),要链接`libtcmalloc_and_profiler.a`。这两个 profiler都是基于tcmalloc的。而[contention profiler](contention_profiler.md)不需要tcmalloc。 -When you remove tcmalloc, not only remove the linkage with tcmalloc but also the macro `-DBRPC_ENABLE_CPU_PROFILER`. +当你移除tcmalloc的时候,不仅要移除tcmalloc的链接,也要移除宏`-DBRPC_ENABLE_CPU_PROFILER`。 ## glog: 3.3+ -brpc implements a default [logging utility](../../src/butil/logging.h) which conflicts with glog. To replace this with glog, add *--with-glog* to config_brpc.sh or add `-DWITH_GLOG=ON` to cmake. +brpc实现了一个默认的[日志功能](../../src/butil/logging.h)它和glog冲突。要替换成glog,可以给config_brpc.sh增加*--with-glog*选项或者给cmake增加`-DWITH_GLOG=ON`选项。 ## valgrind: 3.8+ -brpc detects valgrind automatically (and registers stacks of bthread). Older valgrind(say 3.2) is not supported. +brpc会自动检测valgrind(然后注册bthread的栈)。不支持老版本的valgrind(比如3.2)。 ## thrift: 0.9.3-0.11.0 -no known issues. +无已知问题。 -# Track instances +# 实例追踪 -We provide a program to help you to track and monitor all brpc instances. Just run [trackme_server](https://github.com/brpc/brpc/tree/master/tools/trackme_server/) somewhere and launch need-to-be-tracked instances with -trackme_server=SERVER. The trackme_server will receive pings from instances periodically and print logs when it does. You can aggregate instance addresses from the log and call builtin services of the instances for further information. +我们提供了一个程序去帮助你追踪和监控所有brpc实例。 只需要在某处运行 [trackme_server](https://github.com/brpc/brpc/tree/master/tools/trackme_server/) 然后再带着 -trackme_server=SERVER参数启动需要被追踪的实例。trackme_server将从实例周期性地收到ping消息然后打印日志。您可以从日志中聚合实例地址,并调用实例的内置服务以获取更多信息。 diff --git a/docs/cn/http_client.md b/docs/cn/http_client.md index 90386b59..957ca136 100644 --- a/docs/cn/http_client.md +++ b/docs/cn/http_client.md @@ -114,7 +114,9 @@ URL的一般形式如下图: 若用户没有填且URL中包含host,比如http://www.foo.com/path,则http request中会包含"Host: www.foo.com"。 -若用户没有填且URL不包含host,比如"/index.html?name=value",则框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 +若用户没有填且URL不包含host,比如"/index.html?name=value",但如果Channel初始化的地址包含域名,则框架会以域名作为Host,比如"http://www.foo.com",该http server将会看到"Host: www.foo.com"。如果地址是"http://www.foo.com:8989",则该http server将会看到"Host: www.foo.com:8989"。 + +若用户没有填且URL不包含host,比如"/index.html?name=value",如果Channel初始化的地址也不包含域名,则框架会以目标server的ip和port为Host,地址为10.46.188.39:8989的http server将会看到"Host: 10.46.188.39:8989"。 对应的字段在h2中叫":authority"。 diff --git a/docs/cn/overview.md b/docs/cn/overview.md index cc1ff66e..a5a06288 100644 --- a/docs/cn/overview.md +++ b/docs/cn/overview.md @@ -66,7 +66,7 @@ RPC不是万能的抽象,否则我们也不需要TCP/IP这一层了。但是 * 访问服务? 包含[brpc/channel.h](https://github.com/brpc/brpc/blob/master/src/brpc/channel.h)并参考注释或[示例](https://github.com/brpc/brpc/blob/master/example/echo_c++/client.cpp). * 调整参数? 看看[brpc/controller.h](https://github.com/brpc/brpc/blob/master/src/brpc/controller.h). 注意这个类是Server和Channel共用的,分成了三段,分别标记为Client-side, Server-side和Both-side methods。 -我们尝试让事情变得更加简单,以命名服务为例,在其他RPC实现中,你也许需要复制一长段晦涩的代码才可使用,而在brpc中访问BNS可以这么写"bns://node-name",DNS是`Init("http://domain-name", ...)`,本地文件列表是"file:///home/work/server.list",相信不用解释,你也能明白这些代表什么。 +我们尝试让事情变得更加简单,以命名服务为例,在其他RPC实现中,你也许需要复制一长段晦涩的代码才可使用,而在brpc中访问BNS可以这么写`"bns://node-name"`,DNS是`"http://domain-name"`,本地文件列表是`"file:///home/work/server.list"`,相信不用解释,你也能明白这些代表什么。 ### 使服务更加可靠 diff --git a/docs/cn/rpc_press.md b/docs/cn/rpc_press.md index 6cfe1ddd..611b6136 100644 --- a/docs/cn/rpc_press.md +++ b/docs/cn/rpc_press.md @@ -62,7 +62,7 @@ rpc_press启动后会默认在8888端口启动一个dummy server,用于观察r ``` ./rpc_press -proto=echo.proto -service=example.EchoService -method=Echo -server=0.0.0.0:8002 -input=./input.json -duration=0 -qps=100 TRACE: 01-30 16:10:04: * 0 src/brpc/server.cpp:733] Server[dummy_servers] is serving on port=8888. -TRACE: 01-30 16:10:04: * 0 src/brpc/server.cpp:742] Check out http://db-rpc-dev00.db01.baidu.com:8888 in your web browser. +TRACE: 01-30 16:10:04: * 0 src/brpc/server.cpp:742] Check out http://xxx.com:8888 in your web browser. ``` dummy_server启动时会在终端打印日志,一般按住ctrl点击那个链接可以直接打开对应的内置服务页面,就像这样: diff --git a/docs/cn/rpc_replay.md b/docs/cn/rpc_replay.md index 4ee1119f..973ca210 100644 --- a/docs/cn/rpc_replay.md +++ b/docs/cn/rpc_replay.md @@ -1,4 +1,4 @@ -r31658后,brpc能随机地把一部分请求写入一些文件中,并通过rpc_replay工具回放。目前支持的协议有:baidu_std, hulu_pbrpc, sofa_pbrpc, nshead。 +r31658后,brpc能随机地把一部分请求写入一些文件中,并通过rpc_replay工具回放。目前支持的协议有:baidu_std, hulu_pbrpc, sofa_pbrpc, http, nshead。 # 获取工具 @@ -75,6 +75,7 @@ brpc在[tools/rpc_replay](https://github.com/brpc/brpc/tree/master/tools/rpc_rep - -thread_num:发送线程数,为0时会根据qps自动调节,默认为0。一般不用设置。 - -timeout_ms:超时 - -use_bthread:使用bthread发送,默认是。 +- -http_host:指定回放HTTP请求时的Host字段,如果非标准端口,请补全,比如:www.abc.com:8888,不指定该参数时将使用采样的原始Host字段。 rpc_replay会默认启动一个仅监控用的dummy server。打开后可查看回放的状况。其中rpc_replay_error是回放失败的次数。 diff --git a/docs/cn/rpc_view.md b/docs/cn/rpc_view.md index 929db0cc..b1abaa69 100644 --- a/docs/cn/rpc_view.md +++ b/docs/cn/rpc_view.md @@ -15,7 +15,7 @@ rpc_view可以转发端口被限的server的内置服务。像百度内如果一 ``` $ ./rpc_view 10.46.130.53:9970 TRACE: 02-14 12:12:20: * 0 src/brpc/server.cpp:762] Server[rpc_view_server] is serving on port=8888. -TRACE: 02-14 12:12:20: * 0 src/brpc/server.cpp:771] Check out http://db-rpc-dev00.db01.baidu.com:8888 in web browser. +TRACE: 02-14 12:12:20: * 0 src/brpc/server.cpp:771] Check out http://XXX.com:8888 in web browser. ``` 打开rpc_view在8888端口提供的页面(在secureCRT中按住ctrl点url): diff --git a/docs/cn/rpcz.md b/docs/cn/rpcz.md index 6766d991..e84158da 100644 --- a/docs/cn/rpcz.md +++ b/docs/cn/rpcz.md @@ -55,3 +55,14 @@ TRACEPRINTF("Hello rpcz %d", 123); ``` 这条annotation会按其发生时间插入到对应请求的rpcz中。从这个角度看,rpcz是请求级的日志。如果你用TRACEPRINTF打印了沿路的上下文,便可看到请求在每个阶段停留的时间,牵涉到的数据集和参数。这是个很有用的功能。 + +## 跨bthread传递trace上下文 + +有的业务在处理server请求的时候,会创建子bthread,在子bthread中发起rpc调用。默认情况下,子bthread中的rpc调用跟原来的请求无法建立关联,trace就会断掉。这种情况下,可以在创建子bthread时,指定BTHREAD_INHERIT_SPAN标志,来显式地建立trace上文关联,如: + +```c++ +bthread_attr_t attr = { BTHREAD_STACKTYPE_NORMAL, BTHREAD_INHERIT_SPAN, NULL }; +bthread_start_urgent(&tid, &attr, thread_proc, arg); +``` + +注意:使用这种方式创建子bthread来发送rpc,请确保rpc在server返回response之前完成,否则可能导致使用被释放的Span对象而出core。 diff --git a/docs/cn/server.md b/docs/cn/server.md index e0172da9..bec63bab 100644 --- a/docs/cn/server.md +++ b/docs/cn/server.md @@ -210,7 +210,13 @@ int Start(int port, const ServerOptions* opt); int Start(const char *ip_str, PortRange port_range, const ServerOptions *opt);  // r32009后增加 ``` -"localhost:9000", "cq01-cos-dev00.cq01:8000", “127.0.0.1:7000"都是合法的`ip_and_port_str`。 +合法的`ip_and_port_str`: + +- 127.0.0.1:80 # IPV4 +- [::1]:8080 # IPV6 +- unix:path.sock # Unix domain socket + +关于IPV6和Unix domain socket的使用,详见 [EndPoint](endpoint.md)。 `options`为NULL时所有参数取默认值,如果你要使用非默认值,这么做就行了: @@ -225,6 +231,10 @@ server.Start(..., &options); 一个server只能监听一个端口(不考虑ServerOptions.internal_port),需要监听N个端口就起N个Server。 +## 多进程监听一个端口 + +启动时开启`reuse_port`这个flag,就可以多进程共同监听一个端口(底层是SO_REUSEPORT)。 + # 停止 ```c++ @@ -647,6 +657,8 @@ server.MaxConcurrencyOf("example.EchoService.Echo") = "auto"; 对于这些情况,brpc提供了pthread模式,开启**-usercode_in_pthread**后,用户代码均会在pthread中运行,原先阻塞bthread的函数转而阻塞pthread。 +注意:开启-usercode_in_pthread后,brpc::thread_local_data()不保证能获取到值。 + 打开pthread模式后在性能上的注意点: - 同步RPC都会阻塞worker pthread,server端一般需要设置更多的工作线程(ServerOptions.num_threads),调度效率会略微降低。 @@ -683,6 +695,10 @@ pthread模式可以让一些老代码快速尝试brpc,但我们仍然建议逐 ```shell curl -s -m 1 :/flags/enable_dir_service,enable_threads_service | awk '{if($3=="false"){++falsecnt}else if($3=="Value"){isrpc=1}}END{if(isrpc!=1||falsecnt==2){print "SAFE"}else{print "NOT SAFE"}}' ``` +### 完全禁用内置服务 + +设置ServerOptions.has_builtin_services = false,可以完全禁用内置服务。 + ### 转义外部可控的URL 可调用brpc::WebEscape()对url进行转义,防止恶意URI注入攻击。 @@ -768,15 +784,16 @@ public: delete static_cast(d); } }; - + +MySessionLocalDataFactory g_session_local_data_factory; + int main(int argc, char* argv[]) { ... - MySessionLocalDataFactory session_local_data_factory; brpc::Server server; brpc::ServerOptions options; ... - options.session_local_data_factory = &session_local_data_factory; + options.session_local_data_factory = &g_session_local_data_factory; ... ``` @@ -859,14 +876,15 @@ public: } }; +MyThreadLocalDataFactory g_thread_local_data_factory; + int main(int argc, char* argv[]) { ... - MyThreadLocalDataFactory thread_local_data_factory; brpc::Server server; brpc::ServerOptions options; ... - options.thread_local_data_factory = &thread_local_data_factory; + options.thread_local_data_factory = &g_thread_local_data_factory; ... ``` diff --git a/docs/cn/status.md b/docs/cn/status.md index 948cdce1..8f4b916c 100644 --- a/docs/cn/status.md +++ b/docs/cn/status.md @@ -6,7 +6,7 @@ 上图中字段的含义分别是: -- **non_service_error**: 在service处理过程之外的错误个数。比如client断开连接导致server无法成功写回response算*non_service_error*,此时service处理已结束。作为对比,服务过程中对后端服务的访问错误不是*non_service_error*。即使写出的response代表错误,此error也被记入对应的service,而不是*non_service_error*。 +- **non_service_error**: 在service处理过程之外的错误个数。当获取到合法的service,之后发生的错误就算*service_error*,否则算*non_service_error*(比如请求解析失败,service名称不存在,请求并发度超限被拒绝等)。作为对比,服务过程中对后端服务的访问错误不是*non_service_error*。即使写出的response代表错误,此error也被记入对应的service,而不是*non_service_error*。 - **connection_count**: 向该server发起请求的连接个数。不包含记录在/vars/rpc_channel_connection_count的对外连接的个数。 - **example.EchoService**: 服务的完整名称,包含proto中的包名。 - **Echo (EchoRequest) returns (EchoResponse)**: 方法签名,一个服务可包含多个方法,点击request/response上的链接可查看对应的protobuf结构体。 diff --git a/docs/cn/streaming_log.md b/docs/cn/streaming_log.md index b951b438..cfda269f 100644 --- a/docs/cn/streaming_log.md +++ b/docs/cn/streaming_log.md @@ -135,6 +135,8 @@ TRACE: ... Items: item1 item2 item3 noflush支持bthread,可以实现类似于UB的pushnotice的效果,即检索线程一路打印都暂不刷出(加上noflush),直到最后检索结束时再一次性刷出。注意,如果检索过程是异步的,就不应该使用noflush,因为异步显然会跨越bthread,使noflush仍然失效。 +> 注意:如果编译时开启了glog选项,则不支持noflush。 + ## LOG_IF `LOG_IF(log_level, condition)`只有当condition成立时才会打印,相当于if (condition) { LOG() << ...; },但更加简短。比如: diff --git a/docs/en/atomic_instructions.md b/docs/en/atomic_instructions.md index 4fd97494..3568713f 100644 --- a/docs/en/atomic_instructions.md +++ b/docs/en/atomic_instructions.md @@ -39,7 +39,7 @@ For example: the first variable plays the role of switch, controlling accesses t ```c++ // Thread 1 -// ready was initialized to false +// bool ready was initialized to false p.init(); ready = true; ``` @@ -72,7 +72,7 @@ Above example can be modified as follows: ```c++ // Thread1 -// ready was initialized to false +// std::atomic ready was initialized to false p.init(); ready.store(true, std::memory_order_release); ``` diff --git a/docs/en/client.md b/docs/en/client.md index 15b1f0db..6ac4107e 100644 --- a/docs/en/client.md +++ b/docs/en/client.md @@ -216,6 +216,8 @@ which is round robin. Always choose next server inside the list, next of the las which is weighted round robin. Choose the next server according to the configured weight. The chances a server is selected is consistent with its weight, and the algorithm can make each server selection scattered. +The instance tag must be an int32 number representing the weight, eg. tag="50". + ### random Randomly choose one server from the list, no other settings. Similarly with round robin, the algorithm assumes that servers to access are similar. @@ -224,6 +226,8 @@ Randomly choose one server from the list, no other settings. Similarly with roun which is weighted random. Choose the next server according to the configured weight. The chances a server is selected is consistent with its weight. +Requirements of instance tag is the same as wrr. + ### la which is locality-aware. Perfer servers with lower latencies, until the latency is higher than others, no other settings. Check out [Locality-aware load balancing](lalb.md) for more details. @@ -240,6 +244,8 @@ Do distinguish "key" and "attributes" of the request. Don't compute request_code Check out [Consistent Hashing](consistent_hashing.md) for more details. +Other kind of lb does not need to set Controller.set_request_code(). If request code is set, it will not be used by lb. For example, lb=rr, and call Controller.set_request_code(), even if request_code is the same for every request, lb will balance the requests using the rr policy. + ### Client-side throttling for recovery from cluster downtime Cluster downtime refers to the state in which all servers in the cluster are unavailable. Due to the health check mechanism, when the cluster returns to normal, server will go online one by one. When a server is online, all traffic will be sent to it, which may cause the service to be overloaded again. If circuit breaker is enabled, server may be offline again before the other servers go online, and the cluster can never be recovered. As a solution, brpc provides a client-side throttling mechanism for recovery after cluster downtime. When no server is available in the cluster, the cluster enters recovery state. Assuming that the minimum number of servers that can serve all requests is min_working_instances, current number of servers available in the cluster is q, then in recovery state, the probability of client accepting the request is q/min_working_instances, otherwise it is discarded. If q remains unchanged for a period of time(hold_seconds), the traffic is resent to all available servers and leaves recovery state. Whether the request is rejected in recovery state is indicated by whether controller.ErrorCode() is equal to brpc::ERJECT, and the rejected request will not be retried by the framework. @@ -293,6 +299,12 @@ if (cntl.Failed()) { } ``` +> WARNING: Do NOT use synchronous call when you are holding a pthread lock! Otherwise it is easy to cause deadlock. +> +> Solution (choose one of the two): +> 1. Replace pthread lock with bthread lock (bthread_mutex_t) +> 1. Release the lock before CallMethod + ## Asynchronous call Pass a callback `done` to CallMethod, which resumes after sending request, rather than completion of RPC. When the response from server is received or error occurred(including timedout), done->Run() is called. Post-processing code of the RPC should be put in done->Run() instead of after CallMethod. @@ -301,7 +313,11 @@ Because end of CallMethod does not mean completion of RPC, response/controller m You can new these objects individually and create done by [NewCallback](#use-newcallback), or make response/controller be member of done and [new them together](#Inherit-google::protobuf::Closure). Former one is recommended. -**Request and Channel can be destroyed immediately after asynchronous CallMethod**, which is different from response/controller. Note that "immediately" means destruction of request/Channel can happen **after** CallMethod, not during CallMethod. Deleting a Channel just being used by another thread results in undefined behavior (crash at best). +Request can be destroyed immediately after asynchronous CallMethod. (SelectiveChannel is an exception, in the case of SelectiveChannel, the request object must be released after rpc finish) + +Channel can be destroyed immediately after asynchronous CallMethod. + +Note that "immediately" means destruction of Request/Channel can happen **after** CallMethod, not during CallMethod. Deleting a Channel just being used by another thread results in undefined behavior (crash at best). ### Use NewCallback ```c++ @@ -324,7 +340,7 @@ MyService_Stub stub(&channel); MyRequest request; // you don't have to new request, even in an asynchronous call. request.set_foo(...); cntl->set_timeout_ms(...); -stub.some_method(cntl, &request, response, google::protobuf::NewCallback(OnRPCDone, response, cntl)); +stub.some_method(cntl, &request, response, brpc::NewCallback(OnRPCDone, response, cntl)); ``` Since protobuf 3 changes NewCallback to private, brpc puts NewCallback in [src/brpc/callback.h](https://github.com/brpc/brpc/blob/master/src/brpc/callback.h) after r32035 (and adds more overloads). If your program has compilation issues with NewCallback, replace google::protobuf::NewCallback with brpc::NewCallback. @@ -523,7 +539,7 @@ NOTE2: error code of RPC timeout is **ERPCTIMEDOUT (1008) **, ETIMEDOUT is conne ## Retry -ChannelOptions.max_retry is maximum retrying count for all RPC via the channel, Controller.set_max_retry() overrides value for one RPC. Default value is 3. 0 means no retries. +ChannelOptions.max_retry is maximum retrying count for all RPC via the channel, Default value is 3, 0 means no retries. Controller.set_max_retry() overrides value for one RPC. Controller.retried_count() returns number of retries. @@ -757,7 +773,7 @@ set_request_compress_type() sets compress-type of the request, no compression by NOTE: Attachment is not compressed by brpc. -Check out [compress request body](http_client#压缩request-body) to compress http/h2 body. +Check out [compress request body](http_client.md#compress-request-body) to compress http/h2 body. Supported compressions: diff --git a/docs/en/error_code.md b/docs/en/error_code.md index a739a6b2..571b6d0b 100644 --- a/docs/en/error_code.md +++ b/docs/en/error_code.md @@ -24,8 +24,9 @@ Following table shows common error codes and their descriptions: | Error Code | Value | Retry | Description | Logging message | | -------------- | ----- | ----- | ---------------------------------------- | ---------------------------------------- | | EAGAIN | 11 | Yes | Too many requests at the same time, hardly happening as it's a soft limit. | Resource temporarily unavailable | +| ENODATA | 61 | 是 | 1. The server list returned by Naming Service is empty. 2. When Naming Service changes with all instances modified, Naming Service updates LB by first Remove all and then Add all, the LB instance list may become empty within a short period of time. | Fail to select server from xxx | | ETIMEDOUT | 110 | Yes | Connection timeout. | Connection timed out | -| EHOSTDOWN | 112 | Yes | No available server to send request. The servers may be stopped or stopping(returning ELOGOFF). | "Fail to select server from …" "Not connected to … yet" | +| EHOSTDOWN | 112 | Yes | Possible reasons: A. The list returned by Naming Server is not empty, but LB cannot select an available server, and LB returns an EHOSTDOWN error. Specific possible reasons: a. Server is exiting (returned ELOGOFF) b. Server was blocked because of some previous failure, the specific logic of the block: 1. For single connection type, the only connection socket is blocked by SetFail, and there are many occurrences of SetFailed in the code to trigger this block. 2. For pooled/short connection type, only when the error number meets does_error_affect_main_socket (ECONNREFUSED, ENETUNREACH, EHOSTUNREACH or EINVAL) will it be blocked 3. After blocking, there is a CheckHealth thread to do health check, Just try to connect, the check interval is controlled by the health_check_interval_s of SocketOptions, and the Socket will be unblocked if it is connected successfully. B. Use the SingleServer method to initialize the Channel (without LB), and the only connection is LOGOFF or blocked (same as above) | "Fail to select server from …" "Not connected to … yet" | | ENOSERVICE | 1001 | No | Can't locate the service, hardly happening and usually being ENOMETHOD instead | | | ENOMETHOD | 1002 | No | Can't locate the method. | Misc forms, common ones are "Fail to find method=…" | | EREQUEST | 1003 | No | fail to serialize the request, may be set on either client-side or server-side | Misc forms: "Missing required fields in request: …" "Fail to parse request message, …" "Bad request" | diff --git a/docs/en/getting_started.md b/docs/en/getting_started.md new file mode 100644 index 00000000..4d24f2d7 --- /dev/null +++ b/docs/en/getting_started.md @@ -0,0 +1,344 @@ +[中文版](../cn/getting_started.md) + +# BUILD + +brpc prefers static linkages of deps, so that they don't have to be installed on every machine running the app. + +brpc depends on following packages: + +* [gflags](https://github.com/gflags/gflags): Extensively used to define global options. +* [protobuf](https://github.com/google/protobuf): Serializations of messages, interfaces of services. +* [leveldb](https://github.com/google/leveldb): Required by [/rpcz](rpcz.md) to record RPCs for tracing. + +# Supported Environment + +* [Ubuntu/LinuxMint/WSL](#ubuntulinuxmintwsl) +* [Fedora/CentOS](#fedoracentos) +* [Linux with self-built deps](#linux-with-self-built-deps) +* [MacOS](#macos) + +## Ubuntu/LinuxMint/WSL +### Prepare deps + +Install common deps, [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +```shell +sudo apt-get install -y git g++ make libssl-dev libgflags-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev +``` + +If you need to statically link leveldb: +```shell +sudo apt-get install -y libsnappy-dev +``` + +If you need to enable cpu/heap profilers in examples: +```shell +sudo apt-get install -y libgoogle-perftools-dev +``` + +If you need to run tests, install and compile libgtest-dev (which is not compiled yet): +```shell +sudo apt-get install -y cmake libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv libgtest* /usr/lib/ && cd - +``` +The directory of gtest source code may be changed, try `/usr/src/googletest/googletest` if `/usr/src/gtest` is not there. + +### Compile brpc with config_brpc.sh +git clone brpc, cd into the repo and run +```shell +$ sh config_brpc.sh --headers=/usr/include --libs=/usr/lib +$ make +``` +To change compiler to clang, add `--cxx=clang++ --cc=clang`. + +To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. + +To use brpc with glog, add `--with-glog`. + +To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. + +**Run example** + +```shell +$ cd example/echo_c++ +$ make +$ ./echo_server & +$ ./echo_client +``` + +Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` + +**Run tests** +```shell +$ cd test +$ make +$ sh run_tests.sh +``` + +### Compile brpc with cmake +```shell +cmake -B build && cmake --build build -j6 +``` +To help VSCode or Emacs(LSP) to understand code correctly, add `-DCMAKE_EXPORT_COMPILE_COMMANDS=ON` to generate `compile_commands.json` + +To change compiler to clang, overwrite environment variable `CC` and `CXX` to `clang` and `clang++` respectively. + +To not link debugging symbols, remove `build/CMakeCache.txt` and cmake with `-DWITH_DEBUG_SYMBOLS=OFF` + +To use brpc with glog, cmake with `-DWITH_GLOG=ON`. + +To enable [thrift support](../en/thrift.md), install thrift first and cmake with `-DWITH_THRIFT=ON`. + +**Run example with cmake** + +```shell +$ cd example/echo_c++ +$ cmake -B build && cmake --build build -j4 +$ ./echo_server & +$ ./echo_client +``` +Examples link brpc statically, if you need to link the shared version, remove `CMakeCache.txt` and cmake with `-DLINK_SO=ON` + +**Run tests** + +```shell +$ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test +``` + +## Fedora/CentOS + +### Prepare deps + +CentOS needs to install EPEL generally otherwise many packages are not available by default. +```shell +sudo yum install epel-release +``` + +Install common deps: +```shell +sudo yum install git gcc-c++ make openssl-devel +``` + +Install [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +```shell +sudo yum install gflags-devel protobuf-devel protobuf-compiler leveldb-devel +``` + +If you need to enable cpu/heap profilers in examples: +```shell +sudo yum install gperftools-devel +``` + +If you need to run tests, install and compile gtest-devel (which is not compiled yet): +```shell +sudo yum install gtest-devel +``` + +### Compile brpc with config_brpc.sh + +git clone brpc, cd into the repo and run + +```shell +$ sh config_brpc.sh --headers="/usr/include" --libs="/usr/lib64 /usr/bin" +$ make +``` +To change compiler to clang, add `--cxx=clang++ --cc=clang`. + +To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. + +To use brpc with glog, add `--with-glog`. + +To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. + +**Run example** + +```shell +$ cd example/echo_c++ +$ make +$ ./echo_server & +$ ./echo_client +``` + +Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` + +**Run tests** +```shell +$ cd test +$ make +$ sh run_tests.sh +``` + +### Compile brpc with cmake +Same with [here](#compile-brpc-with-cmake) + +## Linux with self-built deps + +### Prepare deps + +brpc builds itself to both static and shared libs by default, so it needs static and shared libs of deps to be built as well. + +Take [gflags](https://github.com/gflags/gflags) as example, which does not build shared lib by default, you need to pass options to `cmake` to change the behavior: +```shell +$ cmake . -DBUILD_SHARED_LIBS=1 -DBUILD_STATIC_LIBS=1 +$ make +``` + +### Compile brpc + +Keep on with the gflags example, let `../gflags_dev` be where gflags is cloned. + +git clone brpc. cd into the repo and run + +```shell +$ sh config_brpc.sh --headers="../gflags_dev /usr/include" --libs="../gflags_dev /usr/lib64" +$ make +``` + +Here we pass multiple paths to `--headers` and `--libs` to make the script search for multiple places. You can also group all deps and brpc into one directory, then pass the directory to --headers/--libs which actually search all subdirectories recursively and will find necessary files. + +To change compiler to clang, add `--cxx=clang++ --cc=clang`. + +To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. + +To use brpc with glog, add `--with-glog`. + +To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. + +```shell +$ ls my_dev +gflags_dev protobuf_dev leveldb_dev brpc_dev +$ cd brpc_dev +$ sh config_brpc.sh --headers=.. --libs=.. +$ make +``` + +### Compile brpc with cmake +Same with [here](#compile-brpc-with-cmake) + +## MacOS + +Note: In the same running environment, the performance of the current Mac version is about 2.5 times worse than the Linux version. If your service is performance-critical, do not use MacOS as your production environment. + +### Prepare deps + +Install common deps: +```shell +brew install openssl git gnu-getopt coreutils +``` + +Install [gflags](https://github.com/gflags/gflags), [protobuf](https://github.com/google/protobuf), [leveldb](https://github.com/google/leveldb): +```shell +brew install gflags protobuf leveldb +``` + +If you need to enable cpu/heap profilers in examples: +```shell +brew install gperftools +``` + +If you need to run tests, download and compile googletest (which is not compiled yet): +```shell +git clone https://github.com/google/googletest -b release-1.10.0 && cd googletest/googletest && mkdir build && cd build && cmake -DCMAKE_CXX_FLAGS="-std=c++11" .. && make +``` +After the compilation, copy include/ and lib/ into /usr/local/include and /usr/local/lib respectively to expose gtest to all apps + +### Compile brpc with config_brpc.sh +git clone brpc, cd into the repo and run +```shell +$ sh config_brpc.sh --headers=/usr/local/include --libs=/usr/local/lib --cc=clang --cxx=clang++ +$ make +``` +To not link debugging symbols, add `--nodebugsymbols` and compiled binaries will be much smaller. + +To use brpc with glog, add `--with-glog`. + +To enable [thrift support](../en/thrift.md), install thrift first and add `--with-thrift`. + +**Run example** + +```shell +$ cd example/echo_c++ +$ make +$ ./echo_server & +$ ./echo_client +``` + +Examples link brpc statically, if you need to link the shared version, `make clean` and `LINK_SO=1 make` + +**Run tests** +```shell +$ cd test +$ make +$ sh run_tests.sh +``` + +### Compile brpc with cmake +Same with [here](#compile-brpc-with-cmake) + +# Supported deps + +## GCC: 4.8-7.1 + +c++11 is turned on by default to remove dependencies on boost (atomic). + +The over-aligned issues in GCC7 is suppressed temporarily now. + +Using other versions of gcc may generate warnings, contact us to fix. + +Adding `-D__const__=` to cxxflags in your makefiles is a must to avoid [errno issue in gcc4+](thread_local.md). + +## Clang: 3.5-4.0 + +no known issues. + +## glibc: 2.12-2.25 + +no known issues. + +## protobuf: 2.4+ + +Be compatible with pb 3.x and pb 2.x with the same file: +Don't use new types in proto3 and start the proto file with `syntax="proto2";` +[tools/add_syntax_equal_proto2_to_all.sh](https://github.com/brpc/brpc/blob/master/tools/add_syntax_equal_proto2_to_all.sh)can add `syntax="proto2"` to all proto files without it. + +Arena in pb 3.x is not supported yet. + +## gflags: 2.0-2.2.1 + +no known issues. + +## openssl: 0.97-1.1 + +required by https. + +## tcmalloc: 1.7-2.5 + +brpc does **not** link [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html) by default. Users link tcmalloc on-demand. + +Comparing to ptmalloc embedded in glibc, tcmalloc often improves performance. However different versions of tcmalloc may behave really differently. For example, tcmalloc 2.1 may make multi-threaded examples in brpc perform significantly worse(due to a spinlock in tcmalloc) than the one using tcmalloc 1.7 and 2.5. Even different minor versions may differ. When you program behave unexpectedly, remove tcmalloc or try another version. + +Code compiled with gcc 4.8.2 and linked to a tcmalloc compiled with earlier GCC may crash or deadlock before main(), E.g: + +![img](../images/tcmalloc_stuck.png) + +When you meet the issue, compile tcmalloc with the same GCC. + +Another common issue with tcmalloc is that it does not return memory to system as early as ptmalloc. So when there's an invalid memory access, the program may not crash directly, instead it crashes at a unrelated place, or even not crash. When you program has weird memory issues, try removing tcmalloc. + +If you want to use [cpu profiler](cpu_profiler.md) or [heap profiler](heap_profiler.md), do link `libtcmalloc_and_profiler.a`. These two profilers are based on tcmalloc.[contention profiler](contention_profiler.md) does not require tcmalloc. + +When you remove tcmalloc, not only remove the linkage with tcmalloc but also the macro `-DBRPC_ENABLE_CPU_PROFILER`. + +## glog: 3.3+ + +brpc implements a default [logging utility](../../src/butil/logging.h) which conflicts with glog. To replace this with glog, add *--with-glog* to config_brpc.sh or add `-DWITH_GLOG=ON` to cmake. + +## valgrind: 3.8+ + +brpc detects valgrind automatically (and registers stacks of bthread). Older valgrind(say 3.2) is not supported. + +## thrift: 0.9.3-0.11.0 + +no known issues. + +# Track instances + +We provide a program to help you to track and monitor all brpc instances. Just run [trackme_server](https://github.com/brpc/brpc/tree/master/tools/trackme_server/) somewhere and launch need-to-be-tracked instances with -trackme_server=SERVER. The trackme_server will receive pings from instances periodically and print logs when it does. You can aggregate instance addresses from the log and call builtin services of the instances for further information. diff --git a/docs/en/http_client.md b/docs/en/http_client.md index b8aab593..fb41a6b4 100644 --- a/docs/en/http_client.md +++ b/docs/en/http_client.md @@ -115,7 +115,9 @@ If user already sets `Host` header(case insensitive), framework makes no change. If user does not set `Host` header and the URL has host, for example http://www.foo.com/path, the http request contains "Host: www.foo.com". -If user does not set host header and the URL does not have host as well, for example "/index.html?name=value", framework sets `Host` header with IP and port of the target server. A http server at 10.46.188.39:8989 should see `Host: 10.46.188.39:8989`. +If user does not set host header and the URL does not have host as well, for example "/index.html?name=value", but if the address initialized by the channel contains domain name. framework sets `Host` header with domain name of the target server. if this address is "http://www.foo.com", this http server should see `Host: www.foo.com`, if this address is "http://www.foo.com:8989", this http server should be see `Host: www.foo.com:8989`. + +If user does not set host header and the URL does not have host as well, for example "/index.html?name=value", and the address initialized by the channel doesn't contain domain name. framework sets `Host` header with IP and port of the target server. A http server at 10.46.188.39:8989 should see `Host: 10.46.188.39:8989`. The header is named ":authority" in h2. diff --git a/docs/en/server.md b/docs/en/server.md index 90fcfccc..0702fff6 100644 --- a/docs/en/server.md +++ b/docs/en/server.md @@ -227,6 +227,10 @@ server.Start(..., &options); One server can only listen to one port (not counting ServerOptions.internal_port). To listen to N ports, start N servers . +## Multi-process listening to one port + +When the `reuse_port` flag is turned on at startup, multiple processes can listen to one port (use SO_REUSEPORT internal). + # Stop server ```c++ @@ -647,6 +651,8 @@ User code(client-side done, server-side CallMethod) runs in bthreads with 1MB st brpc offers pthread mode to solve the issues. When **-usercode_in_pthread** is turned on, user code will be run in pthreads. Functions that would block bthreads block pthreads. +Note: With -usercode_in_pthread on, brpc::thread_local_data() does not guarantee to return valid value. + Performance issues when pthread mode is on: - Since synchronous RPCs block worker pthreads, server often needs more workers (ServerOptions.num_threads), and scheduling efficiencies will be slightly lower. @@ -683,6 +689,11 @@ Builtin services are useful, on the other hand include a lot of internal informa ```shell curl -s -m 1 :/flags/enable_dir_service,enable_threads_service | awk '{if($3=="false"){++falsecnt}else if($3=="Value"){isrpc=1}}END{if(isrpc!=1||falsecnt==2){print "SAFE"}else{print "NOT SAFE"}}' ``` + +### Disable built-in services completely + +Set ServerOptions.has_builtin_services = false, you can completely disable the built-in services. + ### Escape URLs controllable from public brpc::WebEscape() escapes url to prevent injection attacks with malice. @@ -769,14 +780,15 @@ public: } }; +MySessionLocalDataFactory g_session_local_data_factory; + int main(int argc, char* argv[]) { ... - MySessionLocalDataFactory session_local_data_factory; brpc::Server server; brpc::ServerOptions options; ... - options.session_local_data_factory = &session_local_data_factory; + options.session_local_data_factory = &g_session_local_data_factory; ... ``` @@ -859,14 +871,15 @@ public: } }; +MyThreadLocalDataFactory g_thread_local_data_factory; + int main(int argc, char* argv[]) { ... - MyThreadLocalDataFactory thread_local_data_factory; brpc::Server server; brpc::ServerOptions options; ... - options.thread_local_data_factory = &thread_local_data_factory; + options.thread_local_data_factory = &g_thread_local_data_factory; ... ``` diff --git a/docs/en/status.md b/docs/en/status.md index be35bcd4..69a6a4e0 100644 --- a/docs/en/status.md +++ b/docs/en/status.md @@ -6,7 +6,7 @@ Meanings of the fields above: -- **non_service_error**: number of errors raised outside processing code of the service. For example, the error that server can't write response back due to a broken connection which had been closed by the client, is a *non_service_error* because the service processing already ends. As a contrast, failing to access back-end servers during the processing is an error of the service, not a *non_service_error*. Even if the response written out successfully stands for failure, the error is counted into the service rather than *non_service_error*. +- **non_service_error**: number of errors raised outside processing code of the service. When a valid service is obtained, the subsequent error is regarded as *service_error*, otherwise it is regarded as *non_service_error* (such as request parsing failed, service name does not exist, request concurrency exceeding limit, etc.). As a contrast, failing to access back-end servers during the processing is an error of the service, not a *non_service_error*. Even if the response written out successfully stands for failure, the error is counted into the service rather than *non_service_error*. - **connection_count**: number of connections to the server from clients, not including number of outward connections which are displayed at /vars/rpc_channel_connection_count. - **example.EchoService**: Full name of the service, including the package name defined in proto. - **Echo (EchoRequest) returns (EchoResponse)**: Signature of the method. A service can have multiple methods. Click links on request/response to see schemes of the protobuf messages. diff --git a/docs/en/thrift.md b/docs/en/thrift.md index 8801f143..5719a321 100755 --- a/docs/en/thrift.md +++ b/docs/en/thrift.md @@ -35,7 +35,7 @@ sh config_brpc.sh --headers=/usr/include --libs=/usr/lib64 --with-thrift # Or use cmake mkdir build && cd build && cmake ../ -DWITH_THRIFT=ON ``` -Read [Getting Started](../cn/getting_started.md) for more compilation options. +Read [Getting Started](getting_started.md) for more compilation options. # Client accesses thrift server Steps: diff --git a/docs/images/bthread_concurrency_1.png b/docs/images/bthread_concurrency_1.png index fe3e09ac..7cfe02b0 100644 Binary files a/docs/images/bthread_concurrency_1.png and b/docs/images/bthread_concurrency_1.png differ diff --git a/docs/images/bthread_concurrency_2.png b/docs/images/bthread_concurrency_2.png index 2b364d32..49dfd9a1 100644 Binary files a/docs/images/bthread_concurrency_2.png and b/docs/images/bthread_concurrency_2.png differ diff --git a/docs/images/builtin_service_from_console.png b/docs/images/builtin_service_from_console.png index a714c602..2f0647a2 100644 Binary files a/docs/images/builtin_service_from_console.png and b/docs/images/builtin_service_from_console.png differ diff --git a/docs/images/builtin_service_more.png b/docs/images/builtin_service_more.png index 79c0ef2c..ae3ecdf6 100644 Binary files a/docs/images/builtin_service_more.png and b/docs/images/builtin_service_more.png differ diff --git a/docs/images/bvar_noah1.png b/docs/images/bvar_noah1.png index 6c5247f0..1567e84f 100644 Binary files a/docs/images/bvar_noah1.png and b/docs/images/bvar_noah1.png differ diff --git a/docs/images/connection_timedout.png b/docs/images/connection_timedout.png index 473961d9..5c6803be 100644 Binary files a/docs/images/connection_timedout.png and b/docs/images/connection_timedout.png differ diff --git a/docs/images/dummy_server_2.png b/docs/images/dummy_server_2.png index 1204c5e1..7e46efaf 100644 Binary files a/docs/images/dummy_server_2.png and b/docs/images/dummy_server_2.png differ diff --git a/docs/images/dummy_server_3.png b/docs/images/dummy_server_3.png index 2eb206f3..33a97325 100644 Binary files a/docs/images/dummy_server_3.png and b/docs/images/dummy_server_3.png differ diff --git a/docs/images/flag_setvalue.png b/docs/images/flag_setvalue.png index 39174d3b..40e5159b 100644 Binary files a/docs/images/flag_setvalue.png and b/docs/images/flag_setvalue.png differ diff --git a/docs/images/foobar_bvar.png b/docs/images/foobar_bvar.png index 1f4aa7bb..e794b8cc 100644 Binary files a/docs/images/foobar_bvar.png and b/docs/images/foobar_bvar.png differ diff --git a/docs/images/growth_profiler.png b/docs/images/growth_profiler.png index ff9e699a..dcdf5c98 100644 Binary files a/docs/images/growth_profiler.png and b/docs/images/growth_profiler.png differ diff --git a/docs/images/health_service.png b/docs/images/health_service.png index ac4c24b5..4fdd820f 100644 Binary files a/docs/images/health_service.png and b/docs/images/health_service.png differ diff --git a/docs/images/heap_profiler_3.gif b/docs/images/heap_profiler_3.gif index cf2daf78..7f8f9eb9 100644 Binary files a/docs/images/heap_profiler_3.gif and b/docs/images/heap_profiler_3.gif differ diff --git a/docs/images/ns_access_interval.png b/docs/images/ns_access_interval.png index cbc8a094..278ed9eb 100644 Binary files a/docs/images/ns_access_interval.png and b/docs/images/ns_access_interval.png differ diff --git a/docs/images/protobufs_service.png b/docs/images/protobufs_service.png index 2fe9833e..0de7d07b 100644 Binary files a/docs/images/protobufs_service.png and b/docs/images/protobufs_service.png differ diff --git a/docs/images/restful_1.png b/docs/images/restful_1.png index 80ddf49b..6d2accb0 100644 Binary files a/docs/images/restful_1.png and b/docs/images/restful_1.png differ diff --git a/docs/images/restful_2.png b/docs/images/restful_2.png index b79f8971..4aed686a 100644 Binary files a/docs/images/restful_2.png and b/docs/images/restful_2.png differ diff --git a/docs/images/rpc_press_1.png b/docs/images/rpc_press_1.png index d5c56427..e70ff49e 100644 Binary files a/docs/images/rpc_press_1.png and b/docs/images/rpc_press_1.png differ diff --git a/docs/images/rpc_press_2.png b/docs/images/rpc_press_2.png index a9edc213..460690cc 100644 Binary files a/docs/images/rpc_press_2.png and b/docs/images/rpc_press_2.png differ diff --git a/docs/images/rpc_replay_4.png b/docs/images/rpc_replay_4.png index ddc2a4c9..b9541afe 100644 Binary files a/docs/images/rpc_replay_4.png and b/docs/images/rpc_replay_4.png differ diff --git a/docs/images/rpc_view_1.png b/docs/images/rpc_view_1.png index be8f4900..6f02d483 100644 Binary files a/docs/images/rpc_view_1.png and b/docs/images/rpc_view_1.png differ diff --git a/docs/images/rpc_view_2.png b/docs/images/rpc_view_2.png index 749d0326..7e8432bb 100644 Binary files a/docs/images/rpc_view_2.png and b/docs/images/rpc_view_2.png differ diff --git a/docs/images/rpc_view_3.png b/docs/images/rpc_view_3.png index c6522bd3..cf2738af 100644 Binary files a/docs/images/rpc_view_3.png and b/docs/images/rpc_view_3.png differ diff --git a/docs/images/rpcz.png b/docs/images/rpcz.png index 0d33a0dc..5067b676 100644 Binary files a/docs/images/rpcz.png and b/docs/images/rpcz.png differ diff --git a/docs/images/rpcz_2.png b/docs/images/rpcz_2.png index 3d9bdf3b..c645b6d6 100644 Binary files a/docs/images/rpcz_2.png and b/docs/images/rpcz_2.png differ diff --git a/docs/images/rpcz_3.png b/docs/images/rpcz_3.png index 90f6e14f..57a8007a 100644 Binary files a/docs/images/rpcz_3.png and b/docs/images/rpcz_3.png differ diff --git a/docs/images/rpcz_6.png b/docs/images/rpcz_6.png index fd6366a3..59796fe8 100644 Binary files a/docs/images/rpcz_6.png and b/docs/images/rpcz_6.png differ diff --git a/docs/images/rpcz_7.png b/docs/images/rpcz_7.png index 5951c84f..53b98742 100644 Binary files a/docs/images/rpcz_7.png and b/docs/images/rpcz_7.png differ diff --git a/docs/images/set_flag_invalid_value.png b/docs/images/set_flag_invalid_value.png index 9c762ad8..3d83a475 100644 Binary files a/docs/images/set_flag_invalid_value.png and b/docs/images/set_flag_invalid_value.png differ diff --git a/docs/images/set_flag_reject.png b/docs/images/set_flag_reject.png index 74ceec84..6f342864 100644 Binary files a/docs/images/set_flag_reject.png and b/docs/images/set_flag_reject.png differ diff --git a/docs/images/set_flag_with_form.png b/docs/images/set_flag_with_form.png index 8411aa13..af2d4667 100644 Binary files a/docs/images/set_flag_with_form.png and b/docs/images/set_flag_with_form.png differ diff --git a/docs/images/set_flag_with_form_2.png b/docs/images/set_flag_with_form_2.png index 13afbf95..275f21bc 100644 Binary files a/docs/images/set_flag_with_form_2.png and b/docs/images/set_flag_with_form_2.png differ diff --git a/docs/images/short_conn.png b/docs/images/short_conn.png index f0b494e8..dc57365f 100644 Binary files a/docs/images/short_conn.png and b/docs/images/short_conn.png differ diff --git a/docs/images/vars_1.gif b/docs/images/vars_1.gif index d68cd6c9..b83a689d 100644 Binary files a/docs/images/vars_1.gif and b/docs/images/vars_1.gif differ diff --git a/docs/images/vars_2.gif b/docs/images/vars_2.gif index 1154b8da..715d4c70 100644 Binary files a/docs/images/vars_2.gif and b/docs/images/vars_2.gif differ diff --git a/docs/images/version_service.png b/docs/images/version_service.png index 93f766f0..c514fc33 100644 Binary files a/docs/images/version_service.png and b/docs/images/version_service.png differ diff --git a/docs/images/vlog_service.png b/docs/images/vlog_service.png index 8b9a2629..1d3af568 100644 Binary files a/docs/images/vlog_service.png and b/docs/images/vlog_service.png differ diff --git a/example/echo_c++/server.cpp b/example/echo_c++/server.cpp index df07b16b..41ffd0b1 100644 --- a/example/echo_c++/server.cpp +++ b/example/echo_c++/server.cpp @@ -24,6 +24,8 @@ DEFINE_bool(echo_attachment, true, "Echo attachment as well"); DEFINE_int32(port, 8000, "TCP Port of this server"); +DEFINE_string(listen_addr, "", "Server listen address, may be IPV4/IPV6/UDS." + " If this is set, the flag port will be ignored"); DEFINE_int32(idle_timeout_s, -1, "Connection will be closed if there is no " "read/write operations during the last `idle_timeout_s'"); DEFINE_int32(logoff_ms, 2000, "Maximum duration of server's LOGOFF state " @@ -92,10 +94,19 @@ int main(int argc, char* argv[]) { return -1; } + butil::EndPoint point; + if (!FLAGS_listen_addr.empty()) { + if (butil::str2endpoint(FLAGS_listen_addr.c_str(), &point) < 0) { + LOG(ERROR) << "Invalid listen address:" << FLAGS_listen_addr; + return -1; + } + } else { + point = butil::EndPoint(butil::IP_ANY, FLAGS_port); + } // Start the server. brpc::ServerOptions options; options.idle_timeout_sec = FLAGS_idle_timeout_s; - if (server.Start(FLAGS_port, &options) != 0) { + if (server.Start(point, &options) != 0) { LOG(ERROR) << "Fail to start EchoServer"; return -1; } diff --git a/src/brpc/acceptor.cpp b/src/brpc/acceptor.cpp index 0e12751c..39a2d012 100644 --- a/src/brpc/acceptor.cpp +++ b/src/brpc/acceptor.cpp @@ -240,9 +240,10 @@ void Acceptor::ListConnections(std::vector* conn_list) { void Acceptor::OnNewConnectionsUntilEAGAIN(Socket* acception) { while (1) { - struct sockaddr in_addr; + struct sockaddr_storage in_addr; + bzero(&in_addr, sizeof(in_addr)); socklen_t in_len = sizeof(in_addr); - butil::fd_guard in_fd(accept(acception->fd(), &in_addr, &in_len)); + butil::fd_guard in_fd(accept(acception->fd(), (sockaddr*)&in_addr, &in_len)); if (in_fd < 0) { // no EINTR because listened fd is non-blocking. if (errno == EAGAIN) { @@ -269,7 +270,7 @@ void Acceptor::OnNewConnectionsUntilEAGAIN(Socket* acception) { SocketOptions options; options.keytable_pool = am->_keytable_pool; options.fd = in_fd; - options.remote_side = butil::EndPoint(*(sockaddr_in*)&in_addr); + butil::sockaddr2endpoint(&in_addr, in_len, &options.remote_side); options.user = acception->user(); options.on_edge_triggered_events = InputMessenger::OnNewMessages; options.initial_ssl_ctx = am->_ssl_ctx; diff --git a/src/brpc/builtin/index_service.cpp b/src/brpc/builtin/index_service.cpp index f067b55c..d3c97c33 100644 --- a/src/brpc/builtin/index_service.cpp +++ b/src/brpc/builtin/index_service.cpp @@ -48,8 +48,6 @@ void IndexService::default_method(::google::protobuf::RpcController* controller, Controller *cntl = (Controller*)controller; cntl->http_response().set_content_type("text/plain"); const Server* server = cntl->server(); - const butil::EndPoint my_addr(butil::my_ip(), - server->listen_address().port); const bool use_html = UseHTML(cntl->http_request()); const bool as_more = cntl->http_request().uri().GetQuery("as_more"); if (use_html && !as_more) { @@ -144,8 +142,14 @@ void IndexService::default_method(::google::protobuf::RpcController* controller, << " : Profiling growth of heap" << (!IsHeapProfilerEnabled() ? " (disabled)" : "") << NL; } - os << "curl -H 'Content-Type: application/json' -d 'JSON' " << my_addr - << "/ServiceName/MethodName : Call method by http+json" << NL + os << "curl -H 'Content-Type: application/json' -d 'JSON' "; + if (butil::is_endpoint_extended(server->listen_address())) { + os << ""; + } else { + const butil::EndPoint my_addr(butil::my_ip(), server->listen_address().port); + os << my_addr; + } + os << "/ServiceName/MethodName : Call method by http+json" << NL << Path("/version", html_addr) << " : Version of this server, set by Server::set_version()" << NL diff --git a/src/brpc/builtin/pprof_service.cpp b/src/brpc/builtin/pprof_service.cpp index abc2b8e3..8aa7834b 100644 --- a/src/brpc/builtin/pprof_service.cpp +++ b/src/brpc/builtin/pprof_service.cpp @@ -220,7 +220,7 @@ void PProfService::heap( extra_desc = " (no TCMALLOC_SAMPLE_PARAMETER in env)"; } cntl->SetFailed(ENOMETHOD, "Heap profiler is not enabled%s," - "check out http://wiki.baidu.com/display/RPC", + "check out https://github.com/apache/incubator-brpc/blob/master/docs/cn/heap_profiler.md", extra_desc); return; } diff --git a/src/brpc/channel.cpp b/src/brpc/channel.cpp index 9c6642d5..c55533a1 100755 --- a/src/brpc/channel.cpp +++ b/src/brpc/channel.cpp @@ -256,7 +256,7 @@ int Channel::Init(const char* server_addr, int port, return -1; } } - return InitSingle(point, server_addr, options); + return InitSingle(point, server_addr, options, port); } static int CreateSocketSSLContext(const ChannelOptions& options, @@ -283,20 +283,25 @@ int Channel::Init(butil::EndPoint server_addr_and_port, int Channel::InitSingle(const butil::EndPoint& server_addr_and_port, const char* raw_server_address, - const ChannelOptions* options) { + const ChannelOptions* options, + int raw_port) { GlobalInitializeOrDie(); if (InitChannelOptions(options) != 0) { return -1; } - if (_options.protocol == brpc::PROTOCOL_HTTP && - ::strncmp(raw_server_address, "https://", 8) == 0) { + std::string scheme; + int* port_out = raw_port == -1 ? &raw_port: NULL; + ParseURL(raw_server_address, &scheme, &_service_name, port_out); + if (raw_port != -1) { + _service_name.append(":").append(std::to_string(raw_port)); + } + if (_options.protocol == brpc::PROTOCOL_HTTP && scheme == "https://") { if (_options.mutable_ssl_options()->sni_name.empty()) { - ParseURL(raw_server_address, - NULL, &_options.mutable_ssl_options()->sni_name, NULL); + _options.mutable_ssl_options()->sni_name = _service_name; } } const int port = server_addr_and_port.port; - if (port < 0 || port > 65535) { + if (port < 0) { LOG(ERROR) << "Invalid port=" << port; return -1; } @@ -325,11 +330,15 @@ int Channel::Init(const char* ns_url, if (InitChannelOptions(options) != 0) { return -1; } - if (_options.protocol == brpc::PROTOCOL_HTTP && - ::strncmp(ns_url, "https://", 8) == 0) { + std::string scheme; + int raw_port = -1; + ParseURL(ns_url, &scheme, &_service_name, &raw_port); + if (raw_port != -1) { + _service_name.append(":").append(std::to_string(raw_port)); + } + if (_options.protocol == brpc::PROTOCOL_HTTP && scheme == "https://") { if (_options.mutable_ssl_options()->sni_name.empty()) { - ParseURL(ns_url, - NULL, &_options.mutable_ssl_options()->sni_name, NULL); + _options.mutable_ssl_options()->sni_name = _service_name; } } LoadBalancerWithNaming* lb = new (std::nothrow) LoadBalancerWithNaming; @@ -386,6 +395,12 @@ void Channel::CallMethod(const google::protobuf::MethodDescriptor* method, CHECK(cntl->protocol_param().empty()); cntl->protocol_param() = _options.protocol.param(); } + if (_options.protocol == brpc::PROTOCOL_HTTP) { + URI& uri = cntl->http_request().uri(); + if (uri.host().empty() && !_service_name.empty()) { + uri.SetHostAndPort(_service_name); + } + } cntl->_preferred_index = _preferred_index; cntl->_retry_policy = _options.retry_policy; if (_options.enable_circuit_breaker) { @@ -562,7 +577,7 @@ int Channel::CheckHealth() { return -1; } else { SocketUniquePtr tmp_sock; - LoadBalancer::SelectIn sel_in = { 0, false, false, 0, NULL }; + LoadBalancer::SelectIn sel_in = { 0, false, true, 0, NULL }; LoadBalancer::SelectOut sel_out(&tmp_sock); return _lb->SelectServer(sel_in, &sel_out); } diff --git a/src/brpc/channel.h b/src/brpc/channel.h index 684e8f14..ef97ced3 100644 --- a/src/brpc/channel.h +++ b/src/brpc/channel.h @@ -213,8 +213,10 @@ protected: int InitChannelOptions(const ChannelOptions* options); int InitSingle(const butil::EndPoint& server_addr_and_port, const char* raw_server_address, - const ChannelOptions* options); + const ChannelOptions* options, + int raw_port = -1); + std::string _service_name; butil::EndPoint _server_address; SocketId _server_id; Protocol::SerializeRequest _serialize_request; diff --git a/src/brpc/controller.cpp b/src/brpc/controller.cpp index d16f35d2..25227454 100644 --- a/src/brpc/controller.cpp +++ b/src/brpc/controller.cpp @@ -243,6 +243,7 @@ void Controller::ResetPods() { _timeout_ms = UNSET_MAGIC_NUM; _backup_request_ms = UNSET_MAGIC_NUM; _connect_timeout_ms = UNSET_MAGIC_NUM; + _real_timeout_ms = UNSET_MAGIC_NUM; _deadline_us = -1; _timeout_id = 0; _begin_time_us = 0; @@ -308,6 +309,7 @@ void Controller::Call::Reset() { void Controller::set_timeout_ms(int64_t timeout_ms) { if (timeout_ms <= 0x7fffffff) { _timeout_ms = timeout_ms; + _real_timeout_ms = timeout_ms; } else { _timeout_ms = 0x7fffffff; LOG(WARNING) << "timeout_ms is limited to 0x7fffffff (roughly 24 days)"; @@ -977,6 +979,12 @@ void Controller::HandleSendFailed() { void Controller::IssueRPC(int64_t start_realtime_us) { _current_call.begin_time_us = start_realtime_us; + + // If has retry/backup request,we will recalculate the timeout, + if (_real_timeout_ms > 0) { + _real_timeout_ms -= (start_realtime_us - _begin_time_us) / 1000; + } + // Clear last error, Don't clear _error_text because we append to it. _error_code = 0; diff --git a/src/brpc/controller.h b/src/brpc/controller.h index e4e35339..8e1ac62d 100755 --- a/src/brpc/controller.h +++ b/src/brpc/controller.h @@ -736,6 +736,8 @@ private: int32_t _timeout_ms; int32_t _connect_timeout_ms; int32_t _backup_request_ms; + // If this rpc call has retry/backup request,this var save the real timeout for current call + int64_t _real_timeout_ms; // Deadline of this RPC (since the Epoch in microseconds). int64_t _deadline_us; // Timer registered to trigger RPC timeout event diff --git a/src/brpc/details/controller_private_accessor.h b/src/brpc/details/controller_private_accessor.h index 362c8f06..990efc91 100644 --- a/src/brpc/details/controller_private_accessor.h +++ b/src/brpc/details/controller_private_accessor.h @@ -57,6 +57,10 @@ public: return _cntl->_current_call.sending_sock.get(); } + int64_t real_timeout_ms() { + return _cntl->_real_timeout_ms; + } + void move_in_server_receiving_sock(SocketUniquePtr& ptr) { CHECK(_cntl->_current_call.sending_sock == NULL); _cntl->_current_call.sending_sock.reset(ptr.release()); diff --git a/src/brpc/details/load_balancer_with_naming.cpp b/src/brpc/details/load_balancer_with_naming.cpp index 2fad84b4..7370a1ff 100644 --- a/src/brpc/details/load_balancer_with_naming.cpp +++ b/src/brpc/details/load_balancer_with_naming.cpp @@ -34,11 +34,11 @@ int LoadBalancerWithNaming::Init(const char* ns_url, const char* lb_name, return -1; } if (GetNamingServiceThread(&_nsthread_ptr, ns_url, options) != 0) { - LOG(FATAL) << "Fail to get NamingServiceThread"; + LOG(ERROR) << "Fail to get NamingServiceThread"; return -1; } if (_nsthread_ptr->AddWatcher(this, filter) != 0) { - LOG(FATAL) << "Fail to add watcher into _server_list"; + LOG(ERROR) << "Fail to add watcher into _server_list"; return -1; } return 0; diff --git a/src/brpc/details/naming_service_thread.cpp b/src/brpc/details/naming_service_thread.cpp index 61044519..13acf8ae 100644 --- a/src/brpc/details/naming_service_thread.cpp +++ b/src/brpc/details/naming_service_thread.cpp @@ -337,7 +337,7 @@ int NamingServiceThread::AddWatcher(NamingServiceWatcher* watcher, return -1; } BAIDU_SCOPED_LOCK(_mutex); - if (_watchers.insert(std::make_pair(watcher, filter)).second) { + if (_watchers.emplace(watcher, filter).second) { if (!_last_sockets.empty()) { std::vector added_ids; ServerNodeWithId2ServerId(_last_sockets, &added_ids, filter); diff --git a/src/brpc/esp_message.cpp b/src/brpc/esp_message.cpp index 0c93c12f..583181d3 100644 --- a/src/brpc/esp_message.cpp +++ b/src/brpc/esp_message.cpp @@ -52,6 +52,12 @@ EspMessage* EspMessage::New() const { return new EspMessage; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +EspMessage* EspMessage::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void EspMessage::Clear() { head.body_len = 0; body.clear(); diff --git a/src/brpc/esp_message.h b/src/brpc/esp_message.h index 1b23974c..a17fd917 100644 --- a/src/brpc/esp_message.h +++ b/src/brpc/esp_message.h @@ -54,6 +54,9 @@ public: // implements Message ---------------------------------------------- EspMessage* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + EspMessage* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const EspMessage& from); diff --git a/src/brpc/global.cpp b/src/brpc/global.cpp index 2f654115..ced8a11c 100755 --- a/src/brpc/global.cpp +++ b/src/brpc/global.cpp @@ -129,6 +129,7 @@ struct GlobalExtensions { #endif FileNamingService fns; ListNamingService lns; + DomainListNamingService dlns; DomainNamingService dns; DomainNamingService dns_with_ssl; RemoteFileNamingService rfns; @@ -350,6 +351,7 @@ static void GlobalInitializeOrDieImpl() { #endif NamingServiceExtension()->RegisterOrDie("file", &g_ext->fns); NamingServiceExtension()->RegisterOrDie("list", &g_ext->lns); + NamingServiceExtension()->RegisterOrDie("dlist", &g_ext->dlns); NamingServiceExtension()->RegisterOrDie("http", &g_ext->dns); NamingServiceExtension()->RegisterOrDie("https", &g_ext->dns_with_ssl); NamingServiceExtension()->RegisterOrDie("redis", &g_ext->dns); diff --git a/src/brpc/input_messenger.cpp b/src/brpc/input_messenger.cpp index aff1b981..d9b1a3a9 100644 --- a/src/brpc/input_messenger.cpp +++ b/src/brpc/input_messenger.cpp @@ -67,31 +67,43 @@ ParseResult InputMessenger::CutInputMessage( // selection or by client. if (preferred >= 0 && preferred <= max_index && _handlers[preferred].parse != NULL) { - ParseResult result = - _handlers[preferred].parse(&m->_read_buf, m, read_eof, _handlers[preferred].arg); - if (result.is_ok() || - result.error() == PARSE_ERROR_NOT_ENOUGH_DATA) { - *index = preferred; - return result; - } else if (result.error() != PARSE_ERROR_TRY_OTHERS) { - // Critical error, return directly. - LOG_IF(ERROR, result.error() == PARSE_ERROR_TOO_BIG_DATA) - << "A message from " << m->remote_side() - << "(protocol=" << _handlers[preferred].name - << ") is bigger than " << FLAGS_max_body_size - << " bytes, the connection will be closed." - " Set max_body_size to allow bigger messages"; - return result; - } - if (m->CreatedByConnect() && - // baidu_std may fall to streaming_rpc - (ProtocolType)preferred != PROTOCOL_BAIDU_STD) { - // The protocol is fixed at client-side, no need to try others. - LOG(ERROR) << "Fail to parse response from " << m->remote_side() - << " by " << _handlers[preferred].name - << " at client-side"; - return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG); - } + int cur_index = preferred; + do { + ParseResult result = + _handlers[cur_index].parse(&m->_read_buf, m, read_eof, _handlers[cur_index].arg); + if (result.is_ok() || + result.error() == PARSE_ERROR_NOT_ENOUGH_DATA) { + m->set_preferred_index(cur_index); + *index = cur_index; + return result; + } else if (result.error() != PARSE_ERROR_TRY_OTHERS) { + // Critical error, return directly. + LOG_IF(ERROR, result.error() == PARSE_ERROR_TOO_BIG_DATA) + << "A message from " << m->remote_side() + << "(protocol=" << _handlers[cur_index].name + << ") is bigger than " << FLAGS_max_body_size + << " bytes, the connection will be closed." + " Set max_body_size to allow bigger messages"; + return result; + } + + if (m->CreatedByConnect()) { + if((ProtocolType)cur_index == PROTOCOL_BAIDU_STD) { + // baidu_std may fall to streaming_rpc. + cur_index = (int)PROTOCOL_STREAMING_RPC; + continue; + } else { + // The protocol is fixed at client-side, no need to try others. + LOG(ERROR) << "Fail to parse response from " << m->remote_side() + << " by " << _handlers[preferred].name + << " at client-side"; + return MakeParseError(PARSE_ERROR_ABSOLUTELY_WRONG); + } + } else { + // Try other protocols. + break; + } + } while (true); // Clear context before trying next protocol which probably has // an incompatible context with the current one. if (m->parsing_context()) { diff --git a/src/brpc/memcache.cpp b/src/brpc/memcache.cpp index cd7ea30b..60d6c710 100644 --- a/src/brpc/memcache.cpp +++ b/src/brpc/memcache.cpp @@ -63,6 +63,12 @@ MemcacheRequest* MemcacheRequest::New() const { return new MemcacheRequest; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +MemcacheRequest* MemcacheRequest::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void MemcacheRequest::Clear() { _buf.clear(); _pipelined_count = 0; @@ -207,6 +213,13 @@ MemcacheResponse* MemcacheResponse::New() const { return new MemcacheResponse; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +MemcacheResponse* +MemcacheResponse::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void MemcacheResponse::Clear() { } diff --git a/src/brpc/memcache.h b/src/brpc/memcache.h index bd54c390..ab464573 100644 --- a/src/brpc/memcache.h +++ b/src/brpc/memcache.h @@ -90,6 +90,9 @@ public: // Protobuf methods. MemcacheRequest* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + MemcacheRequest* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const MemcacheRequest& from); @@ -200,6 +203,9 @@ public: // implements Message ---------------------------------------------- MemcacheResponse* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + MemcacheResponse* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const MemcacheResponse& from); diff --git a/src/brpc/nshead_message.cpp b/src/brpc/nshead_message.cpp index e58de2a9..b74089a5 100644 --- a/src/brpc/nshead_message.cpp +++ b/src/brpc/nshead_message.cpp @@ -54,6 +54,12 @@ NsheadMessage* NsheadMessage::New() const { return new NsheadMessage; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +NsheadMessage* NsheadMessage::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void NsheadMessage::Clear() { memset(&head, 0, sizeof(head)); body.clear(); diff --git a/src/brpc/nshead_message.h b/src/brpc/nshead_message.h index 3f41454b..de8810ff 100644 --- a/src/brpc/nshead_message.h +++ b/src/brpc/nshead_message.h @@ -50,6 +50,9 @@ public: // implements Message ---------------------------------------------- NsheadMessage* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + NsheadMessage* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const NsheadMessage& from); diff --git a/src/brpc/policy/baidu_rpc_meta.proto b/src/brpc/policy/baidu_rpc_meta.proto index dfff8ed1..dc716540 100644 --- a/src/brpc/policy/baidu_rpc_meta.proto +++ b/src/brpc/policy/baidu_rpc_meta.proto @@ -42,6 +42,7 @@ message RpcRequestMeta { optional int64 span_id = 5; optional int64 parent_span_id = 6; optional string request_id = 7; // correspond to x-request-id in http header + optional int32 timeout_ms = 8; // client's timeout setting for current call } message RpcResponseMeta { diff --git a/src/brpc/policy/baidu_rpc_protocol.cpp b/src/brpc/policy/baidu_rpc_protocol.cpp index 23a729f6..a15e2246 100644 --- a/src/brpc/policy/baidu_rpc_protocol.cpp +++ b/src/brpc/policy/baidu_rpc_protocol.cpp @@ -51,6 +51,9 @@ DEFINE_bool(baidu_protocol_use_fullname, true, "If this flag is true, baidu_std puts service.full_name in requests" ", otherwise puts service.name (required by jprotobuf)."); +DEFINE_bool(baidu_std_protocol_deliver_timeout_ms, false, + "If this flag is true, baidu_std puts timeout_ms in requests."); + // Notes: // 1. 12-byte header [PRPC][body_size][meta_size] // 2. body_size and meta_size are in network byte order @@ -228,10 +231,12 @@ void SendRpcResponse(int64_t correlation_id, if (span) { span->set_response_size(res_buf.size()); } - if (stream_ptr) { - CHECK(accessor.remote_stream_settings() != NULL); + // Send rpc response over stream even if server side failed to create + // stream for some reasons. + if(cntl->has_remote_stream()){ // Send the response over stream to notify that this stream connection // is successfully built. + // Response_stream can be INVALID_STREAM_ID when error occurs. if (SendStreamData(sock, &res_buf, accessor.remote_stream_settings()->stream_id(), accessor.response_stream()) != 0) { @@ -239,13 +244,18 @@ void SendRpcResponse(int64_t correlation_id, PLOG_IF(WARNING, errcode != EPIPE) << "Fail to write into " << *sock; cntl->SetFailed(errcode, "Fail to write into %s", sock->description().c_str()); - ((Stream*)stream_ptr->conn())->Close(); + if(stream_ptr) { + ((Stream*)stream_ptr->conn())->Close(); + } return; } - // Now it's ok the mark this server-side stream as connectted as all the - // written user data would follower the RPC response. - ((Stream*)stream_ptr->conn())->SetConnected(); - } else { + + if(stream_ptr) { + // Now it's ok the mark this server-side stream as connectted as all the + // written user data would follower the RPC response. + ((Stream*)stream_ptr->conn())->SetConnected(); + } + } else{ // Have the risk of unlimited pending responses, in which case, tell // users to set max_concurrency. Socket::WriteOptions wopt; @@ -346,6 +356,9 @@ void ProcessRpcRequest(InputMessageBase* msg_base) { if (request_meta.has_request_id()) { cntl->set_request_id(request_meta.request_id()); } + if (request_meta.has_timeout_ms()) { + cntl->set_timeout_ms(request_meta.timeout_ms()); + } cntl->set_request_compress_type((CompressType)meta.compress_type()); accessor.set_server(server) .set_security_mode(security_mode) @@ -487,7 +500,7 @@ void ProcessRpcRequest(InputMessageBase* msg_base) { req.get(), res.get(), server, method_status, msg->received_us()); - // optional, just release resourse ASAP + // optional, just release resource ASAP msg.reset(); req_buf.clear(); @@ -552,18 +565,21 @@ void ProcessRpcResponse(InputMessageBase* msg_base) { const bthread_id_t cid = { static_cast(meta.correlation_id()) }; Controller* cntl = NULL; + + StreamId remote_stream_id = meta.has_stream_settings() ? meta.stream_settings().stream_id(): INVALID_STREAM_ID; + const int rc = bthread_id_lock(cid, (void**)&cntl); if (rc != 0) { LOG_IF(ERROR, rc != EINVAL && rc != EPERM) << "Fail to lock correlation_id=" << cid << ": " << berror(rc); - if (meta.has_stream_settings()) { + if (remote_stream_id != INVALID_STREAM_ID) { SendStreamRst(msg->socket(), meta.stream_settings().stream_id()); } return; } ControllerPrivateAccessor accessor(cntl); - if (meta.has_stream_settings()) { + if (remote_stream_id != INVALID_STREAM_ID) { accessor.set_remote_stream_settings( new StreamSettings(meta.stream_settings())); } @@ -615,7 +631,7 @@ void ProcessRpcResponse(InputMessageBase* msg_base) { } while (0); // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } @@ -672,6 +688,13 @@ void PackRpcRequest(butil::IOBuf* req_buf, if (attached_size) { meta.set_attachment_size(attached_size); } + + if (FLAGS_baidu_std_protocol_deliver_timeout_ms) { + if (accessor.real_timeout_ms() > 0) { + request_meta->set_timeout_ms(accessor.real_timeout_ms()); + } + } + Span* span = accessor.span(); if (span) { request_meta->set_trace_id(span->trace_id()); diff --git a/src/brpc/policy/consistent_hashing_load_balancer.cpp b/src/brpc/policy/consistent_hashing_load_balancer.cpp index fd223803..19c8849c 100644 --- a/src/brpc/policy/consistent_hashing_load_balancer.cpp +++ b/src/brpc/policy/consistent_hashing_load_balancer.cpp @@ -69,7 +69,7 @@ bool DefaultReplicaPolicy::Build(ServerId server, } replicas->clear(); for (size_t i = 0; i < num_replicas; ++i) { - char host[32]; + char host[256]; int len = snprintf(host, sizeof(host), "%s-%lu", endpoint2str(ptr->remote_side()).c_str(), i); ConsistentHashingLoadBalancer::Node node; diff --git a/src/brpc/policy/domain_naming_service.cpp b/src/brpc/policy/domain_naming_service.cpp index 6e1d9752..2bd03197 100644 --- a/src/brpc/policy/domain_naming_service.cpp +++ b/src/brpc/policy/domain_naming_service.cpp @@ -122,6 +122,7 @@ int DomainNamingService::GetServers(const char* dns_name, } #endif + //TODO add protocols other than IPv4 supports butil::EndPoint point; point.port = port; for (int i = 0; result->h_addr_list[i] != NULL; ++i) { diff --git a/src/brpc/policy/esp_protocol.cpp b/src/brpc/policy/esp_protocol.cpp index 6665a1a9..5925796b 100644 --- a/src/brpc/policy/esp_protocol.cpp +++ b/src/brpc/policy/esp_protocol.cpp @@ -154,7 +154,7 @@ void ProcessEspResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/http2_rpc_protocol.cpp b/src/brpc/policy/http2_rpc_protocol.cpp index a5e89d01..ab2d2799 100644 --- a/src/brpc/policy/http2_rpc_protocol.cpp +++ b/src/brpc/policy/http2_rpc_protocol.cpp @@ -204,7 +204,6 @@ bool ParseH2Settings(H2Settings* out, butil::IOBufBytesIterator& it, size_t n) { default: // An endpoint that receives a SETTINGS frame with any unknown or // unsupported identifier MUST ignore that setting (section 6.5.2) - LOG(WARNING) << "Unknown setting, id=" << id << " value=" << value; break; } } diff --git a/src/brpc/policy/http_rpc_protocol.cpp b/src/brpc/policy/http_rpc_protocol.cpp index 7bd06ec5..9c2ff96c 100644 --- a/src/brpc/policy/http_rpc_protocol.cpp +++ b/src/brpc/policy/http_rpc_protocol.cpp @@ -17,6 +17,7 @@ #include // MethodDescriptor +#include #include #include // ProtoMessageToJson #include // JsonToProtoMessage @@ -34,6 +35,7 @@ #include "brpc/details/server_private_accessor.h" #include "brpc/span.h" #include "brpc/socket.h" // Socket +#include "brpc/rpc_dump.h" // SampledRequest #include "brpc/http_status_code.h" // HTTP_STATUS_* #include "brpc/details/controller_private_accessor.h" #include "brpc/builtin/index_service.h" // IndexService @@ -65,7 +67,8 @@ DEFINE_int32(http_body_compress_threshold, 512, "Not compress http body when " DEFINE_string(http_header_of_user_ip, "", "http requests sent by proxies may " "set the client ip in http headers. When this flag is non-empty, " "brpc will read ip:port from the specified header for " - "authorization and set Controller::remote_side()"); + "authorization and set Controller::remote_side(). Currently, " + "support IPv4 address only."); DEFINE_bool(pb_enum_as_number, false, "[Not recommended] Convert enums in " @@ -82,6 +85,7 @@ static bool GetUserAddressFromHeaderImpl(const HttpHeader& headers, if (user_addr_str == NULL) { return false; } + //TODO add protocols other than IPv4 supports. if (user_addr_str->find(':') == std::string::npos) { if (butil::str2ip(user_addr_str->c_str(), &user_addr->ip) != 0) { LOG(WARNING) << "Fail to parse ip from " << *user_addr_str; @@ -191,6 +195,9 @@ HttpContentType ParseContentType(butil::StringPiece ct, bool* is_grpc_ct) { if (ct.starts_with("json")) { type = HTTP_CONTENT_JSON; ct.remove_prefix(4); + } else if (ct.starts_with("proto-text")) { + type = HTTP_CONTENT_PROTO_TEXT; + ct.remove_prefix(10); } else if (ct.starts_with("proto")) { type = HTTP_CONTENT_PROTO; ct.remove_prefix(5); @@ -434,6 +441,11 @@ void ProcessHttpResponse(InputMessageBase* msg) { cntl->SetFailed(ERESPONSE, "Fail to parse content"); break; } + } else if (content_type == HTTP_CONTENT_PROTO_TEXT) { + if (!ParsePbTextFromIOBuf(cntl->response(), res_body)) { + cntl->SetFailed(ERESPONSE, "Fail to parse proto-text content"); + break; + } } else if (content_type == HTTP_CONTENT_JSON) { // message body is json butil::IOBufAsZeroCopyInputStream wrapper(res_body); @@ -513,6 +525,12 @@ void SerializeHttpRequest(butil::IOBuf* /*not used*/, return cntl->SetFailed(EREQUEST, "Fail to serialize %s", pbreq->GetTypeName().c_str()); } + } else if (content_type == HTTP_CONTENT_PROTO_TEXT) { + if (!google::protobuf::TextFormat::Print(*pbreq, &wrapper)) { + cntl->request_attachment().clear(); + return cntl->SetFailed(EREQUEST, "Fail to print %s as proto-text", + pbreq->GetTypeName().c_str()); + } } else if (content_type == HTTP_CONTENT_JSON) { std::string err; json2pb::Pb2JsonOptions opt; @@ -757,6 +775,10 @@ HttpResponseSender::~HttpResponseSender() { if (!res->SerializeToZeroCopyStream(&wrapper)) { cntl->SetFailed(ERESPONSE, "Fail to serialize %s", res->GetTypeName().c_str()); } + } else if (content_type == HTTP_CONTENT_PROTO_TEXT) { + if (!google::protobuf::TextFormat::Print(*res, &wrapper)) { + cntl->SetFailed(ERESPONSE, "Fail to print %s as proto-text", res->GetTypeName().c_str()); + } } else { std::string err; json2pb::Pb2JsonOptions opt; @@ -1146,13 +1168,13 @@ ParseResult ParseHttpMessage(butil::IOBuf *source, Socket *socket, return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA); } // Send 400 back. - butil::IOBuf bad_req; + butil::IOBuf resp; HttpHeader header; header.set_status_code(HTTP_STATUS_BAD_REQUEST); - MakeRawHttpRequest(&bad_req, &header, socket->remote_side(), NULL); + MakeRawHttpResponse(&resp, &header, NULL); Socket::WriteOptions wopt; wopt.ignore_eovercrowded = true; - socket->Write(&bad_req, &wopt); + socket->Write(&resp, &wopt); return MakeParseError(PARSE_ERROR_NOT_ENOUGH_DATA); } else { return MakeParseError(PARSE_ERROR_TRY_OTHERS); @@ -1470,6 +1492,12 @@ void ProcessHttpRequest(InputMessageBase *msg) { req->GetDescriptor()->full_name().c_str()); return; } + } else if (content_type == HTTP_CONTENT_PROTO_TEXT) { + if (!ParsePbTextFromIOBuf(req, req_body)) { + cntl->SetFailed(EREQUEST, "Fail to parse http proto-text body as %s", + req->GetDescriptor()->full_name().c_str()); + return; + } } else { butil::IOBufAsZeroCopyInputStream wrapper(req_body); std::string err; @@ -1483,13 +1511,23 @@ void ProcessHttpRequest(InputMessageBase *msg) { } } } + SampledRequest* sample = AskToBeSampled(); + if (sample && !is_http2) { + sample->meta.set_compress_type(COMPRESS_TYPE_NONE); + sample->meta.set_protocol_type(PROTOCOL_HTTP); + sample->meta.set_attachment_size(req_body.size()); + + butil::EndPoint ep; + MakeRawHttpRequest(&sample->request, &req_header, ep, &req_body); + sample->submit(start_parse_us); + } } else { // A http server, just keep content as it is. cntl->request_attachment().swap(req_body); } google::protobuf::Closure* done = new HttpResponseSenderAsDone(&resp_sender); - imsg_guard.reset(); // optional, just release resourse ASAP + imsg_guard.reset(); // optional, just release resource ASAP if (span) { span->set_start_callback_us(butil::cpuwide_time_us()); diff --git a/src/brpc/policy/http_rpc_protocol.h b/src/brpc/policy/http_rpc_protocol.h index d43ff7ea..4dce6037 100644 --- a/src/brpc/policy/http_rpc_protocol.h +++ b/src/brpc/policy/http_rpc_protocol.h @@ -141,6 +141,7 @@ enum HttpContentType { HTTP_CONTENT_OTHERS = 0, HTTP_CONTENT_JSON = 1, HTTP_CONTENT_PROTO = 2, + HTTP_CONTENT_PROTO_TEXT = 3, }; // Parse from the textual content type. One type may have more than one literals. diff --git a/src/brpc/policy/hulu_pbrpc_protocol.cpp b/src/brpc/policy/hulu_pbrpc_protocol.cpp index 50660a06..8f9855e1 100644 --- a/src/brpc/policy/hulu_pbrpc_protocol.cpp +++ b/src/brpc/policy/hulu_pbrpc_protocol.cpp @@ -499,7 +499,7 @@ void ProcessHuluRequest(InputMessageBase* msg_base) { req.get(), res.get(), server, method_status, msg->received_us()); - // optional, just release resourse ASAP + // optional, just release resource ASAP msg.reset(); req_buf.clear(); @@ -619,7 +619,7 @@ void ProcessHuluResponse(InputMessageBase* msg_base) { } // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/list_naming_service.cpp b/src/brpc/policy/list_naming_service.cpp index bffc9a0e..3a8ba45e 100644 --- a/src/brpc/policy/list_naming_service.cpp +++ b/src/brpc/policy/list_naming_service.cpp @@ -32,8 +32,8 @@ bool SplitIntoServerAndTag(const butil::StringPiece& line, butil::StringPiece* server_addr, butil::StringPiece* tag); -int ListNamingService::GetServers(const char *service_name, - std::vector* servers) { +int ParseServerList(const char* service_name, + std::vector* servers) { servers->clear(); // Sort/unique the inserted vector is faster, but may have a different order // of addresses from the file. To make assertions in tests easier, we use @@ -73,6 +73,11 @@ int ListNamingService::GetServers(const char *service_name, return 0; } +int ListNamingService::GetServers(const char *service_name, + std::vector* servers) { + return ParseServerList(service_name, servers); +} + int ListNamingService::RunNamingService(const char* service_name, NamingServiceActions* actions) { std::vector servers; @@ -98,5 +103,22 @@ void ListNamingService::Destroy() { delete this; } +int DomainListNamingService::GetServers(const char* service_name, + std::vector* servers) { + return ParseServerList(service_name, servers); +} + +void DomainListNamingService::Describe(std::ostream& os, + const DescribeOptions&) const { + os << "dlist"; + return; +} + +NamingService* DomainListNamingService::New() const { + return new DomainListNamingService; +} + +void DomainListNamingService::Destroy() { delete this; } + } // namespace policy } // namespace brpc diff --git a/src/brpc/policy/list_naming_service.h b/src/brpc/policy/list_naming_service.h index d99ab4e7..0a69dee5 100644 --- a/src/brpc/policy/list_naming_service.h +++ b/src/brpc/policy/list_naming_service.h @@ -20,6 +20,7 @@ #define BRPC_POLICY_LIST_NAMING_SERVICE #include "brpc/naming_service.h" +#include "brpc/periodic_naming_service.h" namespace brpc { @@ -43,6 +44,18 @@ private: void Destroy() override; }; +class DomainListNamingService : public PeriodicNamingService { +private: + int GetServers(const char* service_name, + std::vector* servers) override; + void Describe(std::ostream& os, + const DescribeOptions& options) const override; + + NamingService* New() const override; + + void Destroy() override; +}; + } // namespace policy } // namespace brpc diff --git a/src/brpc/policy/locality_aware_load_balancer.cpp b/src/brpc/policy/locality_aware_load_balancer.cpp index db139824..6f0c163c 100644 --- a/src/brpc/policy/locality_aware_load_balancer.cpp +++ b/src/brpc/policy/locality_aware_load_balancer.cpp @@ -341,6 +341,10 @@ int LocalityAwareLoadBalancer::SelectServer(const SelectIn& in, SelectOut* out) if (++ntry >= n) { break; } + } else { + if (++ntry >= n) { + break; + } } total = _total.load(butil::memory_order_relaxed); dice = butil::fast_rand_less_than(total); diff --git a/src/brpc/policy/memcache_binary_protocol.cpp b/src/brpc/policy/memcache_binary_protocol.cpp index a5a69e55..d4c39dfd 100644 --- a/src/brpc/policy/memcache_binary_protocol.cpp +++ b/src/brpc/policy/memcache_binary_protocol.cpp @@ -187,7 +187,7 @@ void ProcessMemcacheResponse(InputMessageBase* msg_base) { } // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/nova_pbrpc_protocol.cpp b/src/brpc/policy/nova_pbrpc_protocol.cpp index e21b3122..249e35c7 100644 --- a/src/brpc/policy/nova_pbrpc_protocol.cpp +++ b/src/brpc/policy/nova_pbrpc_protocol.cpp @@ -147,7 +147,7 @@ void ProcessNovaResponse(InputMessageBase* msg_base) { } // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/nshead_mcpack_protocol.cpp b/src/brpc/policy/nshead_mcpack_protocol.cpp index b5e201b8..40d38836 100644 --- a/src/brpc/policy/nshead_mcpack_protocol.cpp +++ b/src/brpc/policy/nshead_mcpack_protocol.cpp @@ -131,7 +131,7 @@ void ProcessNsheadMcpackResponse(InputMessageBase* msg_base) { } // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/nshead_protocol.cpp b/src/brpc/policy/nshead_protocol.cpp index b9498a60..b8945f97 100644 --- a/src/brpc/policy/nshead_protocol.cpp +++ b/src/brpc/policy/nshead_protocol.cpp @@ -317,7 +317,7 @@ void ProcessNsheadRequest(InputMessageBase* msg_base) { } } while (false); - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP if (span) { span->ResetServerSpanName(service->_cached_name); span->set_start_callback_us(butil::cpuwide_time_us()); @@ -367,7 +367,7 @@ void ProcessNsheadResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/public_pbrpc_protocol.cpp b/src/brpc/policy/public_pbrpc_protocol.cpp index d1bc8322..e5f34b2f 100644 --- a/src/brpc/policy/public_pbrpc_protocol.cpp +++ b/src/brpc/policy/public_pbrpc_protocol.cpp @@ -209,7 +209,7 @@ void ProcessPublicPbrpcResponse(InputMessageBase* msg_base) { } // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/redis_protocol.cpp b/src/brpc/policy/redis_protocol.cpp index 32ea629b..90d28d1f 100644 --- a/src/brpc/policy/redis_protocol.cpp +++ b/src/brpc/policy/redis_protocol.cpp @@ -289,7 +289,7 @@ void ProcessRedisResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/sofa_pbrpc_protocol.cpp b/src/brpc/policy/sofa_pbrpc_protocol.cpp index 27d80372..b0769d0f 100644 --- a/src/brpc/policy/sofa_pbrpc_protocol.cpp +++ b/src/brpc/policy/sofa_pbrpc_protocol.cpp @@ -441,7 +441,7 @@ void ProcessSofaRequest(InputMessageBase* msg_base) { req.get(), res.get(), server, method_status, msg->received_us()); - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP // `cntl', `req' and `res' will be deleted inside `done' if (span) { @@ -526,7 +526,7 @@ void ProcessSofaResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/thrift_protocol.cpp b/src/brpc/policy/thrift_protocol.cpp index eae081a0..634f8f37 100755 --- a/src/brpc/policy/thrift_protocol.cpp +++ b/src/brpc/policy/thrift_protocol.cpp @@ -533,7 +533,7 @@ void ProcessThriftRequest(InputMessageBase* msg_base) { " -usercode_in_pthread is on"); } - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP if (span) { span->ResetServerSpanName(cntl->thrift_method_name()); @@ -628,7 +628,7 @@ void ProcessThriftResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/policy/ubrpc2pb_protocol.cpp b/src/brpc/policy/ubrpc2pb_protocol.cpp index 69c9428b..312ec5d9 100644 --- a/src/brpc/policy/ubrpc2pb_protocol.cpp +++ b/src/brpc/policy/ubrpc2pb_protocol.cpp @@ -465,7 +465,7 @@ void ProcessUbrpcResponse(InputMessageBase* msg_base) { // Unlocks correlation_id inside. Revert controller's // error code if it version check of `cid' fails - msg.reset(); // optional, just release resourse ASAP + msg.reset(); // optional, just release resource ASAP accessor.OnResponse(cid, saved_error); } diff --git a/src/brpc/protocol.cpp b/src/brpc/protocol.cpp index 11297ea7..5f39939d 100644 --- a/src/brpc/protocol.cpp +++ b/src/brpc/protocol.cpp @@ -27,6 +27,7 @@ const uint64_t PB_TOTAL_BYETS_LIMITS = #undef private #include +#include #include #include "butil/logging.h" #include "butil/memory/singleton_on_pthread_once.h" @@ -116,7 +117,7 @@ void ListProtocols(std::vector >* vec) { ProtocolEntry* const protocol_map = get_protocol_map(); for (size_t i = 0; i < MAX_PROTOCOL_SIZE; ++i) { if (protocol_map[i].valid.load(butil::memory_order_acquire)) { - vec->push_back(std::make_pair((ProtocolType)i, protocol_map[i].protocol)); + vec->emplace_back((ProtocolType)i, protocol_map[i].protocol); } } } @@ -139,7 +140,7 @@ void SerializeRequestDefault(butil::IOBuf* buf, } if (!SerializeAsCompressedData(*request, buf, cntl->request_compress_type())) { return cntl->SetFailed( - EREQUEST, "Fail to compress request, compress_tpye=%d", + EREQUEST, "Fail to compress request, compress_type=%d", (int)cntl->request_compress_type()); } } @@ -203,17 +204,32 @@ BUTIL_FORCE_INLINE bool ParsePbFromZeroCopyStreamInlined( // According to source code of pb, SetTotalBytesLimit is not a simple set, // avoid calling the function when the limit is definitely unreached. if (PB_TOTAL_BYETS_LIMITS < FLAGS_max_body_size) { +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + decoder.SetTotalBytesLimit(INT_MAX); +#else decoder.SetTotalBytesLimit(INT_MAX, -1); +#endif } return msg->ParseFromCodedStream(&decoder) && decoder.ConsumedEntireMessage(); } +BUTIL_FORCE_INLINE bool ParsePbTextFromZeroCopyStreamInlined( + google::protobuf::Message* msg, + google::protobuf::io::ZeroCopyInputStream* input) { + return google::protobuf::TextFormat::Parse(input, msg); +} + bool ParsePbFromZeroCopyStream( google::protobuf::Message* msg, google::protobuf::io::ZeroCopyInputStream* input) { return ParsePbFromZeroCopyStreamInlined(msg, input); } +bool ParsePbTextFromIOBuf(google::protobuf::Message* msg, const butil::IOBuf& buf) { + butil::IOBufAsZeroCopyInputStream stream(buf); + return ParsePbTextFromZeroCopyStreamInlined(msg, &stream); +} + bool ParsePbFromIOBuf(google::protobuf::Message* msg, const butil::IOBuf& buf) { butil::IOBufAsZeroCopyInputStream stream(buf); return ParsePbFromZeroCopyStreamInlined(msg, &stream); diff --git a/src/brpc/protocol.h b/src/brpc/protocol.h index c1e0602c..b988a396 100755 --- a/src/brpc/protocol.h +++ b/src/brpc/protocol.h @@ -193,6 +193,7 @@ void SerializeRequestDefault(butil::IOBuf* buf, bool ParsePbFromZeroCopyStream(google::protobuf::Message* msg, google::protobuf::io::ZeroCopyInputStream* input); bool ParsePbFromIOBuf(google::protobuf::Message* msg, const butil::IOBuf& buf); +bool ParsePbTextFromIOBuf(google::protobuf::Message* msg, const butil::IOBuf& buf); bool ParsePbFromArray(google::protobuf::Message* msg, const void* data, size_t size); bool ParsePbFromString(google::protobuf::Message* msg, const std::string& str); diff --git a/src/brpc/redis.cpp b/src/brpc/redis.cpp index 50cab086..c482c508 100644 --- a/src/brpc/redis.cpp +++ b/src/brpc/redis.cpp @@ -59,6 +59,12 @@ RedisRequest* RedisRequest::New() const { return new RedisRequest; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +RedisRequest* RedisRequest::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void RedisRequest::Clear() { _ncommand = 0; _has_error = false; @@ -271,6 +277,12 @@ RedisResponse* RedisResponse::New() const { return new RedisResponse; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +RedisResponse* RedisResponse::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void RedisResponse::Clear() { _first_reply.Reset(); _other_replies = NULL; diff --git a/src/brpc/redis.h b/src/brpc/redis.h index d90da9eb..dfaa25dc 100644 --- a/src/brpc/redis.h +++ b/src/brpc/redis.h @@ -108,6 +108,9 @@ public: // Protobuf methods. RedisRequest* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + RedisRequest* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const RedisRequest& from); @@ -178,6 +181,9 @@ public: // implements Message ---------------------------------------------- RedisResponse* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + RedisResponse* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const RedisResponse& from); diff --git a/src/brpc/rtmp.cpp b/src/brpc/rtmp.cpp index 3797972a..ae6eb6ad 100644 --- a/src/brpc/rtmp.cpp +++ b/src/brpc/rtmp.cpp @@ -60,18 +60,22 @@ int WriteWithoutOvercrowded(Socket*, SocketMessagePtr<>& msg); } FlvWriter::FlvWriter(butil::IOBuf* buf) - : _write_header(false), _buf(buf) { + : _write_header(false), _buf(buf), _options() { } -static char g_flv_header[9] = { 'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09 }; +FlvWriter::FlvWriter(butil::IOBuf* buf, const FlvWriterOptions& options) + : _write_header(false), _buf(buf), _options(options) { +} butil::Status FlvWriter::Write(const RtmpVideoMessage& msg) { char buf[32]; char* p = buf; if (!_write_header) { _write_header = true; - memcpy(p, g_flv_header, sizeof(g_flv_header)); - p += sizeof(g_flv_header); + const char flags_bit = static_cast(_options.flv_content_type); + const char header[9] = { 'F', 'L', 'V', 0x01, flags_bit, 0, 0, 0, 0x09 }; + memcpy(p, header, sizeof(header)); + p += sizeof(header); policy::WriteBigEndian4Bytes(&p, 0); // PreviousTagSize0 } // FLV tag @@ -96,8 +100,10 @@ butil::Status FlvWriter::Write(const RtmpAudioMessage& msg) { char* p = buf; if (!_write_header) { _write_header = true; - memcpy(p, g_flv_header, sizeof(g_flv_header)); - p += sizeof(g_flv_header); + const char flags_bit = static_cast(_options.flv_content_type); + const char header[9] = { 'F', 'L', 'V', 0x01, flags_bit, 0, 0, 0, 0x09 }; + memcpy(p, header, sizeof(header)); + p += sizeof(header); policy::WriteBigEndian4Bytes(&p, 0); // PreviousTagSize0 } // FLV tag @@ -125,8 +131,10 @@ butil::Status FlvWriter::WriteScriptData(const butil::IOBuf& req_buf, uint32_t t char* p = buf; if (!_write_header) { _write_header = true; - memcpy(p, g_flv_header, sizeof(g_flv_header)); - p += sizeof(g_flv_header); + const char flags_bit = static_cast(_options.flv_content_type); + const char header[9] = { 'F', 'L', 'V', 0x01, flags_bit, 0, 0, 0, 0x09 }; + memcpy(p, header, sizeof(header)); + p += sizeof(header); policy::WriteBigEndian4Bytes(&p, 0); // PreviousTagSize0 } // FLV tag @@ -179,12 +187,15 @@ FlvReader::FlvReader(butil::IOBuf* buf) butil::Status FlvReader::ReadHeader() { if (!_read_header) { - char header_buf[sizeof(g_flv_header) + 4/* PreviousTagSize0 */]; + // 9 is the size of FlvHeader, which is usually composed of + // { 'F', 'L', 'V', 0x01, 0x05, 0, 0, 0, 0x09 }. + char header_buf[9 + 4/* PreviousTagSize0 */]; const char* p = (const char*)_buf->fetch(header_buf, sizeof(header_buf)); if (p == NULL) { return butil::Status(EAGAIN, "Fail to read, not enough data"); } - if (memcmp(p, g_flv_header, 3) != 0) { + const char flv_header_signature[3] = { 'F', 'L', 'V' }; + if (memcmp(p, flv_header_signature, sizeof(flv_header_signature)) != 0) { LOG(FATAL) << "Fail to parse FLV header"; return butil::Status(EINVAL, "Fail to parse FLV header"); } diff --git a/src/brpc/rtmp.h b/src/brpc/rtmp.h index 90b47305..eb1b4180 100644 --- a/src/brpc/rtmp.h +++ b/src/brpc/rtmp.h @@ -376,6 +376,18 @@ struct RtmpCuePoint { AMFObject data; }; +enum class FlvHeaderFlags : uint8_t { + VIDEO = 0x01, + AUDIO = 0x04, + AUDIO_AND_VIDEO = 0x05, +}; + +struct FlvWriterOptions { + FlvWriterOptions() = default; + + FlvHeaderFlags flv_content_type = FlvHeaderFlags::AUDIO_AND_VIDEO; +}; + struct RtmpSharedObjectMessage { // Not implemented yet. }; @@ -390,6 +402,7 @@ class FlvWriter { public: // Start appending FLV tags into the buffer explicit FlvWriter(butil::IOBuf* buf); + explicit FlvWriter(butil::IOBuf* buf, const FlvWriterOptions& options); // Append a video/audio/metadata/cuepoint message into the output buffer. butil::Status Write(const RtmpVideoMessage&); @@ -403,6 +416,7 @@ private: private: bool _write_header; butil::IOBuf* _buf; + FlvWriterOptions _options; }; class FlvReader { diff --git a/src/brpc/selective_channel.cpp b/src/brpc/selective_channel.cpp index 66a916b1..89f52415 100644 --- a/src/brpc/selective_channel.cpp +++ b/src/brpc/selective_channel.cpp @@ -317,6 +317,7 @@ int Sender::IssueRPC(int64_t start_realtime_us) { // No need to count timeout. We already managed timeout in schan. If // timeout occurs, sub calls are canceled with ERPCTIMEDOUT. sub_cntl->_timeout_ms = -1; + sub_cntl->_real_timeout_ms = _main_cntl->timeout_ms(); // Inherit following fields of _main_cntl. // TODO(gejun): figure out a better way to maintain these fields. diff --git a/src/brpc/serialized_request.cpp b/src/brpc/serialized_request.cpp index 23beeb72..499738f6 100644 --- a/src/brpc/serialized_request.cpp +++ b/src/brpc/serialized_request.cpp @@ -53,6 +53,13 @@ SerializedRequest* SerializedRequest::New() const { return new SerializedRequest; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +SerializedRequest* +SerializedRequest::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void SerializedRequest::Clear() { _serialized.clear(); } diff --git a/src/brpc/serialized_request.h b/src/brpc/serialized_request.h index c59495db..2e30dde9 100644 --- a/src/brpc/serialized_request.h +++ b/src/brpc/serialized_request.h @@ -44,6 +44,9 @@ public: // implements Message ---------------------------------------------- SerializedRequest* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + SerializedRequest* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void CopyFrom(const SerializedRequest& from); void Clear(); diff --git a/src/brpc/server.cpp b/src/brpc/server.cpp index 2fb3c00b..f7b338a3 100644 --- a/src/brpc/server.cpp +++ b/src/brpc/server.cpp @@ -283,6 +283,8 @@ void* Server::UpdateDerivedVars(void* arg) { server->_nerror_bvar.expose_as(prefix, "error"); + server->_eps_bvar.expose_as(prefix, "eps"); + bvar::PassiveStatus uptime_st( prefix, "uptime", GetUptime, (void*)(intptr_t)start_us); @@ -391,6 +393,7 @@ Server::Server(ProfilerLinker) , _last_start_time(0) , _derivative_thread(INVALID_BTHREAD) , _keytable_pool(NULL) + , _eps_bvar(&_nerror_bvar) , _concurrency(0) { BAIDU_CASSERT(offsetof(Server, _concurrency) % 64 == 0, Server_concurrency_must_be_aligned_by_cacheline); @@ -699,7 +702,7 @@ static bool CreateConcurrencyLimiter(const AdaptiveMaxConcurrency& amc, static AdaptiveMaxConcurrency g_default_max_concurrency_of_method(0); -int Server::StartInternal(const butil::ip_t& ip, +int Server::StartInternal(const butil::EndPoint& endpoint, const PortRange& port_range, const ServerOptions *opt) { std::unique_ptr revert_server(this); @@ -940,7 +943,12 @@ int Server::StartInternal(const butil::ip_t& ip, << port_range.max_port << ']'; return -1; } - _listen_addr.ip = ip; + if (butil::is_endpoint_extended(endpoint) && + (port_range.min_port != endpoint.port || port_range.max_port != endpoint.port)) { + LOG(ERROR) << "Only IPv4 address supports port range feature"; + return -1; + } + _listen_addr = endpoint; for (int port = port_range.min_port; port <= port_range.max_port; ++port) { _listen_addr.port = port; butil::fd_guard sockfd(tcp_listen(_listen_addr)); @@ -949,7 +957,7 @@ int Server::StartInternal(const butil::ip_t& ip, continue; } if (port_range.min_port != port_range.max_port) { - LOG(ERROR) << "Fail to listen " << ip + LOG(ERROR) << "Fail to listen " << _listen_addr.ip << ":[" << port_range.min_port << '-' << port_range.max_port << ']'; } else { @@ -1001,6 +1009,11 @@ int Server::StartInternal(const butil::ip_t& ip, " against the purpose of \"being internal\"."; return -1; } + if (butil::is_endpoint_extended(endpoint)) { + LOG(ERROR) << "internal_port is available in IPv4 address only"; + return -1; + } + butil::EndPoint internal_point = _listen_addr; internal_point.port = _options.internal_port; butil::fd_guard sockfd(tcp_listen(internal_point)); @@ -1035,32 +1048,39 @@ int Server::StartInternal(const butil::ip_t& ip, } // Print tips to server launcher. - int http_port = _listen_addr.port; - std::ostringstream server_info; - server_info << "Server[" << version() << "] is serving on port=" - << _listen_addr.port; - if (_options.internal_port >= 0 && _options.has_builtin_services) { - http_port = _options.internal_port; - server_info << " and internal_port=" << _options.internal_port; - } - LOG(INFO) << server_info.str() << '.'; - - if (_options.has_builtin_services) { - LOG(INFO) << "Check out http://" << butil::my_hostname() << ':' - << http_port << " in web browser."; + if (butil::is_endpoint_extended(_listen_addr)) { + const char* builtin_msg = _options.has_builtin_services ? " with builtin service" : ""; + LOG(INFO) << "Server[" << version() << "] is serving on " << _listen_addr + << builtin_msg << '.'; + //TODO add TrackMe support } else { - LOG(WARNING) << "Builtin services are disabled according to " - "ServerOptions.has_builtin_services"; + int http_port = _listen_addr.port; + std::ostringstream server_info; + server_info << "Server[" << version() << "] is serving on port=" + << _listen_addr.port; + if (_options.internal_port >= 0 && _options.has_builtin_services) { + http_port = _options.internal_port; + server_info << " and internal_port=" << _options.internal_port; + } + LOG(INFO) << server_info.str() << '.'; + + if (_options.has_builtin_services) { + LOG(INFO) << "Check out http://" << butil::my_hostname() << ':' + << http_port << " in web browser."; + } else { + LOG(WARNING) << "Builtin services are disabled according to " + "ServerOptions.has_builtin_services"; + } + // For trackme reporting + SetTrackMeAddress(butil::EndPoint(butil::my_ip(), http_port)); } - // For trackme reporting - SetTrackMeAddress(butil::EndPoint(butil::my_ip(), http_port)); revert_server.release(); return 0; } int Server::Start(const butil::EndPoint& endpoint, const ServerOptions* opt) { return StartInternal( - endpoint.ip, PortRange(endpoint.port, endpoint.port), opt); + endpoint, PortRange(endpoint.port, endpoint.port), opt); } int Server::Start(const char* ip_port_str, const ServerOptions* opt) { @@ -1089,7 +1109,11 @@ int Server::Start(const char* ip_str, PortRange port_range, LOG(ERROR) << "Invalid address=`" << ip_str << '\''; return -1; } - return StartInternal(ip, port_range, opt); + return StartInternal(butil::EndPoint(ip, 0), port_range, opt); +} + +int Server::Start(PortRange port_range, const ServerOptions* opt) { + return StartInternal(butil::EndPoint(butil::IP_ANY, 0), port_range, opt); } int Server::Stop(int timeout_ms) { diff --git a/src/brpc/server.h b/src/brpc/server.h index 47bb8547..2d571392 100644 --- a/src/brpc/server.h +++ b/src/brpc/server.h @@ -389,6 +389,8 @@ public: int Start(int port, const ServerOptions* opt); // Start on `ip_str' + any useable port in `range' int Start(const char* ip_str, PortRange range, const ServerOptions *opt); + // Start on IP_ANY + first useable port in `range' + int Start(PortRange range, const ServerOptions* opt); // NOTE: Stop() is paired with Join() to stop a server without losing // requests. The point of separating them is that you can Stop() multiple @@ -559,7 +561,7 @@ friend class Controller; // Create acceptor with handlers of protocols. Acceptor* BuildAcceptor(); - int StartInternal(const butil::ip_t& ip, + int StartInternal(const butil::EndPoint& endpoint, const PortRange& port_range, const ServerOptions *opt); @@ -679,6 +681,7 @@ friend class Controller; // mutable is required for `ServerPrivateAccessor' to change this bvar mutable bvar::Adder _nerror_bvar; + mutable bvar::PerSecond > _eps_bvar; mutable int32_t BAIDU_CACHELINE_ALIGNMENT _concurrency; }; diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp index 36214e69..59a570ac 100644 --- a/src/brpc/socket.cpp +++ b/src/brpc/socket.cpp @@ -1115,7 +1115,13 @@ int Socket::Connect(const timespec* abstime, } else { _ssl_state = SSL_OFF; } - butil::fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0)); + struct sockaddr_storage serv_addr; + socklen_t addr_size = 0; + if (butil::endpoint2sockaddr(remote_side(), &serv_addr, &addr_size) != 0) { + PLOG(ERROR) << "Fail to get sockaddr"; + return -1; + } + butil::fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0)); if (sockfd < 0) { PLOG(ERROR) << "Fail to create socket"; return -1; @@ -1124,13 +1130,8 @@ int Socket::Connect(const timespec* abstime, // We need to do async connect (to manage the timeout by ourselves). CHECK_EQ(0, butil::make_non_blocking(sockfd)); - struct sockaddr_in serv_addr; - bzero((char*)&serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr = remote_side().ip; - serv_addr.sin_port = htons(remote_side().port); const int rc = ::connect( - sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + sockfd, (struct sockaddr*)&serv_addr, addr_size); if (rc != 0 && errno != EINPROGRESS) { PLOG(WARNING) << "Fail to connect to " << remote_side(); return -1; @@ -1217,13 +1218,12 @@ int Socket::CheckConnected(int sockfd) { return -1; } - struct sockaddr_in client; - socklen_t size = sizeof(client); - CHECK_EQ(0, getsockname(sockfd, (struct sockaddr*) &client, &size)); + butil::EndPoint local_point; + CHECK_EQ(0, butil::get_local_side(sockfd, &local_point)); LOG_IF(INFO, FLAGS_log_connected) << "Connected to " << remote_side() << " via fd=" << (int)sockfd << " SocketId=" << id() - << " local_port=" << ntohs(client.sin_port); + << " local_side=" << local_point; if (CreatedByConnect()) { g_vars->channel_conn << 1; } diff --git a/src/brpc/socket_map.h b/src/brpc/socket_map.h index 7a91b345..3e6695ba 100644 --- a/src/brpc/socket_map.h +++ b/src/brpc/socket_map.h @@ -119,7 +119,7 @@ struct SocketMapOptions { // Initial size of the map (proper size reduces number of resizes) // Default: 1024 size_t suggested_map_size; - + // Pooled connections without data transmission for so many seconds will // be closed. No effect for non-positive values. // If idle_timeout_second_dynamic is not NULL, use the dereferenced value diff --git a/src/brpc/stream.cpp b/src/brpc/stream.cpp index e48441e8..67b3541a 100644 --- a/src/brpc/stream.cpp +++ b/src/brpc/stream.cpp @@ -435,11 +435,11 @@ int Stream::OnReceived(const StreamFrameMeta& fm, butil::IOBuf *buf, Socket* soc } break; case FRAME_TYPE_RST: - RPC_VLOG << "stream=" << id() << " recevied rst frame"; + RPC_VLOG << "stream=" << id() << " received rst frame"; Close(); break; case FRAME_TYPE_CLOSE: - RPC_VLOG << "stream=" << id() << " recevied close frame"; + RPC_VLOG << "stream=" << id() << " received close frame"; // TODO:: See the comments in Consume Close(); break; diff --git a/src/brpc/thrift_message.cpp b/src/brpc/thrift_message.cpp index c3be85a5..77e812b5 100644 --- a/src/brpc/thrift_message.cpp +++ b/src/brpc/thrift_message.cpp @@ -59,6 +59,13 @@ ThriftFramedMessage* ThriftFramedMessage::New() const { return new ThriftFramedMessage; } +#if GOOGLE_PROTOBUF_VERSION >= 3006000 +ThriftFramedMessage* +ThriftFramedMessage::New(::google::protobuf::Arena* arena) const { + return CreateMaybeMessage(arena); +} +#endif + void ThriftFramedMessage::Clear() { body.clear(); if (_own_raw_instance) { diff --git a/src/brpc/thrift_message.h b/src/brpc/thrift_message.h index a152b164..8d39548d 100644 --- a/src/brpc/thrift_message.h +++ b/src/brpc/thrift_message.h @@ -84,6 +84,9 @@ public: // implements Message ---------------------------------------------- ThriftFramedMessage* New() const; +#if GOOGLE_PROTOBUF_VERSION >= 3006000 + ThriftFramedMessage* New(::google::protobuf::Arena* arena) const override; +#endif void CopyFrom(const ::google::protobuf::Message& from); void MergeFrom(const ::google::protobuf::Message& from); void CopyFrom(const ThriftFramedMessage& from); diff --git a/src/brpc/uri.cpp b/src/brpc/uri.cpp index 15ca9e95..f5d544c1 100644 --- a/src/brpc/uri.cpp +++ b/src/brpc/uri.cpp @@ -16,6 +16,10 @@ // under the License. +#include // isalnum + +#include + #include "brpc/log.h" #include "brpc/details/http_parser.h" // http_parser_parse_url #include "brpc/uri.h" // URI @@ -98,6 +102,21 @@ inline const char* SplitHostAndPort(const char* host_begin, return host_end; } +// valid characters in URL +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.1 +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 +// https://datatracker.ietf.org/doc/html/rfc3986#section-2.4 +// space is not allowed by rfc3986, but allowed by brpc +static bool is_valid_char(char c) { + static const std::unordered_set other_valid_char = { + ':', '/', '?', '#', '[', ']', '@', '!', '$', '&', + '\'', '(', ')', '*', '+', ',', ';', '=', '-', '.', + '_', '~', '%', ' ' + }; + + return (isalnum(c) || other_valid_char.count(c)); +} + static bool is_all_spaces(const char* p) { for (; *p == ' '; ++p) {} return !*p; @@ -159,7 +178,10 @@ int URI::SetHttpURL(const char* url) { if (action == URI_PARSE_BREAK) { break; } - if (*p == ':') { + if (!is_valid_char(*p)) { + _st.set_error(EINVAL, "invalid character in url"); + return -1; + } else if (*p == ':') { if (p[1] == '/' && p[2] == '/' && need_scheme) { need_scheme = false; _scheme.assign(start, p - start); diff --git a/src/bthread/task_group.cpp b/src/bthread/task_group.cpp index 7c41653e..9f6d42fb 100644 --- a/src/bthread/task_group.cpp +++ b/src/bthread/task_group.cpp @@ -379,6 +379,9 @@ int TaskGroup::start_foreground(TaskGroup** pg, CHECK(m->stack == NULL); m->attr = using_attr; m->local_storage = LOCAL_STORAGE_INIT; + if (using_attr.flags & BTHREAD_INHERIT_SPAN) { + m->local_storage.rpcz_parent_span = tls_bls.rpcz_parent_span; + } m->cpuwide_start_ns = start_ns; m->stat = EMPTY_STAT; m->tid = make_tid(*m->version_butex, slot); @@ -434,6 +437,9 @@ int TaskGroup::start_background(bthread_t* __restrict th, CHECK(m->stack == NULL); m->attr = using_attr; m->local_storage = LOCAL_STORAGE_INIT; + if (using_attr.flags & BTHREAD_INHERIT_SPAN) { + m->local_storage.rpcz_parent_span = tls_bls.rpcz_parent_span; + } m->cpuwide_start_ns = start_ns; m->stat = EMPTY_STAT; m->tid = make_tid(*m->version_butex, slot); diff --git a/src/bthread/types.h b/src/bthread/types.h index 45bf4be1..d56368f1 100644 --- a/src/bthread/types.h +++ b/src/bthread/types.h @@ -46,6 +46,7 @@ static const bthread_attrflags_t BTHREAD_LOG_START_AND_FINISH = 8; static const bthread_attrflags_t BTHREAD_LOG_CONTEXT_SWITCH = 16; static const bthread_attrflags_t BTHREAD_NOSIGNAL = 32; static const bthread_attrflags_t BTHREAD_NEVER_QUIT = 64; +static const bthread_attrflags_t BTHREAD_INHERIT_SPAN = 128; // Key of thread-local data, created by bthread_key_create. typedef struct { diff --git a/src/butil/containers/flat_map.h b/src/butil/containers/flat_map.h index 27777e42..b22ad78f 100644 --- a/src/butil/containers/flat_map.h +++ b/src/butil/containers/flat_map.h @@ -167,6 +167,11 @@ public: // Returns address of the inserted value, NULL on error. mapped_type* insert(const key_type& key, const mapped_type& value); + // Insert a pair of {key, value}. If size()*100/bucket_count() is + // more than load_factor(), a resize() will be done. + // Returns address of the inserted value, NULL on error. + mapped_type* insert(const std::pair& kv); + // Remove |key| and the associated value // Returns: 1 on erased, 0 otherwise. // Remove all items. Allocated spaces are NOT returned by system. diff --git a/src/butil/containers/flat_map_inl.h b/src/butil/containers/flat_map_inl.h index ef08d7bd..e5948920 100644 --- a/src/butil/containers/flat_map_inl.h +++ b/src/butil/containers/flat_map_inl.h @@ -375,6 +375,11 @@ _T* FlatMap<_K, _T, _H, _E, _S>::insert(const key_type& key, return p; } +template +_T* FlatMap<_K, _T, _H, _E, _S>::insert(const std::pair& kv) { + return insert(kv.first, kv.second); +} + template template size_t FlatMap<_K, _T, _H, _E, _S>::erase(const K2& key, _T* old_value) { diff --git a/src/butil/debug/crash_logging.cc b/src/butil/debug/crash_logging.cc index 511dc244..d17e9c0e 100644 --- a/src/butil/debug/crash_logging.cc +++ b/src/butil/debug/crash_logging.cc @@ -151,7 +151,7 @@ size_t InitCrashKeys(const CrashKey* const keys, size_t count, size_t total_keys = 0; for (size_t i = 0; i < count; ++i) { - g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i])); + g_crash_keys_->emplace(keys[i].key_name, keys[i]); total_keys += NumChunksForLength(keys[i].max_length); DCHECK_LT(keys[i].max_length, kLargestValueAllowed); } diff --git a/src/butil/debug/stack_trace_posix.cc b/src/butil/debug/stack_trace_posix.cc index 3d1e169b..3d19d90f 100644 --- a/src/butil/debug/stack_trace_posix.cc +++ b/src/butil/debug/stack_trace_posix.cc @@ -644,7 +644,7 @@ class SandboxSymbolizeHelper { if (modules_.find(region.path) == modules_.end()) { int fd = open(region.path.c_str(), O_RDONLY | O_CLOEXEC); if (fd >= 0) { - modules_.insert(std::make_pair(region.path, fd)); + modules_.emplace(region.path, fd); } else { LOG(WARNING) << "Failed to open file: " << region.path << "\n Error: " << strerror(errno); diff --git a/src/butil/details/extended_endpoint.hpp b/src/butil/details/extended_endpoint.hpp new file mode 100644 index 00000000..e969d9d7 --- /dev/null +++ b/src/butil/details/extended_endpoint.hpp @@ -0,0 +1,377 @@ +// 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 BUTIL_DETAILS_EXTENDED_ENDPOINT_H +#define BUTIL_DETAILS_EXTENDED_ENDPOINT_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "butil/endpoint.h" +#include "butil/logging.h" +#include "butil/strings/string_piece.h" +#include "butil/resource_pool.h" +#include "butil/memory/singleton_on_pthread_once.h" + +namespace butil { +namespace details { + +#if __cplusplus >= 201103L +static_assert(sizeof(EndPoint) == sizeof(EndPoint::ip) + sizeof(EndPoint::port), + "EndPoint size mismatch with the one in POD-style, may cause ABI problem"); +#endif + +// For ipv6/unix socket address. +// +// We have to keep butil::EndPoint ABI compatible because it is used so widely, and the size of butil::EndPoint is +// too small to store more information such as ipv6 address. +// We store enough information about endpoint in such tiny struct by putting real things in another big object +// holding by ResourcePool. The EndPoint::ip saves ResourceId, while EndPoint::port denotes if the EndPoint object +// is an old style ipv4 endpoint. +// Note that since ResourcePool has been implemented in bthread, we copy it into this repo and change its namespace to +// butil::details. Those two headers will not be published. + +// If EndPoint.port equals to this value, we should get the extended endpoint in resource pool. +const static int EXTENDED_ENDPOINT_PORT = 123456789; + +class ExtendedEndPoint; + +// A global unordered set to dedup ExtendedEndPoint +// ExtendedEndPoints which have same ipv6/unix socket address must have same id, +// so that user can simply use the value of EndPoint for comparision. +class GlobalEndPointSet { +public: + ExtendedEndPoint* insert(ExtendedEndPoint* p); + + void erase(ExtendedEndPoint* p); + + static GlobalEndPointSet* instance() { + return ::butil::get_leaky_singleton(); + } + +private: + struct Hash { + size_t operator()(ExtendedEndPoint* const& p) const; + }; + + struct Equals { + bool operator()(ExtendedEndPoint* const& p1, ExtendedEndPoint* const& p2) const; + }; + + typedef std::unordered_set SetType; + SetType _set; + std::mutex _mutex; +}; + +class ExtendedEndPoint { +public: + // Construct ExtendedEndPoint. + // User should use create() functions to get ExtendedEndPoint instance. + ExtendedEndPoint(void) { + _ref_count.store(0, butil::memory_order_relaxed); + _u.sa.sa_family = AF_UNSPEC; + } + +public: + // Create ExtendedEndPoint. + // If creation is successful, create()s will embed the ExtendedEndPoint instance in the given EndPoint*, + // and return it as well. Or else, the given EndPoint* won't be touched. + // + // The format of the parameter is inspired by nginx. + // Valid forms are: + // - ipv6 + // without port: [2400:da00::3b0b] + // with port: [2400:da00::3b0b]:8080 + // - unix domain socket + // abslute path : unix:/path/to/file.sock + // relative path: unix:path/to/file.sock + + static ExtendedEndPoint* create(StringPiece sp, EndPoint* ep) { + sp.trim_spaces(); + if (sp.empty()) { + return NULL; + } + if (sp[0] == '[') { + size_t colon_pos = sp.find(']'); + if (colon_pos == StringPiece::npos || colon_pos == 1 /* [] is invalid */ || ++colon_pos >= sp.size()) { + return NULL; + } + StringPiece port_sp = sp.substr(colon_pos); + if (port_sp.size() < 2 /* colon and at least one integer */ || port_sp[0] != ':') { + return NULL; + } + port_sp.remove_prefix(1); // remove `:' + if (port_sp.size() > 5) { // max 65535 + return NULL; + } + char buf[6]; + buf[port_sp.copy(buf, port_sp.size())] = '\0'; + char* end = NULL; + int port = ::strtol(buf, &end, 10 /* base */); + if (end != buf + port_sp.size()) { + return NULL; + } + return create(sp.substr(0, colon_pos), port, ep); + } else if (sp.starts_with("unix:")) { + return create(sp, EXTENDED_ENDPOINT_PORT, ep); + } + return NULL; + } + + static ExtendedEndPoint* create(StringPiece sp, int port, EndPoint* ep) { + sp.trim_spaces(); + if (sp.empty()) { + return NULL; + } + ExtendedEndPoint* eep = NULL; + if (sp[0] == '[' && port >= 0 && port <= 65535) { + if (sp.back() != ']' || sp.size() == 2 || sp.size() - 2 >= INET6_ADDRSTRLEN) { + return NULL; + } + char buf[INET6_ADDRSTRLEN]; + buf[sp.copy(buf, sp.size() - 2 /* skip `[' and `]' */, 1 /* skip `[' */)] = '\0'; + + in6_addr addr; + if (inet_pton(AF_INET6, buf, &addr) != 1 /* succ */) { + return NULL; + } + + eep = new_extended_endpoint(AF_INET6); + if (eep) { + eep->_u.in6.sin6_addr = addr; + eep->_u.in6.sin6_port = htons(port); + eep->_u.in6.sin6_flowinfo = 0u; + eep->_u.in6.sin6_scope_id = 0u; + eep->_socklen = sizeof(_u.in6); + } + } else if (sp.starts_with("unix:")) { // ignore port + sp.remove_prefix(5); // remove `unix:' + if (sp.empty() || sp.size() >= UDS_PATH_SIZE) { + return NULL; + } + eep = new_extended_endpoint(AF_UNIX); + if (eep) { + int size = sp.copy(eep->_u.un.sun_path, sp.size()); + eep->_u.un.sun_path[size] = '\0'; + eep->_socklen = offsetof(sockaddr_un, sun_path) + size + 1; + } + } + if (eep) { + eep = dedup(eep); + eep->embed_to(ep); + } + return eep; + } + + static ExtendedEndPoint* create(sockaddr_storage* ss, socklen_t size, EndPoint* ep) { + ExtendedEndPoint* eep = NULL; + if (ss->ss_family == AF_INET6 || ss->ss_family == AF_UNIX) { + eep = new_extended_endpoint(ss->ss_family); + } + if (eep) { + memcpy(&eep->_u.ss, ss, size); + eep->_socklen = size; + if (ss->ss_family == AF_UNIX && size == offsetof(sockaddr_un, sun_path)) { + // See unix(7): When the address of an unnamed socket is returned, + // its length is sizeof(sa_family_t), and sun_path should not be inspected. + eep->_u.un.sun_path[0] = '\0'; + } + eep = dedup(eep); + eep->embed_to(ep); + } + return eep; + } + + // Get ExtendedEndPoint instance from EndPoint + static ExtendedEndPoint* address(const EndPoint& ep) { + if (!is_extended(ep)) { + return NULL; + } + ::butil::ResourceId id; + id.value = ep.ip.s_addr; + ExtendedEndPoint* eep = ::butil::address_resource(id); + CHECK(eep) << "fail to address ExtendedEndPoint from EndPoint"; + return eep; + } + + // Check if an EndPoint has embedded ExtendedEndPoint + static bool is_extended(const butil::EndPoint& ep) { + return ep.port == EXTENDED_ENDPOINT_PORT; + } + +private: + friend class GlobalEndPointSet; + + static GlobalEndPointSet* global_set() { + return GlobalEndPointSet::instance(); + } + + static ExtendedEndPoint* new_extended_endpoint(sa_family_t family) { + ::butil::ResourceId id; + ExtendedEndPoint* eep = ::butil::get_resource(&id); + if (eep) { + int64_t old_ref = eep->_ref_count.load(butil::memory_order_relaxed); + CHECK(old_ref == 0) << "new ExtendedEndPoint has reference " << old_ref; + CHECK(eep->_u.sa.sa_family == AF_UNSPEC) << "new ExtendedEndPoint has family " << eep->_u.sa.sa_family << " set"; + eep->_ref_count.store(1, butil::memory_order_relaxed); + eep->_id = id; + eep->_u.sa.sa_family = family; + } + return eep; + } + + void embed_to(EndPoint* ep) const { + CHECK(0 == _id.value >> 32) << "ResourceId beyond index"; + ep->reset(); + ep->ip = ip_t{static_cast(_id.value)}; + ep->port = EXTENDED_ENDPOINT_PORT; + } + + static ExtendedEndPoint* dedup(ExtendedEndPoint* eep) { + eep->_hash = std::hash()(std::string((const char*)&eep->_u, eep->_socklen)); + + ExtendedEndPoint* first_eep = global_set()->insert(eep); + if (first_eep != eep) { + eep->_ref_count.store(0, butil::memory_order_relaxed); + eep->_u.sa.sa_family = AF_UNSPEC; + ::butil::return_resource(eep->_id); + } + return first_eep; + } + +public: + + void dec_ref(void) { + int64_t old_ref = _ref_count.fetch_sub(1, butil::memory_order_relaxed); + CHECK(old_ref >= 1) << "ExtendedEndPoint has unexpected reference " << old_ref; + if (old_ref == 1) { + global_set()->erase(this); + _u.sa.sa_family = AF_UNSPEC; + ::butil::return_resource(_id); + } + } + + void inc_ref(void) { + int64_t old_ref = _ref_count.fetch_add(1, butil::memory_order_relaxed); + CHECK(old_ref >= 1) << "ExtendedEndPoint has unexpected reference " << old_ref; + } + + sa_family_t family(void) const { + return _u.sa.sa_family; + } + + int to(sockaddr_storage* ss) const { + memcpy(ss, &_u.ss, _socklen); + return _socklen; + } + + void to(EndPointStr* ep_str) const { + if (_u.sa.sa_family == AF_UNIX) { + snprintf(ep_str->_buf, sizeof(ep_str->_buf), "unix:%s", _u.un.sun_path); + } else if (_u.sa.sa_family == AF_INET6) { + char buf[INET6_ADDRSTRLEN] = {0}; + const char* ret = inet_ntop(_u.sa.sa_family, &_u.in6.sin6_addr, buf, sizeof(buf)); + CHECK(ret) << "fail to do inet_ntop"; + snprintf(ep_str->_buf, sizeof(ep_str->_buf), "[%s]:%d", buf, ntohs(_u.in6.sin6_port)); + } else { + CHECK(0) << "family " << _u.sa.sa_family << " not supported"; + } + } + + int to_hostname(char* host, size_t host_len) const { + if (_u.sa.sa_family == AF_UNIX) { + snprintf(host, host_len, "unix:%s", _u.un.sun_path); + return 0; + } else if (_u.sa.sa_family == AF_INET6) { + sockaddr_in6 sa = _u.in6; + if (getnameinfo((const sockaddr*) &sa, sizeof(sa), host, host_len, NULL, 0, NI_NAMEREQD) != 0) { + return -1; + } + size_t len = ::strlen(host); + if (len + 1 < host_len) { + snprintf(host + len, host_len - len, ":%d", _u.in6.sin6_port); + } + return 0; + } else { + CHECK(0) << "family " << _u.sa.sa_family << " not supported"; + return -1; + } + } + +private: + static const size_t UDS_PATH_SIZE = sizeof(sockaddr_un::sun_path); + + butil::atomic _ref_count; + butil::ResourceId _id; + size_t _hash; // pre-compute hash code of sockaddr for saving unordered_set query time + socklen_t _socklen; // valid data length of sockaddr + union { + sockaddr sa; + sockaddr_in6 in6; + sockaddr_un un; + sockaddr_storage ss; + } _u; +}; + +inline ExtendedEndPoint* GlobalEndPointSet::insert(ExtendedEndPoint* p) { + std::unique_lock lock(_mutex); + auto it = _set.find(p); + if (it != _set.end()) { + if ((*it)->_ref_count.fetch_add(1, butil::memory_order_relaxed) == 0) { + // another thread is calling dec_ref(), do not reuse it + (*it)->_ref_count.fetch_sub(1, butil::memory_order_relaxed); + _set.erase(it); + _set.insert(p); + return p; + } else { + // the ExtendedEndPoint is valid, reuse it + return *it; + } + } + _set.insert(p); + return p; +} + +inline void GlobalEndPointSet::erase(ExtendedEndPoint* p) { + std::unique_lock lock(_mutex); + auto it = _set.find(p); + if (it == _set.end() || *it != p) { + // another thread has been erase it + return; + } + _set.erase(it); +} + +inline size_t GlobalEndPointSet::Hash::operator()(ExtendedEndPoint* const& p) const { + return p->_hash; +} + +inline bool GlobalEndPointSet::Equals::operator()(ExtendedEndPoint* const& p1, ExtendedEndPoint* const& p2) const { + return p1->_socklen == p2->_socklen + && memcmp(&p1->_u, &p2->_u, p1->_socklen) == 0; +} + +} // namespace details +} // namespace butil + +#endif // BUTIL_DETAILS_EXTENDED_ENDPOINT_H \ No newline at end of file diff --git a/src/butil/endpoint.cpp b/src/butil/endpoint.cpp index 43aa7a28..ba8e475c 100644 --- a/src/butil/endpoint.cpp +++ b/src/butil/endpoint.cpp @@ -25,6 +25,7 @@ #include // strcpy #include // snprintf #include // strtol +#include // sockaddr_un #include #include "butil/fd_guard.h" // fd_guard #include "butil/endpoint.h" // ip_t @@ -38,6 +39,8 @@ DEFINE_bool(reuse_port, false, "Enable SO_REUSEPORT for all listened sockets"); DEFINE_bool(reuse_addr, true, "Enable SO_REUSEADDR for all listened sockets"); +DEFINE_bool(reuse_uds_path, false, "remove unix domain socket file before listen to it"); + __BEGIN_DECLS int BAIDU_WEAK bthread_connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen) { @@ -45,8 +48,59 @@ int BAIDU_WEAK bthread_connect( } __END_DECLS +#include "details/extended_endpoint.hpp" + namespace butil { +using details::ExtendedEndPoint; + +static void set_endpoint(EndPoint* ep, ip_t ip, int port) { + ep->ip = ip; + ep->port = port; + if (ExtendedEndPoint::is_extended(*ep)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(*ep); + if (eep) { + eep->inc_ref(); + } else { + ep->ip = IP_ANY; + ep->port = 0; + } + } +} + +void EndPoint::reset(void) { + if (ExtendedEndPoint::is_extended(*this)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(*this); + if (eep) { + eep->dec_ref(); + } + } + ip = IP_ANY; + port = 0; +} + +EndPoint::EndPoint(ip_t ip2, int port2) : ip(ip2), port(port2) { + // Should never construct an extended endpoint by this way + if (ExtendedEndPoint::is_extended(*this)) { + CHECK(0) << "EndPoint construct with value that points to an extended EndPoint"; + ip = IP_ANY; + port = 0; + } +} + +EndPoint::EndPoint(const EndPoint& rhs) { + set_endpoint(this, rhs.ip, rhs.port); +} + +EndPoint::~EndPoint() { + reset(); +} + +void EndPoint::operator=(const EndPoint& rhs) { + reset(); + set_endpoint(this, rhs.ip, rhs.port); +} + int str2ip(const char* ip_str, ip_t* ip) { // ip_str can be NULL when called by EndPoint(0, ...) if (ip_str != NULL) { @@ -100,6 +154,15 @@ int ip2hostname(ip_t ip, std::string* host) { EndPointStr endpoint2str(const EndPoint& point) { EndPointStr str; + if (ExtendedEndPoint::is_extended(point)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(point); + if (eep) { + eep->to(&str); + } else { + str._buf[0] = '\0'; + } + return str; + } if (inet_ntop(AF_INET, &point.ip, str._buf, INET_ADDRSTRLEN) == NULL) { return endpoint2str(EndPoint(IP_NONE, 0)); } @@ -173,6 +236,10 @@ const char* my_hostname() { } int str2endpoint(const char* str, EndPoint* point) { + if (ExtendedEndPoint::create(str, point)) { + return 0; + } + // Should be enough to hold ip address char buf[64]; size_t i = 0; @@ -204,6 +271,10 @@ int str2endpoint(const char* str, EndPoint* point) { } int str2endpoint(const char* ip_str, int port, EndPoint* point) { + if (ExtendedEndPoint::create(ip_str, port, point)) { + return 0; + } + if (str2ip(ip_str, &point->ip) != 0) { return -1; } @@ -260,6 +331,14 @@ int hostname2endpoint(const char* name_str, int port, EndPoint* point) { } int endpoint2hostname(const EndPoint& point, char* host, size_t host_len) { + if (ExtendedEndPoint::is_extended(point)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(point); + if (eep) { + return eep->to_hostname(host, host_len); + } + return -1; + } + if (ip2hostname(point.ip, host, host_len) == 0) { size_t len = strlen(host); if (len + 1 < host_len) { @@ -271,7 +350,7 @@ int endpoint2hostname(const EndPoint& point, char* host, size_t host_len) { } int endpoint2hostname(const EndPoint& point, std::string* host) { - char buf[128]; + char buf[256]; if (endpoint2hostname(point, buf, sizeof(buf)) == 0) { host->assign(buf); return 0; @@ -280,21 +359,20 @@ int endpoint2hostname(const EndPoint& point, std::string* host) { } int tcp_connect(EndPoint point, int* self_port) { - fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0)); + struct sockaddr_storage serv_addr; + socklen_t serv_addr_size = 0; + if (endpoint2sockaddr(point, &serv_addr, &serv_addr_size) != 0) { + return -1; + } + fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0)); if (sockfd < 0) { return -1; } - struct sockaddr_in serv_addr; - bzero((char*)&serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr = point.ip; - serv_addr.sin_port = htons(point.port); int rc = 0; if (bthread_connect != NULL) { - rc = bthread_connect(sockfd, (struct sockaddr*)&serv_addr, - sizeof(serv_addr)); + rc = bthread_connect(sockfd, (struct sockaddr*) &serv_addr, serv_addr_size); } else { - rc = ::connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + rc = ::connect(sockfd, (struct sockaddr*) &serv_addr, serv_addr_size); } if (rc < 0) { return -1; @@ -311,7 +389,12 @@ int tcp_connect(EndPoint point, int* self_port) { } int tcp_listen(EndPoint point) { - fd_guard sockfd(socket(AF_INET, SOCK_STREAM, 0)); + struct sockaddr_storage serv_addr; + socklen_t serv_addr_size = 0; + if (endpoint2sockaddr(point, &serv_addr, &serv_addr_size) != 0) { + return -1; + } + fd_guard sockfd(socket(serv_addr.ss_family, SOCK_STREAM, 0)); if (sockfd < 0) { return -1; } @@ -342,12 +425,11 @@ int tcp_listen(EndPoint point) { #endif } - struct sockaddr_in serv_addr; - bzero((char*)&serv_addr, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr = point.ip; - serv_addr.sin_port = htons(point.port); - if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) != 0) { + if (FLAGS_reuse_uds_path && serv_addr.ss_family == AF_UNIX) { + ::unlink(((sockaddr_un*) &serv_addr)->sun_path); + } + + if (bind(sockfd, (struct sockaddr*)& serv_addr, serv_addr_size) != 0) { return -1; } if (listen(sockfd, 65535) != 0) { @@ -360,29 +442,82 @@ int tcp_listen(EndPoint point) { } int get_local_side(int fd, EndPoint *out) { - struct sockaddr addr; + struct sockaddr_storage addr; socklen_t socklen = sizeof(addr); - const int rc = getsockname(fd, &addr, &socklen); + const int rc = getsockname(fd, (struct sockaddr*)&addr, &socklen); if (rc != 0) { return rc; } if (out) { - *out = butil::EndPoint(*(sockaddr_in*)&addr); + return sockaddr2endpoint(&addr, socklen, out); } return 0; } int get_remote_side(int fd, EndPoint *out) { - struct sockaddr addr; + struct sockaddr_storage addr; + bzero(&addr, sizeof(addr)); socklen_t socklen = sizeof(addr); - const int rc = getpeername(fd, &addr, &socklen); + const int rc = getpeername(fd, (struct sockaddr*)&addr, &socklen); if (rc != 0) { return rc; } if (out) { - *out = butil::EndPoint(*(sockaddr_in*)&addr); + return sockaddr2endpoint(&addr, socklen, out); } return 0; } +int endpoint2sockaddr(const EndPoint& point, struct sockaddr_storage* ss, socklen_t* size) { + bzero(ss, sizeof(*ss)); + if (ExtendedEndPoint::is_extended(point)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(point); + if (!eep) { + return -1; + } + int ret = eep->to(ss); + if (ret < 0) { + return -1; + } + if (size) { + *size = static_cast(ret); + } + return 0; + } + struct sockaddr_in* in4 = (struct sockaddr_in*) ss; + in4->sin_family = AF_INET; + in4->sin_addr = point.ip; + in4->sin_port = htons(point.port); + if (size) { + *size = sizeof(*in4); + } + return 0; +} + +int sockaddr2endpoint(struct sockaddr_storage* ss, socklen_t size, EndPoint* point) { + if (ss->ss_family == AF_INET) { + *point = EndPoint(*(sockaddr_in*)ss); + return 0; + } + if (ExtendedEndPoint::create(ss, size, point)) { + return 0; + } + return -1; +} + +sa_family_t get_endpoint_type(const EndPoint& point) { + if (ExtendedEndPoint::is_extended(point)) { + ExtendedEndPoint* eep = ExtendedEndPoint::address(point); + if (eep) { + return eep->family(); + } + return AF_UNSPEC; + } + return AF_INET; +} + +bool is_endpoint_extended(const EndPoint& point) { + return ExtendedEndPoint::is_extended(point); +} + } // namespace butil diff --git a/src/butil/endpoint.h b/src/butil/endpoint.h index ea16b258..11831961 100644 --- a/src/butil/endpoint.h +++ b/src/butil/endpoint.h @@ -23,6 +23,7 @@ #define BUTIL_ENDPOINT_H #include // in_addr +#include // sockaddr_un #include // std::ostream #include "butil/containers/hash_tables.h" // hashing functions @@ -79,12 +80,20 @@ ip_t my_ip(); // String form. const char* my_ip_cstr(); -// ipv4 + port +// For IPv4 endpoint, ip and port are real things. +// For UDS/IPv6 endpoint, to keep ABI compatibility, ip is ResourceId, and port is a special flag. +// See str2endpoint implementation for details. struct EndPoint { EndPoint() : ip(IP_ANY), port(0) {} - EndPoint(ip_t ip2, int port2) : ip(ip2), port(port2) {} + EndPoint(ip_t ip2, int port2); explicit EndPoint(const sockaddr_in& in) : ip(in.sin_addr), port(ntohs(in.sin_port)) {} + + EndPoint(const EndPoint&); + ~EndPoint(); + void operator=(const EndPoint&); + + void reset(void); ip_t ip; int port; @@ -92,7 +101,7 @@ struct EndPoint { struct EndPointStr { const char* c_str() const { return _buf; } - char _buf[INET_ADDRSTRLEN + 16]; + char _buf[sizeof("unix:") + sizeof(sockaddr_un::sun_path)]; }; // Convert EndPoint to c-style string. Notice that you can serialize @@ -133,6 +142,18 @@ int get_local_side(int fd, EndPoint *out); // Get the other end of a socket connection int get_remote_side(int fd, EndPoint *out); +// Get sockaddr from endpoint, return -1 on failed +int endpoint2sockaddr(const EndPoint& point, struct sockaddr_storage* ss, socklen_t* size = NULL); + +// Create endpoint from sockaddr, return -1 on failed +int sockaddr2endpoint(struct sockaddr_storage* ss, socklen_t size, EndPoint* point); + +// Get EndPoint type (AF_INET/AF_INET6/AF_UNIX) +sa_family_t get_endpoint_type(const EndPoint& point); + +// Check if endpoint is extended. +bool is_endpoint_extended(const EndPoint& point); + } // namespace butil // Since ip_t is defined from in_addr which is globally defined, due to ADL @@ -185,7 +206,7 @@ inline bool operator!=(EndPoint p1, EndPoint p2) { } inline std::ostream& operator<<(std::ostream& os, const EndPoint& ep) { - return os << ep.ip << ':' << ep.port; + return os << endpoint2str(ep).c_str(); } inline std::ostream& operator<<(std::ostream& os, const EndPointStr& ep_str) { return os << ep_str.c_str(); diff --git a/src/butil/logging.cc b/src/butil/logging.cc index 6c047282..9db75cde 100644 --- a/src/butil/logging.cc +++ b/src/butil/logging.cc @@ -1291,7 +1291,7 @@ struct VModuleList { if (name.find_first_of("*?") == std::string::npos) { _exact_names[name] = verbose_level; } else { - _wild_names.push_back(std::make_pair(name, verbose_level)); + _wild_names.emplace_back(name, verbose_level); } } // Reverse _wild_names so that latter wild cards override former ones. diff --git a/src/butil/memory/ref_counted.h b/src/butil/memory/ref_counted.h index 82704d85..5f01fc1b 100644 --- a/src/butil/memory/ref_counted.h +++ b/src/butil/memory/ref_counted.h @@ -137,7 +137,7 @@ class RefCounted : public subtle::RefCountedBase { ~RefCounted() {} private: - DISALLOW_COPY_AND_ASSIGN(RefCounted); + DISALLOW_COPY_AND_ASSIGN(RefCounted); }; // Forward declaration. diff --git a/src/butil/posix/global_descriptors.cc b/src/butil/posix/global_descriptors.cc index 54874377..8896f5e9 100644 --- a/src/butil/posix/global_descriptors.cc +++ b/src/butil/posix/global_descriptors.cc @@ -46,7 +46,7 @@ void GlobalDescriptors::Set(Key key, int fd) { } } - descriptors_.push_back(std::make_pair(key, fd)); + descriptors_.emplace_back(key, fd); } void GlobalDescriptors::Reset(const Mapping& mapping) { diff --git a/src/butil/stl_util.h b/src/butil/stl_util.h index 19b638f4..1cc15d9d 100644 --- a/src/butil/stl_util.h +++ b/src/butil/stl_util.h @@ -172,8 +172,8 @@ void STLDeleteValues(T* container) { template class STLElementDeleter { public: - STLElementDeleter(T* container) : container_(container) {} - ~STLElementDeleter() { STLDeleteElements(container_); } + STLElementDeleter(T* container) : container_(container) {} + ~STLElementDeleter() { STLDeleteElements(container_); } private: T* container_; @@ -184,8 +184,8 @@ class STLElementDeleter { template class STLValueDeleter { public: - STLValueDeleter(T* container) : container_(container) {} - ~STLValueDeleter() { STLDeleteValues(container_); } + STLValueDeleter(T* container) : container_(container) {} + ~STLValueDeleter() { STLDeleteValues(container_); } private: T* container_; diff --git a/src/butil/strings/string_split.cc b/src/butil/strings/string_split.cc index fceb133e..23b1f89c 100644 --- a/src/butil/strings/string_split.cc +++ b/src/butil/strings/string_split.cc @@ -185,7 +185,7 @@ bool SplitStringIntoKeyValuePairsT(const STR& line, // value or key; just record that the split failed. success = false; } - key_value_pairs->push_back(std::make_pair(key, value)); + key_value_pairs->emplace_back(key, value); } return success; } diff --git a/src/butil/synchronization/waitable_event_posix.cc b/src/butil/synchronization/waitable_event_posix.cc index 5704ed6d..adeb5730 100644 --- a/src/butil/synchronization/waitable_event_posix.cc +++ b/src/butil/synchronization/waitable_event_posix.cc @@ -235,7 +235,7 @@ size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, std::vector > waitables; waitables.reserve(count); for (size_t i = 0; i < count; ++i) - waitables.push_back(std::make_pair(raw_waitables[i], i)); + waitables.emplace_back(raw_waitables[i], i); DCHECK_EQ(count, waitables.size()); diff --git a/src/butil/thread_local.cpp b/src/butil/thread_local.cpp index 4dd8c9d6..f10c5b14 100644 --- a/src/butil/thread_local.cpp +++ b/src/butil/thread_local.cpp @@ -47,7 +47,7 @@ public: if (_fns.capacity() < 16) { _fns.reserve(16); } - _fns.push_back(std::make_pair(fn, arg)); + _fns.emplace_back(fn, arg); } catch (...) { errno = ENOMEM; return -1; diff --git a/src/butil/threading/thread_local.h b/src/butil/threading/thread_local.h index a49e46c7..4eda9378 100644 --- a/src/butil/threading/thread_local.h +++ b/src/butil/threading/thread_local.h @@ -106,7 +106,7 @@ class ThreadLocalPointer { SlotType slot_; - DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer); + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer); }; class ThreadLocalBoolean { diff --git a/src/bvar/detail/sampler.cpp b/src/bvar/detail/sampler.cpp index 9ff91bf1..3a14b375 100644 --- a/src/bvar/detail/sampler.cpp +++ b/src/bvar/detail/sampler.cpp @@ -131,7 +131,7 @@ void SamplerCollector::run() { #ifndef UNIT_TEST // NOTE: // * Following vars can't be created on thread's stack since this thread - // may be adandoned at any time after forking. + // may be abandoned at any time after forking. // * They can't created inside the constructor of SamplerCollector as well, // which results in deadlock. if (s_cumulated_time_bvar == NULL) { diff --git a/src/bvar/latency_recorder.cpp b/src/bvar/latency_recorder.cpp index ed914224..c6532958 100644 --- a/src/bvar/latency_recorder.cpp +++ b/src/bvar/latency_recorder.cpp @@ -89,14 +89,23 @@ int CDF::describe_series( return 0; } +// Return random int value with expectation = `dval' +static int64_t double_to_random_int(double dval) { + int64_t ival = static_cast(dval); + if (dval > ival + butil::fast_rand_double()) { + ival += 1; + } + return ival; +} + static int64_t get_window_recorder_qps(void* arg) { detail::Sample s; - static_cast(arg)->get_span(1, &s); + static_cast(arg)->get_span(&s); // Use floating point to avoid overflow. if (s.time_us <= 0) { return 0; } - return static_cast(round(s.data.num * 1000000.0 / s.time_us)); + return double_to_random_int(s.data.num * 1000000.0 / s.time_us); } static int64_t get_recorder_count(void* arg) { @@ -176,7 +185,7 @@ int64_t LatencyRecorder::qps(time_t window_size) const { if (s.time_us <= 0) { return 0; } - return static_cast(round(s.data.num * 1000000.0 / s.time_us)); + return detail::double_to_random_int(s.data.num * 1000000.0 / s.time_us); } int LatencyRecorder::expose(const butil::StringPiece& prefix1, diff --git a/src/bvar/status.h b/src/bvar/status.h index bf46d09b..3798642b 100644 --- a/src/bvar/status.h +++ b/src/bvar/status.h @@ -27,6 +27,7 @@ #include "butil/synchronization/lock.h" #include "bvar/detail/is_atomical.h" #include "bvar/variable.h" +#include "bvar/reducer.h" namespace bvar { diff --git a/src/bvar/variable.cpp b/src/bvar/variable.cpp index 17eeadcf..d9f7273c 100644 --- a/src/bvar/variable.cpp +++ b/src/bvar/variable.cpp @@ -635,11 +635,11 @@ public: FileDumper *f = new FileDumper( path.AddExtension(key).AddExtension("data").value(), s); WildcardMatcher *m = new WildcardMatcher(value, '?', true); - dumpers.push_back(std::make_pair(f, m)); + dumpers.emplace_back(f, m); } - dumpers.push_back(std::make_pair( + dumpers.emplace_back( new FileDumper(path.AddExtension("data").value(), s), - (WildcardMatcher *)NULL)); + (WildcardMatcher *)NULL); } ~FileDumperGroup() { for (size_t i = 0; i < dumpers.size(); ++i) { diff --git a/test/Makefile b/test/Makefile index 9a63dc1a..a6e035f4 100644 --- a/test/Makefile +++ b/test/Makefile @@ -189,7 +189,7 @@ clean_bins: libbrpc.dbg.$(SOEXT):FORCE $(MAKE) -C.. test/libbrpc.dbg.$(SOEXT) -libbvar.dbg.a:FORCE +libbvar.dbg.a:libbrpc.dbg.$(SOEXT) FORCE $(MAKE) -C.. test/libbvar.dbg.a FORCE: diff --git a/test/brpc_channel_unittest.cpp b/test/brpc_channel_unittest.cpp index b64e1982..644e983b 100644 --- a/test/brpc_channel_unittest.cpp +++ b/test/brpc_channel_unittest.cpp @@ -1305,6 +1305,8 @@ protected: EXPECT_EQ(1, cntl.sub_count()); EXPECT_EQ(brpc::ERPCTIMEDOUT, cntl.sub(0)->ErrorCode()); EXPECT_LT(labs(tm.m_elapsed() - cntl.timeout_ms()), 15); + EXPECT_EQ(-1, cntl.sub(0)->_timeout_ms); + EXPECT_EQ(17, cntl.sub(0)->_real_timeout_ms); StopAndJoin(); } @@ -1958,6 +1960,82 @@ TEST_F(ChannelTest, init_using_naming_service) { // `lb' should be destroyed after } +TEST_F(ChannelTest, parse_hostname) { + brpc::ChannelOptions opt; + opt.succeed_without_server = false; + opt.protocol = brpc::PROTOCOL_HTTP; + brpc::Channel channel; + + ASSERT_EQ(-1, channel.Init("", 8888, &opt)); + ASSERT_EQ("", channel._service_name); + ASSERT_EQ(-1, channel.Init("", &opt)); + ASSERT_EQ("", channel._service_name); + + ASSERT_EQ(0, channel.Init("http://127.0.0.1", 8888, &opt)); + ASSERT_EQ("127.0.0.1:8888", channel._service_name); + ASSERT_EQ(0, channel.Init("http://127.0.0.1:8888", &opt)); + ASSERT_EQ("127.0.0.1:8888", channel._service_name); + + ASSERT_EQ(0, channel.Init("localhost", 8888, &opt)); + ASSERT_EQ("localhost:8888", channel._service_name); + ASSERT_EQ(0, channel.Init("localhost:8888", &opt)); + ASSERT_EQ("localhost:8888", channel._service_name); + + ASSERT_EQ(0, channel.Init("http://baidu.com", &opt)); + ASSERT_EQ("baidu.com", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com:80", &opt)); + ASSERT_EQ("baidu.com:80", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com", 80, &opt)); + ASSERT_EQ("baidu.com:80", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com:8888", &opt)); + ASSERT_EQ("baidu.com:8888", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com", 8888, &opt)); + ASSERT_EQ("baidu.com:8888", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com", "rr", &opt)); + ASSERT_EQ("baidu.com", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com:80", "rr", &opt)); + ASSERT_EQ("baidu.com:80", channel._service_name); + ASSERT_EQ(0, channel.Init("http://baidu.com:8888", "rr", &opt)); + ASSERT_EQ("baidu.com:8888", channel._service_name); + + ASSERT_EQ(0, channel.Init("https://baidu.com", &opt)); + ASSERT_EQ("baidu.com", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com:443", &opt)); + ASSERT_EQ("baidu.com:443", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com", 443, &opt)); + ASSERT_EQ("baidu.com:443", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com:1443", &opt)); + ASSERT_EQ("baidu.com:1443", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com", 1443, &opt)); + ASSERT_EQ("baidu.com:1443", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com", "rr", &opt)); + ASSERT_EQ("baidu.com", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com:443", "rr", &opt)); + ASSERT_EQ("baidu.com:443", channel._service_name); + ASSERT_EQ(0, channel.Init("https://baidu.com:1443", "rr", &opt)); + ASSERT_EQ("baidu.com:1443", channel._service_name); + + const char *address_list[] = { + "10.127.0.1:1234", + "10.128.0.1:1234 enable", + "10.129.0.1:1234", + "localhost:1234", + "baidu.com:1234" + }; + butil::TempFile tmp_file; + { + FILE* fp = fopen(tmp_file.fname(), "w"); + for (size_t i = 0; i < ARRAY_SIZE(address_list); ++i) { + ASSERT_TRUE(fprintf(fp, "%s\n", address_list[i])); + } + fclose(fp); + } + brpc::Channel ns_channel; + std::string ns = std::string("file://") + tmp_file.fname(); + ASSERT_EQ(0, ns_channel.Init(ns.c_str(), "rr", &opt)); + ASSERT_EQ(tmp_file.fname(), ns_channel._service_name); +} + TEST_F(ChannelTest, connection_failed) { for (int i = 0; i <= 1; ++i) { // Flag SingleServer for (int j = 0; j <= 1; ++j) { // Flag Asynchronous diff --git a/test/brpc_http_rpc_protocol_unittest.cpp b/test/brpc_http_rpc_protocol_unittest.cpp index 3452332f..a9b76f37 100644 --- a/test/brpc_http_rpc_protocol_unittest.cpp +++ b/test/brpc_http_rpc_protocol_unittest.cpp @@ -25,10 +25,12 @@ #include #include #include +#include #include "butil/time.h" #include "butil/macros.h" #include "butil/files/scoped_file.h" #include "butil/fd_guard.h" +#include "butil/file_util.h" #include "brpc/socket.h" #include "brpc/acceptor.h" #include "brpc/server.h" @@ -41,6 +43,15 @@ #include "json2pb/pb_to_json.h" #include "json2pb/json_to_pb.h" #include "brpc/details/method_status.h" +#include "brpc/rpc_dump.h" +#include "bvar/collector.h" + +namespace brpc { +DECLARE_bool(rpc_dump); +DECLARE_string(rpc_dump_dir); +DECLARE_int32(rpc_dump_max_requests_in_one_file); +extern bvar::CollectorSpeedLimit g_rpc_dump_sl; +} int main(int argc, char* argv[]) { testing::InitGoogleTest(&argc, argv); @@ -163,6 +174,20 @@ protected: return msg; } + brpc::policy::HttpContext* MakePostProtoTextRequestMessage( + const std::string& path) { + brpc::policy::HttpContext* msg = new brpc::policy::HttpContext(false); + msg->header().uri().set_path(path); + msg->header().set_content_type("application/proto-text"); + msg->header().set_method(brpc::HTTP_METHOD_POST); + + test::EchoRequest req; + req.set_message(EXP_REQUEST); + butil::IOBufAsZeroCopyOutputStream req_stream(&msg->body()); + EXPECT_TRUE(google::protobuf::TextFormat::Print(req, &req_stream)); + return msg; + } + brpc::policy::HttpContext* MakeGetRequestMessage(const std::string& path) { brpc::policy::HttpContext* msg = new brpc::policy::HttpContext(false); msg->header().uri().set_path(path); @@ -310,6 +335,12 @@ TEST_F(HttpTest, verify_request) { VerifyMessage(msg, false); msg->Destroy(); } + { + brpc::policy::HttpContext* msg = + MakePostProtoTextRequestMessage("/EchoService/Echo"); + VerifyMessage(msg, false); + msg->Destroy(); + } } TEST_F(HttpTest, process_request_failed_socket) { @@ -1480,4 +1511,115 @@ TEST_F(HttpTest, spring_protobuf_content_type) { ASSERT_EQ("application/x-protobuf", cntl.http_response().content_type()); } +TEST_F(HttpTest, dump_http_request) { + // save origin value of gflag + auto rpc_dump_dir = brpc::FLAGS_rpc_dump_dir; + auto rpc_dump_max_requests_in_one_file = brpc::FLAGS_rpc_dump_max_requests_in_one_file; + + // set gflag and global variable in order to be sure to dump request + brpc::FLAGS_rpc_dump = true; + brpc::FLAGS_rpc_dump_dir = "dump_http_request"; + brpc::FLAGS_rpc_dump_max_requests_in_one_file = 1; + brpc::g_rpc_dump_sl.ever_grabbed = true; + brpc::g_rpc_dump_sl.sampling_range = bvar::COLLECTOR_SAMPLING_BASE; + + // init channel + const int port = 8923; + brpc::Server server; + EXPECT_EQ(0, server.AddService(&_svc, brpc::SERVER_DOESNT_OWN_SERVICE)); + EXPECT_EQ(0, server.Start(port, nullptr)); + + brpc::Channel channel; + brpc::ChannelOptions options; + options.protocol = "http"; + ASSERT_EQ(0, channel.Init(butil::EndPoint(butil::my_ip(), port), &options)); + + // send request and dump it to file + { + test::EchoRequest req; + req.set_message(EXP_REQUEST); + std::string req_json; + ASSERT_TRUE(json2pb::ProtoMessageToJson(req, &req_json)); + + brpc::Controller cntl; + cntl.http_request().uri() = "/EchoService/Echo"; + cntl.http_request().set_content_type("application/json"); + cntl.http_request().set_method(brpc::HTTP_METHOD_POST); + cntl.request_attachment() = req_json; + channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr); + ASSERT_FALSE(cntl.Failed()); + + // sleep 1s, because rpc_dump doesn't run immediately + sleep(1); + } + + // replay request from dump file + { + brpc::SampleIterator it(brpc::FLAGS_rpc_dump_dir); + brpc::SampledRequest* sample = it.Next(); + ASSERT_NE(nullptr, sample); + + std::unique_ptr sample_guard(sample); + + // the logic of next code is same as that in rpc_replay.cpp + ASSERT_EQ(sample->meta.protocol_type(), brpc::PROTOCOL_HTTP); + brpc::Controller cntl; + cntl.reset_sampled_request(sample_guard.release()); + brpc::HttpMessage http_message; + http_message.ParseFromIOBuf(sample->request); + cntl.http_request().Swap(http_message.header()); + // clear origin Host in header + cntl.http_request().RemoveHeader("Host"); + cntl.http_request().uri().set_host(""); + cntl.request_attachment() = http_message.body().movable(); + + channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr); + ASSERT_FALSE(cntl.Failed()); + ASSERT_EQ("application/json", cntl.http_response().content_type()); + + std::string res_json = cntl.response_attachment().to_string(); + test::EchoResponse res; + json2pb::Json2PbOptions options; + ASSERT_TRUE(json2pb::JsonToProtoMessage(res_json, &res, options)); + ASSERT_EQ(EXP_RESPONSE, res.message()); + } + + // delete dump directory + butil::DeleteFile(butil::FilePath(brpc::FLAGS_rpc_dump_dir), true); + + // restore gflag and global variable + brpc::FLAGS_rpc_dump = false; + brpc::FLAGS_rpc_dump_dir = rpc_dump_dir; + brpc::FLAGS_rpc_dump_max_requests_in_one_file = rpc_dump_max_requests_in_one_file; + brpc::g_rpc_dump_sl.ever_grabbed = false; + brpc::g_rpc_dump_sl.sampling_range = 0; +} + +TEST_F(HttpTest, spring_protobuf_text_content_type) { + const int port = 8923; + brpc::Server server; + EXPECT_EQ(0, server.AddService(&_svc, brpc::SERVER_DOESNT_OWN_SERVICE)); + EXPECT_EQ(0, server.Start(port, nullptr)); + + brpc::Channel channel; + brpc::ChannelOptions options; + options.protocol = "http"; + ASSERT_EQ(0, channel.Init(butil::EndPoint(butil::my_ip(), port), &options)); + + brpc::Controller cntl; + test::EchoRequest req; + test::EchoResponse res; + req.set_message(EXP_REQUEST); + cntl.http_request().set_method(brpc::HTTP_METHOD_POST); + cntl.http_request().uri() = "/EchoService/Echo"; + cntl.http_request().set_content_type("application/proto-text"); + cntl.request_attachment().append(req.Utf8DebugString()); + channel.CallMethod(nullptr, &cntl, nullptr, nullptr, nullptr); + ASSERT_FALSE(cntl.Failed()); + ASSERT_EQ("application/proto-text", cntl.http_response().content_type()); + ASSERT_TRUE(google::protobuf::TextFormat::ParseFromString( + cntl.response_attachment().to_string(), &res)); + ASSERT_EQ(EXP_RESPONSE, res.message()); +} + } //namespace diff --git a/test/brpc_load_balancer_unittest.cpp b/test/brpc_load_balancer_unittest.cpp index f77111e4..66426870 100644 --- a/test/brpc_load_balancer_unittest.cpp +++ b/test/brpc_load_balancer_unittest.cpp @@ -30,6 +30,9 @@ #include "butil/containers/doubly_buffered_data.h" #include "brpc/describable.h" #include "brpc/socket.h" +#include "brpc/socket_map.h" +#include "brpc/global.h" +#include "brpc/details/load_balancer_with_naming.h" #include "butil/strings/string_number_conversions.h" #include "brpc/excluded_servers.h" #include "brpc/policy/weighted_round_robin_load_balancer.h" @@ -556,25 +559,28 @@ TEST_F(LoadBalancerTest, consistent_hashing) { "10.36.150.32:8833", "10.92.149.48:8833", "10.42.122.201:8833", + "[2408:871a:2100:3:0:ff:b025:348d]:8833", + "unix:test.sock", }; for (size_t round = 0; round < ARRAY_SIZE(hashs); ++round) { brpc::policy::ConsistentHashingLoadBalancer chlb(hash_type[round]); std::vector ids; std::vector addrs; - for (int j = 0;j < 5; ++j) - for (int i = 0; i < 5; ++i) { - const char *addr = servers[i]; - //snprintf(addr, sizeof(addr), "192.168.1.%d:8080", i); - butil::EndPoint dummy; - ASSERT_EQ(0, str2endpoint(addr, &dummy)); - brpc::ServerId id(8888); - brpc::SocketOptions options; - options.remote_side = dummy; - options.user = new SaveRecycle; - ASSERT_EQ(0, brpc::Socket::Create(options, &id.id)); - ids.push_back(id); - addrs.push_back(dummy); - chlb.AddServer(id); + for (int j = 0;j < 5; ++j) { + for (size_t i = 0; i < ARRAY_SIZE(servers); ++i) { + const char *addr = servers[i]; + //snprintf(addr, sizeof(addr), "192.168.1.%d:8080", i); + butil::EndPoint dummy; + ASSERT_EQ(0, str2endpoint(addr, &dummy)); + brpc::ServerId id(8888); + brpc::SocketOptions options; + options.remote_side = dummy; + options.user = new SaveRecycle; + ASSERT_EQ(0, brpc::Socket::Create(options, &id.id)); + ids.push_back(id); + addrs.push_back(dummy); + chlb.AddServer(id); + } } std::cout << chlb; for (int i = 0; i < 5; ++i) { @@ -1081,4 +1087,20 @@ TEST_F(LoadBalancerTest, revived_from_all_failed_intergrated) { ASSERT_EQ(0, num_failed.load(butil::memory_order_relaxed)); } +TEST_F(LoadBalancerTest, la_selection_too_long) { + brpc::GlobalInitializeOrDie(); + brpc::LoadBalancerWithNaming lb; + CHECK_EQ(0, lb.Init("list://127.0.0.1:8888", "la", nullptr, nullptr)); + char addr[] = "127.0.0.1:8888"; + butil::EndPoint ep; + ASSERT_EQ(0, str2endpoint(addr, &ep)); + brpc::SocketId id; + ASSERT_EQ(0, brpc::SocketMapFind(brpc::SocketMapKey(ep), &id)); + ASSERT_EQ(0, brpc::Socket::SetFailed(id)); + brpc::LoadBalancer::SelectIn in = { 0, false, false, 0u, nullptr }; + brpc::SocketUniquePtr ptr; + brpc::LoadBalancer::SelectOut out(&ptr); + ASSERT_EQ(EHOSTDOWN, lb.SelectServer(in, &out)); +} + } //namespace diff --git a/test/brpc_server_unittest.cpp b/test/brpc_server_unittest.cpp index f468f805..ee2feb0a 100644 --- a/test/brpc_server_unittest.cpp +++ b/test/brpc_server_unittest.cpp @@ -200,6 +200,16 @@ TEST_F(ServerTest, sanity) { // And release the listen port ASSERT_EQ(0, server.Start("127.0.0.1:8613", NULL)); } + { + brpc::Server server; + brpc::ServerOptions options; + ASSERT_EQ(0, server.Start(brpc::PortRange(8000, 9000), &options)); + ASSERT_TRUE(server.IsRunning()); + ASSERT_EQ(0ul, server.service_count()); + ASSERT_TRUE(NULL == server.first_service()); + ASSERT_EQ(0, server.Stop(0)); + ASSERT_EQ(0, server.Join()); + } butil::EndPoint ep; MyAuthenticator auth; diff --git a/test/brpc_uri_unittest.cpp b/test/brpc_uri_unittest.cpp index bb26fd00..2e8965d2 100644 --- a/test/brpc_uri_unittest.cpp +++ b/test/brpc_uri_unittest.cpp @@ -488,3 +488,7 @@ TEST(URITest, query_remover_key_value_not_changed_after_modified_query) { ASSERT_EQ(qr.value(), "value2"); } +TEST(URITest, valid_character) { + brpc::URI uri; + ASSERT_EQ(0, uri.SetHttpURL("www.baidu2.com':/?#[]@!$&()*+,;=-._~%")); +} diff --git a/test/bthread_unittest.cpp b/test/bthread_unittest.cpp index 66636dbe..72b8da3f 100644 --- a/test/bthread_unittest.cpp +++ b/test/bthread_unittest.cpp @@ -26,6 +26,10 @@ #include "bthread/unstable.h" #include "bthread/task_meta.h" +namespace bthread { + extern __thread bthread::LocalStorage tls_bls; +} + namespace { class BthreadTest : public ::testing::Test{ protected: @@ -508,6 +512,38 @@ TEST_F(BthreadTest, bthread_usleep) { ASSERT_EQ(0, bthread_join(th2, NULL)); } +static const bthread_attr_t BTHREAD_ATTR_NORMAL_WITH_SPAN = +{ BTHREAD_STACKTYPE_NORMAL, BTHREAD_INHERIT_SPAN, NULL }; + +void* test_parent_span(void* p) { + uint64_t *q = (uint64_t *)p; + *q = (uint64_t)(bthread::tls_bls.rpcz_parent_span); + LOG(INFO) << "span id in thread is " << *q; + return NULL; +} + +TEST_F(BthreadTest, test_span) { + uint64_t p1 = 0; + uint64_t p2 = 0; + + uint64_t target = 0xBADBEAFUL; + LOG(INFO) << "target span id is " << target; + + bthread::tls_bls.rpcz_parent_span = (void*)target; + bthread_t th1; + ASSERT_EQ(0, bthread_start_urgent(&th1, &BTHREAD_ATTR_NORMAL_WITH_SPAN, + test_parent_span, &p1)); + ASSERT_EQ(0, bthread_join(th1, NULL)); + + bthread_t th2; + ASSERT_EQ(0, bthread_start_background(&th2, NULL, + test_parent_span, &p2)); + ASSERT_EQ(0, bthread_join(th2, NULL)); + + ASSERT_EQ(p1, target); + ASSERT_NE(p2, target); +} + void* dummy_thread(void*) { return NULL; } diff --git a/test/bthread_work_stealing_queue_unittest.cpp b/test/bthread_work_stealing_queue_unittest.cpp index 50618338..19514cdf 100644 --- a/test/bthread_work_stealing_queue_unittest.cpp +++ b/test/bthread_work_stealing_queue_unittest.cpp @@ -39,7 +39,11 @@ void* steal_thread(void* arg) { if (q->steal(&val)) { stolen->push_back(val); } else { +#if defined(ARCH_CPU_ARM_FAMILY) + asm volatile("yield\n": : :"memory"); +#else asm volatile("pause\n": : :"memory"); +#endif } } return stolen; diff --git a/test/bvar_recorder_unittest.cpp b/test/bvar_recorder_unittest.cpp index e1606b66..6e05c62e 100644 --- a/test/bvar_recorder_unittest.cpp +++ b/test/bvar_recorder_unittest.cpp @@ -206,4 +206,46 @@ TEST(RecorderTest, perf) { << "ns per sample with " << ARRAY_SIZE(threads) << " threads"; } + +TEST(RecorderTest, latency_recorder_qps_accuracy) { + bvar::LatencyRecorder lr1(2); // set windows size to 2s + bvar::LatencyRecorder lr2(2); + bvar::LatencyRecorder lr3(2); + bvar::LatencyRecorder lr4(2); + usleep(3000000); // wait sampler to sample 3 times + + auto write = [](bvar::LatencyRecorder& lr, int times) { + for (int i = 0; i < times; ++i) { + lr << 1; + } + }; + write(lr1, 100); + write(lr2, 101); + write(lr3, 3); + write(lr4, 1); + usleep(1000000); // wait sampler to sample 1 time + + auto read = [](bvar::LatencyRecorder& lr, double exp_qps, int window_size = 0) { + int64_t qps_sum = 0; + int64_t exp_qps_int = (int64_t)exp_qps; + for (int i = 0; i < 1000; ++i) { + int64_t qps = window_size ? lr.qps(window_size): lr.qps(); + EXPECT_GE(qps, exp_qps_int - 1); + EXPECT_LE(qps, exp_qps_int + 1); + qps_sum += qps; + } + double err = fabs(qps_sum / 1000.0 - exp_qps); + return err; + }; + ASSERT_GT(0.1, read(lr1, 100/2.0)); + ASSERT_GT(0.1, read(lr2, 101/2.0)); + ASSERT_GT(0.1, read(lr3, 3/2.0)); + ASSERT_GT(0.1, read(lr4, 1/2.0)); + + ASSERT_GT(0.1, read(lr1, 100/3.0, 3)); + ASSERT_GT(0.1, read(lr2, 101/3.0, 3)); + ASSERT_GT(0.1, read(lr3, 3/3.0, 3)); + ASSERT_GT(0.1, read(lr4, 1/3.0, 3)); +} + } // namespace diff --git a/test/endpoint_unittest.cpp b/test/endpoint_unittest.cpp index 14d7075e..ba9a4ee6 100644 --- a/test/endpoint_unittest.cpp +++ b/test/endpoint_unittest.cpp @@ -20,9 +20,12 @@ #include "butil/endpoint.h" #include "butil/logging.h" #include "butil/containers/flat_map.h" +#include "butil/details/extended_endpoint.hpp" namespace { +using butil::details::ExtendedEndPoint; + TEST(EndPointTest, comparisons) { butil::EndPoint p1(butil::int2ip(1234), 5678); butil::EndPoint p2 = p1; @@ -148,4 +151,326 @@ TEST(EndPointTest, flat_map) { ASSERT_LT(info.longest_length, 32ul) << "detect hash collision and it's too large."; } +void* server_proc(void* arg) { + int listen_fd = (int64_t)arg; + sockaddr_storage ss; + socklen_t len = sizeof(ss); + int fd = accept(listen_fd, (sockaddr*)&ss, &len); + if (fd > 0) { + close(fd); + } + return (void*)(int64_t)fd; +} + +static void test_listen_connect(const std::string& server_addr, const std::string& exp_client_addr) { + butil::EndPoint point; + ASSERT_EQ(0, butil::str2endpoint(server_addr.c_str(), &point)); + ASSERT_EQ(server_addr, butil::endpoint2str(point).c_str()); + + int listen_fd = butil::tcp_listen(point); + ASSERT_GT(listen_fd, 0); + pthread_t pid; + pthread_create(&pid, NULL, server_proc, (void*)(int64_t)listen_fd); + + int fd = butil::tcp_connect(point, NULL); + ASSERT_GT(fd, 0); + + butil::EndPoint point2; + ASSERT_EQ(0, butil::get_local_side(fd, &point2)); + + std::string s = butil::endpoint2str(point2).c_str(); + if (butil::get_endpoint_type(point2) == AF_UNIX) { + ASSERT_EQ(exp_client_addr, s); + } else { + ASSERT_EQ(exp_client_addr, s.substr(0, exp_client_addr.size())); + } + ASSERT_EQ(0, butil::get_remote_side(fd, &point2)); + ASSERT_EQ(server_addr, butil::endpoint2str(point2).c_str()); + close(fd); + + void* ret = nullptr; + pthread_join(pid, &ret); + ASSERT_GT((int64_t)ret, 0); + close(listen_fd); +} + +static void test_parse_and_serialize(const std::string& instr, const std::string& outstr) { + butil::EndPoint ep; + ASSERT_EQ(0, butil::str2endpoint(instr.c_str(), &ep)); + butil::EndPointStr s = butil::endpoint2str(ep); + ASSERT_EQ(outstr, std::string(s.c_str())); +} + +TEST(EndPointTest, ipv4) { + test_listen_connect("127.0.0.1:8787", "127.0.0.1:"); +} + +TEST(EndPointTest, ipv6) { + // FIXME: test environ may not support ipv6 + // test_listen_connect("[::1]:8787", "[::1]:"); + + test_parse_and_serialize("[::1]:8080", "[::1]:8080"); + test_parse_and_serialize(" [::1]:65535 ", "[::1]:65535"); + test_parse_and_serialize(" [2001:0db8:a001:0002:0003:0ab9:C0A8:0102]:65535 ", + "[2001:db8:a001:2:3:ab9:c0a8:102]:65535"); + + butil::EndPoint ep; + ASSERT_EQ(-1, butil::str2endpoint("[2001:db8:1:2:3:ab9:c0a8:102]", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[2001:db8:1:2:3:ab9:c0a8:102]#654321", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("ipv6:2001:db8:1:2:3:ab9:c0a8:102", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[::1", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[]:80", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[]", &ep)); + ASSERT_EQ(-1, butil::str2endpoint("[]:", &ep)); +} + +TEST(EndPointTest, unix_socket) { + ::unlink("test.sock"); + test_listen_connect("unix:test.sock", "unix:"); + ::unlink("test.sock"); + + butil::EndPoint point; + ASSERT_EQ(-1, butil::str2endpoint("", &point)); + ASSERT_EQ(-1, butil::str2endpoint("a.sock", &point)); + ASSERT_EQ(-1, butil::str2endpoint("unix:", &point)); + ASSERT_EQ(-1, butil::str2endpoint(" unix: ", &point)); + ASSERT_EQ(0, butil::str2endpoint("unix://a.sock", 123, &point)); + ASSERT_EQ(std::string("unix://a.sock"), butil::endpoint2str(point).c_str()); + + ASSERT_EQ(-1, butil::str2endpoint("unix:tooloooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooong.sock", &point)); + ASSERT_EQ(0, butil::str2endpoint(" unix:loooooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooong.sock", &point)); + ASSERT_EQ(std::string("unix:loooooooooooooooooooooooooooooooooooooooooooooooooooo" + "ooooooooooooooooooooooooooooooooooooooooooooooong.sock"), butil::endpoint2str(point).c_str()); + char buf[128] = {0}; // braft use this size of buffer + size_t ret = snprintf(buf, sizeof(buf), "%s:%d", butil::endpoint2str(point).c_str(), INT_MAX); + ASSERT_LT(ret, sizeof(buf) - 1); +} + +TEST(EndPointTest, original_endpoint) { + butil::EndPoint ep; + ASSERT_FALSE(ExtendedEndPoint::is_extended(ep)); + ASSERT_EQ(NULL, ExtendedEndPoint::address(ep)); + + ASSERT_EQ(0, butil::str2endpoint("1.2.3.4:5678", &ep)); + ASSERT_FALSE(ExtendedEndPoint::is_extended(ep)); + ASSERT_EQ(NULL, ExtendedEndPoint::address(ep)); + + // ctor & dtor + { + butil::EndPoint ep2(ep); + ASSERT_FALSE(ExtendedEndPoint::is_extended(ep)); + ASSERT_EQ(ep.ip, ep2.ip); + ASSERT_EQ(ep.port, ep2.port); + } + + // assign + butil::EndPoint ep2; + ep2 = ep; + ASSERT_EQ(ep.ip, ep2.ip); + ASSERT_EQ(ep.port, ep2.port); +} + +TEST(EndPointTest, extended_endpoint) { + butil::EndPoint ep; + ASSERT_EQ(0, butil::str2endpoint("unix:sock.file", &ep)); + ASSERT_TRUE(ExtendedEndPoint::is_extended(ep)); + ExtendedEndPoint* eep = ExtendedEndPoint::address(ep); + ASSERT_TRUE(eep); + ASSERT_EQ(AF_UNIX, eep->family()); + ASSERT_EQ(1, eep->_ref_count.load()); + + // copy ctor & dtor + { + butil::EndPoint tmp(ep); + ASSERT_EQ(2, eep->_ref_count.load()); + ASSERT_EQ(eep, ExtendedEndPoint::address(tmp)); + ASSERT_EQ(eep, ExtendedEndPoint::address(ep)); + } + ASSERT_EQ(1, eep->_ref_count.load()); + + butil::EndPoint ep2; + + // extended endpoint assigns to original endpoint + ep2 = ep; + ASSERT_EQ(2, eep->_ref_count.load()); + ASSERT_EQ(eep, ExtendedEndPoint::address(ep2)); + + // original endpoint assigns to extended endpoint + ep2 = butil::EndPoint(); + ASSERT_EQ(1, eep->_ref_count.load()); + ASSERT_FALSE(ExtendedEndPoint::is_extended(ep2)); + + // extended endpoint assigns to extended endpoint + ASSERT_EQ(0, butil::str2endpoint("[::1]:2233", &ep2)); + ExtendedEndPoint* eep2 = ExtendedEndPoint::address(ep2); + ASSERT_TRUE(eep2); + ep2 = ep; + // eep2 has been returned to resource pool, but we can still access it here unsafely. + ASSERT_EQ(0, eep2->_ref_count.load()); + ASSERT_EQ(AF_UNSPEC, eep2->family()); + ASSERT_EQ(2, eep->_ref_count.load()); + ASSERT_EQ(eep, ExtendedEndPoint::address(ep)); + ASSERT_EQ(eep, ExtendedEndPoint::address(ep2)); + + ASSERT_EQ(0, str2endpoint("[::1]:2233", &ep2)); + ASSERT_EQ(1, eep->_ref_count.load()); + eep2 = ExtendedEndPoint::address(ep2); + ASSERT_NE(eep, eep2); + ASSERT_EQ(1, eep2->_ref_count.load()); +} + +TEST(EndPointTest, endpoint_compare) { + butil::EndPoint ep1, ep2, ep3; + + ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:8080", &ep1)); + ASSERT_EQ(0, butil::str2endpoint("127.0.0.1:8080", &ep2)); + ASSERT_EQ(0, butil::str2endpoint("127.0.0.3:8080", &ep3)); + ASSERT_EQ(ep1, ep2); + ASSERT_NE(ep1, ep3); + + ASSERT_EQ(0, butil::str2endpoint("unix:sock1.file", &ep1)); + ASSERT_EQ(0, butil::str2endpoint("unix:sock1.file", &ep2)); + ASSERT_EQ(0, butil::str2endpoint("unix:sock3.file", &ep3)); + ASSERT_EQ(ep1, ep2); + ASSERT_NE(ep1, ep3); + + ASSERT_EQ(0, butil::str2endpoint("[::1]:2233", &ep1)); + ASSERT_EQ(0, butil::str2endpoint("[::1]:2233", &ep2)); + ASSERT_EQ(0, butil::str2endpoint("[::3]:2233", &ep3)); + ASSERT_EQ(ep1, ep2); + ASSERT_NE(ep1, ep3); +} + +TEST(EndPointTest, endpoint_sockaddr_conv_ipv4) { + butil::EndPoint ep; + ASSERT_EQ(0, butil::str2endpoint("1.2.3.4:8086", &ep)); + + in_addr expected_in_addr; + bzero(&expected_in_addr, sizeof(expected_in_addr)); + expected_in_addr.s_addr = 0x04030201u; + + sockaddr_storage ss; + sockaddr_in* in4 = (sockaddr_in*) &ss; + + memset(&ss, 'a', sizeof(ss)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss)); + ASSERT_EQ(AF_INET, ss.ss_family); + ASSERT_EQ(AF_INET, in4->sin_family); + in_port_t port = htons(8086); + ASSERT_EQ(port, in4->sin_port); + ASSERT_EQ(0, memcmp(&in4->sin_addr, &expected_in_addr, sizeof(expected_in_addr))); + + sockaddr_storage ss2; + socklen_t ss2_size = 0; + memset(&ss2, 'b', sizeof(ss2)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss2, &ss2_size)); + ASSERT_EQ(ss2_size, sizeof(*in4)); + ASSERT_EQ(0, memcmp(&ss2, &ss, sizeof(ss))); + + butil::EndPoint ep2; + ASSERT_EQ(0, butil::sockaddr2endpoint(&ss, sizeof(*in4), &ep2)); + ASSERT_EQ(ep2, ep); + + ASSERT_EQ(AF_INET, butil::get_endpoint_type(ep)); +} + +TEST(EndPointTest, endpoint_sockaddr_conv_ipv6) { + butil::EndPoint ep; + ASSERT_EQ(0, butil::str2endpoint("[::1]:8086", &ep)); + + in6_addr expect_in6_addr; + bzero(&expect_in6_addr, sizeof(expect_in6_addr)); + expect_in6_addr.__in6_u.__u6_addr8[15] = 1; + + sockaddr_storage ss; + const sockaddr_in6* sa6 = (sockaddr_in6*) &ss; + + memset(&ss, 'a', sizeof(ss)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss)); + ASSERT_EQ(AF_INET6, ss.ss_family); + ASSERT_EQ(AF_INET6, sa6->sin6_family); + in_port_t port = htons(8086); + ASSERT_EQ(port, sa6->sin6_port); + ASSERT_EQ(0u, sa6->sin6_flowinfo); + ASSERT_EQ(0, memcmp(&expect_in6_addr, &sa6->sin6_addr, sizeof(in6_addr))); + ASSERT_EQ(0u, sa6->sin6_scope_id); + + sockaddr_storage ss2; + socklen_t ss2_size = 0; + memset(&ss2, 'b', sizeof(ss2)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss2, &ss2_size)); + ASSERT_EQ(ss2_size, sizeof(*sa6)); + ASSERT_EQ(0, memcmp(&ss2, &ss, sizeof(ss))); + + butil::EndPoint ep2; + ASSERT_EQ(0, butil::sockaddr2endpoint(&ss, sizeof(*sa6), &ep2)); + ASSERT_STREQ("[::1]:8086", butil::endpoint2str(ep2).c_str()); + + ASSERT_EQ(AF_INET6, butil::get_endpoint_type(ep)); +} + +TEST(EndPointTest, endpoint_sockaddr_conv_unix) { + butil::EndPoint ep; + ASSERT_EQ(0, butil::str2endpoint("unix:sock.file", &ep)); + + sockaddr_storage ss; + const sockaddr_un* un = (sockaddr_un*) &ss; + + memset(&ss, 'a', sizeof(ss)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss)); + ASSERT_EQ(AF_UNIX, ss.ss_family); + ASSERT_EQ(AF_UNIX, un->sun_family); + ASSERT_EQ(0, memcmp("sock.file", un->sun_path, 10)); + + sockaddr_storage ss2; + socklen_t ss2_size = 0; + memset(&ss2, 'b', sizeof(ss2)); + ASSERT_EQ(0, butil::endpoint2sockaddr(ep, &ss2, &ss2_size)); + ASSERT_EQ(offsetof(struct sockaddr_un, sun_path) + strlen("sock.file") + 1, ss2_size); + ASSERT_EQ(0, memcmp(&ss2, &ss, sizeof(ss))); + + butil::EndPoint ep2; + ASSERT_EQ(0, butil::sockaddr2endpoint(&ss, sizeof(sa_family_t) + strlen(un->sun_path) + 1, &ep2)); + ASSERT_STREQ("unix:sock.file", butil::endpoint2str(ep2).c_str()); + + ASSERT_EQ(AF_UNIX, butil::get_endpoint_type(ep)); +} + +void concurrent_proc(void* p) { + for (int i = 0; i < 10000; ++i) { + butil::EndPoint ep; + std::string str("127.0.0.1:8080"); + ASSERT_EQ(0, butil::str2endpoint(str.c_str(), &ep)); + ASSERT_EQ(str, butil::endpoint2str(ep).c_str()); + + str.assign("[::1]:8080"); + ASSERT_EQ(0, butil::str2endpoint(str.c_str(), &ep)); + ASSERT_EQ(str, butil::endpoint2str(ep).c_str()); + + str.assign("unix:test.sock"); + ASSERT_EQ(0, butil::str2endpoint(str.c_str(), &ep)); + ASSERT_EQ(str, butil::endpoint2str(ep).c_str()); + } + *(int*)p = 1; +} + +TEST(EndPointTest, endpoint_concurrency) { + const int T = 5; + pthread_t tids[T]; + int rets[T] = {0}; + for (int i = 0; i < T; ++i) { + pthread_create(&tids[i], nullptr, [](void* p) { + concurrent_proc(p); + return (void*)nullptr; + }, &rets[i]); + } + for (int i = 0; i < T; ++i) { + pthread_join(tids[i], nullptr); + ASSERT_EQ(1, rets[i]); + } +} + } // end of namespace diff --git a/test/flat_map_unittest.cpp b/test/flat_map_unittest.cpp index 6b6e3605..99f30e41 100644 --- a/test/flat_map_unittest.cpp +++ b/test/flat_map_unittest.cpp @@ -287,10 +287,13 @@ TEST_F(FlatMapTest, make_sure_all_methods_compile) { ASSERT_EQ(20, m1[2]); ASSERT_EQ(2u, m1.size()); m1.insert(1, 100); + m1.insert({3, 30}); ASSERT_EQ(100, m1[1]); - ASSERT_EQ(2u, m1.size()); - ASSERT_EQ(NULL, m1.seek(3)); - ASSERT_EQ(0u, m1.erase(3)); + ASSERT_EQ(3u, m1.size()); + ASSERT_TRUE(m1.seek(3)); + ASSERT_EQ(NULL, m1.seek(4)); + ASSERT_EQ(1u, m1.erase(3)); + ASSERT_EQ(0u, m1.erase(4)); ASSERT_EQ(2u, m1.size()); ASSERT_EQ(1u, m1.erase(2)); ASSERT_EQ(1u, m1.size()); diff --git a/tools/BUILD b/tools/BUILD new file mode 100644 index 00000000..99f472fd --- /dev/null +++ b/tools/BUILD @@ -0,0 +1,49 @@ +# 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. + +COPTS = [ + "-D__STDC_FORMAT_MACROS", + "-DBTHREAD_USE_FAST_PTHREAD_MUTEX", + "-D__const__=", + "-D_GNU_SOURCE", + "-DUSE_SYMBOLIZE", + "-DNO_TCMALLOC", + "-D__STDC_LIMIT_MACROS", + "-D__STDC_CONSTANT_MACROS", + "-fPIC", + "-Wno-unused-parameter", + "-fno-omit-frame-pointer", + "-DGFLAGS_NS=google", +] + select({ + "//:with_glog": ["-DBRPC_WITH_GLOG=1"], + "//conditions:default": ["-DBRPC_WITH_GLOG=0"], +}) + +cc_binary( + name = "rpc_replay", + srcs = [ + "rpc_replay/info_thread.h", + "rpc_replay/rpc_replay.cpp", + "rpc_replay/info_thread.cpp", + ], + includes = [ + "rpc_replay", + ], + deps = [ + "//:brpc", + ], + copts = COPTS, +) + diff --git a/tools/gdb_bthread_stack.py b/tools/gdb_bthread_stack.py index 582ba9eb..b36c209c 100644 --- a/tools/gdb_bthread_stack.py +++ b/tools/gdb_bthread_stack.py @@ -1,6 +1,23 @@ #!/usr/bin/env python # coding=utf-8 +# 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. + """ Bthread Stack Print Tool diff --git a/tools/get_brpc_revision.sh b/tools/get_brpc_revision.sh index 919470c1..f1dd7c2c 100755 --- a/tools/get_brpc_revision.sh +++ b/tools/get_brpc_revision.sh @@ -1,10 +1,27 @@ #!/bin/bash +# 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. + output=$(cat $1/RELEASE_VERSION) -version=$(git log -1 --format="%h\\|%cI" 2> /dev/null) +abbr_commit_hash=$(git log -1 --format="%h" 2> /dev/null) +committer_date=($(git log -1 --format="%ci" 2> /dev/null)) +committer_date="${committer_date[0]}T${committer_date[1]}${committer_date[2]:0:3}:${committer_date[2]:3:2}" branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null) if [ $? -eq 0 ] then - output=$output"\\|"$branch"\\|"$version + output=$output"\\|"$branch"\\|"$abbr_commit_hash"\\|"$committer_date fi echo $output diff --git a/tools/rpc_replay/rpc_replay.cpp b/tools/rpc_replay/rpc_replay.cpp index d6f30da0..fda6076e 100644 --- a/tools/rpc_replay/rpc_replay.cpp +++ b/tools/rpc_replay/rpc_replay.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include "info_thread.h" DEFINE_string(dir, "", "The directory of dumped requests"); @@ -42,6 +43,7 @@ DEFINE_string(load_balancer, "", "The algorithm for load balancing"); DEFINE_int32(timeout_ms, 100, "RPC timeout in milliseconds"); DEFINE_int32(max_retry, 3, "Maximum retry times"); DEFINE_int32(dummy_port, 8899, "Port of dummy server(to monitor replaying)"); +DEFINE_string(http_host, "", "Host field for http protocol"); bvar::LatencyRecorder g_latency_recorder("rpc_replay"); bvar::Adder g_error_count("rpc_replay_error_count"); @@ -159,12 +161,30 @@ static void* replay_thread(void* arg) { } brpc::Controller* cntl = new brpc::Controller; + req.Clear(); + + brpc::SerializedRequest* req_ptr = &req; cntl->reset_sampled_request(sample_guard.release()); - - if (sample->meta.protocol_type() == brpc::PROTOCOL_NSHEAD) { + if (sample->meta.protocol_type() == brpc::PROTOCOL_HTTP) { + brpc::HttpMessage http_message; + http_message.ParseFromIOBuf(sample->request); + cntl->http_request().Swap(http_message.header()); + if (!FLAGS_http_host.empty()) { + // reset Host in header + cntl->http_request().SetHeader("Host", FLAGS_http_host); + } + cntl->request_attachment() = http_message.body().movable(); + req_ptr = NULL; + } else if (sample->meta.protocol_type() == brpc::PROTOCOL_NSHEAD) { nshead_req.Clear(); memcpy(&nshead_req.head, sample->meta.nshead().c_str(), sample->meta.nshead().length()); nshead_req.body = sample->request; + req_ptr = &nshead_req; + } else if (sample->meta.attachment_size() > 0) { + sample->request.cutn( + &req.serialized_data(), + sample->request.size() - sample->meta.attachment_size()); + cntl->request_attachment() = sample->request.movable(); } else { req.Clear(); if (sample->meta.attachment_size() > 0) { @@ -179,20 +199,15 @@ static void* replay_thread(void* arg) { g_sent_count << 1; const int64_t start_time = butil::gettimeofday_us(); - google::protobuf::Message* r_req = &req; - if (sample->meta.protocol_type() == brpc::PROTOCOL_NSHEAD) { - // use nshead req; - r_req = &nshead_req; - } if (FLAGS_qps <= 0) { chan->CallMethod(NULL/*use rpc_dump_context in cntl instead*/, - cntl, r_req, NULL/*ignore response*/, NULL); + cntl, req_ptr, NULL/*ignore response*/, NULL); handle_response(cntl, start_time, true); } else { google::protobuf::Closure* done = brpc::NewCallback(handle_response, cntl, start_time, false); chan->CallMethod(NULL/*use rpc_dump_context in cntl instead*/, - cntl, r_req, NULL/*ignore response*/, done); + cntl, req_ptr, NULL/*ignore response*/, done); const int64_t end_time = butil::gettimeofday_us(); int64_t expected_elp = 0; int64_t actual_elp = 0; diff --git a/tools/rpc_view/rpc_view.cpp b/tools/rpc_view/rpc_view.cpp index e060713c..39d7a2ac 100644 --- a/tools/rpc_view/rpc_view.cpp +++ b/tools/rpc_view/rpc_view.cpp @@ -161,7 +161,7 @@ int main(int argc, char* argv[]) { return -1; } // This keeps ad-hoc creation of channels reuse previous connections. - GFLAGS_NS::SetCommandLineOption("defer_close_seconds", "10"); + GFLAGS_NS::SetCommandLineOption("defer_close_second", "10"); brpc::Server server; server.set_version("rpc_view_server");