chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
<!--
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
Licensed 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
|
||||
|
||||
https://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.
|
||||
-->
|
||||
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
|
||||
Core C++ code lives in `src/`: `src/brpc`, `src/bthread`,
|
||||
`src/butil`, `src/bvar`, `src/json2pb`, and `src/mcpack2pb`. Tests are in
|
||||
`test/` and mirror module names. Samples are in `example/`; utilities
|
||||
are in `tools/`. Documentation is under `docs/en` and `docs/cn`; packaging and
|
||||
bindings live in `package/`, `homebrew-formula/`, `python/`, and `java/`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
- `sh config_brpc.sh --headers=/usr/include --libs=/usr/lib && make`: configure and build with Make.
|
||||
- `cmake -B build && cmake --build build -j6`: configure and build with CMake.
|
||||
- `cmake -B build -DBUILD_UNIT_TESTS=ON && cmake --build build -j6 && cd build && ctest`: run CMake tests.
|
||||
- `cd test && make && sh run_tests.sh`: run the Make-based test suite.
|
||||
- `bazel build //:brpc` and `bazel test //test/...`: build or test with Bazel.
|
||||
- `cd example/echo_c++ && make && ./echo_server & ./echo_client`: smoke-test an example.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
|
||||
Follow Google C++ style with 4-space indentation. Keep feature-specific code in
|
||||
the relevant protocol or module, not broad files such as `server.cpp` or
|
||||
`channel.cpp`, unless the behavior is general. Use existing names:
|
||||
`*_unittest.cpp` or `*_unittest.cc`, module prefixes such as `brpc_`,
|
||||
`bthread_`, and `bvar_`, and `.proto` files beside related code.
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
New behavior should include unit tests. Run the smallest relevant test first,
|
||||
then the broader affected suite. Tests use Google Test and live in `test/`.
|
||||
Some integration tests require Redis or MySQL and may skip when absent.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
|
||||
Recent history uses short imperative subjects such as `Fix bazel compile error
|
||||
on macOS`. Keep commits focused, explain behavioral impact when needed, and
|
||||
link issues. Pull requests should describe the change, list tests run, note
|
||||
platform or dependency assumptions, and pass GitHub Actions.
|
||||
|
||||
## Security & Configuration Tips
|
||||
|
||||
Read `SECURITY.md` before reporting vulnerabilities. Avoid committing
|
||||
build directories, local paths, credentials, or machine-specific configuration.
|
||||
Prefer flags such as `--with-glog`, `--with-thrift`, `--with-asan`,
|
||||
or the matching CMake/Bazel options.
|
||||
+582
@@ -0,0 +1,582 @@
|
||||
# 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.
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "objc_library")
|
||||
load("//bazel/tools:brpc_proto_library.bzl", "brpc_proto_library")
|
||||
|
||||
licenses(["notice"]) # Apache v2
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
COPTS = [
|
||||
"-fno-omit-frame-pointer",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_asan": ["-fsanitize=address"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
DEFINES = [
|
||||
"BTHREAD_USE_FAST_PTHREAD_MUTEX",
|
||||
"__const__=__unused__",
|
||||
"_GNU_SOURCE",
|
||||
"USE_SYMBOLIZE",
|
||||
"NO_TCMALLOC",
|
||||
"__STDC_FORMAT_MACROS",
|
||||
"__STDC_LIMIT_MACROS",
|
||||
"__STDC_CONSTANT_MACROS",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_glog": ["BRPC_WITH_GLOG=1"],
|
||||
"//conditions:default": ["BRPC_WITH_GLOG=0"],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_mesalink": ["USE_MESALINK"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_thrift": ["ENABLE_THRIFT_FRAMED_PROTOCOL=1"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_thrift_legacy_version": [],
|
||||
"//conditions:default": ["THRIFT_STDCXX=std"],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_rdma": ["BRPC_WITH_RDMA=1"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_debug_bthread_sche_safety": ["BRPC_DEBUG_BTHREAD_SCHE_SAFETY=1"],
|
||||
"//conditions:default": ["BRPC_DEBUG_BTHREAD_SCHE_SAFETY=0"],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_debug_lock": ["BRPC_DEBUG_LOCK=1"],
|
||||
"//conditions:default": ["BRPC_DEBUG_LOCK=0"],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_no_pthread_mutex_hook": ["NO_PTHREAD_MUTEX_HOOK"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
LINKOPTS = [
|
||||
"-pthread",
|
||||
"-ldl",
|
||||
] + select({
|
||||
"@bazel_tools//src/conditions:darwin": [
|
||||
"-framework CoreFoundation",
|
||||
"-framework CoreGraphics",
|
||||
"-framework CoreData",
|
||||
"-framework CoreText",
|
||||
"-framework Security",
|
||||
"-framework Foundation",
|
||||
"-Wl,-U,_MallocExtension_ReleaseFreeMemory",
|
||||
"-Wl,-U,_ProfilerStart",
|
||||
"-Wl,-U,_ProfilerStop",
|
||||
"-Wl,-U,__Z13GetStackTracePPvii",
|
||||
"-Wl,-U,_RegisterThriftProtocol",
|
||||
"-Wl,-U,_mallctl",
|
||||
"-Wl,-U,_malloc_stats_print",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"-lrt",
|
||||
],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_mesalink": [
|
||||
"-lmesalink",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_rdma": [
|
||||
"-libverbs",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_asan": ["-fsanitize=address"],
|
||||
"//conditions:default": [],
|
||||
})
|
||||
|
||||
genrule(
|
||||
name = "config_h",
|
||||
outs = [
|
||||
"src/butil/config.h",
|
||||
],
|
||||
cmd = """cat << EOF >$@""" + """
|
||||
// This file is auto-generated.
|
||||
#ifndef BUTIL_CONFIG_H
|
||||
#define BUTIL_CONFIG_H
|
||||
#ifdef BRPC_WITH_GLOG
|
||||
#undef BRPC_WITH_GLOG
|
||||
#endif
|
||||
#define BRPC_WITH_GLOG """ + select({
|
||||
"//bazel/config:brpc_with_glog": "1",
|
||||
"//conditions:default": "0",
|
||||
}) +
|
||||
"""
|
||||
#endif // BUTIL_CONFIG_H
|
||||
EOF
|
||||
""",
|
||||
)
|
||||
|
||||
BUTIL_SRCS = [
|
||||
"src/butil/third_party/dmg_fp/g_fmt.cc",
|
||||
"src/butil/third_party/dmg_fp/dtoa_wrapper.cc",
|
||||
"src/butil/third_party/dynamic_annotations/dynamic_annotations.c",
|
||||
"src/butil/third_party/icu/icu_utf.cc",
|
||||
"src/butil/third_party/superfasthash/superfasthash.c",
|
||||
"src/butil/third_party/modp_b64/modp_b64.cc",
|
||||
"src/butil/third_party/symbolize/demangle.cc",
|
||||
"src/butil/third_party/symbolize/symbolize.cc",
|
||||
"src/butil/third_party/snappy/snappy-sinksource.cc",
|
||||
"src/butil/third_party/snappy/snappy-stubs-internal.cc",
|
||||
"src/butil/third_party/snappy/snappy.cc",
|
||||
"src/butil/third_party/murmurhash3/murmurhash3.cpp",
|
||||
"src/butil/arena.cpp",
|
||||
"src/butil/at_exit.cc",
|
||||
"src/butil/atomicops_internals_x86_gcc.cc",
|
||||
"src/butil/base64.cc",
|
||||
"src/butil/base64url.cc",
|
||||
"src/butil/big_endian.cc",
|
||||
"src/butil/cpu.cc",
|
||||
"src/butil/debug/alias.cc",
|
||||
"src/butil/debug/asan_invalid_access.cc",
|
||||
"src/butil/debug/crash_logging.cc",
|
||||
"src/butil/debug/debugger.cc",
|
||||
"src/butil/debug/debugger_posix.cc",
|
||||
"src/butil/debug/dump_without_crashing.cc",
|
||||
"src/butil/debug/proc_maps_linux.cc",
|
||||
"src/butil/debug/stack_trace.cc",
|
||||
"src/butil/debug/stack_trace_posix.cc",
|
||||
"src/butil/environment.cc",
|
||||
"src/butil/files/file.cc",
|
||||
"src/butil/files/file_posix.cc",
|
||||
"src/butil/files/file_enumerator.cc",
|
||||
"src/butil/files/file_enumerator_posix.cc",
|
||||
"src/butil/files/file_path.cc",
|
||||
"src/butil/files/file_path_constants.cc",
|
||||
"src/butil/files/memory_mapped_file.cc",
|
||||
"src/butil/files/memory_mapped_file_posix.cc",
|
||||
"src/butil/files/scoped_file.cc",
|
||||
"src/butil/files/scoped_temp_dir.cc",
|
||||
"src/butil/file_util.cc",
|
||||
"src/butil/file_util_posix.cc",
|
||||
"src/butil/guid.cc",
|
||||
"src/butil/guid_posix.cc",
|
||||
"src/butil/hash.cc",
|
||||
"src/butil/lazy_instance.cc",
|
||||
"src/butil/location.cc",
|
||||
"src/butil/memory/aligned_memory.cc",
|
||||
"src/butil/memory/ref_counted.cc",
|
||||
"src/butil/memory/ref_counted_memory.cc",
|
||||
"src/butil/memory/singleton.cc",
|
||||
"src/butil/memory/weak_ptr.cc",
|
||||
"src/butil/posix/file_descriptor_shuffle.cc",
|
||||
"src/butil/posix/global_descriptors.cc",
|
||||
"src/butil/process_util.cc",
|
||||
"src/butil/rand_util.cc",
|
||||
"src/butil/rand_util_posix.cc",
|
||||
"src/butil/fast_rand.cpp",
|
||||
"src/butil/safe_strerror_posix.cc",
|
||||
"src/butil/sha1_portable.cc",
|
||||
"src/butil/strings/latin1_string_conversions.cc",
|
||||
"src/butil/strings/nullable_string16.cc",
|
||||
"src/butil/strings/safe_sprintf.cc",
|
||||
"src/butil/strings/string16.cc",
|
||||
"src/butil/strings/string_number_conversions.cc",
|
||||
"src/butil/strings/string_split.cc",
|
||||
"src/butil/strings/string_piece.cc",
|
||||
"src/butil/strings/string_util.cc",
|
||||
"src/butil/strings/string_util_constants.cc",
|
||||
"src/butil/strings/stringprintf.cc",
|
||||
"src/butil/strings/utf_offset_string_conversions.cc",
|
||||
"src/butil/strings/utf_string_conversion_utils.cc",
|
||||
"src/butil/strings/utf_string_conversions.cc",
|
||||
"src/butil/synchronization/cancellation_flag.cc",
|
||||
"src/butil/synchronization/condition_variable_posix.cc",
|
||||
"src/butil/synchronization/waitable_event_posix.cc",
|
||||
"src/butil/threading/non_thread_safe_impl.cc",
|
||||
"src/butil/threading/platform_thread_posix.cc",
|
||||
"src/butil/threading/simple_thread.cc",
|
||||
"src/butil/threading/thread_checker_impl.cc",
|
||||
"src/butil/threading/thread_collision_warner.cc",
|
||||
"src/butil/threading/thread_id_name_manager.cc",
|
||||
"src/butil/threading/thread_local_posix.cc",
|
||||
"src/butil/threading/thread_local_storage.cc",
|
||||
"src/butil/threading/thread_local_storage_posix.cc",
|
||||
"src/butil/threading/thread_restrictions.cc",
|
||||
"src/butil/threading/watchdog.cc",
|
||||
"src/butil/time/clock.cc",
|
||||
"src/butil/time/default_clock.cc",
|
||||
"src/butil/time/default_tick_clock.cc",
|
||||
"src/butil/time/tick_clock.cc",
|
||||
"src/butil/time/time.cc",
|
||||
"src/butil/time/time_posix.cc",
|
||||
"src/butil/version.cc",
|
||||
"src/butil/logging.cc",
|
||||
"src/butil/class_name.cpp",
|
||||
"src/butil/errno.cpp",
|
||||
"src/butil/find_cstr.cpp",
|
||||
"src/butil/status.cpp",
|
||||
"src/butil/string_printf.cpp",
|
||||
"src/butil/thread_local.cpp",
|
||||
"src/butil/thread_key.cpp",
|
||||
"src/butil/unix_socket.cpp",
|
||||
"src/butil/endpoint.cpp",
|
||||
"src/butil/fd_utility.cpp",
|
||||
"src/butil/files/temp_file.cpp",
|
||||
"src/butil/files/file_watcher.cpp",
|
||||
"src/butil/time.cpp",
|
||||
"src/butil/zero_copy_stream_as_streambuf.cpp",
|
||||
"src/butil/crc32c.cc",
|
||||
"src/butil/containers/case_ignored_flat_map.cpp",
|
||||
"src/butil/iobuf.cpp",
|
||||
"src/butil/single_iobuf.cpp",
|
||||
"src/butil/iobuf_profiler.cpp",
|
||||
"src/butil/binary_printer.cpp",
|
||||
"src/butil/recordio.cc",
|
||||
"src/butil/popen.cpp",
|
||||
] + select({
|
||||
"@bazel_tools//src/conditions:darwin": [
|
||||
"src/butil/time/time_mac.cc",
|
||||
"src/butil/mac/scoped_mach_port.cc",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"src/butil/file_util_linux.cc",
|
||||
"src/butil/threading/platform_thread_linux.cc",
|
||||
"src/butil/strings/sys_string_conversions_posix.cc",
|
||||
],
|
||||
})
|
||||
|
||||
objc_library(
|
||||
name = "macos_lib",
|
||||
hdrs = [
|
||||
"src/butil/atomicops.h",
|
||||
"src/butil/atomicops_internals_atomicword_compat.h",
|
||||
"src/butil/atomicops_internals_mac.h",
|
||||
"src/butil/base_export.h",
|
||||
"src/butil/basictypes.h",
|
||||
"src/butil/build_config.h",
|
||||
"src/butil/compat.h",
|
||||
"src/butil/compiler_specific.h",
|
||||
"src/butil/containers/hash_tables.h",
|
||||
"src/butil/debug/debugger.h",
|
||||
"src/butil/debug/leak_annotations.h",
|
||||
"src/butil/file_descriptor_posix.h",
|
||||
"src/butil/file_util.h",
|
||||
"src/butil/files/file.h",
|
||||
"src/butil/files/file_path.h",
|
||||
"src/butil/files/scoped_file.h",
|
||||
"src/butil/lazy_instance.h",
|
||||
"src/butil/logging.h",
|
||||
"src/butil/mac/bundle_locations.h",
|
||||
"src/butil/mac/foundation_util.h",
|
||||
"src/butil/mac/scoped_cftyperef.h",
|
||||
"src/butil/mac/scoped_typeref.h",
|
||||
"src/butil/macros.h",
|
||||
"src/butil/string_printf.h",
|
||||
"src/butil/memory/aligned_memory.h",
|
||||
"src/butil/memory/scoped_policy.h",
|
||||
"src/butil/memory/scoped_ptr.h",
|
||||
"src/butil/move.h",
|
||||
"src/butil/port.h",
|
||||
"src/butil/posix/eintr_wrapper.h",
|
||||
"src/butil/scoped_generic.h",
|
||||
"src/butil/strings/string16.h",
|
||||
"src/butil/strings/string_piece.h",
|
||||
"src/butil/strings/string_util.h",
|
||||
"src/butil/strings/string_util_posix.h",
|
||||
"src/butil/strings/sys_string_conversions.h",
|
||||
"src/butil/synchronization/lock.h",
|
||||
"src/butil/third_party/dynamic_annotations/dynamic_annotations.h",
|
||||
"src/butil/third_party/murmurhash3/murmurhash3.h",
|
||||
"src/butil/threading/platform_thread.h",
|
||||
"src/butil/threading/thread_id_name_manager.h",
|
||||
"src/butil/threading/thread_restrictions.h",
|
||||
"src/butil/time.h",
|
||||
"src/butil/time/time.h",
|
||||
"src/butil/type_traits.h",
|
||||
":config_h",
|
||||
],
|
||||
enable_modules = True,
|
||||
includes = ["src/"],
|
||||
non_arc_srcs = [
|
||||
"src/butil/mac/bundle_locations.mm",
|
||||
"src/butil/mac/foundation_util.mm",
|
||||
"src/butil/file_util_mac.mm",
|
||||
"src/butil/threading/platform_thread_mac.mm",
|
||||
"src/butil/strings/sys_string_conversions_mac.mm",
|
||||
],
|
||||
tags = ["manual"],
|
||||
deps = [
|
||||
"@com_github_gflags_gflags//:gflags",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_glog": ["@com_github_google_glog//:glog"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "butil",
|
||||
srcs = BUTIL_SRCS,
|
||||
hdrs = glob([
|
||||
"src/butil/*.h",
|
||||
"src/butil/*.hpp",
|
||||
"src/butil/**/*.h",
|
||||
"src/butil/**/*.hpp",
|
||||
"src/butil/**/**/*.h",
|
||||
"src/butil/**/**/*.hpp",
|
||||
"src/butil/**/**/**/*.h",
|
||||
"src/butil/**/**/**/*.hpp",
|
||||
]) + [
|
||||
"src/butil/third_party/dmg_fp/dtoa.cc",
|
||||
":config_h",
|
||||
],
|
||||
defines = DEFINES + select({
|
||||
"//bazel/config:brpc_build_for_unittest": [
|
||||
"UNIT_TEST",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_gflags_gflags//:gflags",
|
||||
"@com_github_madler_zlib//:zlib",
|
||||
"@com_google_protobuf//:protobuf",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_glog": ["@com_github_google_glog//:glog"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"@bazel_tools//src/conditions:darwin": [":macos_lib"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_with_boringssl": [
|
||||
"@boringssl//:ssl",
|
||||
"@boringssl//:crypto"
|
||||
],
|
||||
"//conditions:default": [
|
||||
"@openssl//:ssl",
|
||||
"@openssl//:crypto"
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "bvar",
|
||||
srcs = glob(
|
||||
[
|
||||
"src/bvar/*.cpp",
|
||||
"src/bvar/detail/*.cpp",
|
||||
],
|
||||
exclude = [
|
||||
"src/bvar/default_variables.cpp",
|
||||
],
|
||||
) + select({
|
||||
"//bazel/config:brpc_build_for_unittest": [],
|
||||
"//conditions:default": ["src/bvar/default_variables.cpp"],
|
||||
}),
|
||||
hdrs = glob([
|
||||
"src/bvar/*.h",
|
||||
"src/bvar/utils/*.h",
|
||||
"src/bvar/detail/*.h",
|
||||
]),
|
||||
defines = [] + select({
|
||||
"//bazel/config:with_babylon_counter": ["WITH_BABYLON_COUNTER=1"],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"//bazel/config:brpc_build_for_unittest": [
|
||||
"BVAR_NOT_LINK_DEFAULT_VARIABLES",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":butil",
|
||||
] + select({
|
||||
"//bazel/config:with_babylon_counter": ["@babylon//:concurrent_counter"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "bthread",
|
||||
srcs = glob([
|
||||
"src/bthread/*.cpp",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"src/bthread/*.h",
|
||||
"src/bthread/*.list",
|
||||
]),
|
||||
defines = [] + select({
|
||||
"//bazel/config:brpc_with_bthread_tracer": [
|
||||
"BRPC_BTHREAD_TRACER",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":butil",
|
||||
":bvar",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_bthread_tracer": [
|
||||
"@com_github_libunwind_libunwind//:libunwind",
|
||||
"@com_google_absl//absl/debugging:stacktrace",
|
||||
"@com_google_absl//absl/debugging:symbolize",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "json2pb",
|
||||
srcs = glob([
|
||||
"src/json2pb/*.cpp",
|
||||
]),
|
||||
hdrs = glob([
|
||||
"src/json2pb/*.h",
|
||||
]),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":butil",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "mcpack2pb",
|
||||
srcs = [
|
||||
"src/mcpack2pb/field_type.cpp",
|
||||
"src/mcpack2pb/mcpack2pb.cpp",
|
||||
"src/mcpack2pb/parser.cpp",
|
||||
"src/mcpack2pb/serializer.cpp",
|
||||
],
|
||||
hdrs = glob([
|
||||
"src/mcpack2pb/*.h",
|
||||
]),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":brpc_idl_options_cc_proto",
|
||||
":butil",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "brpc_idl_options_proto_srcs",
|
||||
srcs = [
|
||||
"src/idl_options.proto",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
brpc_proto_library(
|
||||
name = "brpc_idl_options_cc_proto",
|
||||
srcs = [":brpc_idl_options_proto_srcs"],
|
||||
include = "src",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "brpc_internal_proto_srcs",
|
||||
srcs = glob([
|
||||
"src/brpc/*.proto",
|
||||
"src/brpc/policy/*.proto",
|
||||
"src/brpc/rdma/*.proto",
|
||||
]),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
brpc_proto_library(
|
||||
name = "brpc_internal_cc_proto",
|
||||
srcs = [":brpc_internal_proto_srcs"],
|
||||
include = "src",
|
||||
deps = [":brpc_idl_options_cc_proto"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "brpc",
|
||||
srcs = glob([
|
||||
"src/brpc/*.cpp",
|
||||
"src/brpc/**/*.cpp",
|
||||
],
|
||||
exclude = [
|
||||
"src/brpc/thrift_service.cpp",
|
||||
"src/brpc/thrift_message.cpp",
|
||||
"src/brpc/policy/thrift_protocol.cpp",
|
||||
"src/brpc/event_dispatcher_epoll.cpp",
|
||||
"src/brpc/event_dispatcher_kqueue.cpp",
|
||||
]) + select({
|
||||
"//bazel/config:brpc_with_thrift": glob([
|
||||
"src/brpc/thrift*.cpp",
|
||||
"src/brpc/**/thrift*.cpp",
|
||||
]),
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
hdrs = glob([
|
||||
"src/brpc/*.h",
|
||||
"src/brpc/**/*.h",
|
||||
"src/brpc/event_dispatcher_epoll.cpp",
|
||||
"src/brpc/event_dispatcher_kqueue.cpp",
|
||||
]),
|
||||
copts = COPTS,
|
||||
includes = [
|
||||
"src/",
|
||||
],
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":brpc_internal_cc_proto",
|
||||
":bthread",
|
||||
":butil",
|
||||
":bvar",
|
||||
":json2pb",
|
||||
":mcpack2pb",
|
||||
"@com_github_google_leveldb//:leveldb",
|
||||
] + select({
|
||||
"//bazel/config:brpc_with_thrift": [
|
||||
"@org_apache_thrift//:thrift",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "protoc-gen-mcpack",
|
||||
srcs = [
|
||||
"src/mcpack2pb/generator.cpp",
|
||||
],
|
||||
copts = COPTS,
|
||||
linkopts = LINKOPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":brpc",
|
||||
":brpc_idl_options_cc_proto",
|
||||
"@com_google_protobuf//:protoc_lib",
|
||||
],
|
||||
)
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
<!--
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
-->
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- [0.9.7](#0.9.7)
|
||||
- [0.9.6](#0.9.6)
|
||||
- [0.9.5](#0.9.5)
|
||||
- [0.9.0](#0.9.0)
|
||||
|
||||
## 0.9.7
|
||||
* Add DISCLAIMER-WIP as license issues are not all resolved
|
||||
* Fix many license related issues
|
||||
* Ignore flow control in h2 when sending first request
|
||||
* Add flame graph view for profiling builtin service
|
||||
* Fix bug that _avg_latency maybe zero in lalb
|
||||
* Fix bug that logging namespace conflicts with others
|
||||
* Add gdb_bthread_stack.py to read bthread stack
|
||||
* Adapt to Arm64
|
||||
* Support redis server protocol
|
||||
* Enable circuit breaker for backup request
|
||||
* Support zone for bilibili discovery naming service when fetching server nodes
|
||||
* Add brpc revision in release version
|
||||
|
||||
## 0.9.6
|
||||
* Health (of a connection) can be checked at rpc-level
|
||||
* Fix SSL-related compilation issues on Mac
|
||||
* Support SSL-replacement lib MesaLink
|
||||
* Support consistent hashing with ketama algo.
|
||||
* bvar variables can be exported for prometheus services
|
||||
* String[Multi]Splitter supports '\0' as separator
|
||||
* Support for bilibili discovery service
|
||||
* Improved CircuitBreaker
|
||||
* grpc impl. supports timeout
|
||||
|
||||
## 0.9.5
|
||||
* h2c/grpc are supported now, h2(encrypted) is not included.
|
||||
* thrift support.
|
||||
* Mac build support
|
||||
* Extend ConcurrencyLimiter to control max-concurrency dynamically and an "auto" CL is supported by default
|
||||
* CircuitBreaker support to isolate abnormal servers more effectively
|
||||
|
||||
## 0.9.0
|
||||
* Contains major features of brpc, OK for production usages.
|
||||
* No h2/h2c/rdma support, Mac/Windows ports are not ready yet.
|
||||
@@ -0,0 +1,123 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Apache bRPC is an industrial-grade C++ RPC framework supporting multiple protocols (baidu_std, HTTP/H2, gRPC, thrift, redis, memcached, RTMP, RDMA) on the same port. Used in high-performance systems: search, storage, ML, ads, recommendations. Current version: 1.17.0.
|
||||
|
||||
## Build Commands
|
||||
|
||||
### Make (primary)
|
||||
|
||||
```bash
|
||||
# Generate config (required before first build)
|
||||
./config_brpc.sh --headers=/usr/include --libs=/usr/lib
|
||||
|
||||
# Build library (produces libbrpc.a and libbrpc.so/dylib)
|
||||
make -j$(nproc)
|
||||
|
||||
# Build debug version (with UNIT_TEST flag, no NDEBUG)
|
||||
make debug
|
||||
```
|
||||
|
||||
Config options: `--with-glog`, `--with-thrift`, `--with-rdma`, `--with-asan`, `--werror`
|
||||
|
||||
### CMake
|
||||
|
||||
```bash
|
||||
mkdir build && cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
Key CMake options: `-DWITH_GLOG=ON`, `-DWITH_THRIFT=ON`, `-DWITH_RDMA=ON`, `-DWITH_ASAN=ON`, `-DBUILD_UNIT_TESTS=ON`, `-DDOWNLOAD_GTEST=ON`
|
||||
|
||||
### Bazel
|
||||
|
||||
```bash
|
||||
bazel build -- //... -//example/...
|
||||
```
|
||||
|
||||
## Running Tests
|
||||
|
||||
Tests use Google Test. Build and run with Make:
|
||||
|
||||
```bash
|
||||
cd test
|
||||
make -j$(nproc)
|
||||
./run_tests.sh # runs all: test_butil, test_bvar, bthread_*unittest, brpc_*unittest
|
||||
```
|
||||
|
||||
Run a single test binary:
|
||||
|
||||
```bash
|
||||
cd test
|
||||
./<test_binary> # e.g. ./test_butil
|
||||
./<test_binary> --gtest_filter='TestSuite.TestName' # single test case
|
||||
```
|
||||
|
||||
With CMake:
|
||||
|
||||
```bash
|
||||
cmake .. -DBUILD_UNIT_TESTS=ON -DDOWNLOAD_GTEST=ON
|
||||
make -j$(nproc) && ctest
|
||||
```
|
||||
|
||||
ASAN is used in CI: `ASAN_OPTIONS="detect_leaks=0:detect_stack_use_after_return=1" ./<test_binary>`
|
||||
|
||||
## Architecture
|
||||
|
||||
### Core Libraries (under `src/`)
|
||||
|
||||
- **brpc/** — The RPC framework. Three core abstractions:
|
||||
- `Server` — listens on a port, dispatches requests to registered services. Supports multiple protocols on the same port via protocol detection.
|
||||
- `Channel` — client-side stub for sending RPCs. Configured with naming service, load balancer, timeout, retry policies via `ChannelOptions`.
|
||||
- `Controller` — per-RPC context carrying request metadata, error state, timeout, attachments. Extends `google::protobuf::RpcController`.
|
||||
- `Socket` (`socket.h`) — low-level connection abstraction managing fd lifecycle, SSL, and write buffering. Uses `VersionedRefWithId` for safe concurrent access.
|
||||
|
||||
- **bthread/** — M:N user-level threading (the concurrency foundation of brpc)
|
||||
- `TaskControl` manages a pool of worker pthreads, each running a `TaskGroup`
|
||||
- `TaskGroup` owns a local run queue + work-stealing queue (`work_stealing_queue.h`, lock-free CAS-based)
|
||||
- `bthread_start_urgent()` runs task immediately on current worker; `bthread_start_background()` enqueues for later scheduling
|
||||
- Most brpc callbacks execute in bthreads, not pthreads
|
||||
|
||||
- **butil/** — Base utility library (originally forked from Chromium)
|
||||
- `IOBuf` (`iobuf.h`) — zero-copy buffer using reference-counted blocks with SmallView (2 inline BlockRefs) / BigView (heap) optimization. Core data structure for network I/O.
|
||||
- Also: FlatMap, logging, string utils, time, files, containers
|
||||
- `third_party/` — Bundled snappy, murmurhash3, symbolize, etc.
|
||||
|
||||
- **bvar/** — Multi-dimensional statistics variables
|
||||
- Thread-local aggregation via `AgentCombiner` for lock-free counters
|
||||
- Types: `Adder`, `Recorder`, `LatencyRecorder`, `PassiveStatus`
|
||||
- Composable windows: `PerSecond<Adder<>>`, `Window<>`
|
||||
- Auto-exposed via builtin `/vars` endpoint
|
||||
|
||||
- **json2pb/** — Bidirectional JSON <-> Protobuf conversion
|
||||
|
||||
- **mcpack2pb/** — MCPack format <-> Protobuf conversion (Baidu legacy)
|
||||
|
||||
### Key Design Patterns
|
||||
|
||||
- **Protocol plugins** (`src/brpc/policy/`): Each protocol implements the `Protocol` struct (function pointers for Parse/Serialize/Pack/Process/Verify in `protocol.h`), registered via `RegisterProtocol()`. 18 protocols implemented. Adding a new protocol does not touch core files — see `docs/en/new_protocol.md`.
|
||||
- **Naming services / Load balancers**: Pluggable via `NamingService` and `LoadBalancer` interfaces (DNS, ZK, etcd, round-robin, consistent hashing, locality-aware, etc.)
|
||||
- **Builtin services** (`src/brpc/builtin/`): Every brpc server auto-exposes debug endpoints — `/status`, `/vars`, `/flags`, `/rpcz`, `/hotspots` (cpu/heap/contention profilers).
|
||||
- **Error model**: Functions return 0/-1 with errno; `Controller::SetFailed()` for RPC-level errors; custom error codes defined in `errno.proto`.
|
||||
- **Memory patterns**: `ResourcePool` / `ObjectPool` for socket and bthread recycling; `butil::intrusive_ptr` for hot-path reference counting; IOBuf for zero-copy I/O.
|
||||
|
||||
## Code Style
|
||||
|
||||
- Google C++ Style Guide with **4-space indentation**
|
||||
- Protocol-specific code goes in `src/brpc/policy/`, not in core files like `server.cpp` or `channel.cpp`
|
||||
- General modifications should not be hidden inside protocol-specific files
|
||||
- All changes require unit tests
|
||||
- CI runs on GitHub Actions (Linux gcc/clang, macOS) — must pass before merge
|
||||
- Uses C++11 minimum; some C++17 features with compiler guards. Legacy patterns (`DISALLOW_COPY_AND_ASSIGN` macro) still prevalent.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Required: protobuf (3.x–21.x), gflags, leveldb, openssl. Optional: glog, thrift, gperftools (tcmalloc/profiler), gtest (for tests), libunwind, abseil-cpp, BoringSSL (alternative to openssl).
|
||||
|
||||
## Examples
|
||||
|
||||
30+ examples in `example/` covering echo, HTTP, gRPC, streaming, redis, memcache, thrift, RDMA, coroutine, etc. Each has its own Makefile/CMakeLists.txt/BUILD.
|
||||
+654
@@ -0,0 +1,654 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.16...3.28)
|
||||
project(brpc C CXX)
|
||||
|
||||
option(WITH_GLOG "With glog" OFF)
|
||||
option(WITH_MESALINK "With MesaLink" OFF)
|
||||
option(WITH_BORINGSSL "With BoringSSL" OFF)
|
||||
option(DEBUG "Print debug logs" OFF)
|
||||
option(WITH_DEBUG_SYMBOLS "With debug symbols" ON)
|
||||
option(WITH_THRIFT "With thrift framed protocol supported" OFF)
|
||||
option(WITH_BTHREAD_TRACER "With bthread tracer supported" OFF)
|
||||
option(WITH_SNAPPY "With snappy" OFF)
|
||||
option(WITH_RDMA "With RDMA" OFF)
|
||||
option(WITH_DEBUG_BTHREAD_SCHE_SAFETY "With debugging bthread sche safety" OFF)
|
||||
option(WITH_DEBUG_LOCK "With debugging lock" OFF)
|
||||
option(WITH_ASAN "With AddressSanitizer" OFF)
|
||||
option(BUILD_UNIT_TESTS "Whether to build unit tests" OFF)
|
||||
option(BUILD_FUZZ_TESTS "Whether to build fuzz tests" OFF)
|
||||
option(BUILD_BRPC_TOOLS "Whether to build brpc tools" ON)
|
||||
option(DOWNLOAD_GTEST "Download and build a fresh copy of googletest. Requires Internet access." ON)
|
||||
|
||||
# Enable MACOSX_RPATH. Run "cmake --help-policy CMP0042" for policy details.
|
||||
if(POLICY CMP0042)
|
||||
cmake_policy(SET CMP0042 NEW)
|
||||
endif()
|
||||
|
||||
set(BRPC_VERSION 1.17.0)
|
||||
|
||||
SET(CPACK_GENERATOR "DEB")
|
||||
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "brpc authors")
|
||||
INCLUDE(CPack)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
# require at least gcc 4.8
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8)
|
||||
message(FATAL_ERROR "GCC is too old, please install a newer version supporting C++11")
|
||||
endif()
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# require at least clang 3.3
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.3)
|
||||
message(FATAL_ERROR "Clang is too old, please install a newer version supporting C++11")
|
||||
endif()
|
||||
else()
|
||||
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
|
||||
endif()
|
||||
|
||||
set(BRPC_COMMON_COMPILE_OPTIONS)
|
||||
set(BRPC_COMMON_DEFINITIONS)
|
||||
set(BRPC_COMMON_INCLUDE_DIRS
|
||||
${PROJECT_SOURCE_DIR}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
set(BRPC_COMMON_LINK_OPTIONS)
|
||||
set(BRPC_CXX_STANDARD 11)
|
||||
|
||||
set(WITH_GLOG_VAL "0")
|
||||
if(WITH_GLOG)
|
||||
set(WITH_GLOG_VAL "1")
|
||||
set(BRPC_WITH_GLOG 1)
|
||||
endif()
|
||||
|
||||
if(WITH_DEBUG_SYMBOLS)
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS -g)
|
||||
endif()
|
||||
|
||||
set(WITH_DEBUG_LOCK_VAL "0")
|
||||
if(WITH_DEBUG_LOCK)
|
||||
set(WITH_DEBUG_LOCK_VAL "1")
|
||||
endif()
|
||||
|
||||
if(WITH_THRIFT)
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS ENABLE_THRIFT_FRAMED_PROTOCOL)
|
||||
find_library(THRIFT_LIB NAMES thrift)
|
||||
if (NOT THRIFT_LIB)
|
||||
message(FATAL_ERROR "Fail to find Thrift")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WITH_BTHREAD_TRACER)
|
||||
if (NOT (CMAKE_SYSTEM_NAME STREQUAL "Linux") OR NOT (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64"))
|
||||
message(FATAL_ERROR "bthread tracer is only supported on Linux x86_64 platform")
|
||||
endif()
|
||||
find_path(LIBUNWIND_INCLUDE_PATH NAMES libunwind.h)
|
||||
find_library(LIBUNWIND_LIB NAMES unwind)
|
||||
find_library(LIBUNWIND_X86_64_LIB NAMES unwind-x86_64)
|
||||
if (NOT LIBUNWIND_INCLUDE_PATH OR NOT LIBUNWIND_LIB)
|
||||
message(FATAL_ERROR "Fail to find libunwind, which is needed by bthread tracer")
|
||||
endif()
|
||||
find_package(absl REQUIRED CONFIG)
|
||||
set(bthread_tracer_ABSL_USED_TARGETS absl::base absl::stacktrace absl::symbolize)
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS BRPC_BTHREAD_TRACER)
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${LIBUNWIND_INCLUDE_PATH})
|
||||
endif ()
|
||||
|
||||
set(WITH_RDMA_VAL "0")
|
||||
if(WITH_RDMA)
|
||||
set(WITH_RDMA_VAL "1")
|
||||
endif()
|
||||
|
||||
set(WITH_DEBUG_BTHREAD_SCHE_SAFETY_VAL "0")
|
||||
if(WITH_DEBUG_BTHREAD_SCHE_SAFETY)
|
||||
set(WITH_DEBUG_BTHREAD_SCHE_SAFETY_VAL "1")
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/config.h.in ${PROJECT_SOURCE_DIR}/src/butil/config.h @ONLY)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
|
||||
find_package(GFLAGS REQUIRED)
|
||||
|
||||
execute_process(
|
||||
COMMAND bash -c "${PROJECT_SOURCE_DIR}/tools/get_brpc_revision.sh ${PROJECT_SOURCE_DIR} | tr -d '\n'"
|
||||
OUTPUT_VARIABLE BRPC_REVISION
|
||||
)
|
||||
string(REPLACE "\\|" "|" BRPC_REVISION "${BRPC_REVISION}")
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
include(CheckFunctionExists)
|
||||
CHECK_FUNCTION_EXISTS(clock_gettime HAVE_CLOCK_GETTIME)
|
||||
if(NOT HAVE_CLOCK_GETTIME)
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS NO_CLOCK_GETTIME_IN_MAC)
|
||||
endif()
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS
|
||||
-Wno-deprecated-declarations
|
||||
-Wno-inconsistent-missing-override
|
||||
)
|
||||
endif()
|
||||
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS
|
||||
BRPC_WITH_GLOG=${WITH_GLOG_VAL}
|
||||
BRPC_WITH_RDMA=${WITH_RDMA_VAL}
|
||||
BRPC_DEBUG_BTHREAD_SCHE_SAFETY=${WITH_DEBUG_BTHREAD_SCHE_SAFETY_VAL}
|
||||
BRPC_DEBUG_LOCK=${WITH_DEBUG_LOCK_VAL}
|
||||
BTHREAD_USE_FAST_PTHREAD_MUTEX
|
||||
__const__=__unused__
|
||||
_GNU_SOURCE
|
||||
USE_SYMBOLIZE
|
||||
NO_TCMALLOC
|
||||
__STDC_FORMAT_MACROS
|
||||
__STDC_LIMIT_MACROS
|
||||
__STDC_CONSTANT_MACROS
|
||||
__STRICT_ANSI__
|
||||
)
|
||||
if(NOT DEBUG)
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS NDEBUG)
|
||||
endif()
|
||||
if(WITH_ASAN)
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS -fsanitize=address)
|
||||
list(APPEND BRPC_COMMON_LINK_OPTIONS -fsanitize=address)
|
||||
endif()
|
||||
if(WITH_MESALINK)
|
||||
list(APPEND BRPC_COMMON_DEFINITIONS USE_MESALINK)
|
||||
endif()
|
||||
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS
|
||||
-O2
|
||||
-pipe
|
||||
-Wall
|
||||
-W
|
||||
-fPIC
|
||||
-fstrict-aliasing
|
||||
-Wno-unused-parameter
|
||||
-fno-omit-frame-pointer
|
||||
"-DBRPC_REVISION=\"${BRPC_REVISION}\""
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-offsetof>
|
||||
)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
#required by butil/crc32.cc to boost performance for 10x
|
||||
if((CMAKE_SYSTEM_PROCESSOR MATCHES "(x86)|(X86)|(amd64)|(AMD64)") AND NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.4))
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-msse4>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-msse4.2>
|
||||
)
|
||||
elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64"))
|
||||
# segmentation fault in libcontext
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-fno-gcse>)
|
||||
elseif((CMAKE_SYSTEM_PROCESSOR MATCHES "riscv64"))
|
||||
# RISC-V specific optimizations
|
||||
option(WITH_RISCV_ZBC "Enable RISC-V Zbc carry-less multiplication for CRC32C acceleration" OFF)
|
||||
option(WITH_RISCV_ZVBC "Enable RISC-V Zvbc vector carry-less multiplication for CRC32C acceleration" OFF)
|
||||
if(WITH_RISCV_ZVBC)
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-march=rv64gcv_zbc_zvbc>)
|
||||
elseif(WITH_RISCV_ZBC)
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-march=rv64gc_zbc>)
|
||||
else()
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-march=rv64gc>)
|
||||
endif()
|
||||
endif()
|
||||
if(NOT (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7.0))
|
||||
list(APPEND BRPC_COMMON_COMPILE_OPTIONS $<$<COMPILE_LANGUAGE:CXX>:-Wno-aligned-new>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(Protobuf REQUIRED)
|
||||
if(Protobuf_VERSION VERSION_GREATER 4.21)
|
||||
# required by absl
|
||||
set(BRPC_CXX_STANDARD 17)
|
||||
|
||||
find_package(absl REQUIRED CONFIG)
|
||||
set(protobuf_ABSL_USED_TARGETS
|
||||
absl::absl_check
|
||||
absl::absl_log
|
||||
absl::algorithm
|
||||
absl::base
|
||||
absl::bind_front
|
||||
absl::bits
|
||||
absl::btree
|
||||
absl::cleanup
|
||||
absl::cord
|
||||
absl::core_headers
|
||||
absl::debugging
|
||||
absl::die_if_null
|
||||
absl::dynamic_annotations
|
||||
absl::flags
|
||||
absl::flat_hash_map
|
||||
absl::flat_hash_set
|
||||
absl::function_ref
|
||||
absl::hash
|
||||
absl::layout
|
||||
absl::log_initialize
|
||||
absl::log_globals
|
||||
absl::log_severity
|
||||
absl::memory
|
||||
absl::node_hash_map
|
||||
absl::node_hash_set
|
||||
absl::random_distributions
|
||||
absl::random_random
|
||||
absl::span
|
||||
absl::status
|
||||
absl::statusor
|
||||
absl::strings
|
||||
absl::synchronization
|
||||
absl::time
|
||||
absl::type_traits
|
||||
absl::utility
|
||||
absl::variant
|
||||
)
|
||||
endif()
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_path(LEVELDB_INCLUDE_PATH NAMES leveldb/db.h)
|
||||
find_library(LEVELDB_LIB NAMES leveldb)
|
||||
if ((NOT LEVELDB_INCLUDE_PATH) OR (NOT LEVELDB_LIB))
|
||||
message(FATAL_ERROR "Fail to find leveldb")
|
||||
endif()
|
||||
|
||||
if(WITH_SNAPPY)
|
||||
find_path(SNAPPY_INCLUDE_PATH NAMES snappy.h)
|
||||
find_library(SNAPPY_LIB NAMES snappy)
|
||||
if ((NOT SNAPPY_INCLUDE_PATH) OR (NOT SNAPPY_LIB))
|
||||
message(FATAL_ERROR "Fail to find snappy")
|
||||
endif()
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${SNAPPY_INCLUDE_PATH})
|
||||
endif()
|
||||
|
||||
if(WITH_GLOG)
|
||||
find_path(GLOG_INCLUDE_PATH NAMES glog/logging.h)
|
||||
find_library(GLOG_LIB NAMES glog)
|
||||
if((NOT GLOG_INCLUDE_PATH) OR (NOT GLOG_LIB))
|
||||
message(FATAL_ERROR "Fail to find glog")
|
||||
endif()
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${GLOG_INCLUDE_PATH})
|
||||
endif()
|
||||
|
||||
if(WITH_MESALINK)
|
||||
find_path(MESALINK_INCLUDE_PATH NAMES mesalink/openssl/ssl.h)
|
||||
find_library(MESALINK_LIB NAMES mesalink)
|
||||
if((NOT MESALINK_INCLUDE_PATH) OR (NOT MESALINK_LIB))
|
||||
message(FATAL_ERROR "Fail to find MesaLink")
|
||||
else()
|
||||
message(STATUS "Found MesaLink: ${MESALINK_LIB}")
|
||||
endif()
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${MESALINK_INCLUDE_PATH})
|
||||
endif()
|
||||
|
||||
if(WITH_RDMA)
|
||||
message("brpc compile with rdma")
|
||||
find_path(RDMA_INCLUDE_PATH NAMES infiniband/verbs.h)
|
||||
find_library(RDMA_LIB NAMES ibverbs)
|
||||
if((NOT RDMA_INCLUDE_PATH) OR (NOT RDMA_LIB))
|
||||
message(FATAL_ERROR "Fail to find ibverbs")
|
||||
endif()
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${RDMA_INCLUDE_PATH})
|
||||
endif()
|
||||
|
||||
find_library(PROTOC_LIB NAMES protoc)
|
||||
if(NOT PROTOC_LIB)
|
||||
message(FATAL_ERROR "Fail to find protoc lib")
|
||||
endif()
|
||||
|
||||
if(WITH_BORINGSSL)
|
||||
find_package(BoringSSL)
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${BORINGSSL_INCLUDE_DIR})
|
||||
else()
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND NOT OPENSSL_ROOT_DIR)
|
||||
set(OPENSSL_ROOT_DIR
|
||||
"/usr/local/opt/openssl" # Homebrew installed OpenSSL
|
||||
)
|
||||
endif()
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
list(APPEND BRPC_COMMON_INCLUDE_DIRS
|
||||
${GFLAGS_INCLUDE_PATH}
|
||||
${PROTOBUF_INCLUDE_DIRS}
|
||||
${LEVELDB_INCLUDE_PATH}
|
||||
)
|
||||
|
||||
set(DYNAMIC_LIB
|
||||
${GFLAGS_LIBRARY}
|
||||
${PROTOBUF_LIBRARIES} ${protobuf_ABSL_USED_TARGETS}
|
||||
${LEVELDB_LIB}
|
||||
${PROTOC_LIB}
|
||||
${CMAKE_THREAD_LIBS_INIT}
|
||||
${THRIFT_LIB}
|
||||
dl
|
||||
z)
|
||||
|
||||
if(WITH_BORINGSSL)
|
||||
list(APPEND DYNAMIC_LIB ${BORINGSSL_SSL_LIBRARY})
|
||||
list(APPEND DYNAMIC_LIB ${BORINGSSL_CRYPTO_LIBRARY})
|
||||
else()
|
||||
if(WITH_MESALINK)
|
||||
list(APPEND DYNAMIC_LIB ${OPENSSL_CRYPTO_LIBRARY})
|
||||
list(APPEND DYNAMIC_LIB ${MESALINK_LIB})
|
||||
else()
|
||||
list(APPEND DYNAMIC_LIB ${OPENSSL_SSL_LIBRARY})
|
||||
list(APPEND DYNAMIC_LIB ${OPENSSL_CRYPTO_LIBRARY})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_RDMA)
|
||||
list(APPEND DYNAMIC_LIB ${RDMA_LIB})
|
||||
endif()
|
||||
|
||||
set(BRPC_PRIVATE_LIBS "-lgflags -lprotobuf -lleveldb -lprotoc -lssl -lcrypto -ldl -lz")
|
||||
|
||||
if(WITH_GLOG)
|
||||
set(DYNAMIC_LIB ${GLOG_LIB} ${DYNAMIC_LIB})
|
||||
set(BRPC_PRIVATE_LIBS "-lglog ${BRPC_PRIVATE_LIBS}")
|
||||
endif()
|
||||
|
||||
if(WITH_SNAPPY)
|
||||
set(DYNAMIC_LIB ${DYNAMIC_LIB} ${SNAPPY_LIB})
|
||||
set(BRPC_PRIVATE_LIBS "${BRPC_PRIVATE_LIBS} -lsnappy")
|
||||
endif()
|
||||
|
||||
if (WITH_BTHREAD_TRACER)
|
||||
set(DYNAMIC_LIB ${DYNAMIC_LIB} ${LIBUNWIND_LIB} ${LIBUNWIND_X86_64_LIB} ${bthread_tracer_ABSL_USED_TARGETS})
|
||||
set(BRPC_PRIVATE_LIBS "${BRPC_PRIVATE_LIBS} -lunwind -lunwind-x86_64 -labsl_stacktrace -labsl_symbolize -labsl_debugging_internal -labsl_demangle_internal -labsl_malloc_internal -labsl_raw_logging_internal -labsl_spinlock_wait -labsl_base")
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(DYNAMIC_LIB ${DYNAMIC_LIB} rt)
|
||||
set(BRPC_PRIVATE_LIBS "${BRPC_PRIVATE_LIBS} -lrt")
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(DYNAMIC_LIB ${DYNAMIC_LIB}
|
||||
pthread
|
||||
"-framework CoreFoundation"
|
||||
"-framework CoreGraphics"
|
||||
"-framework CoreData"
|
||||
"-framework CoreText"
|
||||
"-framework Security"
|
||||
"-framework Foundation"
|
||||
"-Wl,-U,_MallocExtension_ReleaseFreeMemory"
|
||||
"-Wl,-U,_ProfilerStart"
|
||||
"-Wl,-U,_ProfilerStop"
|
||||
"-Wl,-U,__Z13GetStackTracePPvii"
|
||||
"-Wl,-U,_mallctl"
|
||||
"-Wl,-U,_malloc_stats_print"
|
||||
)
|
||||
endif()
|
||||
|
||||
list(REMOVE_DUPLICATES BRPC_COMMON_INCLUDE_DIRS)
|
||||
|
||||
add_library(brpc_common_config INTERFACE)
|
||||
target_include_directories(brpc_common_config INTERFACE ${BRPC_COMMON_INCLUDE_DIRS})
|
||||
target_compile_definitions(brpc_common_config INTERFACE ${BRPC_COMMON_DEFINITIONS})
|
||||
target_compile_options(brpc_common_config INTERFACE ${BRPC_COMMON_COMPILE_OPTIONS})
|
||||
target_compile_features(brpc_common_config INTERFACE cxx_std_${BRPC_CXX_STANDARD})
|
||||
if(BRPC_COMMON_LINK_OPTIONS)
|
||||
target_link_options(brpc_common_config INTERFACE ${BRPC_COMMON_LINK_OPTIONS})
|
||||
endif()
|
||||
|
||||
# for *.so
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/output/lib)
|
||||
# for *.a
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/output/lib)
|
||||
|
||||
# the reason why not using file(GLOB_RECURSE...) is that we want to
|
||||
# include different files on different platforms.
|
||||
set(BUTIL_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/dmg_fp/g_fmt.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/dmg_fp/dtoa_wrapper.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/dynamic_annotations/dynamic_annotations.c
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/icu/icu_utf.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/superfasthash/superfasthash.c
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/modp_b64/modp_b64.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/symbolize/demangle.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/symbolize/symbolize.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/snappy/snappy-sinksource.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/snappy/snappy-stubs-internal.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/snappy/snappy.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/third_party/murmurhash3/murmurhash3.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/arena.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/at_exit.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/atomicops_internals_x86_gcc.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/base64.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/base64url.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/big_endian.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/cpu.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/alias.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/asan_invalid_access.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/crash_logging.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/debugger.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/debugger_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/dump_without_crashing.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/proc_maps_linux.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/stack_trace.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/debug/stack_trace_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/environment.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_enumerator.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_enumerator_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_path.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_path_constants.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/memory_mapped_file.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/memory_mapped_file_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/scoped_file.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/scoped_temp_dir.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/file_util.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/file_util_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/guid.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/guid_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/hash.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/lazy_instance.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/location.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/memory/aligned_memory.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/memory/ref_counted.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/memory/ref_counted_memory.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/memory/singleton.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/memory/weak_ptr.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/posix/file_descriptor_shuffle.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/posix/global_descriptors.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/process_util.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/rand_util.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/rand_util_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/fast_rand.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/safe_strerror_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/sha1_portable.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/latin1_string_conversions.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/nullable_string16.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/safe_sprintf.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string16.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string_number_conversions.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string_split.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string_piece.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string_util.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/string_util_constants.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/stringprintf.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/utf_offset_string_conversions.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/utf_string_conversion_utils.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/utf_string_conversions.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/synchronization/cancellation_flag.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/synchronization/condition_variable_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/synchronization/waitable_event_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/non_thread_safe_impl.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/platform_thread_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/simple_thread.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_checker_impl.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_collision_warner.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_id_name_manager.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_local_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_local_storage.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_local_storage_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/thread_restrictions.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/watchdog.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/clock.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/default_clock.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/default_tick_clock.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/tick_clock.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/time.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/time_posix.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/version.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/logging.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/class_name.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/errno.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/find_cstr.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/status.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/string_printf.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/thread_local.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/thread_key.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/unix_socket.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/endpoint.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/fd_utility.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/temp_file.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/files/file_watcher.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/zero_copy_stream_as_streambuf.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/crc32c.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/containers/case_ignored_flat_map.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/iobuf.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/single_iobuf.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/iobuf_profiler.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/binary_printer.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/butil/recordio.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/popen.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(BUTIL_SOURCES ${BUTIL_SOURCES}
|
||||
${PROJECT_SOURCE_DIR}/src/butil/file_util_linux.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/platform_thread_linux.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/sys_string_conversions_posix.cc)
|
||||
elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||
set(BUTIL_SOURCES ${BUTIL_SOURCES}
|
||||
${PROJECT_SOURCE_DIR}/src/butil/mac/bundle_locations.mm
|
||||
${PROJECT_SOURCE_DIR}/src/butil/mac/foundation_util.mm
|
||||
${PROJECT_SOURCE_DIR}/src/butil/file_util_mac.mm
|
||||
${PROJECT_SOURCE_DIR}/src/butil/threading/platform_thread_mac.mm
|
||||
${PROJECT_SOURCE_DIR}/src/butil/strings/sys_string_conversions_mac.mm
|
||||
${PROJECT_SOURCE_DIR}/src/butil/time/time_mac.cc
|
||||
${PROJECT_SOURCE_DIR}/src/butil/mac/scoped_mach_port.cc)
|
||||
endif()
|
||||
|
||||
file(GLOB_RECURSE BVAR_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/bvar/*.cpp")
|
||||
file(GLOB_RECURSE BTHREAD_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/bthread/*.cpp")
|
||||
file(GLOB_RECURSE JSON2PB_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/json2pb/*.cpp")
|
||||
file(GLOB_RECURSE BRPC_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/*.cpp")
|
||||
file(GLOB_RECURSE THRIFT_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/thrift*.cpp")
|
||||
file(GLOB_RECURSE EXCLUDE_SOURCES CONFIGURE_DEPENDS "${PROJECT_SOURCE_DIR}/src/brpc/event_dispatcher_*.cpp")
|
||||
|
||||
if(WITH_THRIFT)
|
||||
message("brpc compile with thrift protocol")
|
||||
else()
|
||||
# Remove thrift sources
|
||||
foreach(v ${THRIFT_SOURCES})
|
||||
list(REMOVE_ITEM BRPC_SOURCES ${v})
|
||||
endforeach()
|
||||
set(THRIFT_SOURCES "")
|
||||
endif()
|
||||
|
||||
foreach(v ${EXCLUDE_SOURCES})
|
||||
list(REMOVE_ITEM BRPC_SOURCES ${v})
|
||||
endforeach()
|
||||
|
||||
set(MCPACK2PB_SOURCES
|
||||
${PROJECT_SOURCE_DIR}/src/mcpack2pb/field_type.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mcpack2pb/mcpack2pb.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mcpack2pb/parser.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mcpack2pb/serializer.cpp)
|
||||
|
||||
include(CompileProto)
|
||||
set(PROTO_FILES idl_options.proto
|
||||
brpc/rtmp.proto
|
||||
brpc/rpc_dump.proto
|
||||
brpc/get_favicon.proto
|
||||
brpc/span.proto
|
||||
brpc/builtin_service.proto
|
||||
brpc/grpc_health_check.proto
|
||||
brpc/get_js.proto
|
||||
brpc/errno.proto
|
||||
brpc/nshead_meta.proto
|
||||
brpc/options.proto
|
||||
brpc/policy/baidu_rpc_meta.proto
|
||||
brpc/policy/hulu_pbrpc_meta.proto
|
||||
brpc/policy/public_pbrpc_meta.proto
|
||||
brpc/policy/sofa_pbrpc_meta.proto
|
||||
brpc/policy/mongo.proto
|
||||
brpc/trackme.proto
|
||||
brpc/streaming_rpc_meta.proto
|
||||
brpc/proto_base.proto
|
||||
brpc/rdma/rdma_handshake.proto)
|
||||
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/output/include/brpc)
|
||||
set(PROTOC_FLAGS ${PROTOC_FLAGS} -I${PROTOBUF_INCLUDE_DIR})
|
||||
compile_proto(PROTO_HDRS PROTO_SRCS ${PROJECT_BINARY_DIR}
|
||||
${PROJECT_BINARY_DIR}/output/include
|
||||
${PROJECT_SOURCE_DIR}/src
|
||||
"${PROTO_FILES}")
|
||||
add_library(PROTO_LIB OBJECT ${PROTO_SRCS} ${PROTO_HDRS})
|
||||
target_link_libraries(PROTO_LIB PRIVATE brpc_common_config)
|
||||
|
||||
set(SOURCES
|
||||
${BVAR_SOURCES}
|
||||
${BTHREAD_SOURCES}
|
||||
${JSON2PB_SOURCES}
|
||||
${MCPACK2PB_SOURCES}
|
||||
${BRPC_SOURCES}
|
||||
${THRIFT_SOURCES}
|
||||
)
|
||||
|
||||
add_subdirectory(src)
|
||||
if(BUILD_UNIT_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
if(BUILD_FUZZ_TESTS)
|
||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
message(FATAL_ERROR "Fuzzing is only supported with clang")
|
||||
endif()
|
||||
if(NOT BUILD_UNIT_TESTS)
|
||||
message(FATAL_ERROR "BUILD_UNIT_TESTS must be enabled to build fuzz tests")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_BRPC_TOOLS)
|
||||
add_subdirectory(tools)
|
||||
endif()
|
||||
|
||||
file(COPY ${CMAKE_CURRENT_BINARY_DIR}/brpc/
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/output/include/brpc/
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
file(COPY ${PROJECT_SOURCE_DIR}/src/
|
||||
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/output/include/
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/output/include/
|
||||
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
||||
FILES_MATCHING
|
||||
PATTERN "*.h"
|
||||
PATTERN "*.hpp"
|
||||
)
|
||||
|
||||
# Install pkgconfig
|
||||
configure_file(cmake/brpc.pc.in ${PROJECT_BINARY_DIR}/brpc.pc @ONLY)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/brpc.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
@@ -0,0 +1,46 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at dev@brpc.apache.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
@@ -0,0 +1,29 @@
|
||||
If you meet any problem or request a new feature, you're welcome to [create an issue](https://github.com/apache/brpc/issues/new/choose).
|
||||
|
||||
If you can solve any of [the issues](https://github.com/apache/brpc/issues), you're welcome to send the PR to us.
|
||||
|
||||
Before the PR:
|
||||
|
||||
* Make sure your code style conforms to [google C++ coding style](https://google.github.io/styleguide/cppguide.html). Indentation is preferred to be 4 spaces.
|
||||
* The code appears where it should be. For example the code to support an extra protocol should not be put in general classes like server.cpp, channel.cpp, while a general modification would better not be hidden inside a very specific protocol.
|
||||
* Has unittests.
|
||||
|
||||
After the PR:
|
||||
|
||||
* Make sure the [GitHub Actions](https://github.com/apache/brpc/actions) passed.
|
||||
|
||||
# Chinese version
|
||||
|
||||
如果你遇到问题或需要新功能,欢迎[创建issue](https://github.com/apache/brpc/issues/new/choose)。
|
||||
|
||||
如果你可以解决某个[issue](https://github.com/apache/brpc/issues), 欢迎发送PR。
|
||||
|
||||
发送PR前请确认:
|
||||
|
||||
* 你的代码符合[google C++代码规范](https://google.github.io/styleguide/cppguide.html)。缩进最好为4个空格。
|
||||
* 代码出现的位置和其定位相符。比如对于某特定协议的扩展代码不该出现在server.cpp, channel.cpp这些较为通用的类中,而一些非常通用的改动也不该深藏在某个特定协议的cpp中。
|
||||
* 有对应的单测代码。
|
||||
|
||||
提交PR后请确认:
|
||||
|
||||
* [GitHub Actions](https://github.com/apache/brpc/actions)成功通过。
|
||||
@@ -0,0 +1,54 @@
|
||||
module(
|
||||
name = 'brpc',
|
||||
version = '1.17.0',
|
||||
compatibility_level = 1,
|
||||
)
|
||||
|
||||
# --registry=https://bcr.bazel.build
|
||||
bazel_dep(name = 'abseil-cpp', version = '20210324.2', repo_name = 'com_google_absl')
|
||||
bazel_dep(name = 'bazel_skylib', version = '1.0.3')
|
||||
bazel_dep(name = 'boringssl', version = '0.0.0-20211025-d4f1ab9')
|
||||
bazel_dep(name = 'protobuf', version = '27.3', repo_name = 'com_google_protobuf')
|
||||
bazel_dep(name = 'gflags', version = '2.2.2', repo_name = 'com_github_gflags_gflags')
|
||||
bazel_dep(name = 'glog', version = '0.5.0', repo_name = 'com_github_google_glog')
|
||||
bazel_dep(name = 'platforms', version = '0.0.4')
|
||||
bazel_dep(name = "apple_support", version = "1.22.1")
|
||||
bazel_dep(name = 'rules_cc', version = '0.0.1')
|
||||
bazel_dep(name = 'rules_proto', version = '4.0.0')
|
||||
bazel_dep(name = 'zlib', version = '1.3.1.bcr.5', repo_name = 'com_github_madler_zlib')
|
||||
bazel_dep(name = 'babylon', version = '1.4.4')
|
||||
# --registry=https://raw.githubusercontent.com/apache/brpc/master/registry
|
||||
# `registry/modules/libunwind/` (see the `--registry=https://raw.githubusercontent.com/apache/brpc/master/registry`
|
||||
# entry at the top of `.bazelrc`). The version suffix `.brpc-no-unwind` marks
|
||||
# this as brpc's self-maintained variant whose `src/unwind/*.c` (the GCC
|
||||
# `_Unwind_*` ABI compatibility layer) is gated by the
|
||||
# `--define=libunwind_hide_unwind_symbols=true` switch. Hiding those
|
||||
# `_Unwind_*` symbols is required for `--define=with_bthread_tracer=true`
|
||||
# to work without crashing in pthread_exit / exception unwinding.
|
||||
# See docs/cn/bthread_tracer.md for background.
|
||||
bazel_dep(name = 'libunwind', version = '1.8.1.brpc-no-unwind', repo_name = 'com_github_libunwind_libunwind')
|
||||
|
||||
# --registry=https://baidu.github.io/babylon/registry
|
||||
bazel_dep(name = 'leveldb', version = '1.23', repo_name = 'com_github_google_leveldb')
|
||||
single_version_override(
|
||||
module_name = "leveldb",
|
||||
registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main",
|
||||
)
|
||||
bazel_dep(name = 'openssl', version = '3.3.2')
|
||||
single_version_override(
|
||||
module_name = "openssl",
|
||||
version = "3.3.2.bcr.1",
|
||||
registry = "https://raw.githubusercontent.com/secretflow/bazel-registry/main",
|
||||
)
|
||||
bazel_dep(name = 'thrift', version = '0.21.0', repo_name = 'org_apache_thrift')
|
||||
|
||||
# test only
|
||||
bazel_dep(name = "gperftools", version = "2.18.1", dev_dependency = True)
|
||||
bazel_dep(name = "nlohmann_json", version = "3.12.0", dev_dependency = True)
|
||||
bazel_dep(name = 'googletest', version = '1.14.0.bcr.1', repo_name = 'com_google_googletest', dev_dependency = True)
|
||||
bazel_dep(name = 'hedron_compile_commands', dev_dependency = True)
|
||||
git_override(
|
||||
module_name = 'hedron_compile_commands',
|
||||
remote = 'https://github.com/hedronvision/bazel-compile-commands-extractor.git',
|
||||
commit = '1e08f8e0507b6b6b1f4416a9a22cf5c28beaba93', # Jun 28, 2024
|
||||
)
|
||||
@@ -0,0 +1,117 @@
|
||||
[中文版](README_cn.md)
|
||||
|
||||
[](https://github.com/apache/brpc/actions/workflows/ci-linux.yml)
|
||||
[](https://github.com/apache/brpc/actions/workflows/ci-macos.yml)
|
||||
|
||||

|
||||

|
||||
|
||||
[bRPC](https://brpc.apache.org/) is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc.
|
||||
|
||||
### "bRPC" means "better RPC".
|
||||
|
||||
You can use it to:
|
||||
* Build a server that can talk in multiple protocols (**on same port**), or access all sorts of services
|
||||
* restful http/https, [h2](https://httpwg.org/specs/rfc9113.html)/[gRPC](https://grpc.io). using http/h2 in bRPC is much more friendly than [libcurl](https://curl.haxx.se/libcurl/). Access protobuf-based protocols with HTTP/h2+json, probably from another language.
|
||||
* [redis](docs/en/redis_client.md) and [memcached](docs/en/memcache_client.md), thread-safe, more friendly and performant than the official clients.
|
||||
* [rtmp](https://github.com/apache/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), for building [streaming services](https://github.com/brpc/media-server).
|
||||
* hadoop_rpc (may be opensourced)
|
||||
* [rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access) support
|
||||
* [thrift](docs/en/thrift.md) support, thread-safe, more friendly and performant than the official clients.
|
||||
* all sorts of protocols used in Baidu: [baidu_std](docs/en/baidu_std.md), [streaming_rpc](docs/en/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc and nshead-based ones.
|
||||
* Build [HA](https://en.wikipedia.org/wiki/High_availability) distributed services using an industrial-grade implementation of [RAFT consensus algorithm](https://raft.github.io) which is opensourced at [braft](https://github.com/brpc/braft)
|
||||
* Servers can handle requests [synchronously](docs/en/server.md) or [asynchronously](docs/en/server.md#asynchronous-service).
|
||||
* Clients can access servers [synchronously](docs/en/client.md#synchronus-call), [asynchronously](docs/en/client.md#asynchronous-call), [semi-synchronously](docs/en/client.md#semi-synchronous-call), or use [combo channels](docs/en/combo_channel.md) to simplify sharded or parallel accesses declaratively.
|
||||
* Debug services [via http](docs/en/builtin_service.md), and run [cpu](docs/en/cpu_profiler.md), [heap](docs/en/heap_profiler.md) and [contention](docs/en/contention_profiler.md) profilers.
|
||||
* Get [better latency and throughput](docs/en/overview.md#better-latency-and-throughput).
|
||||
* [Extend bRPC](docs/en/new_protocol.md) with the protocols used in your organization quickly, or customize components, including [naming services](docs/en/load_balancing.md) (dns, zk, etcd), [load balancers](docs/en/load_balancing.md) (rr, random, consistent hashing)
|
||||
|
||||
# Try it!
|
||||
|
||||
* Read [overview](docs/en/overview.md) to know where bRPC can be used and its advantages.
|
||||
* Read [getting started](docs/en/getting_started.md) for building steps and play with [examples](https://github.com/apache/brpc/tree/master/example/).
|
||||
* Docs:
|
||||
* [Performance benchmark](docs/en/benchmark.md)
|
||||
* [bvar](docs/en/bvar.md)
|
||||
* [bvar_c++](docs/en/bvar_c++.md)
|
||||
* [bthread](docs/en/bthread.md)
|
||||
* [bthread or not](docs/en/bthread_or_not.md)
|
||||
* [thread-local](docs/en/thread_local.md)
|
||||
* [Execution Queue](docs/en/execution_queue.md)
|
||||
* [bthread tracer](docs/en/bthread_tracer.md)
|
||||
* [bthread tagged task group](docs/en/bthread_tagged_task_group.md)
|
||||
* Client
|
||||
* [Basics](docs/en/client.md)
|
||||
* [Error code](docs/en/error_code.md)
|
||||
* [Combo channels](docs/en/combo_channel.md)
|
||||
* [Access http/h2](docs/en/http_client.md)
|
||||
* [Access gRPC](docs/en/http_derivatives.md#h2grpc)
|
||||
* [Access thrift](docs/en/thrift.md#client-accesses-thrift-server)
|
||||
* [Access UB](docs/en/ub_client.md)
|
||||
* [Streaming RPC](docs/en/streaming_rpc.md)
|
||||
* [Access redis](docs/en/redis_client.md)
|
||||
* [Access memcached](docs/en/memcache_client.md)
|
||||
* [Backup request](docs/en/backup_request.md)
|
||||
* [Dummy server](docs/en/dummy_server.md)
|
||||
* Server
|
||||
* [Basics](docs/en/server.md)
|
||||
* [Serve http/h2](docs/en/http_service.md)
|
||||
* [Serve gRPC](docs/en/http_derivatives.md#h2grpc)
|
||||
* [Serve thrift](docs/en/thrift.md#server-processes-thrift-requests)
|
||||
* [Serve Nshead](docs/en/nshead_service.md)
|
||||
* [Debug server issues](docs/en/server_debugging.md)
|
||||
* [Server push](docs/en/server_push.md)
|
||||
* [Avalanche](docs/en/avalanche.md)
|
||||
* [Auto ConcurrencyLimiter](docs/en/auto_concurrency_limiter.md)
|
||||
* [Media Server](https://github.com/brpc/media-server)
|
||||
* [json2pb](docs/en/json2pb.md)
|
||||
* [Builtin Services](docs/en/builtin_service.md)
|
||||
* [status](docs/en/status.md)
|
||||
* [vars](docs/en/vars.md)
|
||||
* [connections](docs/en/connections.md)
|
||||
* [flags](docs/en/flags.md)
|
||||
* [rpcz](docs/en/rpcz.md)
|
||||
* [cpu_profiler](docs/en/cpu_profiler.md)
|
||||
* [heap_profiler](docs/en/heap_profiler.md)
|
||||
* [contention_profiler](docs/en/contention_profiler.md)
|
||||
* Tools
|
||||
* [rpc_press](docs/en/rpc_press.md)
|
||||
* [rpc_replay](docs/en/rpc_replay.md)
|
||||
* [rpc_view](docs/en/rpc_view.md)
|
||||
* [benchmark_http](docs/en/benchmark_http.md)
|
||||
* [parallel_http](docs/en/parallel_http.md)
|
||||
* Others
|
||||
* [IOBuf](docs/en/iobuf.md)
|
||||
* [Streaming Log](docs/en/streaming_log.md)
|
||||
* [FlatMap](docs/en/flatmap.md)
|
||||
* [Coroutine](docs/en/coroutine.md)
|
||||
* [Circuit Breaker](docs/en/circuit_breaker.md)
|
||||
* [RDMA](docs/en/rdma.md)
|
||||
* [Bazel Support](docs/en/bazel_support.md)
|
||||
* [Wireshark baidu_std dissector plugin](docs/en/wireshark_baidu_std.md)
|
||||
* [bRPC introduction](docs/cn/brpc_intro.pptx)(training material)
|
||||
* [A tutorial on building large-scale services](docs/en/tutorial_on_building_services.pptx)(training material)
|
||||
* [bRPC internal](docs/en/brpc_internal.pptx)(training material)
|
||||
* RPC in depth
|
||||
* [New Protocol](docs/en/new_protocol.md)
|
||||
* [Atomic instructions](docs/en/atomic_instructions.md)
|
||||
* [IO](docs/en/io.md)
|
||||
* [Threading Overview](docs/en/threading_overview.md)
|
||||
* [Load Balancing](docs/en/load_balancing.md)
|
||||
* [Locality-aware](docs/en/lalb.md)
|
||||
* [Consistent Hashing](docs/en/consistent_hashing.md)
|
||||
* [Memory Management](docs/en/memory_management.md)
|
||||
* [Timer keeping](docs/en/timer_keeping.md)
|
||||
* [bthread_id](docs/en/bthread_id.md)
|
||||
* Use cases
|
||||
* [User cases](community/cases.md)
|
||||
|
||||
# Contribute code
|
||||
Please refer to [here](CONTRIBUTING.md).
|
||||
|
||||
# Feedback and Getting involved
|
||||
* Report bugs, ask questions or give suggestions by [Github Issues](https://github.com/apache/brpc/issues)
|
||||
* Subscribe to the mailing list(dev-subscribe@brpc.apache.org) to get updated with the project
|
||||
|
||||
# Code of Conduct
|
||||
We follow the code of conduct from Apache Software Foundation, please refer it here [Link](https://www.apache.org/foundation/policies/conduct)
|
||||
@@ -0,0 +1,7 @@
|
||||
# WeHub 来源说明
|
||||
|
||||
- 原始项目:`apache/brpc`
|
||||
- 原始仓库:https://github.com/apache/brpc
|
||||
- 导入方式:上游默认分支的最新快照
|
||||
- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准
|
||||
- 本文件仅用于记录来源,不代表 WeHub 是原项目作者
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
[English version](README.md)
|
||||
|
||||
[](https://github.com/apache/brpc/actions/workflows/ci-linux.yml)
|
||||
[](https://github.com/apache/brpc/actions/workflows/ci-macos.yml)
|
||||
|
||||

|
||||

|
||||
|
||||
[bRPC](https://brpc.apache.org/)是用C++语言编写的工业级RPC框架,常用于搜索、存储、机器学习、广告、推荐等高性能系统。
|
||||
|
||||
### "bRPC"的含义是"better RPC"
|
||||
|
||||
你可以使用它:
|
||||
|
||||
* 搭建能在**一个端口**支持多协议的服务, 或访问各种服务
|
||||
* restful http/https, [h2](https://httpwg.org/specs/rfc9113.html)/[gRPC](https://grpc.io)。使用bRPC的http实现比[libcurl](https://curl.haxx.se/libcurl/)方便多了。从其他语言通过HTTP/h2+json访问基于protobuf的协议.
|
||||
* [redis](docs/cn/redis_client.md)和[memcached](docs/cn/memcache_client.md), 线程安全,比官方client更方便。
|
||||
* [rtmp](https://github.com/apache/brpc/blob/master/src/brpc/rtmp.h)/[flv](https://en.wikipedia.org/wiki/Flash_Video)/[hls](https://en.wikipedia.org/wiki/HTTP_Live_Streaming), 可用于搭建[流媒体服务](https://github.com/brpc/media-server).
|
||||
* hadoop_rpc(可能开源)
|
||||
* 支持[rdma](https://en.wikipedia.org/wiki/Remote_direct_memory_access)
|
||||
* 支持[thrift](docs/cn/thrift.md) , 线程安全,比官方client更方便
|
||||
* 各种百度内使用的协议: [baidu_std](docs/cn/baidu_std.md), [streaming_rpc](docs/cn/streaming_rpc.md), hulu_pbrpc, [sofa_pbrpc](https://github.com/baidu/sofa-pbrpc), nova_pbrpc, public_pbrpc, ubrpc和使用nshead的各种协议.
|
||||
* 基于工业级的[RAFT算法](https://raft.github.io)实现搭建[高可用](https://en.wikipedia.org/wiki/High_availability)分布式系统,已在[braft](https://github.com/brpc/braft)开源。
|
||||
* Server能[同步](docs/cn/server.md)或[异步](docs/cn/server.md#异步service)处理请求。
|
||||
* Client支持[同步](docs/cn/client.md#同步访问)、[异步](docs/cn/client.md#异步访问)、[半同步](docs/cn/client.md#半同步),或使用[组合channels](docs/cn/combo_channel.md)简化复杂的分库或并发访问。
|
||||
* [通过http界面](docs/cn/builtin_service.md)调试服务, 使用[cpu](docs/cn/cpu_profiler.md), [heap](docs/cn/heap_profiler.md), [contention](docs/cn/contention_profiler.md) profilers.
|
||||
* 获得[更好的延时和吞吐](docs/cn/overview.md#更好的延时和吞吐).
|
||||
* 把你组织中使用的协议快速地[加入bRPC](docs/cn/new_protocol.md),或定制各类组件, 包括[命名服务](docs/cn/load_balancing.md#命名服务) (dns, zk, etcd), [负载均衡](docs/cn/load_balancing.md#负载均衡) (rr, random, consistent hashing)
|
||||
|
||||
# 试一下!
|
||||
|
||||
* 通过[概述](docs/cn/overview.md)了解哪里可以用bRPC及其优势。
|
||||
* 阅读[编译步骤](docs/cn/getting_started.md)了解如何开始使用, 之后可以运行一下[示例程序](https://github.com/apache/brpc/tree/master/example/).
|
||||
* 文档:
|
||||
* [性能测试](docs/cn/benchmark.md)
|
||||
* [bvar](docs/cn/bvar.md)
|
||||
* [bvar_c++](docs/cn/bvar_c++.md)
|
||||
* [bthread](docs/cn/bthread.md)
|
||||
* [bthread or not](docs/cn/bthread_or_not.md)
|
||||
* [thread-local](docs/cn/thread_local.md)
|
||||
* [Execution Queue](docs/cn/execution_queue.md)
|
||||
* [bthread tracer](docs/cn/bthread_tracer.md)
|
||||
* [bthread tagged task group](docs/cn/bthread_tagged_task_group.md)
|
||||
* Client
|
||||
* [基础功能](docs/cn/client.md)
|
||||
* [错误码](docs/cn/error_code.md)
|
||||
* [组合channels](docs/cn/combo_channel.md)
|
||||
* [访问http/h2](docs/cn/http_client.md)
|
||||
* [访问gRPC](docs/cn/http_derivatives.md#h2grpc)
|
||||
* [访问thrift](docs/cn/thrift.md#client端访问thrift-server)
|
||||
* [访问UB](docs/cn/ub_client.md)
|
||||
* [Streaming RPC](docs/cn/streaming_rpc.md)
|
||||
* [访问redis](docs/cn/redis_client.md)
|
||||
* [访问memcached](docs/cn/memcache_client.md)
|
||||
* [Backup request](docs/cn/backup_request.md)
|
||||
* [Dummy server](docs/cn/dummy_server.md)
|
||||
* Server
|
||||
* [基础功能](docs/cn/server.md)
|
||||
* [搭建http/h2服务](docs/cn/http_service.md)
|
||||
* [搭建gRPC服务](docs/cn/http_derivatives.md#h2grpc)
|
||||
* [搭建thrift服务](docs/cn/thrift.md#server端处理thrift请求)
|
||||
* [搭建Nshead服务](docs/cn/nshead_service.md)
|
||||
* [高效率排查server卡顿](docs/cn/server_debugging.md)
|
||||
* [推送](docs/cn/server_push.md)
|
||||
* [雪崩](docs/cn/avalanche.md)
|
||||
* [自适应限流](docs/cn/auto_concurrency_limiter.md)
|
||||
* [流媒体服务](https://github.com/brpc/media-server)
|
||||
* [json2pb](docs/cn/json2pb.md)
|
||||
* [内置服务](docs/cn/builtin_service.md)
|
||||
* [status](docs/cn/status.md)
|
||||
* [vars](docs/cn/vars.md)
|
||||
* [connections](docs/cn/connections.md)
|
||||
* [flags](docs/cn/flags.md)
|
||||
* [rpcz](docs/cn/rpcz.md)
|
||||
* [cpu_profiler](docs/cn/cpu_profiler.md)
|
||||
* [heap_profiler](docs/cn/heap_profiler.md)
|
||||
* [contention_profiler](docs/cn/contention_profiler.md)
|
||||
* 工具
|
||||
* [rpc_press](docs/cn/rpc_press.md)
|
||||
* [rpc_replay](docs/cn/rpc_replay.md)
|
||||
* [rpc_view](docs/cn/rpc_view.md)
|
||||
* [benchmark_http](docs/cn/benchmark_http.md)
|
||||
* [parallel_http](docs/cn/parallel_http.md)
|
||||
* 其他
|
||||
* [IOBuf](docs/cn/iobuf.md)
|
||||
* [Streaming Log](docs/cn/streaming_log.md)
|
||||
* [FlatMap](docs/cn/flatmap.md)
|
||||
* [协程](docs/cn/coroutine.md)
|
||||
* [熔断](docs/cn/circuit_breaker.md)
|
||||
* [RDMA](docs/cn/rdma.md)
|
||||
* [Bazel构建支持](docs/cn/bazel_support.md)
|
||||
* [Wireshark baidu_std协议解析插件](docs/cn/wireshark_baidu_std.md)
|
||||
* [bRPC外功修炼宝典](docs/cn/brpc_intro.pptx)(培训材料)
|
||||
* [搭建大型服务入门](docs/en/tutorial_on_building_services.pptx)(培训材料)
|
||||
* [bRPC内功修炼宝典](docs/en/brpc_internal.pptx)(培训材料)
|
||||
* 深入RPC
|
||||
* [New Protocol](docs/cn/new_protocol.md)
|
||||
* [Atomic instructions](docs/cn/atomic_instructions.md)
|
||||
* [IO](docs/cn/io.md)
|
||||
* [Threading Overview](docs/cn/threading_overview.md)
|
||||
* [Load Balancing](docs/cn/load_balancing.md)
|
||||
* [Locality-aware](docs/cn/lalb.md)
|
||||
* [Consistent Hashing](docs/cn/consistent_hashing.md)
|
||||
* [Memory Management](docs/cn/memory_management.md)
|
||||
* [Timer keeping](docs/cn/timer_keeping.md)
|
||||
* [bthread_id](docs/cn/bthread_id.md)
|
||||
* Use cases inside Baidu
|
||||
* [百度地图api入口](docs/cn/case_apicontrol.md)
|
||||
* [联盟DSP](docs/cn/case_baidu_dsp.md)
|
||||
* [ELF学习框架](docs/cn/case_elf.md)
|
||||
* [云平台代理服务](docs/cn/case_ubrpc.md)
|
||||
|
||||
# 贡献代码
|
||||
|
||||
请参考[这里](CONTRIBUTING.md#chinese-version)。
|
||||
|
||||
# 反馈和参与
|
||||
|
||||
* bug、疑惑、修改建议都欢迎提在[Github Issues](https://github.com/apache/brpc/issues)中
|
||||
* 订阅邮件列表(dev-subscribe@brpc.apache.org)获得项目最新信息
|
||||
|
||||
|
||||
# 行为准则
|
||||
我们遵守Apache软件基金会的行为准则, 请参考如下 [链接](https://www.apache.org/foundation/policies/conduct)
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
# Security Policy
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
`apache/brpc` follows the [Apache Software Foundation security process](https://www.apache.org/security/). Please report suspected
|
||||
vulnerabilities privately to `security@apache.org`; do not open public
|
||||
GitHub issues or pull requests for security reports.
|
||||
|
||||
## Threat Model
|
||||
|
||||
What the project treats as in scope and out of scope, the security
|
||||
properties it provides and disclaims, the adversary model, and how
|
||||
findings are triaged are documented in [THREAT_MODEL.md](./THREAT_MODEL.md).
|
||||
+627
@@ -0,0 +1,627 @@
|
||||
# Apache bRPC — Threat Model (v1 draft)
|
||||
|
||||
## §1 Header
|
||||
|
||||
- **Project:** Apache bRPC (`apache/brpc`).
|
||||
- **Scope:** the `apache/brpc` repository only. The PMC confirmed on 2026-05-20 that `apache/brpc-website` is out of scope for this engagement. This draft is first authored by the ASF Security Team and then reviewed by the PMC.
|
||||
- **Version binding:** based on `master` around 2026-05-21. Vulnerability reports should be triaged against the model for the corresponding version, not against `HEAD`; re-bind on each release.
|
||||
- **Authors and status:** drafted by the ASF Security Team (Glasswing pre-scan), revised by PMC member Weibing Wang. **DRAFT v1**
|
||||
- **Reporting entry point:** at drafting time, the repository had no `SECURITY.md`, and the Apache security project index listed only the generic `security@apache.org` address. Until project-level disclosure documentation is published, report vulnerabilities to `security@apache.org` per ASF policy.
|
||||
- **Provenance legend:**
|
||||
- *(documented — source)*: from repository documentation, headers, gflags, source code, or Apache governance artifacts.
|
||||
- *(inferred — Qn)*: inferred from code structure, general RPC security experience, or absence of a defense, with a corresponding question in §14.
|
||||
- *(maintainer)*: not yet used in this draft; to be substituted after PMC confirmation.
|
||||
- **Draft confidence:** most factual descriptions in §1-§13 come from documentation and source code. Intent, default security posture, resource boundaries, the built-in service trust model, and vulnerability triage criteria still require confirmation in §14.
|
||||
- **bRPC overview:** bRPC is an embeddable C++ RPC framework that supports dispatching multiple protocols on the same port via content sniffing, including `baidu_std`, HTTP/1.x, HTTP/2/gRPC, Thrift, Redis, memcached, RTMP, Mongo, the nshead family, and others. It provides naming services, load balancers, optional TLS, bthread scheduling, and HTTP built-in admin services. bRPC is not a standalone daemon; downstream applications link `libbrpc` and start `brpc::Server` in-process.
|
||||
|
||||
---
|
||||
|
||||
## §2 Scope and intended use
|
||||
|
||||
bRPC's primary uses are:
|
||||
|
||||
1. Expose one or more `google::protobuf::Service` implementations on a TCP port, with multiple protocols sharing the same port.
|
||||
2. Act as a client `brpc::Channel` to access a single server or a cluster described by a naming service plus load balancer.
|
||||
3. Be embedded by a host application; deployment, TLS, network exposure, authentication, routing, and process lifecycle are the responsibility of the application or operator.
|
||||
4. Use built-in services such as `/status`, `/vars`, `/flags`, `/connections`, `/rpcz`, and `/health` for debugging and monitoring.
|
||||
5. Use bRPC as an HTTP/h2 server or client, including Restful URL mapping and JSON-to-Protobuf / Protobuf-to-JSON conversion.
|
||||
|
||||
bRPC is not a secure-by-default managed service. Threats land in the runtime code linked by the application and in the built-in admin services the application chooses to expose.
|
||||
|
||||
### Caller / actor roles
|
||||
|
||||
- **Application developer:** defines `.proto` files, implements services, chooses protocols and ports, and calls clients. Trusted within the compile-time and configuration boundary.
|
||||
- **Operator:** runs the binary and configures ports, `internal_port`, TLS, gflags, naming services, rate limiting, and built-in services. Trusted for that instance.
|
||||
- **Client peer (RPC client):** sends bytes to a bRPC server. Untrusted by default; protocol sniffers must handle arbitrary input.
|
||||
- **Server peer:** the remote server that a bRPC client connects to. Response bytes are also untrusted.
|
||||
- **Naming-service backend:** `bns`, `file`, `consul`, `nacos`, DNS, and similar backends. Configured by the operator and treated by bRPC as trusted infrastructure.
|
||||
- **Built-in admin-service consumer:** a person or program accessing `/status`, `/flags`, `/rpcz`, and similar endpoints. Its trust level depends entirely on whether the operator places these endpoints on an internal network or disables them.
|
||||
|
||||
### Component-family table
|
||||
|
||||
| Family | Representative entry point | Touches outside the process? | In model? |
|
||||
| --- | --- | --- | --- |
|
||||
| **C++ runtime core** | `Server::Start`, `Channel::CallMethod`, `Socket`, `InputMessenger` | sockets, files, threads, TLS, optional RDMA | **In model** |
|
||||
| **Wire-protocol parsers** | `Parse*Message` | reads untrusted bytes from sockets | **In model**, highest priority |
|
||||
| **HTTP / h2 (+ gRPC) stack** | `HttpServerHandler`, `H2StreamContext`, `URI` | sockets, TLS, json2pb | **In model** |
|
||||
| **Built-in admin services** | `/status`, `/vars`, `/flags`, `/rpcz`, `/dir`, `/threads`, profiler, metrics | HTTP, `/proc`, filesystem, profiler | **In model**, depending on exposure |
|
||||
| **TLS / SSL layer** | `ServerSSLOptions`, `ChannelSSLOptions` | OpenSSL, certificate files | **In model** |
|
||||
| **Authentication hooks** | `Authenticator::VerifyCredential` | wire token | **In model**, policy is implemented by the application |
|
||||
| **Naming services** | `file://`, `http(s)://`, `consul://`, `nacos://`, `dns://` | files and network | **In model**, backend treated as trusted |
|
||||
| **Load balancers** | rr, wrr, random, la, consistent hashing | pure computation | **In model**, not a direct attack surface |
|
||||
| **Compression layer** | gzip/snappy/zlib/lz4 | CPU/memory | **In model** |
|
||||
| **RDMA transport** | `rdma://` | verbs API, pinned memory | **In model** only when enabled at build time and runtime |
|
||||
| **Coroutine bridge** | `usercode_in_coroutine` | scheduling | **In model** in non-default mode |
|
||||
| **bthread / butil / bvar** | scheduler, IOBuf, counters | threads, futex, files, `/proc` | **In model** |
|
||||
| **mcpack2pb / json2pb** | mcpack/JSON <-> pb | pure computation | **In model** on the corresponding protocol paths |
|
||||
| **Java / Python bindings** | JNI / pyBRPC | language bridge | currently unsupported; **Out of model** |
|
||||
| **tools / test / example / build / community / docs** | CLI, tests, examples, build, governance, docs | non-production runtime surface | **Out of model** |
|
||||
|
||||
Wire-protocol parsers are the most important surface: they directly receive untrusted bytes, are implemented in C++, cover many protocols with different maturity levels, and multi-protocol sniffing can make every registered parser reachable from the same listening port. `enabled_protocols` can restrict the sniffing set, but its implementation details need maintainer confirmation.
|
||||
|
||||
---
|
||||
|
||||
## §3 Out of scope (explicit non-goals)
|
||||
|
||||
The following scenarios or threats are not protected by the bRPC threat model. Matching reports are closed according to §13.
|
||||
|
||||
### Use cases out of scope
|
||||
|
||||
1. **A secure-by-default RPC endpoint.** bRPC provides components, not a `brpcd` with a strong default security posture.
|
||||
2. **A sandbox or process-isolation boundary.** Deserialization, business handlers, and the host application run in the same process.
|
||||
3. **Cryptographic primitives.** TLS security comes from OpenSSL; bRPC only wraps and configures it.
|
||||
4. **An application field-validation framework.** bRPC validates wire framing, not semantic fields such as email addresses, IDs, or business ranges.
|
||||
5. **A complete replacement for authenticated transport.** Bare `baidu_std`, HTTP, Thrift, Redis, and similar protocols have no default authentication; `Authenticator` and mTLS require explicit configuration.
|
||||
6. **A general HTML/JSON rendering security surface.** Escaping in application HTTP handlers is the application's responsibility.
|
||||
7. **A general file server.** `/dir` is disabled by default and is not modeled as a production file-serving feature.
|
||||
8. **Exposing built-in services to the public internet.** bRPC built-in services should only be exposed to trusted internal consumers.
|
||||
|
||||
### Threats explicitly out of scope
|
||||
|
||||
1. Attackers who control the calling process, can read/write process memory, or can call arbitrary bRPC APIs.
|
||||
2. Attackers who control the compiler, dependencies, build environment, or supply chain.
|
||||
3. Side channels such as timing, cache, power, and RowHammer.
|
||||
4. Raw socket-layer DoS: SYN flood, slowloris, half-open connection exhaustion, and kernel table exhaustion.
|
||||
5. Resource exhaustion in `bvar`/`bthread`/`butil` caused purely by in-process call patterns.
|
||||
6. Performance loss caused by an operator intentionally enabling expensive tracing/profiling.
|
||||
7. Compromise of the naming-service backend; bRPC trusts the backend configured by the operator.
|
||||
8. Vulnerabilities in application handlers, such as SQL injection or missing business authorization.
|
||||
|
||||
### Code shipped in the repo but out of scope
|
||||
|
||||
`test/`, `example/`, `tools/`, build and packaging files, `community/`, `docs/`, and `apache/brpc-website` are not modeled as production runtime surfaces. The core boundary is the runtime, parsers, transports, built-in services, and support libraries under `src/`.
|
||||
|
||||
---
|
||||
|
||||
## §4 Trust boundaries and data flow
|
||||
|
||||
bRPC has four main trust boundaries: three on the network surface and one on the operator configuration and naming-service surface.
|
||||
|
||||
### Boundary 1 — the wire (server side, business RPC)
|
||||
|
||||
Server-side socket bytes are untrusted by default:
|
||||
|
||||
```text
|
||||
socket bytes
|
||||
-> Acceptor / Socket
|
||||
-> InputMessenger::CutInputMessage
|
||||
-> try registered protocol parsers one by one
|
||||
-> check whether declared body size exceeds -max_body_size
|
||||
-> protocol message structure
|
||||
-> protobuf / json2pb / mcpack2pb / other decoder
|
||||
-> Service::Method(controller, request, response, done)
|
||||
```
|
||||
|
||||
The trust transition occurs when the framework calls the user's `Service::Method`. Crashes or memory corruption reachable from the wire in parsers, decoders, compression, and TLS handshake are in model. How the business handler processes `Request` is the application's responsibility.
|
||||
|
||||
All protocol parsers are enabled by default. `ServerOptions.enabled_protocols` can restrict the protocol sniffing set.
|
||||
|
||||
### Boundary 2 — the wire (server side, built-in admin services)
|
||||
|
||||
HTTP/h2 requests on the same listening port may be routed to built-in services. `internal_port` can move built-in services to a separate port, and `has_builtin_services=false` can disable them entirely. `/dir` and `/threads` are disabled by default; other built-in services are enabled by default when `has_builtin_services=true`.
|
||||
|
||||
Model posture: built-in services are treated as an operator-trusted surface when placed on an internal port or protected by firewall. If exposed to the public internet, they become a major risk surface. Specific triage depends on Q46/Q50 in §14.
|
||||
|
||||
Even when built-in services are only served internally, they still need to avoid severe attacks such as command injection or denial of service. However, modifying gflags, enabling rpcz, profiling, and similar operations through built-in services can affect business logic or program performance; those effects are normal use and are not security vulnerabilities.
|
||||
|
||||
### Boundary 3 — the wire (client side)
|
||||
|
||||
A bRPC client deserializes server responses through paths symmetric to the server side: untrusted bytes enter parsers and decoders before being handed to the application. Parser/decoder vulnerabilities triggered by malicious server responses are in model.
|
||||
|
||||
### Boundary 4 — the operator config + naming-service backend
|
||||
|
||||
`ServerOptions`, `ChannelOptions`, gflags, flagfiles, TLS file paths, and naming-service URIs are supplied by the operator and are trusted. Server lists returned by naming services are also treated as trusted infrastructure. Backend compromise is out of scope, but crashes in reply parsers on malformed input remain in model.
|
||||
|
||||
### Reachability preconditions per family
|
||||
|
||||
- Parser issues are in model only when reachable from Boundary 1/3; opt-in protocols require operator enablement.
|
||||
- Built-in services are triaged according to the Boundary 2 exposure rules.
|
||||
- TLS issues are inside the network surface; OpenSSL CVEs are handled as external dependency issues.
|
||||
- Compression, json2pb, and mcpack2pb are in model when reachable from attacker input.
|
||||
- RDMA and coroutine paths are modeled only when explicitly enabled or loaded.
|
||||
|
||||
### Data not crossing a boundary
|
||||
|
||||
Purely internal scheduler queues, in-process `bvar` counters, internal `IOBuf` lifetimes, `socket_map` lifetimes, Wireshark dissectors under `tools/`, and similar paths are not trust transitions.
|
||||
|
||||
---
|
||||
|
||||
## §5 Assumptions about the environment
|
||||
|
||||
bRPC is a C++ library that normally runs on Linux/macOS with POSIX sockets, OpenSSL, and a C++ toolchain.
|
||||
|
||||
### OS / runtime / hardware
|
||||
|
||||
- Linux and macOS are supported; Windows is not included in the security support scope for now.
|
||||
- A C++11 or newer toolchain is required.
|
||||
- POSIX sockets and Linux epoll or macOS kqueue are required.
|
||||
- TLS depends on OpenSSL; risks from old OpenSSL versions are managed by the operator.
|
||||
- gflags, protobuf, and leveldb are regular dependencies; tcmalloc/gperftools is optional.
|
||||
|
||||
### Concurrency
|
||||
|
||||
- bthread is an M:N scheduler; the number of worker pthreads depends on CPU count or configuration.
|
||||
- `ServerOptions.num_threads` is a global worker-count hint, not hard isolation for one server.
|
||||
- bthread-local state may be reused; user destructors must be reentrant.
|
||||
- `-usercode_in_pthread` moves user code to pthreads and changes concurrency and queuing semantics.
|
||||
|
||||
### Memory model
|
||||
|
||||
bRPC is memory-unsafe C++. All `Parse*Message`, `Read*`, `Decompress*`, JSON/mcpack/protobuf decoder paths are candidate surfaces for OOB access, UAF, double-free, integer overflow, and stack overflow. `-max_body_size` is the key outer cap; the model depends on parsers correctly checking lengths and avoiding signed/unsigned overflow.
|
||||
|
||||
### Time / clock
|
||||
|
||||
bRPC uses `gettimeofday`, `clock_gettime`, `cpuwide_time_us`, and similar clocks. Resistance to timing side channels is not a goal.
|
||||
|
||||
### Filesystem / network / peripherals
|
||||
|
||||
bRPC opens TCP/Unix sockets, pid files, TLS cert/key files, naming-service files, profile output, rpc dumps, and reads `/proc/*` for bvar. It also sets normal socket options such as keepalive, `SO_REUSEPORT`, and `TCP_USER_TIMEOUT`.
|
||||
|
||||
### What bRPC does *not* do to its host (negative-claim inventory)
|
||||
|
||||
By default it does not install signal handlers. Normal operation does not fork child processes unless a profiler endpoint is triggered. It reads limited environment variables and gflags. First SSL use initializes OpenSSL global state. Logs go to stdout/stderr or glog. It does not modify locale or FPU state. `fork without exec` must happen before bRPC initialization.
|
||||
|
||||
---
|
||||
|
||||
## §5a Build-time and configuration variants
|
||||
|
||||
bRPC's security boundary is affected by build options, gflags, `ServerOptions`, `ChannelOptions`, and SSL options.
|
||||
|
||||
### Build-time options that change the security envelope
|
||||
|
||||
| Knob / flag | Default | Effect on model |
|
||||
| --- | --- | --- |
|
||||
| `WITH_THRIFT` | off | makes compiling and enabling the Thrift parser possible |
|
||||
| `WITH_GLOG` | off | changes the logging backend; not critical |
|
||||
| `WITH_RDMA` | off | introduces RDMA transport and pinned memory |
|
||||
| `WITH_MESALINK` | off | OpenSSL replacement implementation |
|
||||
| `BUILD_UNIT_TESTS` | off | builds tests, out of model |
|
||||
|
||||
*Note: the table above is only an example, not a complete list. Analyze the latest code for details.*
|
||||
|
||||
### Runtime gflags that change the security envelope
|
||||
|
||||
| Flag / option | Default | Effect | Maintainer stance |
|
||||
| --- | --- | --- | --- |
|
||||
| `-max_body_size` | 64 MiB | global body cap for all protocols; protobuf's internal cap is lifted to `INT_MAX` | critical |
|
||||
| `-enable_dir_service` | false | `/dir` reads files | dangerous when enabled |
|
||||
| `-enable_threads_service` | false | `/threads` dumps stacks | dangerous when enabled |
|
||||
| `-immutable_flags` | false | `/flags` can modify reloadable gflags | safer when enabled |
|
||||
| `-usercode_in_pthread` | false | changes the thread model for user code | changes concurrency semantics and may cause deadlocks |
|
||||
| `ServerOptions.has_builtin_services` | true | global switch for built-in services | critical |
|
||||
| `ServerOptions.internal_port` | -1 | built-in services are on the main port by default | critical; safer when set |
|
||||
|
||||
*Note: the table above is only an example, not a complete list. Analyze the latest code for details.*
|
||||
|
||||
### Insecure-default summary (the §13 routing question)
|
||||
|
||||
Defaults triaged by the PMC:
|
||||
|
||||
1. `-max_body_size=64 MiB` is the production default.
|
||||
2. `has_builtin_services=false` or `internal_port!=-1` is operator responsibility.
|
||||
3. The combination of `-immutable_flags=false` and reachable `/flags` is acceptable.
|
||||
4. `ChannelSSLOptions.verify.verify_mode=NOT_SET`, causing client TLS not to verify the server certificate, is acceptable.
|
||||
5. `ChannelSSLOptions.protocols` including TLS 1.0/1.1 is acceptable.
|
||||
6. `force_ssl=false`, causing a TLS-configured port to still accept plaintext, is acceptable.
|
||||
7. `/dir` and `/threads` are disabled by default. If an operator enables them and exposes them publicly, treat that as a dangerous non-default configuration.
|
||||
|
||||
*Note: the table above is only an example, not a complete list. Analyze the latest code for details.*
|
||||
|
||||
---
|
||||
|
||||
## §6 Assumptions about inputs
|
||||
|
||||
bRPC receives three classes of input: socket bytes, in-process API parameters, and operator configuration. Boundary 1/3 wire bytes are the highest risk.
|
||||
|
||||
### Per-parameter trust table
|
||||
|
||||
| Component / function | Parameter | Attacker-controllable? | Caller / operator must enforce |
|
||||
| --- | --- | --- | --- |
|
||||
| `InputMessenger::CutInputMessage` | initial connection bytes | yes | sniffer must defend against arbitrary bytes |
|
||||
| `ParseRpcMessage` | PRPC header, `RpcMeta`, payload | yes | `-max_body_size`, `meta_size <= body_size` |
|
||||
| `ParseHttpMessage` | method, URI, headers, body | yes | HTTP grammar, body cap |
|
||||
| `ParseH2Message` | frame, HPACK headers, SETTINGS | yes | frame/header/HPACK/SETTINGS caps |
|
||||
| Redis / memcache / Thrift / Mongo / nshead parsers, etc. | declared length, frame, body | yes | must be uniformly constrained by `-max_body_size` or protocol caps |
|
||||
| RTMP / AMF | chunk stream, AMF object | yes | chunk and nesting caps |
|
||||
| `json2pb::JsonToProtoMessage` | HTTP JSON body | yes | UTF-8, depth, repeated-field expansion |
|
||||
| `mcpack2pb` | mcpack body | yes | depth and size caps |
|
||||
| `Decompress` | compressed payload | yes | bRPC currently does not guarantee a decompressed-size cap; decompression may fail |
|
||||
| TLS handshake | cert, SNI, ALPN | yes | operator configures verify, SNI, ALPN |
|
||||
| `Authenticator::VerifyCredential` | credential, client_addr | yes; more obvious when header IP is enabled | application implements authentication; operator protects proxy boundary |
|
||||
| Built-in `/flags` | gflag value | yes if reachable | hide built-ins or set `-immutable_flags=true` |
|
||||
| Built-in `/dir` | path | yes if enabled | do not enable on public internet |
|
||||
| Built-in profiler | `seconds` and similar parameters | yes if reachable | hide built-ins and limit profiling cost |
|
||||
| `ServerOptions` / `ChannelOptions` / gflags | configuration values | no | operator trusted |
|
||||
| Naming service reply | backend response | trusted by transitivity | parser should still tolerate malformed replies |
|
||||
|
||||
### Size, shape, rate
|
||||
|
||||
- `-max_body_size=64 MiB` is the global message cap; there is no unified per-method or per-protocol cap.
|
||||
- The cap for unconsumed streaming RPC bytes is `-socket_max_streams_unconsumed_bytes`; default 0 means unlimited.
|
||||
- Pending write buffers per socket are limited by `-socket_max_unwritten_bytes`.
|
||||
- `max_concurrency` limits in-flight requests, not connection count; built-in services are not constrained by this option.
|
||||
- `idle_timeout_sec` is disabled by default; connection count and slowloris are mainly handled by operators / load balancers.
|
||||
- Protobuf has a recursion limit; JSON, mcpack, and AMF depth limits need confirmation.
|
||||
- The framework has no general rate limiting.
|
||||
|
||||
---
|
||||
|
||||
## §7 Adversary model
|
||||
|
||||
### Primary adversary — the wire peer
|
||||
|
||||
The primary attacker is the socket peer, which can send arbitrary bytes, confuse inputs across protocols, declare large lengths, construct compression bombs, trigger parser bugs, or make a client parse malicious server responses. Goals include crashes, memory/CPU exhaustion, OOB read/write, framing bypass, arbitrary command execution, authentication bypass, abuse of reachable built-in services, modification of reloadable gflags, and reading information through `/dir`.
|
||||
|
||||
### Secondary adversary — the authenticated peer
|
||||
|
||||
A peer that passes `Authenticator` is still untrusted at the parser layer. Authentication only narrows the attacker set; it does not change the peer's ability to send bytes.
|
||||
|
||||
### Tertiary adversary — the HTTP-built-in-services consumer
|
||||
|
||||
When `internal_port < 0` and `has_builtin_services=true`, any HTTP client that can reach the listening port may access `/status`, `/vars`, `/flags`, profiler, metrics, and similar endpoints. Final triage depends on Q46/Q50.
|
||||
|
||||
### Out-of-scope adversaries
|
||||
|
||||
In-process attackers, local side-channel attackers, attackers controlling `ServerOptions`/`ChannelOptions`, attackers controlling naming-service backends, build-chain attackers, and attacks that depend on the operator failing to harden network boundaries are not objects that bRPC itself promises to defend against.
|
||||
|
||||
### Adversary capabilities by transport
|
||||
|
||||
- **Plain TCP:** full wire control, no default authentication or encryption.
|
||||
- **TLS:** provides encryption, but client/server certificate verification and TLS-only behavior require operator configuration.
|
||||
- **RDMA:** modeled only when enabled; risks come from the verbs API and memory regions.
|
||||
- **Unix domain socket:** if supported, the trust model is similar to TCP, with socket file permissions managed by the operator.
|
||||
|
||||
---
|
||||
|
||||
## §8 Security properties the project provides
|
||||
|
||||
### Memory and process-safety properties
|
||||
|
||||
**P1. Under configured caps, valid wire input should not cause memory corruption.**
|
||||
Any OOB access, UAF, double-free, or heap corruption in a parser, decoder, decompressor, TLS path, or built-in handler reachable from Boundary 1/3 is a high/critical issue.
|
||||
|
||||
**P2. Bounded recursive structures should not cause stack overflow.**
|
||||
Protobuf, JSON, AMF, mcpack, and similar decoders should reject excessively deep input before exhausting the stack.
|
||||
|
||||
### Wire-format properties
|
||||
|
||||
**P3. Multi-protocol sniffing should be conservative.**
|
||||
When a parser returns `TRY_OTHERS`, it should not consume bytes; the same frame should not be taken over by the wrong parser.
|
||||
|
||||
**P4. `-max_body_size` should be enforced.**
|
||||
Parsers with declared lengths should reject and close the connection when the cap is exceeded; they should not allocate oversized memory first.
|
||||
|
||||
### TLS / transport-security properties (when enabled)
|
||||
|
||||
**P5. With SSL correctly configured and OpenSSL secure, TLS provides confidentiality and integrity.**
|
||||
|
||||
**P6. The server rejects SSLv3 by default.**
|
||||
|
||||
**P7. SNI can be used for certificate selection; fallback behavior on no match is controlled by `strict_sni`.**
|
||||
|
||||
**P8. ALPN selection should be limited to `ServerSSLOptions.alpns`.**
|
||||
|
||||
### Resource-bound properties
|
||||
|
||||
**P9. Single-message memory use should be bounded by `-max_body_size`.**
|
||||
Linear constant-factor overhead is acceptable; superlinear expansion or allocations that bypass the cap should be treated as bugs. See §9 D5 for decompression expansion.
|
||||
|
||||
**P10. Per-socket unwritten buffers should be limited by `-socket_max_unwritten_bytes`.**
|
||||
|
||||
**P11. Server/method concurrency caps should take effect when configured by the operator.**
|
||||
|
||||
### Concurrency properties
|
||||
|
||||
**P12. The framework's own cross-connection state should be thread-safe.** Synchronization of shared state in user handlers is the user's responsibility.
|
||||
|
||||
**P13. `Channel::CallMethod` may be called across threads; `Channel::Init()` is not guaranteed to be thread-safe.**
|
||||
|
||||
### Built-in admin-service properties (conditional on operator exposure)
|
||||
|
||||
**P14. Built-in services should be protected by `has_builtin_services`, `internal_port`, or network boundaries.**
|
||||
|
||||
**P15. `/dir` and `/threads` are disabled by default.**
|
||||
|
||||
---
|
||||
|
||||
## §9 Security properties the project does *not* provide
|
||||
|
||||
### Disclaimed properties (downstream's job, not bRPC's)
|
||||
|
||||
**D1.** Bare protocols do not have default peer authentication; `Authenticator` authenticates per connection, not per request.
|
||||
**D2.** No application-layer authorization is provided; RBAC, ACLs, scopes, and rate limits are implemented by the application or `Interceptor`.
|
||||
**D3.** When `-http_header_of_user_ip` is enabled, bRPC does not defend against attacker-forged headers; trusted proxies and firewalls must enforce the boundary.
|
||||
**D4.** Without a configured cap, streaming does not defend against unlimited growth of unconsumed stream data.
|
||||
**D5.** bRPC does not guarantee a decompressed-output size cap. Decompression failure is allowed, but crashes or memory boundary violations are not.
|
||||
**D6.** bRPC does not guarantee a cap on decoded JSON/repeated-field size. Decode failure is allowed, but crashes or memory boundary violations are not.
|
||||
**D7.** h2/HPACK bomb, CONTINUATION flood, and PING flood defenses are currently missing and should preferably be added.
|
||||
**D8.** No constant-time guarantee is provided.
|
||||
**D9.** OpenSSL weaknesses and default TLS version choices are managed by the operator.
|
||||
**D10.** No anti-replay is provided; request/sequence IDs are only used for request/response matching.
|
||||
**D11.** There is no confidentiality without TLS.
|
||||
**D12.** bRPC does not defend against socket-layer connection floods or slowloris.
|
||||
**D13.** When built-ins are exposed and `-immutable_flags=false`, bRPC does not defend against `/flags` modifying reloadable gflags.
|
||||
**D14.** When built-ins are reachable, bRPC does not defend against information disclosure from `/status`, `/vars`, `/connections`, `/rpcz`, and similar endpoints.
|
||||
**D15.** When built-ins are reachable, bRPC does not defend against CPU/IO cost from profiler endpoints.
|
||||
**D16.** When `internal_port=-1`, user services and built-in services on the same port are not isolated.
|
||||
**D17.** Cross-protocol confusion from the multi-protocol sniffer is a parser issue the framework must defend against, but the risk of not restricting the protocol set is the operator's responsibility.
|
||||
**D18.** Pathological protobuf input under protobuf `TotalBytesLimit=INT_MAX` has no additional framework cap.
|
||||
**D19.** When `force_ssl=false`, a TLS-configured port still accepts plaintext.
|
||||
|
||||
### False-friend properties
|
||||
|
||||
**F1.** `has_builtin_services=true` is an administrative surface, not just harmless debug pages.
|
||||
**F2.** `Authenticator::VerifyCredential` runs once per connection, not once per request.
|
||||
**F3.** `force_ssl=false` means the same port accepts both SSL and non-SSL.
|
||||
**F4.** `Controller::remote_side()` may be affected by `-http_header_of_user_ip`.
|
||||
**F5.** `verify_depth=0` means verification is disabled, not strict verification.
|
||||
**F6.** `Authenticator` does not cover all protocols by default.
|
||||
**F7.** `strict_sni=false` means an unknown SNI falls back to the default certificate.
|
||||
**F8.** `-max_body_size` does not limit decompressed size, decoded JSON size, or protobuf's internal cap.
|
||||
**F9.** The LZ4 enum and documented support status are not fully aligned and need confirmation.
|
||||
|
||||
### Well-known attack classes left to the caller
|
||||
|
||||
Compression bombs, JSON/repeated-field expansion, HPACK bombs, TLS compression oracles, TLS downgrade, authentication brute force, `/flags` weaponization, slow decoder DoS, HTTP request smuggling, cross-protocol confusion, and naming-service spoofing. Except for parser memory safety and explicit claims, most of these are mitigated by the operator or application.
|
||||
|
||||
---
|
||||
|
||||
## §10 Downstream responsibilities
|
||||
|
||||
### Application developer responsibilities
|
||||
|
||||
1. Pin the bRPC version and evaluate against that version's model.
|
||||
2. Treat all deserialized fields as untrusted application input and perform business-semantic validation.
|
||||
3. Implement `Authenticator` when identity is required, remembering that it authenticates per connection.
|
||||
4. Implement `Interceptor` or handler checks when per-request authorization is required.
|
||||
5. Do not call `Channel::Init()` concurrently across threads; `CallMethod()` is the thread-safe path.
|
||||
6. Do not cache gflag values long-term when running with reloadable flags.
|
||||
7. Do not use `assert` for security checks.
|
||||
|
||||
### Operator responsibilities
|
||||
|
||||
8. Hide built-in services on public ports: set `internal_port`, set `has_builtin_services=false`, or use a firewall.
|
||||
9. Keep `-enable_dir_service=false` and `-enable_threads_service=false` on public ports.
|
||||
10. Unless truly needed, set `-immutable_flags=true` or ensure `/flags` is reachable only by trusted operators.
|
||||
11. Configure TLS: server certificate, client verification, removal of TLS 1.0/1.1, and `force_ssl=true` when needed.
|
||||
12. Set `max_concurrency`, per-method concurrency, `idle_timeout_sec`, and streaming caps.
|
||||
13. When enabling `-http_header_of_user_ip`, allow only trusted proxies to access the backend port.
|
||||
14. Do not run bRPC processes as root.
|
||||
15. Use escaping when application HTTP handlers output HTML.
|
||||
16. Put public endpoints behind a load balancer / reverse proxy for connection-rate control, slowloris handling, and L7 hardening.
|
||||
17. Track OpenSSL patches and rotate TLS material separately.
|
||||
18. If compression is enabled, limit decompressed size at the application layer or sidecar.
|
||||
19. Use `enabled_protocols` to expose only required protocols.
|
||||
20. Choose trusted naming-service backends and protect permissions on flagfiles, cert/key files, and pid files.
|
||||
|
||||
### Both
|
||||
|
||||
21. Re-read and revise this model on each major bRPC upgrade or whenever a §12 condition occurs.
|
||||
|
||||
---
|
||||
|
||||
## §11 Known misuse patterns
|
||||
|
||||
**M1.** Exposing a server with default `has_builtin_services=true` and `internal_port=-1` to the public internet.
|
||||
**M2.** Treating `Authenticator` as per-request authentication.
|
||||
**M3.** Enabling `-http_header_of_user_ip` while allowing clients to bypass the trusted proxy and connect directly.
|
||||
**M4.** Calling `Channel::Init()` from multiple threads.
|
||||
**M5.** Using `assert` for security checks.
|
||||
**M6.** Running bRPC as root.
|
||||
**M7.** Configuring SSL but forgetting `force_ssl=true`, leaving plaintext available.
|
||||
**M8.** Using plaintext protocols across trust boundaries.
|
||||
**M9.** Setting `-max_body_size` very large and assuming it will not affect memory.
|
||||
**M10.** Assuming compressed output size is limited by the framework.
|
||||
**M11.** Enabling `/dir` or `/threads` on the public internet.
|
||||
**M12.** Enabling nshead/nova/public/nshead_mcpack and forgetting the sniffer will try these parsers.
|
||||
**M13.** Treating sequence/correlation IDs as nonces.
|
||||
**M14.** Exposing `/flags` while keeping `-immutable_flags=false`.
|
||||
**M15.** Not revalidating `auth_context()` or downstream identity information.
|
||||
**M16.** Combining `-usercode_in_pthread` with high concurrency without setting `max_concurrency`.
|
||||
**M17.** Using an untrusted naming service while client TLS does not verify the server certificate.
|
||||
**M18.** Exposing profiler endpoints to the public internet.
|
||||
**M19.** Enabling `/rpcz` on traffic containing sensitive request fields.
|
||||
**M20.** Enabling RDMA and exposing it to untrusted peers.
|
||||
|
||||
---
|
||||
|
||||
## §11a Known non-findings (recurring false positives)
|
||||
|
||||
**N1.** A parser reading length and checking it with `FLAGS_max_body_size` is by design; it is an issue only if a parser truly skips the check.
|
||||
**N2.** ~~Decompression has no decompressed-size cap, per §9 D5.~~
|
||||
**N3.** Built-in services expose internal information; operators should hide them.
|
||||
**N4.** `/flags` modifying reloadable gflags without authentication is pending Q46.
|
||||
**N5.** `/dir` reads files: disabled by default; enabling it is a dangerous non-default configuration.
|
||||
**N6.** `/threads` dumps stacks: disabled by default.
|
||||
**N7.** bvar reading `/proc/loadavg` and `/proc/stat` is by design.
|
||||
**N8.** Client TLS includes TLS 1.0/1.1 by default; triage pending Q56.
|
||||
**N9.** Client TLS does not verify server certificates by default; triage pending Q57.
|
||||
**N10.** `force_ssl=false` allowing plaintext is documented behavior.
|
||||
**N11.** `Authenticator` running once per connection is documented behavior.
|
||||
**N12.** Issues in test/example/tools/build/community are out of model.
|
||||
**N13.** Hard-coded certificates under `test/` are out of model.
|
||||
**N14.** HTTP Basic credentials in plaintext are a consequence of the operator choosing plaintext transport.
|
||||
**N15.** MD5/SHA-1 used for load-balancer hash distribution is not cryptographic use.
|
||||
**N16.** Wire-reachable integer overflow/OOB is a valid vulnerability class and should not be mechanically closed.
|
||||
**N17.** `session_local_data_factory` lifetime is guaranteed by the user as required by documentation.
|
||||
**N18.** The bthread keytable pool retaining objects is a reuse design.
|
||||
**N19.** `Channel.Init()` being non-thread-safe is documented behavior.
|
||||
**N20.** Runtime certificate add/delete/modify concurrency safety is judged case by case and needs maintainer confirmation.
|
||||
**N21.** Built-ins being handled on the main port is default behavior; triage pending Q50.
|
||||
**N22.** The sniffer trying multiple parsers on unknown bytes is by design.
|
||||
**N23.** A fuzz crash in an opt-in parser can still be `VALID` when the path is enabled.
|
||||
**N24.** Protobuf `TotalBytesLimit=INT_MAX` is documented design; the outer layer relies on `-max_body_size`.
|
||||
**N25.** Naming-service backend replies are trusted.
|
||||
**N26.** Triage for crashes on malformed DNS/consul/nacos replies must distinguish backend compromise from parser hardening.
|
||||
**N27.** Brute-force authentication using many short connections is rate-limited by the operator.
|
||||
**N28.** `/rpcz` may expose sampled request information; this is a design risk after enabling it.
|
||||
**N29.** OpenSSL CVEs are not bRPC CVEs, but they affect deployments.
|
||||
**N30.** `fork without exec` after bRPC initialization is unsupported.
|
||||
|
||||
---
|
||||
|
||||
## §12 Conditions that would change this model
|
||||
|
||||
The following changes should trigger model revision:
|
||||
|
||||
1. A new wire protocol parser or sniffer registration.
|
||||
2. A new built-in admin service, especially one with write capability.
|
||||
3. Changes to security-relevant defaults in §5a: `-max_body_size`, streaming caps, `-immutable_flags`, built-in defaults, `force_ssl`, TLS verification, TLS protocols, and so on.
|
||||
4. An opt-in protocol becoming enabled by default.
|
||||
5. Adding a decompressed-size cap.
|
||||
6. Adding h2/HPACK bomb defenses.
|
||||
7. Adding a per-request authentication mode.
|
||||
8. Adding `SECURITY.md` or a project security page to the repository.
|
||||
9. The PMC publishing a security policy.
|
||||
10. A new CVE class that cannot be triaged under §13.
|
||||
|
||||
Vulnerabilities should be triaged against the model in effect for the affected version, not against the latest `HEAD`.
|
||||
|
||||
---
|
||||
|
||||
## §13 Triage dispositions
|
||||
|
||||
| Disposition | Meaning | Licensed by |
|
||||
| --- | --- | --- |
|
||||
| `VALID` | violates a property claimed in §8, with attacker, input, and component all in model | §8, §6, §7 |
|
||||
| `VALID-HARDENING` | does not violate an existing claim, but the API easily leads to §11 misuse and the project may choose to harden it | §11 |
|
||||
| `OUT-OF-MODEL: trusted-input` | requires controlling configuration or objects marked trusted by the model | §6 |
|
||||
| `OUT-OF-MODEL: adversary-not-in-scope` | requires attacker capabilities excluded by the model | §7, §3 |
|
||||
| `OUT-OF-MODEL: unsupported-component` | located in out-of-scope components such as test/tools/example/build/community/docs | §3 |
|
||||
| `OUT-OF-MODEL: non-default-build` | appears only under dangerous or non-default configuration | §5a |
|
||||
| `BY-DESIGN: property-disclaimed` | belongs to a property explicitly disclaimed in §9 | §9 |
|
||||
| `KNOWN-NON-FINDING` | matches a recurring false-positive pattern in §11a | §11a |
|
||||
| `MODEL-GAP` | cannot be classified and requires model revision | §12 |
|
||||
|
||||
### Worked routing examples
|
||||
|
||||
- `Parse*Message` OOB write: `VALID`.
|
||||
- Private key leak under `test/`: `OUT-OF-MODEL: unsupported-component`.
|
||||
- Client TLS not verifying server certificates by default: `BY-DESIGN`.
|
||||
- Remote access to `/flags/max_body_size?setvalue=0`: operator responsibility.
|
||||
- `/dir?path=/etc/passwd`: if it requires `-enable_dir_service=true`, `OUT-OF-MODEL: non-default-build`.
|
||||
- A small gzip payload decompressing to huge output and causing OOM: `VALID`.
|
||||
- h2 HPACK bomb: if defenses are missing and reachable, potentially `VALID`.
|
||||
- Hostile consul backend redirecting a client: `OUT-OF-MODEL: adversary-not-in-scope`; malformed reply crashes can be judged separately.
|
||||
- OpenSSL CVE: `BY-DESIGN: property-disclaimed`.
|
||||
- `Channel.Init()` data race: `BY-DESIGN`, because documentation says it is not thread-safe.
|
||||
|
||||
---
|
||||
|
||||
## §14 Open questions for the maintainers
|
||||
|
||||
Each question includes the draft recommendation and awaits PMC confirmation, correction, or deletion. After confirmation, the corresponding *(inferred)* tags in the body should be updated to *(maintainer)*.
|
||||
|
||||
---
|
||||
|
||||
### Wave 1 — scope, adversary, and the insecure-default rulings (must answer first)
|
||||
|
||||
**Q1.** Is the server-side wire peer untrusted by default, so the runtime cannot assume input matches any registered protocol? Yes.
|
||||
**Q2.** Is client-side deserialization of server responses in the same model scope as server-side handling of client requests? Yes.
|
||||
**Q40.** Is `-max_body_size=64 MiB` a supported production default, or a baseline that operators must lower per deployment? Production default.
|
||||
**Q50.** `has_builtin_services=true` + `internal_port=-1` puts built-ins on the main port by default; how should public exposure of `/flags`/`/version` be triaged? Operator responsibility.
|
||||
**Q46.** `-immutable_flags=false` allows reachable `/flags` to modify reloadable gflags; should the default be changed to true? No, operator responsibility.
|
||||
**Q57.** Client TLS does not verify server certificates by default; should successful MITM be `BY-DESIGN` or `VALID`/`VALID-HARDENING`? `BY-DESIGN`.
|
||||
**Q56.** Is it acceptable that `ChannelSSLOptions.protocols` includes TLS 1.0/1.1 by default? `BY-DESIGN`.
|
||||
**Q51.** Is `force_ssl=false`, which makes a TLS port still accept plaintext, only operator responsibility? Operator responsibility.
|
||||
|
||||
### Wave 2 — what the runtime does (and does not do) to its host
|
||||
|
||||
**Q9.** Is bRPC explicitly an embedded library rather than a secure-by-default daemon? Yes.
|
||||
**Q10.** Does the user handler run in the same process as the deserializer, with no sandbox provided by bRPC? Yes.
|
||||
**Q11.** Are TLS/SSL cryptographic guarantees entirely inherited from OpenSSL? Yes.
|
||||
**Q12.** Does bRPC validate only wire framing, not application field semantics? Yes.
|
||||
**Q24.** Is Windows outside the security support environment? Yes.
|
||||
**Q28.** Do maintainers agree that parser/read/decompress paths are memory-unsafe C++ attack surfaces? Yes.
|
||||
|
||||
### Wave 3 — wire-protocol parser surface (per-protocol hardening parity)
|
||||
|
||||
**Q8.** Is the list of default-on, opt-in, and client-only protocols accurate? Yes.
|
||||
**Q37.** Should `enabled_protocols` ensure parsers not listed are never tried? Yes.
|
||||
**Q22.** Should vulnerabilities in opt-in parsers be triaged as `OUT-OF-MODEL: non-default-build` when not enabled? Parsers are all enabled by default, so triage as `VALID`.
|
||||
**Q80.** Do all parsers with length framing uniformly check `FLAGS_max_body_size`? Yes.
|
||||
**Q59.** Does h2/HPACK have defenses against bomb, CONTINUATION flood, PING flood, and SETTINGS flood? Preferably yes.
|
||||
**Q68.** Does `mcpack2pb` have recursion/depth caps? Preferably yes.
|
||||
**Q69.** Does `json2pb` have a JSON depth cap? Preferably yes.
|
||||
|
||||
### Wave 4 — built-in admin services trust model
|
||||
|
||||
**Q4.** Is the trust status of built-in admin consumers determined entirely by operator exposure? Yes.
|
||||
**Q19.** Should public access to default built-ins such as `/version` be triaged as operator responsibility? Yes.
|
||||
**Q45.** Because `/dir` and `/threads` are disabled by default, should issues after public enablement be triaged as non-default-build? Yes.
|
||||
**Q71.** Do profiler endpoint `seconds` parameters have reasonable upper bounds? How should DoS after exposure be triaged? Preferably yes.
|
||||
**Q87.** Is there no isolation between built-in services, so a bug in one handler can affect other services in the same process? This is an issue.
|
||||
|
||||
### Wave 5 — TLS / SSL / authentication
|
||||
|
||||
**Q52.** Is `strict_sni=false` fallback to the default certificate a supported posture? Yes.
|
||||
**Q54.** Is server-side mTLS being disabled by default a supported posture? Yes.
|
||||
**Q90.** Which protocols does `Authenticator` actually cover? Does it not cover Thrift/Redis/memcache/Mongo/RTMP/nshead by default? Yes.
|
||||
**Q77.** Is an authenticated peer still an untrusted adversary at the parser layer? Yes.
|
||||
**Q47.** Does `-http_header_of_user_ip` have no built-in trusted-proxy verification? Yes; this is not an issue.
|
||||
|
||||
### Wave 6 — resource bounds, compression, streaming
|
||||
|
||||
**Q42.** Should streaming unconsumed bytes being unlimited by default change default behavior or require explicit operator configuration? Yes.
|
||||
**Q70.** Is it confirmed that bRPC has no decompressed-output size cap? Should `-max_decompressed_size` be added? Preferably yes.
|
||||
**Q73.** Is it true that there is no per-protocol/per-method body cap and only global `-max_body_size`? There is indeed none, and this is acceptable.
|
||||
**Q74.** Is there no framework-level connection-count cap? None.
|
||||
**Q82.** Can the resource policy be stated as: superlinear memory growth over the wire-declared size is a bug when constrained by `-max_body_size`? Yes.
|
||||
**Q93.** Has the HTTP parser been audited for request smuggling defenses? Yes.
|
||||
|
||||
### Wave 7 — concurrency, properties, and remaining open items
|
||||
|
||||
**Q5.** Does RDMA require explicit build-time and runtime enablement, with related vulnerabilities out of model when not enabled? Yes.
|
||||
**Q7.** Are `test/`, `example/`, `tools/`, and `community/` out of model? Yes.
|
||||
**Q18.** Do wire bytes remain untrusted until the user's `Service::Method` is called? Yes.
|
||||
**Q20.** Is client response parsing modeled equivalently to server request parsing? Yes.
|
||||
**Q23.** Can crashes on malformed naming-service replies be `VALID`, while malicious backend redirection is out of model? Yes.
|
||||
**Q72.** Should consul/nacos and similar naming-service parsers defend against malformed input? Yes.
|
||||
**Q79.** Does bRPC have continuous fuzzing/OSS-Fuzz; if not, does P1 mainly rely on code review and CI? No.
|
||||
**Q83.** Under the per-connection bthread model, is synchronization of application shared state entirely the user's responsibility? Yes.
|
||||
**Q84.** Is per-request authorization implemented by `Interceptor` or the handler, not `Authenticator`? Yes.
|
||||
**Q88.** Should cross-protocol sniffer confusion be handled as a framework `VALID` issue? Yes.
|
||||
|
||||
---
|
||||
|
||||
## §15 Optional: machine-readable companion
|
||||
|
||||
v1 does not generate a machine-readable sidecar. After the defaults and triage decisions in §14 wave 1 stabilize, generate a derived index for tooling: entry points and parameter trust levels, in-scope/out-of-scope components, security-relevant gflag defaults, §8 properties, §9 disclaimed properties, §11a non-findings, and §13 disposition labels. This document remains the normative specification.
|
||||
|
||||
---
|
||||
|
||||
## Appendix: SECURITY.md statement -> threat-model § back-map
|
||||
|
||||
At drafting time, the repository had no `SECURITY.md`, and the Apache security project index provided only the generic `security@apache.org` address, with no bRPC project-level security page.
|
||||
|
||||
| Source | Statement | Threat-model section |
|
||||
| --- | --- | --- |
|
||||
| `security.apache.org/projects/` | no project-level security content | N/A |
|
||||
| future `SECURITY.md` | TBD by PMC | to be backfilled in a future version |
|
||||
|
||||
Once the PMC publishes `SECURITY.md` or an official security policy, that artifact should become a higher-authority source. This document should then map back to or link to the official document.
|
||||
|
||||
---
|
||||
|
||||
*End of v1 draft.*
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
# 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.
|
||||
|
||||
load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands")
|
||||
|
||||
refresh_compile_commands(
|
||||
name = "brpc_compdb",
|
||||
# Specify the targets of interest.
|
||||
# For example, specify a dict of targets and their arguments:
|
||||
targets = {
|
||||
"//:brpc": "",
|
||||
},
|
||||
# For more details, feel free to look into refresh_compile_commands.bzl if you want.
|
||||
)
|
||||
@@ -0,0 +1,152 @@
|
||||
# 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.
|
||||
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
|
||||
licenses(["notice"]) # Apache v2
|
||||
|
||||
selects.config_setting_group(
|
||||
name = "brpc_with_glog",
|
||||
match_any = [
|
||||
":brpc_with_glog_deprecated_flag",
|
||||
":brpc_with_glog_new_flag",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_glog_deprecated_flag",
|
||||
define_values = {"with_glog": "true"},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_glog_new_flag",
|
||||
define_values = {"BRPC_WITH_GLOG": "true"},
|
||||
)
|
||||
|
||||
selects.config_setting_group(
|
||||
name = "brpc_with_mesalink",
|
||||
match_any = [
|
||||
":brpc_with_mesalink_deprecated_flag",
|
||||
":brpc_with_mesalink_new_flag",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_mesalink_deprecated_flag",
|
||||
define_values = {"with_mesalink": "true"},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_mesalink_new_flag",
|
||||
define_values = {"BRPC_WITH_MESALINK": "true"},
|
||||
)
|
||||
|
||||
selects.config_setting_group(
|
||||
name = "brpc_with_thrift",
|
||||
match_any = [
|
||||
":brpc_with_thrift_deprecated_flag",
|
||||
":brpc_with_thrift_new_flag",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_thrift_legacy_version",
|
||||
define_values = {"BRPC_WITH_THRIFT_LEGACY_VERSION": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_thrift_deprecated_flag",
|
||||
define_values = {"with_thrift": "true"},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_thrift_new_flag",
|
||||
define_values = {"BRPC_WITH_THRIFT": "true"},
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_build_for_unittest",
|
||||
define_values = {"BRPC_BUILD_FOR_UNITTEST": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_sse42",
|
||||
define_values = {"BRPC_WITH_SSE42": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "darwin",
|
||||
values = {"cpu": "darwin"},
|
||||
visibility = ["//:__subpkgs__"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_rdma",
|
||||
define_values = {"BRPC_WITH_RDMA": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_boringssl",
|
||||
define_values = {"BRPC_WITH_BORINGSSL": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_debug_bthread_sche_safety",
|
||||
define_values = {"with_debug_bthread_sche_safety": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_debug_lock",
|
||||
define_values = {"with_debug_lock": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_asan",
|
||||
define_values = {"with_asan": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_bthread_tracer",
|
||||
constraint_values = [
|
||||
"@platforms//cpu:x86_64",
|
||||
],
|
||||
define_values = {
|
||||
"with_bthread_tracer": "true",
|
||||
},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "brpc_with_no_pthread_mutex_hook",
|
||||
define_values = {"BRPC_WITH_NO_PTHREAD_MUTEX_HOOK": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "with_babylon_counter",
|
||||
define_values = {"with_babylon_counter": "true"},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
# 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.
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
|
||||
|
||||
genrule(
|
||||
name = "crc32c_config_h",
|
||||
srcs = ["src/crc32c_config.h.in"],
|
||||
outs = ["crc32c/crc32c_config.h"],
|
||||
cmd = """
|
||||
sed -e 's/#cmakedefine01/#define/' \
|
||||
""" + select({
|
||||
"@//bazel/config:brpc_with_sse42": """-e 's/ HAVE_SSE42/ HAVE_SSE42 1/' \
|
||||
""",
|
||||
"//conditions:default": """-e 's/ HAVE_SSE42/ HAVE_SSE42 0/' \
|
||||
""",
|
||||
}) + select({
|
||||
"@//bazel/config:brpc_with_glog": """-e 's/ CRC32C_TESTS_BUILT_WITH_GLOG/ CRC32C_TESTS_BUILT_WITH_GLOG 1/' \
|
||||
""",
|
||||
"//conditions:default": """-e 's/ CRC32C_TESTS_BUILT_WITH_GLOG/ CRC32C_TESTS_BUILT_WITH_GLOG 0/' \
|
||||
""",
|
||||
}) + """-e 's/ BYTE_ORDER_BIG_ENDIAN/ BYTE_ORDER_BIG_ENDIAN 0/' \
|
||||
-e 's/ HAVE_BUILTIN_PREFETCH/ HAVE_BUILTIN_PREFETCH 0/' \
|
||||
-e 's/ HAVE_MM_PREFETCH/ HAVE_MM_PREFETCH 0/' \
|
||||
-e 's/ HAVE_ARM64_CRC32C/ HAVE_ARM64_CRC32C 0/' \
|
||||
-e 's/ HAVE_STRONG_GETAUXVAL/ HAVE_STRONG_GETAUXVAL 0/' \
|
||||
-e 's/ HAVE_WEAK_GETAUXVAL/ HAVE_WEAK_GETAUXVAL 0/' \
|
||||
< $< > $@
|
||||
""",
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "crc32c",
|
||||
srcs = [
|
||||
"src/crc32c.cc",
|
||||
"src/crc32c_arm64.cc",
|
||||
"src/crc32c_arm64.h",
|
||||
"src/crc32c_arm64_check.h",
|
||||
"src/crc32c_internal.h",
|
||||
"src/crc32c_portable.cc",
|
||||
"src/crc32c_prefetch.h",
|
||||
"src/crc32c_read_le.h",
|
||||
"src/crc32c_round_up.h",
|
||||
"src/crc32c_sse42.cc",
|
||||
"src/crc32c_sse42.h",
|
||||
"src/crc32c_sse42_check.h",
|
||||
":crc32c_config_h",
|
||||
],
|
||||
hdrs = [
|
||||
"include/crc32c/crc32c.h",
|
||||
],
|
||||
copts = select({
|
||||
"@//bazel/config:brpc_with_sse42": ["-msse4.2"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
strip_include_prefix = "include",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "crc32c_test",
|
||||
srcs = [
|
||||
"src/crc32c_arm64_unittest.cc",
|
||||
"src/crc32c_extend_unittests.h",
|
||||
"src/crc32c_portable_unittest.cc",
|
||||
"src/crc32c_prefetch_unittest.cc",
|
||||
"src/crc32c_read_le_unittest.cc",
|
||||
"src/crc32c_round_up_unittest.cc",
|
||||
"src/crc32c_sse42_unittest.cc",
|
||||
"src/crc32c_test_main.cc",
|
||||
"src/crc32c_unittest.cc",
|
||||
],
|
||||
deps = [
|
||||
":crc32c",
|
||||
"@com_google_googletest//:gtest",
|
||||
"@com_google_googletest//:gtest_main",
|
||||
] + select({
|
||||
"@//bazel/config:brpc_with_glog": ["@com_github_google_glog//:glog"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
Vendored
+59
@@ -0,0 +1,59 @@
|
||||
# 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.
|
||||
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
|
||||
|
||||
filegroup(
|
||||
name = "all_srcs",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
cmake(
|
||||
name = "event",
|
||||
cache_entries = {
|
||||
"EVENT__DISABLE_BENCHMARK": "ON",
|
||||
"EVENT__DISABLE_TESTS": "ON",
|
||||
"EVENT__DISABLE_SAMPLES": "ON",
|
||||
"EVENT__LIBRARY_TYPE": "STATIC",
|
||||
"OPENSSL_ROOT_DIR": "$$EXT_BUILD_DEPS$$/openssl",
|
||||
},
|
||||
generate_args = ["-GNinja"],
|
||||
lib_source = ":all_srcs",
|
||||
linkopts = [
|
||||
"-pthread",
|
||||
],
|
||||
out_static_libs = select({
|
||||
"@platforms//os:windows": [
|
||||
"event.lib",
|
||||
"event_core.lib",
|
||||
"event_extra.lib",
|
||||
"event_openssl.lib",
|
||||
"event_pthreads.lib",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"libevent.a",
|
||||
"libevent_core.a",
|
||||
"libevent_extra.a",
|
||||
"libevent_openssl.a",
|
||||
"libevent_pthreads.a",
|
||||
],
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
# Zlib is only used for testing.
|
||||
"@openssl//:crypto",
|
||||
"@openssl//:ssl",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
# 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.
|
||||
diff --git a/src/glog/logging.h.in b/src/glog/logging.h.in
|
||||
index 421f1e0..a363141 100755
|
||||
--- a/src/glog/logging.h.in
|
||||
+++ b/src/glog/logging.h.in
|
||||
@@ -1334,7 +1334,7 @@ class GOOGLE_GLOG_DLL_DECL LogStreamBuf : public std::streambuf {
|
||||
}
|
||||
|
||||
// This effectively ignores overflow.
|
||||
- int_type overflow(int_type ch) {
|
||||
+ int_type overflow(int_type ch) override {
|
||||
return ch;
|
||||
}
|
||||
|
||||
@@ -1862,7 +1862,7 @@ class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream {
|
||||
NullStreamFatal() { }
|
||||
NullStreamFatal(const char* file, int line, const CheckOpString& result) :
|
||||
NullStream(file, line, result) { }
|
||||
- @ac_cv___attribute___noreturn@ ~NullStreamFatal() throw () { _exit(1); }
|
||||
+ @ac_cv___attribute___noreturn@ ~NullStreamFatal() throw () override { _exit(1); }
|
||||
};
|
||||
|
||||
// Install a signal handler that will dump signal information and a stack
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
# 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.
|
||||
|
||||
exports_files(
|
||||
[
|
||||
"port_config.h",
|
||||
"port.h",
|
||||
],
|
||||
)
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
load("@bazel_skylib//rules:copy_file.bzl", "copy_file")
|
||||
|
||||
copy_file(
|
||||
name = "port_config_h",
|
||||
src = "@//bazel/third_party/leveldb:port_config.h",
|
||||
out = "port/port_config.h",
|
||||
allow_symlink = True,
|
||||
)
|
||||
|
||||
copy_file(
|
||||
name = "port_h",
|
||||
src = "@//bazel/third_party/leveldb:port.h",
|
||||
out = "port/port.h",
|
||||
allow_symlink = True,
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "leveldb",
|
||||
srcs = glob(
|
||||
[
|
||||
"db/**/*.cc",
|
||||
"db/**/*.h",
|
||||
"helpers/**/*.cc",
|
||||
"helpers/**/*.h",
|
||||
"port/**/*.cc",
|
||||
"port/**/*.h",
|
||||
"table/**/*.cc",
|
||||
"table/**/*.h",
|
||||
"util/**/*.cc",
|
||||
"util/**/*.h",
|
||||
],
|
||||
exclude = [
|
||||
"**/*_test.cc",
|
||||
"**/testutil.*",
|
||||
"**/*_bench.cc",
|
||||
"**/*_windows*",
|
||||
"db/leveldbutil.cc",
|
||||
],
|
||||
),
|
||||
hdrs = glob(
|
||||
["include/**/*.h"],
|
||||
exclude = ["doc/**"],
|
||||
) + [
|
||||
":port_h",
|
||||
":port_config_h",
|
||||
],
|
||||
includes = [
|
||||
".",
|
||||
"include",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@com_github_google_crc32c//:crc32c",
|
||||
"@com_github_google_snappy//:snappy",
|
||||
],
|
||||
)
|
||||
Vendored
+34
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_PORT_PORT_H_
|
||||
#define STORAGE_LEVELDB_PORT_PORT_H_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#define LEVELDB_HAS_PORT_CONFIG_H 1
|
||||
|
||||
// Include the appropriate platform specific file below. If you are
|
||||
// porting to a new platform, see "port_example.h" for documentation
|
||||
// of what the new port_<platform>.h file must provide.
|
||||
#include "port/port_stdcxx.h"
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_H_
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
// 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.
|
||||
//
|
||||
// Copyright 2017 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
#define STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
|
||||
// Define to 1 if you have a definition for fdatasync() in <unistd.h>.
|
||||
#define HAVE_FUNC_FDATASYNC 1
|
||||
|
||||
// Define to 1 if you have Google CRC32C.
|
||||
#define HAVE_CRC32C 1
|
||||
|
||||
// Define to 1 if you have Google Snappy.
|
||||
#define HAVE_SNAPPY 1
|
||||
|
||||
// Define to 1 if your processor stores words with the most significant byte
|
||||
// first (like Motorola and SPARC, unlike Intel and VAX).
|
||||
#define LEVELDB_IS_BIG_ENDIAN 0
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
# 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.
|
||||
#
|
||||
# Copyright 2016 The Bazel Authors. All rights reserved.
|
||||
#
|
||||
# Licensed 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.
|
||||
#
|
||||
# Copied from https://github.com/bazelbuild/rules_foreign_cc/blob/0.7.0/examples/third_party/openssl/BUILD.openssl.bazel
|
||||
#
|
||||
# Modifications:
|
||||
# 1. Create alias `ssl` & `crypto` to align with boringssl.
|
||||
# 2. Build with `@com_github_madler_zlib//:zlib`.
|
||||
# 3. Add more configure options coming from debian openssl package configurations.
|
||||
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "configure_make", "configure_make_variant")
|
||||
|
||||
filegroup(
|
||||
name = "all_srcs",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
CONFIGURE_OPTIONS = [
|
||||
"no-idea",
|
||||
"no-mdc2",
|
||||
"no-rc5",
|
||||
"no-ssl3",
|
||||
"no-ssl3-method",
|
||||
"enable-rfc3779",
|
||||
"enable-cms",
|
||||
"no-capieng",
|
||||
"enable-ec_nistp_64_gcc_128",
|
||||
"--with-zlib-include=$$EXT_BUILD_DEPS$$",
|
||||
"--with-zlib-lib=$$EXT_BUILD_DEPS$$",
|
||||
# https://stackoverflow.com/questions/36220341/struct-in6-addr-has-no-member-named-s6-addr32-with-ansi
|
||||
"-D_DEFAULT_SOURCE=1",
|
||||
"-DPEDANTIC",
|
||||
]
|
||||
|
||||
LIB_NAME = "openssl"
|
||||
|
||||
MAKE_TARGETS = [
|
||||
"build_libs",
|
||||
"install_dev",
|
||||
]
|
||||
|
||||
config_setting(
|
||||
name = "msvc_compiler",
|
||||
flag_values = {
|
||||
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
|
||||
},
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "ssl",
|
||||
actual = "openssl",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "crypto",
|
||||
actual = "openssl",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "openssl",
|
||||
actual = select({
|
||||
":msvc_compiler": "openssl_msvc",
|
||||
"//conditions:default": "openssl_default",
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
configure_make_variant(
|
||||
name = "openssl_msvc",
|
||||
build_data = [
|
||||
"@nasm//:nasm",
|
||||
"@perl//:perl",
|
||||
],
|
||||
configure_command = "Configure",
|
||||
configure_in_place = True,
|
||||
configure_options = CONFIGURE_OPTIONS + [
|
||||
"VC-WIN64A",
|
||||
# Unset Microsoft Assembler (MASM) flags set by built-in MSVC toolchain,
|
||||
# as NASM is unsed to build OpenSSL rather than MASM
|
||||
"ASFLAGS=\" \"",
|
||||
],
|
||||
configure_prefix = "$PERL",
|
||||
env = {
|
||||
# The Zi flag must be set otherwise OpenSSL fails to build due to missing .pdb files
|
||||
"CFLAGS": "-Zi",
|
||||
"PATH": "$$(dirname $(execpath @nasm//:nasm)):$$PATH",
|
||||
"PERL": "$(execpath @perl//:perl)",
|
||||
},
|
||||
lib_name = LIB_NAME,
|
||||
lib_source = ":all_srcs",
|
||||
out_static_libs = [
|
||||
"libssl.lib",
|
||||
"libcrypto.lib",
|
||||
],
|
||||
targets = MAKE_TARGETS,
|
||||
toolchain = "@rules_foreign_cc//toolchains:preinstalled_nmake_toolchain",
|
||||
deps = [
|
||||
"@com_github_madler_zlib//:zlib",
|
||||
],
|
||||
)
|
||||
|
||||
# https://wiki.openssl.org/index.php/Compilation_and_Installation
|
||||
configure_make(
|
||||
name = "openssl_default",
|
||||
configure_command = "config",
|
||||
configure_in_place = True,
|
||||
configure_options = CONFIGURE_OPTIONS,
|
||||
env = select({
|
||||
"@platforms//os:macos": {
|
||||
"AR": "",
|
||||
"PERL": "$$EXT_BUILD_ROOT$$/$(PERL)",
|
||||
},
|
||||
"//conditions:default": {
|
||||
"PERL": "$$EXT_BUILD_ROOT$$/$(PERL)",
|
||||
},
|
||||
}),
|
||||
lib_name = LIB_NAME,
|
||||
lib_source = ":all_srcs",
|
||||
# Note that for Linux builds, libssl must come before libcrypto on the linker command-line.
|
||||
# As such, libssl must be listed before libcrypto
|
||||
out_static_libs = [
|
||||
"libssl.a",
|
||||
"libcrypto.a",
|
||||
],
|
||||
targets = MAKE_TARGETS,
|
||||
toolchains = ["@rules_perl//:current_toolchain"],
|
||||
deps = [
|
||||
"@com_github_madler_zlib//:zlib",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "gen_dir",
|
||||
srcs = [":openssl"],
|
||||
output_group = "gen_dir",
|
||||
)
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+498
@@ -0,0 +1,498 @@
|
||||
# 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.
|
||||
#
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Copied from https://github.com/protocolbuffers/protobuf/blob/v3.19.1/BUILD
|
||||
#
|
||||
# Modifications:
|
||||
# 1. Remove all non-cxx rules.
|
||||
# 2. Remove android support.
|
||||
# 3. zlib use @com_github_madler_zlib//:zlib
|
||||
|
||||
# Bazel (https://bazel.build/) BUILD file for Protobuf.
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", native_cc_proto_library = "cc_proto_library")
|
||||
load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain", "proto_library")
|
||||
load(":compiler_config_setting.bzl", "create_compiler_config_setting")
|
||||
load(
|
||||
":protobuf.bzl",
|
||||
"adapt_proto_library",
|
||||
)
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
exports_files(["LICENSE"])
|
||||
|
||||
################################################################################
|
||||
# build configuration
|
||||
################################################################################
|
||||
|
||||
################################################################################
|
||||
# ZLIB configuration
|
||||
################################################################################
|
||||
|
||||
ZLIB_DEPS = ["@com_github_madler_zlib//:zlib"]
|
||||
|
||||
################################################################################
|
||||
# Protobuf Runtime Library
|
||||
################################################################################
|
||||
|
||||
MSVC_COPTS = [
|
||||
"/wd4018", # -Wno-sign-compare
|
||||
"/wd4065", # switch statement contains 'default' but no 'case' labels
|
||||
"/wd4146", # unary minus operator applied to unsigned type, result still unsigned
|
||||
"/wd4244", # 'conversion' conversion from 'type1' to 'type2', possible loss of data
|
||||
"/wd4251", # 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
|
||||
"/wd4267", # 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
"/wd4305", # 'identifier' : truncation from 'type1' to 'type2'
|
||||
"/wd4307", # 'operator' : integral constant overflow
|
||||
"/wd4309", # 'conversion' : truncation of constant value
|
||||
"/wd4334", # 'operator' : result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
|
||||
"/wd4355", # 'this' : used in base member initializer list
|
||||
"/wd4506", # no definition for inline function 'function'
|
||||
"/wd4800", # 'type' : forcing value to bool 'true' or 'false' (performance warning)
|
||||
"/wd4996", # The compiler encountered a deprecated declaration.
|
||||
]
|
||||
|
||||
COPTS = select({
|
||||
":msvc": MSVC_COPTS,
|
||||
"//conditions:default": [
|
||||
"-DHAVE_ZLIB",
|
||||
"-Wmissing-field-initializers",
|
||||
"-Woverloaded-virtual",
|
||||
"-Wno-sign-compare",
|
||||
],
|
||||
})
|
||||
|
||||
create_compiler_config_setting(
|
||||
name = "msvc",
|
||||
value = "msvc-cl",
|
||||
visibility = [
|
||||
# Public, but Protobuf only visibility.
|
||||
"//:__subpackages__",
|
||||
],
|
||||
)
|
||||
|
||||
# Android and MSVC builds do not need to link in a separate pthread library.
|
||||
LINK_OPTS = select({
|
||||
":msvc": [
|
||||
# Suppress linker warnings about files with no symbols defined.
|
||||
"-ignore:4221",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"-lpthread",
|
||||
"-lm",
|
||||
],
|
||||
})
|
||||
|
||||
cc_library(
|
||||
name = "protobuf_lite",
|
||||
srcs = [
|
||||
# AUTOGEN(protobuf_lite_srcs)
|
||||
"src/google/protobuf/any_lite.cc",
|
||||
"src/google/protobuf/arena.cc",
|
||||
"src/google/protobuf/arenastring.cc",
|
||||
"src/google/protobuf/extension_set.cc",
|
||||
"src/google/protobuf/generated_enum_util.cc",
|
||||
"src/google/protobuf/generated_message_table_driven_lite.cc",
|
||||
"src/google/protobuf/generated_message_tctable_lite.cc",
|
||||
"src/google/protobuf/generated_message_util.cc",
|
||||
"src/google/protobuf/implicit_weak_message.cc",
|
||||
"src/google/protobuf/inlined_string_field.cc",
|
||||
"src/google/protobuf/io/coded_stream.cc",
|
||||
"src/google/protobuf/io/io_win32.cc",
|
||||
"src/google/protobuf/io/strtod.cc",
|
||||
"src/google/protobuf/io/zero_copy_stream.cc",
|
||||
"src/google/protobuf/io/zero_copy_stream_impl.cc",
|
||||
"src/google/protobuf/io/zero_copy_stream_impl_lite.cc",
|
||||
"src/google/protobuf/map.cc",
|
||||
"src/google/protobuf/message_lite.cc",
|
||||
"src/google/protobuf/parse_context.cc",
|
||||
"src/google/protobuf/repeated_field.cc",
|
||||
"src/google/protobuf/repeated_ptr_field.cc",
|
||||
"src/google/protobuf/stubs/bytestream.cc",
|
||||
"src/google/protobuf/stubs/common.cc",
|
||||
"src/google/protobuf/stubs/int128.cc",
|
||||
"src/google/protobuf/stubs/status.cc",
|
||||
"src/google/protobuf/stubs/statusor.cc",
|
||||
"src/google/protobuf/stubs/stringpiece.cc",
|
||||
"src/google/protobuf/stubs/stringprintf.cc",
|
||||
"src/google/protobuf/stubs/structurally_valid.cc",
|
||||
"src/google/protobuf/stubs/strutil.cc",
|
||||
"src/google/protobuf/stubs/time.cc",
|
||||
"src/google/protobuf/wire_format_lite.cc",
|
||||
],
|
||||
hdrs = glob([
|
||||
"src/google/protobuf/**/*.h",
|
||||
"src/google/protobuf/**/*.inc",
|
||||
]),
|
||||
copts = COPTS,
|
||||
includes = ["src/"],
|
||||
linkopts = LINK_OPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
PROTOBUF_DEPS = select({
|
||||
":msvc": [],
|
||||
"//conditions:default": ZLIB_DEPS,
|
||||
})
|
||||
|
||||
cc_library(
|
||||
name = "protobuf",
|
||||
srcs = [
|
||||
# AUTOGEN(protobuf_srcs)
|
||||
"src/google/protobuf/any.cc",
|
||||
"src/google/protobuf/any.pb.cc",
|
||||
"src/google/protobuf/api.pb.cc",
|
||||
"src/google/protobuf/compiler/importer.cc",
|
||||
"src/google/protobuf/compiler/parser.cc",
|
||||
"src/google/protobuf/descriptor.cc",
|
||||
"src/google/protobuf/descriptor.pb.cc",
|
||||
"src/google/protobuf/descriptor_database.cc",
|
||||
"src/google/protobuf/duration.pb.cc",
|
||||
"src/google/protobuf/dynamic_message.cc",
|
||||
"src/google/protobuf/empty.pb.cc",
|
||||
"src/google/protobuf/extension_set_heavy.cc",
|
||||
"src/google/protobuf/field_mask.pb.cc",
|
||||
"src/google/protobuf/generated_message_bases.cc",
|
||||
"src/google/protobuf/generated_message_reflection.cc",
|
||||
"src/google/protobuf/generated_message_table_driven.cc",
|
||||
"src/google/protobuf/generated_message_tctable_full.cc",
|
||||
"src/google/protobuf/io/gzip_stream.cc",
|
||||
"src/google/protobuf/io/printer.cc",
|
||||
"src/google/protobuf/io/tokenizer.cc",
|
||||
"src/google/protobuf/map_field.cc",
|
||||
"src/google/protobuf/message.cc",
|
||||
"src/google/protobuf/reflection_ops.cc",
|
||||
"src/google/protobuf/service.cc",
|
||||
"src/google/protobuf/source_context.pb.cc",
|
||||
"src/google/protobuf/struct.pb.cc",
|
||||
"src/google/protobuf/stubs/substitute.cc",
|
||||
"src/google/protobuf/text_format.cc",
|
||||
"src/google/protobuf/timestamp.pb.cc",
|
||||
"src/google/protobuf/type.pb.cc",
|
||||
"src/google/protobuf/unknown_field_set.cc",
|
||||
"src/google/protobuf/util/delimited_message_util.cc",
|
||||
"src/google/protobuf/util/field_comparator.cc",
|
||||
"src/google/protobuf/util/field_mask_util.cc",
|
||||
"src/google/protobuf/util/internal/datapiece.cc",
|
||||
"src/google/protobuf/util/internal/default_value_objectwriter.cc",
|
||||
"src/google/protobuf/util/internal/error_listener.cc",
|
||||
"src/google/protobuf/util/internal/field_mask_utility.cc",
|
||||
"src/google/protobuf/util/internal/json_escaping.cc",
|
||||
"src/google/protobuf/util/internal/json_objectwriter.cc",
|
||||
"src/google/protobuf/util/internal/json_stream_parser.cc",
|
||||
"src/google/protobuf/util/internal/object_writer.cc",
|
||||
"src/google/protobuf/util/internal/proto_writer.cc",
|
||||
"src/google/protobuf/util/internal/protostream_objectsource.cc",
|
||||
"src/google/protobuf/util/internal/protostream_objectwriter.cc",
|
||||
"src/google/protobuf/util/internal/type_info.cc",
|
||||
"src/google/protobuf/util/internal/utility.cc",
|
||||
"src/google/protobuf/util/json_util.cc",
|
||||
"src/google/protobuf/util/message_differencer.cc",
|
||||
"src/google/protobuf/util/time_util.cc",
|
||||
"src/google/protobuf/util/type_resolver_util.cc",
|
||||
"src/google/protobuf/wire_format.cc",
|
||||
"src/google/protobuf/wrappers.pb.cc",
|
||||
],
|
||||
hdrs = glob([
|
||||
"src/**/*.h",
|
||||
"src/**/*.inc",
|
||||
]),
|
||||
copts = COPTS,
|
||||
includes = ["src/"],
|
||||
linkopts = LINK_OPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":protobuf_lite"] + PROTOBUF_DEPS,
|
||||
)
|
||||
|
||||
# This provides just the header files for use in projects that need to build
|
||||
# shared libraries for dynamic loading. This target is available until Bazel
|
||||
# adds native support for such use cases.
|
||||
# TODO(keveman): Remove this target once the support gets added to Bazel.
|
||||
cc_library(
|
||||
name = "protobuf_headers",
|
||||
hdrs = glob([
|
||||
"src/**/*.h",
|
||||
"src/**/*.inc",
|
||||
]),
|
||||
includes = ["src/"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
# Map of all well known protos.
|
||||
# name => (include path, imports)
|
||||
WELL_KNOWN_PROTO_MAP = {
|
||||
"any": ("src/google/protobuf/any.proto", []),
|
||||
"api": (
|
||||
"src/google/protobuf/api.proto",
|
||||
[
|
||||
"source_context",
|
||||
"type",
|
||||
],
|
||||
),
|
||||
"compiler_plugin": (
|
||||
"src/google/protobuf/compiler/plugin.proto",
|
||||
["descriptor"],
|
||||
),
|
||||
"descriptor": ("src/google/protobuf/descriptor.proto", []),
|
||||
"duration": ("src/google/protobuf/duration.proto", []),
|
||||
"empty": ("src/google/protobuf/empty.proto", []),
|
||||
"field_mask": ("src/google/protobuf/field_mask.proto", []),
|
||||
"source_context": ("src/google/protobuf/source_context.proto", []),
|
||||
"struct": ("src/google/protobuf/struct.proto", []),
|
||||
"timestamp": ("src/google/protobuf/timestamp.proto", []),
|
||||
"type": (
|
||||
"src/google/protobuf/type.proto",
|
||||
[
|
||||
"any",
|
||||
"source_context",
|
||||
],
|
||||
),
|
||||
"wrappers": ("src/google/protobuf/wrappers.proto", []),
|
||||
}
|
||||
|
||||
WELL_KNOWN_PROTOS = [value[0] for value in WELL_KNOWN_PROTO_MAP.values()]
|
||||
|
||||
LITE_WELL_KNOWN_PROTO_MAP = {
|
||||
"any": ("src/google/protobuf/any.proto", []),
|
||||
"api": (
|
||||
"src/google/protobuf/api.proto",
|
||||
[
|
||||
"source_context",
|
||||
"type",
|
||||
],
|
||||
),
|
||||
"duration": ("src/google/protobuf/duration.proto", []),
|
||||
"empty": ("src/google/protobuf/empty.proto", []),
|
||||
"field_mask": ("src/google/protobuf/field_mask.proto", []),
|
||||
"source_context": ("src/google/protobuf/source_context.proto", []),
|
||||
"struct": ("src/google/protobuf/struct.proto", []),
|
||||
"timestamp": ("src/google/protobuf/timestamp.proto", []),
|
||||
"type": (
|
||||
"src/google/protobuf/type.proto",
|
||||
[
|
||||
"any",
|
||||
"source_context",
|
||||
],
|
||||
),
|
||||
"wrappers": ("src/google/protobuf/wrappers.proto", []),
|
||||
}
|
||||
|
||||
LITE_WELL_KNOWN_PROTOS = [value[0] for value in LITE_WELL_KNOWN_PROTO_MAP.values()]
|
||||
|
||||
filegroup(
|
||||
name = "well_known_protos",
|
||||
srcs = WELL_KNOWN_PROTOS,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "lite_well_known_protos",
|
||||
srcs = LITE_WELL_KNOWN_PROTOS,
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
adapt_proto_library(
|
||||
name = "cc_wkt_protos_genproto",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [proto + "_proto" for proto in WELL_KNOWN_PROTO_MAP.keys()],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "cc_wkt_protos",
|
||||
deprecation = "Only for backward compatibility. Do not use.",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
################################################################################
|
||||
# Well Known Types Proto Library Rules
|
||||
#
|
||||
# These proto_library rules can be used with one of the language specific proto
|
||||
# library rules i.e. java_proto_library:
|
||||
#
|
||||
# java_proto_library(
|
||||
# name = "any_java_proto",
|
||||
# deps = ["@com_google_protobuf//:any_proto],
|
||||
# )
|
||||
################################################################################
|
||||
|
||||
[proto_library(
|
||||
name = proto[0] + "_proto",
|
||||
srcs = [proto[1][0]],
|
||||
strip_import_prefix = "src",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [dep + "_proto" for dep in proto[1][1]],
|
||||
) for proto in WELL_KNOWN_PROTO_MAP.items()]
|
||||
|
||||
[native_cc_proto_library(
|
||||
name = proto + "_cc_proto",
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [proto + "_proto"],
|
||||
) for proto in WELL_KNOWN_PROTO_MAP.keys()]
|
||||
|
||||
################################################################################
|
||||
# Protocol Buffers Compiler
|
||||
################################################################################
|
||||
|
||||
cc_library(
|
||||
name = "protoc_lib",
|
||||
srcs = [
|
||||
# AUTOGEN(protoc_lib_srcs)
|
||||
"src/google/protobuf/compiler/code_generator.cc",
|
||||
"src/google/protobuf/compiler/command_line_interface.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_enum.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_enum_field.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_extension.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_field.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_file.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_generator.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_helpers.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_map_field.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_message.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_message_field.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_padding_optimizer.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_parse_function_generator.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_primitive_field.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_service.cc",
|
||||
"src/google/protobuf/compiler/cpp/cpp_string_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_doc_comment.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_enum.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_enum_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_field_base.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_generator.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_helpers.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_map_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_message.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_message_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_primitive_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_reflection_class.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_repeated_enum_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_repeated_message_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_repeated_primitive_field.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_source_generator_base.cc",
|
||||
"src/google/protobuf/compiler/csharp/csharp_wrapper_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_context.cc",
|
||||
"src/google/protobuf/compiler/java/java_doc_comment.cc",
|
||||
"src/google/protobuf/compiler/java/java_enum.cc",
|
||||
"src/google/protobuf/compiler/java/java_enum_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_enum_field_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_enum_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_extension.cc",
|
||||
"src/google/protobuf/compiler/java/java_extension_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_file.cc",
|
||||
"src/google/protobuf/compiler/java/java_generator.cc",
|
||||
"src/google/protobuf/compiler/java/java_generator_factory.cc",
|
||||
"src/google/protobuf/compiler/java/java_helpers.cc",
|
||||
"src/google/protobuf/compiler/java/java_kotlin_generator.cc",
|
||||
"src/google/protobuf/compiler/java/java_map_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_map_field_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_message.cc",
|
||||
"src/google/protobuf/compiler/java/java_message_builder.cc",
|
||||
"src/google/protobuf/compiler/java/java_message_builder_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_message_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_message_field_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_message_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_name_resolver.cc",
|
||||
"src/google/protobuf/compiler/java/java_primitive_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_primitive_field_lite.cc",
|
||||
"src/google/protobuf/compiler/java/java_service.cc",
|
||||
"src/google/protobuf/compiler/java/java_shared_code_generator.cc",
|
||||
"src/google/protobuf/compiler/java/java_string_field.cc",
|
||||
"src/google/protobuf/compiler/java/java_string_field_lite.cc",
|
||||
"src/google/protobuf/compiler/js/js_generator.cc",
|
||||
"src/google/protobuf/compiler/js/well_known_types_embed.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_enum.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_enum_field.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_extension.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_field.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_file.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_generator.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_helpers.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_map_field.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_message.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_message_field.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_oneof.cc",
|
||||
"src/google/protobuf/compiler/objectivec/objectivec_primitive_field.cc",
|
||||
"src/google/protobuf/compiler/php/php_generator.cc",
|
||||
"src/google/protobuf/compiler/plugin.cc",
|
||||
"src/google/protobuf/compiler/plugin.pb.cc",
|
||||
"src/google/protobuf/compiler/python/python_generator.cc",
|
||||
"src/google/protobuf/compiler/ruby/ruby_generator.cc",
|
||||
"src/google/protobuf/compiler/subprocess.cc",
|
||||
"src/google/protobuf/compiler/zip_writer.cc",
|
||||
],
|
||||
copts = COPTS,
|
||||
includes = ["src/"],
|
||||
linkopts = LINK_OPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":protobuf"],
|
||||
)
|
||||
|
||||
cc_binary(
|
||||
name = "protoc",
|
||||
srcs = ["src/google/protobuf/compiler/main.cc"],
|
||||
linkopts = LINK_OPTS,
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [":protoc_lib"],
|
||||
)
|
||||
|
||||
proto_lang_toolchain(
|
||||
name = "cc_toolchain",
|
||||
blacklisted_protos = [proto + "_proto" for proto in WELL_KNOWN_PROTO_MAP.keys()],
|
||||
command_line = "--cpp_out=$(OUT)",
|
||||
runtime = ":protobuf",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "objectivec",
|
||||
actual = "//objectivec",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "protobuf_objc",
|
||||
actual = "//objectivec",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
# 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.
|
||||
#
|
||||
# Copyright 2015 The TensorFlow Authors. All Rights Reserved.
|
||||
# Licensed 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.
|
||||
|
||||
# Copied from https://github.com/tensorflow/tensorflow/blob/bdd8bf316e4ab7d699127d192d30eb614a158462/third_party/snappy.BUILD
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
|
||||
cc_library(
|
||||
name = "snappy",
|
||||
srcs = [
|
||||
"config.h",
|
||||
"snappy.cc",
|
||||
"snappy.h",
|
||||
"snappy-internal.h",
|
||||
"snappy-sinksource.cc",
|
||||
"snappy-stubs-internal.cc",
|
||||
"snappy-stubs-internal.h",
|
||||
"snappy-stubs-public.h",
|
||||
],
|
||||
hdrs = [
|
||||
"snappy.h",
|
||||
"snappy-sinksource.h",
|
||||
],
|
||||
copts = [
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-fno-exceptions",
|
||||
"-Wno-sign-compare",
|
||||
"-Wno-shift-negative-value",
|
||||
],
|
||||
includes = ["."],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "config_h",
|
||||
outs = ["config.h"],
|
||||
cmd = "\n".join([
|
||||
"cat <<'EOF' >$@",
|
||||
"#define HAVE_STDDEF_H 1",
|
||||
"#define HAVE_STDINT_H 1",
|
||||
"",
|
||||
"#ifdef __has_builtin",
|
||||
"# if !defined(HAVE_BUILTIN_EXPECT) && __has_builtin(__builtin_expect)",
|
||||
"# define HAVE_BUILTIN_EXPECT 1",
|
||||
"# endif",
|
||||
"# if !defined(HAVE_BUILTIN_CTZ) && __has_builtin(__builtin_ctzll)",
|
||||
"# define HAVE_BUILTIN_CTZ 1",
|
||||
"# endif",
|
||||
"#elif defined(__GNUC__) && (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 4)",
|
||||
"# ifndef HAVE_BUILTIN_EXPECT",
|
||||
"# define HAVE_BUILTIN_EXPECT 1",
|
||||
"# endif",
|
||||
"# ifndef HAVE_BUILTIN_CTZ",
|
||||
"# define HAVE_BUILTIN_CTZ 1",
|
||||
"# endif",
|
||||
"#endif",
|
||||
"",
|
||||
"#ifdef __has_include",
|
||||
"# if !defined(HAVE_BYTESWAP_H) && __has_include(<byteswap.h>)",
|
||||
"# define HAVE_BYTESWAP_H 1",
|
||||
"# endif",
|
||||
"# if !defined(HAVE_UNISTD_H) && __has_include(<unistd.h>)",
|
||||
"# define HAVE_UNISTD_H 1",
|
||||
"# endif",
|
||||
"# if !defined(HAVE_SYS_ENDIAN_H) && __has_include(<sys/endian.h>)",
|
||||
"# define HAVE_SYS_ENDIAN_H 1",
|
||||
"# endif",
|
||||
"# if !defined(HAVE_SYS_MMAN_H) && __has_include(<sys/mman.h>)",
|
||||
"# define HAVE_SYS_MMAN_H 1",
|
||||
"# endif",
|
||||
"# if !defined(HAVE_SYS_UIO_H) && __has_include(<sys/uio.h>)",
|
||||
"# define HAVE_SYS_UIO_H 1",
|
||||
"# endif",
|
||||
"#endif",
|
||||
"",
|
||||
"#ifndef SNAPPY_IS_BIG_ENDIAN",
|
||||
"# ifdef __s390x__",
|
||||
"# define SNAPPY_IS_BIG_ENDIAN 1",
|
||||
"# elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__",
|
||||
"# define SNAPPY_IS_BIG_ENDIAN 1",
|
||||
"# endif",
|
||||
"#endif",
|
||||
"EOF",
|
||||
]),
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "snappy_stubs_public_h",
|
||||
srcs = ["snappy-stubs-public.h.in"],
|
||||
outs = ["snappy-stubs-public.h"],
|
||||
cmd = ("sed " +
|
||||
"-e 's/$${\\(.*\\)_01}/\\1/g' " +
|
||||
"-e 's/$${SNAPPY_MAJOR}/1/g' " +
|
||||
"-e 's/$${SNAPPY_MINOR}/1/g' " +
|
||||
"-e 's/$${SNAPPY_PATCHLEVEL}/7/g' " +
|
||||
"$< >$@"),
|
||||
)
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
# 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.
|
||||
|
||||
load("@rules_foreign_cc//foreign_cc:defs.bzl", "cmake")
|
||||
|
||||
filegroup(
|
||||
name = "all_srcs",
|
||||
srcs = glob(["**"]),
|
||||
)
|
||||
|
||||
cmake(
|
||||
name = "thrift",
|
||||
cache_entries = {
|
||||
"BUILD_COMPILER": "OFF",
|
||||
"BUILD_TESTING": "OFF",
|
||||
"BUILD_TUTORIALS": "OFF",
|
||||
"WITH_AS3": "OFF",
|
||||
"WITH_CPP": "ON",
|
||||
"WITH_C_GLIB": "OFF",
|
||||
"WITH_JAVA": "OFF",
|
||||
"WITH_JAVASCRIPT": "OFF",
|
||||
"WITH_NODEJS": "OFF",
|
||||
"WITH_PYTHON": "OFF",
|
||||
"BUILD_SHARED_LIBS": "OFF",
|
||||
"Boost_USE_STATIC_LIBS": "ON",
|
||||
"BOOST_ROOT": "$$EXT_BUILD_DEPS$$",
|
||||
"LIBEVENT_INCLUDE_DIRS": "$$EXT_BUILD_DEPS$$/event/include",
|
||||
"LIBEVENT_LIBRARIES": "$$EXT_BUILD_DEPS$$/event/lib/libevent.a",
|
||||
"OPENSSL_ROOT_DIR": "$$EXT_BUILD_DEPS$$/openssl",
|
||||
"ZLIB_ROOT": "$$EXT_BUILD_DEPS$$/zlib",
|
||||
},
|
||||
generate_args = ["-GNinja"],
|
||||
lib_source = ":all_srcs",
|
||||
linkopts = [
|
||||
"-pthread",
|
||||
],
|
||||
out_static_libs = select({
|
||||
"@platforms//os:windows": [
|
||||
"thrift.lib",
|
||||
"thriftnb.lib",
|
||||
"thriftz.lib",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"libthrift.a",
|
||||
"libthriftnb.a",
|
||||
"libthriftz.a",
|
||||
],
|
||||
}),
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"@boost//:algorithm",
|
||||
"@boost//:locale",
|
||||
"@boost//:noncopyable",
|
||||
"@boost//:numeric_conversion",
|
||||
"@boost//:scoped_array",
|
||||
"@boost//:smart_ptr",
|
||||
"@boost//:tokenizer",
|
||||
"@com_github_libevent_libevent//:event",
|
||||
"@com_github_madler_zlib//:zlib",
|
||||
"@openssl//:crypto",
|
||||
"@openssl//:ssl",
|
||||
],
|
||||
)
|
||||
Vendored
+17
@@ -0,0 +1,17 @@
|
||||
# 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.
|
||||
#
|
||||
# Thie empty BUILD.bazel file is required to make Bazel treat
|
||||
# this directory as a package.
|
||||
Vendored
+111
@@ -0,0 +1,111 @@
|
||||
# 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.
|
||||
#
|
||||
# Copyright 2008 Google Inc. All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are
|
||||
# met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following disclaimer
|
||||
# in the documentation and/or other materials provided with the
|
||||
# distribution.
|
||||
# * Neither the name of Google Inc. nor the names of its
|
||||
# contributors may be used to endorse or promote products derived from
|
||||
# this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# Copied from https://github.com/protocolbuffers/protobuf/blob/v3.9.1/third_party/zlib.BUILD
|
||||
|
||||
load("@rules_cc//cc:defs.bzl", "cc_library")
|
||||
|
||||
_ZLIB_HEADERS = [
|
||||
"crc32.h",
|
||||
"deflate.h",
|
||||
"gzguts.h",
|
||||
"inffast.h",
|
||||
"inffixed.h",
|
||||
"inflate.h",
|
||||
"inftrees.h",
|
||||
"trees.h",
|
||||
"zconf.h",
|
||||
"zlib.h",
|
||||
"zutil.h",
|
||||
]
|
||||
|
||||
_ZLIB_PREFIXED_HEADERS = ["zlib/include/" + hdr for hdr in _ZLIB_HEADERS]
|
||||
|
||||
# In order to limit the damage from the `includes` propagation
|
||||
# via `:zlib`, copy the public headers to a subdirectory and
|
||||
# expose those.
|
||||
genrule(
|
||||
name = "copy_public_headers",
|
||||
srcs = _ZLIB_HEADERS,
|
||||
outs = _ZLIB_PREFIXED_HEADERS,
|
||||
cmd = "cp $(SRCS) $(@D)/zlib/include/",
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "zlib",
|
||||
srcs = [
|
||||
"adler32.c",
|
||||
"compress.c",
|
||||
"crc32.c",
|
||||
"deflate.c",
|
||||
"gzclose.c",
|
||||
"gzlib.c",
|
||||
"gzread.c",
|
||||
"gzwrite.c",
|
||||
"infback.c",
|
||||
"inffast.c",
|
||||
"inflate.c",
|
||||
"inftrees.c",
|
||||
"trees.c",
|
||||
"uncompr.c",
|
||||
"zutil.c",
|
||||
],
|
||||
hdrs = _ZLIB_PREFIXED_HEADERS,
|
||||
copts = select({
|
||||
":windows": [],
|
||||
"//conditions:default": [
|
||||
"-Wno-unused-variable",
|
||||
"-Wno-implicit-function-declaration",
|
||||
],
|
||||
}),
|
||||
includes = ["zlib/include/"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
config_setting(
|
||||
name = "windows",
|
||||
constraint_values = [
|
||||
"@platforms//os:windows",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# This BUILD file marks bazel/tools/ as a Bazel package so that loads
|
||||
# of the form `//bazel/tools:xxx.bzl` are valid. The directory only
|
||||
# hosts .bzl files (brpc_proto_library.bzl / proto_gen.bzl) and does
|
||||
# not define any build targets.
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(glob(["*.bzl"]))
|
||||
@@ -0,0 +1,138 @@
|
||||
# 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.
|
||||
#
|
||||
# brpc_proto_library: a proto -> cc_library macro that is robust against
|
||||
# protobuf and Bazel version churn.
|
||||
#
|
||||
# Background:
|
||||
# - Old protobuf (< 3.21) used to expose a `cc_proto_library` macro
|
||||
# via `@com_google_protobuf//:protobuf.bzl`; newer protobuf removed
|
||||
# that macro.
|
||||
# - Newer protobuf expects callers to use Bazel's native
|
||||
# `proto_library` / `cc_proto_library` (or the `cc_proto_library`
|
||||
# re-exported from `@com_google_protobuf//bazel:cc_proto_library.bzl`),
|
||||
# but older protobuf does not surface a complete `ProtoInfo`, so
|
||||
# the native rules cannot build it.
|
||||
# - Bazel 8 additionally removed `cc_proto_library` from
|
||||
# `@rules_cc//cc:defs.bzl` (the load now resolves to a stub rule
|
||||
# that fails analysis), forcing every caller to either load it
|
||||
# from `@com_google_protobuf` or replace it.
|
||||
# - Bazel's `load()` is static and cannot be conditional on the
|
||||
# protobuf version, so a single brpc_proto_library.bzl cannot
|
||||
# directly satisfy both old and new protobuf by switching imports.
|
||||
#
|
||||
# Solution (inspired by https://github.com/trpc-group/trpc-cpp/blob/a69d2950f4a9c16b3cb40a2750c69d8940c96820/trpc/trpc.bzl):
|
||||
# We implement the proto compilation rule from scratch (see
|
||||
# proto_gen.bzl in this directory). Dependency information is
|
||||
# propagated through Bazel's native `ProtoInfo` provider, and the
|
||||
# default well-known-proto source is
|
||||
# `@com_google_protobuf//:descriptor_proto` -- a `proto_library`
|
||||
# whose name has been stable across protobuf 3.x / 27.x / 34.x.
|
||||
load("//bazel/tools:proto_gen.bzl", "brpc_proto_gen")
|
||||
|
||||
# `:descriptor_proto` is the protobuf repo's `proto_library` target;
|
||||
# its label has been stable since protobuf 3.x. We consume it through
|
||||
# Bazel's native `ProtoInfo` provider, which is uniform across PB
|
||||
# versions. If a caller's .proto needs additional well-known protos
|
||||
# (any.proto, timestamp.proto, ...), they can pass extra entries via
|
||||
# the `proto_deps` argument.
|
||||
_DEFAULT_PROTO_DEPS = ["@com_google_protobuf//:descriptor_proto"]
|
||||
|
||||
def brpc_proto_library(
|
||||
name,
|
||||
srcs,
|
||||
deps = [],
|
||||
include = None,
|
||||
proto_deps = None,
|
||||
visibility = None,
|
||||
testonly = 0):
|
||||
"""Generate a cc_library from a set of .proto files.
|
||||
|
||||
Args:
|
||||
name: target name.
|
||||
srcs: list of .proto files, paths relative to the current package.
|
||||
deps: list of other `brpc_proto_library` targets this target
|
||||
depends on. The macro internally translates these labels
|
||||
to the underlying `<name>_genproto` rule so that .proto
|
||||
include paths and sources propagate.
|
||||
include: protoc `-I` root AND the resulting cc_library `includes`
|
||||
root, relative to the current package.
|
||||
When omitted, "" or None, the include root is the
|
||||
current package itself (suitable for .proto files
|
||||
sitting directly under the package root, as in `test/`
|
||||
and `example/...`). The root `BUILD.bazel` of brpc must
|
||||
pass `"src"` so that code can reference the protos as
|
||||
`import "brpc/foo.proto"`.
|
||||
proto_deps: list of native `proto_library` dependencies
|
||||
(well-known protos or external .proto libraries).
|
||||
Defaults to
|
||||
`["@com_google_protobuf//:descriptor_proto"]`.
|
||||
Pass `[]` explicitly to disable the default; pass
|
||||
None (the default) to use it.
|
||||
visibility: same semantics as cc_library.
|
||||
testonly: same semantics as cc_library.
|
||||
"""
|
||||
|
||||
real_include = include if include != None else ""
|
||||
real_proto_deps = proto_deps if proto_deps != None else _DEFAULT_PROTO_DEPS
|
||||
|
||||
gen_name = name + "_genproto"
|
||||
brpc_proto_gen(
|
||||
name = gen_name,
|
||||
srcs = srcs,
|
||||
# Rewrite each `brpc_proto_library` dep to its underlying
|
||||
# `<dep>_genproto` rule, which exports BrpcProtoInfo.
|
||||
deps = [d + "_genproto" for d in deps],
|
||||
include = real_include,
|
||||
proto_deps = real_proto_deps,
|
||||
visibility = visibility,
|
||||
testonly = testonly,
|
||||
)
|
||||
|
||||
# Split the generated files into .pb.h / .pb.cc via OutputGroupInfo
|
||||
# and feed them to cc_library's hdrs / srcs separately. Bazel
|
||||
# rejects declaring the same File in both hdrs and srcs, so a
|
||||
# plain DefaultInfo wouldn't work here.
|
||||
native.filegroup(
|
||||
name = gen_name + "_hdrs",
|
||||
srcs = [":" + gen_name],
|
||||
output_group = "hdrs",
|
||||
visibility = visibility,
|
||||
testonly = testonly,
|
||||
)
|
||||
native.filegroup(
|
||||
name = gen_name + "_srcs",
|
||||
srcs = [":" + gen_name],
|
||||
output_group = "srcs",
|
||||
visibility = visibility,
|
||||
testonly = testonly,
|
||||
)
|
||||
|
||||
native.cc_library(
|
||||
name = name,
|
||||
srcs = [":" + gen_name + "_srcs"],
|
||||
hdrs = [":" + gen_name + "_hdrs"],
|
||||
# cc_library `includes` is required, otherwise the .pb.cc
|
||||
# files inside this cc_library cannot find the .pb.h headers
|
||||
# they just generated (the headers live under
|
||||
# bazel-bin/<package>/<include>/...). When include="" we pass
|
||||
# "." to mean "the current package itself"; Bazel then exposes
|
||||
# both `-I <package>` and `-I bazel-bin/<package>`
|
||||
# automatically to dependents.
|
||||
includes = [real_include if real_include else "."],
|
||||
deps = deps + ["@com_google_protobuf//:protobuf"],
|
||||
visibility = visibility,
|
||||
testonly = testonly,
|
||||
)
|
||||
@@ -0,0 +1,240 @@
|
||||
# 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.
|
||||
#
|
||||
# brpc's self-implemented proto -> .pb.{h,cc} compilation rule.
|
||||
#
|
||||
# Cross-protobuf-version compatibility strategy (inspired by trpc.bzl):
|
||||
# 1) Do NOT load `@com_google_protobuf//:protobuf.bzl` and do NOT
|
||||
# call `native.cc_proto_library` -- this side-steps the double
|
||||
# incompatibility of newer protobuf removing the macro and older
|
||||
# protobuf missing a complete `ProtoInfo` provider.
|
||||
# 2) Do NOT hardcode any protobuf-repo-internal filegroup names
|
||||
# (such as `well_known_protos` / `descriptor_proto_srcs`) -- the
|
||||
# names of those filegroups have shifted between protobuf 3.x
|
||||
# and 27.x+.
|
||||
# 3) Propagate dependencies via Bazel's native `ProtoInfo` provider:
|
||||
# callers pass `proto_library` targets through `proto_deps`
|
||||
# (defaulting to `@com_google_protobuf//:descriptor_proto`, a
|
||||
# `proto_library` whose label has been stable across PB versions),
|
||||
# and this rule walks `transitive_sources` / `transitive_proto_path`
|
||||
# to collect .proto source files and protoc `-I` paths -- fully
|
||||
# decoupled from the PB version.
|
||||
# 4) Among `brpc_proto_gen` targets themselves, dependencies are
|
||||
# propagated via the custom `BrpcProtoInfo` provider declared
|
||||
# below.
|
||||
#
|
||||
# Note on minimum Bazel version: this rule itself does not call any
|
||||
# Bazel-version-specific API, but it still consumes `ProtoInfo` from
|
||||
# `proto_library` targets (e.g. `descriptor_proto`) defined inside
|
||||
# the protobuf repo. Protobuf >= 34 calls
|
||||
# `ProtoInfo(option_deps = ..., extension_declarations = ...)` in its
|
||||
# own `proto_library` implementation, and those fields are only
|
||||
# accepted by Bazel >= 8. In other words, the minimum Bazel version
|
||||
# brpc requires is whatever the bundled protobuf version requires --
|
||||
# we just don't add any extra requirement on top of that.
|
||||
|
||||
BrpcProtoInfo = provider(
|
||||
doc = "Carries transitive .proto sources and protoc -I flags between brpc_proto_gen targets.",
|
||||
fields = {
|
||||
"transitive_srcs": "depset of all transitive .proto Files.",
|
||||
"transitive_imports": "depset of strings, all -I flags needed by protoc.",
|
||||
},
|
||||
)
|
||||
|
||||
def _resolve_include_dir(ctx):
|
||||
"""Compute this target's protoc include root (workspace-relative).
|
||||
|
||||
Examples:
|
||||
ctx.label.package = "" + include = "src" -> "src"
|
||||
ctx.label.package = "test" + include = "" -> "test"
|
||||
ctx.label.package = "" + include = "" -> "."
|
||||
"""
|
||||
pkg = ctx.label.package
|
||||
inc = ctx.attr.include.rstrip("/")
|
||||
if pkg and inc:
|
||||
return pkg + "/" + inc
|
||||
if pkg:
|
||||
return pkg
|
||||
if inc:
|
||||
return inc
|
||||
return "."
|
||||
|
||||
def _proto_gen_impl(ctx):
|
||||
srcs = ctx.files.srcs
|
||||
include_dir = _resolve_include_dir(ctx)
|
||||
bin_root = ctx.bin_dir.path
|
||||
|
||||
# `-I` flags for this target itself: the source-tree root plus
|
||||
# the corresponding bin-dir root. The bin-dir entry is needed
|
||||
# when a transitive dep generates .proto files into bazel-bin
|
||||
# (e.g. via a custom code generator).
|
||||
own_imports = ["-I" + include_dir]
|
||||
if include_dir == ".":
|
||||
own_imports.append("-I" + bin_root)
|
||||
else:
|
||||
own_imports.append("-I" + bin_root + "/" + include_dir)
|
||||
|
||||
# Collect transitive info from other `brpc_proto_gen` deps.
|
||||
dep_srcs_list = [d[BrpcProtoInfo].transitive_srcs for d in ctx.attr.deps]
|
||||
dep_imports_list = [d[BrpcProtoInfo].transitive_imports for d in ctx.attr.deps]
|
||||
|
||||
# Collect native `proto_library` deps (well-known protos or
|
||||
# external .proto libraries). Key design: .proto sources and
|
||||
# `-I` paths are obtained via Bazel's native `ProtoInfo`,
|
||||
# avoiding any reliance on protobuf-repo-internal filegroup
|
||||
# names (descriptor_proto_srcs / well_known_protos / ...).
|
||||
# `transitive_proto_path` already accounts for the
|
||||
# `_virtual_imports` paths created by `strip_import_prefix`, so
|
||||
# protoc can consume the paths directly.
|
||||
#
|
||||
# Important: `strip_import_prefix` causes .proto files to live at
|
||||
# virtual paths under bazel-out/<config>/bin/<workspace_root>/<virtual>
|
||||
# (see trpc.bzl for prior art). For every `proto_dep` repo we
|
||||
# therefore expose FOUR candidate `-I` paths to be safe:
|
||||
# 1) <workspace_root> -- source root (plain proto_library)
|
||||
# 2) <bin_dir>/<workspace_root> -- virtual root after strip_import_prefix
|
||||
# 3) <workspace_root>/src -- fallback for older PB descriptor_proto without strip
|
||||
# 4) <bin_dir>/<workspace_root>/src -- same as #3, under bin-dir
|
||||
# protoc only warns on non-existent `-I` paths, so this is harmless.
|
||||
proto_dep_src_depsets = []
|
||||
proto_dep_imports = []
|
||||
extra_pb_root_imports = []
|
||||
# Belt-and-suspenders: also feed each `proto_dep`'s default outputs
|
||||
# (i.e. the .proto files themselves) into `inputs`, in case the
|
||||
# ProtoInfo's transitive_sources does not cover everything we need.
|
||||
proto_dep_src_depsets.append(depset(direct = ctx.files.proto_deps))
|
||||
for pd in ctx.attr.proto_deps:
|
||||
pi = pd[ProtoInfo]
|
||||
proto_dep_src_depsets.append(pi.transitive_sources)
|
||||
for path in pi.transitive_proto_path.to_list():
|
||||
proto_dep_imports.append("-I" + path)
|
||||
wsroot = pd.label.workspace_root
|
||||
if wsroot:
|
||||
extra_pb_root_imports.append("-I" + wsroot)
|
||||
extra_pb_root_imports.append("-I" + bin_root + "/" + wsroot)
|
||||
extra_pb_root_imports.append("-I" + wsroot + "/src")
|
||||
extra_pb_root_imports.append("-I" + bin_root + "/" + wsroot + "/src")
|
||||
# Deduplicate the workspace-level `-I` entries so the same repo
|
||||
# is not listed multiple times when several proto_deps share it.
|
||||
proto_dep_imports.extend(depset(extra_pb_root_imports).to_list())
|
||||
|
||||
all_imports = depset(
|
||||
direct = own_imports + proto_dep_imports,
|
||||
transitive = dep_imports_list,
|
||||
)
|
||||
|
||||
# Output files: every .proto produces a .pb.h and a .pb.cc.
|
||||
# NB: the `path` argument of `ctx.actions.declare_file(path)` is
|
||||
# *relative to the current package*, while `src.short_path` is
|
||||
# *relative to the workspace root*. The two differ by the package
|
||||
# prefix; we must strip that prefix before calling declare_file,
|
||||
# otherwise the outputs would land at
|
||||
# bazel-bin/<pkg>/<pkg>/... (one level too deep).
|
||||
pkg = ctx.label.package
|
||||
pkg_prefix = pkg + "/" if pkg else ""
|
||||
outs = []
|
||||
for src in srcs:
|
||||
if not src.short_path.endswith(".proto"):
|
||||
fail("brpc_proto_gen srcs must be .proto, got: %s" % src.short_path)
|
||||
|
||||
rel = src.short_path
|
||||
if pkg_prefix and rel.startswith(pkg_prefix):
|
||||
rel = rel[len(pkg_prefix):]
|
||||
base = rel[:-len(".proto")]
|
||||
outs.append(ctx.actions.declare_file(base + ".pb.h"))
|
||||
outs.append(ctx.actions.declare_file(base + ".pb.cc"))
|
||||
|
||||
# protoc's --cpp_out points at the include root under bin_root.
|
||||
# After protoc organizes outputs by their import-relative path,
|
||||
# the .pb.{h,cc} files land exactly where declare_file declared
|
||||
# them above.
|
||||
if include_dir == ".":
|
||||
cpp_out_dir = bin_root
|
||||
else:
|
||||
cpp_out_dir = bin_root + "/" + include_dir
|
||||
|
||||
args = ctx.actions.args()
|
||||
args.add_all(all_imports.to_list())
|
||||
args.add("--cpp_out=" + cpp_out_dir)
|
||||
args.add_all([s.path for s in srcs])
|
||||
|
||||
ctx.actions.run(
|
||||
executable = ctx.executable.protoc,
|
||||
arguments = [args],
|
||||
inputs = depset(
|
||||
direct = srcs,
|
||||
transitive = dep_srcs_list + proto_dep_src_depsets,
|
||||
),
|
||||
outputs = outs,
|
||||
mnemonic = "BrpcProtoCompile",
|
||||
progress_message = "BrpcProtoCompile %s" % ctx.label,
|
||||
)
|
||||
|
||||
hdr_files = [f for f in outs if f.path.endswith(".pb.h")]
|
||||
src_files = [f for f in outs if f.path.endswith(".pb.cc")]
|
||||
|
||||
return [
|
||||
DefaultInfo(files = depset(outs)),
|
||||
# Split outputs so the brpc_proto_library macro can route
|
||||
# them to cc_library.hdrs vs cc_library.srcs separately.
|
||||
OutputGroupInfo(
|
||||
hdrs = depset(hdr_files),
|
||||
srcs = depset(src_files),
|
||||
),
|
||||
BrpcProtoInfo(
|
||||
transitive_srcs = depset(direct = srcs, transitive = dep_srcs_list),
|
||||
transitive_imports = all_imports,
|
||||
),
|
||||
]
|
||||
|
||||
brpc_proto_gen = rule(
|
||||
implementation = _proto_gen_impl,
|
||||
attrs = {
|
||||
"srcs": attr.label_list(
|
||||
allow_files = [".proto"],
|
||||
mandatory = True,
|
||||
),
|
||||
# Other `brpc_proto_gen` targets; deps are propagated via the
|
||||
# custom `BrpcProtoInfo` provider.
|
||||
"deps": attr.label_list(
|
||||
providers = [BrpcProtoInfo],
|
||||
default = [],
|
||||
),
|
||||
# The include root, relative to the current BUILD package
|
||||
# (e.g. "src" for the brpc root BUILD).
|
||||
"include": attr.string(default = ""),
|
||||
"protoc": attr.label(
|
||||
default = Label("@com_google_protobuf//:protoc"),
|
||||
executable = True,
|
||||
# `"host"` is the legacy spelling of `"exec"`. Modern
|
||||
# Bazel (>= 6) prefers `"exec"`, but still accepts
|
||||
# `"host"` as an alias through at least Bazel 8.x. We
|
||||
# keep `"host"` here for the broad range of Bazel
|
||||
# versions used to build brpc; switch to `"exec"` once
|
||||
# `"host"` is finally removed (likely a future major
|
||||
# Bazel release).
|
||||
cfg = "host",
|
||||
allow_files = True,
|
||||
),
|
||||
# Native `proto_library` deps (well-known protos or external
|
||||
# .proto libraries). .proto sources and -I paths are obtained
|
||||
# automatically through Bazel's native `ProtoInfo`, fully
|
||||
# decoupled from the protobuf version.
|
||||
"proto_deps": attr.label_list(
|
||||
providers = [ProtoInfo],
|
||||
default = [],
|
||||
),
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
# 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.
|
||||
|
||||
cmake_minimum_required(VERSION 3.16...3.28)
|
||||
|
||||
project(googletest-download NONE)
|
||||
|
||||
include(ExternalProject)
|
||||
ExternalProject_Add(googletest
|
||||
GIT_REPOSITORY https://github.com/google/googletest.git
|
||||
GIT_TAG release-1.12.0
|
||||
SOURCE_DIR "${PROJECT_BINARY_DIR}/googletest-src"
|
||||
BINARY_DIR "${PROJECT_BINARY_DIR}/googletest-build"
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
TEST_COMMAND ""
|
||||
)
|
||||
@@ -0,0 +1,34 @@
|
||||
# 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.
|
||||
|
||||
function(compile_proto OUT_HDRS OUT_SRCS DESTDIR HDR_OUTPUT_DIR PROTO_DIR PROTO_FILES)
|
||||
foreach(P ${PROTO_FILES})
|
||||
string(REPLACE .proto .pb.h HDR ${P})
|
||||
set(HDR_RELATIVE ${HDR})
|
||||
set(HDR ${DESTDIR}/${HDR})
|
||||
string(REPLACE .proto .pb.cc SRC ${P})
|
||||
set(SRC ${DESTDIR}/${SRC})
|
||||
list(APPEND HDRS ${HDR})
|
||||
list(APPEND SRCS ${SRC})
|
||||
add_custom_command(
|
||||
OUTPUT ${HDR} ${SRC}
|
||||
COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} ${PROTOC_FLAGS} -I${PROTO_DIR} --cpp_out=${DESTDIR} ${PROTO_DIR}/${P}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${HDR} ${HDR_OUTPUT_DIR}/${HDR_RELATIVE}
|
||||
DEPENDS ${PROTO_DIR}/${P}
|
||||
)
|
||||
endforeach()
|
||||
set(${OUT_HDRS} ${HDRS} PARENT_SCOPE)
|
||||
set(${OUT_SRCS} ${SRCS} PARENT_SCOPE)
|
||||
endfunction()
|
||||
@@ -0,0 +1,77 @@
|
||||
# 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.
|
||||
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# find_package(BORINGSSL)
|
||||
#
|
||||
# Variables used by this module, they can change the default behaviour and need
|
||||
# to be set before calling find_package:
|
||||
#
|
||||
# BORINGSSL_ROOT_DIR Set this variable to the root installation of
|
||||
# boringssl if the module has problems finding the
|
||||
# proper installation path.
|
||||
#
|
||||
# Variables defined by this module:
|
||||
#
|
||||
# BORINGSSL_FOUND System has boringssl, include and library dirs found
|
||||
# BORINGSSL_INCLUDE_DIR The boringssl include directories.
|
||||
# BORINGSSL_LIBRARIES The boringssl libraries.
|
||||
# BORINGSSL_CRYPTO_LIBRARY The boringssl crypto library.
|
||||
# BORINGSSL_SSL_LIBRARY The boringssl ssl library.
|
||||
# BORING_USE_STATIC_LIBS Whether use static library.
|
||||
|
||||
if(BORING_USE_STATIC_LIBS)
|
||||
set(_boringssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
if(MSVC)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
else()
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_path(BORINGSSL_ROOT_DIR
|
||||
NAMES include/openssl/ssl.h include/openssl/base.h include/openssl/hkdf.h
|
||||
HINTS ${BORINGSSL_ROOT_DIR})
|
||||
|
||||
find_path(BORINGSSL_INCLUDE_DIR
|
||||
NAMES openssl/ssl.h openssl/base.h openssl/hkdf.h
|
||||
HINTS ${BORINGSSL_ROOT_DIR}/include)
|
||||
|
||||
find_library(BORINGSSL_SSL_LIBRARY
|
||||
NAMES ssl
|
||||
HINTS ${BORINGSSL_ROOT_DIR}/lib)
|
||||
|
||||
find_library(BORINGSSL_CRYPTO_LIBRARY
|
||||
NAMES crypto
|
||||
HINTS ${BORINGSSL_ROOT_DIR}/lib)
|
||||
|
||||
set(BORINGSSL_LIBRARIES ${BORINGSSL_SSL_LIBRARY} ${BORINGSSL_CRYPTO_LIBRARY}
|
||||
CACHE STRING "BoringSSL SSL and crypto libraries" FORCE)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(BoringSSL DEFAULT_MSG
|
||||
BORINGSSL_LIBRARIES
|
||||
BORINGSSL_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
BORINGSSL_ROOT_DIR
|
||||
BORINGSSL_INCLUDE_DIR
|
||||
BORINGSSL_LIBRARIES
|
||||
BORINGSSL_CRYPTO_LIBRARY
|
||||
BORINGSSL_SSL_LIBRARY
|
||||
)
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boringssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
@@ -0,0 +1,41 @@
|
||||
# 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.
|
||||
|
||||
set(_gflags_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
|
||||
find_path(GFLAGS_INCLUDE_PATH gflags/gflags.h)
|
||||
|
||||
if (GFLAGS_STATIC)
|
||||
if (WIN32)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .lib ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
else (WIN32)
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a ${CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
endif (WIN32)
|
||||
endif (GFLAGS_STATIC)
|
||||
find_library(GFLAGS_LIBRARY NAMES gflags libgflags)
|
||||
if(GFLAGS_INCLUDE_PATH AND GFLAGS_LIBRARY)
|
||||
set(GFLAGS_FOUND TRUE)
|
||||
endif(GFLAGS_INCLUDE_PATH AND GFLAGS_LIBRARY)
|
||||
if(GFLAGS_FOUND)
|
||||
if(NOT GFLAGS_FIND_QUIETLY)
|
||||
message(STATUS "Found gflags: ${GFLAGS_LIBRARY}")
|
||||
endif(NOT GFLAGS_FIND_QUIETLY)
|
||||
else(GFLAGS_FOUND)
|
||||
if(GFLAGS_FIND_REQUIRED)
|
||||
message(FATAL_ERROR "Could not find gflags library.")
|
||||
endif(GFLAGS_FIND_REQUIRED)
|
||||
endif(GFLAGS_FOUND)
|
||||
|
||||
set(CMAKE_FIND_LIBRARY_SUFFIXES ${_gflags_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
|
||||
@@ -0,0 +1,66 @@
|
||||
# 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.
|
||||
|
||||
# Tries to find Gperftools.
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# find_package(Gperftools)
|
||||
#
|
||||
# Variables used by this module, they can change the default behaviour and need
|
||||
# to be set before calling find_package:
|
||||
#
|
||||
# Gperftools_ROOT_DIR Set this variable to the root installation of
|
||||
# Gperftools if the module has problems finding
|
||||
# the proper installation path.
|
||||
#
|
||||
# Variables defined by this module:
|
||||
#
|
||||
# GPERFTOOLS_FOUND System has Gperftools libs/headers
|
||||
# GPERFTOOLS_LIBRARIES The Gperftools libraries (tcmalloc & profiler)
|
||||
# GPERFTOOLS_INCLUDE_DIR The location of Gperftools headers
|
||||
|
||||
find_library(GPERFTOOLS_TCMALLOC
|
||||
NAMES tcmalloc
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_library(GPERFTOOLS_PROFILER
|
||||
NAMES profiler
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_library(GPERFTOOLS_TCMALLOC_AND_PROFILER
|
||||
NAMES tcmalloc_and_profiler
|
||||
HINTS ${Gperftools_ROOT_DIR}/lib)
|
||||
|
||||
find_path(GPERFTOOLS_INCLUDE_DIR
|
||||
NAMES gperftools/heap-profiler.h
|
||||
HINTS ${Gperftools_ROOT_DIR}/include)
|
||||
|
||||
set(GPERFTOOLS_LIBRARIES ${GPERFTOOLS_TCMALLOC_AND_PROFILER})
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(
|
||||
Gperftools
|
||||
DEFAULT_MSG
|
||||
GPERFTOOLS_LIBRARIES
|
||||
GPERFTOOLS_INCLUDE_DIR)
|
||||
|
||||
mark_as_advanced(
|
||||
Gperftools_ROOT_DIR
|
||||
GPERFTOOLS_TCMALLOC
|
||||
GPERFTOOLS_PROFILER
|
||||
GPERFTOOLS_TCMALLOC_AND_PROFILER
|
||||
GPERFTOOLS_LIBRARIES
|
||||
GPERFTOOLS_INCLUDE_DIR)
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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.
|
||||
|
||||
# Setup googletest
|
||||
configure_file("${PROJECT_SOURCE_DIR}/cmake/CMakeLists.download_gtest.in" ${PROJECT_BINARY_DIR}/googletest-download/CMakeLists.txt)
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -G "${CMAKE_GENERATOR}" .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/googletest-download
|
||||
)
|
||||
if(result)
|
||||
message(FATAL_ERROR "CMake step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} --build .
|
||||
RESULT_VARIABLE result
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/googletest-download
|
||||
)
|
||||
if(result)
|
||||
message(FATAL_ERROR "Build step for googletest failed: ${result}")
|
||||
endif()
|
||||
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
|
||||
add_subdirectory(${PROJECT_BINARY_DIR}/googletest-src
|
||||
${PROJECT_BINARY_DIR}/googletest-build
|
||||
EXCLUDE_FROM_ALL)
|
||||
@@ -0,0 +1,26 @@
|
||||
# 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.
|
||||
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
|
||||
Name: brpc
|
||||
Description: Apache bRPC is an Industrial-grade RPC framework using C++ Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. "bRPC" means "better RPC".
|
||||
Version: @BRPC_VERSION@
|
||||
Cflags: -I${includedir}
|
||||
Libs: -L${libdir}/ -lbrpc
|
||||
Libs.private: @BRPC_PRIVATE_LIBS@
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env 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.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
g_package_link=${1}
|
||||
g_package_keys=${2:-https://downloads.apache.org/brpc/KEYS}
|
||||
|
||||
g_valid_package_link=' '
|
||||
g_valid_package_name=' '
|
||||
g_valid_package_checksum=' '
|
||||
g_valid_package_sig=' '
|
||||
g_valid_package_content=' '
|
||||
g_valid_package_license=' '
|
||||
g_valid_package_binary=' '
|
||||
|
||||
summary() {
|
||||
cat <<EOF
|
||||
|
||||
- [${g_valid_package_link}] the links of the package are valid;
|
||||
- [${g_valid_package_checksum}] the checksum of the package is valid;
|
||||
- [${g_valid_package_sig}] the signature of the package is valid;
|
||||
- [${g_valid_package_content}] RELEASE_VERSION in the source code matches the current release;
|
||||
- [${g_valid_package_license}] LICENSE and NOTICE are not absent, note that we use CI based on Skywalking-eyes to check the license;
|
||||
- [${g_valid_package_binary}] no compiled archives bundled in the source archive.
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
on_exit() {
|
||||
summary
|
||||
}
|
||||
|
||||
validate_package() {
|
||||
local ver=$(echo ${g_package_link%/} | rev | cut -d'/' -f1 | rev)
|
||||
local package_name="apache-brpc-${ver}-src.tar.gz"
|
||||
|
||||
for suffix in "" ".asc" ".sha512"; do
|
||||
wget --quiet -c "${g_package_link%/}/${package_name}${suffix}"
|
||||
done
|
||||
|
||||
g_valid_package_link='x'
|
||||
|
||||
sha512sum --status -c ${package_name}.sha512 \
|
||||
&& g_valid_package_checksum='x'
|
||||
|
||||
# Import keys published by the author,
|
||||
# and verify the package is signed by the author.
|
||||
# No need to trust the public keys.
|
||||
wget --quiet ${g_package_keys} -O - | gpg --import \
|
||||
&& gpg --verify ${package_name}.asc \
|
||||
&& g_valid_package_sig='x'
|
||||
|
||||
tar -xf ${package_name}
|
||||
|
||||
(
|
||||
pushd ${package_name%.tar.gz} > /dev/null 2>&1
|
||||
[[ -f RELEASE_VERSION ]] \
|
||||
&& [[ $(cat RELEASE_VERSION) = "${ver}" ]] \
|
||||
&& grep BRPC_VERSION CMakeLists.txt | grep -q ${ver}
|
||||
) && g_valid_package_content='x'
|
||||
|
||||
(
|
||||
pushd ${package_name%.tar.gz} > /dev/null 2>&1
|
||||
[[ -f LICENSE && -f NOTICE ]]
|
||||
) && g_valid_package_license='x'
|
||||
|
||||
local has_unexpected_binary=
|
||||
for i in $(find ${package_name%.tar.gz} -type f); do
|
||||
file ${i} | grep -v 'GIF\|JPEG\|PNG\|SVG\|PowerPoint\|Git\|JSON\|PEM\|empty\|text' && has_unexpected_binary=1
|
||||
done
|
||||
[[ -z "${has_unexpected_binary}" ]] && g_valid_package_binary='x'
|
||||
}
|
||||
|
||||
trap on_exit EXIT
|
||||
|
||||
validate_package
|
||||
@@ -0,0 +1,130 @@
|
||||
# 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、凤巢、地图等)
|
||||
* 集群规模: 4000多个活跃模块,600w以上实例
|
||||
* 使用版本: baidu内部版本
|
||||
* 信息提供者:wwbmmm
|
||||
|
||||
## brpc在维沃的落地情况
|
||||
* 公司名称: 维沃(vivo)
|
||||
* 落地项目: 在线推荐系统
|
||||
* 使用版本: 社区版本0.9.7
|
||||
* 信息提供者:guodongxiaren
|
||||
|
||||
## brpc在爱奇艺的落地情况
|
||||
* 公司名称: 爱奇艺(iqiyi)
|
||||
* 落地项目: 广告、推荐、搜索
|
||||
* 使用版本: 基于社区版本定制
|
||||
* 集群规模: 3000+台机器(广告)
|
||||
* 信息提供者:cdjingit
|
||||
|
||||
## brpc在第四范式的落地情况
|
||||
* 公司名称: 第四范式(4paradigm)
|
||||
* 落地项目: 风控、推荐、智能运维等
|
||||
* 使用版本: 基于社区版本定制
|
||||
* 信息提供者:dl239
|
||||
|
||||
## brpc在 小红书的落地情况
|
||||
* 公司名称: 小红书
|
||||
* 落地项目: 推荐系统,基础架构(存储等)
|
||||
* 集群规模: 1w以上实例(推荐)
|
||||
* 使用版本: 基于社区版本定制
|
||||
* 信息提供者:lzfhust
|
||||
|
||||
## brpc在作业帮的落地情况
|
||||
* 公司名称: 作业帮
|
||||
* 落地项目: 长连接IM,消息分发系统
|
||||
* 集群规模: 2000+核
|
||||
* 使用版本: 基于0.9.7定制
|
||||
* 信息提供者:xdh0817
|
||||
|
||||
## brpc在欢聚时代的落地情况
|
||||
* 公司名称: 欢聚时代
|
||||
* 落地项目: 推荐、直播
|
||||
* 使用版本: 基于社区版本定制
|
||||
* 信息提供者:chenBright
|
||||
|
||||
## brpc 在 Apache Doris 中的应用
|
||||
* 落地项目:Apache Doris
|
||||
* 使用版本:1.2.0
|
||||
* 使用情况:Apache Doris 作为一款 MPP 分析型数据库,其内部节点间使用 Apache Brpc 作为主要 RPC 框架。Brpc 为 Doris 提供了稳定易用的高性能通信机制。并且 BRPC 提供的 bthread,bvar 等基础库,以及各种性能调试工具,也极大的方便了 Doris 的开发和调试工作。
|
||||
* 信息提供者:morningman
|
||||
|
||||
## brpc在BaikalDB中的应用
|
||||
* 落地项目:BaikalDB
|
||||
* 使用版本:社区版0.9.7&百度内部stable
|
||||
* 使用情况:BaikalDB是一款面向OLTP场景为主的NewSQL数据库,其内部节点均采用brpc框架通信,实现数据处理和复制以及集群管理,brpc的高性能对OLTP场景的低延迟至关重要,同时brpc集成的性能调优工具对SQL性能优化提效显著。
|
||||
* 信息提供者:tullyliu
|
||||
|
||||
## brpc在数美科技的落地情况
|
||||
* 公司名称: 数美科技(nextdata)
|
||||
* 落地项目: 智能风控业务(模型特征服务、规则引擎)
|
||||
* 使用版本: 1.0.0
|
||||
* 信息提供者:day253
|
||||
|
||||
## brpc在信网信息的落地情况
|
||||
* 公司名称:信网(eyou.com)
|
||||
* 落地项目:分布式元数据存储引擎服务
|
||||
* 使用版本:1.3.0
|
||||
* 集群规模:10台+
|
||||
* 信息提供者:haihuju
|
||||
|
||||
## brpc在滴滴的落地情况
|
||||
* 公司名称:滴滴(www.didiglobal.com)
|
||||
* 落地项目:地图等业务
|
||||
* 使用版本:0.9.3
|
||||
* 信息提供者:NIGHTFIGHTING
|
||||
|
||||
## brpc在网易的实现
|
||||
* 公司名称:网易
|
||||
* 实施项目:高性能分布式存储系统Curve。
|
||||
* 集群规模:单个集群30,000个节点
|
||||
* 使用版本:1.3.0
|
||||
* 信息提供者:Divyansh200102
|
||||
|
||||
## brpc在搜狗的实现
|
||||
* 公司名称:搜狗
|
||||
* 实现项目:SRPC是搜狗几乎所有在线服务都使用的企业级RPC系统之一
|
||||
主要功能包括支持 bRPC 等 RPC 协议。
|
||||
* IDL:PB
|
||||
* 通讯方式:TCP
|
||||
* 网络数据:二进制
|
||||
* 信息提供者:Divyansh20010
|
||||
|
||||
## brpc在B站的实现
|
||||
* 公司名称:哔哩哔哩
|
||||
* 实现项目:支持bilibili在获取服务器节点时发现命名服务的zone,支持bilibili发现服务
|
||||
* 使用版本:0.9.7,0.9.6
|
||||
* 信息提供者:Divyansh200102
|
||||
|
||||
## brpc在腾讯的实现
|
||||
* 公司名称:腾讯
|
||||
* 已实现的项目:Doris、StarRocks是后端的BRPC端口,用于后端实例之间的通信。
|
||||
* 端口号:8060
|
||||
* 信息提供者:Divyansh200102
|
||||
|
||||
## brpc在阿里巴巴的实现
|
||||
* 公司名称:阿里巴巴
|
||||
* 已实现项目:阿里云,bRPC端口用于与另一个BE通信。
|
||||
* 端口名称:brpc_port
|
||||
* 默认端口号:8060
|
||||
* 通讯方向:FE <--> BE,BE <--> BE
|
||||
* 信息提供者:Divyansh200102
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,68 @@
|
||||
# 这里记录发展Committer 和 PPMC成员的流程和参考网站信息
|
||||
|
||||
## 1. 如何发展committer
|
||||
|
||||
### 前置条件
|
||||
1. 贡献者commit数量达到10个以上
|
||||
2. 贡献者个人有意愿接受邀请成为committer
|
||||
3. 贡献者订阅dev@brpc.apache.org,并发邮件介绍自己
|
||||
|
||||
### 成为committer的路程
|
||||
1. 提名者在private@brpc中发起讨论和投票,投票通过即OK (最少3+1, +1 > -1),[邮件模版](https://community.apache.org/newcommitter.html#committer-vote-template)
|
||||
2. 提名者发送close vote邮件给private@brpc, 标题可以为subject [RESULT][VOTE],[邮件模版](https://community.apache.org/newcommitter.html#close-vote)
|
||||
3. 提名者给被提名者发invite letter([邮件模板](https://community.apache.org/newcommitter.html#committer-invite-template)),并得到回复后再提示他提交ICLA([邮件模板](https://community.apache.org/newcommitter.html#committer-accept-template))
|
||||
4. 被提名者填写[CLA](https://www.apache.org/licenses/contributor-agreements.html), 个人贡献者需要下载[ICLA](https://www.apache.org/licenses/icla.pdf)填写个人信息并签名,发送电子版给 secretary@apache.org。(注意:1. ICLA需要填写信息完全,包括邮寄地址和签名,否则会被ASF的秘书打回, 2. ICLA中含有个人信息,所以应该单发给secretary@apache.org,不应该同时抄送PMC或其他邮件组 3. ICLA中的preffered Apache id和notify project虽然是optional,但最好填上,这样秘书会帮新commiter申请好apache id,否则就需要PMC chair或者ASF member去申请)个人信息填写项(除签名外)可以使用 PDF 阅读器或浏览器填写,填写后保存进行签名。签名方式支持:
|
||||
- 打印 该pdf 文件,手工填写表单(姓名、邮箱、邮寄地址),然后手写签名,最后扫描为电子版;
|
||||
- 使用支持手写的设备进行电子签名;
|
||||
- 使用 `gpg` 进行电子签名,即对填写好个人基本信息的 pdf 文件进行操作(需要提前生成与登记邮箱匹配的公钥/密钥对):`gpg --armor --detach-sign icla.pdf`;
|
||||
- 使用 `DocuSign` 进行签名;
|
||||
|
||||
5. 提名者发送announce邮件到dev@brpc.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
|
||||
|
||||
* https://juejin.cn/post/6844903788982042632
|
||||
|
||||
### Suggested steps from secretary@apache.org
|
||||
Please do these things:
|
||||
|
||||
1. Hold the discussion and vote on your private@ list. This avoids any issues related to personnel, which should remain private.
|
||||
2. If the vote is successful, announce the result to the private@ list with a new email thread with subject [RESULT][VOTE]. This makes it easier for secretary to find the result of the vote in order to request the account at the time of the filing of the ICLA.
|
||||
3. Only if the candidate accepts committership, announce the new committer on your dev@ list.
|
||||
|
||||
Doing these things will make everyone's job easier.
|
||||
|
||||
|
||||
|
||||
## 2. 如何把committer变成为PPMC
|
||||
|
||||
### 流程参考:Apache官网文档
|
||||
* https://incubator.apache.org/guides/ppmc.html#voting_in_a_new_ppmc_member
|
||||
* https://community.apache.org/newcommitter.html
|
||||
* https://incubator.apache.org/guides/ppmc.html#podling_project_management_committee_ppmc
|
||||
|
||||
### 实际流程
|
||||
1. 在private@brpc中发起讨论,如果没有反对,则继续
|
||||
2. 在private@brpc中发起投票
|
||||
3. 在private@brpc中发邮件,结束投票,并通知private@incubator.apache.org
|
||||
4. 在private@brpc中和dev中announce new PPMC
|
||||
5. 设定他的权限,通过访问https://whimsy.apache.org/roster/ppmc/brpc
|
||||
6. 帮他订阅private邮件组,参见https://whimsy.apache.org/committers/moderationhelper.cgi
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# Here is the doc for the process and reference website information of developing committee and PPMC members.
|
||||
|
||||
|
||||
## 1. How to develop committers
|
||||
|
||||
### Preconditions
|
||||
|
||||
1. More than 10 commits from the contributor
|
||||
2. Contributor is willing to accept the invitation to become a committer
|
||||
3. Contributor subscribe to dev@brpc.apache.org and introduce himself by sending an email to dev@apache.org
|
||||
|
||||
### The journey to become a committer
|
||||
|
||||
1. The nominator sends an email to private@brpc to initiate discussion and begins to voting. If the voting is passed, it is OK (at least 3 +1, +1 >- 1). (See the [Vote email template](https://community.apache.org/newcommitter.html#committer-vote-template))
|
||||
2. The nominator sends a close vote email to private@brpc, the title can be subject [RESULT] [VOTE]. (See the [close email template](https://community.apache.org/newcommitter.html#close-vote))
|
||||
3. The nominator sends an invitation letter to the nominee ([email template](https://community.apache.org/newcommitter.html#committer-invite-template)) and prompts him to submit ICLA after receiving a reply ([email template](https://community.apache.org/newcommitter.html#committer-accept-template))
|
||||
4. The nominee fills in [ICLA](https://www.apache.org/licenses/contributor-agreements.html), individual contributors need to download [ICLA](https://www.apache.org/licenses/icla.pdf) Fill in personal information and sign, and send the electronic version to secretary@apache.org 。 (Note: 1. ICLA needs to fill in complete information, including mailing address and signature, otherwise it will be returned by ASF's secretary. 2. ICLA contains personal information, it should be send to secretary@apache.org only, not to the PMC or other mail list) The personal information entry (except for the signature) can be filled in with a PDF reader or browser, and then saved for signature. Signature method support:
|
||||
* Print pdf documents and scan them into electronic version after handwritten signature;
|
||||
* Electronic signature using devices that support handwriting;
|
||||
* Use 'gpg' for electronic signature, that is, to operate the pdf file filled with personal basic information (a public key/key pair matching the registered mailbox needs to be generated in advance): 'gpg -- armor -- detach sign icla. pdf';
|
||||
* Use 'DocuSign' to sign;
|
||||
5. The nominator sends an announcement email to dev@brpc.apache.org
|
||||
|
||||
### How to grant a committer permission on github
|
||||
|
||||
1. Add as committer ([https://whimsy.apache.org/roster/ppmc/brpc](https://whimsy.apache.org/roster/ppmc/brpc))
|
||||
2. Let him set github id ([https://id.apache.org/](https://id.apache.org/))
|
||||
3. Let him visit the website and get github permission([https://gitbox.apache.org/setup/](https://id.apache.org/))
|
||||
|
||||
### Documents related to the new committer on the official Apache website
|
||||
|
||||
* [https://community.apache.org/newcommitter.html](https://community.apache.org/newcommitter.html)
|
||||
* [https://infra.apache.org/new-committers-guide.html](https://infra.apache.org/new-committers-guide.html)
|
||||
* [https://juejin.cn/post/6844903788982042632](https://juejin.cn/post/6844903788982042632)
|
||||
|
||||
### Suggested steps from secretary@apache.org
|
||||
|
||||
Please do these things:
|
||||
1. Hold the discussion and vote on your private@ list. This avoids any issues related to personnel, which should remain private.
|
||||
2. If the vote is successful, announce the result to the private@ list with a new email thread with subject [RESULT][VOTE]. This makes it easier for secretary to find the result of the vote in order to request the account at the time of the filing of the ICLA.
|
||||
3. Only if the candidate accepts committership, announce the new committer on your dev@ list.
|
||||
|
||||
Doing these things will make everyone's job easier.
|
||||
|
||||
## 2. How to upgrade a committer into a PPMC
|
||||
|
||||
### Process reference: Apache official website document
|
||||
|
||||
* https://incubator.apache.org/guides/ppmc.html#voting_in_a_new_ppmc_member
|
||||
* https://community.apache.org/newcommitter.html
|
||||
* https://incubator.apache.org/guides/ppmc.html#podling_project_management_committee_ppmc
|
||||
|
||||
### Actual process
|
||||
|
||||
1. Initiate discussion in the private@brpc . If there is no objection, continue
|
||||
2. Open a Vote in private@brpc
|
||||
3. Send an email to close the voting and notify private@incubator.apache.org
|
||||
4. Announce new PPMC On private@brpc
|
||||
5. Set his authority by visiting https://whimsy.apache.org/roster/ppmc/brpc
|
||||
6. Help him subscribe to a private email group. See https://whimsy.apache.org/committers/moderationhelper.cgi
|
||||
@@ -0,0 +1,71 @@
|
||||
# 值周工程师的职责如下
|
||||
|
||||
## 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
|
||||
* 刘帅 @serverglen
|
||||
* 胡希国 @Huixxi
|
||||
* 杨立明 @yanglimingcn
|
||||
|
||||
## 5. 值周记录如下
|
||||
|
||||
| 时间(月/日/年) | 值周人 | 值周report|
|
||||
| ---- | ---- | --- |
|
||||
| 06/21/2021 to 06/27/2021 | 李磊 | https://lists.apache.org/thread/czolsqo80jzsytqc7dp37knqwxnkymx1
|
||||
| 06/28/2021 to 07/04/2021 | 蔡道进 | https://lists.apache.org/thread/c05fcjdjh7473ho1ylyl98mxscmnkbv0
|
||||
| 07/05/2021 - 07/12/2021 | 何磊 | https://lists.apache.org/thread/hz1vn7358v5fslglg2cl4g8x0wtxy4lv
|
||||
| 08/16/2021 - 08/22/2021 | 朱佳顺 | https://lists.apache.org/thread/g36jtjc3v75lfoc7m3ynzplgbqlsjjrd
|
||||
| 08/23/2021 - 08/29/2021 | 李磊 | https://lists.apache.org/thread/2098ndgdy6fs2b1s8tywpmz47n5swh29
|
||||
| 08/30/2021 - 09/05/2021 | 蔡道进 | https://lists.apache.org/thread/wjxdomg7fp75dc0n0rmh37bkwd8w7myv
|
||||
| 09/06/2021 - 09/12/2021 | 何磊 | https://lists.apache.org/thread/cwjtocpjbqjgndog53rqw8gs6f9c0rmo
|
||||
| 09/20/2021 - 09/26/2021 | 李磊 | https://lists.apache.org/thread/p779y43hogv7ftckc4cqx006gv9j65r8
|
||||
| 09/27/2021 - 10/03/2021 | 蔡道进 | https://lists.apache.org/thread/ync12fx2dwjb2l3p4yck0kodmkgzz7wd
|
||||
| 12/13/2021 - 12/19/2021 | 蔡道进 | https://lists.apache.org/thread/mvclsy79859mrbdso1xzm6y7yz3lg6w0
|
||||
| 01/24/2022 - 02/06/2022 | 王伟冰 | https://lists.apache.org/thread/ttgqnw4hfw0qnb7swvnn2kxb5b9hkdbo
|
||||
| 04/10/2022 - 04/16/2022 | 刘帅 | https://lists.apache.org/thread/0l67lqnw0l6rgfwkvhrc168prwr7fp60
|
||||
| 04/17/2022 - 04/24/2022 | 朱佳顺| https://lists.apache.org/thread/xwkpyonndgtp8tq4c9v4yfowqx7fg9gl
|
||||
| 04/25/2022 - 05/01/2022 | 李磊 | https://lists.apache.org/thread/cx7t1glptr7x7posxstsb1691h4m4mbl
|
||||
| 05/02/2022 - 05/15/2022 | 王伟冰 | https://lists.apache.org/thread/tgyqkhh6fqgyzcn5d56kt46hk978wogx
|
||||
| 06/06/2022 - 06/12/2022 | 何磊 | https://lists.apache.org/thread/s19p7ygnsj0bknfjvrh0wlf1mbgstxbk
|
||||
| 06/13/2022 - 06/19/2022 | 刘帅 | https://lists.apache.org/thread/tvqjyz6rh7jb1zcclx0lh6zrcsf9ykxr
|
||||
| 07/25/2022 - 07/31/2022 | 王伟冰 | https://lists.apache.org/thread/83scwkkfxrp6kkkoltbrn1fthfy3w0qz
|
||||
| 08/08/2022 - 08/14/2022 | 何磊 | https://lists.apache.org/thread/jj16rzfh34yrt6o0xqfdz9wtdtzxzswq
|
||||
| 08/15/2022 - 08/21/2022 | 刘帅 | https://lists.apache.org/thread/jp69sm7c8fs3dkdd828qk0fsojqwwz6h
|
||||
| 09/05/2022 - 09/12/2022 | 王伟冰 | https://lists.apache.org/thread/4jjk2hxw9s2wskccclqb8fvpqxqffnlb
|
||||
| 09/12/2022 - 09/18/2022 | 蔡道进| https://lists.apache.org/thread/8mo7zl0l2yrd8tp4v3kx86rnlyfk6wz4
|
||||
| 09/19/2022 - 09/25/2022 | 何磊 | https://lists.apache.org/thread/qlkr7cmwow3ob47dt80tpx0zrgzg7bdm
|
||||
| 09/26/2022 - 10/09/2022 | 刘帅 | https://lists.apache.org/thread/b0lwr8wyflmhqlnf0kkh1j30tgt5qw54
|
||||
| 10/10/2022 - 10/16/2022 | 朱佳顺 | https://lists.apache.org/thread/y8sgbprxt21j6r0812dlftosfov6pbgk
|
||||
| 10/17/2022 - 10/23/2022 | 李磊 | https://lists.apache.org/thread/qn2270p9qsrglkh7oy013ts1zk5rlhwx
|
||||
| 10/24/2022 - 10/30/2022 | 王伟冰 | https://lists.apache.org/thread/k74155opmwmopgtsqo6p79z9q0f0sv8j
|
||||
| 10/31/2022 - 11/06/2022 | 蔡道进 | https://lists.apache.org/thread/t4z49yt7ymp4vr5sms0m4cpoo94db4oz
|
||||
| 11/14/2022 - 11/20/2022 | 刘帅 | https://lists.apache.org/thread/nq50fp79ox3follc7gxp814cvcqnmqzz
|
||||
| 11/21/2022 - 11/27/2022 | 朱佳顺 | https://lists.apache.org/thread/57kzov5g3j4vv6l2zcyw0msm36qglc8k
|
||||
| 11/28/2022 - 12/04/2022 | 李磊 | https://lists.apache.org/thread/92hbzcd662slj75v3ndjf69o1dgsnd6o
|
||||
| 12/05/2022 - 12/11/2022 | 王伟冰 | https://lists.apache.org/thread/99o15h9hk5dd73jv8wyp49l8mbw0j611
|
||||
| 12/19/2022 - 12/25/2022 | 何磊 | https://lists.apache.org/thread/g1stjjo1mc9ds47do6gosrw5k6wwb9mj
|
||||
| 12/26/2022 - 01/01/2023 | 刘帅 | https://lists.apache.org/thread/3xw1gkobqmvr6oo375x3gsfcqvg80n23
|
||||
| 01/02/2023 - 01/08/2023 | 朱佳顺 | https://lists.apache.org/thread/sm4f209c8ltols04gpmzo386b02dyc9s
|
||||
| 01/09/2023 - 01/15/2023 | 李磊 | https://lists.apache.org/thread/fydjz4h88omsrb7fzw65wl64kh5r520s
|
||||
| 01/16/2023 - 01/29/2023 | 王伟冰 | https://lists.apache.org/thread/k5bb4pn2v23dksmwpwqphzlc4bd92ndr
|
||||
| 01/30/2023 - 02/05/2023 | 蔡道进 | https://lists.apache.org/thread/4scwpdh163l92czm5pvc7ox78n44mrn4
|
||||
| 02/13/2023 - 02/19/2023 | 刘帅 | https://lists.apache.org/thread/jwynp880hdhcfkqwq7thsm05y037wzy5
|
||||
| 02/27/2023 - 03/05/2023 | 李磊 | https://lists.apache.org/thread/94ndftsvooydfnn8hdddv294pp0tfvdm
|
||||
| 03/06/2023 - 03/12/2023 | 王伟冰 | https://lists.apache.org/thread/bv3qw5w8gj9xs576fplxqhktopkbk7md
|
||||
| 03/27/2023 - 04/03/2023 | 刘帅 | https://lists.apache.org/thread/fk37cn8r5pd1y10vjvzvkl211b67vn4q
|
||||
@@ -0,0 +1,592 @@
|
||||
# brpc 发布 apache release 版本流程 step by step
|
||||
|
||||
## 准备工作
|
||||
|
||||
### 1. 确认 Release Notes
|
||||
|
||||
该阶段会持续一周左右,主要工作包含:
|
||||
- 确认 Release Notes 中是否有遗漏项以及各项的描述是否准确、恰当;
|
||||
- 合入计划发布但未及时处理的 PR。
|
||||
|
||||
Release Notes 需要提供中英两个版本,用于不同的对外发布渠道。
|
||||
|
||||
模板如下:
|
||||
```
|
||||
[Release Notes]
|
||||
|
||||
Feature:
|
||||
- new feature 1;
|
||||
- new feature 2;
|
||||
- ...
|
||||
|
||||
Bugfix:
|
||||
- fix bug 1;
|
||||
- fix bug 2;
|
||||
- ...
|
||||
|
||||
Enhancement:
|
||||
- improve blabla
|
||||
|
||||
Other:
|
||||
- ...
|
||||
```
|
||||
|
||||
### 2. 设置 GPG(非首次发版的同学请直接跳过此步骤)
|
||||
|
||||
#### 1. 安装 GPG
|
||||
|
||||
通常 Linux 发行版中会集成 `GnuPG` 工具,OSX 可以使用 [`brew`](https://brew.sh/) 安装。
|
||||
```bash
|
||||
brew install gnupg
|
||||
```
|
||||
|
||||
也可以直接在[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 邮件地址,`Real Name` 使用姓名 Pinyin、Apache ID 或 GitHub ID 等均可:
|
||||
```
|
||||
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
|
||||
<n> = key expires in n days
|
||||
<n>w = key expires in n weeks
|
||||
<n>m = key expires in n months
|
||||
<n>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) <lorinlee@apache.org>"
|
||||
|
||||
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) <lorinlee@apache.org>
|
||||
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) <lorinlee@apache.org>
|
||||
sub rsa4096 2021-10-17 [E]
|
||||
```
|
||||
|
||||
其中 `C30F211F071894258497F46392E18A11B6585834` 为公钥ID。
|
||||
|
||||
#### 4. 将公钥公布到服务器
|
||||
|
||||
命令如下:
|
||||
|
||||
```bash
|
||||
gpg --keyserver hkps://pgp.mit.edu --send-key C30F211F071894258497F46392E18A11B6585834
|
||||
```
|
||||
keyserver 也可以用 `hkps://keys.openpgp.org` 或 `hkps://keyserver.ubuntu.com`,Web 上都提供了比较方便的查询接口。
|
||||
|
||||
#### 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) <lorinlee@apache.org>
|
||||
sub rsa4096 2021-10-17 [E]
|
||||
```
|
||||
|
||||
将上面的 fingerprint `C30F 211F 0718 9425 8497 F463 92E1 8A11 B658 5834` 粘贴到⾃己 Apache ⽤户信息 https://id.apache.org 的 `OpenPGP Public Key Primary Fingerprint:` 字段中。
|
||||
|
||||
## 制作发布包
|
||||
|
||||
### 1. 拉出发版分支
|
||||
|
||||
如果是发布新的 2 位版本,如 `1.0.0`,则需要从 master 拉出新的分支 `release-1.0`。
|
||||
|
||||
如果是在已有的 2 位版本上发布新的 3 位版本,如 `1.0.1` 版本,则只需要在已有的 `release-1.0` 分支上修改加上要发布的内容。
|
||||
|
||||
发版过程中的操作都在 release 分支(如:`release-1.0`)上操作,如果发版过程发现代码有问题需要修改,也在该分支上进行修改。发版完成后,将该分支合回 master 分支。
|
||||
|
||||
### 2. 更新 `NOTICE` 文件
|
||||
|
||||
检查 `NOTICE` 文件中的年份是否需要更新,通常在年初发版时需重点关注。
|
||||
|
||||
### 3. 编辑版本号相关文件
|
||||
|
||||
#### 更新 `RELEASE_VERSION` 文件
|
||||
|
||||
编辑项目根目录下 `RELEASE_VERSION` 文件,更新版本号,并提交至代码仓库,本文以 `1.0.0` 版本为例,文件内容为:
|
||||
|
||||
```
|
||||
1.0.0
|
||||
```
|
||||
|
||||
#### 更新 `CMakeLists.txt` 文件
|
||||
编辑项目根目录下 `CMakeLists.txt` 文件,更新版本号,并提交至代码仓库,本文以 `1.0.0` 版本为例,修改 `BRPC_VERSION` 为:
|
||||
|
||||
```
|
||||
set(BRPC_VERSION 1.0.0)
|
||||
```
|
||||
|
||||
#### 更新 `package/rpm/brpc.spec` 文件
|
||||
|
||||
编辑项目根目录下 `package/rpm/brpc.spec` 文件,更新版本号,并提交至代码仓库,本文以 `1.0.0` 版本为例,修改 `Version` 为:
|
||||
|
||||
```
|
||||
Version: 1.0.0
|
||||
```
|
||||
|
||||
#### 更新 `MODULE.bazel` 文件
|
||||
|
||||
编辑项目根目录下 `MODULE.bazel` 文件,更新版本号,并提交至代码仓库,本文以 `1.0.0` 版本为例,修改 `version` 为:
|
||||
|
||||
```
|
||||
# in MODULE.bazel
|
||||
module(
|
||||
...
|
||||
version = '1.0.0',
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### 4. 创建发布 tag
|
||||
```bash
|
||||
export BRPCVERSION=1.0.0
|
||||
export BRPCBRANCH=1.0
|
||||
export BRPCUSERNAME=lorinlee
|
||||
```
|
||||
拉取发布分支,创建并推送 tag
|
||||
```bash
|
||||
git clone -b release-$BRPCBRANCH git@github.com:apache/brpc.git ~/brpc
|
||||
|
||||
cd ~/brpc
|
||||
|
||||
git tag -a $BRPCVERSION -m "release $BRPCVERSION"
|
||||
|
||||
git push origin --tags
|
||||
```
|
||||
|
||||
### 5. 打包发布包
|
||||
|
||||
```bash
|
||||
git archive --format=tar $BRPCVERSION --prefix=apache-brpc-$BRPCVERSION-src/ | gzip > apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
```
|
||||
或
|
||||
```bash
|
||||
git archive --format=tar.gz $BRPCVERSION --prefix=apache-brpc-$BRPCVERSION-src/ --output=apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
```
|
||||
|
||||
### 6. 生成签名文件
|
||||
|
||||
```bash
|
||||
gpg -u $BRPCUSERNAME@apache.org --armor --output apache-brpc-$BRPCVERSION-src.tar.gz.asc --detach-sign apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
|
||||
gpg --verify apache-brpc-$BRPCVERSION-src.tar.gz.asc apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
```
|
||||
|
||||
### 7. 生成哈希文件
|
||||
|
||||
```bash
|
||||
sha512sum apache-brpc-$BRPCVERSION-src.tar.gz > apache-brpc-$BRPCVERSION-src.tar.gz.sha512
|
||||
|
||||
sha512sum --check apache-brpc-$BRPCVERSION-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=$BRPCUSERNAME co https://dist.apache.org/repos/dist/dev/brpc/
|
||||
|
||||
cd ~/brpc_svn/dev/brpc
|
||||
```
|
||||
|
||||
### 2. 添加 GPG 公钥
|
||||
|
||||
仅第一次部署的账号需要添加,只要 KEYS 中包含已经部署过的账户的公钥即可。
|
||||
|
||||
```bash
|
||||
(gpg --list-sigs $BRPCUSERNAME && gpg -a --export $BRPCUSERNAME) >> KEYS
|
||||
```
|
||||
|
||||
注意:当有多个名相同的 key 时,可以指定完整邮件地址或者公钥来导出指定的公钥信息。如:
|
||||
```bash
|
||||
(gpg --list-sigs $BRPCUSERNAME@apache.org && gpg -a --export $BRPCUSERNAME@apache.org) >> KEYS
|
||||
```
|
||||
或:
|
||||
```bash
|
||||
(gpg --list-sigs C30F211F071894258497F46392E18A11B6585834 && gpg -a --export C30F211F071894258497F46392E18A11B6585834) >> KEYS
|
||||
```
|
||||
|
||||
### 3. 将待发布的代码包添加至 SVN 目录
|
||||
|
||||
```bash
|
||||
mkdir -p ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
|
||||
cd ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
|
||||
cp ~/brpc/apache-brpc-$BRPCVERSION-src.tar.gz ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
|
||||
cp ~/brpc/apache-brpc-$BRPCVERSION-src.tar.gz.asc ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
|
||||
cp ~/brpc/apache-brpc-$BRPCVERSION-src.tar.gz.sha512 ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
```
|
||||
|
||||
### 4. 提交 SVN
|
||||
|
||||
退回到上级目录,使用 Apache LDAP 账号提交 SVN。
|
||||
|
||||
```bash
|
||||
cd ~/brpc_svn/dev/brpc
|
||||
|
||||
svn add *
|
||||
|
||||
svn --username=$BRPCUSERNAME commit -m "release $BRPCVERSION"
|
||||
```
|
||||
|
||||
## 检查发布结果
|
||||
```bash
|
||||
cd ~/brpc_svn/dev/brpc/$BRPCVERSION
|
||||
```
|
||||
### 1. 检查 sha512 哈希
|
||||
|
||||
```bash
|
||||
sha512sum --check apache-brpc-$BRPCVERSION-src.tar.gz.sha512
|
||||
```
|
||||
|
||||
### 2. 检查 GPG 签名
|
||||
|
||||
首先导入发布人公钥。从 svn 仓库导入 KEYS 到本地环境。(发布版本的人不需要再导入,帮助做验证的人需要导入,用户名填发版人的即可)
|
||||
|
||||
```bash
|
||||
curl https://dist.apache.org/repos/dist/dev/brpc/KEYS >> KEYS
|
||||
|
||||
gpg --import KEYS
|
||||
```
|
||||
|
||||
设置信任该用户的签名,执行以下命令,填写发布人的用户名
|
||||
```bash
|
||||
gpg --edit-key $BRPCUSERNAME
|
||||
```
|
||||
|
||||
输出为
|
||||
```
|
||||
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 签名检查。
|
||||
```bash
|
||||
gpg --verify apache-brpc-$BRPCVERSION-src.tar.gz.asc apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
```
|
||||
|
||||
### 3. 检查发布内容
|
||||
|
||||
#### 1. 对比源码包与 github 上的 tag 内容差异
|
||||
|
||||
```bash
|
||||
curl -Lo tag-$BRPCVERSION.tar.gz https://github.com/apache/brpc/archive/refs/tags/$BRPCVERSION.tar.gz
|
||||
|
||||
tar xvzf tag-$BRPCVERSION.tar.gz
|
||||
|
||||
tar xvzf apache-brpc-$BRPCVERSION-src.tar.gz
|
||||
|
||||
diff -r brpc-$BRPCVERSION apache-brpc-$BRPCVERSION-src
|
||||
```
|
||||
|
||||
#### 2. 检查源码包的文件内容
|
||||
|
||||
- 检查源码包是否包含由于包含不必要文件,致使 tarball 过于庞大
|
||||
- 存在 LICENSE 和 NOTICE 文件,并且 NOTICE 文件中的年份正确
|
||||
- 只存在文本文件,不存在二进制文件
|
||||
- 所有文件的开头都有 ASF 许可证
|
||||
- 能够正确编译,单元测试可以通过
|
||||
- 检查是否有多余文件或文件夹,例如空文件夹等
|
||||
- 检查第三方依赖许可证:
|
||||
- 第三方依赖的许可证兼容
|
||||
- 所有第三方依赖的许可证都在 LICENSE 文件中声名
|
||||
- 依赖许可证的完整版全部在 license 目录
|
||||
- 如果依赖的是 Apache 许可证并且存在 NOTICE 文件,那么这些 NOTICE 文件也需要加入到版本的 NOTICE 文件中
|
||||
|
||||
## 在 Apache bRPC 社区发起投票
|
||||
|
||||
该阶段会持续至少 3 天。
|
||||
|
||||
### 1. 投票阶段
|
||||
|
||||
1. 发起投票邮件到 dev@brpc.apache.org。PMC 需要先按文档检查版本的正确性,然后再进行投票。经过至少 72 小时并统计到 3 个 +1 PMC member 票后,方可进入下一阶段。
|
||||
2. 宣布投票结果,发起投票结果邮件到 dev@brpc.apache.org。
|
||||
|
||||
### 2. 投票邮件模板
|
||||
|
||||
#### Apache bRPC 社区投票邮件模板
|
||||
|
||||
标题:
|
||||
```
|
||||
[VOTE] Release Apache bRPC 1.0.0
|
||||
```
|
||||
|
||||
正文:
|
||||
```
|
||||
Hi Apache bRPC Community,
|
||||
|
||||
This is a call for vote to release Apache bRPC version 1.0.0
|
||||
|
||||
[Release Note]
|
||||
- xxx
|
||||
|
||||
The release candidates:
|
||||
https://dist.apache.org/repos/dist/dev/brpc/1.0.0/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/brpc/releases/tag/1.0.0
|
||||
|
||||
Release Commit ID:
|
||||
https://github.com/apache/brpc/commit/xxx
|
||||
|
||||
Keys to verify the Release Candidate:
|
||||
https://dist.apache.org/repos/dist/dev/brpc/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the 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 are +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
|
||||
```
|
||||
|
||||
注:`Release Commit ID` 填写当前 tag(1.0.0) 的 commit id。
|
||||
|
||||
#### Apache bRPC 社区宣布结果邮件模板
|
||||
|
||||
标题:
|
||||
```
|
||||
[RESULT] [VOTE] Release Apache bRPC 1.0.0
|
||||
```
|
||||
|
||||
正文:
|
||||
```
|
||||
Hi all,
|
||||
|
||||
The vote to release Apache bRPC 1.0.0 has passed.
|
||||
|
||||
The vote PASSED with 3 binding +1, 3 non binding +1 and no -1 votes:
|
||||
|
||||
Binding votes:
|
||||
- xxx
|
||||
- yyy
|
||||
- zzz
|
||||
|
||||
Non-binding votes:
|
||||
- aaa
|
||||
- bbb
|
||||
- ccc
|
||||
|
||||
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. I will process to publish the release and send ANNOUNCE.
|
||||
|
||||
Regards,
|
||||
LorinLee
|
||||
```
|
||||
|
||||
### 3. 投票未通过
|
||||
|
||||
若社区投票未通过,则在 release 分支对代码仓库进行修改,重新打包,发起投票。
|
||||
|
||||
## 完成发布
|
||||
|
||||
### 1. 将软件包从 Apache SVN 仓库的 dist/dev 目录移至 dist/release 目录
|
||||
|
||||
注意:该过程需要 PMC 成员进行,投票通过后可以联系 PMC 成员进行相关操作,首次发版的成员还需要更新公钥信息 KEYS。
|
||||
|
||||
```bash
|
||||
svn mv https://dist.apache.org/repos/dist/dev/brpc/$BRPCVERSION https://dist.apache.org/repos/dist/release/brpc/$BRPCVERSION -m "release brpc $BRPCVERSION"
|
||||
```
|
||||
|
||||
### 2. 创建 GitHub 版本发布页
|
||||
|
||||
在 [GitHub Releases 页面](https://github.com/apache/brpc/tags)的对应 tag 上点击,创建新的 Release,编辑版本号及版本说明。注意Release title统一根据本次版本号写为Apache bRPC 1.x.0,并点击 Publish release。
|
||||
|
||||
### 3. 更新网站下载页
|
||||
|
||||
等待并确认新的发布版本同步至 Apache 镜像后,更新如下页面:<https://brpc.apache.org/docs/downloadbrpc/>, 更新方式在 <https://github.com/apache/brpc-website/> 仓库中,注意中英文都要更新。
|
||||
|
||||
GPG 签名文件和哈希校验文件的下载链接应该使用这个前缀:https://downloads.apache.org/brpc/
|
||||
|
||||
代码包的下载链接应该使用这个前缀:https://dlcdn.apache.org/brpc/
|
||||
|
||||
### 4. 发送邮件通知新版发布
|
||||
|
||||
发送邮件到 dev@brpc.apache.org 和 announce@apache.org 通知完成版本发布。
|
||||
|
||||
注意:发邮件账号必须使用**个人 apache 邮箱**,且邮件内容必须是**纯文本格式**(可在 gmail 选择"纯文本模式")。announce@apache.org 邮件组需要经过人工审核才能送达,发出邮件后请耐心等待,一般会在一天之内通过。
|
||||
|
||||
个人 apache 邮箱配置方式可以参考:https://shenyu.apache.org/zh/community/use-apache-email
|
||||
注意:SMTP 服务器需要填 `mail-relay.apache.org`。
|
||||
|
||||
通知邮件模板如下:
|
||||
|
||||
标题:
|
||||
```
|
||||
[ANNOUNCE] Apache bRPC 1.0.0 released
|
||||
```
|
||||
|
||||
正文:
|
||||
注:`Brief notes of this release` 仅需列出本次发版的主要变更,且无需指出对应贡献人和 PR 编号,建议参考下之前的 Announce 邮件。
|
||||
```
|
||||
Hi all,
|
||||
|
||||
The Apache bRPC community is glad to announce the new release
|
||||
of Apache bRPC 1.0.0.
|
||||
|
||||
Apache bRPC is an Industrial-grade RPC framework using C++ Language,
|
||||
which is often used in high performance systems such as Search, Storage,
|
||||
Machine learning, Advertisement, Recommendation etc.
|
||||
|
||||
Brief notes of this release:
|
||||
- xxx
|
||||
- yyy
|
||||
- zzz
|
||||
|
||||
More details regarding Apache brpc can be found at:
|
||||
https://brpc.apache.org/
|
||||
|
||||
The release is available for download at:
|
||||
https://brpc.apache.org/download/
|
||||
|
||||
The release notes can be found here:
|
||||
https://github.com/apache/brpc/releases/tag/1.0.0
|
||||
|
||||
Website: https://brpc.apache.org/
|
||||
|
||||
Apache bRPC Resources:
|
||||
- Issue: https://github.com/apache/brpc/issues/
|
||||
- Mailing list: dev@brpc.apache.org
|
||||
- Documents: https://brpc.apache.org/docs/
|
||||
|
||||
We would like to thank all contributors of the Apache bRPC community
|
||||
who made this release possible!
|
||||
|
||||
|
||||
Best Regards,
|
||||
Apache bRPC Community
|
||||
```
|
||||
|
||||
### 5. 其他对外公共账号
|
||||
|
||||
#### 微信公众号
|
||||
|
||||
参考 <https://mp.weixin.qq.com/s/DeFhpAV_AYsn_Xd1ylPTSg>。
|
||||
建议先在腾讯文档中编辑后粘贴至公众号,统一字体大小和格式,参考 [腾讯文档:bRPC 1.11.0](https://docs.qq.com/doc/DYmZ2Tnpub1lySWZO?_t=1730208105245&u=31460cd039dd4461877a61ab9f56be1f)
|
||||
|
||||
|
||||
### 6. 更新 master 分支
|
||||
|
||||
发版完成后,将 release 分支合并到 master 分支。
|
||||
@@ -0,0 +1,595 @@
|
||||
# brpc apache release guide step by step
|
||||
|
||||
## Preparation
|
||||
|
||||
### 1. Confirm the release notes
|
||||
|
||||
It will cost about 1 week or so to finish this phase, during this phase:
|
||||
* The content of the `Release Notes` will be confirmed,
|
||||
- no missing changes;
|
||||
- and the description is clear;
|
||||
* The PRs those are scheduled for the release can be merged.
|
||||
|
||||
The `Release Notes` template:
|
||||
```
|
||||
[Release Notes]
|
||||
|
||||
Feature:
|
||||
- ...
|
||||
|
||||
Bugfix:
|
||||
- ...
|
||||
|
||||
Enhancement:
|
||||
- ...
|
||||
|
||||
Other:
|
||||
- ...
|
||||
```
|
||||
|
||||
### 2. Setup GPG(Please skip this step if you are not a first time distributor)
|
||||
|
||||
#### 1. Install GPG
|
||||
|
||||
The popular Linux distributions should have the GnuPG pre-installed. With OSX, you can install GnuPG using [`brew`](https://brew.sh/):
|
||||
```bash
|
||||
brew install gnupg
|
||||
```
|
||||
|
||||
You can also download the installation package from [the GnuPG official website](https://www.gnupg.org/download/index.html). The commands of GnuPG version 1.x and version 2.x are slightly different. The following instructions take the `GnuPG-2.3.1` version (OSX) as an example.
|
||||
|
||||
After the installation is complete, execute the following command to check the version number.
|
||||
```bash
|
||||
gpg --version
|
||||
```
|
||||
|
||||
#### 2. Create key
|
||||
|
||||
After the installation is complete, execute the following command to create a key.
|
||||
|
||||
```bash
|
||||
gpg --full-gen-key
|
||||
```
|
||||
|
||||
Complete the key creation according to the prompts. Note that the mailbox should use the Apache email address, and the `Real Name` can use Apache ID or GitHub ID:
|
||||
|
||||
```
|
||||
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
|
||||
<n> = key expires in n days
|
||||
<n>w = key expires in n weeks
|
||||
<n>m = key expires in n months
|
||||
<n>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) <lorinlee@apache.org>"
|
||||
|
||||
Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
|
||||
You need a Passphrase to protect your secret key. # Input password
|
||||
|
||||
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) <lorinlee@apache.org>
|
||||
sub rsa4096 2021-10-17 [E]
|
||||
```
|
||||
|
||||
#### 3. Check the generated key
|
||||
|
||||
```bash
|
||||
gpg --list-keys
|
||||
```
|
||||
|
||||
output:
|
||||
|
||||
```
|
||||
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) <lorinlee@apache.org>
|
||||
sub rsa4096 2021-10-17 [E]
|
||||
```
|
||||
|
||||
Note that `C30F211F071894258497F46392E18A11B6585834` is the public key.
|
||||
|
||||
#### 4. Publish the public key to server
|
||||
|
||||
Execute the following command:
|
||||
|
||||
```bash
|
||||
gpg --keyserver hkps://pgp.mit.edu --send-key C30F211F071894258497F46392E18A11B6585834
|
||||
```
|
||||
|
||||
The keyserver can also use hkps://keys.openpgp.org or hkps://keyserver.ubuntu.com,which provide web page to view the keys.
|
||||
|
||||
#### 5. Generate fingerprint and upload to Apache user profile
|
||||
|
||||
Since the public key server has no verifying mechanism, anyone can upload the public key in your name, so there is no way to guarantee the reliability of the public key on the server. Usually, you can publish a public key fingerprint on your website and let others check the downloaded public key.
|
||||
|
||||
Execute the following command to view the fingerprint.
|
||||
```
|
||||
gpg --fingerprint lorinlee # user id
|
||||
```
|
||||
|
||||
output:
|
||||
```
|
||||
/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) <lorinlee@apache.org>
|
||||
sub rsa4096 2021-10-17 [E]
|
||||
```
|
||||
|
||||
Paste the above fingerprint `C30F 211F 0718 9425 8497 F463 92E1 8A11 B658 5834` into the `OpenPGP Public Key Primary Fingerprint:` field of your Apache user information on https://id.apache.org.
|
||||
|
||||
## Make the release
|
||||
|
||||
### 1. Create release branch
|
||||
|
||||
If you are releasing a new MAJOR/MINOR version, like `1.0.0`, you need to create a new branch `release-1.0` from master.
|
||||
|
||||
If you are releasing a new PATCH version from existing MINOR version, like `1.0.1`, you only need to modify the existing `release-1.0` branch and add the content to be released.
|
||||
|
||||
The code modification during the release process are performed on the release branch (such as `release-1.0`). After the release is complete, please merge the release branch back into the master branch.
|
||||
|
||||
### 2. Update the `NOTICE` file
|
||||
|
||||
Check and update the YEAR field of the `NOTIEC` file.
|
||||
|
||||
### 3. Update version in source code
|
||||
|
||||
#### Update `RELEASE_VERSION` file
|
||||
|
||||
Edit the `RELEASE_VERSION` file in the project root directory, update the version number, and submit it to the code repository. For example, the `1.0.0` version of the file is:
|
||||
|
||||
```
|
||||
1.0.0
|
||||
```
|
||||
|
||||
#### Update the `CMakeLists.txt` file
|
||||
|
||||
Edit the `CMakeLists.txt` file in the project root directory, update the version number, and submit it to the code repository. For example:
|
||||
|
||||
```
|
||||
set(BRPC_VERSION 1.0.0)
|
||||
```
|
||||
|
||||
#### Update the `package/rpm/brpc.spec` file
|
||||
|
||||
Edit the `/package/rpm/brpc.spec` file in the project root directory, update the version number, and submit it to the code repository. For example:
|
||||
|
||||
```
|
||||
Version: 1.0.0
|
||||
```
|
||||
|
||||
#### Update the `MODULE.bazel` file
|
||||
|
||||
Edit the `MODULE.bazel` file in the project root directory, update the version number, and submit it to the code repository. For example:
|
||||
|
||||
```
|
||||
# in MODULE.bazel
|
||||
module(
|
||||
...
|
||||
version = '1.0.0',
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
### 4. Create releasing tag
|
||||
|
||||
Pull the release branch to tag, for example:
|
||||
|
||||
```bash
|
||||
git clone -b release-1.0 git@github.com:apache/brpc.git ~/brpc
|
||||
|
||||
cd ~/brpc
|
||||
|
||||
git tag -a 1.0.0 -m "release 1.0.0"
|
||||
|
||||
git push origin --tags
|
||||
```
|
||||
|
||||
### 5. Create releasing package
|
||||
|
||||
```bash
|
||||
git archive --format=tar 1.0.0 --prefix=apache-brpc-1.0.0-src/ | gzip > apache-brpc-1.0.0-src.tar.gz
|
||||
```
|
||||
or
|
||||
```bash
|
||||
git archive --format=tar.gz 1.0.0 --prefix=apache-brpc-1.0.0-src/ --output=apache-brpc-1.0.0-src.tar.gz
|
||||
```
|
||||
|
||||
### 6. Generate GPG signature
|
||||
|
||||
```bash
|
||||
gpg -u lorinlee@apache.org --armor --output apache-brpc-1.0.0-src.tar.gz.asc --detach-sign apache-brpc-1.0.0-src.tar.gz
|
||||
|
||||
gpg --verify apache-brpc-1.0.0-src.tar.gz.asc apache-brpc-1.0.0-src.tar.gz
|
||||
```
|
||||
|
||||
### 7. Generate SHA512 checksum
|
||||
|
||||
```bash
|
||||
sha512sum apache-brpc-1.0.0-src.tar.gz > apache-brpc-1.0.0-src.tar.gz.sha512
|
||||
|
||||
sha512sum --check apache-brpc-1.0.0-src.tar.gz.sha512
|
||||
```
|
||||
|
||||
## Publish to Apache SVN repository
|
||||
|
||||
### 1. Checkout dist/dev/brpc directory
|
||||
|
||||
If there is no local working directory, create a local working directory first. Checkout the Apache SVN repository, username needs to use your own Apache LDAP username:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/brpc_svn/dev/
|
||||
|
||||
cd ~/brpc_svn/dev/
|
||||
|
||||
svn --username=lorinlee co https://dist.apache.org/repos/dist/dev/brpc/
|
||||
|
||||
cd ~/brpc_svn/dev/brpc
|
||||
```
|
||||
|
||||
### 2. Add GPG public key
|
||||
|
||||
A new release manager must add the key into KEYS file for the first time.
|
||||
|
||||
```
|
||||
(gpg --list-sigs lorinlee && gpg -a --export lorinlee) >> KEYS
|
||||
```
|
||||
|
||||
Note: if you have more than one keys that use the name, you should explicitly specify the key by email or fingerprint to export.
|
||||
|
||||
By email
|
||||
```bash
|
||||
(gpg --list-sigs lorinlee@apache.org && gpg -a --export lorinlee@apache.org) >> KEYS
|
||||
```
|
||||
By fingerprint:
|
||||
```bash
|
||||
(gpg --list-sigs C30F211F071894258497F46392E18A11B6585834 && gpg -a --export C30F211F071894258497F46392E18A11B6585834) >> KEYS
|
||||
```
|
||||
|
||||
### 3. Add the releasing package to SVN directory
|
||||
|
||||
```bash
|
||||
mkdir -p ~/brpc_svn/dev/brpc/1.0.0
|
||||
|
||||
cd ~/brpc_svn/dev/brpc/1.0.0
|
||||
|
||||
cp ~/brpc/apache-brpc-1.0.0-src.tar.gz ~/brpc_svn/dev/brpc/1.0.0
|
||||
|
||||
cp ~/brpc/apache-brpc-1.0.0-src.tar.gz.asc ~/brpc_svn/dev/brpc/1.0.0
|
||||
|
||||
cp ~/brpc/apache-brpc-1.0.0-src.tar.gz.sha512 ~/brpc_svn/dev/brpc/1.0.0
|
||||
```
|
||||
|
||||
### 4. Submit SVN
|
||||
|
||||
Return to the parent directory and use the Apache LDAP account to submit SVN
|
||||
|
||||
```bash
|
||||
cd ~/brpc_svn/dev/brpc
|
||||
|
||||
svn add *
|
||||
|
||||
svn --username=lorinlee commit -m "release 1.0.0"
|
||||
```
|
||||
|
||||
## Verify release
|
||||
```bash
|
||||
cd ~/brpc_svn/dev/brpc/1.0.0
|
||||
```
|
||||
### 1. Verify SHA512 checksum
|
||||
|
||||
```bash
|
||||
sha512sum --check apache-brpc-1.0.0-src.tar.gz.sha512
|
||||
```
|
||||
|
||||
### 2. Verify GPG signature
|
||||
|
||||
First import the publisher's public key. Import KEYS from the svn repository to the local. (The person who releases the version does not need to import it again. The person who verify needs to import it.)
|
||||
|
||||
```bash
|
||||
curl https://dist.apache.org/repos/dist/dev/brpc/KEYS >> KEYS
|
||||
|
||||
gpg --import KEYS
|
||||
```
|
||||
|
||||
Trust the signature of publisher, execute the following command using the publisher's user name
|
||||
|
||||
```bash
|
||||
gpg --edit-key lorinlee
|
||||
```
|
||||
|
||||
output:
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
Then verify the GPG signature:
|
||||
```
|
||||
gpg --verify apache-brpc-1.0.0-src.tar.gz.asc apache-brpc-1.0.0-src.tar.gz
|
||||
```
|
||||
|
||||
### 3. Check release content
|
||||
|
||||
#### 1. Compare the difference between the candidate source package and the package download through github tag page
|
||||
|
||||
Should use the `tar.gz` format tarball.
|
||||
|
||||
```bash
|
||||
curl -Lo tag-1.0.0.tar.gz https://github.com/apache/brpc/archive/refs/tags/1.0.0.tar.gz
|
||||
|
||||
tar xvzf tag-1.0.0.tar.gz
|
||||
|
||||
tar xvzf apache-brpc-1.0.0-src.tar.gz
|
||||
|
||||
diff -r brpc-1.0.0 apache-brpc-1.0.0-src
|
||||
```
|
||||
|
||||
#### 2. Check file content
|
||||
|
||||
- Check whether the source code package contains unnecessary files, which makes the tarball too large
|
||||
- LICENSE and NOTICE files exist
|
||||
- The year in the NOTICE file is correct
|
||||
- Only text files exist, no binary files exist
|
||||
- All files have an ASF license at the beginning
|
||||
- Source code can be compiled correctly, and the unit test can pass
|
||||
- Check whether there are redundant files or folders, such as empty folders
|
||||
- Check for third-party dependency licenses:
|
||||
- Third party dependency license compatibility
|
||||
- All licenses of third-party dependency are declared in the LICENSE file
|
||||
- The complete version of the dependency license is in the license directory
|
||||
- If third-party dependency have the Apache license and have NOTICE files, these NOTICE files also need to be added to the releasing NOTICE file
|
||||
|
||||
## Vote in the Apache bRPC community
|
||||
|
||||
This stage will cost 3+ days.
|
||||
|
||||
### 1. Vote stage
|
||||
|
||||
1. Send a voting email to `dev@brpc.apache.org`. PMC needs to check the correctness of the version according to the document before voting. After at least 72 hours and 3 +1 PMC member votes, you can move to the next stage.
|
||||
2. Announce the voting result and send the voting result to dev@brpc.apache.org.
|
||||
|
||||
### 2. Vote email template
|
||||
|
||||
1. Apache bRPC community vote email template
|
||||
|
||||
Title:
|
||||
```
|
||||
[VOTE] Release Apache bRPC 1.0.0
|
||||
```
|
||||
|
||||
Content:
|
||||
|
||||
Note: `Release Commit ID` fills in the commit ID of the last commit of the current release branch.
|
||||
|
||||
```
|
||||
Hi Apache bRPC Community,
|
||||
|
||||
This is a call for vote to release Apache bRPC version
|
||||
1.0.0
|
||||
|
||||
[Release Note]
|
||||
- xxx
|
||||
|
||||
The release candidates:
|
||||
https://dist.apache.org/repos/dist/dev/brpc/1.0.0/
|
||||
|
||||
Git tag for the release:
|
||||
https://github.com/apache/brpc/releases/tag/1.0.0
|
||||
|
||||
Release Commit ID:
|
||||
https://github.com/apache/brpc/commit/xxx
|
||||
|
||||
Keys to verify the Release Candidate:
|
||||
https://dist.apache.org/repos/dist/dev/brpc/KEYS
|
||||
|
||||
The vote will be open for at least 72 hours or until the 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 are +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 community announcement of vote result template
|
||||
|
||||
Title:
|
||||
```
|
||||
[Result] [VOTE] Release Apache bRPC 1.0.0
|
||||
```
|
||||
|
||||
Content:
|
||||
```
|
||||
Hi all,
|
||||
|
||||
The vote to release Apache bRPC 1.0.0 has passed.
|
||||
|
||||
The vote PASSED with 3 binding +1, 3 non binding +1 and no -1 votes:
|
||||
|
||||
Binding votes:
|
||||
- xxx
|
||||
- yyy
|
||||
- zzz
|
||||
|
||||
Non-binding votes:
|
||||
- aaa
|
||||
- bbb
|
||||
- ccc
|
||||
|
||||
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. I will process to publish the release and send ANNOUNCE.
|
||||
|
||||
Regards,
|
||||
LorinLee
|
||||
```
|
||||
|
||||
### 3. Vote not passed
|
||||
|
||||
If the community vote is not passed, please modify the code of the release branch, package and vote again.
|
||||
|
||||
|
||||
## Finish the release
|
||||
|
||||
### 1. Move the release package from Apache SVN directory dist/dev to dist/release
|
||||
|
||||
Note: PMC members only, and it will also update the `KEYS` if changed.
|
||||
|
||||
```
|
||||
svn mv https://dist.apache.org/repos/dist/dev/brpc/1.0.0 https://dist.apache.org/repos/dist/release/brpc/1.0.0 -m "release brpc 1.0.0"
|
||||
```
|
||||
|
||||
### 2. Create github release
|
||||
|
||||
1. On the [GitHub Releases page](https://github.com/apache/brpc/tags) Click the corresponding version of to create a new Release
|
||||
2. Edit the version number and version description, and click `Publish release`
|
||||
|
||||
### 3. Update download page
|
||||
|
||||
After waiting and confirming that the new release is synchronized to the Apache image, update the following page: <https://brpc.apache.org/docs/downloadbrpc/> by change the code in <https://github.com/apache/brpc-website/>. Please update both Chinese and English.
|
||||
|
||||
The download links of GPG signature files and hash check files should use this prefix: `https://downloads.apache.org/brpc/`
|
||||
|
||||
The download link of the code package should use this prefix: `https://dlcdn.apache.org/brpc/`
|
||||
|
||||
### 4. Send email to announce release finished
|
||||
|
||||
Send mail to `dev@brpc.apache.org` and `announce@apache.org` to announce the completion of release.
|
||||
|
||||
Note: The email account must use **personal apache email**, and the email content must be **plain text format** ("plain text mode" can be selected in gmail). And email to `announce@apache.org` mail group will be delivered after manual review. Please wait patiently after sending the email, and it will be passed within one day.
|
||||
|
||||
See https://shenyu.apache.org/community/use-apache-email for more details to setup a personal apache account email, the `SMTP` server should be `mail-relay.apache.org`.
|
||||
|
||||
The announcement email template:
|
||||
|
||||
Title:
|
||||
```
|
||||
[ANNOUNCE] Apache bRPC 1.0.0 released
|
||||
```
|
||||
|
||||
Content:
|
||||
|
||||
Note: `Brief notes of this release` It is only necessary to list the main changes of this release, without corresponding contributors and PR numbers. It is recommended to refer to the previous announcement email.
|
||||
|
||||
```
|
||||
Hi all,
|
||||
|
||||
The Apache bRPC community is glad to announce the new release
|
||||
of Apache bRPC 1.0.0.
|
||||
|
||||
Apache bRPC is an Industrial-grade RPC framework using C++ Language,
|
||||
which is often used in high performance systems such as Search, Storage,
|
||||
Machine learning, Advertisement, Recommendation etc.
|
||||
|
||||
Brief notes of this release:
|
||||
- xxx
|
||||
- yyy
|
||||
- zzz
|
||||
|
||||
More details regarding Apache brpc can be found at:
|
||||
https://brpc.apache.org/
|
||||
|
||||
The release is available for download at:
|
||||
https://brpc.apache.org/download/
|
||||
|
||||
The release notes can be found here:
|
||||
https://github.com/apache/brpc/releases/tag/1.0.0
|
||||
|
||||
Website: https://brpc.apache.org/
|
||||
|
||||
Apache bRPC Resources:
|
||||
- Issue: https://github.com/apache/brpc/issues/
|
||||
- Mailing list: dev@brpc.apache.org
|
||||
- Documents: https://brpc.apache.org/docs/
|
||||
|
||||
We would like to thank all contributors of the Apache bRPC community
|
||||
who made this release possible!
|
||||
|
||||
|
||||
Best Regards,
|
||||
Apache bRPC Community
|
||||
```
|
||||
|
||||
### 5. Publish WeChat official account announcement
|
||||
|
||||
Reference <https://mp.weixin.qq.com/s/DeFhpAV_AYsn_Xd1ylPTSg>.
|
||||
|
||||
### 6. Update master branch
|
||||
|
||||
After the release is completed, merge the release branch into the `master` branch.
|
||||
@@ -0,0 +1,27 @@
|
||||
# bRPC 发版日程
|
||||
|
||||
|版本号|发版时间|发版人|
|
||||
|-|-|-|
|
||||
|1.0.0|2021-10-31|李磊|
|
||||
|1.1.0|2022-03-29|王伟冰|
|
||||
|1.2.0|2022-07-11|刘帅|
|
||||
|1.3.0|2022-10-25|胡希国|
|
||||
|1.4.0|2023-02-07|王晓峰|
|
||||
|1.5.0|2023-04-xx|胡希国|
|
||||
|1.6.0|2023-07-xx|陈光明|
|
||||
|1.7.0|2023-10-xx|李磊|
|
||||
|1.8.0|2024-01-xx|王伟冰|
|
||||
|1.9.0|2024-04-xx|刘帅|
|
||||
|1.10.0|2024-07-xx|王晓峰|
|
||||
|1.11.0|2024-10-xx|胡希国|
|
||||
|1.12.0|2025-01-xx|陈光明|
|
||||
|1.13.0|2025-04-xx|李磊|
|
||||
|1.14.0|2025-07-xx|王伟冰|
|
||||
|1.15.0|2025-10-xx|刘帅|
|
||||
|1.16.0|2026-01-xx|王晓峰|
|
||||
|1.17.0|2026-05-xx|胡希国|
|
||||
|1.18.0|2026-09-xx|陈光明|
|
||||
|1.19.0|2027-01-xx|李磊|
|
||||
|1.20.0|2027-05-xx|王伟冰|
|
||||
|1.21.0|2027-09-xx|刘帅|
|
||||
|1.22.0|2028-01-xx|王晓峰|
|
||||
@@ -0,0 +1,54 @@
|
||||
# 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
|
||||
|
||||

|
||||
|
||||
|
||||
## 常见的问题导致 -1
|
||||
|
||||

|
||||
|
||||
## Vote 时候的检查项
|
||||
|
||||
毕业后检查时不再需要 `DISCLAIMER` 以及 `incubating`
|
||||
|
||||
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 的时候,同样需要给出明确的理由
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 246 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 223 KiB |
+24
@@ -0,0 +1,24 @@
|
||||
// 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_CONFIG_H
|
||||
#define BUTIL_CONFIG_H
|
||||
|
||||
#ifdef BRPC_WITH_GLOG
|
||||
#undef BRPC_WITH_GLOG
|
||||
#endif
|
||||
#cmakedefine BRPC_WITH_GLOG @WITH_GLOG_VAL@
|
||||
|
||||
#endif // BUTIL_CONFIG_H
|
||||
Executable
+673
@@ -0,0 +1,673 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
# 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.
|
||||
|
||||
BOLD='\033[1m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[0;33m'
|
||||
CYAN='\033[0;36m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
print_info() {
|
||||
printf "${CYAN}[INFO]${NC} %s\n" "$1"
|
||||
}
|
||||
print_success() {
|
||||
printf "${GREEN}[OK]${NC} %s\n" "$1"
|
||||
}
|
||||
print_step() {
|
||||
printf "${BOLD}==> %s${NC}\n" "$1"
|
||||
}
|
||||
|
||||
SYSTEM=$(uname -s)
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
if [ -z "$BASH" ] || [ "$BASH" = "/bin/sh" ] ; then
|
||||
ECHO=echo
|
||||
else
|
||||
ECHO='echo -e'
|
||||
fi
|
||||
SO=dylib
|
||||
LDD="otool -L"
|
||||
if [ "$(getopt -V)" = " --" ]; then
|
||||
>&2 $ECHO "gnu-getopt must be installed and used"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
if [ -z "$BASH" ]; then
|
||||
ECHO=echo
|
||||
else
|
||||
ECHO='echo -e'
|
||||
fi
|
||||
SO=so
|
||||
LDD=ldd
|
||||
fi
|
||||
|
||||
TEMP=`getopt -o v: --long headers:,libs:,cc:,cxx:,with-glog,with-thrift,with-rdma,with-mesalink,with-bthread-tracer,with-debug-bthread-sche-safety,with-debug-lock,with-asan,with-riscv-zvbc,with-riscv-zbc,nodebugsymbols,werror -n 'config_brpc' -- "$@"`
|
||||
WITH_GLOG=0
|
||||
WITH_THRIFT=0
|
||||
WITH_RDMA=0
|
||||
WITH_MESALINK=0
|
||||
WITH_BTHREAD_TRACER=0
|
||||
WITH_ASAN=0
|
||||
WITH_RISCV_ZVBC=0
|
||||
WITH_RISCV_ZBC=0
|
||||
BRPC_DEBUG_BTHREAD_SCHE_SAFETY=0
|
||||
DEBUGSYMBOLS=-g
|
||||
WERROR=
|
||||
BRPC_DEBUG_LOCK=0
|
||||
|
||||
if [ $? != 0 ] ; then >&2 $ECHO "Terminating..."; exit 1 ; fi
|
||||
|
||||
# Note the quotes around `$TEMP': they are essential!
|
||||
eval set -- "$TEMP"
|
||||
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
REALPATH=realpath
|
||||
else
|
||||
REALPATH="readlink -f"
|
||||
fi
|
||||
|
||||
# Convert to abspath always so that generated mk is include-able from everywhere
|
||||
while true; do
|
||||
case "$1" in
|
||||
--headers ) HDRS_IN="$(${REALPATH} $2)"; shift 2 ;;
|
||||
--libs ) LIBS_IN="$(${REALPATH} $2)"; shift 2 ;;
|
||||
--cc ) CC=$2; shift 2 ;;
|
||||
--cxx ) CXX=$2; shift 2 ;;
|
||||
--with-glog ) WITH_GLOG=1; shift 1 ;;
|
||||
--with-thrift) WITH_THRIFT=1; shift 1 ;;
|
||||
--with-rdma) WITH_RDMA=1; shift 1 ;;
|
||||
--with-mesalink) WITH_MESALINK=1; shift 1 ;;
|
||||
--with-bthread-tracer) WITH_BTHREAD_TRACER=1; shift 1 ;;
|
||||
--with-debug-bthread-sche-safety ) BRPC_DEBUG_BTHREAD_SCHE_SAFETY=1; shift 1 ;;
|
||||
--with-debug-lock ) BRPC_DEBUG_LOCK=1; shift 1 ;;
|
||||
--with-asan) WITH_ASAN=1; shift 1 ;;
|
||||
--with-riscv-zvbc) WITH_RISCV_ZVBC=1; shift 1 ;;
|
||||
--with-riscv-zbc) WITH_RISCV_ZBC=1; shift 1 ;;
|
||||
--nodebugsymbols ) DEBUGSYMBOLS=; shift 1 ;;
|
||||
--werror ) WERROR=-Werror; shift 1 ;;
|
||||
-- ) shift; break ;;
|
||||
* ) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
print_step "Configuring brpc (${SYSTEM})"
|
||||
print_info "Headers path: ${HDRS_IN}"
|
||||
print_info "Libs path: ${LIBS_IN}"
|
||||
|
||||
if [ -z "$CC" ]; then
|
||||
if [ ! -z "$CXX" ]; then
|
||||
>&2 $ECHO "--cc and --cxx must be both set or unset"
|
||||
exit 1
|
||||
fi
|
||||
CC=gcc
|
||||
CXX=g++
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
CC=clang
|
||||
CXX=clang++
|
||||
fi
|
||||
elif [ -z "$CXX" ]; then
|
||||
>&2 $ECHO "--cc and --cxx must be both set or unset"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "CC=$CC, CXX=$CXX"
|
||||
|
||||
GCC_VERSION=$(CXX=$CXX tools/print_gcc_version.sh)
|
||||
if [ $GCC_VERSION -gt 0 ] && [ $GCC_VERSION -lt 40800 ]; then
|
||||
>&2 $ECHO "GCC is too old, please install a newer version supporting C++11"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$HDRS_IN" ] || [ -z "$LIBS_IN" ]; then
|
||||
>&2 $ECHO "config_brpc: --headers=HDRPATHS --libs=LIBPATHS must be specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
find_dir_of_lib() {
|
||||
local lib=$(find ${LIBS_IN} -name "lib${1}.a" -o -name "lib${1}.$SO" 2>/dev/null | head -n1)
|
||||
if [ ! -z "$lib" ]; then
|
||||
dirname $lib
|
||||
fi
|
||||
}
|
||||
find_dir_of_lib_or_die() {
|
||||
local dir=$(find_dir_of_lib $1)
|
||||
if [ -z "$dir" ]; then
|
||||
>&2 $ECHO "Fail to find $1 from --libs"
|
||||
exit 1
|
||||
else
|
||||
$ECHO $dir
|
||||
fi
|
||||
}
|
||||
|
||||
find_bin() {
|
||||
TARGET_BIN=$(find -L ${LIBS_IN} -type f -name "$1" 2>/dev/null | head -n1)
|
||||
if [ ! -z "$TARGET_BIN" ]; then
|
||||
$ECHO $TARGET_BIN
|
||||
else
|
||||
which "$1" 2>/dev/null
|
||||
fi
|
||||
}
|
||||
find_bin_or_die() {
|
||||
TARGET_BIN=$(find_bin "$1")
|
||||
if [ ! -z "$TARGET_BIN" ]; then
|
||||
$ECHO $TARGET_BIN
|
||||
else
|
||||
>&2 $ECHO "Fail to find $1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
find_dir_of_header() {
|
||||
find -L ${HDRS_IN} -path "*/$1" | head -n1 | sed "s|$1||g"
|
||||
}
|
||||
|
||||
find_dir_of_header_excluding() {
|
||||
find -L ${HDRS_IN} -path "*/$1" | grep -v "$2\$" | head -n1 | sed "s|$1||g"
|
||||
}
|
||||
|
||||
find_dir_of_header_or_die() {
|
||||
if [ -z "$2" ]; then
|
||||
local dir=$(find_dir_of_header $1)
|
||||
else
|
||||
local dir=$(find_dir_of_header_excluding $1 $2)
|
||||
fi
|
||||
if [ -z "$dir" ]; then
|
||||
>&2 $ECHO "Fail to find $1 from --headers"
|
||||
exit 1
|
||||
fi
|
||||
$ECHO $dir
|
||||
}
|
||||
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
if [ -d "/usr/local/opt/openssl" ]; then
|
||||
LIBS_IN="/usr/local/opt/openssl/lib $LIBS_IN"
|
||||
HDRS_IN="/usr/local/opt/openssl/include $HDRS_IN"
|
||||
elif [ -d "/opt/homebrew/Cellar" ]; then
|
||||
LIBS_IN="/opt/homebrew/Cellar $LIBS_IN"
|
||||
HDRS_IN="/opt/homebrew/Cellar $HDRS_IN"
|
||||
fi
|
||||
fi
|
||||
|
||||
print_step "Checking dependencies"
|
||||
|
||||
# User specified path of openssl, if not given it's empty
|
||||
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 mesalink/openssl/ssl.h)
|
||||
print_success "Found openssl: lib=${OPENSSL_LIB:-system}, hdr=${OPENSSL_HDR}"
|
||||
|
||||
if [ $WITH_MESALINK != 0 ]; then
|
||||
MESALINK_HDR=$(find_dir_of_header_or_die mesalink/openssl/ssl.h)
|
||||
OPENSSL_HDR="$OPENSSL_HDR\n$MESALINK_HDR"
|
||||
fi
|
||||
|
||||
STATIC_LINKINGS=
|
||||
DYNAMIC_LINKINGS="-lpthread -lssl -lcrypto -ldl -lz"
|
||||
|
||||
if [ $WITH_MESALINK != 0 ]; then
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -lmesalink"
|
||||
fi
|
||||
|
||||
if [ "$SYSTEM" = "Linux" ]; then
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -lrt"
|
||||
fi
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework CoreFoundation"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework CoreGraphics"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework CoreData"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework CoreText"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework Security"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -framework Foundation"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_MallocExtension_ReleaseFreeMemory"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_ProfilerStart"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_ProfilerStop"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,__Z13GetStackTracePPvii"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_RegisterThriftProtocol"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_mallctl"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -Wl,-U,_malloc_stats_print"
|
||||
fi
|
||||
append_linking() {
|
||||
if [ -f $1/lib${2}.a ]; then
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
# *.a must be explicitly specified in clang
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS $1/lib${2}.a"
|
||||
else
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS -l$2"
|
||||
fi
|
||||
export STATICALLY_LINKED_$2=1
|
||||
else
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -l$2"
|
||||
export STATICALLY_LINKED_$2=0
|
||||
fi
|
||||
}
|
||||
|
||||
GFLAGS_LIB=$(find_dir_of_lib_or_die gflags)
|
||||
append_linking $GFLAGS_LIB gflags
|
||||
print_success "Found gflags: $GFLAGS_LIB"
|
||||
|
||||
PROTOBUF_LIB=$(find_dir_of_lib_or_die protobuf)
|
||||
append_linking $PROTOBUF_LIB protobuf
|
||||
print_success "Found protobuf: $PROTOBUF_LIB"
|
||||
|
||||
LEVELDB_LIB=$(find_dir_of_lib_or_die leveldb)
|
||||
print_success "Found leveldb: $LEVELDB_LIB"
|
||||
# required by leveldb
|
||||
if [ -f $LEVELDB_LIB/libleveldb.a ]; then
|
||||
if [ -f $LEVELDB_LIB/libleveldb.$SO ]; then
|
||||
if $LDD $LEVELDB_LIB/libleveldb.$SO | grep -q libsnappy; then
|
||||
SNAPPY_LIB=$(find_dir_of_lib snappy)
|
||||
REQUIRE_SNAPPY="yes"
|
||||
fi
|
||||
fi
|
||||
if [ -z "$REQUIRE_SNAPPY" ]; then
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS $LEVELDB_LIB/libleveldb.a"
|
||||
else
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS -lleveldb"
|
||||
fi
|
||||
elif [ -f $SNAPPY_LIB/libsnappy.a ]; then
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS $LEVELDB_LIB/libleveldb.a $SNAPPY_LIB/libsnappy.a"
|
||||
else
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS -lleveldb -lsnappy"
|
||||
fi
|
||||
else
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -lleveldb"
|
||||
fi
|
||||
else
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -lleveldb"
|
||||
fi
|
||||
|
||||
PROTOC=$(find_bin_or_die protoc)
|
||||
print_success "Found protoc: $PROTOC"
|
||||
|
||||
GFLAGS_HDR=$(find_dir_of_header_or_die gflags/gflags.h)
|
||||
|
||||
PROTOBUF_HDR=$(find_dir_of_header_or_die google/protobuf/message.h)
|
||||
PROTOBUF_VERSION=$(grep '#define GOOGLE_PROTOBUF_VERSION [0-9]\+' $PROTOBUF_HDR/google/protobuf/stubs/common.h | awk '{print $3}')
|
||||
if [ "$PROTOBUF_VERSION" -ge 4022000 ]; then
|
||||
# from v22, utf8_validity should be explicitly linked
|
||||
# https://github.com/protocolbuffers/protobuf/blob/a847a8dc4ba1d99e7ba917146c84438b4de7d085/cmake/libprotobuf.cmake#L47
|
||||
UTF8_VALIDITY_LIB=$(find_dir_of_lib utf8_validity)
|
||||
append_linking "$UTF8_VALIDITY_LIB" utf8_validity
|
||||
|
||||
ABSL_HDR=$(find_dir_of_header_or_die absl/base/config.h)
|
||||
ABSL_LIB=$(find_dir_of_lib_or_die absl_strings)
|
||||
ABSL_TARGET_NAMES="
|
||||
absl_bad_optional_access
|
||||
absl_bad_variant_access
|
||||
absl_base
|
||||
absl_city
|
||||
absl_civil_time
|
||||
absl_cord
|
||||
absl_cord_internal
|
||||
absl_cordz_functions
|
||||
absl_cordz_handle
|
||||
absl_cordz_info
|
||||
absl_crc32c
|
||||
absl_crc_cord_state
|
||||
absl_crc_cpu_detect
|
||||
absl_crc_internal
|
||||
absl_debugging_internal
|
||||
absl_demangle_internal
|
||||
absl_die_if_null
|
||||
absl_examine_stack
|
||||
absl_exponential_biased
|
||||
absl_flags
|
||||
absl_flags_commandlineflag
|
||||
absl_flags_commandlineflag_internal
|
||||
absl_flags_config
|
||||
absl_flags_internal
|
||||
absl_flags_marshalling
|
||||
absl_flags_private_handle_accessor
|
||||
absl_flags_program_name
|
||||
absl_flags_reflection
|
||||
absl_graphcycles_internal
|
||||
absl_hash
|
||||
absl_hashtablez_sampler
|
||||
absl_int128
|
||||
absl_kernel_timeout_internal
|
||||
absl_leak_check
|
||||
absl_log_entry
|
||||
absl_log_globals
|
||||
absl_log_initialize
|
||||
absl_log_internal_check_op
|
||||
absl_log_internal_conditions
|
||||
absl_log_internal_format
|
||||
absl_log_internal_globals
|
||||
absl_log_internal_log_sink_set
|
||||
absl_log_internal_message
|
||||
absl_log_internal_nullguard
|
||||
absl_log_internal_proto
|
||||
absl_log_severity
|
||||
absl_log_sink
|
||||
absl_low_level_hash
|
||||
absl_malloc_internal
|
||||
absl_raw_hash_set
|
||||
absl_raw_logging_internal
|
||||
absl_spinlock_wait
|
||||
absl_stacktrace
|
||||
absl_status
|
||||
absl_statusor
|
||||
absl_str_format_internal
|
||||
absl_strerror
|
||||
absl_string_view
|
||||
absl_strings
|
||||
absl_strings_internal
|
||||
absl_symbolize
|
||||
absl_synchronization
|
||||
absl_throw_delegate
|
||||
absl_time
|
||||
absl_time_zone
|
||||
"
|
||||
for i in $ABSL_TARGET_NAMES; do
|
||||
# ignore interface targets
|
||||
if [ -n "$(find_dir_of_lib $i)" ]; then
|
||||
append_linking "$ABSL_LIB" "$i"
|
||||
fi
|
||||
done
|
||||
CXXFLAGS="-std=c++17"
|
||||
print_success "Found protobuf version $PROTOBUF_VERSION (>= v22, using C++17 with abseil)"
|
||||
else
|
||||
CXXFLAGS="-std=c++0x"
|
||||
print_success "Found protobuf version $PROTOBUF_VERSION"
|
||||
fi
|
||||
|
||||
CPPFLAGS=
|
||||
|
||||
if [ $WITH_ASAN != 0 ]; then
|
||||
CPPFLAGS="${CPPFLAGS} -fsanitize=address"
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -fsanitize=address"
|
||||
fi
|
||||
|
||||
LEVELDB_HDR=$(find_dir_of_header_or_die leveldb/db.h)
|
||||
|
||||
if [ $WITH_BTHREAD_TRACER != 0 ]; then
|
||||
if [ "$SYSTEM" != "Linux" ] || [ "$(uname -m)" != "x86_64" ]; then
|
||||
>&2 $ECHO "bthread tracer is only supported on Linux x86_64 platform"
|
||||
exit 1
|
||||
fi
|
||||
LIBUNWIND_HDR=$(find_dir_of_header_or_die libunwind.h)
|
||||
LIBUNWIND_LIB=$(find_dir_of_lib_or_die unwind)
|
||||
ABSL_HDR=$(find_dir_of_header_or_die absl/base/config.h)
|
||||
ABSL_LIB=$(find_dir_of_lib_or_die absl_symbolize)
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -DBRPC_BTHREAD_TRACER"
|
||||
|
||||
if [ -f "$LIBUNWIND_LIB/libunwind.$SO" ]; then
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -lunwind -lunwind-x86_64"
|
||||
else
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS -lunwind -lunwind-x86_64"
|
||||
fi
|
||||
if [ -f "$ABSL_LIB/libabsl_base.$SO" ]; then
|
||||
DYNAMIC_LINKINGS="$DYNAMIC_LINKINGS -labsl_stacktrace -labsl_symbolize -labsl_debugging_internal -labsl_demangle_internal -labsl_malloc_internal -labsl_raw_logging_internal -labsl_spinlock_wait -labsl_base"
|
||||
else
|
||||
STATIC_LINKINGS="$STATIC_LINKINGS -labsl_stacktrace -labsl_symbolize -labsl_debugging_internal -labsl_demangle_internal -labsl_malloc_internal -labsl_raw_logging_internal -labsl_spinlock_wait -labsl_base"
|
||||
fi
|
||||
fi
|
||||
|
||||
HDRS=$($ECHO "$LIBUNWIND_HDR\n$GFLAGS_HDR\n$PROTOBUF_HDR\n$ABSL_HDR\n$LEVELDB_HDR\n$OPENSSL_HDR" | sort | uniq)
|
||||
LIBS=$($ECHO "$LIBUNWIND_LIB\n$GFLAGS_LIB\n$PROTOBUF_LIB\n$ABSL_LIB\n$LEVELDB_LIB\n$OPENSSL_LIB\n$SNAPPY_LIB" | sort | uniq)
|
||||
|
||||
absent_in_the_list() {
|
||||
TMP=`$ECHO "$1\n$2" | sort | uniq`
|
||||
if [ "${TMP}" = "$2" ]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
OUTPUT_CONTENT="# Generated by config_brpc.sh, don't modify manually"
|
||||
append_to_output() {
|
||||
OUTPUT_CONTENT="${OUTPUT_CONTENT}\n$*"
|
||||
}
|
||||
# $1: libname, $2: indentation
|
||||
append_to_output_headers() {
|
||||
if absent_in_the_list "$1" "$HDRS"; then
|
||||
append_to_output "${2}HDRS+=$1"
|
||||
HDRS=`$ECHO "${HDRS}\n$1" | sort | uniq`
|
||||
fi
|
||||
}
|
||||
# $1: libname, $2: indentation
|
||||
append_to_output_libs() {
|
||||
if absent_in_the_list "$1" "$LIBS"; then
|
||||
append_to_output "${2}LIBS+=$1"
|
||||
LIBS=`$ECHO "${LIBS}\n$1" | sort | uniq`
|
||||
fi
|
||||
}
|
||||
# $1: libdir, $2: libname, $3: indentation
|
||||
append_to_output_linkings() {
|
||||
if [ -f $1/lib$2.a ]; then
|
||||
append_to_output_libs $1 $3
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
append_to_output "${3}STATIC_LINKINGS+=$1/lib$2.a"
|
||||
else
|
||||
append_to_output "${3}STATIC_LINKINGS+=-l$2"
|
||||
fi
|
||||
export STATICALLY_LINKED_$2=1
|
||||
else
|
||||
append_to_output_libs $1 $3
|
||||
append_to_output "${3}DYNAMIC_LINKINGS+=-l$2"
|
||||
export STATICALLY_LINKED_$2=0
|
||||
fi
|
||||
}
|
||||
|
||||
#can't use \n in texts because sh does not support -e
|
||||
append_to_output "SYSTEM=$SYSTEM"
|
||||
append_to_output "HDRS=$($ECHO $HDRS)"
|
||||
append_to_output "LIBS=$($ECHO $LIBS)"
|
||||
append_to_output "PROTOC=$PROTOC"
|
||||
append_to_output "PROTOBUF_HDR=$PROTOBUF_HDR"
|
||||
append_to_output "CC=$CC"
|
||||
append_to_output "CXX=$CXX"
|
||||
append_to_output "GCC_VERSION=$GCC_VERSION"
|
||||
append_to_output "STATIC_LINKINGS=$STATIC_LINKINGS"
|
||||
append_to_output "DYNAMIC_LINKINGS=$DYNAMIC_LINKINGS"
|
||||
|
||||
# CPP means C PreProcessing, not C PlusPlus
|
||||
CPPFLAGS="${CPPFLAGS} -DBRPC_WITH_GLOG=$WITH_GLOG -DBRPC_DEBUG_BTHREAD_SCHE_SAFETY=$BRPC_DEBUG_BTHREAD_SCHE_SAFETY -DBRPC_DEBUG_LOCK=$BRPC_DEBUG_LOCK"
|
||||
|
||||
# Avoid over-optimizations of TLS variables by GCC>=4.8
|
||||
# See: https://github.com/apache/brpc/issues/1693
|
||||
CPPFLAGS="${CPPFLAGS} -D__const__=__unused__"
|
||||
|
||||
if [ ! -z "$DEBUGSYMBOLS" ]; then
|
||||
CPPFLAGS="${CPPFLAGS} $DEBUGSYMBOLS"
|
||||
fi
|
||||
if [ ! -z "$WERROR" ]; then
|
||||
CPPFLAGS="${CPPFLAGS} $WERROR"
|
||||
fi
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
CPPFLAGS="${CPPFLAGS} -Wno-deprecated-declarations -Wno-inconsistent-missing-override"
|
||||
version=`sw_vers -productVersion | awk -F '.' '{print $1 "." $2}'`
|
||||
if [[ `echo "$version<10.12" | bc -l` == 1 ]]; then
|
||||
CPPFLAGS="${CPPFLAGS} -DNO_CLOCK_GETTIME_IN_MAC"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $WITH_THRIFT != 0 ]; then
|
||||
THRIFT_LIB=$(find_dir_of_lib_or_die thriftnb)
|
||||
THRIFT_HDR=$(find_dir_of_header_or_die thrift/Thrift.h)
|
||||
append_to_output_libs "$THRIFT_LIB"
|
||||
append_to_output_headers "$THRIFT_HDR"
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -DENABLE_THRIFT_FRAMED_PROTOCOL"
|
||||
|
||||
if [ -f "$THRIFT_LIB/libthriftnb.$SO" ]; then
|
||||
append_to_output "DYNAMIC_LINKINGS+=-lthriftnb -levent -lthrift"
|
||||
else
|
||||
append_to_output "STATIC_LINKINGS+=-lthriftnb"
|
||||
fi
|
||||
# get thrift version
|
||||
thrift_version=$(thrift --version | awk '{print $3}')
|
||||
major=$(echo "$thrift_version" | awk -F '.' '{print $1}')
|
||||
minor=$(echo "$thrift_version" | awk -F '.' '{print $2}')
|
||||
if [ $((major)) -eq 0 -a $((minor)) -lt 11 ]; then
|
||||
CPPFLAGS="${CPPFLAGS} -D_THRIFT_VERSION_LOWER_THAN_0_11_0_"
|
||||
echo "less"
|
||||
else
|
||||
echo "greater"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $WITH_RDMA != 0 ]; then
|
||||
RDMA_LIB=$(find_dir_of_lib_or_die ibverbs)
|
||||
RDMA_HDR=$(find_dir_of_header_or_die infiniband/verbs.h)
|
||||
append_to_output_libs "$RDMA_LIB"
|
||||
append_to_output_headers "$RDMA_HDR"
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -DBRPC_WITH_RDMA"
|
||||
|
||||
append_to_output "DYNAMIC_LINKINGS+=-libverbs"
|
||||
append_to_output "WITH_RDMA=1"
|
||||
fi
|
||||
|
||||
if [ $WITH_MESALINK != 0 ]; then
|
||||
CPPFLAGS="${CPPFLAGS} -DUSE_MESALINK"
|
||||
fi
|
||||
|
||||
append_to_output "CPPFLAGS=${CPPFLAGS}"
|
||||
append_to_output "# without the flag, linux+arm64 may crash due to folding on TLS.
|
||||
ifeq (\$(CC),gcc)
|
||||
ifeq (\$(shell uname -p),aarch64)
|
||||
CPPFLAGS+=-fno-gcse
|
||||
endif
|
||||
endif
|
||||
"
|
||||
|
||||
# RISC-V Zvbc/Zbc support
|
||||
if [ "$(uname -m)" = "riscv64" ]; then
|
||||
if [ $WITH_RISCV_ZVBC != 0 ]; then
|
||||
CXXFLAGS="${CXXFLAGS} -march=rv64gcv_zbc_zvbc"
|
||||
print_success "RISC-V Zvbc enabled: -march=rv64gcv_zbc_zvbc"
|
||||
elif [ $WITH_RISCV_ZBC != 0 ]; then
|
||||
CXXFLAGS="${CXXFLAGS} -march=rv64gc_zbc"
|
||||
print_success "RISC-V Zbc enabled: -march=rv64gc_zbc"
|
||||
fi
|
||||
fi
|
||||
|
||||
append_to_output "CXXFLAGS=${CXXFLAGS}"
|
||||
|
||||
append_to_output "ifeq (\$(NEED_LIBPROTOC), 1)"
|
||||
PROTOC_LIB=$(find $PROTOBUF_LIB -name "libprotoc.*" | head -n1)
|
||||
if [ -z "$PROTOC_LIB" ]; then
|
||||
append_to_output " \$(error \"Fail to find libprotoc\")"
|
||||
else
|
||||
# libprotobuf and libprotoc must be linked same statically or dynamically
|
||||
# otherwise the bin will crash.
|
||||
if [ $STATICALLY_LINKED_protobuf -gt 0 ]; then
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
append_to_output " STATIC_LINKINGS+=$(find $PROTOBUF_LIB -name "libprotoc.a" | head -n1)"
|
||||
else
|
||||
append_to_output " STATIC_LINKINGS+=-lprotoc"
|
||||
fi
|
||||
else
|
||||
append_to_output " DYNAMIC_LINKINGS+=-lprotoc"
|
||||
fi
|
||||
fi
|
||||
append_to_output "endif"
|
||||
|
||||
OLD_HDRS=$HDRS
|
||||
OLD_LIBS=$LIBS
|
||||
append_to_output "ifeq (\$(NEED_GPERFTOOLS), 1)"
|
||||
# required by cpu/heap profiler
|
||||
TCMALLOC_LIB=$(find_dir_of_lib tcmalloc_and_profiler)
|
||||
if [ -z "$TCMALLOC_LIB" ]; then
|
||||
append_to_output " \$(error \"Fail to find gperftools\")"
|
||||
elif [ $WITH_ASAN != 0 ]; then
|
||||
append_to_output " \$(error \"gperftools is not compatible with ASAN\")"
|
||||
else
|
||||
append_to_output " CPPFLAGS+=-DBRPC_ENABLE_CPU_PROFILER"
|
||||
append_to_output_libs "$TCMALLOC_LIB" " "
|
||||
if [ -f $TCMALLOC_LIB/libtcmalloc.$SO ]; then
|
||||
append_to_output " DYNAMIC_LINKINGS+=-ltcmalloc_and_profiler"
|
||||
else
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
append_to_output " STATIC_LINKINGS+=$TCMALLOC_LIB/libtcmalloc.a"
|
||||
else
|
||||
append_to_output " STATIC_LINKINGS+=-ltcmalloc_and_profiler"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
append_to_output "endif"
|
||||
|
||||
if [ $WITH_GLOG != 0 ]; then
|
||||
GLOG_LIB=$(find_dir_of_lib_or_die glog)
|
||||
GLOG_HDR=$(find_dir_of_header_or_die glog/logging.h windows/glog/logging.h)
|
||||
append_to_output_libs "$GLOG_LIB"
|
||||
append_to_output_headers "$GLOG_HDR"
|
||||
if [ -f "$GLOG_LIB/libglog.$SO" ]; then
|
||||
append_to_output "DYNAMIC_LINKINGS+=-lglog"
|
||||
else
|
||||
if [ "$SYSTEM" = "Darwin" ]; then
|
||||
append_to_output "STATIC_LINKINGS+=$GLOG_LIB/libglog.a"
|
||||
else
|
||||
append_to_output "STATIC_LINKINGS+=-lglog"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# required by UT
|
||||
#gtest
|
||||
GTEST_LIB=$(find_dir_of_lib gtest)
|
||||
HDRS=$OLD_HDRS
|
||||
LIBS=$OLD_LIBS
|
||||
append_to_output "ifeq (\$(NEED_GTEST), 1)"
|
||||
if [ -z "$GTEST_LIB" ]; then
|
||||
append_to_output " \$(error \"Fail to find gtest\")"
|
||||
else
|
||||
GTEST_HDR=$(find_dir_of_header_or_die gtest/gtest.h)
|
||||
append_to_output_libs $GTEST_LIB " "
|
||||
append_to_output_headers $GTEST_HDR " "
|
||||
append_to_output_linkings $GTEST_LIB gtest " "
|
||||
append_to_output_linkings $GTEST_LIB gtest_main " "
|
||||
fi
|
||||
append_to_output "endif"
|
||||
|
||||
# generate src/butil/config.h
|
||||
cat << EOF > src/butil/config.h
|
||||
// This file is auto-generated by $(basename "$0"). DON'T edit it!
|
||||
#ifndef BUTIL_CONFIG_H
|
||||
#define BUTIL_CONFIG_H
|
||||
|
||||
#ifdef BRPC_WITH_GLOG
|
||||
#undef BRPC_WITH_GLOG
|
||||
#endif
|
||||
#define BRPC_WITH_GLOG $WITH_GLOG
|
||||
|
||||
#endif // BUTIL_CONFIG_H
|
||||
EOF
|
||||
|
||||
print_step "Generating output files"
|
||||
|
||||
# write to config.mk
|
||||
$ECHO "$OUTPUT_CONTENT" > config.mk
|
||||
print_success "Generated config.mk"
|
||||
print_success "Generated src/butil/config.h"
|
||||
|
||||
printf "\n"
|
||||
print_step "Configuration complete"
|
||||
print_info "Compiler: $CC / $CXX"
|
||||
print_info "C++ std: $CXXFLAGS"
|
||||
print_info "System: $SYSTEM"
|
||||
if [ $WITH_GLOG -ne 0 ]; then print_info "With glog: yes"; fi
|
||||
if [ $WITH_THRIFT -ne 0 ]; then print_info "With thrift: yes"; fi
|
||||
if [ $WITH_RDMA -ne 0 ]; then print_info "With RDMA: yes"; fi
|
||||
if [ $WITH_MESALINK -ne 0 ]; then print_info "With MesaLink: yes"; fi
|
||||
if [ $WITH_BTHREAD_TRACER -ne 0 ]; then print_info "With bthread tracer: yes"; fi
|
||||
if [ $WITH_ASAN -ne 0 ]; then print_info "With ASAN: yes"; fi
|
||||
printf "\n${GREEN}brpc is now configured. You can build it with 'make'.${NC}\n"
|
||||
@@ -0,0 +1,107 @@
|
||||
[English version](../en/atomic_instructions.md)
|
||||
|
||||
我们都知道多核编程常用锁避免多个线程在修改同一个数据时产生[race condition](http://en.wikipedia.org/wiki/Race_condition)。当锁成为性能瓶颈时,我们又总想试着绕开它,而不可避免地接触了原子指令。但在实践中,用原子指令写出正确的代码是一件非常困难的事,琢磨不透的race condition、[ABA problem](https://en.wikipedia.org/wiki/ABA_problem)、[memory fence](https://en.wikipedia.org/wiki/Memory_barrier)很烧脑,这篇文章试图通过介绍[SMP](http://en.wikipedia.org/wiki/Symmetric_multiprocessing)架构下的原子指令帮助大家入门。C++11正式引入了[原子指令](http://en.cppreference.com/w/cpp/atomic/atomic),我们就以其语法描述。
|
||||
|
||||
顾名思义,原子指令是**对软件**不可再分的指令,比如x.fetch_add(n)指原子地给x加上n,这个指令**对软件**要么没做,要么完成,不会观察到中间状态。常见的原子指令有:
|
||||
|
||||
| 原子指令 (x均为std::atomic<int>) | 作用 |
|
||||
| ---------------------------------------- | ---------------------------------------- |
|
||||
| x.load() | 返回x的值。 |
|
||||
| x.store(n) | 把x设为n,什么都不返回。 |
|
||||
| x.exchange(n) | 把x设为n,返回设定之前的值。 |
|
||||
| x.compare_exchange_strong(expected_ref, desired) | 若x等于expected_ref,则设为desired,返回成功;否则把最新值写入expected_ref,返回失败。 |
|
||||
| x.compare_exchange_weak(expected_ref, desired) | 相比compare_exchange_strong可能有[spurious wakeup](http://en.wikipedia.org/wiki/Spurious_wakeup)。 |
|
||||
| x.fetch_add(n), x.fetch_sub(n) | 原子地做x += n, x-= n,返回修改之前的值。 |
|
||||
|
||||
你已经可以用这些指令做原子计数,比如多个线程同时累加一个原子变量,以统计这些线程对一些资源的操作次数。但是,这可能会有两个问题:
|
||||
|
||||
- 这个操作没有你想象地快。
|
||||
- 如果你尝试通过看似简单的原子操作控制对一些资源的访问,你的程序有很大几率会crash。
|
||||
|
||||
# Cacheline
|
||||
|
||||
没有任何竞争或只被一个线程访问的原子操作是比较快的,“竞争”指的是多个线程同时访问同一个[cacheline](https://en.wikipedia.org/wiki/CPU_cache#Cache_entries)。现代CPU为了以低价格获得高性能,大量使用了cache,并把cache分了多级。百度内常见的Intel E5-2620拥有32K的L1 dcache和icache,256K的L2 cache和15M的L3 cache。其中L1和L2 cache为每个核心独有,L3则所有核心共享。一个核心写入自己的L1 cache是极快的(4 cycles, ~2ns),但当另一个核心读或写同一处内存时,它得确认看到其他核心中对应的cacheline。对于软件来说,这个过程是原子的,不能在中间穿插其他代码,只能等待CPU完成[一致性同步](https://en.wikipedia.org/wiki/Cache_coherence),这个复杂的硬件算法使得原子操作会变得很慢,在E5-2620上竞争激烈时fetch_add会耗费700纳秒左右。访问被多个线程频繁共享的内存往往是比较慢的。比如像一些场景临界区看着很小,但保护它的spinlock性能不佳,因为spinlock使用的exchange, fetch_add等指令必须等待最新的cacheline,看上去只有几条指令,花费若干微秒并不奇怪。
|
||||
|
||||
要提高性能,就要避免让CPU频繁同步cacheline。这不单和原子指令本身的性能有关,还会影响到程序的整体性能。最有效的解决方法很直白:**尽量避免共享**。
|
||||
|
||||
- 一个依赖全局多生产者多消费者队列(MPMC)的程序难有很好的多核扩展性,因为这个队列的极限吞吐取决于同步cache的延时,而不是核心的个数。最好是用多个SPMC或多个MPSC队列,甚至多个SPSC队列代替,在源头就规避掉竞争。
|
||||
- 另一个例子是计数器,如果所有线程都频繁修改一个计数器,性能就会很差,原因同样在于不同的核心在不停地同步同一个cacheline。如果这个计数器只是用作打打日志之类的,那我们完全可以让每个线程修改thread-local变量,在需要时再合并所有线程中的值,性能可能有[几十倍的差别](bvar.md)。
|
||||
|
||||
一个相关的编程陷阱是false sharing:对那些不怎么被修改甚至只读变量的访问,由于同一个cacheline中的其他变量被频繁修改,而不得不经常等待cacheline同步而显著变慢了。多线程中的变量尽量按访问规律排列,频繁被其他线程修改的变量要放在独立的cacheline中。要让一个变量或结构体按cacheline对齐,可以include \<butil/macros.h\>后使用BAIDU_CACHELINE_ALIGNMENT宏,请自行grep brpc的代码了解用法。
|
||||
|
||||
# Memory fence
|
||||
|
||||
仅靠原子技术实现不了对资源的访问控制,即使简单如[spinlock](https://en.wikipedia.org/wiki/Spinlock)或[引用计数](https://en.wikipedia.org/wiki/Reference_counting),看上去正确的代码也可能会crash。这里的关键在于**重排指令**导致了读写顺序的变化。只要没有依赖,代码中在后面的指令就可能跑到前面去,[编译器](http://preshing.com/20120625/memory-ordering-at-compile-time/)和[CPU](https://en.wikipedia.org/wiki/Out-of-order_execution)都会这么做。
|
||||
|
||||
这么做的动机非常自然,CPU要尽量塞满每个cycle,在单位时间内运行尽量多的指令。如上节中提到的,访存指令在等待cacheline同步时要花费数百纳秒,最高效地自然是同时同步多个cacheline,而不是一个个做。一个线程在代码中对多个变量的依次修改,可能会以不同的次序同步到另一个线程所在的核心上。不同线程对数据的需求不同,按需同步也会导致cacheline的读序和写序不同。
|
||||
|
||||
如果其中第一个变量扮演了开关的作用,控制对后续变量的访问。那么当这些变量被一起同步到其他核心时,更新顺序可能变了,第一个变量未必是第一个更新的,然而其他线程还认为它代表着其他变量有效,去访问了实际已被删除的变量,从而导致未定义的行为。比如下面的代码片段:
|
||||
|
||||
```c++
|
||||
// Thread 1
|
||||
// bool ready was initialized to false
|
||||
p.init();
|
||||
ready = true;
|
||||
```
|
||||
|
||||
```c++
|
||||
// Thread2
|
||||
if (ready) {
|
||||
p.bar();
|
||||
}
|
||||
```
|
||||
从人的角度,这是对的,因为线程2在ready为true时才会访问p,按线程1的逻辑,此时p应该初始化好了。但对多核机器而言,这段代码可能难以正常运行:
|
||||
|
||||
- 线程1中的ready = true可能会被编译器或cpu重排到p.init()之前,从而使线程2看到ready为true时,p仍然未初始化。这种情况同样也会在线程2中发生,p.bar()中的一些代码可能被重排到检查ready之前。
|
||||
- 即使没有重排,ready和p的值也会独立地同步到线程2所在核心的cache,线程2仍然可能在看到ready为true时看到未初始化的p。
|
||||
|
||||
注:x86/x64的load带acquire语意,store带release语意,上面的代码刨除编译器和CPU因素可以正确运行。
|
||||
|
||||
通过这个简单例子,你可以窥见原子指令编程的复杂性了吧。为了解决这个问题,CPU和编译器提供了[memory fence](http://en.wikipedia.org/wiki/Memory_barrier),让用户可以声明访存指令间的可见性(visibility)关系,boost和C++11对memory fence做了抽象,总结为如下几种[memory order](http://en.cppreference.com/w/cpp/atomic/memory_order).
|
||||
|
||||
| memory order | 作用 |
|
||||
| -------------------- | ---------------------------------------- |
|
||||
| memory_order_relaxed | 没有fencing作用 |
|
||||
| memory_order_consume | 后面依赖此原子变量的访存指令勿重排至此条指令之前 |
|
||||
| memory_order_acquire | 后面访存指令勿重排至此条指令之前 |
|
||||
| memory_order_release | 前面访存指令勿重排至此条指令之后。当此条指令的结果对其他线程可见后,之前的所有指令都可见 |
|
||||
| memory_order_acq_rel | acquire + release语意 |
|
||||
| memory_order_seq_cst | acq_rel语意外加所有使用seq_cst的指令有严格地全序关系 |
|
||||
|
||||
有了memory order,上面的例子可以这么更正:
|
||||
|
||||
```c++
|
||||
// Thread1
|
||||
// std::atomic<bool> ready was initialized to false
|
||||
p.init();
|
||||
ready.store(true, std::memory_order_release);
|
||||
```
|
||||
|
||||
```c++
|
||||
// Thread2
|
||||
if (ready.load(std::memory_order_acquire)) {
|
||||
p.bar();
|
||||
}
|
||||
```
|
||||
|
||||
线程2中的acquire和线程1的release配对,确保线程2在看到ready==true时能看到线程1 release之前所有的访存操作。
|
||||
|
||||
注意,memory fence不等于可见性,即使线程2恰好在线程1在把ready设置为true后读取了ready也不意味着它能看到true,因为同步cache是有延时的。memory fence保证的是可见性的顺序:“假如我看到了a的最新值,那么我一定也得看到b的最新值”。
|
||||
|
||||
一个相关问题是:如何知道看到的值是新还是旧?一般分两种情况:
|
||||
|
||||
- 值是特殊的。比如在上面的例子中,ready=true是个特殊值,只要线程2看到ready为true就意味着更新了。只要设定了特殊值,读到或没有读到特殊值都代表了一种含义。
|
||||
- 总是累加。一些场景下没有特殊值,那我们就用fetch_add之类的指令累加一个变量,只要变量的值域足够大,在很长一段时间内,新值和之前所有的旧值都会不相同,我们就能区分彼此了。
|
||||
|
||||
原子指令的例子可以看boost.atomic的[Example](http://www.boost.org/doc/libs/1_56_0/doc/html/atomic/usage_examples.html),atomic的官方描述可以看[这里](http://en.cppreference.com/w/cpp/atomic/atomic)。
|
||||
|
||||
# wait-free & lock-free
|
||||
|
||||
原子指令能为我们的服务赋予两个重要属性:[wait-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom)和[lock-free](http://en.wikipedia.org/wiki/Non-blocking_algorithm#Lock-freedom)。前者指不管OS如何调度线程,每个线程都始终在做有用的事;后者比前者弱一些,指不管OS如何调度线程,至少有一个线程在做有用的事。如果我们的服务中使用了锁,那么OS可能把一个刚获得锁的线程切换出去,这时候所有依赖这个锁的线程都在等待,而没有做有用的事,所以用了锁就不是lock-free,更不会是wait-free。为了确保一件事情总在确定时间内完成,实时系统的关键代码至少是lock-free的。在百度广泛又多样的在线服务中,对时效性也有着严苛的要求,如果RPC中最关键的部分满足wait-free或lock-free,就可以提供更稳定的服务质量。事实上,brpc中的读写都是wait-free的,具体见[IO](io.md)。
|
||||
|
||||
值得提醒的是,常见想法是lock-free或wait-free的算法会更快,但事实可能相反,因为:
|
||||
|
||||
- lock-free和wait-free必须处理更多更复杂的race condition和ABA problem,完成相同目的的代码比用锁更复杂。代码越多,耗时就越长。
|
||||
- 使用mutex的算法变相带“后退”效果。后退(backoff)指出现竞争时尝试另一个途径以临时避免竞争,mutex出现竞争时会使调用者睡眠,使拿到锁的那个线程可以很快地独占完成一系列流程,总体吞吐可能反而高了。
|
||||
|
||||
mutex导致低性能往往是因为临界区过大(限制了并发度),或竞争过于激烈(上下文切换开销变得突出)。lock-free/wait-free算法的价值在于其保证了一个或所有线程始终在做有用的事,而不是绝对的高性能。但在一种情况下lock-free和wait-free算法的性能多半更高:就是算法本身可以用少量原子指令实现。实现锁也是要用原子指令的,当算法本身用一两条指令就能完成的时候,相比额外用锁肯定是更快了。
|
||||
@@ -0,0 +1,174 @@
|
||||
# 自适应限流
|
||||
|
||||
服务的处理能力是有客观上限的。当请求速度超过服务的处理速度时,服务就会过载。
|
||||
|
||||
如果服务持续过载,会导致越来越多的请求积压,最终所有的请求都必须等待较长时间才能被处理,从而使整个服务处于瘫痪状态。
|
||||
|
||||
与之相对的,如果直接拒绝掉一部分请求,反而能够让服务能够"及时"处理更多的请求。对应的方法就是[设置最大并发](https://github.com/apache/brpc/blob/master/docs/cn/server.md#%E9%99%90%E5%88%B6%E6%9C%80%E5%A4%A7%E5%B9%B6%E5%8F%91)。
|
||||
|
||||
自适应限流能动态调整服务的最大并发,在保证服务不过载的前提下,让服务尽可能多的处理请求。
|
||||
|
||||
## 使用场景
|
||||
通常情况下要让服务不过载,只需在上线前进行压力测试,并通过little's law计算出best_max_concurrency就可以了。但在服务数量多,拓扑复杂,且处理能力会逐渐变化的局面下,使用固定的最大并发会带来巨大的测试工作量,很不方便。自适应限流就是为了解决这个问题。
|
||||
|
||||
使用自适应限流前建议做到:
|
||||
1. 客户端开启了重试功能。
|
||||
|
||||
2. 服务端有多个节点。
|
||||
|
||||
这样当一个节点返回过载时,客户端可以向其他的节点发起重试,从而尽量不丢失流量。
|
||||
|
||||
## 开启方法
|
||||
目前只有method级别支持自适应限流。如果要为某个method开启自适应限流,只需要将它的最大并发设置为"auto"即可。
|
||||
|
||||
```c++
|
||||
// Set auto concurrency limiter for all methods
|
||||
brpc::ServerOptions options;
|
||||
options.method_max_concurrency = "auto";
|
||||
|
||||
// Set auto concurrency limiter for specific method
|
||||
server.MaxConcurrencyOf("example.EchoService.Echo") = "auto";
|
||||
```
|
||||
|
||||
## 原理
|
||||
|
||||
### 名词
|
||||
**concurrency**: 同时处理的请求数,又被称为“并发度”。
|
||||
|
||||
**max_concurrency**: 设置的最大并发度。超过并发的请求会被拒绝(返回ELIMIT错误),在集群层面,client应重试到另一台server上去。
|
||||
|
||||
**best_max_concurrency**: 并发的物理含义是任务处理槽位,天然存在上限,这个上限就是best_max_concurrency。若max_concurrency设置的过大,则concurrency可能大于best_max_concurrency,任务将无法被及时处理而暂存在各种队列中排队,系统也会进入拥塞状态。若max_concurrency设置的过小,则concurrency总是会小于best_max_concurrency,限制系统达到本可以达到的更高吞吐。
|
||||
|
||||
**noload_latency**: 单纯处理任务的延时,不包括排队时间。另一种解释是低负载的延时。由于正确处理任务得经历必要的环节,其中会耗费cpu或等待下游返回,noload_latency是一个服务固有的属性,但可能随时间逐渐改变(由于内存碎片,压力变化,业务数据变化等因素)。
|
||||
|
||||
**min_latency**: 实际测定的latency中的较小值的ema,当concurrency不大于best_max_concurrency时,min_latency和noload_latency接近(可能轻微上升)。
|
||||
|
||||
**peak_qps**: qps的上限。注意是处理或回复的qps而不是接收的qps。值取决于best_max_concurrency / noload_latency,这两个量都是服务的固有属性,故peak_qps也是服务的固有属性,和拥塞状况无关,但可能随时间逐渐改变。
|
||||
|
||||
**max_qps**: 实际测定的qps中的较大值。由于qps具有上限,max_qps总是会小于peak_qps,不论拥塞与否。
|
||||
|
||||
### Little's Law
|
||||
在服务处于稳定状态时: concurrency = latency * qps。 这是自适应限流的理论基础。
|
||||
|
||||
当服务没有超载时,随着流量的上升,latency基本稳定(接近noload_latency),qps和concurrency呈线性关系一起上升。
|
||||
|
||||
当流量超过服务的peak_qps时,则concurrency和latency会一起上升,而qps会稳定在peak_qps。
|
||||
|
||||
假如一个服务的peak_qps和noload_latency都比较稳定,那么它的best_max_concurrency = noload_latency * peak_qps。
|
||||
|
||||
自适应限流就是要找到服务的noload_latency和peak_qps, 并将最大并发设置为靠近两者乘积的一个值。
|
||||
|
||||
### 计算公式
|
||||
|
||||
自适应限流会不断的对请求进行采样,当采样窗口的样本数量足够时,会根据样本的平均延迟和服务当前的qps计算出下一个采样窗口的max_concurrency:
|
||||
|
||||
> max_concurrency = max_qps * ((2+alpha) * min_latency - latency)
|
||||
|
||||
alpha为可接受的延时上升幅度,默认0.3。
|
||||
|
||||
latency是当前采样窗口内所有请求的平均latency。
|
||||
|
||||
max_qps是最近一段时间测量到的qps的极大值。
|
||||
|
||||
min_latency是最近一段时间测量到的latency较小值的ema,是noload_latency的估算值。
|
||||
|
||||
注意:当计算出来的 max_concurrency 和当前的 max_concurrency 的值不同时,每次对 max_concurrency 的调整的比例有一个上限,让 max_concurrency
|
||||
的[变化更为平滑](https://github.com/apache/brpc/blob/master/src/brpc/policy/auto_concurrency_limiter.cpp#L249)。
|
||||
|
||||
当服务处于低负载时,min_latency约等于noload_latency,此时计算出来的max_concurrency会高于concurrency,但低于best_max_concurrency,给流量上涨留探索空间。而当服务过载时,服务的qps约等于max_qps,同时latency开始明显超过min_latency,此时max_concurrency则会接近concurrency,并通过定期衰减避免远离best_max_concurrency,保证服务不会过载。
|
||||
|
||||
|
||||
### 估算noload_latency
|
||||
服务的noload_latency并非是一成不变的,自适应限流必须能够正确的探测noload_latency的变化。当noload_latency下降时,是很容感知到的,因为这个时候latency也会下降。难点在于当latency上涨时,需要能够正确的辨别到底是服务过载了,还是noload_latency上涨了。
|
||||
|
||||
可能的方案有:
|
||||
1. 取最近一段时间的最小latency来近似noload_latency
|
||||
2. 取最近一段时间的latency的各种平均值来预测noload_latency
|
||||
3. 收集请求的平均排队等待时间,使用latency - queue_time作为noload_latency
|
||||
4. 每隔一段时间缩小max_concurrency,过一小段时间后以此时的latency作为noload_latency
|
||||
|
||||
方案1和方案2的问题在于:假如服务持续处于高负载,那么最近的所有latency都会高出noload_latency,从而使得算法估计的noload_latency不断升高。
|
||||
|
||||
方案3的问题在于,假如服务的性能瓶颈在下游服务,那么请求在服务本身的排队等待时间无法反应整体的负载情况。
|
||||
|
||||
方案4是最通用的,也经过了大量实验的考验。缩小max_concurrency和公式中的alpha存在关联。让我们做个假想实验,若latency极为稳定并都等于min_latency,那么公式简化为max_concurrency = max_qps * latency * (1 + alpha)。根据little's law,qps最多为max_qps * (1 + alpha). alpha是qps的"探索空间",若alpha为0,则qps被锁定为max_qps,算法可能无法探索到peak_qps。但在qps已经达到peak_qps时,alpha会使延时上升(已拥塞),此时测定的min_latency会大于noload_latency,一轮轮下去最终会导致min_latency不收敛。定期降低max_concurrency就是阻止这个过程,并给min_latency下降提供"探索空间"。
|
||||
|
||||
#### 减少重测时的流量损失
|
||||
|
||||
每隔一段时间,自适应限流算法都会缩小max_concurrency,并持续一段时间,然后将此时的latency作为服务的noload_latency,以处理noload_latency上涨了的情况。测量noload_latency时,必须让先服务处于低负载的状态,因此对max_concurrency的缩小是难以避免的。
|
||||
|
||||
由于max_concurrency < concurrency时,服务会拒绝掉所有的请求,限流算法将"排空所有的经历过排队等待的请求的时间" 设置为 latency * 2 ,以确保用于计算min_latency的样本绝大部分都是没有经过排队等待的。
|
||||
|
||||
由于服务的latency通常都不会太长,这种做法所带来的流量损失也很小。
|
||||
|
||||
#### 应对抖动
|
||||
即使服务自身没有过载,latency也会发生波动,根据Little's Law,latency的波动会导致server的concurrency发生波动。
|
||||
|
||||
我们在设计自适应限流的计算公式时,考虑到了latency发生抖动的情况:
|
||||
当latency与min_latency很接近时,根据计算公式会得到一个较高max_concurrency来适应concurrency的波动,从而尽可能的减少“误杀”。同时,随着latency的升高,max_concurrency会逐渐降低,以保护服务不会过载。
|
||||
|
||||
从另一个角度来说,当latency也开始升高时,通常意味着某处(不一定是服务本身,也有可能是下游服务)消耗了大量CPU资源,这个时候缩小max_concurrency也是合理的。
|
||||
|
||||
#### 平滑处理
|
||||
为了减少个别窗口的抖动对限流算法的影响,同时尽量降低计算开销,计算min_latency时会通过使用[EMA](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average)来进行平滑处理:
|
||||
|
||||
```
|
||||
if latency < min_latency:
|
||||
min_latency = latency * ema_alpha + (1 - ema_alpha) * min_latency
|
||||
else:
|
||||
do_nothing
|
||||
```
|
||||
|
||||
### 估算peak_qps
|
||||
|
||||
#### 提高qps增长的速度
|
||||
当服务启动时,由于服务本身需要进行一系列的初始化,tcp本身也有慢启动等一系列原因。服务在刚启动时的qps一定会很低。这就导致了服务启动时的max_concurrency也很低。而按照上面的计算公式,当max_concurrency很低的时候,预留给qps增长的冗余concurrency也很低(即:alpha * max_qps * min_latency)。从而会影响当流量增加时,服务max_concurrency的增加速度。
|
||||
|
||||
假如从启动到打满qps的时间过长,这期间会损失大量流量。在这里我们采取的措施有两个,
|
||||
|
||||
1. 采样方面,一旦采到的请求数量足够多,直接提交当前采样窗口,而不是等待采样窗口的到时间了才提交
|
||||
2. 计算公式方面,当current_qps > 保存的max_qps时,直接进行更新,不进行平滑处理。
|
||||
|
||||
在进行了这两个处理之后,绝大部分情况下都能够在2秒左右将qps打满。
|
||||
|
||||
#### 平滑处理
|
||||
为了减少个别窗口的抖动对限流算法的影响,同时尽量降低计算开销,在计算max_qps时,会通过使用[EMA](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average)来进行平滑处理:
|
||||
|
||||
```
|
||||
if current_qps > max_qps:
|
||||
max_qps = current_qps
|
||||
else:
|
||||
max_qps = current_qps * ema_alpha / 10 + (1 - ema_alpha / 10) * max_qps
|
||||
```
|
||||
将max_qps的ema参数置为min_latency的ema参数的十分之一的原因是: max_qps 下降了通常并不意味着极限qps也下降了。而min_latency下降了,通常意味着noload_latency确实下降了。
|
||||
|
||||
### 与netflix gradient算法的对比
|
||||
|
||||
netflix中的gradient算法公式为:max_concurrency = min_latency / latency * max_concurrency + queue_size。
|
||||
|
||||
其中latency是采样窗口的最小latency,min_latency是最近多个采样窗口的最小latency。min_latency / latency就是算法中的"梯度",当latency大于min_latency时,max_concurrency会逐渐减少;反之,max_concurrency会逐渐上升,从而让max_concurrency围绕在best_max_concurrency附近。
|
||||
|
||||
这个公式可以和本文的算法进行类比:
|
||||
|
||||
* gradient算法中的latency和本算法的不同,前者的latency是最小值,后者是平均值。netflix的原意是最小值能更好地代表noload_latency,但实际上只要不对max_concurrency做定期衰减,不管最小值还是平均值都有可能不断上升使算法不收敛。最小值并不能带来额外的好处,反而会使算法更不稳定。
|
||||
* gradient算法中的max_concurrency / latency从概念上和qps有关联(根据little's law),但可能严重脱节。比如在重测
|
||||
min_latency前,若所有latency都小于min_latency,那么max_concurrency会不断下降甚至到0;但按照本算法,max_qps和min_latency仍然是稳定的,它们计算出的max_concurrency也不会剧烈变动。究其本质,gradient算法在迭代max_concurrency时,latency并不能代表实际并发为max_concurrency时的延时,两者是脱节的,所以max_concurrency / latency的实际物理含义不明,与qps可能差异甚大,最后导致了很大的偏差。
|
||||
* gradient算法的queue_size推荐为sqrt(max_concurrency),这是不合理的。netflix对queue_size的理解大概是代表各种不可控环节的缓存,比如socket里的,和max_concurrency存在一定的正向关系情有可原。但在我们的理解中,这部分queue_size作用微乎其微,没有或用常量即可。我们关注的queue_size是给concurrency上升留出的探索空间: max_concurrency的更新是有延迟的,在并发从低到高的增长过程中,queue_size的作用就是在max_concurrency更新前不限制qps上升。而当concurrency高时,服务可能已经过载了,queue_size就应该小一点,防止进一步恶化延时。这里的queue_size和并发是反向关系。
|
||||
|
||||
## 错误率惩罚阈值
|
||||
|
||||
`auto_cl_error_rate_punish_threshold`用于设置错误率"死区",低于该阈值的错误率不会产生惩罚,避免少量错误请求对max_concurrency的过度影响。
|
||||
|
||||
| GFlag | 默认值 | 有效范围 | 说明 |
|
||||
|-------|--------|----------|------|
|
||||
| auto_cl_error_rate_punish_threshold | 0 | [0, 1) | 错误率惩罚阈值,0表示禁用 |
|
||||
|
||||
- **默认值为0**:禁用该功能,保持原有行为
|
||||
- **设置为有效值(如0.1)**:错误率 ≤ 阈值时惩罚为0;错误率 > 阈值时惩罚线性增长
|
||||
- **无效值处理**:≥1 的值会被忽略,等同于0
|
||||
|
||||
**示例**:
|
||||
```
|
||||
# 错误率低于10%时不惩罚,高于10%时线性增加惩罚
|
||||
--auto_cl_error_rate_punish_threshold=0.1
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
“雪崩”指的是访问服务集群时绝大部分请求都超时,且在流量减少时仍无法恢复的现象。下面解释这个现象的来源。
|
||||
|
||||
当流量超出服务的最大qps时,服务将无法正常服务;当流量恢复正常时(小于服务的处理能力),积压的请求会被处理,虽然其中很大一部分可能会因为处理的不及时而超时,但服务本身一般还是会恢复正常的。这就相当于一个水池有一个入水口和一个出水口,如果入水量大于出水量,水池子终将盛满,多出的水会溢出来。但如果入水量降到出水量之下,一段时间后水池总会排空。雪崩并不是单一服务能产生的。
|
||||
|
||||
如果一个请求经过两个服务,情况就有所不同了。比如请求访问A服务,A服务又访问了B服务。当B被打满时,A处的client会大量超时,如果A处的client在等待B返回时也阻塞了A的服务线程(常见),且使用了固定个数的线程池(常见),那么A处的最大qps就从**线程数 / 平均延时**,降到了**线程数 / 超时**。由于超时往往是平均延时的3至4倍,A处的最大qps会相应地下降3至4倍,从而产生比B处更激烈的拥塞。如果A还有类似的上游,拥塞会继续传递上去。但这个过程还是可恢复的。B处的流量终究由最前端的流量触发,只要最前端的流量回归正常,B处的流量总会慢慢降下来直到能正常回复大多数请求,从而让A恢复正常。
|
||||
|
||||
但有两个例外:
|
||||
|
||||
1. A可能对B发起了过于频繁的基于超时的重试。这不仅会让A的最大qps降到**线程数 / 超时**,还会让B处的qps翻**重试次数**倍。这就可能陷入恶性循环了:只要**线程数 / 超时 \* 重试次数**大于B的最大qps,B就无法恢复 -> A处的client会继续超时 -> A继续重试 -> B继续无法恢复。
|
||||
2. A或B没有限制某个缓冲或队列的长度,或限制过于宽松。拥塞请求会大量地积压在那里,要恢复就得全部处理完,时间可能长得无法接受。由于有限长的缓冲或队列需要在填满时解决等待、唤醒等问题,有时为了简单,代码可能会假定缓冲或队列不会满,这就埋下了种子。即使队列是有限长的,恢复时间也可能很长,因为清空队列的过程是个追赶问题,排空的时间取决于**积压的请求数 / (最大qps - 当前qps)**,如果当前qps和最大qps差的不多,积压的请求又比较多,那排空时间就遥遥无期了。
|
||||
|
||||
了解这些因素后可以更好的理解brpc中相关的设计。
|
||||
|
||||
1. 拥塞时A服务最大qps的跳变是因为线程个数是**硬限**,单个请求的处理时间很大程度上决定了最大qps。而brpc server端默认在bthread中处理请求,个数是软限,单个请求超时只是阻塞所在的bthread,并不会影响为新请求建立新的bthread。brpc也提供了完整的异步接口,让用户可以进一步提高io-bound服务的并发度,降低服务被打满的可能性。
|
||||
2. brpc中[重试](client.md#重试)默认只在连接出错时发起,避免了流量放大,这是比较有效率的重试方式。如果需要基于超时重试,可以设置[backup request](client.md#重试),这类重试最多只有一次,放大程度降到了最低。brpc中的RPC超时是deadline,超过后RPC一定会结束,这让用户对服务的行为有更好的预判。在之前的一些实现中,RPC超时是单次超时*重试次数,在实践中容易误判。
|
||||
3. brpc server端的[max_concurrency选项](server.md#限制最大并发)控制了server的最大并发:当同时处理的请求数超过max_concurrency时,server会回复client错误,而不是继续积压。这一方面在服务开始的源头控制住了积压的请求数,尽量避免延生到用户缓冲或队列中,另一方面也让client尽快地去重试其他server,对集群来说是个更好的策略。
|
||||
|
||||
对于brpc的用户来说,要防止雪崩,主要注意两点:
|
||||
|
||||
1. 评估server的最大并发,设置合理的max_concurrency值。这个默认是不设的,也就是不限制。无论程序是同步还是异步,用户都可以通过 **最大qps \* 非拥塞时的延时**(秒)来评估最大并发,原理见[little's law](https://en.wikipedia.org/wiki/Little%27s_law),这两个量都可以在brpc中的内置服务中看到。max_concurrency与最大并发相等或大一些就行了。
|
||||
2. 注意考察重试发生时的行为,特别是在定制RetryPolicy时。如果你只是用默认的brpc重试,一般是安全的。但用户程序也常会自己做重试,比如通过一个Channel访问失败后,去访问另外一个Channel,这种情况下要想清楚重试发生时最差情况下请求量会放大几倍,服务是否可承受。
|
||||
@@ -0,0 +1,120 @@
|
||||
有时为了保证可用性,需要同时访问两路服务,哪个先返回就取哪个。在brpc中,这有多种做法:
|
||||
|
||||
# 当后端server可以挂在一个命名服务内时
|
||||
|
||||
Channel开启backup request。这个Channel会先向其中一个server发送请求,如果在ChannelOptions.backup_request_ms后还没回来,再向另一个server发送。之后哪个先回来就取哪个。在设置了合理的backup_request_ms后,大部分时候只会发一个请求,对后端服务只有一倍压力。
|
||||
|
||||
示例代码见[example/backup_request_c++](https://github.com/apache/brpc/blob/master/example/backup_request_c++)。这个例子中,client设定了在2ms后发送backup request,server在碰到偶数位的请求后会故意睡眠20ms以触发backup request。
|
||||
|
||||
运行后,client端和server端的日志分别如下,"index"是请求的编号。可以看到server端在收到第一个请求后会故意sleep 20ms,client端之后发送另一个同样index的请求,最终的延时并没有受到故意sleep的影响。
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
/rpcz也显示client在2ms后触发了backup超时并发出了第二个请求。
|
||||
|
||||

|
||||
|
||||
## 选择合理的backup_request_ms
|
||||
|
||||
可以观察brpc默认提供的latency_cdf图,或自行添加。cdf图的y轴是延时(默认微秒),x轴是小于y轴延时的请求的比例。在下图中,选择backup_request_ms=2ms可以大约覆盖95.5%的请求,选择backup_request_ms=10ms则可以覆盖99.99%的请求。
|
||||
|
||||

|
||||
|
||||
自行添加的方法:
|
||||
|
||||
```c++
|
||||
#include <bvar/bvar.h>
|
||||
#include <butil/time.h>
|
||||
...
|
||||
bvar::LatencyRecorder my_func_latency("my_func");
|
||||
...
|
||||
butil::Timer tm;
|
||||
tm.start();
|
||||
my_func();
|
||||
tm.stop();
|
||||
my_func_latency << tm.u_elapsed(); // u代表微秒,还有s_elapsed(), m_elapsed(), n_elapsed()分别对应秒,毫秒,纳秒。
|
||||
|
||||
// 好了,在/vars中会显示my_func_qps, my_func_latency, my_func_latency_cdf等很多计数器。
|
||||
```
|
||||
|
||||
## Backup Request 限流
|
||||
|
||||
如需限制 backup request 的发送比例,可使用内置工厂函数创建限流策略,也可自行实现 `BackupRequestPolicy` 接口。
|
||||
|
||||
优先级顺序:`backup_request_policy` > `backup_request_ms`。
|
||||
|
||||
### 使用内置限流策略
|
||||
|
||||
调用 `CreateRateLimitedBackupPolicy` 创建限流策略,并将其设置到 `ChannelOptions.backup_request_policy`:
|
||||
|
||||
```c++
|
||||
#include "brpc/backup_request_policy.h"
|
||||
#include <memory>
|
||||
|
||||
brpc::RateLimitedBackupPolicyOptions opts;
|
||||
opts.backup_request_ms = 10; // 超过10ms未返回时发送backup请求
|
||||
opts.max_backup_ratio = 0.3; // backup请求比例上限30%
|
||||
opts.window_size_seconds = 10; // 滑动窗口宽度(秒)
|
||||
opts.update_interval_seconds = 5; // 缓存比例的刷新间隔(秒)
|
||||
|
||||
// CreateRateLimitedBackupPolicy返回的指针由调用方负责释放。
|
||||
// policy的生命周期必须长于channel——先销毁channel,再销毁policy。
|
||||
std::unique_ptr<brpc::BackupRequestPolicy> policy(
|
||||
brpc::CreateRateLimitedBackupPolicy(opts));
|
||||
|
||||
brpc::ChannelOptions options;
|
||||
options.backup_request_policy = policy.get(); // Channel不拥有该对象
|
||||
channel.Init(..., &options);
|
||||
// channel必须在policy析构之前销毁。
|
||||
```
|
||||
|
||||
参数说明(`RateLimitedBackupPolicyOptions`):
|
||||
|
||||
| 字段 | 默认值 | 说明 |
|
||||
|------|--------|------|
|
||||
| `backup_request_ms` | -1 | 超时阈值(毫秒)。-1 表示继承 `ChannelOptions.backup_request_ms`(仅在通过 `ChannelOptions.backup_request_policy` 设置策略时有效;通过 Controller 注入时没有 channel 级的回退值,应显式指定 >= 0 的值)。必须 >= -1。 |
|
||||
| `max_backup_ratio` | 0.1 | backup比例上限,取值范围 (0, 1] |
|
||||
| `window_size_seconds` | 10 | 滑动窗口宽度(秒),取值范围 [1, 3600] |
|
||||
| `update_interval_seconds` | 5 | 缓存刷新间隔(秒),必须 >= 1 |
|
||||
|
||||
参数不合法时 `CreateRateLimitedBackupPolicy` 返回 `NULL`。
|
||||
|
||||
### 使用自定义 BackupRequestPolicy
|
||||
|
||||
如需完全控制,可实现 `BackupRequestPolicy` 接口并设置到 `ChannelOptions.backup_request_policy`:
|
||||
|
||||
```c++
|
||||
#include "brpc/backup_request_policy.h"
|
||||
|
||||
class MyBackupPolicy : public brpc::BackupRequestPolicy {
|
||||
public:
|
||||
int32_t GetBackupRequestMs(const brpc::Controller*) const override {
|
||||
return 10; // 10ms后发送backup
|
||||
}
|
||||
bool DoBackup(const brpc::Controller*) const override {
|
||||
return should_allow_backup(); // 自定义逻辑
|
||||
}
|
||||
void OnRPCEnd(const brpc::Controller*) override {
|
||||
// 每次RPC结束时调用,可在此更新统计
|
||||
}
|
||||
};
|
||||
|
||||
MyBackupPolicy my_policy;
|
||||
brpc::ChannelOptions options;
|
||||
options.backup_request_policy = &my_policy; // Channel不拥有该对象,需保证其生命周期长于Channel
|
||||
channel.Init(..., &options);
|
||||
```
|
||||
|
||||
### 实现说明
|
||||
|
||||
- 比例通过bvar计数器在滑动时间窗口内统计。缓存值通过无锁CAS选举最多每 `update_interval_seconds` 刷新一次,因此每次RPC的开销极低(公共路径仅有两次原子读)。
|
||||
- Backup决策在做出时立即计数(RPC完成前),以便在延迟抖动期间更快地反馈。总RPC数在完成时统计。这意味着比例在抖动期间可能短暂滞后,这是设计有意为之——限流器的目标是近似的尽力而为的节流,而非精确执行。
|
||||
- 每个使用限流的Channel会维护两个 `bvar::Window` 采样任务,在Channel数量极多的部署中请留意此开销。
|
||||
|
||||
# 当后端server不能挂在一个命名服务内时
|
||||
|
||||
【推荐】建立一个开启backup request的SelectiveChannel,其中包含两个sub channel。访问这个SelectiveChannel和上面的情况类似,会先访问一个sub channel,如果在ChannelOptions.backup_request_ms后没返回,再访问另一个sub channel。如果一个sub channel对应一个集群,这个方法就是在两个集群间做互备。SelectiveChannel的例子见[example/selective_echo_c++](https://github.com/apache/brpc/tree/master/example/selective_echo_c++),具体做法请参考上面的过程。
|
||||
|
||||
【不推荐】发起两个异步RPC后Join它们,它们的done内是相互取消的逻辑。示例代码见[example/cancel_c++](https://github.com/apache/brpc/tree/master/example/cancel_c++)。这种方法的问题是总会发两个请求,对后端服务有两倍压力,这个方法怎么算都是不经济的,你应该尽量避免用这个方法。
|
||||
@@ -0,0 +1,159 @@
|
||||
# 介绍
|
||||
|
||||
baidu_std是一种基于TCP协议的二进制RPC通信协议。它以Protobuf作为基本的数据交换格式,并基于Protobuf内置的RPC Service形式,规定了通信双方之间的数据交换协议,以实现完整的RPC调用。
|
||||
|
||||
baidu_std不考虑跨TCP连接的情况。
|
||||
|
||||
# 基本协议
|
||||
|
||||
## 服务
|
||||
|
||||
所有RPC服务都在某个IP地址上通过某个端口发布。
|
||||
|
||||
一个端口可以同时发布多个服务。服务以名字标识。服务名必须是UpperCamelCase,由大小写字母和数字组成,长度不超过64个字符。
|
||||
|
||||
一个服务可以包含一个或多个方法。每个方法以名字标识,由大小写字母、数字和下划线组成,长度不超过64个字符。考虑到不同语言风格差异较大,这里对方法命名格式不做强制规定。
|
||||
|
||||
四元组唯一地标识了一个RPC方法。
|
||||
|
||||
调用方法所需参数应放在一个Protobuf消息内。如果方法有返回结果,也同样应放在一个Protobuf消息内。具体定义由通信双方自行约定。特别地,可以使用空的Protobuf消息来表示请求/响应为空的情况。
|
||||
|
||||
## 包
|
||||
|
||||
包是baidu_std的基本数据交换单位。每个包由包头和包体组成,其中包体又分为元数据、数据、附件三部分。具体的参数和返回结果放在数据部分。
|
||||
|
||||
包分为请求包和响应包两种。它们的包头格式一致,但元数据部分的定义不同。
|
||||
|
||||
## 包头
|
||||
|
||||
包头长度固定为12字节。前四字节为协议标识PRPC,中间四字节是一个32位整数,表示包体长度(不包括包头的12字节),最后四字节是一个32位整数,表示包体中的元数据包长度。整数均采用网络字节序表示。
|
||||
|
||||
## 元数据
|
||||
|
||||
元数据用于描述请求/响应。
|
||||
|
||||
```
|
||||
message RpcMeta {
|
||||
optional RpcRequestMeta request = 1;
|
||||
optional RpcResponseMeta response = 2;
|
||||
optional int32 compress_type = 3;
|
||||
optional int64 correlation_id = 4;
|
||||
optional int32 attachment_size = 5;
|
||||
optional ChunkInfo chuck_info = 6;
|
||||
optional bytes authentication_data = 7;
|
||||
};
|
||||
```
|
||||
|
||||
请求包中只有request,响应包中只有response。实现可以根据域的存在性来区分请求包和响应包。
|
||||
|
||||
| 参数 | 说明 |
|
||||
| ------------------- | ---------------------------------------- |
|
||||
| request | 请求包元数据 |
|
||||
| response | 响应包元数据 |
|
||||
| compress_type | 详见附录[压缩算法](#compress_algorithm) |
|
||||
| correlation_id | 请求包中的该域由请求方设置,用于唯一标识一个RPC请求。请求方有义务保证其唯一性,协议本身对此不做任何检查。响应方需要在对应的响应包里面将correlation_id设为同样的值。 |
|
||||
| attachment_size | 附件大小,详见[附件](#attachment) |
|
||||
| chuck_info | 详见[Chunk模式](#chunk-mode) |
|
||||
| authentication_data | 用于存放身份认证相关信息 |
|
||||
|
||||
### 请求包元数据
|
||||
|
||||
请求包的元数据主要描述了需要调用的RPC方法信息,Protobuf如下
|
||||
|
||||
```
|
||||
message RpcRequestMeta {
|
||||
required string service_name = 1;
|
||||
required string method_name = 2;
|
||||
optional int64 log_id = 3;
|
||||
};
|
||||
```
|
||||
|
||||
| 参数 | 说明 |
|
||||
| ------------ | ---------------------------- |
|
||||
| service_name | 服务名,约束见上文 |
|
||||
| method_name | 方法名,约束见上文 |
|
||||
| log_id | 用于打印日志。可用于存放BFE_LOGID。该参数可选。 |
|
||||
|
||||
### 响应包元数据
|
||||
|
||||
响应包的元数据是对返回结果的描述。如果出现任何异常,错误也会放在元数据中。其Protobuf描述如下
|
||||
|
||||
```
|
||||
message RpcResponseMeta {
|
||||
optional int32 error_code = 1;
|
||||
optional string error_text = 2;
|
||||
};
|
||||
```
|
||||
|
||||
| 参数 | 说明 |
|
||||
| ---------- | ------------------------------------ |
|
||||
| error_code | 发生错误时的错误号,0表示正常,非0表示错误。具体含义由应用方自行定义。 |
|
||||
| error_text | 错误的文本描述 |
|
||||
|
||||
### 对元数据的扩展
|
||||
|
||||
某些实现需要在元数据中增加自己专有的字段。为了避免冲突,并保证不同实现之间相互调用的兼容性,所有实现都需要向接口规范委员会申请一个专用的序号用于存放自己的扩展字段。
|
||||
|
||||
以Hulu为例,被分配的序号为100。因此Hulu可以使用这样的proto定义:
|
||||
|
||||
```
|
||||
message RpcMeta {
|
||||
optional RpcRequestMeta request = 1;
|
||||
optional RpcResponseMeta response = 2;
|
||||
optional int32 compress_type = 3;
|
||||
optional int64 correlation_id = 4;
|
||||
optional int32 attachment_size = 5;
|
||||
optional ChunkInfo chuck_info = 6;
|
||||
optional HuluMeta hulu_meta = 100;
|
||||
};
|
||||
message RpcRequestMeta {
|
||||
required string service_name = 1;
|
||||
required string method_name = 2;
|
||||
optional int64 log_id = 3;
|
||||
optional HuluRequestMeta hulu_request_meta = 100;
|
||||
};
|
||||
message RpcResponseMeta {
|
||||
optional int32 error_code = 1;
|
||||
optional string error_text = 2;
|
||||
optional HuluResponseMeta hulu_response_meta = 100;
|
||||
};
|
||||
```
|
||||
|
||||
因为只是将100这个序号保留给Hulu使用,因此Hulu可以自由决定是否添加这些字段,以及使用什么样的名字。其余实现使用的proto中不存在这些定义,会直接作为Unknown字段忽略。
|
||||
|
||||
当前分配的序号如下
|
||||
|
||||
| 序号 | 实现 |
|
||||
| ---- | ---- |
|
||||
| 100 | Hulu |
|
||||
| 101 | Sofa |
|
||||
|
||||
## 数据
|
||||
|
||||
自定义的Protobuf Message。用于存放参数或返回结果。
|
||||
|
||||
## Attachment
|
||||
|
||||
某些场景下需要通过RPC来传递二进制数据,例如文件上传下载,多媒体转码等等。将这些二进制数据打包在Protobuf内会增加不必要的内存拷贝。因此协议允许使用附件的方式直接传送二进制数据。
|
||||
|
||||
附件总是放在包体的最后,紧跟数据部分。消息包需要携带附件时,应将RpcMeta中的attachment_size设为附件的实际字节数。
|
||||
|
||||
## 压缩算法
|
||||
|
||||
可以使用指定的压缩算法来压缩消息包中的数据部分。
|
||||
|
||||
| 值 | 含义 |
|
||||
| ---- | -------- |
|
||||
| 0 | 不压缩 |
|
||||
| 1 | 使用Snappy |
|
||||
| 2 | 使用gzip |
|
||||
|
||||
# HTTP接口
|
||||
|
||||
服务应以标准的HTTP协议对外发布接口。
|
||||
|
||||
数据交换格式默认应使用JSON。Content-Type使用application/json。有特殊需求的接口不受此限制。例如上传文件可以使用multipart/form-data;下载文件可以根据实际内容选用合适的Content-Type。
|
||||
|
||||
URL和JSON中的字符编码一律使用UTF-8。
|
||||
|
||||
建议使用RESTful形式的Web Service接口。由于RESTful并非一个严格的规范,本规范对此不做强制规定。
|
||||
@@ -0,0 +1,20 @@
|
||||
## bRPC 作为Bazel第三方依赖
|
||||
1. bRPC 依赖于一些开源库, 但这些库并没有提供bazel支持, 所以需要你手动将一部分依赖加入到你的构建项目中.
|
||||
2. 将 /example/build_with_bazel/*.BUILD 和 brpc_workspace.bzl 该文件移动到你的项目根目录下, 将
|
||||
```c++
|
||||
load("@//:brpc_workspace.bzl", "brpc_workspace")
|
||||
brpc_workspace();
|
||||
```
|
||||
内容添加到你的WORKSPACE中.
|
||||
|
||||
3. 链接请使用
|
||||
```c++
|
||||
...
|
||||
deps = [
|
||||
"@apache_brpc//:bthread",
|
||||
"@apache_brpc//:brpc",
|
||||
"@apache_brpc//:butil",
|
||||
"@apache_brpc//:bvar",
|
||||
]
|
||||
...
|
||||
```
|
||||
@@ -0,0 +1,221 @@
|
||||
NOTE: following tests were done in 2015, which may not reflect latest status of the package.
|
||||
|
||||
# 序言
|
||||
|
||||
在多核的前提下,性能和线程是紧密联系在一起的。线程间的跳转对高频IO操作的性能有决定性作用: 一次跳转意味着至少3-20微秒的延时,由于每个核心的L1 cache独立(我们的cpu L2 cache也是独立的),随之而来是大量的cache miss,一些变量的读取、写入延时会从纳秒级上升几百倍至微秒级: 等待cpu把对应的cacheline同步过来。有时这带来了一个出乎意料的结果,当每次的处理都很简短时,一个多线程程序未必比一个单线程程序更快。因为前者可能在每次付出了大的切换代价后只做了一点点“正事”,而后者在不停地做“正事”。不过单线程也是有代价的,它工作良好的前提是“正事”都很快,否则一旦某次变慢就使后续的所有“正事”都被延迟了。在一些处理时间普遍较短的程序中,使用(多个不相交的)单线程能最大程度地”做正事“,由于每个请求的处理时间确定,延时表现也很稳定,各种http server正是这样。但我们的检索服务要做的事情可就复杂多了,有大量的后端服务需要访问,广泛存在的长尾请求使每次处理的时间无法确定,排序策略也越来越复杂。如果还是使用(多个不相交的)单线程的话,一次难以预计的性能抖动,或是一个大请求可能导致后续一堆请求被延迟。
|
||||
|
||||
为了避免请求之间相互影响,请求级的线程跳转是brpc必须付出的代价,我们能做的是使[线程跳转最优化](io.md#the-full-picture)。不过,对服务的性能测试还不能很好地体现这点。测试中的处理往往极为简单,使得线程切换的影响空前巨大,通过控制多线程和单线程处理的比例,我们可以把一个测试服务的qps从100万到500万操纵自如(同机),这损伤了性能测试结果的可信度。要知道,真实的服务并不是在累加一个数字,或者echo一个字符串,一个qps几百万的echo程序没有指导意义。鉴于此,在发起性能测试一年后(15年底),在brpc又经历了1200多次改动后,我们需要review所有的测试,加强其中的线程因素,以获得对真实场景有明确意义的结果。具体来说:
|
||||
|
||||
- 请求不应等长,要有长尾。这能考察RPC能否让请求并发,否则一个慢请求会影响大量后续请求。
|
||||
- 要有多级server的场景。server内用client访问下游server,这能考察server和client的综合表现。
|
||||
- 要有一个client访问多个server的场景。这能考察负载均衡是否足够并发,真实场景中很少一个client只访问一个server。
|
||||
|
||||
我们希望这套测试场景对其他服务的性能测试有借鉴意义。
|
||||
|
||||
# 测试目标
|
||||
|
||||
## UB
|
||||
|
||||
百度在08年开发的RPC框架,在百度产品线广泛使用,已被brpc代替。UB的每个请求独占一个连接(连接池),在大规模服务中每台机器都需要保持大量的连接,限制了其使用场景,像百度的分布式系统没有用UB。UB只支持nshead+mcpack协议,也没怎么考虑扩展性,所以增加新协议和新功能往往要调整大段代码,在实践中大部分人“知难而退”了。UB缺乏调试和运维接口,服务的运行状态对用户基本是黑盒,只能靠低效地打日志来追踪问题,服务出现问题时常要拉上维护者一起排查,效率很低。UB有多个变种:
|
||||
|
||||
* ubrpc: 百度在10年基于UB开发的RPC框架,用.idl文件(类似.proto)描述数据的schema,而不是手动打包。这个RPC有被使用,但不广泛。
|
||||
|
||||
- nova_pbrpc: 百度网盟团队在12年基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,协议是nshead + user's protobuf。
|
||||
- public_pbrpc: 百度在13年初基于UB开发的RPC框架,用protobuf代替mcpack作为序列化方法,但协议与nova_pbrpc不同,大致是nshead + meta protobuf。meta protobuf中有个string字段包含user's protobuf。由于用户数据要序列化两次,这个RPC的性能很差,没有被推广开来。
|
||||
|
||||
我们以在百度网盟团队广泛使用的nova_pbrpc为UB的代表。测试时其代码为r10500。早期的UB支持CPOOL和XPOOL,分别使用[select](http://linux.die.net/man/2/select)和[leader-follower模型](http://kircher-schwanninger.de/michael/publications/lf.pdf),后来提供了EPOLL,使用[epoll](http://man7.org/linux/man-pages/man7/epoll.7.html)处理多路连接。鉴于产品线大都是用EPOLL模型,我们的UB配置也使用EPOLL。UB只支持[连接池](client.md#连接方式),结果用“**ubrpc_mc**"指代(mc代表"multiple
|
||||
connection")。虽然这个名称不太准确(见上文对ubrpc的介绍),但在本文的语境下,请默认ubrpc = UB。
|
||||
|
||||
## hulu-pbrpc
|
||||
|
||||
百度在13年基于saber(kylin变种)和protobuf实现的RPC框架,hulu在多线程实现上有较多问题,已被brpc代替,测试时其代码为`pbrpc_2-0-15-27959_PD_BL`。hulu-pbrpc只支持单连接,结果用“**hulu-pbrpc**"指代。
|
||||
|
||||
## brpc
|
||||
|
||||
INF在2014年底开发至今的rpc产品,支持百度内所有协议(不限于protobuf),并第一次统一了百度主要分布式系统和业务线的RPC框架。测试时代码为r31906。brpc既支持单连接也支持连接池,前者的结果用"**baidu-rpc**"指代,后者用“**baidu-rpc_mc**"指代。
|
||||
|
||||
## sofa-pbrpc
|
||||
|
||||
百度大搜团队在13年基于boost::asio和protobuf实现的RPC框架,有多个版本,咨询相关同学后,确认ps/opensource下的和github上的较新,且会定期同步。故测试使用使用ps/opensource下的版本。测试时其代码为`sofa-pbrpc_1-0-2_BRANCH`。sofa-pbrpc只支持单连接,结果用“**sofa-pbrpc**”指代。
|
||||
|
||||
## apache thrift
|
||||
|
||||
thrift是由facebook最早在07年开发的序列化方法和rpc框架,包含独特的序列化格式和IDL,支持很多编程语言。开源后改名[apache thrift](https://thrift.apache.org/),fb自己有一个[fbthrift分支](https://github.com/facebook/fbthrift),我们使用的是apache thrift。测试时其代码为`thrift_0-9-1-400_PD_BL`。thrift的缺点是: 代码看似分层清晰,client和server选择很多,但没有一个足够通用,每个server实现都只能解决很小一块场景,每个client都线程不安全,实际使用很麻烦。由于thrift没有线程安全的client,所以每个线程中都得建立一个client,使用独立的连接。在测试中thrift其实是占了其他实现的便宜: 它的client不需要处理多线程问题。thrift的结果用"**thrift_mc**"指代。
|
||||
|
||||
## gRPC
|
||||
|
||||
由google开发的rpc框架,使用http/2和protobuf 3.0,测试时其代码为<https://github.com/grpc/grpc/tree/release-0_11>。gRPC并不是stubby,定位更像是为了推广http/2和protobuf 3.0,但鉴于很多人对它的表现很感兴趣,我们也(很麻烦地)把它加了进来。gRPC的结果用"**grpc**"指代。
|
||||
|
||||
# 测试方法
|
||||
|
||||
如序言中解释的那样,性能数字有巨大的调整空间。这里的关键在于,我们对RPC的底线要求是什么,脱离了这个底线,测试中的表现就严重偏离真实环境中的了。
|
||||
|
||||
这个底线我们认为是**RPC必须能处理长尾**。
|
||||
|
||||
在百度的环境中,这是句大白话,哪个产品线,哪个系统没有长尾呢?作为承载大部分服务的RPC框架自然得处理好长尾,减少长尾对正常请求的影响。但在实现层面,这个问题对设计的影响太大了。如果测试中没有长尾,那么RPC实现就可以假设每个请求都差不多快,这时候最优的方法是用多个线程独立地处理请求。由于没有上下文切换和cache一致性同步,程序的性能会显著高于多个线程协作时的表现。
|
||||
|
||||
比如简单的echo程序,处理一个请求只需要200-300纳秒,单个线程可以达到300-500万的吞吐。但如果多个线程协作,即使在极其流畅的系统中,也要付出3-5微秒的上下文切换代价和1微秒的cache同步代价,这还没有考虑多个线程间的其他互斥逻辑,一般来说单个线程的吞吐很难超过10万,即使24核全部用满,吞吐也只有240万,不及一个线程。这正是以http server为典型的服务选用[单线程模型](threading_overview.md#单线程reactor)的原因(多个线程独立运行eventloop): 大部分http请求的处理时间是可预测的,对下游的访问也不会有任何阻塞代码。这个模型可以最大化cpu利用率,同时提供可接受的延时。
|
||||
|
||||
多线程付出这么大的代价是为了**隔离请求间的影响**。一个计算复杂或索性阻塞的过程不会影响到其他请求,1%的长尾最终只会影响到1%的性能。而多个独立的线程是保证不了这点的,一个请求进入了一个线程就等于“定了终生”,如果前面的请求慢了一下,那也只能跟着慢了。1%的长尾会影响远超1%的请求,最终表现不佳。换句话说,乍看上去多线程模型“慢”了,但在真实应用中反而会获得更好的综合性能。
|
||||
|
||||
延时能精确地体现出长尾的干扰作用,如果普通请求的延时没有被长尾请求干扰,就说明RPC成功地隔离了请求。而QPS无法体现这点,只要CPU都在忙,即使一个正常请求进入了挤满长尾的队列而被严重延迟,最终的QPS也变化不大。为了测量长尾的干扰作用,我们在涉及到延时的测试中都增加了1%的长尾请求。
|
||||
|
||||
# 开始测试
|
||||
|
||||
## 环境
|
||||
|
||||
性能测试使用的机器配置为:
|
||||
|
||||
- 单机1: CPU开超线程24核,E5-2620 @ 2.00GHz;64GB内存;OS linux 2.6.32_1-15-0-0
|
||||
- 多机1(15台+8台): CPU均未开超线程12核,其中15台的CPU为E5-2420 @ 1.90GHz.,64GB内存,千兆网卡,无法开启多队列。其余8台为E5-2620 2.0GHz,千兆网卡,绑定多队列到前8个核。这些长期测试机器比较杂,跨了多个机房,测试中延时在1ms以上的就是这批机器。
|
||||
- 多机2(30台): CPU未开超线程12核,E5-2620 v3 @ 2.40GHz.;96GB内存;OS linux 2.6.32_1-17-0-0;万兆网卡,绑定多队列到前8个核。这是临时借用的新机器,配置非常好,都在广州机房,延时非常短,测试中延时在几百微秒的就是这批机器。
|
||||
|
||||
下面所有的曲线图是使用brpc开发的dashboard程序绘制的,去掉路径后可以看到和所有brpc
|
||||
server一样的[内置服务](builtin_service.md)。
|
||||
|
||||
## 配置
|
||||
|
||||
如无特殊说明,所有测试中的配置只是数量差异(线程数,请求大小,client个数etc),而不是模型差异。我们确保用户看到的qps和延时是同一个场景的不同维度,而不是无法统一的两个场景。
|
||||
|
||||
所有RPC server都配置了24个工作线程,这些线程一般运行用户的处理逻辑。关于每种RPC的特殊说明:
|
||||
|
||||
- UB: 配置了12个reactor线程,使用EPOOL模型。连接池限制数配置为线程个数(24)
|
||||
- hulu-pbrpc: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。hulu有个“共享队列“的配置项,默认不打开,作用是把fd静态散列到多个线程中,由于线程间不再争抢,hulu的qps会显著提高,但会明显地被长尾影响(原因见[测试方法](#测试方法))。考虑到大部分使用者并不会去改配置,我们也选择不打开。
|
||||
- thrift: 额外配置了12个IO线程。这些线程会处理fd读取,请求解析等任务。thrift的client不支持多线程,每个线程得使用独立的client,连接也都是分开的。
|
||||
- sofa-pbrpc: 按照sofa同学的要求,把io_service_pool_size配置为24,work_thread_num配置为1。大概含义是使用独立的24组线程池,每组1个worker thread。和hulu不打开“共享队列”时类似,这个配置会显著提高sofa-pbrpc的QPS,但同时使它失去了处理长尾的能力。如果你在真实产品中使用,我们不建议这个配置。(而应该用io_service_pool_size=1, work_thread_num=24)
|
||||
- brpc: 尽管brpc的client运行在bthread中时会获得10%~20%的QPS提升和更低的延时,但测试中的client都运行统一的pthread中。
|
||||
|
||||
所有的RPC client都以多个线程同步方式发送,这种方法最接近于真实系统中的情况,在考察QPS时也兼顾了延时因素。
|
||||
|
||||
一种流行的方案是client不停地往连接中写入数据看server表现,这个方法的弊端在于: server一下子能读出大量请求,不同RPC的比拼变成了“for循环执行用户代码”的比拼,而不是分发请求的效率。在真实系统中server很少能同时读到超过4个请求。这个方法也完全放弃了延时,client其实是让server陷入了雪崩时才会进入的状态,所有请求都因大量排队而超时了。
|
||||
|
||||
## 同机单client→单server在不同请求下的QPS(越高越好)
|
||||
|
||||
本测试运行在[单机1](#环境)上。图中的数值均为用户数据的字节数,实际的请求尺寸还要包括协议头,一般会增加40字节左右。
|
||||
|
||||
(X轴是用户数据的字节数,Y轴是对应的QPS)
|
||||
|
||||

|
||||
|
||||
以_mc结尾的曲线代表client和server保持多个连接(线程数个),在本测试中会有更好的表现。
|
||||
|
||||
**分析**
|
||||
|
||||
* brpc: 当请求包小于16KB时,单连接下的吞吐超过了多连接的ubrpc_mc和thrift_mc,随着请求包变大,内核对单个连接的写入速度成为瓶颈。而多连接下的brpc则达到了测试中最高的2.3GB/s。注意: 虽然使用连接池的brpc在发送大包时吞吐更高,但也会耗费更多的CPU(UB和thrift也是这样)。下图中的单连接brpc已经可以提供800多兆的吞吐,足以打满万兆网卡,而使用的CPU可能只有多链接下的1/2(写出过程是[wait-free的](io.md#发消息)),真实系统中请优先使用单链接。
|
||||
* thrift: 初期明显低于brpc,随着包变大超过了单连接的brpc。
|
||||
* UB:和thrift类似的曲线,但平均要低4-5万QPS,在32K包时超过了单连接的brpc。整个过程中QPS几乎没变过。
|
||||
* gRPC: 初期几乎与UB平行,但低1万左右,超过8K开始下降。
|
||||
* hulu-pbrpc和sofa-pbrpc: 512字节前高于UB和gRPC,但之后就急转直下,相继垫底。这个趋势是写不够并发的迹象。
|
||||
|
||||
## 同机单client→单server在不同线程数下的QPS(越高越好)
|
||||
|
||||
本测试运行在[单机1](#环境)上。
|
||||
|
||||
(X轴是线程数,Y轴是对应的QPS)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
|
||||
brpc: 随着发送线程增加,QPS在快速增加,有很好的多线程扩展性。
|
||||
|
||||
UB和thrift: 8个线程下高于brpc,但超过8个线程后被brpc迅速超过,thrift继续“平移”,UB出现了明显下降。
|
||||
|
||||
gRPC,hulu-pbrpc,sofa-pbrpc: 几乎重合,256个线程时相比1个线程时只有1倍的提升,多线程扩展性不佳。
|
||||
|
||||
## 同机单client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
|
||||
本测试运行在[单机1](#环境)上。考虑到不同RPC的处理能力,我们选择了一个较低、在不少系统中会达到的的QPS: 1万。
|
||||
|
||||
本测试中有1%的长尾请求耗时5毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
|
||||
|
||||
(X轴是延时(微秒),Y轴是小于X轴延时的请求比例)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
- brpc: 平均延时短,几乎没有被长尾影响。
|
||||
- UB和thrift: 平均延时比brpc高1毫秒,受长尾影响不大。
|
||||
- hulu-pbrpc: 走向和UB和thrift类似,但平均延时进一步增加了1毫秒。
|
||||
- gRPC : 初期不错,到长尾区域后表现糟糕,直接有一部分请求超时了。(反复测试都是这样,像是有bug)
|
||||
- sofa-pbrpc: 30%的普通请求(上图未显示)被长尾严重干扰。
|
||||
|
||||
## 跨机多client→单server的QPS(越高越好)
|
||||
|
||||
本测试运行在[多机1](#环境)上。
|
||||
|
||||
(X轴是client数,Y轴是对应的QPS)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
* brpc: 随着cilent增加,server的QPS在快速增加,有不错的client扩展性。
|
||||
* sofa-pbrpc: 随着client增加,server的QPS也在快速增加,但幅度不如brpc,client扩展性也不错。从16个client到32个client时的提升较小。
|
||||
* hulu-pbrpc: 随着client增加,server的QPS在增加,但幅度进一步小于sofa-pbrpc。
|
||||
* UB: 增加client几乎不能增加server的QPS。
|
||||
* thrift: 平均QPS低于UB,增加client几乎不能增加server的QPS。
|
||||
* gRPC: 垫底、增加client几乎不能增加server的QPS。
|
||||
|
||||
## 跨机多client→单server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
|
||||
|
||||
本测试运行在[多机1](#环境)上。负载均衡算法为round-robin或RPC默认提供的。由于有32个client且一些RPC的单client能力不佳,我们为每个client仅设定了2500QPS,这是一个真实业务系统能达到的数字。
|
||||
|
||||
本测试中有1%的长尾请求耗时15毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
|
||||
|
||||
(X轴是延时(微秒),Y轴是小于X轴延时的请求比例)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
- brpc: 平均延时短,几乎没有被长尾影响。
|
||||
- UB和thrift: 平均延时短,受长尾影响小,平均延时高于brpc
|
||||
- sofa-pbrpc: 14%的普通请求被长尾严重干扰。
|
||||
- hulu-pbrpc: 15%的普通请求被长尾严重干扰。
|
||||
- gRPC: 已经完全失控,非常糟糕。
|
||||
|
||||
## 跨机多client→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
|
||||
|
||||
本测试运行在[多机2](#环境)上。20台每台运行4个client,多线程同步访问10台server。负载均衡算法为round-robin或RPC默认提供的。由于gRPC访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含gRPC。
|
||||
|
||||
本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
|
||||
|
||||
(X轴是延时(微秒),Y轴是小于X轴延时的请求比例)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
- brpc和UB: 平均延时短,几乎没有被长尾影响。
|
||||
- thrift: 平均延时显著高于brpc和UB。
|
||||
- sofa-pbrpc: 2.5%的普通请求被长尾严重干扰。
|
||||
- hulu-pbrpc: 22%的普通请求被长尾严重干扰。
|
||||
|
||||
## 跨机多client→多server→多server在固定QPS下的延时[CDF](vars.md#统计和查看分位值)(越左越好,越直越好)
|
||||
|
||||
本测试运行在[多机2](#环境)上。14台每台运行4个client,多线程同步访问8台server,这些server还会同步访问另外8台server。负载均衡算法为round-robin或RPC默认提供的。由于gRPC访问多server较麻烦且有很大概率仍表现不佳,这个测试不包含gRPC。
|
||||
|
||||
本测试中有1%的长尾请求耗时10毫秒,长尾请求的延时不计入结果,因为我们考察的是普通请求是否被及时处理了。
|
||||
|
||||
(X轴是延时(微秒),Y轴是小于X轴延时的请求比例)
|
||||
|
||||

|
||||
|
||||
**分析**
|
||||
- brpc: 平均延时短,几乎没有被长尾影响。
|
||||
- UB: 平均延时短,长尾区域略差于brpc。
|
||||
- thrift: 平均延时显著高于brpc和UB。
|
||||
- sofa-pbrpc: 17%的普通请求被长尾严重干扰,其中2%的请求延时极长。
|
||||
- hulu-pbrpc: 基本消失在视野中,已无法正常工作。
|
||||
|
||||
# 结论
|
||||
|
||||
brpc: 在吞吐,平均延时,长尾处理上都表现优秀。
|
||||
|
||||
UB: 平均延时和长尾处理的表现都不错,吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。
|
||||
|
||||
thrift: 单机的平均延时和吞吐尚可,多机的平均延时明显高于brpc和UB。吞吐的扩展性较差,提高线程数和client数几乎不能提升吞吐。
|
||||
|
||||
sofa-pbrpc: 处理小包的吞吐尚可,大包的吞吐显著低于其他RPC,延时受长尾影响很大。
|
||||
|
||||
hulu-pbrpc: 单机表现和sofa-pbrpc类似,但多机的延时表现极差。
|
||||
|
||||
gRPC: 几乎在所有参与的测试中垫底,可能它的定位是给google cloud platform的用户提供一个多语言,对网络友好的实现,性能还不是要务。
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
可代替[ab](https://httpd.apache.org/docs/2.2/programs/ab.html)测试http server极限性能。ab功能较多但年代久远,有时本身可能会成为瓶颈。benchmark_http基本上就是一个brpc http client,性能很高,功能较少,一般压测够用了。
|
||||
|
||||
使用方法:
|
||||
|
||||
首先你得[下载和编译](getting_started.md)了brpc源码,然后去example/http_c++目录编译,成功后应该能看到benchmark_http。
|
||||
Binary file not shown.
@@ -0,0 +1,66 @@
|
||||
[bthread](https://github.com/apache/brpc/tree/master/src/bthread)是brpc使用的M:N线程库,目的是在提高程序的并发度的同时,降低编码难度,并在核数日益增多的CPU上提供更好的scalability和cache locality。”M:N“是指M个bthread会映射至N个pthread,一般M远大于N。由于linux当下的pthread实现([NPTL](http://en.wikipedia.org/wiki/Native_POSIX_Thread_Library))是1:1的,M个bthread也相当于映射至N个[LWP](http://en.wikipedia.org/wiki/Light-weight_process)。bthread的前身是Distributed Process(DP)中的fiber,一个N:1的合作式线程库,等价于event-loop库,但写的是同步代码。
|
||||
|
||||
# Goals
|
||||
|
||||
- 用户可以延续同步的编程模式,能在数百纳秒内建立bthread,可以用多种原语同步。
|
||||
- bthread所有接口可在pthread中被调用并有合理的行为,使用bthread的代码可以在pthread中正常执行。
|
||||
- 能充分利用多核。
|
||||
- better cache locality, supporting NUMA is a plus.
|
||||
|
||||
# NonGoals
|
||||
|
||||
- 提供pthread的兼容接口,只需链接即可使用。**拒绝理由**: bthread没有优先级,不适用于所有的场景,链接的方式容易使用户在不知情的情况下误用bthread,造成bug。
|
||||
- 覆盖各类可能阻塞的glibc函数和系统调用,让原本阻塞系统线程的函数改为阻塞bthread。**拒绝理由**:
|
||||
- bthread阻塞可能切换系统线程,依赖系统TLS的函数的行为未定义。
|
||||
- 和阻塞pthread的函数混用时可能死锁。
|
||||
- 这类hook函数本身的效率一般更差,因为往往还需要额外的系统调用,如epoll。但这类覆盖对N:1合作式线程库(fiber)有一定意义:虽然函数本身慢了,但若不覆盖会更慢(系统线程阻塞会导致所有fiber阻塞)。
|
||||
- 修改内核让pthread支持同核快速切换。**拒绝理由**: 拥有大量pthread后,每个线程对资源的需求被稀释了,基于thread-local cache的代码效果会很差,比如tcmalloc。而独立的bthread不会有这个问题,因为它最终还是被映射到了少量的pthread。bthread相比pthread的性能提升很大一部分来自更集中的线程资源。另一个考量是可移植性,bthread更倾向于纯用户态代码。
|
||||
|
||||
# FAQ
|
||||
|
||||
##### Q:bthread是协程(coroutine)吗?
|
||||
|
||||
不是。我们常说的协程特指N:1线程库,即所有的协程运行于一个系统线程中,计算能力和各类eventloop库等价。由于不跨线程,协程之间的切换不需要系统调用,可以非常快(100ns-200ns),受cache一致性的影响也小。但代价是协程无法高效地利用多核,代码必须非阻塞,否则所有的协程都被卡住,对开发者要求苛刻。协程的这个特点使其适合写运行时间确定的IO服务器,典型如http server,在一些精心调试的场景中,可以达到非常高的吞吐。但百度内大部分在线服务的运行时间并不确定,且很多检索由几十人合作完成,一个缓慢的函数会卡住所有的协程。在这点上eventloop是类似的,一个回调卡住整个loop就卡住了,比如ub**a**server(注意那个a,不是ubserver)是百度对异步框架的尝试,由多个并行的eventloop组成,真实表现糟糕:回调里打日志慢一些,访问redis卡顿,计算重一点,等待中的其他请求就会大量超时。所以这个框架从未流行起来。
|
||||
|
||||
bthread是一个M:N线程库,一个bthread被卡住不会影响其他bthread。关键技术两点:work stealing调度和butex,前者让bthread更快地被调度到更多的核心上,后者让bthread和pthread可以相互等待和唤醒。这两点协程都不需要。更多线程的知识查看[这里](threading_overview.md)。
|
||||
|
||||
##### Q: 我应该在程序中多使用bthread吗?
|
||||
|
||||
不应该。除非你需要在一次RPC过程中[让一些代码并发运行](bthread_or_not.md),你不应该直接调用bthread函数,把这些留给brpc做更好。
|
||||
|
||||
##### Q:bthread和pthread worker如何对应?
|
||||
|
||||
pthread worker在任何时间只会运行一个bthread,当前bthread挂起时,pthread worker先尝试从本地runqueue弹出一个待运行的bthread,若没有,则随机偷另一个worker的待运行bthread,仍然没有才睡眠并会在有新的待运行bthread时被唤醒。
|
||||
|
||||
##### Q:bthread中能调用阻塞的pthread或系统函数吗?
|
||||
|
||||
可以,只阻塞当前pthread worker。其他pthread worker不受影响。
|
||||
|
||||
##### Q:一个bthread阻塞会影响其他bthread吗?
|
||||
|
||||
不影响。若bthread因bthread API而阻塞,它会把当前pthread worker让给其他bthread。若bthread因pthread API或系统函数而阻塞,当前pthread worker上待运行的bthread会被其他空闲的pthread worker偷过去运行。
|
||||
|
||||
##### Q:pthread中可以调用bthread API吗?
|
||||
|
||||
可以。bthread API在bthread中被调用时影响的是当前bthread,在pthread中被调用时影响的是当前pthread。使用bthread API的代码可以直接运行在pthread中。
|
||||
|
||||
##### Q:若有大量的bthread调用了阻塞的pthread或系统函数,会影响RPC运行么?
|
||||
|
||||
会。比如有8个pthread worker,当有8个bthread都调用了系统usleep()后,处理网络收发的RPC代码就暂时无法运行了。只要阻塞时间不太长, 这一般**没什么影响**, 毕竟worker都用完了, 除了排队也没有什么好方法.
|
||||
在brpc中用户可以选择调大worker数来缓解问题, 在server端可设置[ServerOptions.num_threads](server.md#worker线程数)或[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency), 在client端可设置[-bthread_concurrency](http://brpc.baidu.com:8765/flags/bthread_concurrency).
|
||||
|
||||
那有没有完全规避的方法呢?
|
||||
|
||||
- 一个容易想到的方法是动态增加worker数. 但实际未必如意, 当大量的worker同时被阻塞时,
|
||||
它们很可能在等待同一个资源(比如同一把锁), 增加worker可能只是增加了更多的等待者.
|
||||
- 那区分io线程和worker线程? io线程专门处理收发, worker线程调用用户逻辑, 即使worker线程全部阻塞也不会影响io线程. 但增加一层处理环节(io线程)并不能缓解拥塞, 如果worker线程全部卡住, 程序仍然会卡住,
|
||||
只是卡的地方从socket缓冲转移到了io线程和worker线程之间的消息队列. 换句话说, 在worker卡住时,
|
||||
还在运行的io线程做的可能是无用功. 事实上, 这正是上面提到的**没什么影响**真正的含义. 另一个问题是每个请求都要从io线程跳转至worker线程, 增加了一次上下文切换, 在机器繁忙时, 切换都有一定概率无法被及时调度, 会导致更多的延时长尾.
|
||||
- 一个实际的解决方法是[限制最大并发](server.md#限制最大并发), 只要同时被处理的请求数低于worker数, 自然可以规避掉"所有worker被阻塞"的情况.
|
||||
- 另一个解决方法当被阻塞的worker超过阈值时(比如8个中的6个), 就不在原地调用用户代码了, 而是扔到一个独立的线程池中运行. 这样即使用户代码全部阻塞, 也总能保留几个worker处理rpc的收发. 不过目前bthread模式并没有这个机制, 但类似的机制在[打开pthread模式](server.md#pthread模式)时已经被实现了. 那像上面提到的, 这个机制是不是在用户代码都阻塞时也在做"无用功"呢? 可能是的. 但这个机制更多是为了规避在一些极端情况下的死锁, 比如所有的用户代码都lock在一个pthread mutex上, 并且这个mutex需要在某个RPC回调中unlock, 如果所有的worker都被阻塞, 那么就没有线程来处理RPC回调了, 整个程序就死锁了. 虽然绝大部分的RPC实现都有这个潜在问题, 但实际出现频率似乎很低, 只要养成不在锁内做RPC的好习惯, 这是完全可以规避的.
|
||||
|
||||
##### Q:bthread会有[Channel](https://gobyexample.com/channels)吗?
|
||||
|
||||
不会。channel代表的是两点间的关系,而很多现实问题是多点的,这个时候使用channel最自然的解决方案就是:有一个角色负责操作某件事情或某个资源,其他线程都通过channel向这个角色发号施令。如果我们在程序中设置N个角色,让它们各司其职,那么程序就能分类有序地运转下去。所以使用channel的潜台词就是把程序划分为不同的角色。channel固然直观,但是有代价:额外的上下文切换。做成任何事情都得等到被调用处被调度,处理,回复,调用处才能继续。这个再怎么优化,再怎么尊重cache locality,也是有明显开销的。另外一个现实是:用channel的代码也不好写。由于业务一致性的限制,一些资源往往被绑定在一起,所以一个角色很可能身兼数职,但它做一件事情时便无法做另一件事情,而事情又有优先级。各种打断、跳出、继续形成的最终代码异常复杂。
|
||||
|
||||
我们需要的往往是buffered channel,扮演的是队列和有序执行的作用,bthread提供了[ExecutionQueue](execution_queue.md),可以完成这个目的。
|
||||
@@ -0,0 +1,36 @@
|
||||
bthread_id是一个特殊的同步结构,它可以互斥RPC过程中的不同环节,也可以O(1)时间内找到RPC上下文(即Controller)。注意,这里我们谈论的是bthread_id_t,不是bthread_t(bthread的tid),这个名字起的确实不太好,容易混淆。
|
||||
|
||||
具体来说,bthread_id解决的问题有:
|
||||
|
||||
- 在发送RPC过程中response回来了,处理response的代码和发送代码产生竞争。
|
||||
- 设置timer后很快触发了,超时处理代码和发送代码产生竞争。
|
||||
- 重试产生的多个response同时回来产生的竞争。
|
||||
- 通过correlation_id在O(1)时间内找到对应的RPC上下文,而无需建立从correlation_id到RPC上下文的全局哈希表。
|
||||
- 取消RPC。
|
||||
|
||||
上文提到的bug在其他rpc框架中广泛存在,下面我们来看下brpc是如何通过bthread_id解决这些问题的。
|
||||
|
||||
bthread_id包括两部分,一个是用户可见的64位id,另一个是对应的不可见的bthread::Id结构体。用户接口都是操作id的。从id映射到结构体的方式和brpc中的[其他结构](memory_management.md)类似:32位是内存池的位移,32位是version。前者O(1)时间定位,后者防止ABA问题。
|
||||
|
||||
bthread_id的接口不太简洁,有不少API:
|
||||
|
||||
- create
|
||||
- lock
|
||||
- unlock
|
||||
- unlock_and_destroy
|
||||
- join
|
||||
- error
|
||||
|
||||
这么多接口是为了满足不同的使用流程。
|
||||
|
||||
- 发送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
|
||||
@@ -0,0 +1,59 @@
|
||||
brpc提供了[异步接口](client.md#异步访问),所以一个常见的问题是:我应该用异步接口还是bthread?
|
||||
|
||||
短回答:延时不高时你应该先用简单易懂的同步接口,不行的话用异步接口,只有在需要多核并行计算时才用bthread。
|
||||
|
||||
# 同步或异步
|
||||
|
||||
异步即用回调代替阻塞,有阻塞的地方就有回调。虽然在javascript这种语言中回调工作的很好,接受度也非常高,但只要用过,就会发现这和我们需要的回调是两码事,这个区别不是[lambda](https://en.wikipedia.org/wiki/Anonymous_function),也不是[future](https://en.wikipedia.org/wiki/Futures_and_promises),而是javascript是单线程的。javascript的回调放到多线程下可能没有一个能跑过,竞争太多,单线程的同步方法和多线程的同步方法是完全不同的。那是不是服务能搞成类似的形式呢?多个线程,每个都是独立的eventloop。可以,ub**a**server就是(注意带a),但实际效果糟糕,因为阻塞改回调可不简单,当阻塞发生在循环,条件分支,深层子函数中时,改造特别困难,况且很多老代码、第三方代码根本不可能去改造。结果是代码中会出现不可避免的阻塞,导致那个线程中其他回调都被延迟,流量超时,server性能不符合预期。如果你说,”我想把现在的同步代码改造为大量的回调,除了我其他人都看不太懂,并且性能可能更差了”,我猜大部分人不会同意。别被那些鼓吹异步的人迷惑了,他们写的是从头到尾从下到上全异步且不考虑多线程的代码,和你要写的完全是两码事。
|
||||
|
||||
brpc中的异步和单线程的异步是完全不同的,异步回调会运行在与调用处不同的线程中,你会获得多核扩展性,但代价是你得意识到多线程问题。你可以在回调中阻塞,只要线程够用,对server整体的性能并不会有什么影响。不过异步代码还是很难写的,所以我们提供了[组合访问](combo_channel.md)来简化问题,通过组合不同的channel,你可以声明式地执行复杂的访问,而不用太关心其中的细节。
|
||||
|
||||
当然,延时不长,qps不高时,我们更建议使用同步接口,这也是创建bthread的动机:维持同步代码也能提升交互性能。
|
||||
|
||||
**判断使用同步或异步**:计算qps * latency(in seconds),如果和cpu核数是同一数量级,就用同步,否则用异步。
|
||||
|
||||
比如:
|
||||
|
||||
- qps = 2000,latency = 10ms,计算结果 = 2000 * 0.01s = 20。和常见的32核在同一个数量级,用同步。
|
||||
- qps = 100, latency = 5s, 计算结果 = 100 * 5s = 500。和核数不在同一个数量级,用异步。
|
||||
- qps = 500, latency = 100ms,计算结果 = 500 * 0.1s = 50。基本在同一个数量级,可用同步。如果未来延时继续增长,考虑异步。
|
||||
|
||||
这个公式计算的是同时进行的平均请求数(你可以尝试证明一下),和线程数,cpu核数是可比的。当这个值远大于cpu核数时,说明大部分操作并不耗费cpu,而是让大量线程阻塞着,使用异步可以明显节省线程资源(栈占用的内存)。当这个值小于或和cpu核数差不多时,异步能节省的线程资源就很有限了,这时候简单易懂的同步代码更重要。
|
||||
|
||||
# 异步或bthread
|
||||
|
||||
有了bthread这个工具,用户甚至可以自己实现异步。以“半同步”为例,在brpc中用户有多种选择:
|
||||
|
||||
- 发起多个异步RPC后挨个Join,这个函数会阻塞直到RPC结束。(这儿是为了和bthread对比,实现中我们建议你使用[ParallelChannel](combo_channel.md#parallelchannel),而不是自己Join)
|
||||
- 启动多个bthread各自执行同步RPC后挨个join bthreads。
|
||||
|
||||
哪种效率更高呢?显然是前者。后者不仅要付出创建bthread的代价,在RPC过程中bthread还被阻塞着,不能用于其他用途。
|
||||
|
||||
**如果仅仅是为了并发RPC,别用bthread。**
|
||||
|
||||
不过当你需要并行计算时,问题就不同了。使用bthread可以简单地构建树形的并行计算,充分利用多核资源。比如检索过程中有三个环节可以并行处理,你可以建立两个bthread运行两个环节,在原地运行剩下的环节,最后join那两个bthread。过程大致如下:
|
||||
```c++
|
||||
bool search() {
|
||||
...
|
||||
bthread th1, th2;
|
||||
if (bthread_start_background(&th1, NULL, part1, part1_args) != 0) {
|
||||
LOG(ERROR) << "Fail to create bthread for part1";
|
||||
return false;
|
||||
}
|
||||
if (bthread_start_background(&th2, NULL, part2, part2_args) != 0) {
|
||||
LOG(ERROR) << "Fail to create bthread for part2";
|
||||
return false;
|
||||
}
|
||||
part3(part3_args);
|
||||
bthread_join(th1);
|
||||
bthread_join(th2);
|
||||
return true;
|
||||
}
|
||||
```
|
||||
这么实现的point:
|
||||
- 你当然可以建立三个bthread分别执行三个部分,最后join它们,但相比这个方法要多耗费一个线程资源。
|
||||
- bthread从建立到执行是有延时的(调度延时),在不是很忙的机器上,这个延时的中位数在3微秒左右,90%在10微秒内,99.99%在30微秒内。这说明两点:
|
||||
- 计算时间超过1ms时收益比较明显。如果计算非常简单,几微秒就结束了,用bthread是没有意义的。
|
||||
- 尽量让原地运行的部分最慢,那样bthread中的部分即使被延迟了几微秒,最后可能还是会先结束,而消除掉延迟的影响。并且join一个已结束的bthread时会立刻返回,不会有上下文切换开销。
|
||||
|
||||
另外当你有类似线程池的需求时,像执行一类job的线程池时,也可以用bthread代替。如果对job的执行顺序有要求,你可以使用基于bthread的[ExecutionQueue](execution_queue.md)。
|
||||
@@ -0,0 +1,53 @@
|
||||
|
||||
# Bthread tagged task group
|
||||
|
||||
在很多应用开发过程中都会有线程资源隔离的需求,比如服务分为控制层和数据层,数据层的请求压力大,不希望控制层受到影响;再比如,服务有多个磁盘,希望服务不同磁盘的线程之间没有什么影响;bthread的任务组打标签就是实现bthread的worker线程池按照tag分组,让不同分组之间达到没有互相影响的目的。服务是按照server级别做tag分组的,用户需要将不同分组的service安排到不同server上,不同server将使用不同端口。还有些场景需要有一些后台任务或者定时任务在单独的线程池中调度,这些任务没有service,这种情况也可以使用tag分组专门划分一个线程池,让这些任务在这个tag分组上执行,这个线程池的并发度任务数等都由用户自己控制。用户可以在这个基础上实现多种策略,比如,将tag组限制在NUMA的某个组,设置一些线程本地变量等。
|
||||
实现的逻辑就是在bthread层面创建了多个worker分组,每个分组的处理逻辑和原来一样;bthread接口层面在bthread_attr_t里面增加了tag字段,让用户去设置;rpc层面在brpc::ServerOptions里面增加了bthread_tag字段,用于指定这个server在哪个worker分组上执行。
|
||||
|
||||
|
||||
# 使用方式
|
||||
|
||||
在example/bthread_tag_echo_c++里面有一个实例代码,分别启动服务端和客户端,服务端将worker划分成3个tag(分组),例子里面可以设置FLAGS_tag1,FLAGS_tag2,给不同server打标签。剩下的一个tag(分组)给服务的后台任务使用。
|
||||
|
||||
```c++
|
||||
服务端启动
|
||||
./echo_server -task_group_ntags 3 -tag1 0 -tag2 1 -bthread_concurrency 20 -bthread_min_concurrency 8 -event_dispatcher_num 1
|
||||
|
||||
客户端启动
|
||||
./echo_client -dummy_port 8888 -server "0.0.0.0:8002" -use_bthread true
|
||||
./echo_client -dummy_port 8889 -server "0.0.0.0:8003" -use_bthread true
|
||||
```
|
||||
|
||||
FLAGS_bthread_concurrency为所有线程的数,FLAGS_bthread_min_concurrency为所有分组的线程数的下限,FLAGS_event_dispatcher_num为单个分组中事件驱动器的数量。FLAGS_bthread_current_tag为将要修改的分组的tag值,FLAGS_bthread_concurrency_by_tag设置这个分组的线程数。
|
||||
一般情况应用创建的bthread不需要设置bthread_attr_t的tag字段,创建的bthread会在当前tag上下文中执行;如果希望创建的bthread不在当前tag上下文中执行,可以设置bthread_attr_t的tag字段为希望的值,这么做会对性能有些损失,关键路径上应该避免这么做。
|
||||
|
||||
Q:如何动态改变分组线程的数量?
|
||||
|
||||
A:你可以根据你的服务更自由的设计你的每个分组的线程数,启动的时候会根据你设置的 bthread_concurrency 来初始化线程池,如果你设置了 bthread_min_concurrency,那么会根据 bthread_min_concurrency 来设置线程池,对于 server 来说,num_threads 就是该 tag 对应的 worker 数量。可以通过设置 FLAGS_bthread_current_tag 和 FLAGS_bthread_concurrency_by_tag 来改变某个分组的线程数。如果没有设置(相当于没有启用分组,默认值为BTHREAD_TAG_INVALID),num_threads的含义是所有分组的 worker 总数。
|
||||
|
||||
Q:不同分组之间有什么关系吗?
|
||||
|
||||
A:不同分组是独立的线程池和事件驱动器,完全没有关系。
|
||||
|
||||
Q:可以在分组之间做bthread的同步操作吗?
|
||||
|
||||
A:可以的,每个bthread都有自己的tag标签,挂起后重新投入运行将继续在这个tag的线程池上执行。
|
||||
|
||||
Q:客户端发送和接收RPC消息是在哪个分组上执行的?
|
||||
|
||||
A:这取决于客户端的上下文,如果客户端不在任何tag分组上,那将使用tag0分组收发消息;否则将在当前所在的tag分组收发消息。
|
||||
|
||||
Q:如何将一个分组的线程绑定到指定的一些cpu上面。
|
||||
|
||||
A:int bthread_set_tagged_worker_startfn(void (*start_fn)(bthread_tag_t))这个函数用于在某个分组上做一些初始化的工作,比如:可以实现绑核的代码,根据tag入参来确定不同分组绑定不同的cpu。
|
||||
|
||||
# 监控
|
||||
|
||||
目前监控按照tag划分的指标有,线程的数量、线程的使用量、bthread_count、连接信息
|
||||
|
||||
线程使用量:
|
||||
|
||||
动态调整tag1分组的线程数
|
||||
|
||||
设置tag1:
|
||||
设置所有tag:
|
||||
@@ -0,0 +1,217 @@
|
||||
gdb(ptrace)+ gdb_bthread_stack.py主要的缺点是要慢和阻塞进程,需要一种高效的追踪bthread调用栈的方法。
|
||||
|
||||
bRPC框架的协作式用户态协程无法像Golang内建的抢占式协程一样实现高效的STW(Stop the World),框架也无法干预用户逻辑的执行,所以要追踪bthread调用栈是比较困难的。
|
||||
|
||||
在线追踪bthread调用栈需要解决以下问题:
|
||||
1. 追踪挂起bthread的调用栈。
|
||||
2. 追踪运行中bthread的调用栈。
|
||||
|
||||
# bthread状态模型
|
||||
|
||||
以下是目前的bthread状态模型。
|
||||
|
||||

|
||||
|
||||
# 设计方案
|
||||
|
||||
## 核心思路
|
||||
|
||||
为了解决上述两个问题,该方案实现了STB(Stop The Bthread),核心思路可以简单总结为,在追踪bthread调用栈的过程中,状态不能流转到当前追踪方法不支持的状态。STB包含了两种追踪模式:上下文(context)追踪模式和信号追踪模式。
|
||||
|
||||
### 上下文(context)追踪模式
|
||||
上下文追踪模式可以追踪挂起bthread的调用栈。挂起的bthread栈是稳定的,利用TaskMeta.stack中保存的上下文信息(x86_64下关键的寄存器主要是RIP、RSP、RBP),通过一些可以回溯指定上下文调用栈的库来追踪bthread调用栈。但是挂起的bthread随时可能会被唤醒,执行逻辑(包括jump_stack),则bthread栈会一直变化。不稳定的上下文是不能用来追踪调用栈的,需要在jump_stack前拦截bthread的调度,等到调用栈追踪完成后才继续运行bthread。所以,上下文追踪模式支持就绪、挂起这两个状态。
|
||||
|
||||
### 信号追踪模式
|
||||
|
||||
信号追踪模式可以追踪运行中bthread的调用栈。运行中bthread是不稳定的,不能使用TaskMeta.stack来追踪bthread调用栈。只能另辟蹊径,使用信号中断bthread运行逻辑,在信号处理函数中回溯bthread调用栈。使用信号有两个问题:
|
||||
|
||||
1. 异步信号安全问题。
|
||||
2. 信号追踪模式不支持jump_stack。调用栈回溯需要寄存器信息,但jump_stack会操作寄存器,这个过程是不安全的,所以jump_stack不能被信号中断,需要在jump_stack前拦截bthread的调度,等到bthread调用栈追踪完成后才继续挂起bthread。
|
||||
|
||||
所以,追踪模式只支持运行状态。
|
||||
|
||||
### 小结
|
||||
|
||||
jump_stack是bthread挂起或者运行的必经之路,也是STB的拦截点。STB将状态分成三类:
|
||||
1. 上下文追踪模式的状态:就绪、挂起。
|
||||
2. 支持信号追踪模式的状态:运行。
|
||||
3. 不支持追踪的状态。jump_stack的过程是不允许使用以上两种调用栈追踪方法,需要在jump_stack前拦截bthread的调度,等到调用栈追踪完成后才继续调度bthread。
|
||||
|
||||
### 详细流程
|
||||
|
||||
以下是引入STB后的bthread状态模型,在原来bthread状态模型的基础上,加入两个状态(拦截点):将运行、挂起中。
|
||||
|
||||

|
||||
|
||||
经过上述分析,总结出STB的流程:
|
||||
|
||||
1. TaskTracer(实现STB的一个模块)收到追踪bthread调用栈的请求时,标识正在追踪。追踪完成后,标识追踪完成,并TaskTracer发信号通知可能处于将运行或者挂起中状态的bthread。根据bthread状态,TaskTracer执行不同的逻辑:
|
||||
- 创建、就绪但还没分配栈、销毁:直接结束追踪。
|
||||
- 挂起、就绪:使用上下文追踪模式追踪bthread的调用栈。
|
||||
- 运行:使用信号追踪模式追踪bthread的调用栈。
|
||||
- 将运行、挂起中:TaskTracer自旋等到bthread状态流转到下一个状态(挂起或者运行)后继续追踪。
|
||||
|
||||
2. TaskTracer追踪时,bthread根据状态也会执行不同的逻辑:
|
||||
- 创建、就绪但还没分配栈、就绪:不需要额外处理。
|
||||
- 挂起、运行:通知TaskTracer继续追踪。
|
||||
- 将运行、挂起中、销毁:bthread通过条件变量等到TaskTracer追踪完成。TaskTracer追踪完成后会通过条件变量通知bthread继续执行jump_stack。
|
||||
|
||||
# 使用方法
|
||||
|
||||
1. 下载安装libunwind和abseil-cpp。**注意:libunwind 必须从源码编译,不要使用系统包管理器安装的 `libunwind-dev` / `libunwind-devel`**,否则会触发本文末尾「[已知问题:libunwind 与 libgcc_s 的 `_Unwind_*` 符号冲突](#已知问题libunwind-与-libgcc_s-的-_unwind_-符号冲突)」中描述的崩溃。bazel 构建可以跳过此步,直接使用 brpc 仓库自维护的 libunwind 版本。
|
||||
2. 给config_brpc.sh增加`--with-bthread-tracer`选项或者给cmake增加`-DWITH_BTHREAD_TRACER=ON`选项或者给bazel(Bzlmod模式)增加`--define with_bthread_tracer=true`选项。
|
||||
3. 访问服务的内置服务:`http://ip:port/bthreads/<bthread_id>?st=1`或者代码里调用`bthread::stack_trace()`函数。
|
||||
4. 如果希望追踪pthread的调用栈,在对应pthread上调用`bthread::init_for_pthread_stack_trace()`函数获取一个伪bthread_t,然后使用步骤3即可获取pthread调用栈。
|
||||
|
||||
下面是追踪bthread调用栈的输出示例:
|
||||
```shell
|
||||
#0 0x00007fdbbed500b5 __clock_gettime_2
|
||||
#1 0x000000000041f2b6 butil::cpuwide_time_ns()
|
||||
#2 0x000000000041f289 butil::cpuwide_time_us()
|
||||
#3 0x000000000041f1b9 butil::EveryManyUS::operator bool()
|
||||
#4 0x0000000000413289 (anonymous namespace)::spin_and_log()
|
||||
#5 0x00007fdbbfa58dc0 bthread::TaskGroup::task_runner()
|
||||
```
|
||||
|
||||
# 已知问题
|
||||
|
||||
## libunwind 与 libgcc_s 的 `_Unwind_*` 符号冲突
|
||||
|
||||
### 现象
|
||||
|
||||
启用 bthread tracer 后,可能在 `bthread_exit` / `pthread_exit` 或者 C++ 异常处理路径上偶发段错误,类似如下调用栈:
|
||||
|
||||
```text
|
||||
#0 0x0000000000000000 in ?? ()
|
||||
#1 0x00007fa2b5d6458a in _ULx86_64_dwarf_find_proc_info ()
|
||||
from /root/.cache/bazel/_bazel_root/743b333b2429a1dbd390ef66b59c771d/execroot/_main/bazel-out/k8-fastbuild/bin/test/../_solib_k8/libexternal_Slibunwind~_Slibunwind.so
|
||||
#2 0x00007fa2b5d6668d in fetch_proc_info ()
|
||||
from /root/.cache/bazel/_bazel_root/743b333b2429a1dbd390ef66b59c771d/execroot/_main/bazel-out/k8-fastbuild/bin/test/../_solib_k8/libexternal_Slibunwind~_Slibunwind.so
|
||||
#3 0x00007fa2b5d681a1 in _ULx86_64_dwarf_make_proc_info ()
|
||||
from /root/.cache/bazel/_bazel_root/743b333b2429a1dbd390ef66b59c771d/execroot/_main/bazel-out/k8-fastbuild/bin/test/../_solib_k8/libexternal_Slibunwind~_Slibunwind.so
|
||||
#4 0x00007fa2b5d70cfd in _ULx86_64_get_proc_info ()
|
||||
from /root/.cache/bazel/_bazel_root/743b333b2429a1dbd390ef66b59c771d/execroot/_main/bazel-out/k8-fastbuild/bin/test/../_solib_k8/libexternal_Slibunwind~_Slibunwind.so
|
||||
#5 0x00007fa2b5d6c775 in __libunwind_Unwind_GetLanguageSpecificData ()
|
||||
from /root/.cache/bazel/_bazel_root/743b333b2429a1dbd390ef66b59c771d/execroot/_main/bazel-out/k8-fastbuild/bin/test/../_solib_k8/libexternal_Slibunwind~_Slibunwind.so
|
||||
#6 0x00007fa2b503c6df in __gxx_personality_v0 () from /lib/x86_64-linux-gnu/libstdc++.so.6
|
||||
#7 0x00007fa2b5452ce5 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
#8 0x00007fa2b54533c0 in _Unwind_ForcedUnwind () from /lib/x86_64-linux-gnu/libgcc_s.so.1
|
||||
#9 0x00007fa2b4ca57a4 in __GI___pthread_unwind (buf=<optimized out>) at ./nptl/unwind.c:130
|
||||
#10 0x00007fa2b4c9dd22 in __do_cancel () at ../sysdeps/nptl/pthreadP.h:271
|
||||
#11 __GI___pthread_exit (value=0x0) at ./nptl/pthread_exit.c:36
|
||||
#12 0x0000000000000000 in ?? ()
|
||||
```
|
||||
|
||||
### 根因
|
||||
|
||||
libunwind 的 `src/unwind/*.c` 实现了 GCC 的 `_Unwind_*` ABI 兼容层(`_Unwind_GetLanguageSpecificData`、`_Unwind_ForcedUnwind`、`_Unwind_Resume` 等),与 `libgcc_s.so.1` 提供同名的全局符号。当 libunwind 以**动态库**形式被链接,并且在最终二进制的 `DT_NEEDED` 列表中位置比 `libgcc_s.so.1` 靠前时,运行时动态链接器 `ld.so` 会把 `pthread_exit` / 异常处理触发的 `_Unwind_*` 调用解析到 libunwind 中的 DWARF 实现。该实现需要的内部上下文在 `pthread_exit` 路径上未被 bRPC 初始化好,从而触发空指针访问。
|
||||
|
||||
这是一个 ELF **运行时符号解析顺序**问题,与编译器(GCC / Clang)无关 —— Clang 默认运行时同样使用 `libstdc++ + libgcc_s`,会复现完全一致的崩溃。
|
||||
|
||||
### 解决方案
|
||||
|
||||
> **重要:不要使用系统包管理器安装的 libunwind**(例如 `apt install libunwind-dev`、`yum install libunwind-devel`)。多数发行版打包的 `libunwind.so` 仍把 `_Unwind_*` 暴露在动态符号表中,会触发本节描述的崩溃。
|
||||
>
|
||||
> 必须使用**从源码编译的 libunwind**。上游 `./configure` + `make` 默认会通过 `-Wl,--version-script` 把 `_Unwind_*` 标为 local,不导出到动态符号表,从而避免冲突。
|
||||
|
||||
下表汇总了三种构建方式的推荐方案:
|
||||
|
||||
| 构建方式 | 推荐方案 |
|
||||
|---|---|
|
||||
| `config_brpc.sh` + `make` | 从源码编译并安装 libunwind,把头文件与库目录显式传给 `config_brpc.sh` |
|
||||
| `cmake` | 从源码编译并安装 libunwind,把头文件与库目录显式传给 `cmake` |
|
||||
| `bazel`(Bzlmod) | 直接使用 brpc 仓库自维护的 libunwind 版本 |
|
||||
|
||||
### make (config_brpc.sh)
|
||||
|
||||
源码编译并安装 libunwind 到一个独立目录(避免污染系统目录),然后让 `config_brpc.sh` 显式从该目录查找 libunwind。
|
||||
|
||||
```bash
|
||||
# 1) 源码编译 libunwind(推荐 v1.8.1 或以上版本)
|
||||
git clone https://github.com/libunwind/libunwind.git
|
||||
cd libunwind && git checkout tags/v1.8.1
|
||||
mkdir -p /opt/libunwind
|
||||
autoreconf -i
|
||||
./configure --prefix=/opt/libunwind
|
||||
make -j$(nproc) && make install
|
||||
cd ..
|
||||
|
||||
# 2) 让 config_brpc.sh 使用 /libunwind 下的头文件与库(不要让它自动找到系统的 libunwind-dev)
|
||||
cd brpc
|
||||
sh config_brpc.sh \
|
||||
--with-bthread-tracer \
|
||||
--headers="/opt/libunwind/include /usr/include" \
|
||||
--libs="/opt/libunwind/lib /usr/lib /usr/lib64"
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
构建完成后可用以下命令确认 libunwind.so 没有导出 `_Unwind_*`:
|
||||
|
||||
```bash
|
||||
nm -D /libunwind/lib/libunwind.so | grep ' T _Unwind_' \
|
||||
&& echo "WARN: _Unwind_* exported" \
|
||||
|| echo "OK: _Unwind_* hidden"
|
||||
```
|
||||
|
||||
### cmake
|
||||
|
||||
[`CMakeLists.txt`](../../CMakeLists.txt:90-100) 通过 `find_library(... NAMES unwind unwind-x86_64)` 查找 libunwind。同样需要先源码编译 libunwind 到独立前缀,再用 `CMAKE_PREFIX_PATH` 让 cmake 优先在该前缀下查找:
|
||||
|
||||
```bash
|
||||
# 1) 源码编译 libunwind(同 make 章节)
|
||||
|
||||
# 2) 让 cmake 在 /libunwind 下优先查找头文件和库
|
||||
cd brpc
|
||||
mkdir build && cd build
|
||||
cmake -DWITH_BTHREAD_TRACER=ON \
|
||||
-DCMAKE_PREFIX_PATH=/opt/libunwind \
|
||||
..
|
||||
make -j$(nproc)
|
||||
```
|
||||
|
||||
> 提示:如果系统已经装了 `libunwind-dev`,`find_library` 仍可能优先匹配到 `/usr/lib`。可在 cmake 命令上额外指定
|
||||
> `-DLIBUNWIND_LIB=/libunwind/lib/libunwind.so -DLIBUNWIND_X86_64_LIB=/libunwind/lib/libunwind-x86_64.so -DLIBUNWIND_INCLUDE_PATH=/libunwind/include`
|
||||
> 强制走自编译版本,避免系统包混入。
|
||||
|
||||
### bazel (Bzlmod)
|
||||
|
||||
bRPC 仓库已经在 [`registry/modules/libunwind/`](../../registry/modules/libunwind/) 维护了一份 libunwind 的 Bzlmod overlay,并通过 [`.bazelrc`](../../.bazelrc) 中的 `--registry=https://github.com/apache/brpc/registry` 使用 bRPC 维护的 overlay。版本号采用 `<base>.brpc-no-unwind` 后缀(例如 `1.8.3.brpc-no-unwind`),用以区别于 BCR 上的同基版本。该 overlay 增加了一个开关:
|
||||
|
||||
```
|
||||
--define libunwind_hide_unwind_symbols=true
|
||||
```
|
||||
|
||||
开启后,libunwind 的 `src/unwind/*.c`(即 GCC `_Unwind_*` 兼容层)整体不参与编译,等效于上游 autoconf 默认效果。bRPC 只使用 libunwind 的 `unw_*` 原生 API(`unw_getcontext`、`unw_init_local`、`unw_step` 等),不依赖 `_Unwind_*` 兼容层,因此该开关安全无副作用。
|
||||
|
||||
`.bazelrc` 已默认在 `build:test` / `test` 配置下打开此开关:
|
||||
|
||||
```
|
||||
build:test --define libunwind_hide_unwind_symbols=true
|
||||
test --define libunwind_hide_unwind_symbols=true
|
||||
```
|
||||
|
||||
用户按文档使用方法第 2 步加上 `--define=with_bthread_tracer=true` 即可:
|
||||
|
||||
```bash
|
||||
# 测试场景,.bazelrc 中 test 配置已自动带上 hide 开关
|
||||
bazel test //test:bthread_unittest
|
||||
|
||||
# 非测试构建(生产部署),需要显式同时带上两个 define
|
||||
bazel build --define=with_bthread_tracer=true \
|
||||
--define=libunwind_hide_unwind_symbols=true \
|
||||
//...
|
||||
```
|
||||
|
||||
> **特别注意**:如果在生产构建中只启用 `--define=with_bthread_tracer=true` 而漏掉 `--define=libunwind_hide_unwind_symbols=true`,binary 在 `pthread_exit` / 异常路径上会概率性崩溃。
|
||||
|
||||
构建后可用以下命令验证 libunwind 共享库没有导出 `_Unwind_*`:
|
||||
|
||||
```bash
|
||||
nm -D bazel-bin/external/_solib_*/libexternal*libunwind*.so 2>/dev/null \
|
||||
| grep ' T _Unwind_' || echo "OK: no _Unwind_* exported by libunwind.so"
|
||||
```
|
||||
|
||||
|
||||
# 相关flag
|
||||
|
||||
- `signal_trace_timeout_ms`:信号追踪模式的超时时间,默认为50ms。
|
||||
@@ -0,0 +1,57 @@
|
||||
[English version](../en/builtin_service.md)
|
||||
|
||||
# 什么是内置服务?
|
||||
|
||||
内置服务以多种形式展现服务器内部状态,提高你开发和调试服务的效率。brpc通过HTTP协议提供内置服务,可通过浏览器或curl访问,服务器会根据User-Agent返回纯文本或html,你也可以添加?console=1要求返回纯文本。我们在自己的开发机上启动了[一个长期运行的例子](http://brpc.baidu.com:8765/)(只能百度内访问),你可以点击后随便看看。如果服务端口被限(比如百度内不是所有的端口都能被笔记本访问到),可以使用[rpc_view](rpc_view.md)转发。
|
||||
|
||||
下面是分别从浏览器和终端访问的截图,注意其中的logo是百度内部的名称,在开源版本中是brpc。
|
||||
|
||||
**从浏览器访问**
|
||||
|
||||

|
||||
|
||||
**从命令行访问** 
|
||||
|
||||
# 安全模式
|
||||
|
||||
出于安全考虑,直接对外服务需要隐藏内置服务(包括经过nginx或其他http server转发流量的),具体方法请阅读[这里](server.md#安全模式)。
|
||||
|
||||
# 主要服务
|
||||
|
||||
[/status](status.md): 显示所有服务的主要状态。
|
||||
|
||||
[/vars](vars.md): 用户可定制的,描绘各种指标的计数器。
|
||||
|
||||
[/connections](connections.md): 所有连接的统计信息。
|
||||
|
||||
[/flags](flags.md): 所有gflags的状态,可动态修改。
|
||||
|
||||
[/rpcz](rpcz.md): 查看所有的RPC的细节。
|
||||
|
||||
[cpu profiler](cpu_profiler.md): 分析cpu热点。
|
||||
|
||||
[heap profiler](heap_profiler.md): 分析内存占用。
|
||||
|
||||
[contention profiler](contention_profiler.md): 分析锁竞争。
|
||||
|
||||
# 其他服务
|
||||
|
||||
[/version](http://brpc.baidu.com:8765/version): 查看服务器的版本。用户可通过Server::set_version()设置Server的版本,如果用户没有设置,框架会自动为用户生成,规则:`brpc_server_<service-name1>_<service-name2> ...`
|
||||
|
||||

|
||||
|
||||
[/health](http://brpc.baidu.com:8765/health): 探测服务的存活情况。
|
||||
|
||||

|
||||
|
||||
[/protobufs](http://brpc.baidu.com:8765/protobufs): 查看程序中所有的protobuf结构体。
|
||||
|
||||

|
||||
|
||||
[/vlog](http://brpc.baidu.com:8765/vlog): 查看程序中当前可开启的[VLOG](streaming_log.md#VLOG) (对glog无效)。
|
||||
|
||||

|
||||
|
||||
/dir: 浏览服务器上的所有文件,方便但非常危险,默认关闭。
|
||||
|
||||
/threads: 查看进程内所有线程的运行状况,调用时对程序性能影响较大,默认关闭。
|
||||
@@ -0,0 +1,80 @@
|
||||
[English version](../en/bvar.md)
|
||||
|
||||
# 什么是bvar?
|
||||
|
||||
[bvar](https://github.com/apache/brpc/tree/master/src/bvar/)是多线程环境下的计数器类库,支持[单维度bvar](bvar_c++.md)和[多维度mbvar](mbvar_c++.md),方便记录和查看用户程序中的各类数值,它利用了thread local存储减少了cache bouncing,相比UbMonitor(百度内的老计数器库)几乎不会给程序增加性能开销,也快于竞争频繁的原子操作。brpc集成了bvar,[/vars](vars.md)可查看所有曝光的bvar,[/vars/VARNAME](vars.md)可查阅某个bvar,在brpc中的使用方法请查看[vars](vars.md)。brpc大量使用了bvar提供统计数值,当你需要在多线程环境中计数并展现时,应该第一时间想到bvar。但bvar不能代替所有的计数器,它的本质是把写时的竞争转移到了读:读得合并所有写过的线程中的数据,而不可避免地变慢了。当你读写都很频繁或得基于最新值做一些逻辑判断时,你不应该用bvar。
|
||||
|
||||
为了理解bvar的原理,你得先阅读[Cacheline这节](atomic_instructions.md#cacheline),其中提到的计数器例子便是bvar。当很多线程都在累加一个计数器时,每个线程只累加私有的变量而不参与全局竞争,在读取时累加所有线程的私有变量。虽然读比之前慢多了,但由于这类计数器的读多为低频的记录和展现,慢点无所谓。而写就快多了,极小的开销使得用户可以无顾虑地使用bvar监控系统,这便是我们设计bvar的目的。
|
||||
|
||||
下图是bvar和原子变量,静态UbMonitor,动态UbMonitor在被多个线程同时使用时的开销。可以看到bvar的耗时基本和线程数无关,一直保持在极低的水平(~20纳秒)。而动态UbMonitor在24核时每次累加的耗时达7微秒,这意味着使用300次bvar的开销才抵得上使用一次动态UbMonitor变量。
|
||||
|
||||

|
||||
|
||||
# 新增bvar
|
||||
|
||||
增加C++ bvar的方法请看[快速介绍](bvar_c++.md#quick-introduction). bvar默认统计了进程、系统的一些变量,以process\_, system\_等开头,比如:
|
||||
|
||||
```
|
||||
process_context_switches_involuntary_second : 14
|
||||
process_context_switches_voluntary_second : 15760
|
||||
process_cpu_usage : 0.428
|
||||
process_cpu_usage_system : 0.142
|
||||
process_cpu_usage_user : 0.286
|
||||
process_disk_read_bytes_second : 0
|
||||
process_disk_write_bytes_second : 260902
|
||||
process_faults_major : 256
|
||||
process_faults_minor_second : 14
|
||||
process_memory_resident : 392744960
|
||||
system_core_count : 12
|
||||
system_loadavg_15m : 0.040
|
||||
system_loadavg_1m : 0.000
|
||||
system_loadavg_5m : 0.020
|
||||
```
|
||||
|
||||
还有像brpc内部的各类计数器:
|
||||
|
||||
```
|
||||
bthread_switch_second : 20422
|
||||
bthread_timer_scheduled_second : 4
|
||||
bthread_timer_triggered_second : 4
|
||||
bthread_timer_usage : 2.64987e-05
|
||||
bthread_worker_count : 13
|
||||
bthread_worker_usage : 1.33733
|
||||
bvar_collector_dump_second : 0
|
||||
bvar_collector_dump_thread_usage : 0.000307385
|
||||
bvar_collector_grab_second : 0
|
||||
bvar_collector_grab_thread_usage : 1.9699e-05
|
||||
bvar_collector_pending_samples : 0
|
||||
bvar_dump_interval : 10
|
||||
bvar_revision : "34975"
|
||||
bvar_sampler_collector_usage : 0.00106495
|
||||
iobuf_block_count : 89
|
||||
iobuf_block_count_hit_tls_threshold : 0
|
||||
iobuf_block_memory : 729088
|
||||
iobuf_newbigview_second : 10
|
||||
```
|
||||
|
||||
# 监控bvar
|
||||
打开bvar的[dump功能](bvar_c++.md#export-all-variables)以导出所有的bvar到文件,格式就如上文一样,每行是一对"名字:值"。打开dump功能后应检查monitor/目录下是否有数据,比如:
|
||||
|
||||
```
|
||||
$ ls monitor/
|
||||
bvar.echo_client.data bvar.echo_server.data
|
||||
|
||||
$ tail -5 monitor/bvar.echo_client.data
|
||||
process_swaps : 0
|
||||
process_time_real : 2580.157680
|
||||
process_time_system : 0.380942
|
||||
process_time_user : 0.741887
|
||||
process_username : "gejun"
|
||||
```
|
||||
每次导出都会覆盖之前的文件,这与普通log添加在后面是不同的。
|
||||
|
||||
监控系统应定期搜集每台单机导出的数据,并把它们汇总到一起。这里以百度内的noah为例,bvar定义的变量会出现在noah的指标项中,用户可勾选并查看历史曲线。
|
||||
|
||||

|
||||

|
||||
|
||||
# 导出到Prometheus
|
||||
|
||||
将[Prometheus](https://prometheus.io)的抓取url地址的路径设置为`/brpc_metrics`即可,例如brpc server跑在本机的8080端口,则抓取url配置为`127.0.0.1:8080/brpc_metrics`。
|
||||
@@ -0,0 +1,658 @@
|
||||
- [bvar Introduction](#bvar-introduction)
|
||||
- [bvar::Variable](#bvarvariable)
|
||||
- [Export all variables](#export-all-variables)
|
||||
- [bvar::Reducer](#bvarreducer)
|
||||
- [bvar::Adder](#bvaradder)
|
||||
- [bvar::Maxer](#bvarmaxer)
|
||||
- [bvar::Miner](#bvarminer)
|
||||
- [bvar::IntRecorder](#bvarintrecorder)
|
||||
- [bvar::LatencyRecorder](#bvarlatencyrecorder)
|
||||
- [bvar::Window](#bvarwindow)
|
||||
- [How to use bvar::Window](#how-to-use-bvarwindow)
|
||||
- [bvar::PerSecond](#bvarpersecond)
|
||||
- [Difference with Window](#difference-with-window)
|
||||
- [bvar::WindowEx](#bvarwindowex)
|
||||
- [How to use bvar::WindowEx](#how-to-use-bvarwindowex)
|
||||
- [Difference between bvar::WindowEx and bvar::Window](#difference-between-bvarwindowex-and-bvarwindow)
|
||||
- [bvar::PerSecondEx](#bvarpersecondex)
|
||||
- [How to use bvar::PerSecondEx](#how-to-use-bvarpersecondex)
|
||||
- [Difference between bvar::PerSecondEx and bvar::WindowEx](#difference-between-bvarpersecondex-and-bvarwindowex)
|
||||
- [Difference between bvar::PerSecondEx and bvar::PerSecond](#difference-between-bvarpersecondex-and-bvarpersecond)
|
||||
- [bvar::Status](#bvarstatus)
|
||||
- [bvar::PassiveStatus](#bvarpassivestatus)
|
||||
- [bvar::GFlag](#bvargflag)
|
||||
|
||||
# bvar Introduction
|
||||
|
||||
单维度bvar使用文档,多维度mbvar请[移步](mbvar_c++.md)。
|
||||
|
||||
bvar分为多个具体的类,常用的有:
|
||||
|
||||
| 类型 | 说明 |
|
||||
|-----------------|-----------------------------------------------------------------------------------------|
|
||||
| bvar::Adder\<T\>| 计数器,默认0,varname << N相当于varname += N |
|
||||
| bvar::Maxer\<T\> | 求最大值,默认std::numeric_limits<T>::min(),varname << N相当于varname = max(varname, N) |
|
||||
| bvar::Miner\<T\>| 求最小值,默认std::numeric_limits<T>::max(),varname << N相当于varname = min(varname, N) |
|
||||
| bvar::IntRecorder| 求自使用以来的平均值。注意这里的定语不是“一段时间内”。一般要通过Window衍生出时间窗口内的平均值 |
|
||||
| bvar::Window\<VAR\>| 获得某个bvar在一段时间内的累加值。Window衍生于已存在的bvar,会自动更新 |
|
||||
| bvar::PerSecond\<VAR\>| 获得某个bvar在一段时间内平均每秒的累加值。PerSecond也是会自动更新的衍生变量 |
|
||||
| bvar::WindowEx\<T\> | 获得某个bvar在一段时间内的累加值。不依赖其他的bvar,需要给它发送数据 |
|
||||
| bvar::PerSecondEx\<T\>| 获得某个bvar在一段时间内平均每秒的累加值。不依赖其他的bvar,需要给它发送数据 |
|
||||
| bvar::LatencyRecorder| 专用于记录延时和qps的变量。输入延时,平均延时/最大延时/qps/总次数 都有了 |
|
||||
| bvar::Status\<T\> | 记录和显示一个值,拥有额外的set_value函数 |
|
||||
| bvar::PassiveStatus | 按需显示值。在一些场合中,我们无法set_value或不知道以何种频率set_value,更适合的方式也许是当需要显示时才打印。用户传入打印回调函数实现这个目的 |
|
||||
| bvar::GFlag | 将重要的gflags公开为bvar,以便监控它们 |
|
||||
|
||||
例子:
|
||||
```c++
|
||||
#include <bvar/bvar.h>
|
||||
|
||||
namespace foo {
|
||||
namespace bar {
|
||||
|
||||
// bvar::Adder<T>用于累加,下面定义了一个统计read error总数的Adder。
|
||||
bvar::Adder<int> g_read_error;
|
||||
// 把bvar::Window套在其他bvar上就可以获得时间窗口内的值。
|
||||
bvar::Window<bvar::Adder<int> > g_read_error_minute("foo_bar", "read_error", &g_read_error, 60);
|
||||
// ^ ^ ^
|
||||
// 前缀 监控项名称 60秒,忽略则为10秒
|
||||
|
||||
// bvar::LatencyRecorder是一个复合变量,可以统计:总量、qps、平均延时,延时分位值,最大延时。
|
||||
bvar::LatencyRecorder g_write_latency("foo_bar", "write");
|
||||
// ^ ^
|
||||
// 前缀 监控项,别加latency!LatencyRecorder包含多个bvar,它们会加上各自的后缀,比如write_qps, write_latency等等。
|
||||
|
||||
// 定义一个统计“已推入task”个数的变量。
|
||||
bvar::Adder<int> g_task_pushed("foo_bar", "task_pushed");
|
||||
// 把bvar::PerSecond套在其他bvar上可以获得时间窗口内*平均每秒*的值,这里是每秒内推入task的个数。
|
||||
bvar::PerSecond<bvar::Adder<int> > g_task_pushed_second("foo_bar", "task_pushed_second", &g_task_pushed);
|
||||
// ^ ^
|
||||
// 和Window不同,PerSecond会除以时间窗口的大小. 时间窗口是最后一个参数,这里没填,就是默认10秒。
|
||||
|
||||
} // bar
|
||||
} // foo
|
||||
```
|
||||
|
||||
在应用的地方:
|
||||
|
||||
```c++
|
||||
// 碰到read error
|
||||
foo::bar::g_read_error << 1;
|
||||
|
||||
// write_latency是23ms
|
||||
foo::bar::g_write_latency << 23;
|
||||
|
||||
// 推入了1个task
|
||||
foo::bar::g_task_pushed << 1;
|
||||
```
|
||||
|
||||
注意Window<>和PerSecond<>都是衍生变量,会自动更新,你不用给它们推值。你当然也可以把bvar作为成员变量或局部变量。
|
||||
|
||||
**确认变量名是全局唯一的!** 否则会曝光失败,如果-bvar_abort_on_same_name为true,程序会直接abort。
|
||||
|
||||
程序中有来自各种模块不同的bvar,为避免重名,建议如此命名:**模块_类名_指标**
|
||||
|
||||
- **模块**一般是程序名,可以加上产品线的缩写,比如inf_ds,ecom_retrbs等等。
|
||||
- **类名**一般是类名或函数名,比如storage_manager, file_transfer, rank_stage1等等。
|
||||
- **指标**一般是count,qps,latency这类。
|
||||
|
||||
一些正确的命名如下:
|
||||
|
||||
```
|
||||
iobuf_block_count : 29 # 模块=iobuf 类名=block 指标=count
|
||||
iobuf_block_memory : 237568 # 模块=iobuf 类名=block 指标=memory
|
||||
process_memory_resident : 34709504 # 模块=process 类名=memory 指标=resident
|
||||
process_memory_shared : 6844416 # 模块=process 类名=memory 指标=shared
|
||||
rpc_channel_connection_count : 0 # 模块=rpc 类名=channel_connection 指标=count
|
||||
rpc_controller_count : 1 # 模块=rpc 类名=controller 指标=count
|
||||
rpc_socket_count : 6 # 模块=rpc 类名=socket 指标=count
|
||||
```
|
||||
|
||||
目前bvar会做名字归一化,不管你打入的是foo::BarNum, foo.bar.num, foo bar num , foo-bar-num,最后都是foo_bar_num。
|
||||
|
||||
关于指标:
|
||||
|
||||
- 个数以_count为后缀,比如request_count, error_count。
|
||||
- 每秒的个数以_second为后缀,比如request_second, process_inblocks_second,已经足够明确,不用写成_count_second或_per_second。
|
||||
- 每分钟的个数以_minute为后缀,比如request_minute, process_inblocks_minute
|
||||
|
||||
如果需要使用定义在另一个文件中的计数器,需要在头文件中声明对应的变量。
|
||||
|
||||
```c++
|
||||
namespace foo {
|
||||
namespace bar {
|
||||
// 注意g_read_error_minute和g_task_pushed_second都是衍生的bvar,会自动更新,不要声明。
|
||||
extern bvar::Adder<int> g_read_error;
|
||||
extern bvar::LatencyRecorder g_write_latency;
|
||||
extern bvar::Adder<int> g_task_pushed;
|
||||
} // bar
|
||||
} // foo
|
||||
```
|
||||
|
||||
**不要跨文件定义全局Window或PerSecond**。不同编译单元中全局变量的初始化顺序是[未定义的](https://isocpp.org/wiki/faq/ctors#static-init-order)。在foo.cpp中定义`Adder<int> foo_count`,在foo_qps.cpp中定义`PerSecond<Adder<int> > foo_qps(&foo_count);`是**错误**的做法。
|
||||
|
||||
About thread-safety:
|
||||
|
||||
- bvar是线程兼容的。你可以在不同的线程里操作不同的bvar。比如你可以在多个线程中同时expose或hide**不同的**bvar,它们会合理地操作需要共享的全局数据,是安全的。
|
||||
- **除了读写接口**,bvar的其他函数都是线程不安全的:比如说你不能在多个线程中同时expose或hide**同一个**bvar,这很可能会导致程序crash。一般来说,读写之外的其他接口也没有必要在多个线程中同时操作。
|
||||
|
||||
计时可以使用butil::Timer,接口如下:
|
||||
|
||||
```c++
|
||||
#include <butil/time.h>
|
||||
namespace butil {
|
||||
class Timer {
|
||||
public:
|
||||
enum TimerType { STARTED };
|
||||
|
||||
Timer();
|
||||
|
||||
// butil::Timer tm(butil::Timer::STARTED); // tm is already started after creation.
|
||||
explicit Timer(TimerType);
|
||||
|
||||
// Start this timer
|
||||
void start();
|
||||
|
||||
// Stop this timer
|
||||
void stop();
|
||||
|
||||
// Get the elapse from start() to stop().
|
||||
int64_t n_elapsed() const; // in nanoseconds
|
||||
int64_t u_elapsed() const; // in microseconds
|
||||
int64_t m_elapsed() const; // in milliseconds
|
||||
int64_t s_elapsed() const; // in seconds
|
||||
};
|
||||
} // namespace butil
|
||||
```
|
||||
|
||||
# bvar::Variable
|
||||
|
||||
Variable是所有bvar的基类,主要提供全局注册,列举,查询等功能。
|
||||
|
||||
用户以默认参数建立一个bvar时,这个bvar并未注册到任何全局结构中,在这种情况下,bvar纯粹是一个更快的计数器。我们称把一个bvar注册到全局表中的行为为“曝光”,可通过`expose`函数曝光:
|
||||
```c++
|
||||
// Expose this variable globally so that it's counted in following functions:
|
||||
// list_exposed
|
||||
// count_exposed
|
||||
// describe_exposed
|
||||
// find_exposed
|
||||
// Return 0 on success, -1 otherwise.
|
||||
int expose(const butil::StringPiece& name);
|
||||
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的例子:
|
||||
```c++
|
||||
bvar::Adder<int> count1;
|
||||
|
||||
count1 << 10 << 20 << 30; // values add up to 60.
|
||||
count1.expose("count1"); // expose the variable globally
|
||||
CHECK_EQ("60", bvar::Variable::describe_exposed("count1"));
|
||||
count1.expose("another_name_for_count1"); // expose the variable with another name
|
||||
CHECK_EQ("", bvar::Variable::describe_exposed("count1"));
|
||||
CHECK_EQ("60", bvar::Variable::describe_exposed("another_name_for_count1"));
|
||||
|
||||
bvar::Adder<int> count2("count2"); // exposed in constructor directly
|
||||
CHECK_EQ("0", bvar::Variable::describe_exposed("count2")); // default value of Adder<int> is 0
|
||||
|
||||
bvar::Status<std::string> status1("count2", "hello"); // the name conflicts. if -bvar_abort_on_same_name is true,
|
||||
// program aborts, otherwise a fatal log is printed.
|
||||
```
|
||||
|
||||
为避免重名,bvar的名字应加上前缀,建议为`<namespace>_<module>_<name>`。为了方便使用,我们提供了**expose_as**函数,接收一个前缀。
|
||||
```c++
|
||||
// Expose this variable with a prefix.
|
||||
// Example:
|
||||
// namespace foo {
|
||||
// namespace bar {
|
||||
// class ApplePie {
|
||||
// ApplePie() {
|
||||
// // foo_bar_apple_pie_error
|
||||
// _error.expose_as("foo_bar_apple_pie", "error");
|
||||
// }
|
||||
// private:
|
||||
// bvar::Adder<int> _error;
|
||||
// };
|
||||
// } // foo
|
||||
// } // bar
|
||||
int expose_as(const butil::StringPiece& prefix, const butil::StringPiece& name);
|
||||
```
|
||||
|
||||
# Export all variables
|
||||
|
||||
最常见的导出需求是通过HTTP接口查询和写入本地文件。前者在brpc中通过[/vars](vars.md)服务提供,后者则已实现在bvar中,默认不打开。有几种方法打开这个功能:
|
||||
|
||||
- 用[gflags](flags.md)解析输入参数,在程序启动时加入-bvar_dump,或在brpc中也可通过[/flags](flags.md)服务在启动后动态修改。gflags的解析方法如下,在main函数处添加如下代码:
|
||||
|
||||
```c++
|
||||
#include <gflags/gflags.h>
|
||||
...
|
||||
int main(int argc, char* argv[]) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true/*表示把识别的参数从argc/argv中删除*/);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
- 不想用gflags解析参数,希望直接在程序中默认打开,在main函数处添加如下代码:
|
||||
|
||||
```c++
|
||||
#include <gflags/gflags.h>
|
||||
...
|
||||
int main(int argc, char* argv[]) {
|
||||
if (google::SetCommandLineOption("bvar_dump", "true").empty()) {
|
||||
LOG(FATAL) << "Fail to enable bvar dump";
|
||||
}
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
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.\<app\>.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 | \<app\> | Every dumped name starts with this prefix |
|
||||
| bvar_dump_tabs | \<check the code\> | Dump bvar into different tabs according to the filters (separated by semicolon), format: *(tab_name=wildcards) |
|
||||
|
||||
当bvar_dump_file不为空时,程序会启动一个后台导出线程以bvar_dump_interval指定的间隔更新bvar_dump_file,其中包含了被bvar_dump_include匹配且不被bvar_dump_exclude匹配的所有bvar。
|
||||
|
||||
比如我们把所有的gflags修改为下图:
|
||||
|
||||

|
||||
|
||||
导出文件为:
|
||||
|
||||
```
|
||||
$ cat bvar.echo_server.data
|
||||
rpc_server_8002_builtin_service_count : 20
|
||||
rpc_server_8002_connection_count : 1
|
||||
rpc_server_8002_nshead_service_adaptor : brpc::policy::NovaServiceAdaptor
|
||||
rpc_server_8002_service_count : 1
|
||||
rpc_server_8002_start_time : 2015/07/24-21:08:03
|
||||
rpc_server_8002_uptime_ms : 14740954
|
||||
```
|
||||
|
||||
像”`iobuf_block_count : 8`”被bvar_dump_include过滤了,“`rpc_server_8002_error : 0`”则被bvar_dump_exclude排除了。
|
||||
|
||||
如果你的程序没有使用brpc,仍需要动态修改gflag(一般不需要),可以调用google::SetCommandLineOption(),如下所示:
|
||||
```c++
|
||||
#include <gflags/gflags.h>
|
||||
...
|
||||
if (google::SetCommandLineOption("bvar_dump_include", "*service*").empty()) {
|
||||
LOG(ERROR) << "Fail to set bvar_dump_include";
|
||||
return -1;
|
||||
}
|
||||
LOG(INFO) << "Successfully set bvar_dump_include to *service*";
|
||||
```
|
||||
|
||||
请勿直接设置FLAGS_bvar_dump_file / FLAGS_bvar_dump_include / FLAGS_bvar_dump_exclude。
|
||||
一方面这些gflag类型都是std::string,直接覆盖是线程不安全的;另一方面不会触发validator(检查正确性的回调),所以也不会启动后台导出线程。
|
||||
|
||||
用户也可以使用dump_exposed函数自定义如何导出进程中的所有已曝光的bvar:
|
||||
```c++
|
||||
// Implement this class to write variables into different places.
|
||||
// If dump() returns false, Variable::dump_exposed() stops and returns -1.
|
||||
class Dumper {
|
||||
public:
|
||||
virtual bool dump(const std::string& name, const butil::StringPiece& description) = 0;
|
||||
};
|
||||
|
||||
// Options for Variable::dump_exposed().
|
||||
struct DumpOptions {
|
||||
// Contructed with default options.
|
||||
DumpOptions();
|
||||
// If this is true, string-type values will be quoted.
|
||||
bool quote_string;
|
||||
// The ? in wildcards. Wildcards in URL need to use another character
|
||||
// because ? is reserved.
|
||||
char question_mark;
|
||||
// Separator for white_wildcards and black_wildcards.
|
||||
char wildcard_separator;
|
||||
// Name matched by these wildcards (or exact names) are kept.
|
||||
std::string white_wildcards;
|
||||
// Name matched by these wildcards (or exact names) are skipped.
|
||||
std::string black_wildcards;
|
||||
};
|
||||
|
||||
class Variable {
|
||||
...
|
||||
...
|
||||
// Find all exposed variables matching `white_wildcards' but
|
||||
// `black_wildcards' and send them to `dumper'.
|
||||
// Use default options when `options' is NULL.
|
||||
// Return number of dumped variables, -1 on error.
|
||||
static int dump_exposed(Dumper* dumper, const DumpOptions* options);
|
||||
};
|
||||
```
|
||||
|
||||
# bvar::Reducer
|
||||
|
||||
Reducer用二元运算符把多个值合并为一个值,运算符需满足结合律,交换律,没有副作用。只有满足这三点,我们才能确保合并的结果不受线程私有数据如何分布的影响。像减法就不满足结合律和交换律,它无法作为此处的运算符。
|
||||
```c++
|
||||
// Reduce multiple values into one with `Op': e1 Op e2 Op e3 ...
|
||||
// `Op' shall satisfy:
|
||||
// - associative: a Op (b Op c) == (a Op b) Op c
|
||||
// - commutative: a Op b == b Op a;
|
||||
// - no side effects: a Op b never changes if a and b are fixed.
|
||||
// otherwise the result is undefined.
|
||||
template <typename T, typename Op>
|
||||
class Reducer : public Variable;
|
||||
```
|
||||
reducer << e1 << e2 << e3的作用等价于reducer = e1 op e2 op e3。
|
||||
|
||||
常见的Redcuer子类有bvar::Adder, bvar::Maxer, bvar::Miner。
|
||||
|
||||
## bvar::Adder
|
||||
|
||||
顾名思义,用于累加,Op为+。
|
||||
```c++
|
||||
bvar::Adder<int> value;
|
||||
value << 1 << 2 << 3 << -4;
|
||||
CHECK_EQ(2, value.get_value());
|
||||
|
||||
bvar::Adder<double> fp_value; // 可能有warning
|
||||
fp_value << 1.0 << 2.0 << 3.0 << -4.0;
|
||||
CHECK_DOUBLE_EQ(2.0, fp_value.get_value());
|
||||
```
|
||||
Adder<>可用于非基本类型,对应的类型至少要重载`T operator+(T, T)`。一个已经存在的例子是std::string,下面的代码会把string拼接起来:
|
||||
```c++
|
||||
// This is just proof-of-concept, don't use it for production code because it makes a
|
||||
// bunch of temporary strings which is not efficient, use std::ostringstream instead.
|
||||
bvar::Adder<std::string> concater;
|
||||
std::string str1 = "world";
|
||||
concater << "hello " << str1;
|
||||
CHECK_EQ("hello world", concater.get_value());
|
||||
```
|
||||
|
||||
## bvar::Maxer
|
||||
用于取最大值,运算符为std::max。
|
||||
```c++
|
||||
bvar::Maxer<int> value;
|
||||
value << 1 << 2 << 3 << -4;
|
||||
CHECK_EQ(3, value.get_value());
|
||||
```
|
||||
Since Maxer<> use std::numeric_limits<T>::min() as the identity, it cannot be applied to generic types unless you specialized std::numeric_limits<> (and overloaded operator<, yes, not operator>).
|
||||
|
||||
## bvar::Miner
|
||||
|
||||
用于取最小值,运算符为std::min。
|
||||
```c++
|
||||
bvar::Maxer<int> value;
|
||||
value << 1 << 2 << 3 << -4;
|
||||
CHECK_EQ(-4, value.get_value());
|
||||
```
|
||||
Since Miner<> use std::numeric_limits<T>::max() as the identity, it cannot be applied to generic types unless you specialized std::numeric_limits<> (and overloaded operator<).
|
||||
|
||||
# bvar::IntRecorder
|
||||
|
||||
用于计算平均值。
|
||||
```c++
|
||||
// For calculating average of numbers.
|
||||
// Example:
|
||||
// IntRecorder latency;
|
||||
// latency << 1 << 3 << 5;
|
||||
// CHECK_EQ(3, latency.average());
|
||||
class IntRecorder : public Variable;
|
||||
```
|
||||
|
||||
# bvar::LatencyRecorder
|
||||
|
||||
专用于计算latency和qps的计数器。只需填入latency数据,就能获得latency / max_latency / qps / count。统计窗口是最后一个参数,不填为bvar_dump_interval(这里没填)。
|
||||
|
||||
注意:LatencyRecorder没有继承Variable,而是多个bvar的组合。
|
||||
```c++
|
||||
LatencyRecorder write_latency("table2_my_table_write"); // produces 4 variables:
|
||||
// table2_my_table_write_latency
|
||||
// table2_my_table_write_max_latency
|
||||
// table2_my_table_write_qps
|
||||
// table2_my_table_write_count
|
||||
// In your write function
|
||||
write_latency << the_latency_of_write;
|
||||
```
|
||||
|
||||
# bvar::Window
|
||||
|
||||
获得之前一段时间内的统计值。Window不能独立存在,必须依赖于一个已有的计数器。Window会自动更新,不用给它发送数据。出于性能考虑,Window的数据来自于每秒一次对原计数器的采样,在最差情况下,Window的返回值有1秒的延时。
|
||||
```c++
|
||||
// Get data within a time window.
|
||||
// The time unit is 1 second fixed.
|
||||
// Window relies on other bvar which should be constructed before this window and destructs after this window.
|
||||
// R must:
|
||||
// - have get_sampler() (not require thread-safe)
|
||||
// - defined value_type and sampler_type
|
||||
template <typename R>
|
||||
class Window : public Variable;
|
||||
```
|
||||
|
||||
## How to use bvar::Window
|
||||
```c++
|
||||
bvar::Adder<int> sum;
|
||||
bvar::Maxer<int> max_value;
|
||||
bvar::IntRecorder avg_value;
|
||||
|
||||
// sum_minute.get_value()是sum在之前60秒内的累加值。
|
||||
bvar::Window<bvar::Adder<int> > sum_minute(&sum, 60);
|
||||
|
||||
// max_value_minute.get_value()是max_value在之前60秒内的最大值。
|
||||
bvar::Window<bvar::Maxer<int> > max_value_minute(&max_value, 60);
|
||||
|
||||
// avg_value_minute.get_value()是avg_value在之前60秒内的平均值。
|
||||
bvar::Window<IntRecorder> avg_value_minute(&avg_value, 60);
|
||||
```
|
||||
|
||||
# bvar::PerSecond
|
||||
|
||||
获得之前一段时间内平均每秒的统计值。它和Window基本相同,除了返回值会除以时间窗口之外。
|
||||
```c++
|
||||
bvar::Adder<int> sum;
|
||||
|
||||
// sum_per_second.get_value()是sum在之前60秒内*平均每秒*的累加值,省略最后一个时间窗口的话默认为bvar_dump_interval。
|
||||
bvar::PerSecond<bvar::Adder<int> > sum_per_second(&sum, 60);
|
||||
```
|
||||
**PerSecond并不总是有意义**
|
||||
|
||||
上面的代码中没有Maxer,因为一段时间内的最大值除以时间窗口是没有意义的。
|
||||
```c++
|
||||
bvar::Maxer<int> max_value;
|
||||
|
||||
// 错误!最大值除以时间是没有意义的
|
||||
bvar::PerSecond<bvar::Maxer<int> > max_value_per_second_wrong(&max_value);
|
||||
|
||||
// 正确,把Window的时间窗口设为1秒才是正确的做法
|
||||
bvar::Window<bvar::Maxer<int> > max_value_per_second(&max_value, 1);
|
||||
```
|
||||
|
||||
## Difference with Window
|
||||
|
||||
比如要统计内存在上一分钟内的变化,用Window<>的话,返回值的含义是”上一分钟内存增加了18M”,用PerSecond<>的话,返回值的含义是“上一分钟平均每秒增加了0.3M”。
|
||||
|
||||
Window的优点是精确值,适合一些比较小的量,比如“上一分钟的错误数“,如果这用PerSecond的话,得到可能是”上一分钟平均每秒产生了0.0167个错误",这相比于”上一分钟有1个错误“显然不够清晰。另外一些和时间无关的量也要用Window,比如统计上一分钟cpu占用率的方法是用一个Adder同时累加cpu时间和真实时间,然后用Window获得上一分钟的cpu时间和真实时间,两者相除就得到了上一分钟的cpu占用率,这和时间无关,用PerSecond会产生错误的结果。
|
||||
|
||||
# bvar::WindowEx
|
||||
|
||||
获得之前一段时间内的统计值。WindowEx是独立存在的,不依赖其他的计数器,需要给它发送数据。出于性能考虑,WindowEx每秒对数据做一次统计,在最差情况下,WindowEx的返回值有1秒的延时。
|
||||
```c++
|
||||
// Get data within a time window.
|
||||
// The time unit is 1 second fixed.
|
||||
// Window not relies on other bvar.
|
||||
|
||||
// R must:
|
||||
// - window_size must be a constant
|
||||
template <typename R, time_t window_size = 0>
|
||||
class WindowEx : public adapter::WindowExAdapter<R, adapter::WindowExType<R> > {
|
||||
public:
|
||||
typedef adapter::WindowExAdapter<R, adapter::WindowExType<R> > Base;
|
||||
|
||||
WindowEx() : Base(window_size) {}
|
||||
|
||||
WindowEx(const base::StringPiece& name) : Base(window_size) {
|
||||
this->expose(name);
|
||||
}
|
||||
|
||||
WindowEx(const base::StringPiece& prefix,
|
||||
const base::StringPiece& name)
|
||||
: Base(window_size) {
|
||||
this->expose_as(prefix, name);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## How to use bvar::WindowEx
|
||||
```c++
|
||||
const int window_size = 60;
|
||||
|
||||
// sum_minute.get_value()是60秒内的累加值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
bvar::WindowEx<bvar::Adder<int>, window_size> sum_minute("sum_minute");
|
||||
sum_minute << 1 << 2 << 3;
|
||||
|
||||
// max_minute.get_value()是60秒内的最大值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
bvar::WindowEx<bvar::Maxer<int>, window_size> max_minute("max_minute");
|
||||
max_minute << 1 << 2 << 3;
|
||||
|
||||
// min_minute.get_value()是60秒内的最小值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
bvar::WindowEx<bvar::Miner<int>, window_size> min_minute("min_minute");
|
||||
min_minute << 1 << 2 << 3;
|
||||
|
||||
// avg_minute.get_value是60秒内的平均值(返回值是bvar::Stat),省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
bvar::WindowEx<bvar::IntRecorder, window_size> avg_minute("avg_minute");
|
||||
avg_minute << 1 << 2 << 3;
|
||||
// 获得avg_minuter 60秒内的平均值stat
|
||||
bvar::Stat avg_stat = avg_minute.get_value();
|
||||
// 获得整型平均值
|
||||
int64_t avg_int = avg_stat.get_average_int();
|
||||
// 获得double类型平均值
|
||||
double avg_double = avg_stat.get_average_double();
|
||||
```
|
||||
|
||||
## Difference between bvar::WindowEx and bvar::Window
|
||||
|
||||
- bvar::Window 不能独立存在,必须依赖于一个已有的计数器。Window会自动更新,不用给它发送数据;window_size是通过构造函数参数传递的。
|
||||
|
||||
- bvar::WindowEx 是独立存在的,不依赖其他的计数器,需要给它发送数据。使用起来比较方便;window_size是通过模板参数传递的,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
|
||||
# bvar::PerSecondEx
|
||||
获得之前一段时间内平均每秒的统计值。它和WindowEx基本相同,除了返回值会除以时间窗口之外。
|
||||
```c++
|
||||
// Get data per second within a time window.
|
||||
// The only difference between PerSecondEx and WindowEx is that PerSecondEx divides
|
||||
// the data by time duration.
|
||||
|
||||
// R must:
|
||||
// - window_size must be a constant
|
||||
template <typename R, time_t window_size = 0>
|
||||
class PerSecondEx : public adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > {
|
||||
public:
|
||||
typedef adapter::WindowExAdapter<R, adapter::PerSecondExType<R> > Base;
|
||||
|
||||
PerSecondEx() : Base(window_size) {}
|
||||
|
||||
PerSecondEx(const base::StringPiece& name) : Base(window_size) {
|
||||
this->expose(name);
|
||||
}
|
||||
|
||||
PerSecondEx(const base::StringPiece& prefix,
|
||||
const base::StringPiece& name)
|
||||
: Base(window_size) {
|
||||
this->expose_as(prefix, name);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## How to use bvar::PerSecondEx
|
||||
|
||||
```c++
|
||||
const int window_size = 60;
|
||||
|
||||
// sum_per_second.get_value()是60秒内*平均每秒*的累加值,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
bvar::PerSecondEx<bvar::Adder<int>, window_size> sum_per_second("sum_per_second");
|
||||
sum_per_second << 1 << 2 << 3;
|
||||
```
|
||||
|
||||
## Difference between bvar::PerSecondEx and bvar::WindowEx
|
||||
|
||||
- bvar::PerSecondEx 获得之前一段时间内平均每秒的统计值。它和WindowEx基本相同,除了返回值会除以时间窗口之外。
|
||||
|
||||
## Difference between bvar::PerSecondEx and bvar::PerSecond
|
||||
- bvar::PerSecond 不能独立存在,必须依赖于一个已有的计数器。PerSecond会自动更新,不用给它发送数据;window_size是通过构造函数参数传递的。
|
||||
- bvar::PerSecondEx 是独立存在的,不依赖其他的计数器,需要给它发送数据。使用起来比较方便;window_size是通过模板参数传递的,省略最后一个window_size(时间窗口)的话默认为bvar_dump_interval。
|
||||
|
||||
# bvar::Status
|
||||
|
||||
记录和显示一个值,拥有额外的set_value函数。
|
||||
|
||||
```c++
|
||||
// Display a rarely or periodically updated value.
|
||||
// Usage:
|
||||
// bvar::Status<int> foo_count1(17);
|
||||
// foo_count1.expose("my_value");
|
||||
//
|
||||
// bvar::Status<int> foo_count2;
|
||||
// foo_count2.set_value(17);
|
||||
//
|
||||
// bvar::Status<int> foo_count3("my_value", 17);
|
||||
//
|
||||
// Notice that Tp needs to be std::string or acceptable by boost::atomic<Tp>.
|
||||
template <typename Tp>
|
||||
class Status : public Variable;
|
||||
```
|
||||
|
||||
# bvar::PassiveStatus
|
||||
|
||||
按需显示值。在一些场合中,我们无法set_value或不知道以何种频率set_value,更适合的方式也许是当需要显示时才打印。用户传入打印回调函数实现这个目的。
|
||||
```c++
|
||||
// Display a updated-by-need value. This is done by passing in an user callback
|
||||
// which is called to produce the value.
|
||||
// Example:
|
||||
// int print_number(void* arg) {
|
||||
// ...
|
||||
// return 5;
|
||||
// }
|
||||
//
|
||||
// // number1 : 5
|
||||
// bvar::PassiveStatus status1("number1", print_number, arg);
|
||||
//
|
||||
// // foo_number2 : 5
|
||||
// bvar::PassiveStatus status2(typeid(Foo), "number2", print_number, arg);
|
||||
template <typename Tp>
|
||||
class PassiveStatus : public Variable;
|
||||
```
|
||||
虽然很简单,但PassiveStatus是最有用的bvar之一,因为很多统计量已经存在,我们不需要再次存储它们,而只要按需获取。比如下面的代码声明了一个在linux下显示进程用户名的bvar:
|
||||
```c++
|
||||
static void get_username(std::ostream& os, void*) {
|
||||
char buf[32];
|
||||
if (getlogin_r(buf, sizeof(buf)) == 0) {
|
||||
buf[sizeof(buf)-1] = '\0';
|
||||
os << buf;
|
||||
} else {
|
||||
os << "unknown";
|
||||
}
|
||||
}
|
||||
PassiveStatus<std::string> g_username("process_username", get_username, NULL);
|
||||
```
|
||||
|
||||
# bvar::GFlag
|
||||
Expose important gflags as bvar so that they're monitored.
|
||||
```c++
|
||||
DEFINE_int32(my_flag_that_matters, 8, "...");
|
||||
|
||||
// Expose the gflag as *same-named* bvar so that it's monitored.
|
||||
static bvar::GFlag s_gflag_my_flag_that_matters("my_flag_that_matters");
|
||||
// ^
|
||||
// the gflag name
|
||||
|
||||
// Expose the gflag as a bvar named "foo_bar_my_flag_that_matters".
|
||||
static bvar::GFlag s_gflag_my_flag_that_matters_with_prefix("foo_bar", "my_flag_that_matters");
|
||||
```
|
||||
# babylon counter bvar
|
||||
|
||||
原理和性能见[babylon介绍](https://github.com/baidu/babylon/tree/main/example/use-counter-with-bvar)。
|
||||
|
||||
目前只支持bazel编译方式:`--define with_babylon_counter=true`,babylon版本要求:>= 1.4.4。打开开关后,即可使用基于babylon counter实现的更高性能的bvar,无需修改代码。
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# 进展
|
||||
|
||||
| 时间 | 内容 | 说明 |
|
||||
| ----------- | ------------------------------------- | -------------- |
|
||||
| 8.11 - 8.28 | 调研 + 研发 + 自测 | 自测性能报告见附件 |
|
||||
| 9.8 - 9.22 | QA测试 | QA测试报告见附件 |
|
||||
| 10.8 | 北京机房1台机器上线 | |
|
||||
| 10.14 | 北京机房1台机器上线 | 修复URL编码问题 |
|
||||
| 10.19 | 北京机房7/35机器上线杭州和南京各2台机器上线 | 开始小流量上线 |
|
||||
| 10.22 | 北京机房10/35机器上线杭州机房5/26机器上线南京机房5/19机器上线 | 修复http响应数据压缩问题 |
|
||||
| 11.3 | 北京机房10/35机器上线 | 修复RPC内存泄露问题 |
|
||||
| 11.6 | 杭州机房5/26机器上线南京机房5/19机器上线 | 同北京机房版本 |
|
||||
| 11.9 | 北京机房全流量上线 | |
|
||||
|
||||
截止目前,线上服务表现稳定。
|
||||
|
||||
# QA测试结论
|
||||
|
||||
1. 【性能测试】单机支持最大QPS:**9000+**。可以有效解决原来hulu_pbrpc中一个慢服务拖垮所有服务的问题。性能很好。
|
||||
2. 【稳定性测试】长时间压测没问题。
|
||||
|
||||
QA测试结论:通过
|
||||
|
||||
# 性能提升实时统计
|
||||
|
||||
统计时间2015.11.3 15:00 – 2015.11.9 14:30,共**143.5**小时(近6天)不间断运行。北京机房升级前和升级后同机房各6台机器,共**12**台线上机器的Noah监控数据。
|
||||
|
||||
| 指标 | 升级**前**均值hulu_pbrpc | 升级**后**均值brpc | 收益对比 | 说明 |
|
||||
| -------- | ------------------- | ------------- | ------------ | ------------------------ |
|
||||
| CPU占用率 | 67.35% | 29.28% | 降低**56.53**% | |
|
||||
| 内存占用 | 327.81MB | 336.91MB | 基本持平 | |
|
||||
| 鉴权平响(ms) | 0.605 | 0.208 | 降低**65.62**% | |
|
||||
| 转发平响(ms) | 22.49 | 23.18 | 基本持平 | 依赖后端各个服务的性能 |
|
||||
| 总线程数 | 193 | 132 | 降低**31.61**% | Baidu RPC版本线程数使用率较低,还可降低 |
|
||||
| 极限QPS | 3000 | 9000 | 提升**3**倍 | 线下使用Geoconv和Geocoder服务测试 |
|
||||
|
||||
**CPU使用率(%)**(红色为升级前,蓝色为升级后)
|
||||

|
||||
|
||||
**内存使用量(KB)**(红色为升级前,蓝色为升级后)
|
||||

|
||||
|
||||
**鉴权平响(ms)**(红色为升级前,蓝色为升级后)
|
||||

|
||||
|
||||
**转发平响(ms)**(红色为升级前,蓝色为升级后)
|
||||

|
||||
|
||||
**总线程数(个)**(红色为升级前,蓝色为升级后)
|
||||

|
||||
@@ -0,0 +1,23 @@
|
||||
# 背景
|
||||
|
||||
baidu-dsp是联盟基于Ad Exchange和RTB模式的需求方平台,服务大客户、代理的投放产品体系。我们改造了多个模块,均取得了显著的效果。本文只介绍其中关于super-nova-as的改动。super-nova-as是的baidu-dsp的AS,之前使用ub-aserver编写,为了尽量减少改动,我们没有改造整个as,而只是把super-nova-as连接下游(ctr-server、cvr-server、super-nova-bs)的client从ubrpc升级为brpc。
|
||||
|
||||
# 结论
|
||||
|
||||
1. as的吞吐量有显著提升(不到1500 -> 2500+)
|
||||
2. cpu优化:从1500qps 50%cpu_idle提高到2000qps 50% cpu_idle;
|
||||
3. 超时率改善明显。
|
||||
|
||||
# 测试过程
|
||||
|
||||
1. 环境:1个as,1个bs,1个ctr,1个cvr;部署情况为:bs单机部署,as+ctr+cvr混布;ctr和cvr为brpc版本
|
||||
2. 分别采用1000,1500压力对ubrpc版本的as进行压测,发现1500压力下,as对bs有大量的超时,as到达瓶颈;
|
||||
3. 分别采用2000,2500压力对brpc版本的as进行压测,发现2500压力下,as机器的cpu_idle低于30%,as到达瓶颈。brpc对资源利用充分。
|
||||
|
||||
| | ubrpc | brpc |
|
||||
| -------- | ---------------------------------------- | ---------------------------------------- |
|
||||
| 流量 |  |  |
|
||||
| bs成功率 |  |  |
|
||||
| cpu_idle |  |  |
|
||||
| ctr成功率 |  |  |
|
||||
| cvr成功率 |  |  |
|
||||
@@ -0,0 +1,87 @@
|
||||
# 背景
|
||||
|
||||
ELF(Essential/Extreme/Excellent Learning Framework) 框架为公司内外的大数据应用提供学习/挖掘算法开发支持。 平台主要包括数据迭代处理的框架支持,并行计算过程中的通信支持和用于存储大规模参数的分布式、快速、高可用参数服务器。应用于fcr-model,公有云bml,大数据实验室,语音技术部门等等。之前是基于[zeromq](http://zeromq.org/)封装的rpc,这次改用brpc。
|
||||
|
||||
# 结论
|
||||
|
||||
单个rpc-call以及单次请求所有rpc-call的提升非常显著,延时都缩短了**50%**以上,总训练的耗时除了ftrl-sync-no-shuffle提升不明显外,其余三个算法训练总体性能都有**15%**左右的提升。现在瓶颈在于计算逻辑,所以相对单个的rpc的提升没有那么多。该成果后续会推动凤巢模型训练的上线替换。详细耗时见下节。
|
||||
|
||||
# 性能对比报告
|
||||
|
||||
## 算法总耗时
|
||||
|
||||
ftrl算法: 替换前总耗时2:4:39, 替换后总耗时1:42:48, 提升18%
|
||||
|
||||
ftrl-sync-no-shuffle算法: 替换前总耗时3:20:47, 替换后总耗时3:15:28, 提升2.5%
|
||||
|
||||
ftrl-sync算法: 替换前总耗时4:28:47, 替换后总耗时3:45:57, 提升16%
|
||||
|
||||
fm-sync算法: 替换前总耗时6:16:37, 替换后总耗时5:21:00, 提升14.6%
|
||||
|
||||
## 子任务耗时
|
||||
|
||||
单个rpc-call(针对ftrl算法)
|
||||
|
||||
| | Average | Max | Min |
|
||||
| ---- | --------- | --------- | ---------- |
|
||||
| 替换前 | 164.946ms | 7938.76ms | 0.249756ms |
|
||||
| 替换后 | 10.4198ms | 2614.38ms | 0.076416ms |
|
||||
| 缩短 | 93% | 67% | 70% |
|
||||
|
||||
单次请求所有rpc-call(针对ftrl算法)
|
||||
|
||||
| | Average | Max | Min |
|
||||
| ---- | -------- | -------- | --------- |
|
||||
| 替换前 | 658.08ms | 7123.5ms | 1.88159ms |
|
||||
| 替换后 | 304.878 | 2571.34 | 0 |
|
||||
| 缩短 | 53.7% | 63.9% | |
|
||||
|
||||
## 结论
|
||||
|
||||
单个rpc-call以及单次请求所有rpc-call的提升非常显著,提升都在50%以上,总任务的耗时除了ftrl-sync-no-shuffle提升不明显外,其余三个算法都有15%左右的提升,现在算法的瓶颈在于对计算逻辑,所以相对单个的rpc的提升没有那么多。
|
||||
|
||||
附cpu profiling结果, top 40没有rpc相关函数。
|
||||
```
|
||||
Total: 8664 samples
|
||||
755 8.7% 8.7% 757 8.7% baidu::elf::Partitioner
|
||||
709 8.2% 16.9% 724 8.4% baidu::elf::GlobalShardWriterClient::local_aggregator::{lambda#1}::operator [clone .part.1341]
|
||||
655 7.6% 24.5% 655 7.6% boost::detail::lcast_ret_unsigned
|
||||
582 6.7% 31.2% 642 7.4% baidu::elf::RobinHoodLinkedHashMap
|
||||
530 6.1% 37.3% 2023 23.4% std::vector
|
||||
529 6.1% 43.4% 529 6.1% std::binary_search
|
||||
406 4.7% 48.1% 458 5.3% tc_delete
|
||||
405 4.7% 52.8% 2454 28.3% baidu::elf::GlobalShardWriterClient
|
||||
297 3.4% 56.2% 297 3.4% __memcpy_sse2_unaligned
|
||||
256 3.0% 59.2% 297 3.4% tc_new
|
||||
198 2.3% 61.5% 853 9.8% std::__introsort_loop
|
||||
157 1.8% 63.3% 157 1.8% baidu::elf::GrowableArray
|
||||
152 1.8% 65.0% 177 2.0% calculate_grad
|
||||
142 1.6% 66.7% 699 8.1% baidu::elf::BlockTableReaderManager
|
||||
137 1.6% 68.2% 656 7.6% baidu::elf::DefaultWriterServer
|
||||
127 1.5% 69.7% 127 1.5% _init
|
||||
122 1.4% 71.1% 582 6.7% __gnu_cxx::__normal_iterator
|
||||
117 1.4% 72.5% 123 1.4% baidu::elf::GrowableArray::emplace_back
|
||||
116 1.3% 73.8% 116 1.3% baidu::elf::RobinHoodHashMap::insert
|
||||
101 1.2% 75.0% 451 5.2% baidu::elf::NoCacheReaderClient
|
||||
99 1.1% 76.1% 3614 41.7% parse_ins
|
||||
97 1.1% 77.2% 97 1.1% std::basic_string::_Rep::_M_dispose [clone .part.12]
|
||||
96 1.1% 78.3% 154 1.8% std::basic_string
|
||||
91 1.1% 79.4% 246 2.8% boost::algorithm::split_iterator
|
||||
87 1.0% 80.4% 321 3.7% boost::function2
|
||||
76 0.9% 81.3% 385 4.4% boost::detail::function::functor_manager
|
||||
69 0.8% 82.1% 69 0.8% std::locale::~locale
|
||||
63 0.7% 82.8% 319 3.7% std::__unguarded_linear_insert
|
||||
58 0.7% 83.5% 2178 25.2% boost::algorithm::split [clone .constprop.2471]
|
||||
54 0.6% 84.1% 100 1.2% std::vector::_M_emplace_back_aux
|
||||
49 0.6% 84.7% 49 0.6% boost::algorithm::detail::is_any_ofF
|
||||
47 0.5% 85.2% 79 0.9% baidu::elf::DefaultReaderServer
|
||||
41 0.5% 85.7% 41 0.5% std::locale::_S_initialize
|
||||
39 0.5% 86.1% 677 7.8% boost::detail::function::function_obj_invoker2
|
||||
39 0.5% 86.6% 39 0.5% memset
|
||||
39 0.5% 87.0% 39 0.5% std::locale::locale
|
||||
38 0.4% 87.5% 50 0.6% FTRLAggregator::serialize
|
||||
36 0.4% 87.9% 67 0.8% tcmalloc::CentralFreeList::ReleaseToSpans
|
||||
34 0.4% 88.3% 34 0.4% madvise
|
||||
34 0.4% 88.7% 38 0.4% tcmalloc::CentralFreeList::FetchFromOneSpans
|
||||
32 0.4% 89.0% 32 0.4% std::__insertion_sort
|
||||
```
|
||||
@@ -0,0 +1,45 @@
|
||||
# 背景
|
||||
|
||||
云平台部把使用ubrpc的模块改造为使用brpc。由于使用了mcpack2pb的转换功能,这个模块既能被老的ubrpc client访问,也可以通过protobuf类的协议访问(baidu_std,sofa_pbrpc等)。
|
||||
|
||||
原有使用43台机器(对ubrpc也有富余),brpc使用3台机器即可(此时访问redis的io达到瓶颈)。当前流量4w qps,支持流量增长,考虑跨机房冗余,避免redis和vip瓶颈,brpc实际使用8台机器提供服务。
|
||||
|
||||
brpc改造后的connecter收益明显,可以用较少的机器提供更优质的服务。收益分3个方面:
|
||||
|
||||
# 相同配置的机器qps和latency的比较
|
||||
|
||||
通过逐渐缩容,不断增加connecter的压力,获得单机qps和latency的对应数据如下:
|
||||

|
||||
|
||||
机器配置:cpu: 24 Intel(R) Xeon(R) CPU E5645 @ 2.40GHz || mem: 64G
|
||||
|
||||
混布情况:同机部署了逻辑层2.0/3.0和C逻辑层,均有流量
|
||||
|
||||
图中可以看到随着压力的增大:
|
||||
* brpc的延时,增加微乎其微,提供了较为一致的延时体验
|
||||
* ubrpc的延时,快速增大,到了6000~8000qps的时候,出现*queue full*,服务不可用。
|
||||
|
||||
# 不同配置机器qps和延时的比较
|
||||
qps固定为6500,观察延时。
|
||||
|
||||
| 机器名称 | 略 | 略 |
|
||||
| ----- | ---------------------------------------- | ---------------------------------------- |
|
||||
| cpu | 24 Intel(R) Xeon(R) CPU E5645 @ 2.40GHz | 24 Intel(R) Xeon(R) CPU E5-2620 0 @ 2.00GHz |
|
||||
| ubrpc | 8363.46(us) | 12649.5(us) |
|
||||
| brpc | 3364.66(us) | 3382.15(us) |
|
||||
|
||||
有此可见:
|
||||
|
||||
* ubrpc在不同配置下性能表现差异大,在配置较低的机器下表现较差。
|
||||
* brpc表现的比ubrpc好,在较低配置的机器上也能有好的表现,因机器不同带来的差异不大。
|
||||
|
||||
# 相同配置机器idle分布的比较
|
||||
|
||||
机器配置:cpu: 24 Intel(R) Xeon(R) CPU E5645 @ 2.40GHz || mem:64G
|
||||
|
||||

|
||||
|
||||
在线上缩容 不断增大压力过程中:
|
||||
|
||||
* ubrpc cpu idle分布在35%~60%,在55%最集中,最低30%;
|
||||
* brpc cpu idle分布在60%~85%,在75%最集中,最低50%; brpc比ubrpc对cpu的消耗低。
|
||||
@@ -0,0 +1,65 @@
|
||||
# 熔断功能
|
||||
当我们发起一个rpc之后,brpc首先会从命名服务(naming service)拿到一个可用节点列表,之后根据负载均衡策略挑选出一个节点作为实际访问的节点。当某个节点出现故障时,brpc能够自动将它从可用节点列表中剔除,并周期性的对故障节点进行健康检查。
|
||||
|
||||
# 默认的熔断策略
|
||||
brpc 默认会提供一个简单的熔断策略,在的默认的熔断策略下,brpc若检测到某个节点无法建立连接,则会将该节点熔断。当某一次rpc返回以下错误时,brpc会认为目标节点无法建立连接:ECONNREFUSED 、ENETUNREACH、EHOSTUNREACH、EINVAL。
|
||||
|
||||
这里需要指出的是,假如brpc发现某个节点出现连续三次连接超时(而不是rpc超时),那么也会把第三次超时当做ENETUNREACH来处理。所以假如rpc的超时时间设置的比连接超时更短,那么当节点无法建立连接时,rpc超时会比连接超时更早触发,最终导致永远触发不了熔断。所以在自定义超时时间时,需要保证rpc的超时时间大于连接超时时间。即ChannelOptions.timeout_ms > ChannelOptions.connect_timeout_ms。
|
||||
|
||||
默认的熔断策略是一直开启的,并不需要做任何配置,也无法关闭。
|
||||
|
||||
# 可选的熔断策略
|
||||
仅仅依赖上述默认的熔断策略有时候并不能完全满足需求,举个极端的例子: 假如某个下游节点逻辑线程全部卡死,但是io线程能够正常工作,那么所有的请求都会超时,但是tcp连接却能够正常建立。对于这类情况,brpc在默认的熔断策略的基础上,提供了更加激进的熔断策略。开启之后brpc会根据出错率来判断节点是否处于故障状态。
|
||||
|
||||
## 开启方法
|
||||
可选的熔断策略默认是关闭的,用户可以根据实际需要在ChannelOptions中开启:
|
||||
```
|
||||
brpc::ChannelOptions option;
|
||||
option.enable_circuit_breaker = true;
|
||||
```
|
||||
|
||||
## 工作原理
|
||||
可选的熔断由CircuitBreaker实现,在开启了熔断之后,CircuitBreaker会记录每一个请求的处理结果,并维护一个累计出错时长,记为acc_error_cost,当acc_error_cost > max_error_cost时,熔断该节点。
|
||||
|
||||
**每次请求返回成功之后,更新max_error_cost:**
|
||||
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则指该次请求的耗时。
|
||||
|
||||
**每次请求返回之后,都会更新acc_error_cost:**
|
||||
1. 如果请求处理成功,则令 acc_error_cost = alpha * acc_error_cost
|
||||
2. 如果请求处理失败,则令 acc_error_cost = acc_error_cost + min(latency, ema_latency * 2)
|
||||
|
||||
|
||||
上面的alpha与计算max_error_cost所用到的alpha为同一个值。考虑到出现超时等错误时,latency往往会远远大于ema_latency。所以在计算acc_error_cost时对失败请求的latency进行了修正,使其值不超过ema_latency的两倍。这个倍率同样可以通过gflag配置。
|
||||
|
||||
|
||||
**根据实际需要配置熔断参数:**
|
||||
|
||||
为了允许某个节点在短时间内抖动,同时又能够剔除长期错误率较高的节点,CircuitBreaker同时维护了长短两个窗口,长窗口阈值较低,短窗口阈值较高。长窗口的主要作用是剔除那些长期错误率较高的服务。我们可以根据实际的qps及对于错误的容忍程度来调整circuit_breaker_long_window_size及circuit_breaker_long_window_error_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。
|
||||
|
||||
由于计算EMA需要积累一定量的数据,在熔断的初始阶段(即目前已经收集到的请求 < 窗口大小),会直接使用错误数量来判定是否该熔断,即:若 acc_error_count > window_size * max_error_rate 为真,则进行熔断。
|
||||
|
||||
## 熔断的范围
|
||||
brpc在决定熔断某个节点时,会熔断掉整个连接,即:
|
||||
1. 假如我们使用pooled模式,那么会熔断掉所有的连接。
|
||||
2. brpc的tcp连接是会被channel所共享的,当某个连接被熔断之后,所有的channel都不能再使用这个故障的连接。
|
||||
3. 假如想要避免2中所述的情况,可以通过设置ChannelOptions.connection_group将channel放进不同的ConnectionGroup,不同ConnectionGroup的channel并不会共享连接。
|
||||
|
||||
## 熔断数据的收集
|
||||
只有通过开启了enable_circuit_breaker的channel发送的请求,才会将请求的处理结果提交到CircuitBreaker。所以假如我们决定对下游某个服务开启可选的熔断策略,最好是在所有的连接到该服务的channel里都开启enable_circuit_breaker。
|
||||
|
||||
## 熔断的恢复
|
||||
目前brpc使用通用的健康检查来判定某个节点是否已经恢复,即只要能够建立tcp连接则认为该节点已经恢复。为了能够正确的摘除那些能够建立tcp连接的故障节点,每次熔断之后会先对故障节点进行一段时间的隔离,隔离期间故障节点即不会被lb选中,也不会进行健康检查。若节点在短时间内被连续熔断,则隔离时间翻倍。初始的隔离时间为100ms,最大的隔离时间和判断两次熔断是否为连续熔断的时间间隔都使用circuit_breaker_max_isolation_duration_ms控制,默认为30秒。
|
||||
|
||||
## 数据体现
|
||||
节点的熔断次数、最近一次从熔断中恢复之后的累积错误数都可以在监控页面的/connections里找到,即便我们没有开启可选的熔断策略,brpc也会对这些数据进行统计。nBreak表示进程启动之后该节点的总熔断次数,RecentErr则表示该节点最近一次从熔断中恢复之后,累计的出错请求数。
|
||||
|
||||
由于brpc默认熔断策略是一直开启的,即便我们没有开启可选的熔断策略,nBreak还是可能会大于0,这时nBreak通常是因为tcp连接建立失败而产生的。
|
||||
|
||||
Executable
+1028
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,509 @@
|
||||
[English version](../en/combo_channel.md)
|
||||
|
||||
随着服务规模的增大,对下游的访问流程会越来越复杂,其中往往包含多个同时发起的RPC或有复杂的层次结构。但这类代码的多线程陷阱很多,用户可能写出了bug也不自知,复现和调试也比较困难。而且实现要么只能支持同步的情况,要么得为异步重写一套。以"在多个异步RPC完成后运行一些代码"为例,它的同步实现一般是异步地发起多个RPC,然后逐个等待各自完成;它的异步实现一般是用一个带计数器的回调,每当一个RPC完成时计数器减一,直到0时调用回调。可以看到它的缺点:
|
||||
|
||||
- 同步和异步代码不一致。用户无法轻易地从一个模式转为另一种模式。从设计的角度,不一致暗示了没有抓住本质。
|
||||
- 往往不能被取消。正确及时地取消一个操作不是一件易事,何况是组合访问。但取消对于终结无意义的等待是很必要的。
|
||||
- 不能继续组合。比如你很难把一个上述实现变成“更大"的访问模式的一部分。换个场景还得重写一套。
|
||||
|
||||
我们需要更好的抽象。如果我们能以不同的方式把一些Channel组合为更大的Channel,并把不同的访问模式置入其中,那么用户可以便用统一接口完成同步、异步、取消等操作。这种channel在brpc中被称为组合channel。
|
||||
|
||||
# ParallelChannel
|
||||
|
||||
ParallelChannel (有时被称为“pchan”)同时访问其包含的sub channel,并合并它们的结果。用户可通过CallMapper修改请求,通过ResponseMerger合并结果。ParallelChannel看起来就像是一个Channel:
|
||||
|
||||
- 支持同步和异步访问。
|
||||
- 发起异步操作后可以立刻删除。
|
||||
- 可以取消。
|
||||
- 支持超时。
|
||||
|
||||
示例代码见[example/parallel_echo_c++](https://github.com/apache/brpc/tree/master/example/parallel_echo_c++/)。
|
||||
|
||||
任何brpc::ChannelBase的子类都可以加入ParallelChannel,包括ParallelChannel和其他组合Channel。
|
||||
|
||||
用户可以设置ParallelChannelOptions.fail_limit来控制访问的最大失败次数,当失败的访问达到这个数目时,RPC会立刻结束而不等待超时。
|
||||
|
||||
用户可以设置ParallelChannelOptions.success_limit来控制访问的最大成功次数,当成功的访问达到这个数目时,RPC会立刻结束。ParallelChannelOptions.fail_limit的优先级高于ParallelChannelOptions.success_limit,只有未设置fail_limit时,success_limit才会生效。
|
||||
|
||||
一个sub channel可多次加入同一个ParallelChannel。当你需要对同一个服务发起多次异步访问并等待它们完成的话,这很有用。
|
||||
|
||||
ParallelChannel的内部结构大致如下:
|
||||
|
||||

|
||||
|
||||
## 插入sub channel
|
||||
|
||||
可通过如下接口把sub channel插入ParallelChannel:
|
||||
|
||||
```c++
|
||||
int AddChannel(brpc::ChannelBase* sub_channel,
|
||||
ChannelOwnership ownership,
|
||||
CallMapper* call_mapper,
|
||||
ResponseMerger* response_merger);
|
||||
```
|
||||
|
||||
当ownership为brpc::OWNS_CHANNEL时,sub_channel会在ParallelChannel析构时被删除。一个sub channel可能会多次加入一个ParallelChannel,如果其中一个指明了ownership为brpc::OWNS_CHANNEL,那个sub channel会在ParallelChannel析构时被最多删除一次。
|
||||
|
||||
访问ParallelChannel时调用AddChannel是**线程不安全**的。
|
||||
|
||||
## CallMapper
|
||||
|
||||
用于把对ParallelChannel的调用转化为对sub channel的调用。如果call_mapper是NULL,sub channel的请求就是ParallelChannel的请求,而response则New()自ParallelChannel的response。如果call_mapper不为NULL,则会在ParallelChannel析构时被删除。call_mapper内含引用计数,一个call_mapper可与多个sub channel关联。
|
||||
|
||||
```c++
|
||||
class CallMapper {
|
||||
public:
|
||||
virtual ~CallMapper();
|
||||
|
||||
virtual SubCall Map(int channel_index/*starting from 0*/,
|
||||
int channel_count,
|
||||
const google::protobuf::MethodDescriptor* method,
|
||||
const google::protobuf::Message* request,
|
||||
google::protobuf::Message* response) = 0;
|
||||
|
||||
virtual void MapController(int channel_index/*starting from 0*/, int channel_count,
|
||||
const Controller* main_cntl, Controller* sub_cntl);
|
||||
};
|
||||
```
|
||||
### Map
|
||||
|
||||
channel_index:该sub channel在ParallelChannel中的位置,从0开始计数。
|
||||
|
||||
channel_count:ParallelChannel中sub channel的数量。
|
||||
|
||||
method/request/response:ParallelChannel.CallMethod()的参数。
|
||||
|
||||
返回的SubCall被用于访问对应sub channel,SubCall有两个特殊值:
|
||||
|
||||
- 返回SubCall::Bad()则对ParallelChannel的该次访问立刻失败,Controller.ErrorCode()为EREQUEST。
|
||||
- 返回SubCall::Skip()则跳过对该sub channel的访问,如果所有的sub channel都被跳过了,该次访问立刻失败,Controller.ErrorCode()为ECANCELED。
|
||||
|
||||
常见的Map()实现有:
|
||||
|
||||
- 广播request。这也是call_mapper为NULL时的行为:
|
||||
```c++
|
||||
class Broadcaster : public CallMapper {
|
||||
public:
|
||||
SubCall Map(int channel_index/*starting from 0*/,
|
||||
const google::protobuf::MethodDescriptor* method,
|
||||
const google::protobuf::Message* request,
|
||||
google::protobuf::Message* response) {
|
||||
// method/request和pchan保持一致.
|
||||
// response是new出来的,最后的flag告诉pchan在RPC结束后删除Response。
|
||||
return SubCall(method, request, response->New(), DELETE_RESPONSE);
|
||||
}
|
||||
};
|
||||
```
|
||||
- 修改request中的字段后再发。
|
||||
```c++
|
||||
class ModifyRequest : public CallMapper {
|
||||
public:
|
||||
SubCall Map(int channel_index/*starting from 0*/,
|
||||
const google::protobuf::MethodDescriptor* method,
|
||||
const google::protobuf::Message* request,
|
||||
google::protobuf::Message* response) {
|
||||
FooRequest* copied_req = brpc::Clone<FooRequest>(request);
|
||||
copied_req->set_xxx(...);
|
||||
// 拷贝并修改request,最后的flag告诉pchan在RPC结束后删除Request和Response。
|
||||
return SubCall(method, copied_req, response->New(), DELETE_REQUEST | DELETE_RESPONSE);
|
||||
}
|
||||
};
|
||||
```
|
||||
- request和response已经包含了sub request/response,直接取出来。
|
||||
```c++
|
||||
class UseFieldAsSubRequest : public CallMapper {
|
||||
public:
|
||||
SubCall Map(int channel_index/*starting from 0*/,
|
||||
const google::protobuf::MethodDescriptor* method,
|
||||
const google::protobuf::Message* request,
|
||||
google::protobuf::Message* response) {
|
||||
if (channel_index >= request->sub_request_size()) {
|
||||
// sub_request不够,说明外面准备数据的地方和pchan中sub channel的个数不符.
|
||||
// 返回Bad()让该次访问立刻失败
|
||||
return SubCall::Bad();
|
||||
}
|
||||
// 取出对应的sub request,增加一个sub response,最后的flag为0告诉pchan什么都不用删
|
||||
return SubCall(sub_method, request->sub_request(channel_index), response->add_sub_response(), 0);
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### MapController
|
||||
|
||||
channel_index:该sub channel在ParallelChannel中的位置,从0开始计数。
|
||||
|
||||
channel_count:ParallelChannel中sub channel的数量。
|
||||
|
||||
main_cntl:ParallelChannel.CallMethod()的参数。
|
||||
|
||||
sub_cntl:sub channel的请求对应的controller。默认实现:拷贝main_cntl的http_request和request_attachment到sub_cntl中。
|
||||
|
||||
注意:修改ClientSettings相关配置(如超时、重试等)是无效的,因为所有sub_cntl都是使用main_cntl的ClientSettings配置。
|
||||
|
||||
## ResponseMerger
|
||||
|
||||
response_merger把sub channel的response合并入总的response,其为NULL时,则使用response->MergeFrom(*sub_response),MergeFrom的行为可概括为“除了合并repeated字段,其余都是覆盖”。如果你需要更复杂的行为,则需实现ResponseMerger。response_merger是一个个执行的,所以你并不需要考虑多个Merge同时运行的情况。response_merger在ParallelChannel析构时被删除。response_merger内含引用计数,一个response_merger可与多个sub channel关联。
|
||||
|
||||
Result的取值有:
|
||||
- MERGED: 成功合并。
|
||||
- FAIL: sub_response没有合并成功,会被记作一次失败。比如有10个sub channels且fail_limit为4,只要有4个合并结果返回了FAIL,这次RPC就会达到fail_limit并立刻结束。
|
||||
- FAIL_ALL: 使本次RPC直接结束。
|
||||
|
||||
|
||||
## 获得访问sub channel时的controller
|
||||
|
||||
有时访问者需要了解访问sub channel时的细节,通过Controller.sub(i)可获得访问sub channel的controller.
|
||||
|
||||
```c++
|
||||
// Get the controllers for accessing sub channels in combo channels.
|
||||
// Ordinary channel:
|
||||
// sub_count() is 0 and sub() is always NULL.
|
||||
// ParallelChannel/PartitionChannel:
|
||||
// sub_count() is #sub-channels and sub(i) is the controller for
|
||||
// accessing i-th sub channel inside ParallelChannel, if i is outside
|
||||
// [0, sub_count() - 1], sub(i) is NULL.
|
||||
// NOTE: You must test sub() against NULL, ALWAYS. Even if i is inside
|
||||
// range, sub(i) can still be NULL:
|
||||
// * the rpc call may fail and terminate before accessing the sub channel
|
||||
// * the sub channel was skipped
|
||||
// SelectiveChannel/DynamicPartitionChannel:
|
||||
// sub_count() is always 1 and sub(0) is the controller of successful
|
||||
// or last call to sub channels.
|
||||
int sub_count() const;
|
||||
const Controller* sub(int index) const;
|
||||
```
|
||||
|
||||
# SelectiveChannel
|
||||
|
||||
[SelectiveChannel](https://github.com/apache/brpc/blob/master/src/brpc/selective_channel.h) (有时被称为“schan”)按负载均衡算法访问其包含的Channel,相比普通Channel它更加高层:把流量分给sub channel,而不是具体的Server。SelectiveChannel主要用来支持机器组之间的负载均衡,它具备Channel的主要属性:
|
||||
|
||||
- 支持同步和异步访问。
|
||||
- 发起异步操作后可以立刻删除。
|
||||
- 可以取消。
|
||||
- 支持超时。
|
||||
|
||||
示例代码见[example/selective_echo_c++](https://github.com/apache/brpc/tree/master/example/selective_echo_c++/)。
|
||||
|
||||
任何brpc::ChannelBase的子类都可加入SelectiveChannel,包括SelectiveChannel和其他组合Channel。
|
||||
|
||||
SelectiveChannel的重试独立于其中的sub channel,当SelectiveChannel访问某个sub channel失败后(本身可能重试),它会重试另外一个sub channel。
|
||||
|
||||
目前SelectiveChannel要求**request必须在RPC结束前有效**,其他channel没有这个要求。如果你使用SelectiveChannel发起异步操作,确保request在done中才被删除。
|
||||
|
||||
## 使用SelectiveChannel
|
||||
|
||||
SelectiveChannel的初始化和普通Channel基本一样,但Init不需要指定命名服务,因为SelectiveChannel通过AddChannel动态添加sub channel,而普通Channel通过命名服务动态管理server。
|
||||
|
||||
```c++
|
||||
#include <brpc/selective_channel.h>
|
||||
...
|
||||
brpc::SelectiveChannel schan;
|
||||
brpc::ChannelOptions schan_options;
|
||||
schan_options.timeout_ms = ...;
|
||||
schan_options.backup_request_ms = ...;
|
||||
schan_options.max_retry = ...;
|
||||
if (schan.Init(load_balancer, &schan_options) != 0) {
|
||||
LOG(ERROR) << "Fail to init SelectiveChannel";
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
初始化完毕后通过AddChannel加入sub channel。
|
||||
|
||||
```c++
|
||||
if (schan.AddChannel(sub_channel, NULL/*ChannelHandle*/) != 0) { // 第二个参数ChannelHandle用于删除sub channel,不用删除可填NULL
|
||||
LOG(ERROR) << "Fail to add sub_channel";
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
注意:
|
||||
|
||||
- 和ParallelChannel不同,SelectiveChannel的AddChannel可在任意时刻调用,即使该SelectiveChannel正在被访问(下一次访问时生效)
|
||||
- SelectiveChannel总是own sub channel,这和ParallelChannel可选择ownership是不同的。
|
||||
- 如果AddChannel第二个参数不为空,会填入一个类型为brpc::SelectiveChannel::ChannelHandle的值,这个handle可作为RemoveAndDestroyChannel的参数来动态删除一个channel。
|
||||
- SelectiveChannel会用自身的超时覆盖sub channel初始化时指定的超时。比如某个sub channel的超时为100ms,SelectiveChannel的超时为500ms,实际访问时的超时是500ms。
|
||||
|
||||
访问SelectiveChannel的方式和普通Channel是一样的。
|
||||
|
||||
## 例子: 往多个命名服务分流
|
||||
|
||||
一些场景中我们需要向多个命名服务下的机器分流,原因可能有:
|
||||
|
||||
- 完成同一个检索功能的机器被挂载到了不同的命名服务下。
|
||||
- 机器被拆成了多个组,流量先分流给一个组,再分流到组内机器。组间的分流方式和组内有所不同。
|
||||
|
||||
这都可以通过SelectiveChannel完成。
|
||||
|
||||
下面的代码创建了一个SelectiveChannel,并插入三个访问不同bns的普通Channel。
|
||||
|
||||
```c++
|
||||
brpc::SelectiveChannel channel;
|
||||
brpc::ChannelOptions schan_options;
|
||||
schan_options.timeout_ms = FLAGS_timeout_ms;
|
||||
schan_options.max_retry = FLAGS_max_retry;
|
||||
if (channel.Init("c_murmurhash", &schan_options) != 0) {
|
||||
LOG(ERROR) << "Fail to init SelectiveChannel";
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
brpc::Channel* sub_channel = new brpc::Channel;
|
||||
if (sub_channel->Init(ns_node_name[i], "rr", NULL) != 0) {
|
||||
LOG(ERROR) << "Fail to init sub channel " << i;
|
||||
return -1;
|
||||
}
|
||||
if (channel.AddChannel(sub_channel, NULL/*handle for removal*/) != 0) {
|
||||
LOG(ERROR) << "Fail to add sub_channel to channel";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
...
|
||||
XXXService_Stub stub(&channel);
|
||||
stub.FooMethod(&cntl, &request, &response, NULL);
|
||||
...
|
||||
```
|
||||
|
||||
# PartitionChannel
|
||||
|
||||
[PartitionChannel](https://github.com/apache/brpc/blob/master/src/brpc/partition_channel.h)是特殊的ParallelChannel,它会根据命名服务中的tag自动建立对应分库的sub channel。这样用户就可以把所有的分库机器挂在一个命名服务内,通过tag来指定哪台机器对应哪个分库。示例代码见[example/partition_echo_c++](https://github.com/apache/brpc/tree/master/example/partition_echo_c++/)。
|
||||
|
||||
ParititonChannel只能处理一种分库方法,当用户需要多种分库方法共存,或从一个分库方法平滑地切换为另一种分库方法时,可以使用DynamicPartitionChannel,它会根据不同的分库方式动态地建立对应的sub PartitionChannel,并根据容量把请求分配给不同的分库。示例代码见[example/dynamic_partition_echo_c++](https://github.com/apache/brpc/tree/master/example/dynamic_partition_echo_c++/)。
|
||||
|
||||
如果分库在不同的命名服务内,那么用户得自行用ParallelChannel组装,即每个sub channel对应一个分库(使用不同的命名服务)。ParellelChannel的使用方法见[上面](#ParallelChannel)。
|
||||
|
||||
## 使用PartitionChannel
|
||||
|
||||
首先定制PartitionParser。这个例子中tag的形式是N/M,N代表分库的index,M是分库的个数。比如0/3代表一共3个分库,这是第一个。
|
||||
|
||||
```c++
|
||||
#include <brpc/partition_channel.h>
|
||||
...
|
||||
class MyPartitionParser : public brpc::PartitionParser {
|
||||
public:
|
||||
bool ParseFromTag(const std::string& tag, brpc::Partition* out) {
|
||||
// "N/M" : #N partition of M partitions.
|
||||
size_t pos = tag.find_first_of('/');
|
||||
if (pos == std::string::npos) {
|
||||
LOG(ERROR) << "Invalid tag=" << tag;
|
||||
return false;
|
||||
}
|
||||
char* endptr = NULL;
|
||||
out->index = strtol(tag.c_str(), &endptr, 10);
|
||||
if (endptr != tag.data() + pos) {
|
||||
LOG(ERROR) << "Invalid index=" << butil::StringPiece(tag.data(), pos);
|
||||
return false;
|
||||
}
|
||||
out->num_partition_kinds = strtol(tag.c_str() + pos + 1, &endptr, 10);
|
||||
if (endptr != tag.c_str() + tag.size()) {
|
||||
LOG(ERROR) << "Invalid num=" << tag.data() + pos + 1;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
然后初始化PartitionChannel。
|
||||
|
||||
```c++
|
||||
#include <brpc/partition_channel.h>
|
||||
...
|
||||
brpc::PartitionChannel channel;
|
||||
|
||||
brpc::PartitionChannelOptions options;
|
||||
options.protocol = ...; // PartitionChannelOptions继承了ChannelOptions,后者有的前者也有
|
||||
options.timeout_ms = ...; // 同上
|
||||
options.fail_limit = 1; // PartitionChannel自己的选项,意思同ParalellChannel中的fail_limit。这里为1的意思是只要有1个分库访问失败,这次RPC就失败了。
|
||||
|
||||
if (channel.Init(num_partition_kinds, new MyPartitionParser(),
|
||||
server_address, load_balancer, &options) != 0) {
|
||||
LOG(ERROR) << "Fail to init PartitionChannel";
|
||||
return -1;
|
||||
}
|
||||
// 访问方法和普通Channel是一样的
|
||||
```
|
||||
|
||||
## 使用DynamicPartitionChannel
|
||||
|
||||
DynamicPartitionChannel的使用方法和PartitionChannel基本上是一样的,先定制PartitionParser再初始化,但Init时不需要num_partition_kinds,因为DynamicPartitionChannel会为不同的分库方法动态建立不同的sub PartitionChannel。
|
||||
|
||||
下面演示一下使用DynamicPartitionChannel平滑地从3库变成4库。
|
||||
|
||||
首先分别在8004, 8005, 8006端口启动三个server。
|
||||
|
||||
```
|
||||
$ ./echo_server -server_num 3
|
||||
TRACE: 09-06 10:40:39: * 0 server.cpp:159] EchoServer is serving on port=8004
|
||||
TRACE: 09-06 10:40:39: * 0 server.cpp:159] EchoServer is serving on port=8005
|
||||
TRACE: 09-06 10:40:39: * 0 server.cpp:159] EchoServer is serving on port=8006
|
||||
TRACE: 09-06 10:40:40: * 0 server.cpp:192] S[0]=0 S[1]=0 S[2]=0 [total=0]
|
||||
TRACE: 09-06 10:40:41: * 0 server.cpp:192] S[0]=0 S[1]=0 S[2]=0 [total=0]
|
||||
TRACE: 09-06 10:40:42: * 0 server.cpp:192] S[0]=0 S[1]=0 S[2]=0 [total=0]
|
||||
```
|
||||
|
||||
启动后每个Server每秒会打印上一秒收到的流量,目前都是0。
|
||||
|
||||
在本地启动使用DynamicPartitionChannel的Client,初始化代码如下:
|
||||
|
||||
```c++
|
||||
...
|
||||
brpc::DynamicPartitionChannel channel;
|
||||
brpc::PartitionChannelOptions options;
|
||||
// 访问任何分库失败都认为RPC失败。调大这个数值可以使访问更宽松,比如等于2的话表示至少两个分库失败才算失败。
|
||||
options.fail_limit = 1;
|
||||
if (channel.Init(new MyPartitionParser(), "file://server_list", "rr", &options) != 0) {
|
||||
LOG(ERROR) << "Fail to init channel";
|
||||
return -1;
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
命名服务"file://server_list"的内容是:
|
||||
```
|
||||
0.0.0.0:8004 0/3 # 表示3分库中的第一个分库,其他依次类推
|
||||
0.0.0.0:8004 1/3
|
||||
0.0.0.0:8004 2/3
|
||||
```
|
||||
|
||||
3分库方案的3个库都在8004端口对应的server上。启动Client后Client发现了8004,并向其发送流量。
|
||||
|
||||
```
|
||||
$ ./echo_client
|
||||
TRACE: 09-06 10:51:10: * 0 src/brpc/policy/file_naming_service.cpp:83] Got 3 unique addresses from `server_list'
|
||||
TRACE: 09-06 10:51:10: * 0 src/brpc/socket.cpp:779] Connected to 0.0.0.0:8004 via fd=3 SocketId=0 self_port=46544
|
||||
TRACE: 09-06 10:51:11: * 0 client.cpp:226] Sending EchoRequest at qps=132472 latency=371
|
||||
TRACE: 09-06 10:51:12: * 0 client.cpp:226] Sending EchoRequest at qps=132658 latency=370
|
||||
TRACE: 09-06 10:51:13: * 0 client.cpp:226] Sending EchoRequest at qps=133208 latency=369
|
||||
```
|
||||
|
||||
同时Server端收到了3倍的流量:因为访问一次Client端要访问三次8004,分别对应每个分库。
|
||||
|
||||
```
|
||||
TRACE: 09-06 10:51:11: * 0 server.cpp:192] S[0]=398866 S[1]=0 S[2]=0 [total=398866]
|
||||
TRACE: 09-06 10:51:12: * 0 server.cpp:192] S[0]=398117 S[1]=0 S[2]=0 [total=398117]
|
||||
TRACE: 09-06 10:51:13: * 0 server.cpp:192] S[0]=398873 S[1]=0 S[2]=0 [total=398873]
|
||||
```
|
||||
|
||||
开始修改分库,在server_list中加入4分库的8005:
|
||||
|
||||
```
|
||||
0.0.0.0:8004 0/3
|
||||
0.0.0.0:8004 1/3
|
||||
0.0.0.0:8004 2/3
|
||||
|
||||
0.0.0.0:8005 0/4
|
||||
0.0.0.0:8005 1/4
|
||||
0.0.0.0:8005 2/4
|
||||
0.0.0.0:8005 3/4
|
||||
```
|
||||
|
||||
观察Client和Server的输出变化。Client端发现了server_list的变化并重新载入,但qps并没有什么变化。
|
||||
|
||||
```
|
||||
TRACE: 09-06 10:57:10: * 0 src/brpc/policy/file_naming_service.cpp:83] Got 7 unique addresses from `server_list'
|
||||
TRACE: 09-06 10:57:10: * 0 src/brpc/socket.cpp:779] Connected to 0.0.0.0:8005 via fd=7 SocketId=768 self_port=39171
|
||||
TRACE: 09-06 10:57:11: * 0 client.cpp:226] Sending EchoRequest at qps=135346 latency=363
|
||||
TRACE: 09-06 10:57:12: * 0 client.cpp:226] Sending EchoRequest at qps=134201 latency=366
|
||||
TRACE: 09-06 10:57:13: * 0 client.cpp:226] Sending EchoRequest at qps=137627 latency=356
|
||||
TRACE: 09-06 10:57:14: * 0 client.cpp:226] Sending EchoRequest at qps=136775 latency=359
|
||||
TRACE: 09-06 10:57:15: * 0 client.cpp:226] Sending EchoRequest at qps=139043 latency=353
|
||||
```
|
||||
|
||||
server端的变化比较大。8005收到了流量,并且和8004的流量比例关系约为4:3。
|
||||
|
||||
```
|
||||
TRACE: 09-06 10:57:09: * 0 server.cpp:192] S[0]=398597 S[1]=0 S[2]=0 [total=398597]
|
||||
TRACE: 09-06 10:57:10: * 0 server.cpp:192] S[0]=392839 S[1]=0 S[2]=0 [total=392839]
|
||||
TRACE: 09-06 10:57:11: * 0 server.cpp:192] S[0]=334704 S[1]=83219 S[2]=0 [total=417923]
|
||||
TRACE: 09-06 10:57:12: * 0 server.cpp:192] S[0]=206215 S[1]=273873 S[2]=0 [total=480088]
|
||||
TRACE: 09-06 10:57:13: * 0 server.cpp:192] S[0]=204520 S[1]=270483 S[2]=0 [total=475003]
|
||||
TRACE: 09-06 10:57:14: * 0 server.cpp:192] S[0]=207055 S[1]=273725 S[2]=0 [total=480780]
|
||||
TRACE: 09-06 10:57:15: * 0 server.cpp:192] S[0]=208453 S[1]=276803 S[2]=0 [total=485256]
|
||||
```
|
||||
|
||||
一次RPC要访问三次8004或四次8005,8004和8005流量比是3:4,说明Client以1:1的比例访问了3分库和4分库。这个比例关系取决于其容量。容量的计算是递归的:
|
||||
|
||||
- 普通Channel的容量等于它其中所有server的容量之和。如果命名服务没有配置权值,单个server的容量为1。
|
||||
- ParallelChannel或PartitionChannel的容量等于它其中Sub Channel容量的最小值。
|
||||
- SelectiveChannel的容量等于它其中Sub Channel的容量之和。
|
||||
- DynamicPartitionChannel的容量等于它其中Sub PartitionChannel的容量之和。
|
||||
|
||||
在这儿的场景中,3分库和4分库的容量都是1,因为所有的3库都在8004一台server上,所有的4库都在8005一台server上。
|
||||
|
||||
在4分库方案加入加入8006端口的server:
|
||||
|
||||
```
|
||||
0.0.0.0:8004 0/3
|
||||
0.0.0.0:8004 1/3
|
||||
0.0.0.0:8004 2/3
|
||||
|
||||
0.0.0.0:8005 0/4
|
||||
0.0.0.0:8005 1/4
|
||||
0.0.0.0:8005 2/4
|
||||
0.0.0.0:8005 3/4
|
||||
|
||||
0.0.0.0:8006 0/4
|
||||
0.0.0.0:8006 1/4
|
||||
0.0.0.0:8006 2/4
|
||||
0.0.0.0:8006 3/4
|
||||
```
|
||||
|
||||
Client的变化仍旧不大:
|
||||
|
||||
```
|
||||
TRACE: 09-06 11:11:51: * 0 src/brpc/policy/file_naming_service.cpp:83] Got 11 unique addresses from `server_list'
|
||||
TRACE: 09-06 11:11:51: * 0 src/brpc/socket.cpp:779] Connected to 0.0.0.0:8006 via fd=8 SocketId=1280 self_port=40759
|
||||
TRACE: 09-06 11:11:51: * 0 client.cpp:226] Sending EchoRequest at qps=131799 latency=372
|
||||
TRACE: 09-06 11:11:52: * 0 client.cpp:226] Sending EchoRequest at qps=136217 latency=361
|
||||
TRACE: 09-06 11:11:53: * 0 client.cpp:226] Sending EchoRequest at qps=133531 latency=368
|
||||
TRACE: 09-06 11:11:54: * 0 client.cpp:226] Sending EchoRequest at qps=136072 latency=361
|
||||
```
|
||||
|
||||
Server端可以看到8006收到了流量。三台server的流量比例约为3:4:4。这是因为3分库的容量仍为1,而4分库由于8006的加入变成了2。3分库和4分库的流量比例是3:8。4分库中的每个分库在8005和8006上都有实例,同一个分库的不同实例使用round robin分流,所以8005和8006平摊了流量。最后的效果就是3:4:4。
|
||||
|
||||
```
|
||||
TRACE: 09-06 11:11:51: * 0 server.cpp:192] S[0]=199625 S[1]=263226 S[2]=0 [total=462851]
|
||||
TRACE: 09-06 11:11:52: * 0 server.cpp:192] S[0]=143248 S[1]=190717 S[2]=159756 [total=493721]
|
||||
TRACE: 09-06 11:11:53: * 0 server.cpp:192] S[0]=133003 S[1]=178328 S[2]=178325 [total=489656]
|
||||
TRACE: 09-06 11:11:54: * 0 server.cpp:192] S[0]=135534 S[1]=180386 S[2]=180333 [total=496253]
|
||||
```
|
||||
|
||||
尝试去掉3分库中的一个分库: (你可以在file://server_list中使用#注释一行)
|
||||
|
||||
```
|
||||
0.0.0.0:8004 0/3
|
||||
0.0.0.0:8004 1/3
|
||||
#0.0.0.0:8004 2/3
|
||||
|
||||
0.0.0.0:8005 0/4
|
||||
0.0.0.0:8005 1/4
|
||||
0.0.0.0:8005 2/4
|
||||
0.0.0.0:8005 3/4
|
||||
|
||||
0.0.0.0:8006 0/4
|
||||
0.0.0.0:8006 1/4
|
||||
0.0.0.0:8006 2/4
|
||||
0.0.0.0:8006 3/4
|
||||
```
|
||||
|
||||
Client端发现了这点。
|
||||
|
||||
```
|
||||
TRACE: 09-06 11:17:47: * 0 src/brpc/policy/file_naming_service.cpp:83] Got 10 unique addresses from `server_list'
|
||||
TRACE: 09-06 11:17:47: * 0 client.cpp:226] Sending EchoRequest at qps=131653 latency=373
|
||||
TRACE: 09-06 11:17:48: * 0 client.cpp:226] Sending EchoRequest at qps=120560 latency=407
|
||||
TRACE: 09-06 11:17:49: * 0 client.cpp:226] Sending EchoRequest at qps=124100 latency=395
|
||||
TRACE: 09-06 11:17:50: * 0 client.cpp:226] Sending EchoRequest at qps=123743 latency=397
|
||||
```
|
||||
|
||||
Server端更明显,8004很快没有了流量。这是因为去掉的分库已经是3分库中最后的2/3分库,去掉后3分库的容量变为了0,导致8004分不到任何流量了。
|
||||
|
||||
```
|
||||
TRACE: 09-06 11:17:47: * 0 server.cpp:192] S[0]=130864 S[1]=174499 S[2]=174548 [total=479911]
|
||||
TRACE: 09-06 11:17:48: * 0 server.cpp:192] S[0]=20063 S[1]=230027 S[2]=230098 [total=480188]
|
||||
TRACE: 09-06 11:17:49: * 0 server.cpp:192] S[0]=0 S[1]=245961 S[2]=245888 [total=491849]
|
||||
TRACE: 09-06 11:17:50: * 0 server.cpp:192] S[0]=0 S[1]=250198 S[2]=250150 [total=500348]
|
||||
```
|
||||
|
||||
在真实的线上环境中,我们会逐渐地增加4分库的server,同时下掉3分库中的server。DynamicParititonChannel会按照每种分库方式的容量动态切分流量。当某个时刻3分库的容量变为0时,我们便平滑地把Server从3分库变为了4分库,同时并没有修改Client的代码。
|
||||
@@ -0,0 +1,52 @@
|
||||
[connections服务](http://brpc.baidu.com:8765/connections)可以查看所有的连接。一个典型的页面如下:
|
||||
|
||||
server_socket_count: 5
|
||||
|
||||
| CreatedTime | RemoteSide | SSL | Protocol | fd | BytesIn/s | In/s | BytesOut/s | Out/s | BytesIn/m | In/m | BytesOut/m | Out/m | SocketId |
|
||||
| -------------------------- | ------------------- | ---- | --------- | ---- | --------- | ---- | ---------- | ----- | --------- | ---- | ---------- | ----- | -------- |
|
||||
| 2015/09/21-21:32:09.630840 | 172.22.38.217:51379 | No | http | 19 | 1300 | 1 | 269 | 1 | 68844 | 53 | 115860 | 53 | 257 |
|
||||
| 2015/09/21-21:32:09.630857 | 172.22.38.217:51380 | No | http | 20 | 1308 | 1 | 5766 | 1 | 68884 | 53 | 129978 | 53 | 258 |
|
||||
| 2015/09/21-21:32:09.630880 | 172.22.38.217:51381 | No | http | 21 | 1292 | 1 | 1447 | 1 | 67672 | 52 | 143414 | 52 | 259 |
|
||||
| 2015/09/21-21:32:01.324587 | 127.0.0.1:55385 | No | baidu_std | 15 | 1480 | 20 | 880 | 20 | 88020 | 1192 | 52260 | 1192 | 512 |
|
||||
| 2015/09/21-21:32:01.325969 | 127.0.0.1:55387 | No | baidu_std | 17 | 4016 | 40 | 1554 | 40 | 238879 | 2384 | 92660 | 2384 | 1024 |
|
||||
|
||||
channel_socket_count: 1
|
||||
|
||||
| CreatedTime | RemoteSide | SSL | Protocol | fd | BytesIn/s | In/s | BytesOut/s | Out/s | BytesIn/m | In/m | BytesOut/m | Out/m | SocketId |
|
||||
| -------------------------- | -------------- | ---- | --------- | ---- | --------- | ---- | ---------- | ----- | --------- | ---- | ---------- | ----- | -------- |
|
||||
| 2015/09/21-21:32:01.325870 | 127.0.0.1:8765 | No | baidu_std | 16 | 1554 | 40 | 4016 | 40 | 92660 | 2384 | 238879 | 2384 | 0 |
|
||||
|
||||
channel_short_socket_count: 0
|
||||
|
||||
上述信息分为三段:
|
||||
|
||||
- 第一段是server接受(accept)的连接。
|
||||
- 第二段是server与下游的单连接(使用brpc::Channel建立),fd为-1的是虚拟连接,对应第三段中所有相同RemoteSide的连接。
|
||||
- 第三段是server与下游的短连接或连接池(pooled connections),这些连接从属于第二段中的相同RemoteSide的虚拟连接。
|
||||
|
||||
表格标题的含义:
|
||||
|
||||
- RemoteSide : 远端的ip和端口。
|
||||
- SSL:是否使用SSL加密,若为Yes的话,一般是HTTPS连接。
|
||||
- Protocol : 使用的协议,可能为baidu_std hulu_pbrpc sofa_pbrpc memcache http public_pbrpc nova_pbrpc nshead_server等。
|
||||
- fd : file descriptor(文件描述符),可能为-1。
|
||||
- BytesIn/s : 上一秒读入的字节数
|
||||
- In/s : 上一秒读入的消息数(消息是对request和response的统称)
|
||||
- BytesOut/s : 上一秒写出的字节数
|
||||
- Out/s : 上一秒写出的消息数
|
||||
- BytesIn/m: 上一分钟读入的字节数
|
||||
- In/m: 上一分钟读入的消息数
|
||||
- BytesOut/m: 上一分钟写出的字节数
|
||||
- Out/m: 上一分钟写出的消息数
|
||||
- SocketId :内部id,用于debug,用户不用关心。
|
||||
|
||||
|
||||
|
||||
典型截图分别如下所示:
|
||||
|
||||
单连接:
|
||||
|
||||
连接池:
|
||||
|
||||
短连接:
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# 概述
|
||||
|
||||
一些场景希望同样的请求尽量落到一台机器上,比如访问缓存集群时,我们往往希望同一种请求能落到同一个后端上,以充分利用其上已有的缓存,不同的机器承载不同的稳定working set。而不是随机地散落到所有机器上,那样的话会迫使所有机器缓存所有的内容,最终由于存不下形成颠簸而表现糟糕。 我们都知道hash能满足这个要求,比如当有n台服务器时,输入x总是会发送到第hash(x) % n台服务器上。但当服务器变为m台时,hash(x) % n和hash(x) % m很可能都不相等,这会使得几乎所有请求的发送目的地都发生变化,如果目的地是缓存服务,所有缓存将失效,继而对原本被缓存遮挡的数据库或计算服务造成请求风暴,触发雪崩。一致性哈希是一种特殊的哈希算法,在增加服务器时,发向每个老节点的请求中只会有一部分转向新节点,从而实现平滑的迁移。[这篇论文](http://blog.phpdr.net/wp-content/uploads/2012/08/Consistent-Hashing-and-Random-Trees.pdf)中提出了一致性hash的概念。
|
||||
|
||||
一致性hash满足以下四个性质:
|
||||
|
||||
- 平衡性 (Balance) : 每个节点被选到的概率是O(1/n)。
|
||||
- 单调性 (Monotonicity) : 当新节点加入时, 不会有请求在老节点间移动, 只会从老节点移动到新节点。当有节点被删除时,也不会影响落在别的节点上的请求。
|
||||
- 分散性 (Spread) : 当上游的机器看到不同的下游列表时(在上线时及不稳定的网络中比较常见), 同一个请求尽量映射到少量的节点中。
|
||||
- 负载 (Load) : 当上游的机器看到不同的下游列表的时候, 保证每台下游分到的请求数量尽量一致。
|
||||
|
||||
# 实现方式
|
||||
|
||||
所有server的32位hash值在32位整数值域上构成一个环(Hash Ring),环上的每个区间和一个server唯一对应,如果一个key落在某个区间内, 它就被分流到对应的server上。
|
||||
|
||||

|
||||
|
||||
当删除一个server的,它对应的区间会归属于相邻的server,所有的请求都会跑过去。当增加一个server时,它会分割某个server的区间并承载落在这个区间上的所有请求。单纯使用Hash Ring很难满足我们上节提到的属性,主要两个问题:
|
||||
|
||||
- 在机器数量较少的时候, 区间大小会不平衡。
|
||||
- 当一台机器故障的时候, 它的压力会完全转移到另外一台机器, 可能无法承载。
|
||||
|
||||
为了解决这个问题,我们为每个server计算m个hash值,从而把32位整数值域划分为n*m个区间,当key落到某个区间时,分流到对应的server上。那些额外的hash值使得区间划分更加均匀,被称为虚拟节点(Virtual Node)。当删除一个server时,它对应的m个区间会分别合入相邻的区间中,那个server上的请求会较为平均地转移到其他server上。当增加server时,它会分割m个现有区间,从对应server上分别转移一些请求过来。
|
||||
|
||||
由于节点故障和变化不常发生,我们选择了修改复杂度为O(n)的有序数组来存储hash ring,每次分流使用二分查找来选择对应的机器,由于存储是连续的,查找效率比基于平衡二叉树的实现高。线程安全性请参照[Double Buffered Data](lalb.md#doublybuffereddata)章节.
|
||||
|
||||
# 使用方式
|
||||
|
||||
我们内置了分别基于murmurhash3和md5两种hash算法的实现,使用要做两件事:
|
||||
|
||||
- 在Channel.Init 时指定*load_balancer_name*为 "c_murmurhash" 或 "c_md5"。
|
||||
- 发起rpc时通过Controller::set_request_code(uint64_t)填入请求的hash code。
|
||||
|
||||
> request的hash算法并不需要和lb的hash算法保持一致,只需要hash的值域是32位无符号整数。由于memcache默认使用md5,访问memcached集群时请选择c_md5保证兼容性,其他场景可以选择c_murmurhash以获得更高的性能和更均匀的分布。
|
||||
|
||||
# 虚拟节点个数
|
||||
|
||||
通过-chash\_num\_replicas可设置默认的虚拟节点个数,默认值为100。对于某些特殊场合,对虚拟节点个数有自定义的需求,可以通过将*load_balancer_name*加上参数replicas=<num>配置,如:
|
||||
```c++
|
||||
channel.Init("http://...", "c_murmurhash:replicas=150", &options);
|
||||
```
|
||||
@@ -0,0 +1,31 @@
|
||||
brpc可以分析花在等待锁上的时间及发生等待的函数。
|
||||
|
||||
# 开启方法
|
||||
|
||||
按需开启。无需配置,不依赖tcmalloc,不需要链接frame pointer或libunwind。如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)。
|
||||
|
||||
# 图示
|
||||
|
||||
当很多线程争抢同一把锁时,一些线程无法立刻获得锁,而必须睡眠直到某个线程退出临界区。这个争抢过程我们称之为**contention**。在多核机器上,当多个线程需要操作同一个资源却被一把锁挡住时,便无法充分发挥多个核心的并发能力。现代OS通过提供比锁更底层的同步原语,使得无竞争锁完全不需要系统调用,只是一两条wait-free,耗时10-20ns的原子操作,非常快。而锁一旦发生竞争,一些线程就要陷入睡眠,再次醒来触发了OS的调度代码,代价至少为3-5us。所以让锁尽量无竞争,让所有线程“一起飞”是需要高性能的server的永恒话题。
|
||||
|
||||
r31906后brpc支持contention profiler,可以分析在等待锁上花费了多少时间。等待过程中线程是睡着的不会占用CPU,所以contention profiler中的时间并不是cpu时间,也不会出现在[cpu profiler](cpu_profiler.md)中。cpu profiler可以抓到特别频繁的锁(以至于花费了很多cpu),但耗时真正巨大的临界区往往不是那么频繁,而无法被cpu profiler发现。**contention profiler和cpu profiler好似互补关系,前者分析等待时间(被动),后者分析忙碌时间。**还有一类由用户基于condition或sleep发起的主动等待时间,无需分析。
|
||||
|
||||
目前contention profiler支持pthread_mutex_t(非递归)和bthread_mutex_t,开启后每秒最多采集1000个竞争锁,这个数字由参数-bvar_collector_expected_per_second控制(同时影响rpc_dump)。
|
||||
|
||||
| Name | Value | Description | Defined At |
|
||||
| ---------------------------------- | ----- | ---------------------------------------- | ------------------ |
|
||||
| bvar_collector_expected_per_second | 1000 | Expected number of samples to be collected per second | bvar/collector.cpp |
|
||||
|
||||
如果一秒内竞争锁的次数N超过了1000,那么每把锁会有1000/N的概率被采集。在我们的各类测试场景中(qps在10万-60万不等)没有观察到被采集程序的性能有明显变化。
|
||||
|
||||
我们通过实际例子来看下如何使用contention profiler,点击“contention”按钮(more左侧)后就会开启默认10秒的分析过程。下图是libraft中的一个示例程序的锁状况,这个程序是3个节点复制组的leader,qps在10-12万左右。左上角的**Total seconds: 2.449**是采集时间内(10秒)在锁上花费的所有等待时间。注意是“等待”,无竞争的锁不会被采集也不会出现在下图中。顺着箭头往下走能看到每份时间来自哪些函数。
|
||||
|
||||

|
||||
|
||||
上图有点大,让我们放大一个局部看看。下图红框中的0.768是这个局部中最大的数字,它代表raft::LogManager::get_entry在等待涉及到bvar::detail::UniqueLockBase的函数上共等待了0.768秒(10秒内)。我们如果觉得这个时间不符合预期,就可以去排查代码。
|
||||
|
||||

|
||||
|
||||
点击上方的count选择框,可以查看锁的竞争次数。选择后左上角变为了**Total samples: 439026**,代表采集时间内总共的锁竞争次数(估算)。图中箭头上的数字也相应地变为了次数,而不是时间。对比同一份结果的时间和次数,可以更深入地理解竞争状况。
|
||||
|
||||

|
||||
@@ -0,0 +1,193 @@
|
||||
# C++20 协程支持
|
||||
|
||||
bRPC 支持 C++20 协程说明文档。
|
||||
|
||||
> 注:该功能是实验性的,请勿在生产环境下使用。
|
||||
|
||||
## 使用说明
|
||||
|
||||
### 适用场景
|
||||
|
||||
C++协程适用于极高并发的场景。由于bthread使用了mmap,存在系统限制,一个进程bthread数量一般最多到万级别,如果采用同步方式用一个bthread来处理一个请求,那么请求的并发度也只能到万级别。如果采用异步方式来写代码,可以达到更高的并发,但又会导致代码难以维护。这时我们就可以使用C++协程,以类似同步的方式来写代码,而达到异步的性能效果。
|
||||
|
||||
### 使用前提
|
||||
|
||||
1. 需要使用支持c++20的编译器,如gcc 11
|
||||
2. 需要编译选项中加上 `-std=c++20`
|
||||
|
||||
### 简单示例
|
||||
|
||||
以下例子显示了如何在bRPC中启动一个C++20协程,在协程中发起 RPC调用,并等待返回结果。
|
||||
|
||||
```cpp
|
||||
#include <brpc/channel.h>
|
||||
#include <brpc/coroutine.h>
|
||||
|
||||
// 协程函数的返回类型,需要是brpc::experimental::Awaitable<T>
|
||||
// T是函数返回的实际数据类型
|
||||
brpc::experimental::Awaitable<int> RpcCall(brpc::Channel& channel) {
|
||||
EchoRequest request;
|
||||
EchoResponse response;
|
||||
EchoService_Stub stub(&_channel);
|
||||
brpc::Controller cntl;
|
||||
brpc::experimental::AwaitableDone done;
|
||||
stub.Echo(&cntl, &request, &response, &done);
|
||||
// 等待RPC返回结果
|
||||
co_await done.awaitable();
|
||||
// 返回数据,注意这里用co_return而不是return
|
||||
// 因为函数返回值类型是brpc::experimental::Awaitable<int>而不是int
|
||||
co_return cntl.ErrorCode();
|
||||
}
|
||||
|
||||
brpc::experimental::Awaitable<void> CoroutineMain(const char* server) {
|
||||
brpc::Channel channel;
|
||||
channel.Init(server, NULL);
|
||||
// co_await会从Awaitable<int>得到int类型的返回值
|
||||
int code = co_await RpcCall(channel);
|
||||
printf("Rpc result:%d\n", code);
|
||||
}
|
||||
|
||||
int main() {
|
||||
// 启动协程
|
||||
brpc::experimental::Coroutine coro(CoroutineMain("127.0.0.1:8080"));
|
||||
// 等待协程执行完成
|
||||
coro.join();
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
更完整的例子可以查看源码中的`example/coroutine/coroutine_server.cpp`文件。
|
||||
|
||||
### 更多用法
|
||||
|
||||
1. 在非协程环境下等待一个协程执行完成:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Coroutine coro(func(args));
|
||||
coro.join();
|
||||
```
|
||||
|
||||
2. 在非协程环境下等待协程完成并获取返回值:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Coroutine coro(func(args)); // func的返回值类型为Awaitable<int>
|
||||
int result = coro.join<int>();
|
||||
```
|
||||
|
||||
3. 在协程环境下等待协程执行完成:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Coroutine coro(func(args));
|
||||
... // 做一些其它事情
|
||||
co_await coro.awaitable();
|
||||
```
|
||||
|
||||
4. 在协程环境下等待协程执行完成并获取返回值:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Coroutine coro(func(args)); // func的返回值类型为Awaitable<int>
|
||||
... // 做一些其它事情
|
||||
int ret = co_await coro.awaitable<int>();
|
||||
```
|
||||
|
||||
5. 在协程环境下sleep:
|
||||
```cpp
|
||||
co_await brpc::experimental::Coroutine::usleep(1000);
|
||||
```
|
||||
|
||||
### 注意事项
|
||||
|
||||
1. 协程不保证一个函数的上下文都在同一个pthread或同一个bthread下执行。在co_await之后,代码所在的pthread或bthread可能发生变化,因此依赖于pthread或bthread的线程局部变量的代码(比如rpcz功能)将无法正确工作。
|
||||
2. 不应在协程中使用阻塞bthread(如bthread_join、同步RPC)或阻塞pthread的函数,否则可能导致死锁或者长耗时。
|
||||
3. 不要在不必要的地方使用协程,如下面的代码,虽然也能正常工作,但没有意义:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Awaitable<int> inplace_func() {
|
||||
co_return 123;
|
||||
}
|
||||
```
|
||||
|
||||
### 实现极致性能
|
||||
|
||||
如果确保服务的处理代码都运行在协程之中,并且没有任何阻塞bthread或阻塞pthread操作,则可以开启`usercode_in_coroutine`这个flag。开启这个flag之后,bRPC会简化服务端处理逻辑,减少不必要的bthread开销。在这种情况下,实际的工作线程数量将由event_dispatcher_num控制,而不再是由bthread worker数量控制。
|
||||
|
||||
## 实现原理
|
||||
|
||||
### C++20协程实现原理
|
||||
|
||||
为了方便理解,我们把上面的CoroutineMain函数稍微改写一下,把co_await前后的逻辑分成两部分:
|
||||
|
||||
```cpp
|
||||
brpc::experimental::Awaitable<void> CoroutineMain(const char* server) {
|
||||
brpc::Channel channel;
|
||||
channel.Init(server, NULL);
|
||||
brpc::experimental::Awaitable<int> awaitable = RpcCall(channel);
|
||||
|
||||
int code = co_await awaitable;
|
||||
printf("Rpc result:%d\n", code);
|
||||
}
|
||||
```
|
||||
|
||||
上面的代码实际上是怎么执行的呢?当你使用co_await关键字的时候,编译器会把co_await后面的步骤转换成一个callback函数,把这个callback传给实际co_await的那个`Awaitable<T>`对象,比如上面的CoroutineMain函数,经过编译器转换后会变成大概如下的逻辑(简化版,实际要比这个复杂得多):
|
||||
|
||||
```cpp
|
||||
|
||||
brpc::experimental::Awaitable<void> CoroutineMain(const char* server) {
|
||||
// 根据函数返回类型,找到Awaitable<void>的名为promise_type的子类
|
||||
// 在函数的入口,创建一个promise_type类型的对象
|
||||
auto promise = new brpc::experimental::Awaitable<void>::promise_type();
|
||||
// 从promise对象中创建返回Awaitable对象
|
||||
Awaitable<void> ret = promise->get_return_object();
|
||||
|
||||
// co_await之前的逻辑,保持不变
|
||||
brpc::Channel channel;
|
||||
channel.Init(server, NULL);
|
||||
brpc::experimental::Awaitable<int> awaitable = RpcCall(channel);
|
||||
|
||||
// co_await的逻辑,转成一个await_suspend的函数调用,传入一个callback函数
|
||||
awaitable.await_suspend([promise, &awaitable]() {
|
||||
// co_await之后的逻辑,转移到callback函数中
|
||||
int code = awaitable.await_resume();
|
||||
printf("Rpc result:%d\n", code);
|
||||
// 在final_suspend里面,会做一些唤醒调用者、资源释放的工作
|
||||
promise->final_suspend();
|
||||
delete promise;
|
||||
})
|
||||
// 返回Awaitable<void>对象,以便上层函数进行处理
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
也就是说,co_await就是一个语法转换器,把看似同步的代码转化成异步调用的代码,仅此而已。至于Awaitable类和promise类的具体实现,编译器就不关心了,这是基础库需要做的。比如在brpc中封装了brpc::experimental::Awaitable类和promise子类,实现了await_suspend/await_resume等逻辑,使协程可以正确的工作起来。
|
||||
|
||||
### 原子等待操作
|
||||
|
||||
上面我们看到的是一个中间函数,它co_await一个子函数返回的Awaitable对象,然后自己也返回一个Awaitable对象。这样层层调用一定有一个尽头,即原子等待操作,它会返回Awaitable对象,但是它内部不再有co_await/co_return这样的语句了。目前实现了3种原子等待操作,未来可以扩展更多。
|
||||
|
||||
1. 等待RPC返回结果: `AwaitableDone::awaitable()`
|
||||
2. 等待sleep: `Coroutine::usleep()`
|
||||
3. 等待另一个协程完成: `Coroutine::awaitable()`
|
||||
|
||||
下面是一个原子等待操作的示例实现,我们需要手动创建一个promise对象,设置set_needs_suspend(),然后发起一个异步调用(如bthread_timer_add),在回调函数里设置好返回值、调用promise->on_done(),最后根据promise返回Awaitable对象即可。
|
||||
|
||||
```cpp
|
||||
inline Awaitable<int> Coroutine::usleep(int sleep_us) {
|
||||
auto promise = new detail::AwaitablePromise<int>();
|
||||
promise->set_needs_suspend();
|
||||
bthread_timer_t timer;
|
||||
auto abstime = butil::microseconds_from_now(sleep_us);
|
||||
auto cb = [](void* p) {
|
||||
auto promise = static_cast<detail::AwaitablePromise<int>*>(p);
|
||||
promise->set_value(0);
|
||||
promise->on_done();
|
||||
};
|
||||
bthread_timer_add(&timer, abstime, cb, promise);
|
||||
return Awaitable<int>(promise);
|
||||
}
|
||||
```
|
||||
|
||||
### 协程与多线程
|
||||
|
||||
上面我们可以看到,协程本质上就是一种callback,和线程没有直接关系。它可以是单线程的,也可以是多线程的,这完全取决于它的原子等待操作里是怎么调用callback的。在bRPC的环境里,callback有可能从另一个pthread或bthread发起,所以协程也是需要考虑多线程问题。比如,有可能在调用co_await语句之前,要等待的事情就已经结束了,对于这种情况co_await应该立即返回。
|
||||
|
||||
协程和线程可以一起使用,比如我们可以使用bthread将任务scale到多核,然后在任务内部的子任务用协程来实现异步化。
|
||||
@@ -0,0 +1,5 @@
|
||||
# Couchbase 示例
|
||||
|
||||
本文档尚未翻译为中文。
|
||||
|
||||
请参阅[英文版](../en/couchbase_example.md)获取完整内容。
|
||||
@@ -0,0 +1,119 @@
|
||||
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。注意: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端的认证,否则可能会看到这个:
|
||||
|
||||
```
|
||||
$ tools/pprof --text localhost:9002/pprof/profile
|
||||
Use of uninitialized value in substitution (s///) at tools/pprof line 2703.
|
||||
http://localhost:9002/profile/symbol doesn't exist
|
||||
```
|
||||
|
||||
server端可能会有这样的日志:
|
||||
|
||||
```
|
||||
FATAL: 12-26 10:01:25: * 0 [src/brpc/policy/giano_authenticator.cpp:65][4294969345] Giano fails to verify credentical, 70003
|
||||
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后的结果:
|
||||
|
||||
- 左上角是总体信息,包括时间,程序名,总采样数等等。
|
||||
- View框中可以选择查看之前运行过的profile结果,Diff框中可选择查看和之前的结果的变化量,重启后清空。
|
||||
- 代表函数调用的方框中的字段从上到下依次为:函数名,这个函数本身(除去所有子函数)占的采样数和比例,这个函数及调用的所有子函数累计的采样数和比例。采样数越大框越大。
|
||||
- 方框之间连线上的数字表示被采样到的上层函数对下层函数的调用数,数字越大线越粗。
|
||||
|
||||
热点分析一般开始于找到最大的框最粗的线考察其来源及去向。
|
||||
|
||||
cpu profiler的原理是在定期被调用的SIGPROF handler中采样所在线程的栈,由于handler(在linux 2.6后)会被随机地摆放于活跃线程的栈上运行,cpu profiler在运行一段时间后能以很大的概率采集到所有活跃线程中的活跃函数,最后根据栈代表的函数调用关系汇总为调用图,并把地址转换成符号,这就是我们看到的结果图了。采集频率由环境变量CPUPROFILE_FREQUENCY控制,默认100,即每秒钟100次或每10ms一次。在实践中cpu profiler对原程序的影响不明显。
|
||||
|
||||

|
||||
|
||||
在Linux下,你也可以使用[pprof](https://github.com/apache/brpc/blob/master/tools/pprof)或gperftools中的pprof进行profiling。
|
||||
|
||||
比如`pprof --text localhost:9002 --seconds=5`的意思是统计运行在本机9002端口的server的cpu情况,时长5秒。一次运行的例子如下:
|
||||
|
||||
```
|
||||
$ tools/pprof --text 0.0.0.0:9002 --seconds=5
|
||||
Gathering CPU profile from http://0.0.0.0:9002/pprof/profile?seconds=5 for 5 seconds to
|
||||
/home/gejun/pprof/echo_server.1419501210.0.0.0.0
|
||||
Be patient...
|
||||
Wrote profile to /home/gejun/pprof/echo_server.1419501210.0.0.0.0
|
||||
Removing funlockfile from all stack traces.
|
||||
Total: 2946 samples
|
||||
1161 39.4% 39.4% 1161 39.4% syscall
|
||||
248 8.4% 47.8% 248 8.4% bthread::TaskControl::steal_task
|
||||
227 7.7% 55.5% 227 7.7% writev
|
||||
87 3.0% 58.5% 88 3.0% ::cpp_alloc
|
||||
74 2.5% 61.0% 74 2.5% __read_nocancel
|
||||
46 1.6% 62.6% 48 1.6% tc_delete
|
||||
42 1.4% 64.0% 42 1.4% brpc::Socket::Address
|
||||
41 1.4% 65.4% 41 1.4% epoll_wait
|
||||
35 1.2% 66.6% 35 1.2% memcpy
|
||||
33 1.1% 67.7% 33 1.1% __pthread_getspecific
|
||||
33 1.1% 68.8% 33 1.1% brpc::Socket::Write
|
||||
33 1.1% 69.9% 33 1.1% epoll_ctl
|
||||
28 1.0% 70.9% 42 1.4% brpc::policy::ProcessRpcRequest
|
||||
27 0.9% 71.8% 27 0.9% butil::IOBuf::_push_back_ref
|
||||
27 0.9% 72.7% 27 0.9% bthread::TaskGroup::ending_sched
|
||||
```
|
||||
|
||||
省略–text进入交互模式,如下图所示:
|
||||
|
||||
```
|
||||
$ tools/pprof localhost:9002 --seconds=5
|
||||
Gathering CPU profile from http://0.0.0.0:9002/pprof/profile?seconds=5 for 5 seconds to
|
||||
/home/gejun/pprof/echo_server.1419501236.0.0.0.0
|
||||
Be patient...
|
||||
Wrote profile to /home/gejun/pprof/echo_server.1419501236.0.0.0.0
|
||||
Removing funlockfile from all stack traces.
|
||||
Welcome to pprof! For help, type 'help'.
|
||||
(pprof) top
|
||||
Total: 2954 samples
|
||||
1099 37.2% 37.2% 1099 37.2% syscall
|
||||
253 8.6% 45.8% 253 8.6% bthread::TaskControl::steal_task
|
||||
240 8.1% 53.9% 240 8.1% writev
|
||||
90 3.0% 56.9% 90 3.0% ::cpp_alloc
|
||||
67 2.3% 59.2% 67 2.3% __read_nocancel
|
||||
47 1.6% 60.8% 47 1.6% butil::IOBuf::_push_back_ref
|
||||
42 1.4% 62.2% 56 1.9% brpc::policy::ProcessRpcRequest
|
||||
41 1.4% 63.6% 41 1.4% epoll_wait
|
||||
38 1.3% 64.9% 38 1.3% epoll_ctl
|
||||
37 1.3% 66.1% 37 1.3% memcpy
|
||||
35 1.2% 67.3% 35 1.2% brpc::Socket::Address
|
||||
```
|
||||
|
||||
# MacOS的额外配置
|
||||
|
||||
在MacOS下,gperftools中的perl pprof脚本无法将函数地址转变成函数名,解决办法是:
|
||||
|
||||
1. 安装[standalone pprof](https://github.com/google/pprof),并把下载的pprof二进制文件路径写入环境变量GOOGLE_PPROF_BINARY_PATH中
|
||||
2. 安装llvm-symbolizer(将函数符号转化为函数名),直接用brew安装即可:`brew install llvm`
|
||||
|
||||
# 火焰图
|
||||
|
||||
若需要结果以火焰图的方式展示,请下载并安装[FlameGraph](https://github.com/brendangregg/FlameGraph)工具,将环境变量FLAMEGRAPH_PL_PATH正确设置到本地的/path/to/flamegraph.pl后启动server即可。
|
||||
@@ -0,0 +1,24 @@
|
||||
如果你的程序只使用了brpc的client或根本没有使用brpc,但你也想使用brpc的内置服务,只要在程序中启动一个空的server就行了,这种server我们称为**dummy server**。
|
||||
|
||||
# 使用了brpc的client
|
||||
|
||||
只要在程序运行目录建立dummy_server.port文件,填入一个端口号(比如8888),程序会马上在这个端口上启动一个dummy server。在浏览器中访问它的内置服务,便可看到同进程内的所有bvar。
|
||||
 
|
||||
|
||||

|
||||
|
||||
# 没有使用brpc
|
||||
|
||||
你必须手动加入dummy server。你得先查看[Getting Started](getting_started.md)如何下载和编译brpc,然后在程序入口处加入如下代码片段:
|
||||
|
||||
```c++
|
||||
#include <brpc/server.h>
|
||||
|
||||
...
|
||||
|
||||
int main() {
|
||||
...
|
||||
brpc::StartDummyServerAt(8888/*port*/);
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -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::开头的地址)
|
||||
@@ -0,0 +1,81 @@
|
||||
[English version](../en/error_code.md)
|
||||
|
||||
brpc使用[brpc::Controller](https://github.com/apache/brpc/blob/master/src/brpc/controller.h)设置和获取一次RPC的参数,`Controller::ErrorCode()`和`Controller::ErrorText()`则分别是该次RPC的错误码和错误描述,RPC结束后才能访问,否则结果未定义。ErrorText()由Controller的基类google::protobuf::RpcController定义,ErrorCode()则是brpc::Controller定义的。Controller还有个Failed()方法告知该次RPC是否失败,这三者的关系是:
|
||||
|
||||
- 当Failed()为true时,ErrorCode()一定为非0,ErrorText()则为非空。
|
||||
- 当Failed()为false时,ErrorCode()一定为0,ErrorText()未定义(目前在brpc中会为空,但你最好不要依赖这个事实)
|
||||
|
||||
# 标记RPC为错误
|
||||
|
||||
brpc的client端和server端都有Controller,都可以通过SetFailed()修改其中的ErrorCode和ErrorText。当多次调用一个Controller的SetFailed时,ErrorCode会被覆盖,ErrorText则是**添加**而不是覆盖。在client端,框架会额外加上第几次重试,在server端,框架会额外加上server的地址信息。
|
||||
|
||||
client端Controller的SetFailed()常由框架调用,比如发送request失败,接收到的response不符合要求等等。只有在进行较复杂的访问操作时用户才可能需要设置client端的错误,比如在访问后端前做额外的请求检查,发现有错误时把RPC设置为失败。
|
||||
|
||||
server端Controller的SetFailed()常由用户在服务回调中调用。当处理过程发生错误时,一般调用SetFailed()并释放资源后就return了。框架会把错误码和错误信息按交互协议填入response,client端的框架收到后会填入它那边的Controller中,从而让用户在RPC结束后取到。需要注意的是,**server端在SetFailed()时默认不打印送往client的错误**。打日志是比较慢的,在繁忙的线上磁盘上,很容易出现巨大的lag。一个错误频发的client容易减慢整个server的速度而影响到其他的client,理论上来说这甚至能成为一种攻击手段。对于希望在server端看到错误信息的场景,可以打开gflag **-log_error_text**(可动态开关),server会在每次失败的RPC后把对应Controller的ErrorText()打印出来。
|
||||
|
||||
# brpc的错误码
|
||||
|
||||
brpc使用的所有ErrorCode都定义在[errno.proto](https://github.com/apache/brpc/blob/master/src/brpc/errno.proto)中,*SYS_*开头的来自linux系统,与/usr/include/errno.h中定义的精确一致,定义在proto中是为了跨语言。其余的是brpc自有的。
|
||||
|
||||
[berror(error_code)](https://github.com/apache/brpc/blob/master/src/butil/errno.h)可获得error_code的描述,berror()可获得当前[system errno](http://www.cplusplus.com/reference/cerrno/errno/)的描述。**ErrorText() != berror(ErrorCode())**,ErrorText()会包含更具体的错误信息。brpc默认包含berror,你可以直接使用。
|
||||
|
||||
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 | 是 | 可能原因:一、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" |
|
||||
| EAUTH | 1004 | 否 | 认证失败 | "Authentication failed" |
|
||||
| ETOOMANYFAILS | 1005 | 否 | ParallelChannel中太多子channel失败 | "%d/%d channels failed, fail_limit=%d" |
|
||||
| EBACKUPREQUEST | 1007 | 是 | 触发backup request时设置,不会出现在ErrorCode中,但可在/rpcz里看到 | “reached backup timeout=%dms" |
|
||||
| ERPCTIMEDOUT | 1008 | 否 | RPC超时 | "reached timeout=%dms" |
|
||||
| EFAILEDSOCKET | 1009 | 是 | RPC进行过程中TCP连接出现问题 | "The socket was SetFailed" |
|
||||
| EHTTP | 1010 | 否 | 非2xx状态码的HTTP访问结果均认为失败并被设置为这个错误码。默认不重试,可通过RetryPolicy定制 | Bad http call |
|
||||
| EOVERCROWDED | 1011 | 是 | 连接上有过多的未发送数据,常由同时发起了过多的异步访问导致。可通过参数-socket_max_unwritten_bytes控制,默认64MB。 | The server is overcrowded |
|
||||
| EINTERNAL | 2001 | 否 | Server端Controller.SetFailed没有指定错误码时使用的默认错误码。 | "Internal Server Error" |
|
||||
| ERESPONSE | 2002 | 否 | response解析错误,client端和server端都可能设置 | 形式广泛"Missing required fields in response: ...""Fail to parse response message, ""Bad response" |
|
||||
| ELOGOFF | 2003 | 是 | Server已经被Stop了 | "Server is going to quit" |
|
||||
| ELIMIT | 2004 | 是 | 同时处理的请求数超过ServerOptions.max_concurrency了 | "Reached server's limit=%d on concurrent requests" |
|
||||
|
||||
# 自定义错误码
|
||||
|
||||
在C++/C中你可以通过宏、常量、enum等方式定义ErrorCode:
|
||||
```c++
|
||||
#define ESTOP -114 // C/C++
|
||||
static const int EMYERROR = 30; // C/C++
|
||||
const int EMYERROR2 = -31; // C++ only
|
||||
```
|
||||
如果你需要用berror返回这些新错误码的描述,你可以在.cpp或.c文件的全局域中调用BAIDU_REGISTER_ERRNO(error_code, description)进行注册,比如:
|
||||
```c++
|
||||
BAIDU_REGISTER_ERRNO(ESTOP, "the thread is stopping")
|
||||
BAIDU_REGISTER_ERRNO(EMYERROR, "my error")
|
||||
```
|
||||
strerror和strerror_r不认识使用BAIDU_REGISTER_ERRNO定义的错误码,自然地,printf类的函数中的%m也不能转化为对应的描述,你必须使用%s并配以berror()。
|
||||
```c++
|
||||
errno = ESTOP;
|
||||
printf("Describe errno: %m\n"); // [Wrong] Describe errno: Unknown error -114
|
||||
printf("Describe errno: %s\n", strerror_r(errno, NULL, 0)); // [Wrong] Describe errno: Unknown error -114
|
||||
printf("Describe errno: %s\n", berror()); // [Correct] Describe errno: the thread is stopping
|
||||
printf("Describe errno: %s\n", berror(errno)); // [Correct] Describe errno: the thread is stopping
|
||||
```
|
||||
当同一个error code被重复注册时,那么会出现链接错误:
|
||||
|
||||
```
|
||||
redefinition of `class BaiduErrnoHelper<30>'
|
||||
```
|
||||
或者在程序启动时会abort:
|
||||
```
|
||||
Fail to define EMYERROR(30) which is already defined as `Read-only file system', abort
|
||||
```
|
||||
|
||||
你得确保不同的模块对ErrorCode的理解是相同的,否则当两个模块把一个错误码理解为不同的错误时,它们之间的交互将出现无法预计的行为。为了防止这种情况出现,你最好这么做:
|
||||
- 优先使用系统错误码,它们的值和含义一般是固定不变的。
|
||||
- 多个交互的模块使用同一份错误码定义,防止后续修改时产生不一致。
|
||||
- 使用BAIDU_REGISTER_ERRNO描述新错误码,以确保同一个进程内错误码是互斥的。
|
||||
@@ -0,0 +1,219 @@
|
||||
# 概述
|
||||
|
||||
类似于kylin的ExecMan, [ExecutionQueue](https://github.com/apache/brpc/blob/master/src/bthread/execution_queue.h)提供了异步串行执行的功能。ExecutionQueue的相关技术最早使用在RPC中实现[多线程向同一个fd写数据](io.md#发消息). 在r31345之后加入到bthread。 ExecutionQueue 提供了如下基本功能:
|
||||
|
||||
- 异步有序执行: 任务在另外一个单独的线程中执行, 并且执行顺序严格和提交顺序一致.
|
||||
- Multi Producer: 多个线程可以同时向一个ExecutionQueue提交任务
|
||||
- 支持cancel一个已经提交的任务
|
||||
- 支持stop
|
||||
- 支持高优任务插队
|
||||
|
||||
和ExecMan的主要区别:
|
||||
- ExecutionQueue的任务提交接口是[wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom)的, ExecMan依赖了lock, 这意味着当机器整体比较繁忙的时候,使用ExecutionQueue不会因为某个进程被系统强制切换导致所有线程都被阻塞。
|
||||
- ExecutionQueue支持批量处理: 执行线程可以批量处理提交的任务, 获得更好的locality. ExecMan的某个线程处理完某个AsyncClient的AsyncContext之后下一个任务很可能是属于另外一个AsyncClient的AsyncContex, 这时候cpu cache会在不同AsyncClient依赖的资源间进行不停的切换。
|
||||
- ExecutionQueue的处理函数不会被绑定到固定的线程中执行, ExecMan中是根据AsyncClient hash到固定的执行线程,不同的ExecutionQueue之间的任务处理完全独立,当线程数足够多的情况下,所有非空闲的ExecutionQueue都能同时得到调度。同时也意味着当线程数不足的时候,ExecutionQueue无法保证公平性, 当发生这种情况的时候需要动态增加bthread的worker线程来增加整体的处理能力.
|
||||
- ExecutionQueue运行线程为bthread, 可以随意的使用一些bthread同步原语而不用担心阻塞pthread的执行. 而在ExecMan里面得尽量避免使用较高概率会导致阻塞的同步原语.
|
||||
|
||||
# 背景
|
||||
|
||||
在多核并发编程领域, [Message passing](https://en.wikipedia.org/wiki/Message_passing)作为一种解决竞争的手段得到了比较广泛的应用,它按照业务依赖的资源将逻辑拆分成若干个独立actor,每个actor负责对应资源的维护工作,当一个流程需要修改某个资源的时候,
|
||||
就转化为一个消息发送给对应actor,这个actor(通常在另外的上下文中)根据命令内容对这个资源进行相应的修改,之后可以选择唤醒调用者(同步)或者提交到下一个actor(异步)的方式进行后续处理。
|
||||
|
||||

|
||||
|
||||
# ExecutionQueue Vs Mutex
|
||||
|
||||
ExecutionQueue和mutex都可以用来在多线程场景中消除竞争. 相比较使用mutex,
|
||||
使用ExecutionQueue有着如下几个优点:
|
||||
|
||||
- 角色划分比较清晰, 概念理解上比较简单, 实现中无需考虑锁带来的问题(比如死锁)
|
||||
- 能保证任务的执行顺序,mutex的唤醒顺序不能得到严格保证.
|
||||
- 所有线程各司其职,都能在做有用的事情,不存在等待.
|
||||
- 在繁忙、卡顿的情况下能更好的批量执行,整体上获得较高的吞吐.
|
||||
|
||||
但是缺点也同样明显:
|
||||
|
||||
- 一个流程的代码往往散落在多个地方,代码理解和维护成本高。
|
||||
- 为了提高并发度, 一件事情往往会被拆分到多个ExecutionQueue进行流水线处理,这样会导致在多核之间不停的进行切换,会付出额外的调度以及同步cache的开销, 尤其是竞争的临界区非常小的情况下, 这些开销不能忽略.
|
||||
- 同时原子的操作多个资源实现会变得复杂, 使用mutex可以同时锁住多个mutex, 用了ExeuctionQueue就需要依赖额外的dispatch queue了。
|
||||
- 由于所有操作都是单线程的,某个任务运行慢了就会阻塞同一个ExecutionQueue的其他操作。
|
||||
- 并发控制变得复杂,ExecutionQueue可能会由于缓存的任务过多占用过多的内存。
|
||||
|
||||
不考虑性能和复杂度,理论上任何系统都可以只使用mutex或者ExecutionQueue来消除竞争.
|
||||
但是复杂系统的设计上,建议根据不同的场景灵活决定如何使用这两个工具:
|
||||
|
||||
- 如果临界区非常小,竞争又不是很激烈,优先选择使用mutex, 之后可以结合[contention profiler](contention_profiler.md)来判断mutex是否成为瓶颈。
|
||||
- 需要有序执行,或者无法消除的激烈竞争但是可以通过批量执行来提高吞吐, 可以选择使用ExecutionQueue。
|
||||
|
||||
总之,多线程编程没有万能的模型,需要根据具体的场景,结合丰富的profliling工具,最终在复杂度和性能之间找到合适的平衡。
|
||||
|
||||
**特别指出一点**,Linux中mutex无竞争的lock/unlock只有需要几条原子指令,在绝大多数场景下的开销都可以忽略不计.
|
||||
|
||||
# 使用方式
|
||||
|
||||
### 实现执行函数
|
||||
|
||||
```
|
||||
// Iterate over the given tasks
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// #include <bthread/execution_queue.h>
|
||||
//
|
||||
// int demo_execute(void* meta, TaskIterator<T>& iter) {
|
||||
// if (iter.is_queue_stopped()) {
|
||||
// // destroy meta and related resources
|
||||
// return 0;
|
||||
// }
|
||||
// for (; iter; ++iter) {
|
||||
// // do_something(meta, *iter)
|
||||
// // or do_something(meta, iter->a_member_of_T)
|
||||
// }
|
||||
// return 0;
|
||||
// }
|
||||
template <typename T>
|
||||
class TaskIterator;
|
||||
```
|
||||
|
||||
### 启动一个ExecutionQueue:
|
||||
|
||||
```
|
||||
struct ExecutionQueueOptions {
|
||||
ExecutionQueueOptions();
|
||||
|
||||
// Execute in resident pthread instead of bthread. default: false.
|
||||
bool use_pthread;
|
||||
|
||||
// Attribute of the bthread which execute runs on. default: BTHREAD_ATTR_NORMAL
|
||||
// Bthread will be used when executor = NULL and use_pthread == false.
|
||||
bthread_attr_t bthread_attr;
|
||||
|
||||
// Executor that tasks run on. default: NULL
|
||||
// Note that TaskOptions.in_place_if_possible = false will not work, if implementation of
|
||||
// Executor is in-place(synchronous).
|
||||
Executor * executor;
|
||||
};
|
||||
|
||||
// Start a ExecutionQueue. If |options| is NULL, the queue will be created with
|
||||
// default options.
|
||||
// Returns 0 on success, errno otherwise
|
||||
// NOTE: type |T| can be non-POD but must be copy-constructible
|
||||
template <typename T>
|
||||
int execution_queue_start(
|
||||
ExecutionQueueId<T>* id,
|
||||
const ExecutionQueueOptions* options,
|
||||
int (*execute)(void* meta, TaskIterator<T>& iter),
|
||||
void* meta);
|
||||
```
|
||||
|
||||
创建的返回值是一个64位的id, 相当于ExecutionQueue实例的一个[弱引用](https://en.wikipedia.org/wiki/Weak_reference), 可以wait-free的在O(1)时间内定位一个ExecutionQueue, 你可以到处拷贝这个id, 甚至可以放在RPC中,作为远端资源的定位工具。
|
||||
你必须保证meta的生命周期,在对应的ExecutionQueue真正停止前不会释放.
|
||||
|
||||
### 停止一个ExecutionQueue:
|
||||
|
||||
```
|
||||
// Stop the ExecutionQueue.
|
||||
// After this function is called:
|
||||
// - All the following calls to execution_queue_execute would fail immediately.
|
||||
// - The executor will call |execute| with TaskIterator::is_queue_stopped() being
|
||||
// true exactly once when all the pending tasks have been executed, and after
|
||||
// this point it's ok to release the resource referenced by |meta|.
|
||||
// Returns 0 on success, errno othrwise
|
||||
template <typename T>
|
||||
int execution_queue_stop(ExecutionQueueId<T> id);
|
||||
|
||||
// Wait until the the stop task (Iterator::is_queue_stopped() returns true) has
|
||||
// been executed
|
||||
template <typename T>
|
||||
int execution_queue_join(ExecutionQueueId<T> id);
|
||||
```
|
||||
|
||||
stop和join都可以多次调用, 都会有合理的行为。stop可以随时调用而不用当心线程安全性问题。
|
||||
|
||||
和fd的close类似,如果stop不被调用, 相应的资源会永久泄露。
|
||||
|
||||
安全释放meta的时机: 可以在execute函数中收到iter.is_queue_stopped()==true的任务的时候释放,也可以等到join返回之后释放. 注意不要double-free
|
||||
|
||||
### 提交任务
|
||||
|
||||
```
|
||||
struct TaskOptions {
|
||||
TaskOptions();
|
||||
TaskOptions(bool high_priority, bool in_place_if_possible);
|
||||
|
||||
// Executor would execute high-priority tasks in the FIFO order but before
|
||||
// all pending normal-priority tasks.
|
||||
// NOTE: We don't guarantee any kind of real-time as there might be tasks still
|
||||
// in process which are uninterruptible.
|
||||
//
|
||||
// Default: false
|
||||
bool high_priority;
|
||||
|
||||
// If |in_place_if_possible| is true, execution_queue_execute would call
|
||||
// execute immediately instead of starting a bthread if possible
|
||||
//
|
||||
// Note: Running callbacks in place might cause the dead lock issue, you
|
||||
// should be very careful turning this flag on.
|
||||
//
|
||||
// Default: false
|
||||
bool in_place_if_possible;
|
||||
};
|
||||
|
||||
const static TaskOptions TASK_OPTIONS_NORMAL = TaskOptions(/*high_priority=*/ false, /*in_place_if_possible=*/ false);
|
||||
const static TaskOptions TASK_OPTIONS_URGENT = TaskOptions(/*high_priority=*/ true, /*in_place_if_possible=*/ false);
|
||||
const static TaskOptions TASK_OPTIONS_INPLACE = TaskOptions(/*high_priority=*/ false, /*in_place_if_possible=*/ true);
|
||||
|
||||
// Thread-safe and Wait-free.
|
||||
// Execute a task with defaut TaskOptions (normal task);
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
typename butil::add_const_reference<T>::type task);
|
||||
|
||||
// Thread-safe and Wait-free.
|
||||
// Execute a task with options. e.g
|
||||
// bthread::execution_queue_execute(queue, task, &bthread::TASK_OPTIONS_URGENT)
|
||||
// If |options| is NULL, we will use default options (normal task)
|
||||
// If |handle| is not NULL, we will assign it with the handler of this task.
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
typename butil::add_const_reference<T>::type task,
|
||||
const TaskOptions* options);
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
typename butil::add_const_reference<T>::type task,
|
||||
const TaskOptions* options,
|
||||
TaskHandle* handle);
|
||||
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
T&& task);
|
||||
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
T&& task,
|
||||
const TaskOptions* options);
|
||||
|
||||
template <typename T>
|
||||
int execution_queue_execute(ExecutionQueueId<T> id,
|
||||
T&& task,
|
||||
const TaskOptions* options,
|
||||
TaskHandle* handle);
|
||||
|
||||
```
|
||||
|
||||
high_priority的task之间的执行顺序也会**严格按照提交顺序**, 这点和ExecMan不同, ExecMan的QueueExecEmergent的AsyncContex执行顺序是undefined. 但是这也意味着你没有办法将任何任务插队到一个high priority的任务之前执行.
|
||||
|
||||
开启inplace_if_possible, 在无竞争的场景中可以省去一次线程调度和cache同步的开销. 但是可能会造成死锁或者递归层数过多(比如不停的ping-pong)的问题,开启前请确定你的代码中不存在这些问题。
|
||||
|
||||
### 取消一个已提交任务
|
||||
|
||||
```
|
||||
/// [Thread safe and ABA free] Cancel the corresponding task.
|
||||
// Returns:
|
||||
// -1: The task was executed or h is an invalid handle
|
||||
// 0: Success
|
||||
// 1: The task is executing
|
||||
int execution_queue_cancel(const TaskHandle& h);
|
||||
```
|
||||
|
||||
返回非0仅仅意味着ExecutionQueue已经将对应的task递给过execute, 真实的逻辑中可能将这个task缓存在另外的容器中,所以这并不意味着逻辑上的task已经结束,你需要在自己的业务上保证这一点.
|
||||
@@ -0,0 +1,137 @@
|
||||
brpc使用gflags管理配置。如果你的程序也使用gflags,那么你应该已经可以修改和brpc相关的flags,你可以浏览[flags服务](http://brpc.baidu.com:8765/flags)了解每个flag的具体功能。如果你的程序还没有使用gflags,我们建议你使用,原因如下:
|
||||
|
||||
- 命令行和文件均可传入,前者方便做测试,后者适合线上运维。放在文件中的gflags可以reload。而configure只支持从文件读取配置。
|
||||
- 你可以在浏览器中查看brpc服务器中所有gflags,并对其动态修改(如果允许的话)。configure不可能做到这点。
|
||||
- gflags分散在和其作用紧密关联的文件中,更好管理。而使用configure需要聚集到一个庞大的读取函数中。
|
||||
|
||||
# Usage of gflags
|
||||
|
||||
gflags一般定义在需要它的源文件中。#include <gflags/gflags.h>后在全局scope加入DEFINE_*\<type\>*(*\<name\>*, *\<default-value\>*, *\<description\>*); 比如:
|
||||
|
||||
```c++
|
||||
#include <gflags/gflags.h>
|
||||
...
|
||||
DEFINE_bool(hex_log_id, false, "Show log_id in hexadecimal");
|
||||
DEFINE_int32(health_check_interval, 3, "seconds between consecutive health-checkings");
|
||||
```
|
||||
|
||||
一般在main函数开头用ParseCommandLineFlags处理程序参数:
|
||||
|
||||
```c++
|
||||
#include <gflags/gflags.h>
|
||||
...
|
||||
int main(int argc, char* argv[]) {
|
||||
google::ParseCommandLineFlags(&argc, &argv, true/*表示把识别的参数从argc/argv中删除*/);
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
如果要从conf/gflags.conf中加载gflags,则可以加上参数-flagfile=conf/gflags.conf。如果希望默认(什么参数都不加)就从文件中读取,则可以在程序中直接给flagfile赋值,一般这么写
|
||||
|
||||
```c++
|
||||
google::SetCommandLineOption("flagfile", "conf/gflags.conf");
|
||||
```
|
||||
|
||||
程序启动时会检查conf/gflags.conf是否存在,如果不存在则会报错:
|
||||
|
||||
```
|
||||
$ ./my_program
|
||||
conf/gflags.conf: No such file or directory
|
||||
```
|
||||
|
||||
更具体的使用指南请阅读[官方文档](http://gflags.github.io/gflags/)。
|
||||
|
||||
# flagfile
|
||||
|
||||
在命令行中参数和值之间可不加等号,而在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中参数可由单横线(如-foo)或双横线(如--foo)打头,但不能以三横线或更多横线打头,否则的话是无效参数且不会报错!
|
||||
|
||||
flagfile中以`#开头的行被认为是注释。开头的空格和空白行都会被忽略。`
|
||||
|
||||
flagfile中可以使用`--flagfile包含另一个flagfile。`
|
||||
|
||||
# Change gflag on-the-fly
|
||||
|
||||
[flags服务](http://brpc.baidu.com:8765/flags)可以查看服务器进程中所有的gflags。修改过的flags会以红色高亮。“修改过”指的是修改这一行为,即使再改回默认值,仍然会显示为红色。
|
||||
|
||||
/flags:列出所有的gflags
|
||||
|
||||
/flags/NAME:查询名字为NAME的gflag
|
||||
|
||||
/flags/NAME1,NAME2,NAME3:查询名字为NAME1或NAME2或NAME3的gflag
|
||||
|
||||
/flags/foo*,b$r:查询名字与某一统配符匹配的gflag,注意用$代替?匹配单个字符,因为?在url中有特殊含义。
|
||||
|
||||
访问/flags/NAME?setvalue=VALUE即可动态修改一个gflag的值,validator会被调用。
|
||||
|
||||
为了防止误修改,需要动态修改的gflag必须有validator,显示此类gflag名字时有(R)后缀。
|
||||
|
||||

|
||||
|
||||
*修改成功后会显示如下信息*:
|
||||
|
||||

|
||||
|
||||
*尝试修改不允许修改的gflag会显示如下错误信息*:
|
||||
|
||||

|
||||
|
||||
*设置一个不允许的值会显示如下错误(flag值不会变化)*:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
r31658之后支持可视化地修改,在浏览器上访问时将看到(R)下多了下划线:
|
||||
|
||||

|
||||
|
||||
点击后在一个独立页面可视化地修改对应的flag:
|
||||
|
||||

|
||||
|
||||
填入true后确定:
|
||||
|
||||

|
||||
|
||||
返回/flags可以看到对应的flag已经被修改了:
|
||||
|
||||

|
||||
|
||||
|
||||
|
||||
关于重载gflags,重点关注:
|
||||
|
||||
- 避免在一段代码中多次调用同一个gflag,应把该gflag的值保存下来并调用该值。因为gflag的值随时可能变化,而产生意想不到的结果。
|
||||
- 使用google::GetCommandLineOption()访问string类型的gflag,直接访问是线程不安全的。
|
||||
- 处理逻辑和副作用应放到validator里去。比如修改FLAGS_foo后得更新另一处的值,如果只是写在程序初始化的地方,而不是validator里,那么重载时这段逻辑就运行不到了。
|
||||
|
||||
如果你确认某个gflag不需要额外的线程同步和处理逻辑就可以重载,那么可以用如下方式为其注册一个总是返回true的validator:
|
||||
|
||||
```c++
|
||||
DEFINE_bool(hex_log_id, false, "Show log_id in hexadecimal");
|
||||
BRPC_VALIDATE_GFLAG(hex_log_id, brpc::PassValidate/*always true*/);
|
||||
```
|
||||
|
||||
这个flag是单纯的开关,修改后不需要更新其他数据(没有处理逻辑),代码中前面看到true后面看到false也不会产生什么后果(不需要线程同步),所以我们让其默认可重载。
|
||||
|
||||
对于int32和int64类型,有一个判断是否为正数的常用validator:
|
||||
|
||||
```c++
|
||||
DEFINE_int32(health_check_interval, 3, "seconds between consecutive health-checkings");
|
||||
BRPC_VALIDATE_GFLAG(health_check_interval, brpc::PositiveInteger);
|
||||
```
|
||||
|
||||
以上操作都可以在命令行中进行:
|
||||
|
||||
```shell
|
||||
$ curl brpc.baidu.com:8765/flags/health_check_interval
|
||||
Name | Value | Description | Defined At
|
||||
---------------------------------------
|
||||
health_check_interval (R) | 3 | seconds between consecutive health-checkings | src/brpc/socket_map.cpp
|
||||
```
|
||||
|
||||
1.0.251.32399后增加了-immutable_flags,打开后所有的gflags将不能被动态修改。当一个服务对某个gflag值比较敏感且不希望在线上被误改,可打开这个开关。打开这个开关的同时也意味着你无法动态修改线上的配置,每次修改都要重启程序,对于还在调试阶段或待收敛阶段的程序不建议打开。
|
||||
@@ -0,0 +1,179 @@
|
||||
# NAME
|
||||
|
||||
FlatMap - Maybe the fastest hashmap, with tradeoff of space.
|
||||
|
||||
# EXAMPLE
|
||||
|
||||
```c++
|
||||
#include <string>
|
||||
#include <butil/logging.h>
|
||||
#include <butil/containers/flat_map.h>
|
||||
|
||||
void flatmap_example() {
|
||||
butil::FlatMap<int, std::string> map;
|
||||
// bucket_count: initial count of buckets, big enough to avoid resize.
|
||||
// load_factor: element_count * 100 / bucket_count, 80 as default.
|
||||
int bucket_count = 1000;
|
||||
int load_factor = 80;
|
||||
map.init(bucket_count, load_factor);
|
||||
map.insert(10, "hello");
|
||||
map[20] = "world";
|
||||
std::string* value = map.seek(20);
|
||||
CHECK(value != NULL);
|
||||
|
||||
CHECK_EQ(2UL, map.size());
|
||||
CHECK_EQ(0UL, map.erase(30));
|
||||
CHECK_EQ(1UL, map.erase(10));
|
||||
|
||||
LOG(INFO) << "All elements of the map:";
|
||||
for (butil::FlatMap<int, std::string>::const_iterator it = map.begin(); it != map.end(); ++it) {
|
||||
LOG(INFO) << it->first << " : " << it->second;
|
||||
}
|
||||
map.clear();
|
||||
CHECK_EQ(0UL, map.size());
|
||||
}
|
||||
|
||||
void flatmap_erase_hinted_during_iteration_example() {
|
||||
typedef butil::FlatMap<int, int> Map;
|
||||
Map map;
|
||||
// bucket_count: initial count of buckets, big enough to avoid resize.
|
||||
// load_factor: element_count * 100 / bucket_count, 80 as default.
|
||||
int bucket_count = 1000;
|
||||
int load_factor = 80;
|
||||
map.init(bucket_count, load_factor);
|
||||
const int N = 10;
|
||||
for (int i = 0; i < N; ++i) {
|
||||
map[i] = i;
|
||||
}
|
||||
|
||||
for (Map::const_iterator it = map.begin(); it != map.end(); ++it) {
|
||||
// After `erase()', ++iterator may fail.
|
||||
// We need to save iterator before `erase()'
|
||||
// and restore iterator after `erase()'.
|
||||
typename Map::PositionHint hint{};
|
||||
map.save_iterator(it, &hint);
|
||||
if (it->first % 2 == 0) {
|
||||
CHECK_EQ(1UL, map.erase(it->first));
|
||||
}
|
||||
it = map.restore_iterator(hint);
|
||||
if (it == map.end()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_EQ((size_t)(N / 2), map.size());
|
||||
|
||||
LOG(INFO) << "All remaining elements of the map:";
|
||||
for (Map::const_iterator it = map.begin(); it != map.end(); ++it) {
|
||||
CHECK_EQ(1, it->first % 2);
|
||||
LOG(INFO) << it->first << " : " << it->second;
|
||||
}
|
||||
map.clear();
|
||||
CHECK_EQ(0UL, map.size());
|
||||
}
|
||||
```
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
[FlatMap](https://github.com/apache/brpc/blob/master/src/butil/containers/flat_map.h)可能是最快的哈希表,但当value较大时它需要更多的内存,它最适合作为检索过程中需要极快查找的小字典。
|
||||
|
||||
原理:把开链桶中第一个节点的内容直接放桶内。由于在实践中,大部分桶没有冲突或冲突较少,所以大部分操作只需要一次内存跳转:通过哈希值访问对应的桶。桶内两个及以上元素仍存放在链表中,由于桶之间彼此独立,一个桶的冲突不会影响其他桶,性能很稳定。在很多时候,FlatMap的查找性能和原生数组接近。
|
||||
|
||||
# BENCHMARK
|
||||
|
||||
下面是FlatMap和其他key/value容器的比较:
|
||||
|
||||
- [AlignHashMap](https://svn.baidu.com/app/ecom/nova/trunk/public/util/container/alignhash.h):闭链中较快的实现。
|
||||
- [CowHashMap](https://svn.baidu.com/app/ecom/nova/trunk/afs/smalltable/cow_hash_map.hpp):smalltable中的开链哈希表,和普通开链不同的是带Copy-on-write逻辑。
|
||||
- [std::map](http://www.cplusplus.com/reference/map/map/):非哈希表,一般是红黑树。
|
||||
|
||||
```
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 8 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 15/19/30/102ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 7/11/33/146ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 10/28/26/93ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 6/9/29/100ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 10/21/26/130ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 5/10/30/104ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 32 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 23/31/31/130ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 9/11/72/104ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 20/53/28/112ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 7/10/29/101ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 20/46/28/137ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 7/10/29/112ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 128 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 34/109/91/179ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 8/11/33/112ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 28/76/86/169ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 8/9/30/110ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Sequentially inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 28/68/87/201ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Sequentially erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 9/9/30/125ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 8 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 14/56/29/157ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 9/11/31/181ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 11/17/27/156ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 6/10/30/204ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 13/26/27/212ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 7/11/38/309ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 32 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 24/32/32/181ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 10/12/32/182ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 21/46/35/168ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 7/10/36/209ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 24/46/31/240ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 8/11/40/314ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:474] [ value = 128 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 100 into FlatMap/AlignHashMap/CowHashMap/std::map takes 36/114/93/231ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 100 from FlatMap/AlighHashMap/CowHashMap/std::map takes 9/12/35/190ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 1000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 44/94/88/224ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 1000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 8/10/34/236ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:521] Randomly inserting 10000 into FlatMap/AlignHashMap/CowHashMap/std::map takes 46/92/93/314ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:558] Randomly erasing 10000 from FlatMap/AlighHashMap/CowHashMap/std::map takes 12/11/42/362ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 8 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/7/12/54ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 3/7/11/78ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/8/13/172ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 32 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 5/8/12/55ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/8/11/82ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 6/10/14/164ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 128 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 7/9/13/56ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 6/10/12/93ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 9/12/21/166ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 8 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/7/11/56ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 3/7/11/79ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/9/13/173ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 32 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 5/8/12/54ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 4/8/11/100ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 6/10/14/165ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:576] [ value = 128 bytes ]
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 100 from FlatMap/AlignHashMap/CowHashMap/std::map takes 7/9/12/56ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 1000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 6/10/12/88ns
|
||||
TRACE: 12-30 13:19:53: * 0 [test_flat_map.cpp:637] Seeking 10000 from FlatMap/AlignHashMap/CowHashMap/std::map takes 9/14/20/169ns
|
||||
```
|
||||
# Overview of hashmaps
|
||||
|
||||
哈希表是最常用的数据结构,它的基本原理是通过[计算哈希值](http://en.wikipedia.org/wiki/Hash_function)把不同的key分散到不同的区间,在查找时通过key的哈希值能快速地缩小查找区间。在使用恰当参数的前提下,哈希表在大部分时候能在O(1)时间内把一个key映射为value。像其他算法一样,这个“O(1)”在不同的实现中差异很大。哈希表的实现一般有两部分:
|
||||
|
||||
## 计算哈希值(非加密型)
|
||||
|
||||
即把key散列开的方法,最常见的莫过于线性同余,但一个好的哈希算法(非加密型)要考虑很多因素:
|
||||
|
||||
- 结果是确定的。
|
||||
- [雪崩效应](http://en.wikipedia.org/wiki/Avalanche_effect):输入中一个bit的变化应该尽量影响输出所有bit的变化。
|
||||
- 输出应尽量在值域中均匀分布。
|
||||
- 充分利用现代cpu特性:成块计算,减少分支,循环展开等等。
|
||||
|
||||
大部分哈希算法针对的只是一个key,不会耗用太多的cpu。影响主要来自哈希表的整体数据分布,对于工程师来说,选用何种算法得看实践效果,一些最简单的方法也许就有很好的效果。通用算法可选择Murmurhash。
|
||||
|
||||
## 解决冲突
|
||||
|
||||
哈希值可能重合,解决冲突是哈希表性能的另一关键因素。常见的冲突解决方法有:
|
||||
|
||||
- 开链哈希(open hashing, closed addressing): 开链哈希表是链表的数组,其中链表一般称为桶。当若干个key落到同一个桶时,做链表插入。这是最通用的结构,有很多优点:占用内存为O(NumElement * (KeySize + ValueSize + SomePointers)),resize时候不会使之前的存放key/value的内存失效。桶之间是独立的,一个桶的冲突不会影响到其他桶,平均查找时间较为稳定,独立的桶也易于高并发。缺点是至少要两次内存跳转:先跳到桶入口,再跳到桶中的第一个节点。对于一些很小的表这个问题不明显,因为当表很小时,节点内存是接近的,但当表变大时,访存就愈发随机。如果一次访存在50ns左右(2G左右主频),开链哈希的查找时间往往就在100ns以上。在检索端的层层ranking过程中,对一些热点字典的查找1秒内可能有几百万次以上,开链哈希有时会成为热点。一些产品线可能对开链哈希的内存也有诟病,因为每对key/value都需要额外的指针。
|
||||
- [闭链哈希](http://en.wikipedia.org/wiki/Open_addressing)(closed hashing or open addressing): 闭链的初衷是减少内存跳转,桶不再是链表入口,而只需要记录一对key/value与一些标记,当桶被占时,按照不同的探查方法直到找到空桶为止。比如线性探查就是查找下一个桶,二次探查是按1,2,4,9...平方数位移查找。优点是:当表很空时或冲突较少时,查找只需要一次访存,也不需要管理节点内存池。但仅此而已,这个方法带来了更多缺点:桶个数必须大于元素个数,resize后之前的内存全部失效,难以并发. 更关键的是聚集效应:当区域内元素较多时(超过70%,其实不算多),大量元素的实际桶和它们应在的桶有较大位移。这使哈希表的主要操作都要扫过一大片内存才能找到元素,性能不稳定难以预测。闭链哈希表在很多人的印象中“很快”,但在复杂的应用中往往不如开链哈希表,并且可能是数量级的慢。闭链有一些衍生版本试图解决这个问题,比如[Hopscotch hashing](http://en.wikipedia.org/wiki/Hopscotch_hashing)。
|
||||
- 混合开链和闭链:一般是把桶数组中的一部分拿出来作为容纳冲突元素的空间,典型如[Coalesced hashing](http://en.wikipedia.org/wiki/Coalesced_hashing),但这种结构没有解决开链的内存跳转问题,结构又比闭链复杂很多,工程效果并不好。
|
||||
- 多次哈希:一般用多个哈希表代替一个哈希表,当发生冲突时(用另一个哈希值)尝试另一个哈希表。典型如[Cuckoo hashing](http://en.wikipedia.org/wiki/Cuckoo_hashing),这个结构也没有解决内存跳转。
|
||||
@@ -0,0 +1,401 @@
|
||||
[English version](../en/getting_started.md)
|
||||
|
||||
# 构建
|
||||
|
||||
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.
|
||||
|
||||
# 支持的环境
|
||||
|
||||
* [Ubuntu/LinuxMint/WSL](#ubuntulinuxmintwsl)
|
||||
* [Fedora/CentOS](#fedoracentos)
|
||||
* [自己构建依赖的Linux](#自己构建依赖的Linux)
|
||||
* [MacOS](#macos)
|
||||
* [docker](#docker)
|
||||
|
||||
## Ubuntu/LinuxMint/WSL
|
||||
### 依赖准备
|
||||
|
||||
安装依赖:
|
||||
```shell
|
||||
sudo apt-get install -y git g++ make libssl-dev libgflags-dev libprotobuf-dev libprotoc-dev protobuf-compiler libleveldb-dev
|
||||
```
|
||||
|
||||
如果你需要静态链接leveldb:
|
||||
```shell
|
||||
sudo apt-get install -y libsnappy-dev
|
||||
```
|
||||
|
||||
如果你需要通过源码编译生成 leveldb 静态库:
|
||||
|
||||
```shell
|
||||
git clone --recurse-submodules https://github.com/google/leveldb.git
|
||||
mkdir -p build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_POSITION_INDEPENDENT_CODE=ON .. && cmake --build .
|
||||
sudo cp -r ../include/leveldb /usr/include/ && sudo cp libleveldb.a /usr/lib/
|
||||
```
|
||||
|
||||
如果你要在样例中启用cpu/heap的profiler:
|
||||
|
||||
```shell
|
||||
sudo apt-get install -y libgoogle-perftools-dev
|
||||
```
|
||||
|
||||
如果你要运行测试,那么要安装并编译libgtest-dev(它没有被默认编译):
|
||||
```shell
|
||||
sudo apt-get install -y cmake libgtest-dev && cd /usr/src/gtest && sudo cmake . && sudo make && sudo mv lib/libgtest* /usr/lib/ && cd -
|
||||
```
|
||||
gtest源码目录可能变动,如果`/usr/src/gtest`不存在,请尝试`/usr/src/googletest/googletest`。
|
||||
|
||||
### 使用config_brpc.sh编译brpc
|
||||
git克隆brpc,进入到项目目录,然后运行
|
||||
```shell
|
||||
$ sh config_brpc.sh --headers=/usr/include --libs=/usr/lib
|
||||
$ make
|
||||
```
|
||||
修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。
|
||||
|
||||
不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。
|
||||
|
||||
使用glog版的brpc,添加选项`--with-glog`。
|
||||
|
||||
要启用 [thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。
|
||||
|
||||
**运行样例**
|
||||
|
||||
```shell
|
||||
$ cd example/echo_c++
|
||||
$ make
|
||||
$ ./echo_server &
|
||||
$ ./echo_client
|
||||
```
|
||||
|
||||
上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make`
|
||||
|
||||
**运行测试**
|
||||
```shell
|
||||
$ cd test
|
||||
$ make
|
||||
$ sh run_tests.sh
|
||||
```
|
||||
|
||||
### 使用cmake编译brpc
|
||||
|
||||
```shell
|
||||
mkdir build && cd build && cmake .. && cmake --build . -j6
|
||||
```
|
||||
对于 cmake 3.13+ 也可以使用如下命令进行编译:
|
||||
```shell
|
||||
cmake -B build && cmake --build build -j6
|
||||
```
|
||||
要帮助VSCode或Emacs(LSP)去正确地理解代码,添加`-DCMAKE_EXPORT_COMPILE_COMMANDS=ON`选项去生成`compile_commands.json`。
|
||||
|
||||
要修改编译器为clang,请修改环境变量`CC`和`CXX`为`clang`和`clang++`。
|
||||
|
||||
不想链接调试符号,请移除`build/CMakeCache.txt`,然后用`-DWITH_DEBUG_SYMBOLS=OFF`选项执行cmake。
|
||||
|
||||
想要让brpc使用glog,用`-DWITH_GLOG=ON`选项执行cmake。
|
||||
|
||||
要启用 [thrift 支持](../en/thrift.md),先安装thrift,然后用`-DWITH_THRIFT=ON`选项执行cmake。
|
||||
|
||||
**用cmake运行样例**
|
||||
|
||||
```shell
|
||||
$ cd example/echo_c++
|
||||
$ cmake -B build && cmake --build build -j4
|
||||
$ cd build
|
||||
$ ./echo_server &
|
||||
$ ./echo_client
|
||||
```
|
||||
|
||||
上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请先移除`CMakeCache.txt`,然后用`-DLINK_SO=ON`选项重新执行cmake。
|
||||
|
||||
**运行测试**
|
||||
|
||||
```shell
|
||||
$ mkdir build && cd build && cmake -DBUILD_UNIT_TESTS=ON .. && make && make test
|
||||
```
|
||||
|
||||
## Fedora/CentOS
|
||||
|
||||
### 依赖准备
|
||||
|
||||
CentOS一般需要安装EPEL,否则很多包都默认不可用。
|
||||
```shell
|
||||
sudo yum install epel-release
|
||||
```
|
||||
|
||||
安装依赖:
|
||||
```shell
|
||||
sudo yum install git gcc-c++ make openssl-devel gflags-devel protobuf-devel protobuf-compiler leveldb-devel
|
||||
```
|
||||
|
||||
如果你要在样例中启用cpu/heap的profiler:
|
||||
```shell
|
||||
sudo yum install gperftools-devel
|
||||
```
|
||||
|
||||
如果你要运行测试,那么要安装ligtest-dev:
|
||||
```shell
|
||||
sudo yum install gtest-devel
|
||||
```
|
||||
|
||||
### 使用config_brpc.sh编译brpc
|
||||
|
||||
git克隆brpc,进入项目目录然后执行:
|
||||
|
||||
```shell
|
||||
$ sh config_brpc.sh --headers="/usr/include" --libs="/usr/lib64 /usr/bin"
|
||||
$ make
|
||||
```
|
||||
修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。
|
||||
|
||||
不想链接调试符号,添加选项`--nodebugsymbols` 然后编译将会得到更轻量的二进制文件。
|
||||
|
||||
想要让brpc使用glog,添加选项:`--with-glog`。
|
||||
|
||||
要启用 [thrift 支持](../en/thrift.md),先安装thrift,然后添加选项:`--with-thrift`。
|
||||
|
||||
**运行样例**
|
||||
|
||||
```shell
|
||||
$ cd example/echo_c++
|
||||
$ make
|
||||
$ ./echo_server &
|
||||
$ ./echo_client
|
||||
```
|
||||
|
||||
上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make`
|
||||
|
||||
**运行测试**
|
||||
```shell
|
||||
$ cd test
|
||||
$ make
|
||||
$ sh run_tests.sh
|
||||
```
|
||||
|
||||
### 使用cmake编译brpc
|
||||
参考[这里](#使用cmake编译brpc)
|
||||
|
||||
### 使用vcpkg编译brpc
|
||||
|
||||
[vcpkg](https://github.com/microsoft/vcpkg) 是一个全平台支持的包管理器,你可以使用以下步骤vcpkg轻松编译brpc:
|
||||
|
||||
```shell
|
||||
$ git clone https://github.com/microsoft/vcpkg.git
|
||||
$ ./bootstrap-vcpkg.bat # 使用 powershell
|
||||
$ ./bootstrap-vcpkg.sh # 使用 bash
|
||||
$ ./vcpkg install brpc
|
||||
```
|
||||
|
||||
## 自己构建依赖的Linux
|
||||
|
||||
### 依赖准备
|
||||
|
||||
brpc默认会构建出静态库和共享库,因此它也需要依赖有静态库和共享库两个版本。
|
||||
|
||||
以[gflags](https://github.com/gflags/gflags)为例,它默认不构建共享库,你需要给`cmake`指定选项去改变这一行为:
|
||||
```shell
|
||||
$ cmake . -DBUILD_SHARED_LIBS=1 -DBUILD_STATIC_LIBS=1
|
||||
$ make
|
||||
```
|
||||
|
||||
### 编译brpc
|
||||
|
||||
还以gflags为例,`../gflags_dev`表示gflags被克隆的位置。
|
||||
|
||||
git克隆brpc。进入到项目目录然后运行:
|
||||
|
||||
```shell
|
||||
$ sh config_brpc.sh --headers="../gflags_dev /usr/include" --libs="../gflags_dev /usr/lib64"
|
||||
$ make
|
||||
```
|
||||
|
||||
这里我们给`--headers`和`--libs`传递多个路径使得脚本能够在多个地方进行检索。你也可以打包所有依赖和brpc一起放到一个目录中,然后把目录传递给 --headers/--libs选项,它会递归搜索所有子目录直到找到必须的文件。
|
||||
|
||||
修改编译器为clang,添加选项`--cxx=clang++ --cc=clang`。
|
||||
|
||||
不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。
|
||||
|
||||
使用glog版的brpc,添加选项`--with-glog`。
|
||||
|
||||
要启用[thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。
|
||||
|
||||
```shell
|
||||
$ ls my_dev
|
||||
gflags_dev protobuf_dev leveldb_dev brpc_dev
|
||||
$ cd brpc_dev
|
||||
$ sh config_brpc.sh --headers=.. --libs=..
|
||||
$ make
|
||||
```
|
||||
|
||||
### 使用cmake编译brpc
|
||||
参考[这里](#使用cmake编译brpc)
|
||||
|
||||
## MacOS
|
||||
|
||||
注意:在相同硬件条件下,MacOS版brpc的性能可能明显差于Linux版。如果你的服务是性能敏感的,请不要使用MacOS作为你的生产环境。
|
||||
|
||||
### Apple Silicon
|
||||
|
||||
master HEAD已支持M1系列芯片,M2未测试过。欢迎通过issues向我们报告遗留的warning/error。
|
||||
|
||||
### 依赖准备
|
||||
|
||||
安装依赖:
|
||||
```shell
|
||||
brew install ./homebrew-formula/protobuf.rb
|
||||
brew install openssl git gnu-getopt coreutils gflags leveldb
|
||||
```
|
||||
|
||||
如果你要在样例中启用cpu/heap的profiler:
|
||||
```shell
|
||||
brew install gperftools
|
||||
```
|
||||
|
||||
如果你要运行测试,需安装gtest。先运行`brew install googletest`看看homebrew是否支持(老版本没有),没有的话请下载和编译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
|
||||
```
|
||||
在编译完成后,复制`include/`和`lib/`目录到`/usr/local/include`和`/usr/local/lib`目录中,以便于让所有应用都能使用gtest。
|
||||
|
||||
### OpenSSL
|
||||
Monterey中openssl的安装位置可能不再位于`/usr/local/opt/openssl`,很可能会在`/opt/homebrew/Cellar`目录下,如果编译时报告找不到openssl:
|
||||
|
||||
* 先运行`brew link openssl --force`看看`/usr/local/opt/openssl`是否出现了
|
||||
* 没有的话可以自行设置软链:`sudo ln -s /opt/homebrew/Cellar/openssl@3/3.0.3 /usr/local/opt/openssl`。请注意此命令中openssl的目录可能随环境变化而变化,可通过`brew info openssl`查看。
|
||||
|
||||
### 使用config_brpc.sh编译brpc
|
||||
git克隆brpc,进入到项目目录然后运行:
|
||||
```shell
|
||||
$ sh config_brpc.sh --headers=/usr/local/include --libs=/usr/local/lib --cc=clang --cxx=clang++
|
||||
$ make
|
||||
```
|
||||
MacOS Monterey下的brew安装路径可能改变,如有路径相关的错误,可考虑设置如下:
|
||||
|
||||
```shell
|
||||
$ sh config_brpc.sh --headers=/opt/homebrew/include --libs=/opt/homebrew/lib --cc=clang --cxx=clang++
|
||||
$ make
|
||||
```
|
||||
|
||||
不想链接调试符号,添加选项`--nodebugsymbols`,然后编译将会得到更轻量的二进制文件。
|
||||
|
||||
使用glog版的brpc,添加选项`--with-glog`。
|
||||
|
||||
要启用[thrift 支持](../en/thrift.md),首先安装thrift并且添加选项`--with-thrift`。
|
||||
|
||||
**运行样例**
|
||||
|
||||
```shell
|
||||
$ cd example/echo_c++
|
||||
$ make
|
||||
$ ./echo_server &
|
||||
$ ./echo_client
|
||||
```
|
||||
上述操作会链接brpc的静态库到样例中,如果你想链接brpc的共享库,请依次执行:`make clean`和`LINK_SO=1 make`
|
||||
|
||||
**运行测试**
|
||||
```shell
|
||||
$ cd test
|
||||
$ make
|
||||
$ sh run_tests.sh
|
||||
```
|
||||
|
||||
### 使用cmake编译brpc
|
||||
参考[这里](#使用cmake编译brpc)
|
||||
|
||||
## Docker
|
||||
使用docker 编译brpc:
|
||||
|
||||
```shell
|
||||
$ mkdir -p ~/brpc
|
||||
$ cd ~/brpc
|
||||
$ git clone https://github.com/apache/brpc.git
|
||||
$ cd brpc
|
||||
$ docker build -t brpc:master .
|
||||
$ docker images
|
||||
$ docker run -it brpc:master /bin/bash
|
||||
```
|
||||
|
||||
# 支持的依赖
|
||||
|
||||
## GCC: 4.8-11.2
|
||||
|
||||
**推荐 8.2 及以上版本。**
|
||||
|
||||
默认启用 c++11,以去除对 boost 的依赖(比如 atomic)。
|
||||
|
||||
理论支持 c++11 的编译器都应可以,但部分编译器版本对 c++11 的支持存在问题。目前 GCC 4.8 可支持编译的最高版本为 1.5.0。
|
||||
|
||||
GCC7中over-aligned的问题暂时被禁止。
|
||||
|
||||
使用其他版本的gcc可能会产生编译警告,请联系我们予以修复。
|
||||
|
||||
请在makefile中给cxxflags增加`-D__const__=__unused__`选项以避免[gcc4+中的errno问题](thread_local.md).
|
||||
|
||||
## Clang: 3.5-4.0
|
||||
|
||||
无已知问题。
|
||||
|
||||
## glibc: 2.12-2.25
|
||||
|
||||
无已知问题。
|
||||
|
||||
## protobuf: 3.0-5.29
|
||||
|
||||
bRPC 中使用了 protobuf 内部 API,上游不保证相关 API 的兼容性,目前测试可以支持到 v29(5.29),如有问题欢迎[反馈](https://github.com/apache/brpc/issues)。
|
||||
|
||||
[1.8.0](https://github.com/apache/brpc/releases/tag/1.8.0) 中 [#2406](https://github.com/apache/brpc/pull/2406) 和 [#2493](https://github.com/apache/brpc/pull/2493)引入了部分 proto3 语法,所以目前 bRPC 不再兼容 protobuf 2.x 版本。如果你希望使用 2.x 版本,可以使用 1.8.0 之前的 bRPC 版本。
|
||||
|
||||
## gflags: 2.1-2.2.2
|
||||
|
||||
2.1.1 中存在一处已知问题,需要[补丁](https://github.com/gflags/gflags/commit/408061b46974cc8377a8a794a048ecae359ad887)。
|
||||
|
||||
## openssl: 0.97-1.1
|
||||
|
||||
被https功能需要。
|
||||
|
||||
## tcmalloc: 1.7-2.5
|
||||
|
||||
brpc默认**不**链接 [tcmalloc](http://goog-perftools.sourceforge.net/doc/tcmalloc.html)。用户按需要链接tcmalloc。
|
||||
|
||||
和glibc内置的ptmalloc相比,tcmalloc通常能提升性能。然而不同版本的tcmalloc可能表现迥异。例如:tcmalloc 2.1与 tcmalloc 1.7和2.5相比,可能会让brpc的多线程样例性能显著恶化(tcmalloc中的一个自旋锁导致的)。甚至不同的小版本号之间变现也可能不同。当你的程序表现不符合预期的时候,移除tcmalloc然后尝试其他版本。
|
||||
|
||||
用gcc4.8.2编译然后链接更早版本GCC编译的tcmalloc,可能会让程序中main()函数之前挂掉或者死锁,例如:
|
||||
|
||||

|
||||
|
||||
当你遇到这个问题的时候,请用同一个GCC重新编译tcmalloc。
|
||||
|
||||
另外一个使用tcmalloc的常见问题是,它不会像 ptmalloc一样及时地归还内存给系统。因此当有一个无效的内存访问的时候,程序可能不会直接挂掉,取而代之的是它可能在一个不相关的地方挂掉,或者甚至一直不挂掉。当你的程序出现怪异的内存问题的时候,尝试移除tcmalloc。
|
||||
|
||||
如果你要使用[cpu profiler](cpu_profiler.md)或[heap profiler](heap_profiler.md),要链接`libtcmalloc_and_profiler.a`。这两个 profiler都是基于tcmalloc的。而[contention profiler](contention_profiler.md)不需要tcmalloc。
|
||||
|
||||
当你移除tcmalloc的时候,不仅要移除tcmalloc的链接,也要移除宏`-DBRPC_ENABLE_CPU_PROFILER`。
|
||||
|
||||
## glog: 3.3+
|
||||
|
||||
brpc实现了一个默认的[日志功能](../../src/butil/logging.h)它和glog冲突。要替换成glog,可以给config_brpc.sh增加`--with-glog`选项或者给cmake增加`-DWITH_GLOG=ON`选项。
|
||||
|
||||
## valgrind: 3.8+
|
||||
|
||||
brpc会自动检测valgrind(然后注册bthread的栈)。不支持老版本的valgrind(比如3.2)。
|
||||
|
||||
## thrift: 0.9.3-0.11.0
|
||||
|
||||
无已知问题。
|
||||
|
||||
## libunwind: 1.3-1.8.1
|
||||
|
||||
bRPC默认**不**链接 [libunwind](https://github.com/libunwind/libunwind)。用户需要追踪bthread功能则链接libunwind,可以给config_brpc.sh增加`--with-bthread-tracer`选项或者给cmake增加`-DWITH_BTHREAD_TRACER=ON`选项,如果是用 bazel 构建,请添加 `--define with_bthread_tracer=true` 选项。
|
||||
|
||||
建议使用最新版本的libunwind。
|
||||
|
||||
# 实例追踪
|
||||
|
||||
我们提供了一个程序去帮助你追踪和监控所有brpc实例。 只需要在某处运行 [trackme_server](https://github.com/apache/brpc/tree/master/tools/trackme_server/) 然后再带着 -trackme_server=SERVER参数启动需要被追踪的实例。trackme_server将从实例周期性地收到ping消息然后打印日志。您可以从日志中聚合实例地址,并调用实例的内置服务以获取更多信息。
|
||||
@@ -0,0 +1,169 @@
|
||||
brpc可以分析内存是被哪些函数占据的。heap profiler的原理是每分配满一些内存就采样调用处的栈,“一些”由环境变量TCMALLOC_SAMPLE_PARAMETER控制,默认524288,即512K字节。根据栈表现出的函数调用关系汇总为我们看到的结果图。在实践中heap profiler对原程序的影响不明显。
|
||||
|
||||
# 开启方法
|
||||
|
||||
1. 链接`libtcmalloc_and_profiler.a`
|
||||
|
||||
1. 如果tcmalloc使用frame pointer而不是libunwind回溯栈,请确保在CXXFLAGS或CFLAGS中加上`-fno-omit-frame-pointer`,否则函数间的调用关系会丢失,最后产生的图片中都是彼此独立的函数方框。
|
||||
|
||||
2. 在shell中`export TCMALLOC_SAMPLE_PARAMETER=524288`。该变量指每分配这么多字节内存时做一次统计,默认为0,代表不开启内存统计。[官方文档](http://goog-perftools.sourceforge.net/doc/tcmalloc.html)建议设置为524288。这个变量也可在运行前临时设置,如`TCMALLOC_SAMPLE_PARAMETER=524288 ./server`。如果没有这个环境变量,可能会看到这样的结果:
|
||||
|
||||
```
|
||||
$ tools/pprof --text localhost:9002/pprof/heap
|
||||
Fetching /pprof/heap profile from http://localhost:9002/pprof/heap to
|
||||
/home/gejun/pprof/echo_server.1419559063.localhost.pprof.heap
|
||||
Wrote profile to /home/gejun/pprof/echo_server.1419559063.localhost.pprof.heap
|
||||
/home/gejun/pprof/echo_server.1419559063.localhost.pprof.heap: header size >= 2**16
|
||||
```
|
||||
|
||||
3. 如果只是brpc client或没有使用brpc,看[这里](dummy_server.md)。
|
||||
|
||||
注意要关闭Server端的认证,否则可能会看到这个:
|
||||
|
||||
```
|
||||
$ tools/pprof --text localhost:9002/pprof/heap
|
||||
Use of uninitialized value in substitution (s///) at tools/pprof line 2703.
|
||||
http://localhost:9002/pprof/symbol doesn't exist
|
||||
```
|
||||
|
||||
server端可能会有这样的日志:
|
||||
|
||||
```
|
||||
FATAL: 12-26 10:01:25: * 0 [src/brpc/policy/giano_authenticator.cpp:65][4294969345] Giano fails to verify credentical, 70003
|
||||
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
|
||||
```
|
||||
|
||||
# 图示
|
||||
|
||||

|
||||
|
||||
左上角是当前程序通过malloc分配的内存总量,顺着箭头上的数字可以看到内存来自哪些函数。方框内第一个百分比是本函数的内存占用比例, 第二个百分比是本函数及其子函数的内存占用比例
|
||||
|
||||
点击左上角的text选择框可以查看文本格式的结果,有时候这种按分配量排序的形式更方便。
|
||||
|
||||

|
||||
|
||||
左上角的两个选择框作用分别是:
|
||||
|
||||
- View:当前正在看的profile。选择\<new profile\>表示新建一个。新建完毕后,View选择框中会出现新profile,URL也会被修改为对应的地址。这意味着你可以通过粘贴URL分享结果,点击链接的人将看到和你一模一样的结果,而不是重做profiling的结果。你可以在框中选择之前的profile查看。历史profiie保留最近的32个,可通过[--max_profiles_kept](http://brpc.baidu.com:8765/flags/max_profiles_kept)调整。
|
||||
- Diff:和选择的profile做对比。<none>表示什么都不选。如果你选择了之前的某个profile,那么将看到View框中的profile相比Diff框中profile的变化量。
|
||||
|
||||
下图演示了勾选Diff和Text的效果。
|
||||
|
||||

|
||||
|
||||
在Linux下,你也可以使用pprof脚本(tools/pprof)在命令行中查看文本格式结果:
|
||||
|
||||
```
|
||||
$ tools/pprof --text db-rpc-dev00.db01:8765/pprof/heap
|
||||
Fetching /pprof/heap profile from http://db-rpc-dev00.db01:8765/pprof/heap to
|
||||
/home/gejun/pprof/play_server.1453216025.db-rpc-dev00.db01.pprof.heap
|
||||
Wrote profile to /home/gejun/pprof/play_server.1453216025.db-rpc-dev00.db01.pprof.heap
|
||||
Adjusting heap profiles for 1-in-524288 sampling rate
|
||||
Heap version 2
|
||||
Total: 38.9 MB
|
||||
35.8 92.0% 92.0% 35.8 92.0% ::cpp_alloc
|
||||
2.1 5.4% 97.4% 2.1 5.4% butil::FlatMap
|
||||
0.5 1.3% 98.7% 0.5 1.3% butil::IOBuf::append
|
||||
0.5 1.3% 100.0% 0.5 1.3% butil::IOBufAsZeroCopyOutputStream::Next
|
||||
0.0 0.0% 100.0% 0.6 1.5% MallocExtension::GetHeapSample
|
||||
0.0 0.0% 100.0% 0.5 1.3% ProfileHandler::Init
|
||||
0.0 0.0% 100.0% 0.5 1.3% ProfileHandlerRegisterCallback
|
||||
0.0 0.0% 100.0% 0.5 1.3% __do_global_ctors_aux
|
||||
0.0 0.0% 100.0% 1.6 4.2% _end
|
||||
0.0 0.0% 100.0% 0.5 1.3% _init
|
||||
0.0 0.0% 100.0% 0.6 1.5% brpc::CloseIdleConnections
|
||||
0.0 0.0% 100.0% 1.1 2.9% brpc::GlobalUpdate
|
||||
0.0 0.0% 100.0% 0.6 1.5% brpc::PProfService::heap
|
||||
0.0 0.0% 100.0% 1.9 4.9% brpc::Socket::Create
|
||||
0.0 0.0% 100.0% 2.9 7.4% brpc::Socket::Write
|
||||
0.0 0.0% 100.0% 3.8 9.7% brpc::Span::CreateServerSpan
|
||||
0.0 0.0% 100.0% 1.4 3.5% brpc::SpanQueue::Push
|
||||
0.0 0.0% 100.0% 1.9 4.8% butil::ObjectPool
|
||||
0.0 0.0% 100.0% 0.8 2.0% butil::ResourcePool
|
||||
0.0 0.0% 100.0% 1.0 2.6% butil::iobuf::tls_block
|
||||
0.0 0.0% 100.0% 1.0 2.6% bthread::TimerThread::Bucket::schedule
|
||||
0.0 0.0% 100.0% 1.6 4.1% bthread::get_stack
|
||||
0.0 0.0% 100.0% 4.2 10.8% bthread_id_create
|
||||
0.0 0.0% 100.0% 1.1 2.9% bvar::Variable::describe_series_exposed
|
||||
0.0 0.0% 100.0% 1.0 2.6% bvar::detail::AgentGroup
|
||||
0.0 0.0% 100.0% 0.5 1.3% bvar::detail::Percentile::operator
|
||||
0.0 0.0% 100.0% 0.5 1.3% bvar::detail::PercentileSamples
|
||||
0.0 0.0% 100.0% 0.5 1.3% bvar::detail::Sampler::schedule
|
||||
0.0 0.0% 100.0% 6.5 16.8% leveldb::Arena::AllocateNewBlock
|
||||
0.0 0.0% 100.0% 0.5 1.3% leveldb::VersionSet::LogAndApply
|
||||
0.0 0.0% 100.0% 4.2 10.8% pthread_mutex_unlock
|
||||
0.0 0.0% 100.0% 0.5 1.3% pthread_once
|
||||
0.0 0.0% 100.0% 0.5 1.3% std::_Rb_tree
|
||||
0.0 0.0% 100.0% 1.5 3.9% std::basic_string
|
||||
0.0 0.0% 100.0% 3.5 9.0% std::string::_Rep::_S_create
|
||||
```
|
||||
|
||||
brpc还提供一个类似的growth profiler分析内存的分配去向(不考虑释放)。
|
||||
|
||||

|
||||
|
||||
# MacOS的额外配置
|
||||
|
||||
1. 安装[standalone pprof](https://github.com/google/pprof),并把下载的pprof二进制文件路径写入环境变量GOOGLE_PPROF_BINARY_PATH中
|
||||
2. 安装llvm-symbolizer(将函数符号转化为函数名),直接用brew安装即可:`brew install llvm`
|
||||
|
||||
# Jemalloc Heap Profiler
|
||||
|
||||
## 开启方法
|
||||
|
||||
1. 编译[jemalloc](https://github.com/jemalloc/jemalloc)时需--enable-prof以支持profiler, 安装完成后bin目录下会有jeprof文件。
|
||||
2. 启动进程前最好配置env `JEPROF_FILE=/xxx/jeprof`,否则进程默认用$PATH里的jeprof解析。
|
||||
3. 进程开启profiler:
|
||||
- 启动进程并开启profiler功能:`MALLOC_CONF="prof:true" LD_PRELOAD=/xxx/lib/libjemalloc.so ./bin/test_server`,MALLOC_CONF是env项,prof:true只做一些初始化动作,并不会采样,但[prof_active](https://jemalloc.net/jemalloc.3.html#opt.prof_active)默认是true,所以进程启动就会采样。
|
||||
- 若静态链接jemalloc:`MALLOC_CONF="prof:true" ./bin/test_server`。
|
||||
- 或通过下面的gflags控制,gflags不会反应MALLOC_CONF值。
|
||||
4. 相关gflags说明:
|
||||
- FLAGS_je_prof_active:true:开启采样,false:关闭采样。
|
||||
- FLAGS_je_prof_dump:修改值会生成heap文件,用于手动操作jeprof分析。
|
||||
- FLAGS_je_prof_reset:清理已采样数据,并且动态设置采样率,[默认](https://jemalloc.net/jemalloc.3.html#opt.lg_prof_sample)2^19B(512K),对性能影响可忽略。
|
||||
5. 若要做memory leak:
|
||||
- `MALLOC_CONF="prof:true,prof_leak:true,prof_final:true" LD_PRELOAD=/xxx/lib/libjemalloc.so ./bin/test_server` ,进程退出时生成heap文件。
|
||||
- 注:可`kill pid`优雅退出,不可`kill -9 pid`;可用`FLAGS_graceful_quit_on_sigterm=true FLAGS_graceful_quit_on_sighup=true`来支持优雅退出。
|
||||
|
||||
注:
|
||||
- 每次dump的都是从采样至今的所有数据,若触发了reset,接来下dump的是从reset至今的所有数据,方便做diff。
|
||||
- 更多jemalloc profiler选项请参考[官网](https://jemalloc.net/jemalloc.3.html),如`prof_leak_error:true`则检测到内存泄漏,进程立即退出。
|
||||
|
||||
## 样例
|
||||
|
||||
- jeprof命令`jeprof ip:port/pprof/heap`。
|
||||
|
||||

|
||||
|
||||
- curl生成text格式`curl ip:port/pprof/heap?display=text`。
|
||||
|
||||

|
||||
|
||||
- curl生成svg图片格式`curl ip:port/pprof/heap?display=svg`。
|
||||
|
||||

|
||||
|
||||
- 分配对象个数`curl ip:port/pprof/heap?display=svg&extra_options=inuse_objects`。
|
||||
|
||||
- curl生成火焰图`curl ip:port/pprof/heap?display=flamegraph`。需配置env FLAMEGRAPH_PL_PATH=/xxx/flamegraph.pl,[flamegraph](https://github.com/brendangregg/FlameGraph)
|
||||
|
||||

|
||||
|
||||
- curl获取内存统计信息`curl ip:port/pprof/heap?display=stats&opts=Ja`或`curl ip:port/memory?opts=Ja`,更多opts请参考[opts](https://github.com/jemalloc/jemalloc/blob/dev/include/jemalloc/internal/stats.h#L9)。
|
||||
|
||||

|
||||
|
||||
- 内存使用量可关注:
|
||||
1. jemalloc.stats下的
|
||||
- [resident](https://jemalloc.net/jemalloc.3.html#stats.resident)
|
||||
- [metadata](https://jemalloc.net/jemalloc.3.html#stats.metadata)
|
||||
- [allocated](https://jemalloc.net/jemalloc.3.html#stats.allocated):jeprof分析的就是这部分内存。
|
||||
- [active](https://jemalloc.net/jemalloc.3.html#stats.active):active - allocated ≈ unuse。
|
||||
2. stats.arenas下的:
|
||||
- [resident](https://jemalloc.net/jemalloc.3.html#stats.arenas.i.resident)
|
||||
- [pactive](https://jemalloc.net/jemalloc.3.html#stats.arenas.i.pactive)
|
||||
- [base](https://jemalloc.net/jemalloc.3.html#stats.arenas.i.base):含义近似metadata
|
||||
- [small.allocated](https://jemalloc.net/jemalloc.3.html#stats.arenas.i.small.allocated)
|
||||
- [large.allocated](https://jemalloc.net/jemalloc.3.html#stats.arenas.i.large.allocated):arena allocated ≈ small.allocated + large.allocated
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
[English version](../en/http_client.md)
|
||||
|
||||
# 示例
|
||||
|
||||
[example/http_c++](https://github.com/apache/brpc/blob/master/example/http_c++/http_client.cpp)
|
||||
|
||||
# 关于h2
|
||||
|
||||
brpc把HTTP/2协议统称为"h2",不论是否加密。然而未开启ssl的HTTP/2连接在/connections中会按官方名称h2c显示,而开启ssl的会显示为h2。
|
||||
|
||||
brpc中http和h2的编程接口基本没有区别。除非特殊说明,所有提到的http特性都同时对h2有效。
|
||||
|
||||
# 创建Channel
|
||||
|
||||
brpc::Channel可访问http/h2服务,ChannelOptions.protocol须指定为PROTOCOL_HTTP或PROTOCOL_H2。
|
||||
|
||||
设定好协议后,`Channel::Init`的第一个参数可为任意合法的URL。注意:允许任意URL是为了省去用户取出host和port的麻烦,`Channel::Init`只用其中的host及port,其他部分都会丢弃。
|
||||
|
||||
```c++
|
||||
brpc::ChannelOptions options;
|
||||
options.protocol = brpc::PROTOCOL_HTTP; // or brpc::PROTOCOL_H2
|
||||
if (channel.Init("www.baidu.com" /*any url*/, &options) != 0) {
|
||||
LOG(ERROR) << "Fail to initialize channel";
|
||||
return -1;
|
||||
}
|
||||
```
|
||||
|
||||
http/h2 channel也支持bns地址或其他NamingService。
|
||||
|
||||
# GET
|
||||
|
||||
```c++
|
||||
brpc::Controller cntl;
|
||||
cntl.http_request().uri() = "www.baidu.com/index.html"; // 设置为待访问的URL
|
||||
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
|
||||
```
|
||||
|
||||
HTTP/h2和protobuf关系不大,所以除了Controller和done,CallMethod的其他参数均为NULL。如果要异步操作,最后一个参数传入done。
|
||||
|
||||
`cntl.response_attachment()`是回复的body,类型也是butil::IOBuf。IOBuf可通过to_string()转化为std::string,但是需要分配内存并拷贝所有内容,如果关注性能,处理过程应直接支持IOBuf,而不要求连续内存。
|
||||
|
||||
# POST
|
||||
|
||||
默认的HTTP Method为GET,可设置为POST或[更多http method](https://github.com/apache/brpc/blob/master/src/brpc/http_method.h)。待POST的数据应置入request_attachment(),它([butil::IOBuf](https://github.com/apache/brpc/blob/master/src/butil/iobuf.h))可以直接append std::string或char*。
|
||||
|
||||
```c++
|
||||
brpc::Controller cntl;
|
||||
cntl.http_request().uri() = "..."; // 设置为待访问的URL
|
||||
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
|
||||
cntl.request_attachment().append("{\"message\":\"hello world!\"}");
|
||||
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
|
||||
```
|
||||
|
||||
需要大量打印过程的body建议使用butil::IOBufBuilder,它的用法和std::ostringstream是一样的。对于有大量对象要打印的场景,IOBufBuilder简化了代码,效率也可能比c-style printf更高。
|
||||
|
||||
```c++
|
||||
brpc::Controller cntl;
|
||||
cntl.http_request().uri() = "..."; // 设置为待访问的URL
|
||||
cntl.http_request().set_method(brpc::HTTP_METHOD_POST);
|
||||
butil::IOBufBuilder os;
|
||||
os << "A lot of printing" << printable_objects << ...;
|
||||
os.move_to(cntl.request_attachment());
|
||||
channel.CallMethod(NULL, &cntl, NULL, NULL, NULL/*done*/);
|
||||
```
|
||||
# 控制HTTP版本
|
||||
|
||||
brpc的http行为默认是http/1.1。
|
||||
|
||||
http/1.0相比http/1.1缺少长连接功能,brpc client与一些古老的http server通信时可能需要按如下方法设置为1.0。
|
||||
```c++
|
||||
cntl.http_request().set_version(1, 0);
|
||||
```
|
||||
|
||||
设置http版本对h2无效,但是client收到的h2 response和server收到的h2 request中的version会被设置为(2, 0)。
|
||||
|
||||
brpc server会自动识别HTTP版本,并相应回复,无需用户设置。
|
||||
|
||||
# URL
|
||||
|
||||
URL的一般形式如下图:
|
||||
|
||||
```
|
||||
// URI scheme : http://en.wikipedia.org/wiki/URI_scheme
|
||||
//
|
||||
// foo://username:password@example.com:8042/over/there/index.dtb?type=animal&name=narwhal#nose
|
||||
// \_/ \_______________/ \_________/ \__/ \___/ \_/ \______________________/ \__/
|
||||
// | | | | | | | |
|
||||
// | userinfo host port | | query fragment
|
||||
// | \________________________________/\_____________|____|/ \__/ \__/
|
||||
// scheme | | | | | |
|
||||
// authority | | | | |
|
||||
// path | | interpretable as keys
|
||||
// | |
|
||||
// \_______________________________________________|____|/ \____/ \_____/
|
||||
// | | | | |
|
||||
// hierarchical part | | interpretable as values
|
||||
// | |
|
||||
// interpretable as filename |
|
||||
// |
|
||||
// |
|
||||
// interpretable as extension
|
||||
```
|
||||
|
||||
在上面例子中可以看到,Channel.Init()和cntl.http_request().uri()被设置了相同的URL。为什么Channel不直接利用Init时传入的URL,而需要给uri()再设置一次?
|
||||
|
||||
确实,在简单使用场景下,这两者有所重复,但在复杂场景中,两者差别很大,比如:
|
||||
|
||||
- 访问命名服务(如BNS)下的多个http/h2 server。此时Channel.Init传入的是对该命名服务有意义的名称(如BNS中的节点名称),对uri()的赋值则是包含Host的完整URL(比如"www.foo.com/index.html?name=value")。
|
||||
- 通过http/h2 proxy访问目标server。此时Channel.Init传入的是proxy server的地址,但uri()填入的是目标server的URL。
|
||||
|
||||
## Host字段
|
||||
|
||||
若用户自己填写了"host"字段(大小写不敏感),框架不会修改。
|
||||
|
||||
若用户没有填且URL中包含host,比如http://www.foo.com/path,则http request中会包含"Host: www.foo.com"。
|
||||
|
||||
若用户没有填且URL不包含host,比如"/index.html?name=value",但如果Channel初始化的地址scheme为http(s)且包含域名,则框架会以域名作为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"。
|
||||
|
||||
# 常见设置
|
||||
|
||||
以http request为例 (对response的操作自行替换), 常见操作方式如下所示:
|
||||
|
||||
访问名为Foo的header
|
||||
```c++
|
||||
const std::string* value = cntl->http_request().GetHeader("Foo"); //不存在为NULL
|
||||
```
|
||||
设置名为Foo的header
|
||||
```c++
|
||||
cntl->http_request().SetHeader("Foo", "value");
|
||||
```
|
||||
访问名为Foo的query
|
||||
```c++
|
||||
const std::string* value = cntl->http_request().uri().GetQuery("Foo"); // 不存在为NULL
|
||||
```
|
||||
设置名为Foo的query
|
||||
```c++
|
||||
cntl->http_request().uri().SetQuery("Foo", "value");
|
||||
```
|
||||
设置HTTP Method
|
||||
```c++
|
||||
cntl->http_request().set_method(brpc::HTTP_METHOD_POST);
|
||||
```
|
||||
设置url
|
||||
```c++
|
||||
cntl->http_request().uri() = "http://www.baidu.com";
|
||||
```
|
||||
设置content-type
|
||||
```c++
|
||||
cntl->http_request().set_content_type("text/plain");
|
||||
```
|
||||
访问body
|
||||
```c++
|
||||
butil::IOBuf& buf = cntl->request_attachment();
|
||||
std::string str = cntl->request_attachment().to_string(); // 有拷贝
|
||||
```
|
||||
设置body
|
||||
```c++
|
||||
cntl->request_attachment().append("....");
|
||||
butil::IOBufBuilder os;
|
||||
os << "....";
|
||||
os.move_to(cntl->request_attachment());
|
||||
```
|
||||
|
||||
Notes on http header:
|
||||
|
||||
- 根据[rfc2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),http header的field_name不区分大小写。brpc支持大小写不敏感,同时会在打印时保持用户传入的大小写。
|
||||
- 若http header中出现了相同的field_name, 根据[rfc2616](http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2),value应合并到一起,用逗号(,)分隔,用户自行处理.
|
||||
- query之间用"&"分隔, key和value之间用"="分隔, value可以省略,比如key1=value1&key2&key3=value3中key2是合理的query,值为空字符串。
|
||||
|
||||
# 查看HTTP消息
|
||||
|
||||
打开[-http_verbose](http://brpc.baidu.com:8765/flags/http_verbose)即可看到所有的http/h2 request和response,注意这应该只用于线下调试,而不是线上程序。
|
||||
|
||||
# HTTP错误
|
||||
|
||||
当Server返回的http status code不是2xx时,该次http/h2访问被视为失败,client端会把`cntl->ErrorCode()`设置为EHTTP,用户可通过`cntl->http_response().status_code()`获得具体的http错误。同时server端可以把代表错误的html或json置入`cntl->response_attachment()`作为http body传递回来。
|
||||
|
||||
如果Server也是brpc框架实现的服务,client端希望在http/h2失败时获取brpc Server返回的真实`ErrorCode`,而不是统一设置的`EHTTP`,则需要设置GFlag`-use_http_error_code=true`。
|
||||
|
||||
# 压缩request body
|
||||
|
||||
调用Controller::set_request_compress_type(brpc::COMPRESS_TYPE_GZIP)将尝试用gzip压缩http body。
|
||||
|
||||
“尝试”指的是压缩有可能不发生,条件有:
|
||||
|
||||
- body尺寸小于-http_body_compress_threshold指定的字节数,默认是512。这是因为gzip并不是一个很快的压缩算法,当body较小时,压缩增加的延时可能比网络传输省下的还多。
|
||||
|
||||
# 解压response body
|
||||
|
||||
出于通用性考虑brpc不会自动解压response body,解压代码并不复杂,用户可以自己做,方法如下:
|
||||
|
||||
```c++
|
||||
#include <brpc/policy/gzip_compress.h>
|
||||
...
|
||||
const std::string* encoding = cntl->http_response().GetHeader("Content-Encoding");
|
||||
if (encoding != NULL && *encoding == "gzip") {
|
||||
butil::IOBuf uncompressed;
|
||||
if (!brpc::policy::GzipDecompress(cntl->response_attachment(), &uncompressed)) {
|
||||
LOG(ERROR) << "Fail to un-gzip response body";
|
||||
return;
|
||||
}
|
||||
cntl->response_attachment().swap(uncompressed);
|
||||
}
|
||||
// cntl->response_attachment()中已经是解压后的数据了
|
||||
```
|
||||
|
||||
# 持续下载
|
||||
|
||||
http client往往需要等待到body下载完整才结束RPC,这个过程中body都会存在内存中,如果body超长或无限长(比如直播用的flv文件),那么内存会持续增长,直到超时。这样的http client不适合下载大文件。
|
||||
|
||||
brpc client支持在读取完body前就结束RPC,让用户在RPC结束后再读取持续增长的body。注意这个功能不等同于“支持http chunked mode”,brpc的http实现一直支持解析chunked mode,这里的问题是如何让用户处理超长或无限长的body,和body是否以chunked mode传输无关。
|
||||
|
||||
使用方法如下:
|
||||
|
||||
1. 首先实现ProgressiveReader,接口如下:
|
||||
|
||||
```c++
|
||||
#include <brpc/progressive_reader.h>
|
||||
...
|
||||
class ProgressiveReader {
|
||||
public:
|
||||
// Called when one part was read.
|
||||
// Error returned is treated as *permanent* and the socket where the
|
||||
// data was read will be closed.
|
||||
// A temporary error may be handled by blocking this function, which
|
||||
// may block the HTTP parsing on the socket.
|
||||
virtual butil::Status OnReadOnePart(const void* data, size_t length) = 0;
|
||||
|
||||
// Called when there's nothing to read anymore. The `status' is a hint for
|
||||
// why this method is called.
|
||||
// - status.ok(): the message is complete and successfully consumed.
|
||||
// - otherwise: socket was broken or OnReadOnePart() failed.
|
||||
// This method will be called once and only once. No other methods will
|
||||
// be called after. User can release the memory of this object inside.
|
||||
virtual void OnEndOfMessage(const butil::Status& status) = 0;
|
||||
};
|
||||
```
|
||||
OnReadOnePart在每读到一段数据时被调用,OnEndOfMessage在数据结束或连接断开时调用,实现前仔细阅读注释。
|
||||
|
||||
2. 发起RPC前设置`cntl.response_will_be_read_progressively();`
|
||||
这告诉brpc在读取http response时只要读完header部分RPC就可以结束了。
|
||||
|
||||
3. RPC结束后调用`cntl.ReadProgressiveAttachmentBy(new MyProgressiveReader);`
|
||||
MyProgressiveReader就是用户实现ProgressiveReader的实例。用户可以在这个实例的OnEndOfMessage接口中删除这个实例。
|
||||
|
||||
# 持续上传
|
||||
|
||||
目前Post的数据必须是完整生成好的,不适合POST超长的body。
|
||||
|
||||
# 访问带认证的Server
|
||||
|
||||
根据Server的认证方式生成对应的auth_data,并设置为http header "Authorization"的值。比如用的是curl,那就加上选项`-H "Authorization : <auth_data>"。`
|
||||
|
||||
# 发送https请求
|
||||
https是http over SSL的简称,SSL并不是http特有的,而是对所有协议都有效。开启客户端SSL的一般性方法见[这里](client.md#开启ssl)。为方便使用,brpc会对https开头的uri自动开启SSL。
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user