Repository: alibaba/sentinel-cpp
Branch: master
Commit: 36871db9cc0c
Files: 305
Total size: 1.1 MB
Directory structure:
gitextract_c4eu14oz/
├── .bazelrc
├── .clang-format
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ └── workflows/
│ └── main.yml
├── .gitignore
├── BUILD
├── CMakeLists.txt
├── LICENSE
├── README.md
├── WORKSPACE
├── bazel/
│ ├── BUILD
│ ├── copts.bzl
│ ├── fmtlib.BUILD
│ ├── osx.tbb.BUILD
│ ├── spdlog.BUILD
│ ├── tbb.BUILD
│ └── third_party_repositories.bzl
├── cmake/
│ ├── common.cmake
│ └── third_party.cmake
├── examples/
│ ├── CMakeLists.txt
│ ├── abseil/
│ │ ├── BUILD
│ │ └── abseil_string.cc
│ ├── benchmark/
│ │ ├── BUILD
│ │ └── benchamrk_test.cc
│ ├── cache/
│ │ ├── BUILD
│ │ └── cache_test.cc
│ ├── fmt/
│ │ ├── BUILD
│ │ └── fmt_test.cc
│ ├── gtest/
│ │ ├── BUILD
│ │ └── hello_test.cc
│ ├── json/
│ │ ├── BUILD
│ │ └── nlohmann_json_example.cc
│ ├── libevent/
│ │ ├── BUILD
│ │ └── libevent_echosrv1.c
│ ├── log/
│ │ ├── BUILD
│ │ └── log_test.cc
│ ├── sentinel-cpp/
│ │ ├── BUILD
│ │ ├── basic_concurrency_limit.cc
│ │ ├── basic_param_limit.cc
│ │ ├── basic_qps_limit.cc
│ │ └── basic_system_limit.cc
│ └── tbb/
│ ├── BUILD
│ └── tbb_test.cc
├── format.sh
├── sentinel-core/
│ ├── BUILD
│ ├── circuitbreaker/
│ │ ├── BUILD
│ │ ├── circuit_breaker.cc
│ │ ├── circuit_breaker.h
│ │ ├── error_circuit_breaker.cc
│ │ ├── error_circuit_breaker.h
│ │ ├── rt_circuit_breaker.cc
│ │ ├── rt_circuit_breaker.h
│ │ ├── rule.cc
│ │ ├── rule.h
│ │ ├── rule_manager.cc
│ │ ├── rule_manager.h
│ │ ├── slot.cc
│ │ ├── slot.h
│ │ └── slot_test.cc
│ ├── common/
│ │ ├── BUILD
│ │ ├── constants.h
│ │ ├── entry.h
│ │ ├── entry_context.cc
│ │ ├── entry_context.h
│ │ ├── entry_node.h
│ │ ├── entry_result.cc
│ │ ├── entry_result.h
│ │ ├── entry_type.h
│ │ ├── global_status.cc
│ │ ├── global_status.h
│ │ ├── resource_wrapper.h
│ │ ├── rule.h
│ │ ├── string_resource_wrapper.h
│ │ ├── tracer.cc
│ │ ├── tracer.h
│ │ └── tracer_test.cc
│ ├── config/
│ │ ├── BUILD
│ │ ├── config_constants.h
│ │ ├── local_config.cc
│ │ ├── local_config.h
│ │ └── local_config_test.cc
│ ├── flow/
│ │ ├── BUILD
│ │ ├── default_traffic_shaping_calculator.cc
│ │ ├── default_traffic_shaping_calculator.h
│ │ ├── default_traffic_shaping_checker.cc
│ │ ├── default_traffic_shaping_checker.h
│ │ ├── flow_rule.cc
│ │ ├── flow_rule.h
│ │ ├── flow_rule_checker.cc
│ │ ├── flow_rule_checker.h
│ │ ├── flow_rule_constants.h
│ │ ├── flow_rule_manager.cc
│ │ ├── flow_rule_manager.h
│ │ ├── flow_slot.cc
│ │ ├── flow_slot.h
│ │ ├── flow_slot_test.cc
│ │ ├── throttling_traffic_shaping_checker.cc
│ │ ├── throttling_traffic_shaping_checker.h
│ │ ├── traffic_shaping_calculator.h
│ │ ├── traffic_shaping_checker.h
│ │ ├── traffic_shaping_controller.cc
│ │ ├── traffic_shaping_controller.h
│ │ └── traffic_shaping_controller_test.cc
│ ├── init/
│ │ ├── BUILD
│ │ ├── init_target.h
│ │ ├── init_target_registry.h
│ │ └── init_target_registry_test.cc
│ ├── log/
│ │ ├── BUILD
│ │ ├── block/
│ │ │ ├── BUILD
│ │ │ ├── block_log_task.cc
│ │ │ ├── block_log_task.h
│ │ │ └── block_log_task_test.cc
│ │ ├── log_base.cc
│ │ ├── log_base.h
│ │ ├── log_base_test.cc
│ │ ├── logger.cc
│ │ ├── logger.h
│ │ ├── logger_test.cc
│ │ └── metric/
│ │ ├── BUILD
│ │ ├── metric_log_task.cc
│ │ ├── metric_log_task.h
│ │ ├── metric_reader.cc
│ │ ├── metric_reader.h
│ │ ├── metric_reader_test.cc
│ │ ├── metric_searcher.cc
│ │ ├── metric_searcher.h
│ │ ├── metric_searcher_test.cc
│ │ ├── metric_test_utils.h
│ │ ├── metric_writer.cc
│ │ ├── metric_writer.h
│ │ └── metric_writer_test.cc
│ ├── param/
│ │ ├── BUILD
│ │ ├── param_flow_item.cc
│ │ ├── param_flow_item.h
│ │ ├── param_flow_rule.cc
│ │ ├── param_flow_rule.h
│ │ ├── param_flow_rule_checker.cc
│ │ ├── param_flow_rule_checker.h
│ │ ├── param_flow_rule_constants.h
│ │ ├── param_flow_rule_manager.cc
│ │ ├── param_flow_rule_manager.h
│ │ ├── param_flow_slot.cc
│ │ ├── param_flow_slot.h
│ │ ├── param_flow_slot_test.cc
│ │ └── statistic/
│ │ ├── BUILD
│ │ ├── any_cmp.cc
│ │ ├── any_cmp.h
│ │ ├── any_cmp_test.cc
│ │ ├── lru_cache.h
│ │ ├── param_bucket.cc
│ │ ├── param_bucket.h
│ │ ├── param_event.h
│ │ ├── param_leap_array.cc
│ │ ├── param_leap_array.h
│ │ ├── param_leap_array_key.h
│ │ ├── param_metric.cc
│ │ ├── param_metric.h
│ │ ├── param_metric_test.cc
│ │ └── scalable_cache.h
│ ├── property/
│ │ ├── BUILD
│ │ ├── dynamic_sentinel_property.h
│ │ ├── dynamic_sentinel_property_test.cc
│ │ ├── property_listener.h
│ │ └── sentinel_property.h
│ ├── public/
│ │ ├── BUILD
│ │ ├── sph_u.h
│ │ └── sph_u_test.cc
│ ├── slot/
│ │ ├── BUILD
│ │ ├── base/
│ │ │ ├── BUILD
│ │ │ ├── default_slot_chain_impl.cc
│ │ │ ├── default_slot_chain_impl.h
│ │ │ ├── default_slot_chain_impl_test.cc
│ │ │ ├── rule_checker_slot.h
│ │ │ ├── slot.h
│ │ │ ├── slot_base.h
│ │ │ ├── slot_chain.h
│ │ │ ├── stats_slot.h
│ │ │ ├── token_result.cc
│ │ │ └── token_result.h
│ │ ├── global_slot_chain.cc
│ │ ├── global_slot_chain.h
│ │ ├── log_slot.cc
│ │ ├── log_slot.h
│ │ ├── resource_node_builder_slot.cc
│ │ ├── resource_node_builder_slot.h
│ │ ├── resource_node_builder_slot_test.cc
│ │ ├── statistic_slot.cc
│ │ ├── statistic_slot.h
│ │ └── statistic_slot_test.cc
│ ├── statistic/
│ │ ├── base/
│ │ │ ├── BUILD
│ │ │ ├── bucket_leap_array.cc
│ │ │ ├── bucket_leap_array.h
│ │ │ ├── bucket_leap_array_test.cc
│ │ │ ├── leap_array.h
│ │ │ ├── metric.h
│ │ │ ├── metric_bucket.cc
│ │ │ ├── metric_bucket.h
│ │ │ ├── metric_event.h
│ │ │ ├── metric_item.cc
│ │ │ ├── metric_item.h
│ │ │ ├── metric_item_test.cc
│ │ │ ├── sliding_window_metric.cc
│ │ │ ├── sliding_window_metric.h
│ │ │ ├── sliding_window_metric_test.cc
│ │ │ ├── stat_config.h
│ │ │ ├── stat_config_manager.cc
│ │ │ ├── stat_config_manager.h
│ │ │ └── window_wrap.h
│ │ └── node/
│ │ ├── BUILD
│ │ ├── cluster_node.cc
│ │ ├── cluster_node.h
│ │ ├── node.h
│ │ ├── resource_node_storage.cc
│ │ ├── resource_node_storage.h
│ │ ├── statistic_node.cc
│ │ └── statistic_node.h
│ ├── system/
│ │ ├── BUILD
│ │ ├── system_rule.cc
│ │ ├── system_rule.h
│ │ ├── system_rule_manager.cc
│ │ ├── system_rule_manager.h
│ │ ├── system_slot.cc
│ │ ├── system_slot.h
│ │ ├── system_slot_mock.h
│ │ ├── system_slot_test.cc
│ │ ├── system_status_listener.cc
│ │ ├── system_status_listener.h
│ │ └── system_status_listener_test.cc
│ ├── test/
│ │ └── mock/
│ │ ├── common/
│ │ │ ├── BUILD
│ │ │ ├── mock.cc
│ │ │ └── mock.h
│ │ ├── flow/
│ │ │ ├── BUILD
│ │ │ ├── mock.cc
│ │ │ └── mock.h
│ │ ├── init/
│ │ │ ├── BUILD
│ │ │ └── mock.h
│ │ ├── property/
│ │ │ ├── BUILD
│ │ │ ├── mock.cc
│ │ │ └── mock.h
│ │ ├── slot/
│ │ │ ├── BUILD
│ │ │ ├── mock.cc
│ │ │ └── mock.h
│ │ └── statistic/
│ │ ├── base/
│ │ │ ├── BUILD
│ │ │ ├── mock.cc
│ │ │ └── mock.h
│ │ └── node/
│ │ ├── BUILD
│ │ ├── mock.cc
│ │ └── mock.h
│ ├── transport/
│ │ ├── BUILD
│ │ ├── command/
│ │ │ ├── BUILD
│ │ │ ├── command_handler.h
│ │ │ ├── command_request.cc
│ │ │ ├── command_request.h
│ │ │ ├── command_request_test.cc
│ │ │ ├── command_response.h
│ │ │ ├── handler/
│ │ │ │ ├── BUILD
│ │ │ │ ├── fetch_cluster_node_handler.cc
│ │ │ │ ├── fetch_cluster_node_handler.h
│ │ │ │ ├── fetch_cluster_node_handler_test.cc
│ │ │ │ ├── fetch_metric_log_handler.cc
│ │ │ │ ├── fetch_metric_log_handler.h
│ │ │ │ ├── get_switch_status_handler.cc
│ │ │ │ ├── get_switch_status_handler.h
│ │ │ │ ├── set_switch_status_handler.cc
│ │ │ │ ├── set_switch_status_handler.h
│ │ │ │ ├── set_switch_status_handler_test.cc
│ │ │ │ ├── version_handler.cc
│ │ │ │ ├── version_handler.h
│ │ │ │ └── vo/
│ │ │ │ ├── BUILD
│ │ │ │ ├── statistic_node_vo.cc
│ │ │ │ └── statistic_node_vo.h
│ │ │ ├── http_command_center.cc
│ │ │ ├── http_command_center.h
│ │ │ ├── http_command_utils.cc
│ │ │ ├── http_command_utils.h
│ │ │ ├── http_server.cc
│ │ │ ├── http_server.h
│ │ │ ├── http_server_init_target.cc
│ │ │ └── http_server_init_target.h
│ │ ├── common/
│ │ │ ├── BUILD
│ │ │ ├── event_loop_thread.cc
│ │ │ ├── event_loop_thread.h
│ │ │ └── event_loop_thread_test.cc
│ │ └── constants.h
│ └── utils/
│ ├── BUILD
│ ├── file_utils.cc
│ ├── file_utils.h
│ ├── macros.h
│ ├── time_utils.cc
│ ├── time_utils.h
│ ├── utils.cc
│ └── utils.h
├── sentinel-datasource-extension/
│ ├── datasource/
│ │ ├── BUILD
│ │ ├── abstract_readable_data_source.h
│ │ ├── abstract_readable_data_source_unittests.cc
│ │ ├── converter.h
│ │ └── readable_data_source.h
│ └── test/
│ └── mock/
│ └── datasource/
│ ├── BUILD
│ ├── mock.cc
│ └── mock.h
├── tests/
│ ├── BUILD
│ └── tsan-flow.cc
└── third_party/
└── nlohmann/
├── BUILD
└── json.hpp
================================================
FILE CONTENTS
================================================
================================================
FILE: .bazelrc
================================================
# Clang TSAN
build:clang-tsan --copt -fsanitize=thread
build:clang-tsan --linkopt -fsanitize=thread
================================================
FILE: .clang-format
================================================
# Use the Google style in this project.
BasedOnStyle: Google
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
## Issue Description
Type: *bug report* or *feature request*
### Describe what happened (or what feature you want)
### Describe what you expected to happen
### How to reproduce it (as minimally and precisely as possible)
1.
2.
3.
### Tell us your environment
### Anything else we need to know?
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
### Describe what this PR does / why we need it
### Does this pull request fix one issue?
### Describe how you did it
### Describe how to verify it
### Special notes for reviews
================================================
FILE: .github/workflows/main.yml
================================================
name: CI
on:
push:
branches: master
pull_request:
branches: "*"
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
java: [8]
os: [ubuntu-18.04]
steps:
- uses: actions/checkout@v2
- name: Install bazel
run: |
wget https://github.com/bazelbuild/bazel/releases/download/3.7.0/bazel_3.7.0-linux-x86_64.deb
sudo dpkg -i bazel_3.7.0-linux-x86_64.deb
- name: Install clang8
run: |
echo "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" | sudo tee -a /etc/apt/sources.list
echo "deb-src http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main" | sudo tee -a /etc/apt/sources.list
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update
sudo apt-get -y install clang-8 clang-format-8
- name: Clang format checker
run: |
bash format.sh
- name: Library build
run: |
bazel build //sentinel-core/... && bazel build //sentinel-datasource-extension/...
- name: Sentinel core unit tests
run: |
bazel test --test_filter=*-ParamMetricTest.TestOperateMetric --test_output=errors --strategy=TestRunner=standalone //sentinel-core/...
- name: Sentinel datasource extension tests
run: |
bazel build //sentinel-core/... && bazel build //sentinel-datasource-extension/...
- name: tsan for flow control
run: |
bazel build -c dbg --config=clang-tsan //tests/... && ./bazel-bin/tests/tsan-flow
================================================
FILE: .gitignore
================================================
/bazel-*
BROWSE
/build
/build_*
.cache
/ci/bazel-*
/ci/prebuilt/thirdparty
/ci/prebuilt/thirdparty_build
compile_commands.json
cscope.*
.deps
/docs/landing_source/.bundle
/generated
*.pyc
**/pyformat
SOURCE_VERSION
*.sw*
tags
TAGS
/test/coverage/BUILD
/tools/.aspell.en.pws
.vimrc
.vs
.vscode
.DS_Store
.gdb_history
================================================
FILE: BUILD
================================================
load("@rules_foreign_cc//tools/build_defs:configure.bzl", "configure_make")
# load("@rules_foreign_cc//tools/build_defs:make.bzl", "make")
configure_make(
name = "libevent",
visibility = ["//visibility:public"],
configure_options = [
"--enable-shared=no",
"--disable-libevent-regress",
"--disable-openssl",
],
lib_source = "@com_github_libevent//:all",
out_lib_dir = "lib",
)
# make(
# name = "com_github_libtbb",
# visibility = ["//visibility:public"],
# out_lib_dir = "lib",
# lib_source = "@com_github_libtbb//:all",
# )
================================================
FILE: CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.14)
project(sentinel-cpp VERSION 1.0.1 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 14)
set(SENTINEL_ROOT_DIR "${CMAKE_SOURCE_DIR}")
set(SENTINEL_CORE_ROOT_DIR "${CMAKE_SOURCE_DIR}/sentinel-core")
include( cmake/common.cmake )
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner].
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.
================================================
FILE: README.md
================================================
[](https://travis-ci.org/alibaba/sentinel-cpp)
[](https://www.apache.org/licenses/LICENSE-2.0.html)
[](https://gitter.im/alibaba/Sentinel)
# Sentinel: The Sentinel of Your Microservices
## Build
1. Install the latest version of [Bazel](https://bazel.build/versions/master/docs/install.html) in your environment.
2. Build the project.
```sh
bazel build -c opt //...
```
3. Run the [QPS limiting demo](https://github.com/alibaba/sentinel-cpp/blob/master/examples/sentinel-cpp/basic_qps_limit.cc):
```sh
CSP_SENTINEL_APP_NAME=my-demo ./bazel-bin/examples/sentinel-cpp/sentinel_basic_qps_limit
```
================================================
FILE: WORKSPACE
================================================
# Copyright 2019 Alibaba Inc. 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.
workspace(name = "com_alibaba_sentinel")
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Rule repository
http_archive(
name = "rules_foreign_cc",
strip_prefix = "rules_foreign_cc-master",
url = "https://github.com/bazelbuild/rules_foreign_cc/archive/master.zip",
)
load("@rules_foreign_cc//:workspace_definitions.bzl", "rules_foreign_cc_dependencies")
rules_foreign_cc_dependencies()
# abseil-cpp
http_archive(
name = "com_google_absl",
urls = ["https://github.com/abseil/abseil-cpp/archive/61c9bf3e3e1c28a4aa6d7f1be4b37fd473bb5529.tar.gz"], # 2019-06-05
strip_prefix = "abseil-cpp-61c9bf3e3e1c28a4aa6d7f1be4b37fd473bb5529",
sha256 = "7ddf863ddced6fa5bf7304103f9c7aa619c20a2fcf84475512c8d3834b9d14fa",
)
# Google Test
http_archive(
name = "com_google_googletest",
urls = ["https://github.com/google/googletest/archive/8b6d3f9c4a774bef3081195d422993323b6bb2e0.zip"], # 2019-03-05
strip_prefix = "googletest-8b6d3f9c4a774bef3081195d422993323b6bb2e0",
sha256 = "d21ba93d7f193a9a0ab80b96e8890d520b25704a6fac976fe9da81fffb3392e3",
)
# Google Benchmark
http_archive(
name = "com_google_benchmark",
urls = ["https://github.com/google/benchmark/archive/505be96ab23056580a3a2315abba048f4428b04e.tar.gz"],
strip_prefix = "benchmark-505be96ab23056580a3a2315abba048f4428b04e",
sha256 = "0de43b6eaddd356f1d6cd164f73f37faf2f6c96fd684e1f7ea543ce49c1d144e",
)
load("//bazel:third_party_repositories.bzl", "include_third_party_repositories")
include_third_party_repositories()
================================================
FILE: bazel/BUILD
================================================
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//visibility:public"])
config_setting(
name = "llvm_compiler",
values = {
"compiler": "llvm",
},
)
config_setting(
name = "windows",
values = {
"cpu": "x64_windows",
},
)
config_setting(
name = "is_osx",
define_values = {"os":"osx"},
)
================================================
FILE: bazel/copts.bzl
================================================
"""
Flags specified here must not impact ABI. Code compiled with and without these
opts will be linked together, and in some cases headers compiled with and
without these options will be part of the same program.
We use the same flags as absl.
"""
load(
"@com_google_absl//absl:copts/GENERATED_copts.bzl",
"ABSL_GCC_EXCEPTIONS_FLAGS",
"ABSL_GCC_FLAGS",
"ABSL_GCC_TEST_FLAGS",
"ABSL_LLVM_EXCEPTIONS_FLAGS",
"ABSL_LLVM_FLAGS",
"ABSL_LLVM_TEST_FLAGS",
"ABSL_MSVC_EXCEPTIONS_FLAGS",
"ABSL_MSVC_FLAGS",
"ABSL_MSVC_LINKOPTS",
"ABSL_MSVC_TEST_FLAGS",
)
WERROR = ["-Werror=return-type", "-Werror=switch"]
DEFAULT_COPTS = select({
"//bazel:windows": ABSL_MSVC_FLAGS,
"//bazel:llvm_compiler": ABSL_LLVM_FLAGS,
"//conditions:default": ABSL_GCC_FLAGS + WERROR + ["-std=c++14"],
})
TEST_COPTS = DEFAULT_COPTS + select({
"//bazel:windows": ABSL_MSVC_TEST_FLAGS,
"//bazel:llvm_compiler": ABSL_LLVM_TEST_FLAGS,
"//conditions:default": ABSL_GCC_TEST_FLAGS + WERROR + ["-std=c++14"],
})
================================================
FILE: bazel/fmtlib.BUILD
================================================
licenses(["notice"]) # Apache 2
cc_library(
name = "fmtlib",
srcs = glob([
"fmt/*.cc",
]),
hdrs = glob([
"include/fmt/*.h",
]),
defines = ["FMT_HEADER_ONLY"],
includes = ["include"],
visibility = ["//visibility:public"],
)
================================================
FILE: bazel/osx.tbb.BUILD
================================================
licenses(["notice"]) # Apache 2
package(default_visibility = ["//visibility:public"])
genrule(
name = "build_tbb_osx",
srcs = glob(["**"]) + [
"@local_config_cc//:toolchain",
],
cmd = """
set -e
set -u
WORK_DIR=$$PWD
DEST_DIR=$$PWD/$(@D)
cd $$(dirname $(location :Makefile))
make
files=build/*/*.dylib;
echo cp $$files $$DEST_DIR
cp $$files $$DEST_DIR
cd $$WORK_DIR
""",
outs = [
"libtbb.dylib",
"libtbbmalloc.dylib",
"libtbbmalloc_proxy.dylib",
],
)
cc_library(
name = "tbb_osx",
hdrs = glob([
"include/serial/**",
"include/tbb/**/**",
]),
srcs = [
"libtbb.dylib",
"libtbbmalloc.dylib",
"libtbbmalloc_proxy.dylib",
],
includes = ["include"],
visibility = ["//visibility:public"],
)
================================================
FILE: bazel/spdlog.BUILD
================================================
licenses(["notice"]) # Apache 2
cc_library(
name = "spdlog",
hdrs = glob([
"include/**/*.cc",
"include/**/*.h",
]),
defines = ["SPDLOG_FMT_EXTERNAL"],
includes = ["include"],
visibility = ["//visibility:public"],
deps = ["@com_github_fmtlib_fmt//:fmtlib"],
)
================================================
FILE: bazel/tbb.BUILD
================================================
licenses(["notice"]) # Apache 2
package(default_visibility = ["//visibility:public"])
genrule(
name = "build_tbb",
srcs = glob(["**"]) + [
"@local_config_cc//:toolchain",
],
cmd = """
set -e
set -u
WORK_DIR=$$PWD
DEST_DIR=$$PWD/$(@D)
cd $$(dirname $(location :Makefile))
make
if [[ $$(echo `uname` | grep 'Dar') != "" ]]; then
files=build/*/*.dylib;
else files=build/*/*.so*;
fi
echo cp $$files $$DEST_DIR
cp $$files $$DEST_DIR
cd $$WORK_DIR
""",
outs = [
"libtbb.so",
"libtbbmalloc.so",
"libtbbmalloc_proxy.so",
"libtbb.so.2",
"libtbbmalloc.so.2",
"libtbbmalloc_proxy.so.2",
],
)
cc_library(
name = "tbb",
hdrs = glob([
"include/serial/**",
"include/tbb/**/**",
]),
srcs = [
"libtbb.so",
"libtbbmalloc.so",
"libtbbmalloc_proxy.so",
"libtbb.so.2",
"libtbbmalloc.so.2",
"libtbbmalloc_proxy.so.2",
],
includes = ["include"],
visibility = ["//visibility:public"],
)
================================================
FILE: bazel/third_party_repositories.bzl
================================================
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive", "http_file")
all_content = """filegroup(name = "all", srcs = glob(["**"]), visibility = ["//visibility:public"])"""
def include_third_party_repositories():
http_archive(
name = "com_github_libevent",
build_file_content = all_content,
strip_prefix = "libevent-2.1.8-stable",
urls = ["https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/libevent-2.1.8-stable.tar.gz"],
)
http_archive(
name = "com_github_libtbb",
build_file = "//bazel:tbb.BUILD",
strip_prefix = "oneTBB-2020.3",
urls = ["https://github.com/oneapi-src/oneTBB/archive/v2020.3.tar.gz"],
sha256 = "ebc4f6aa47972daed1f7bf71d100ae5bf6931c2e3144cf299c8cc7d041dca2f3",
)
http_archive(
name = "com_github_libtbb_osx",
build_file = "//bazel:osx.tbb.BUILD",
strip_prefix = "oneTBB-2020.3",
urls = ["https://github.com/oneapi-src/oneTBB/archive/v2020.3.tar.gz"],
sha256 = "ebc4f6aa47972daed1f7bf71d100ae5bf6931c2e3144cf299c8cc7d041dca2f3",
)
http_archive(
name = "com_github_fmtlib_fmt",
sha256 = "4c0741e10183f75d7d6f730b8708a99b329b2f942dad5a9da3385ab92bb4a15c",
strip_prefix = "fmt-5.3.0",
urls = ["https://github.com/fmtlib/fmt/releases/download/5.3.0/fmt-5.3.0.zip"],
build_file = "//bazel:fmtlib.BUILD",
)
http_archive(
name = "com_github_gabime_spdlog",
build_file = "//bazel:spdlog.BUILD",
sha256 = "160845266e94db1d4922ef755637f6901266731c4cb3b30b45bf41efa0e6ab70",
strip_prefix = "spdlog-1.3.1",
urls = ["https://github.com/gabime/spdlog/archive/v1.3.1.tar.gz"],
)
================================================
FILE: cmake/common.cmake
================================================
cmake_minimum_required( VERSION 3.14 )
find_package(Libevent REQUIRED)
include_directories(${LIBEVENT_INCLUDE_DIR})
include( ${SENTINEL_ROOT_DIR}/cmake/third_party.cmake )
include_directories( ${SENTINEL_ROOT_DIR} )
include_directories(${SENTINEL_CORE_ROOT_DIR}/common)
include_directories(${SENTINEL_CORE_ROOT_DIR}/config)
include_directories(${SENTINEL_CORE_ROOT_DIR}/property)
include_directories(${SENTINEL_CORE_ROOT_DIR}/public)
# common
file(GLOB COMMON_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/common/*.cc")
file(GLOB COMMON_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/common/*_test.cc")
list(REMOVE_ITEM COMMON_SOURCE_FILES ${COMMON_TEST_SOURCE_FILES})
# config
file(GLOB CONFIG_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/config/*.cc")
file(GLOB CONFIG_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/config/*_test.cc")
list(REMOVE_ITEM CONFIG_SOURCE_FILES ${CONFIG_TEST_SOURCE_FILES})
# log
file(GLOB_RECURSE LOG_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/log/*.cc")
file(GLOB_RECURSE LOG_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/log/*_test.cc")
list(REMOVE_ITEM LOG_SOURCE_FILES ${LOG_TEST_SOURCE_FILES})
# utils
file(GLOB UTILS_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/utils/*.cc")
# file(GLOB UTILS_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/utils/*test.cc")
# list(REMOVE_ITEM UTILS_SOURCE_FILES ${UTILS_TEST_SOURCE_FILES})
# slot
file(GLOB_RECURSE SLOT_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/slot/*.cc")
file(GLOB_RECURSE SLOT_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/slot/*_test.cc")
list(REMOVE_ITEM SLOT_SOURCE_FILES ${SLOT_TEST_SOURCE_FILES})
# statistic
file(GLOB_RECURSE STATISTIC_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/statistic/*.cc")
file(GLOB_RECURSE STATISTIC_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/statistic/*_test.cc")
list(REMOVE_ITEM STATISTIC_SOURCE_FILES ${STATISTIC_TEST_SOURCE_FILES})
# transport
file(GLOB_RECURSE TRANSPORT_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/transport/*.cc")
file(GLOB_RECURSE TRANSPORT_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/transport/*_test.cc")
list(REMOVE_ITEM TRANSPORT_SOURCE_FILES ${TRANSPORT_TEST_SOURCE_FILES})
# flow
file(GLOB FLOW_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/flow/*.cc")
file(GLOB FLOW_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/flow/*_test.cc")
list(REMOVE_ITEM FLOW_SOURCE_FILES ${FLOW_TEST_SOURCE_FILES})
# circuitbreaker
file(GLOB CIRCUIT_BREAKER_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/circuitbreaker/*.cc")
file(GLOB CIRCUIT_BREAKER_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/circuitbreaker/*_test.cc")
list(REMOVE_ITEM CIRCUIT_BREAKER_SOURCE_FILES ${CIRCUIT_BREAKER_TEST_SOURCE_FILES})
# param
file(GLOB_RECURSE PARAM_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/param/*.cc")
file(GLOB_RECURSE PARAM_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/param/*_test.cc")
list(REMOVE_ITEM PARAM_SOURCE_FILES ${PARAM_TEST_SOURCE_FILES})
# system
file(GLOB_RECURSE SYSTEM_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/system/*.cc")
file(GLOB_RECURSE SYSTEM_TEST_SOURCE_FILES "${SENTINEL_CORE_ROOT_DIR}/system/*_test.cc")
list(REMOVE_ITEM SYSTEM_SOURCE_FILES ${SYSTEM_TEST_SOURCE_FILES})
list(APPEND
SENTINEL_SOURCE_FILES
${COMMON_SOURCE_FILES}
${CONFIG_SOURCE_FILES}
${LOG_SOURCE_FILES}
${UTILS_SOURCE_FILES}
${SLOT_SOURCE_FILES}
${STATISTIC_SOURCE_FILES}
${TRANSPORT_SOURCE_FILES}
${FLOW_SOURCE_FILES}
${CIRCUIT_BREAKER_SOURCE_FILES}
${PARAM_SOURCE_FILES}
${SYSTEM_SOURCE_FILES})
add_library(sentinel STATIC ${SENTINEL_SOURCE_FILES})
target_link_libraries(sentinel PUBLIC
libevent::core
libevent::extra
libevent::pthreads
TBB::tbb
spdlog
absl::flat_hash_set
absl::str_format
absl::synchronization
absl::strings
absl::any)
================================================
FILE: cmake/third_party.cmake
================================================
cmake_minimum_required( VERSION 3.14 )
include( FetchContent )
#######################################################################
# Declare project dependencies
#######################################################################
FetchContent_Declare( abseil
GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git
GIT_TAG 20230125.0
)
FetchContent_Declare( spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.11.0
)
FetchContent_Declare( onetbb
GIT_REPOSITORY https://github.com/oneapi-src/oneTBB.git
GIT_TAG v2021.9.0
)
FetchContent_MakeAvailable(abseil)
FetchContent_MakeAvailable(spdlog)
FetchContent_MakeAvailable(onetbb)
================================================
FILE: examples/CMakeLists.txt
================================================
cmake_minimum_required(VERSION 3.14)
project(sentinel-cpp VERSION 1.0.1 LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 14)
set(SENTINEL_CORE_ROOT_DIR "${CMAKE_SOURCE_DIR}/../sentinel-core")
set(SENTINEL_ROOT_DIR "${CMAKE_SOURCE_DIR}/..")
include( ${CMAKE_SOURCE_DIR}/../cmake/common.cmake )
# basic_qps_limit
add_executable(basic_qps_limit ${CMAKE_SOURCE_DIR}/../examples/sentinel-cpp/basic_qps_limit.cc)
target_link_libraries(basic_qps_limit sentinel)
# basic_concurrency_limit
add_executable(basic_concurrency_limit ${CMAKE_SOURCE_DIR}/../examples/sentinel-cpp/basic_concurrency_limit.cc)
target_link_libraries(basic_concurrency_limit sentinel)
# basic_param_limit
add_executable(basic_param_limit ${CMAKE_SOURCE_DIR}/../examples/sentinel-cpp/basic_param_limit.cc)
target_link_libraries(basic_param_limit sentinel)
# basic_system_limit
add_executable(basic_system_limit ${CMAKE_SOURCE_DIR}/../examples/sentinel-cpp/basic_system_limit.cc)
target_link_libraries(basic_system_limit sentinel)
================================================
FILE: examples/abseil/BUILD
================================================
cc_binary(
name = "asbeil_string",
srcs = ["abseil_string.cc"],
deps = [
"@com_google_absl//absl/strings",
],
)
================================================
FILE: examples/abseil/abseil_string.cc
================================================
#include
#include
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
std::string Greet(absl::string_view person) {
return absl::StrCat("Hello ", person);
}
int main(int argc, char* argv[]) {
std::cout << Greet(argc < 2 ? "world" : argv[1]) << std::endl;
}
================================================
FILE: examples/benchmark/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
cc_binary(
name = "benchmark_test",
srcs = ["benchamrk_test.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/public:sph_u_lib",
"@com_google_benchmark//:benchmark",
"//sentinel-core/log/metric:metric_log_task_lib",
],
)
================================================
FILE: examples/benchmark/benchamrk_test.cc
================================================
#include
#include
#include
#include
#include
#include "sentinel-core/log/metric/metric_log_task.h"
#include "sentinel-core/param/param_flow_rule_constants.h"
#include "sentinel-core/param/param_flow_rule_manager.h"
#include "sentinel-core/public/sph_u.h"
static void BM_StringCreation(benchmark::State& state) {
for (auto _ : state) std::string empty_string;
}
// Register the function as a benchmark
// BENCHMARK(BM_StringCreation);
static void ParamRun(benchmark::State& state) {
std::string myResource("some_param_test");
Sentinel::Param::ParamFlowRule rule0, rule1, rule12;
rule0.set_resource(myResource);
rule0.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule0.set_threshold(10000);
rule0.set_cache_size(200);
rule0.set_interval_in_ms(1000);
rule0.set_param_idx(0);
rule1.set_resource(myResource);
rule1.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule1.set_threshold(INT_MAX);
rule1.set_param_idx(1);
rule1.set_interval_in_ms(1000);
Sentinel::Param::ParamFlowItem item0(std::string("nonexisting-str"),
Sentinel::Param::ParamItemType::kString,
100);
rule1.set_param_flow_item_list({item0});
rule12.set_resource("non-existing-resource"); // should not work
rule12.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule12.set_threshold(1);
rule12.set_param_idx(1);
rule12.set_interval_in_ms(1000);
Sentinel::Param::ParamFlowRuleManager::GetInstance().LoadRules(
{rule1, rule0, rule12});
for (auto _ : state) {
int randParam = rand() % 10;
auto r =
Sentinel::SphU::Entry(myResource.c_str(), Sentinel::EntryType::IN, 1, 0,
randParam); //, std::string("example"));
r->Exit();
}
}
static void ParamNotRun(benchmark::State& state) {
std::string myResource("some_param_test");
Sentinel::Log::Logger::InitDefaultLogger();
Sentinel::Log::MetricLogTask metric_log_task;
metric_log_task.Initialize();
for (auto _ : state) {
int randParam = rand() % 10;
auto r =
Sentinel::SphU::Entry(myResource.c_str(), Sentinel::EntryType::IN, 1);
r->Exit();
}
}
static void NoCache(benchmark::State& state) {
std::unordered_map m;
int i = 0;
for (auto _ : state) {
m.insert(std::make_pair<>(i, i + 1));
i++;
}
}
enum class ParamItemType { kInt32 = 0, kInt64, kString };
class MyAny : public absl::any {
public:
// std::atomic type_;
ParamItemType my_type_;
// MyAny(int v) : absl::any(v), type_(ParamItemType::kInt) {}
MyAny(int32_t v) : absl::any(v), my_type_(ParamItemType::kInt32) {}
MyAny(int64_t v) : absl::any(v), my_type_(ParamItemType::kInt64) {}
MyAny(std::string v) : absl::any(v), my_type_(ParamItemType::kString) {}
ParamItemType my_type() const noexcept { return my_type_; }
operator int32_t() {
assert(my_type_ == ParamItemType::kInt32);
return absl::any_cast(*this);
}
operator int64_t() {
assert(my_type_ == ParamItemType::kInt64);
return absl::any_cast(*this);
}
operator std::string() {
assert(my_type_ == ParamItemType::kString);
return absl::any_cast(*this);
}
friend bool operator==(const MyAny& a0, const MyAny& a1) {
std::cout << "==," << std::endl;
if (a0.my_type_ == a1.my_type_) {
switch (a0.my_type_) {
case ParamItemType::kInt32:
return absl::any_cast(a0) == absl::any_cast(a1);
case ParamItemType::kInt64:
return absl::any_cast(a0) == absl::any_cast(a1);
case ParamItemType::kString:
return absl::any_cast(a0) ==
absl::any_cast(a1);
default:
return false;
}
}
if (a0.my_type_ == ParamItemType::kInt32 &&
a1.my_type_ == ParamItemType::kInt64) {
return absl::any_cast(a0) == absl::any_cast(a1);
} else if (a0.my_type_ == ParamItemType::kInt64 &&
a1.my_type_ == ParamItemType::kInt32) {
return absl::any_cast(a0) == absl::any_cast(a1);
}
return false;
}
};
namespace std {
template <>
struct hash {
size_t operator()(const MyAny& any) const {
// std::cout << "hash of " << static_cast(any.my_type()) << ", " <<
// any.type().name() << ", " << any.has_value() << std::endl;
int val = -1;
switch (any.my_type()) {
case ParamItemType::kInt32:
return hash{}(absl::any_cast(any));
case ParamItemType::kInt64:
return hash{}(absl::any_cast(any));
case ParamItemType::kString:
return hash{}(absl::any_cast(any));
default:
return -1;
}
}
};
} // namespace std
static void WithCache(benchmark::State& state) {
std::unordered_map m;
int32_t i = 0;
for (auto _ : state) {
m.insert(std::make_pair<>(i, i + 1));
i++;
}
}
// BENCHMARK(ParamRun)->MinTime(8);
BENCHMARK(NoCache)->MinTime(4);
BENCHMARK(WithCache)->MinTime(4);
BENCHMARK_MAIN();
================================================
FILE: examples/cache/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_binary(
name = "cache_test",
srcs = ["cache_test.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/param/statistic:scalable_cache_lib",
"//sentinel-core/param/statistic:any_cmp_lib",
"//sentinel-core/log:logger_lib",
],
)
================================================
FILE: examples/cache/cache_test.cc
================================================
#include
#include
#include
#include
#include
#include "sentinel-core/log/logger.h"
#include "sentinel-core/param/statistic/any_cmp.h"
#include "sentinel-core/param/statistic/scalable_cache.h"
using ScalableCache =
Sentinel::Param::ThreadSafeLRUCache;
using ScalableCacheUniquePtr = std::unique_ptr;
ScalableCache cache(100);
std::atomic winStart(0);
std::atomic stop(false);
constexpr int THREAD_NUM = 10;
int64_t CurrentTimeMillis() {
return std::chrono::duration_cast(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void Increase(int id, int cnt) {
while (!stop.load()) {
int64_t curTime = CurrentTimeMillis();
if (curTime - 500 > winStart) {
// int64_t sz = cache.size();
// Sentinel::Log::Logger::Log(Sentinel::Log::Logger::kDefaultFileLogger,
// Sentinel::info, "size={}", sz);
cache.clear();
winStart.store(curTime - curTime % 500);
}
cache.increase(id, cnt);
}
}
void Timer() {
int cnt = 6;
while (!stop.load()) {
std::cout << "[" << cnt << "] " << cache.size() << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if (cnt-- <= 0) {
stop.store(true);
}
}
}
int main() {
Sentinel::Log::Logger::InitDefaultLogger();
std::thread myThreads[THREAD_NUM];
std::thread t0(Timer);
for (int i = 0; i < THREAD_NUM; i++) {
myThreads[i] = std::thread(Increase, 11000 + 100 * i, 1);
}
t0.join();
for (int i = 0; i < THREAD_NUM; i++) {
myThreads[i].join();
}
// std::vector> pairs;
// cache.snapshotPairs(pairs);
// for (const auto& pair : pairs) {
// std::cout << pair.second << " ";
// } std::cout << "\n";
return 0;
}
================================================
FILE: examples/fmt/BUILD
================================================
cc_binary(
name = "fmt_test",
srcs = ["fmt_test.cc"],
deps = [
"@com_github_fmtlib_fmt//:fmtlib",
],
)
================================================
FILE: examples/fmt/fmt_test.cc
================================================
#include
#include
#include "fmt/format.h"
int main() {
std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
std::cout << s << std::endl;
return 0;
}
================================================
FILE: examples/gtest/BUILD
================================================
cc_test(
name = "hello_test",
srcs = ["hello_test.cc"],
deps = [
"@com_google_googletest//:gtest_main",
],
)
================================================
FILE: examples/gtest/hello_test.cc
================================================
#include
#include "gtest/gtest.h"
TEST(HelloTest, Basic) { EXPECT_EQ("Hello tester", "Hello tester"); }
================================================
FILE: examples/json/BUILD
================================================
cc_binary(
name = "nlohmann_json_example",
srcs = ["nlohmann_json_example.cc"],
deps = [
"//third_party/nlohmann:nlohmann_json_lib",
],
)
================================================
FILE: examples/json/nlohmann_json_example.cc
================================================
#include
#include
#include "third_party/nlohmann/json.hpp"
int main() {
try {
auto j2 = nlohmann::json::parse("sa");
} catch (std::exception& ex) {
std::cerr << "Exception: " << ex.what() << std::endl;
}
// create a JSON array
auto j3 = nlohmann::json::parse("{ \"happy\": true, \"pi\": 3.141 }");
auto c = j3["pi"];
if (c.is_number()) {
int d = static_cast(c);
std::cout << d << std::endl;
}
std::cout << typeid(c).name() << std::endl;
return 0;
}
================================================
FILE: examples/libevent/BUILD
================================================
cc_binary(
name = "libevent_echosrv1",
srcs = ["libevent_echosrv1.c"],
deps = [
"//:libevent",
],
)
================================================
FILE: examples/libevent/libevent_echosrv1.c
================================================
// Copied from
// https://github.com/jasonish/libevent-examples/blob/6d20f0d86c2cd263f5edff28862bc09ce4a3220f/echo-server/libevent_echosrv1.c
// for testing purposes only.
/*
* Copyright (c) 2011, Jason Ish
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
/*
* libevent echo server example.
*/
#include
#include
#include
/* For inet_ntoa. */
#include
/* Required by event.h. */
#include
#include
#include
#include
#include
#include
#include
#include
/* Libevent. */
#include
/* Port to listen on. */
#define SERVER_PORT 5555
/**
* A struct for client specific data, in this simple case the only
* client specific data is the read event.
*/
struct client {
struct event ev_read;
};
/**
* Set a socket to non-blocking mode.
*/
int setnonblock(int fd) {
int flags;
flags = fcntl(fd, F_GETFL);
if (flags < 0) return flags;
flags |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, flags) < 0) return -1;
return 0;
}
/**
* This function will be called by libevent when the client socket is
* ready for reading.
*/
void on_read(int fd, short ev, void *arg) {
struct client *client = (struct client *)arg;
u_char buf[8196];
int len, wlen;
len = read(fd, buf, sizeof(buf));
if (len == 0) {
/* Client disconnected, remove the read event and the
* free the client structure. */
printf("Client disconnected.\n");
close(fd);
event_del(&client->ev_read);
free(client);
return;
} else if (len < 0) {
/* Some other error occurred, close the socket, remove
* the event and free the client structure. */
printf("Socket failure, disconnecting client: %s", strerror(errno));
close(fd);
event_del(&client->ev_read);
free(client);
return;
}
/* XXX For the sake of simplicity we'll echo the data write
* back to the client. Normally we shouldn't do this in a
* non-blocking app, we should queue the data and wait to be
* told that we can write.
*/
wlen = write(fd, buf, len);
if (wlen < len) {
/* We didn't write all our data. If we had proper
* queueing/buffering setup, we'd finish off the write
* when told we can write again. For this simple case
* we'll just lose the data that didn't make it in the
* write.
*/
printf("Short write, not all data echoed back to client.\n");
}
}
/**
* This function will be called by libevent when there is a connection
* ready to be accepted.
*/
void on_accept(int fd, short ev, void *arg) {
int client_fd;
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
struct client *client;
/* Accept the new connection. */
client_fd = accept(fd, (struct sockaddr *)&client_addr, &client_len);
if (client_fd == -1) {
warn("accept failed");
return;
}
/* Set the client socket to non-blocking mode. */
if (setnonblock(client_fd) < 0)
warn("failed to set client socket non-blocking");
/* We've accepted a new client, allocate a client object to
* maintain the state of this client. */
client = calloc(1, sizeof(*client));
if (client == NULL) err(1, "malloc failed");
/* Setup the read event, libevent will call on_read() whenever
* the clients socket becomes read ready. We also make the
* read event persistent so we don't have to re-add after each
* read. */
event_set(&client->ev_read, client_fd, EV_READ | EV_PERSIST, on_read, client);
/* Setting up the event does not activate, add the event so it
* becomes active. */
event_add(&client->ev_read, NULL);
printf("Accepted connection from %s\n", inet_ntoa(client_addr.sin_addr));
}
int main(int argc, char **argv) {
int listen_fd;
struct sockaddr_in listen_addr;
int reuseaddr_on = 1;
/* The socket accept event. */
struct event ev_accept;
/* Initialize libevent. */
event_init();
// comment out since it is called from test and should terminate
// /* Create our listening socket. This is largely boiler plate
// * code that I'll abstract away in the future. */
// listen_fd = socket(AF_INET, SOCK_STREAM, 0);
// if (listen_fd < 0)
// err(1, "listen failed");
// if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on,
// sizeof(reuseaddr_on)) == -1)
// err(1, "setsockopt failed");
// memset(&listen_addr, 0, sizeof(listen_addr));
// listen_addr.sin_family = AF_INET;
// listen_addr.sin_addr.s_addr = INADDR_ANY;
// listen_addr.sin_port = htons(SERVER_PORT);
// if (bind(listen_fd, (struct sockaddr *)&listen_addr, sizeof(listen_addr))
// < 0)
// err(1, "bind failed");
// if (listen(listen_fd, 5) < 0)
// err(1, "listen failed");
//
// /* Set the socket to non-blocking, this is essential in event
// * based programming with libevent. */
// if (setnonblock(listen_fd) < 0)
// err(1, "failed to set server socket to non-blocking");
//
// /* We now have a listening socket, we create a read event to
// * be notified when a client connects. */
// event_set(&ev_accept, listen_fd, EV_READ | EV_PERSIST, on_accept, NULL);
// event_add(&ev_accept, NULL);
//
// /* Start the libevent event loop. */
// event_dispatch();
return 0;
}
================================================
FILE: examples/log/BUILD
================================================
cc_binary(
name = "log_test",
srcs = ["log_test.cc"],
deps = [
"@com_github_gabime_spdlog//:spdlog",
],
)
================================================
FILE: examples/log/log_test.cc
================================================
#include "spdlog/spdlog.h"
int main() {
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical(
"Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", {});
SPDLOG_DEBUG("Some debug message");
// Set the default logger to file logger
// auto file_logger = spdlog::basic_logger_mt("basic_logger",
// "logs/basic.txt"); spdlog::set_default_logger(file_logger);
}
================================================
FILE: examples/sentinel-cpp/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
# https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary
cc_binary(
name = "sentinel_basic_qps_limit",
srcs = ["basic_qps_limit.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/public:sph_u_lib",
"//sentinel-core/log/metric:metric_log_task_lib",
"//sentinel-core/transport/command:http_command_center_init_target",
],
)
cc_binary(
name = "sentinel_basic_concurrency_limit",
srcs = ["basic_concurrency_limit.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/public:sph_u_lib",
"//sentinel-core/log/metric:metric_log_task_lib",
"//sentinel-core/transport/command:http_command_center_init_target",
],
)
# https://docs.bazel.build/versions/master/be/c-cpp.html#cc_binary
cc_binary(
name = "sentinel_basic_system_limit",
srcs = ["basic_system_limit.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/public:sph_u_lib",
"//sentinel-core/log/metric:metric_log_task_lib",
"//sentinel-core/transport/command:http_command_center_init_target",
],
)
cc_binary(
name = "sentinel_basic_param_limit",
srcs = ["basic_param_limit.cc"],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/public:sph_u_lib",
"//sentinel-core/log/metric:metric_log_task_lib",
"//sentinel-core/transport/command:http_command_center_init_target",
],
)
================================================
FILE: examples/sentinel-cpp/basic_concurrency_limit.cc
================================================
#include
#include
#include
#include
#include
#include "sentinel-core/flow/flow_rule_manager.h"
#include "sentinel-core/init/init_target_registry.h"
#include "sentinel-core/log/metric/metric_log_task.h"
#include "sentinel-core/public/sph_u.h"
#include "sentinel-core/transport/command/http_server_init_target.h"
Sentinel::Init::InitTargetRegister<
Sentinel::Transport::HttpCommandCenterInitTarget>
HCCIT_registered;
void doEntry(const char* resource) {
while (true) {
auto r = Sentinel::SphU::Entry(resource);
if (r->IsBlocked()) {
std::cout << "b";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
std::cout << "\n~~passed~~\n";
std::this_thread::sleep_for(std::chrono::milliseconds(10));
r->Exit();
}
}
}
void doOneEntry() { doEntry("my_open_api_abc"); }
void doAnotherEntry() { doEntry("big_brother_service:foo()"); }
/*
* Run the demo: CSP_SENTINEL_APP_NAME=your-app-name
* ./bazel-bin/examples/sentinel-cpp/sentinel_basic_concurrency_limit
*/
int main() {
// Initialize for Sentinel.
Sentinel::Log::Logger::InitDefaultLogger();
Sentinel::Transport::HttpCommandCenterInitTarget command_center_init;
command_center_init.Initialize();
Sentinel::Log::MetricLogTask metric_log_task;
metric_log_task.Initialize();
Sentinel::Flow::FlowRule rule1{"my_open_api_abc"};
rule1.set_metric_type(Sentinel::Flow::FlowMetricType::kThreadCount);
rule1.set_count(2);
Sentinel::Flow::FlowRuleManager::GetInstance().LoadRules({rule1});
std::thread t1(doOneEntry);
std::thread t2(doOneEntry);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
std::thread t3(doOneEntry);
std::thread t4(doAnotherEntry);
std::thread t5(doOneEntry);
std::thread t6(doOneEntry);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
return 0;
}
================================================
FILE: examples/sentinel-cpp/basic_param_limit.cc
================================================
#include
#include
#include
#include
#include
#include
#include
#include
#include "sentinel-core/init/init_target_registry.h"
#include "sentinel-core/log/metric/metric_log_task.h"
#include "sentinel-core/param/param_flow_rule_constants.h"
#include "sentinel-core/param/param_flow_rule_manager.h"
#include "sentinel-core/public/sph_u.h"
#include "sentinel-core/transport/command/http_server_init_target.h"
std::mutex mtx;
std::atomic stop(false);
constexpr int SECONDS = 60, THREAD_NUM = 8;
std::atomic pass, block, seconds(SECONDS);
std::vector> pPassCnt(10);
std::vector> pBlockCnt(10);
std::thread myThreads[THREAD_NUM];
int threadCnt = 0;
int64_t CurrentTimeMillis() {
return std::chrono::duration_cast(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void RunTask() {
double ans = 1.001;
for (int i = 0; i < 3000; i++) {
double index = (rand() % 5) * ((CurrentTimeMillis() / 1000) % 3);
ans = pow(ans, index);
}
}
void DoEntry(const char* resource) {
int cnt = 0;
while (!stop.load()) {
int32_t randParam = (rand() + (CurrentTimeMillis() / 10)) % 10;
randParam = ((cnt++)) % 50;
auto r = Sentinel::SphU::Entry(resource, Sentinel::EntryType::IN, 1, 0,
randParam, std::string("example"));
if (r->IsBlocked()) {
// pBlockCnt[randParam].fetch_add(1);
block.fetch_add(1);
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
} else {
pass.fetch_add(1);
// pPassCnt[randParam].fetch_add(1);
RunTask();
// std::this_thread::sleep_for(std::chrono::milliseconds(100));
r->Exit();
}
}
}
void TimerTask() {
int oldTotal = 0, oldPass = 0, oldBlock = 0;
int oldPTotal[10] = {0}, oldPPass[10] = {0}, oldPBlock[10] = {0};
std::cerr << "Begin to statistic!!!" << std::endl;
int cnt = 0;
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int globalPass = pass.load();
int globalBlock = block.load();
int globalTotal = globalBlock + globalPass;
int oneSecondPass = globalPass - oldPass;
int oneSecondBlock = globalBlock - oldBlock;
int oneSecondTotal = globalTotal - oldTotal;
oldPass = globalPass;
oldBlock = globalBlock;
oldTotal = globalTotal;
std::cerr << "[" << seconds.load() << "] total=" << oneSecondTotal
<< ", pass=" << oneSecondPass << ", block=" << oneSecondBlock
<< std::endl;
int leftSec = seconds.fetch_sub(1);
if (leftSec <= 0) {
stop.store(true);
break;
}
}
}
/*
* Run the demo: CSP_SENTINEL_APP_NAME=your-app-name
* ./bazel-bin/examples/sentinel-cpp/sentinel_basic_param_limit
*/
int main() {
// Initialize for Sentinel.
Sentinel::Log::Logger::InitDefaultLogger();
Sentinel::Transport::HttpCommandCenterInitTarget command_center_init;
command_center_init.Initialize();
Sentinel::Log::MetricLogTask metric_log_task;
metric_log_task.Initialize();
std::string myResource("some_param_test");
Sentinel::Param::ParamFlowRule rule0, rule1, rule12, rule11;
rule0.set_resource(myResource);
rule0.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule0.set_threshold(2000);
rule0.set_cache_size(200);
rule0.set_interval_in_ms(1000);
rule0.set_param_idx(0);
rule1.set_resource(myResource);
rule1.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule1.set_threshold(6000);
rule1.set_param_idx(1);
rule1.set_interval_in_ms(1000);
Sentinel::Param::ParamFlowItem item0(std::string("nonexisting-str"),
Sentinel::Param::ParamItemType::kString,
100);
rule1.set_param_flow_item_list({item0});
rule12.set_resource("non-existing-resource"); // should not work
rule12.set_metric_type(Sentinel::Param::ParamFlowMetricType::kQps);
rule12.set_threshold(1);
rule12.set_param_idx(1);
rule12.set_interval_in_ms(1000);
Sentinel::Param::ParamFlowRuleManager::GetInstance().LoadRules(
{rule1, rule0, rule12});
std::thread t0(TimerTask);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cerr << "Thread num is " << THREAD_NUM << std::endl;
for (int i = 0; i < THREAD_NUM; i++) {
myThreads[i] = std::thread(DoEntry, myResource.c_str());
}
for (int i = 0; i < THREAD_NUM; i++) {
myThreads[i].join();
}
t0.join();
return 0;
}
================================================
FILE: examples/sentinel-cpp/basic_qps_limit.cc
================================================
#include
#include
#include
#include
#include
#include "sentinel-core/flow/flow_rule_manager.h"
#include "sentinel-core/init/init_target_registry.h"
#include "sentinel-core/log/metric/metric_log_task.h"
#include "sentinel-core/public/sph_u.h"
#include "sentinel-core/transport/command/http_server_init_target.h"
void DoEntry(const char* resource) {
while (true) {
auto r = Sentinel::SphU::Entry(resource);
if (r->IsBlocked()) {
// Indicating the request is blocked. We can do something for this.
std::cout << "b";
std::this_thread::sleep_for(std::chrono::milliseconds(8));
} else {
std::cout << "\n~~passed~~\n";
std::this_thread::sleep_for(std::chrono::milliseconds(8));
r->Exit();
}
std::cout.flush();
}
}
void DoOneEntry() { DoEntry("my_open_api_abc"); }
void DoAnotherEntry() { DoEntry("m1:my_another_api_233"); }
/*
* Run the demo: CSP_SENTINEL_APP_NAME=your-app-name
* ./bazel-bin/examples/sentinel-cpp/sentinel_basic_qps_limit
*/
int main() {
// Initialize for Sentinel.
Sentinel::Log::Logger::InitDefaultLogger();
Sentinel::Transport::HttpCommandCenterInitTarget command_center_init;
command_center_init.Initialize();
Sentinel::Log::MetricLogTask metric_log_task;
metric_log_task.Initialize();
Sentinel::Flow::FlowRule rule1{"my_open_api_abc"};
rule1.set_count(10);
rule1.set_control_behavior(Sentinel::Flow::FlowControlBehavior::kThrotting);
Sentinel::Flow::FlowRule rule2{"m1:my_another_api_233"};
rule2.set_count(5);
Sentinel::Flow::FlowRuleManager::GetInstance().LoadRules({rule1, rule2});
std::thread t1(DoOneEntry);
std::thread t2(DoOneEntry);
std::thread t21(DoOneEntry);
std::thread t22(DoOneEntry);
std::thread t23(DoOneEntry);
std::this_thread::sleep_for(std::chrono::milliseconds(5));
std::thread t3(DoOneEntry);
std::thread t4(DoAnotherEntry);
std::this_thread::sleep_for(std::chrono::milliseconds(9));
std::thread t5(DoOneEntry);
std::thread t6(DoOneEntry);
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
t6.join();
return 0;
}
================================================
FILE: examples/sentinel-cpp/basic_system_limit.cc
================================================
#include
#include
#include
#include
#include
#include
#include
#include "sentinel-core/init/init_target_registry.h"
#include "sentinel-core/log/metric/metric_log_task.h"
#include "sentinel-core/public/sph_u.h"
#include "sentinel-core/system/system_rule_manager.h"
#include "sentinel-core/transport/command/http_server_init_target.h"
std::atomic pass, block, seconds(1200);
std::atomic stop(false);
int64_t CurrentTimeMillis() {
return std::chrono::duration_cast(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
void RunTask() {
double ans = 1.001;
for (int i = 0; i < 400; i++) {
double index = (rand() % 5) * ((CurrentTimeMillis() / 1000) % 3);
ans = pow(ans, index);
}
}
void DoEntry(const char* resource, Sentinel::EntryType trafficType) {
while (true) {
auto r = Sentinel::SphU::Entry(resource, trafficType);
if (r->IsBlocked()) {
// Indicating the request is blocked. We can do something for this.
block.fetch_add(1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else {
pass.fetch_add(1);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
r->Exit();
}
std::cout.flush();
}
}
void TimerTask() {
int oldTotal = 0, oldPass = 0, oldBlock = 0;
std::cout << "Begin to statistic!!!" << std::endl;
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
int globalPass = pass.load();
int globalBlock = block.load();
int globalTotal = globalBlock + globalPass;
int oneSecondPass = globalPass - oldPass;
int oneSecondBlock = globalBlock - oldBlock;
int oneSecondTotal = globalTotal - oldTotal;
oldPass = globalPass;
oldBlock = globalBlock;
oldTotal = globalTotal;
std::cout << "[" << seconds.load() << "] total=" << oneSecondTotal
<< ", pass=" << oneSecondPass << ", block=" << oneSecondBlock
<< std::endl;
int leftSec = seconds.fetch_sub(1);
if (leftSec <= 0) {
stop.store(true);
break;
}
}
}
/*
* Run the demo: CSP_SENTINEL_APP_NAME=your-app-name
* ./bazel-bin/examples/sentinel-cpp/sentinel_basic_qps_limit
*/
int main() {
// Initialize for Sentinel.
Sentinel::Log::Logger::InitDefaultLogger();
Sentinel::Transport::HttpCommandCenterInitTarget command_center_init;
command_center_init.Initialize();
Sentinel::Log::MetricLogTask metric_log_task;
metric_log_task.Initialize();
Sentinel::System::SystemStatusListener::GetInstance().Initialize();
Sentinel::System::SystemRule rule1, rule2, rule3;
rule1.set_rule_type(Sentinel::System::MetricType::kQps);
rule1.set_threshold(static_cast(250));
rule2.set_rule_type(Sentinel::System::MetricType::kConcurrency);
rule2.set_threshold(4);
rule3.set_rule_type(Sentinel::System::MetricType::kCpuUsage);
rule3.set_threshold(static_cast(0.8));
Sentinel::System::SystemRule rule4{Sentinel::System::MetricType::kCpuUsage,
0.7};
Sentinel::System::SystemRule badRule{Sentinel::System::MetricType::kRt, -2};
Sentinel::System::SystemRuleManager::GetInstance().LoadRules(
{rule1, rule2, rule3, rule4, badRule});
std::thread t0(TimerTask);
std::thread t1(DoEntry, "my_open_api_abc", Sentinel::EntryType::IN);
std::this_thread::sleep_for(std::chrono::milliseconds(47));
std::thread t2(DoEntry, "my_open_api_abc", Sentinel::EntryType::IN);
std::this_thread::sleep_for(std::chrono::milliseconds(23));
std::thread t3(DoEntry, "my_open_api_abc", Sentinel::EntryType::IN);
// std::this_thread::sleep_for(std::chrono::milliseconds(19));
std::thread t4(DoEntry, "my_open_api_abc", Sentinel::EntryType::IN);
// std::thread t5(DoEntry, "foo", Sentinel::EntryType::OUT);
t0.join();
t1.join();
t2.join();
t3.join();
t4.join();
// t5.join();
return 0;
}
================================================
FILE: examples/tbb/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_binary(
name = "tbb_test",
srcs = ["tbb_test.cc"],
# srcs = select({
# "//bazel:is_android": [],
# "//bazel:is_wsl": [ "tbb_test.cc" ],
# "//conditions:default": [ "tbb_test.cc" ],
# }),
copts = DEFAULT_COPTS,
deps = select({
"//bazel:is_osx": [
"//sentinel-core/log:logger_lib",
"@com_github_libtbb_osx//:tbb_osx",
],
"//conditions:default": [
"//sentinel-core/log:logger_lib",
"@com_github_libtbb//:tbb",
],
}),
)
================================================
FILE: examples/tbb/tbb_test.cc
================================================
#include
#include
#include
#include "sentinel-core/log/logger.h"
#include "tbb/concurrent_hash_map.h"
constexpr int NUM_THREADS = 10000;
class Stu {
public:
int id;
std::string name;
~Stu() { std::cout << "Destructor" << std::endl; }
};
tbb::concurrent_hash_map> stuMap;
int main() {
Sentinel::Log::Logger::InitDefaultLogger();
std::cout << "loop begin\n";
decltype(stuMap)::accessor ac;
if (stuMap.insert(ac, std::make_pair<>(1, nullptr))) {
ac->second = std::make_unique();
std::cout << stuMap.size() << std::endl;
}
ac.release();
decltype(stuMap)::accessor ac2;
stuMap.insert(ac2, std::make_pair<>(1, std::make_unique()));
std::cout << "===== END =====" << stuMap.size() << std::endl;
return 0;
}
================================================
FILE: format.sh
================================================
#!/usr/bin/env bash
CMD=clang-format-8
$CMD -version
$CMD -i -style=Google $(git ls-files|grep -E ".*\.(cc|h)$")
CHANGED="$(git ls-files --modified)"
if [[ ! -z "$CHANGED" ]]; then
echo "The following files have changes due to incrrect format:"
echo "$CHANGED"
echo "please use format.sh script fix it"
exit 1
else
echo "No changes."
fi
================================================
FILE: sentinel-core/BUILD
================================================
================================================
FILE: sentinel-core/circuitbreaker/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_library(
name = "rule_lib",
srcs = [
"rule.h",
"rule.cc",
],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/common:rule_lib",
"@com_google_absl//absl/strings:str_format",
]
)
cc_library(
name = "circuit_breaker_interface",
srcs = [
"circuit_breaker.h",
"circuit_breaker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":rule_lib",
"//sentinel-core/statistic/node:node_interface",
"//sentinel-core/utils:utils_lib",
]
)
cc_library(
name = "rt_circuit_breaker_lib",
srcs = [
"rt_circuit_breaker.h",
"rt_circuit_breaker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":circuit_breaker_interface",
":rule_lib",
"//sentinel-core/statistic/base:leap_array_lib",
]
)
cc_library(
name = "error_circuit_breaker_lib",
srcs = [
"error_circuit_breaker.h",
"error_circuit_breaker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":circuit_breaker_interface",
":rule_lib",
"//sentinel-core/statistic/base:leap_array_lib",
]
)
cc_library(
name = "rule_manager_lib",
srcs = [
"rule_manager.h",
"rule_manager.cc",
],
copts = DEFAULT_COPTS,
deps = [
":error_circuit_breaker_lib",
":rt_circuit_breaker_lib",
":rule_lib",
"//sentinel-core/log:logger_lib",
"//sentinel-core/property:dynamic_sentinel_property_lib",
"//sentinel-core/property:property_listener_interface",
"//sentinel-core/property:sentinel_property_interface",
"@com_google_absl//absl/synchronization",
]
)
cc_library(
name = "rule_slot_lib",
srcs = [
"slot.h",
"slot.cc",
],
copts = DEFAULT_COPTS,
deps = [
":rule_manager_lib",
"//sentinel-core/slot/base:rule_checker_slot_interface",
"//sentinel-core/slot/base:stats_slot_interface",
]
)
cc_test(
name = "slot_unittests",
srcs = [
"slot_test.cc",
],
copts = TEST_COPTS,
deps = [
":rule_slot_lib",
"//sentinel-core/common:string_resource_wrapper_lib",
"//sentinel-core/test/mock/statistic/node:mock_lib",
"@com_google_googletest//:gtest_main",
]
)
# cc_library(
# name = "flow_rule_manager_lib",
# srcs = [
# "flow_rule_manager.h",
# "flow_rule_manager.cc",
# ],
# copts = DEFAULT_COPTS,
# deps = [
# ":flow_rule_constants_lib",
# ":flow_rule_lib",
# ":traffic_shaping_controller_lib",
# ":default_traffic_shaping_calculator_lib",
# ":default_traffic_shaping_checker_lib",
# "//sentinel-core/property:property_listener_interface",
# "//sentinel-core/log:logger_lib",
# "//sentinel-core/property:sentinel_property_interface",
# "//sentinel-core/property:dynamic_sentinel_property_lib",
# "@com_google_absl//absl/synchronization",
# ]
# )
# cc_test(
# name = "flow_slot_unittests",
# srcs = [
# "flow_slot_test.cc",
# ],
# copts = TEST_COPTS,
# deps = [
# ":flow_slot_lib",
# "//sentinel-core/common:string_resource_wrapper_lib",
# "//sentinel-core/test/mock/statistic/node:mock_lib",
# "@com_google_googletest//:gtest_main",
# ]
# )
================================================
FILE: sentinel-core/circuitbreaker/circuit_breaker.cc
================================================
#include
#include
#include "sentinel-core/circuitbreaker/circuit_breaker.h"
#include "sentinel-core/utils/time_utils.h"
namespace Sentinel {
namespace CircuitBreaker {
State AbstractCircuitBreaker::CurrentState() { return current_state_.load(); }
const Rule& AbstractCircuitBreaker::GetRule() const { return rule_; }
bool AbstractCircuitBreaker::RetryTimeoutArrived() {
return Utils::TimeUtils::CurrentTimeMillis().count() >= next_retry_timestamp_;
}
void AbstractCircuitBreaker::UpdateNextRetryTimestamp() {
this->next_retry_timestamp_ =
Utils::TimeUtils::CurrentTimeMillis().count() + retry_timeout_ms_;
}
bool AbstractCircuitBreaker::FromCloseToOpen(double snapshot) {
auto expected = State::kClosed;
if (current_state_.compare_exchange_strong(expected, State::kOpen)) {
UpdateNextRetryTimestamp();
return true;
}
return false;
}
bool AbstractCircuitBreaker::FromOpenToHalfOpen() {
auto expected = State::kOpen;
return current_state_.compare_exchange_strong(expected, State::kHalfOpen);
}
bool AbstractCircuitBreaker::FromHalfOpenToOpen(double snapshot) {
auto expected = State::kHalfOpen;
if (current_state_.compare_exchange_strong(expected, State::kOpen)) {
UpdateNextRetryTimestamp();
return true;
}
return false;
}
bool AbstractCircuitBreaker::FromHalfOpenToClose() {
auto expected = State::kHalfOpen;
if (current_state_.compare_exchange_strong(expected, State::kClosed)) {
Reset();
return true;
}
return false;
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/circuit_breaker.h
================================================
#pragma once
#include
#include
#include "sentinel-core/circuitbreaker/rule.h"
#include "sentinel-core/statistic/node/node.h"
namespace Sentinel {
namespace CircuitBreaker {
enum class State { kClosed = 0, kOpen = 1, kHalfOpen = 2 };
class CircuitBreaker {
public:
CircuitBreaker() = default;
virtual ~CircuitBreaker() = default;
virtual const Rule& GetRule() const = 0;
virtual State CurrentState() = 0;
virtual bool TryPass(Stat::NodeSharedPtr& node) = 0;
virtual void Reset() = 0;
virtual void RecordComplete(int64_t rt, const std::string& error) = 0;
};
using CircuitBreakerSharedPtr = std::shared_ptr;
class AbstractCircuitBreaker : public CircuitBreaker {
public:
explicit AbstractCircuitBreaker(const Rule& rule)
: rule_(rule), retry_timeout_ms_(rule.retry_timeout_sec() * 1000) {}
virtual ~AbstractCircuitBreaker() = default;
State CurrentState() override;
const Rule& GetRule() const override;
protected:
bool RetryTimeoutArrived();
void UpdateNextRetryTimestamp();
bool FromCloseToOpen(double snapshot);
bool FromOpenToHalfOpen();
bool FromHalfOpenToOpen(double snapshot);
bool FromHalfOpenToClose();
const Rule rule_;
const int32_t retry_timeout_ms_;
std::atomic current_state_{State::kClosed};
int64_t next_retry_timestamp_ = 0;
};
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/error_circuit_breaker.cc
================================================
#include
#include "sentinel-core/circuitbreaker/error_circuit_breaker.h"
namespace Sentinel {
namespace CircuitBreaker {
std::shared_ptr SimpleErrorCounterLeapArray::NewEmptyBucket(
int64_t) {
return std::make_shared();
}
void SimpleErrorCounterLeapArray::ResetWindowTo(
const Stat::WindowWrapSharedPtr& w,
int64_t start_time) {
// Update the start time and reset value.
w->ResetTo(start_time);
w->Value()->Reset();
}
bool ErrorCircuitBreaker::TryPass(Stat::NodeSharedPtr&) {
State state = current_state_.load();
if (state == State::kClosed) {
return true;
}
if (state == State::kOpen) {
return RetryTimeoutArrived() && FromOpenToHalfOpen();
}
return false;
}
void ErrorCircuitBreaker::Reset() {
// NOTE: only reset the current bucket as the sampleCount=1
sliding_counter_->CurrentWindow()->Value()->Reset();
}
void ErrorCircuitBreaker::RecordComplete(int64_t, const std::string& err) {
auto counter = sliding_counter_->CurrentWindow()->Value();
if (!err.empty()) {
counter->AddErrorCount(1);
}
counter->AddTotalCount(1);
RecordAndHandleStateChange(err);
}
void ErrorCircuitBreaker::RecordAndHandleStateChange(const std::string& err) {
if (current_state_.load() == State::kOpen) {
return;
}
if (current_state_.load() == State::kHalfOpen) {
if (!err.empty()) {
FromHalfOpenToOpen(1);
} else {
FromHalfOpenToClose();
}
return;
}
auto counters = sliding_counter_->Values();
int64_t err_count = 0, total_count = 0;
for (auto& c : counters) {
err_count += c->error_count();
total_count += c->total_count();
}
if (total_count < rule_.min_request_amount()) {
return;
}
double cur_value = strategy_ == Strategy::kErrorRatio
? err_count * 1.0 / total_count
: err_count;
if (cur_value > threshold_) {
auto cs = current_state_.load();
switch (cs) {
case State::kClosed:
FromCloseToOpen(cur_value);
break;
case State::kHalfOpen:
FromHalfOpenToOpen(cur_value);
break;
default:
break;
}
}
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/error_circuit_breaker.h
================================================
#pragma once
#include
#include
#include "sentinel-core/circuitbreaker/circuit_breaker.h"
#include "sentinel-core/circuitbreaker/rule.h"
#include "sentinel-core/statistic/base/leap_array.h"
namespace Sentinel {
namespace CircuitBreaker {
class SimpleErrorCounter {
public:
explicit SimpleErrorCounter() = default;
virtual ~SimpleErrorCounter() = default;
int64_t AddErrorCount(int64_t count) {
return error_count_.fetch_add(count) + count;
};
int64_t AddTotalCount(int64_t count) {
return total_count_.fetch_add(count) + count;
};
void Reset() {
error_count_ = 0;
total_count_ = 0;
};
int64_t error_count() { return error_count_.load(); };
int64_t total_count() { return total_count_.load(); };
private:
std::atomic error_count_{0};
std::atomic total_count_{0};
};
class SimpleErrorCounterLeapArray : public Stat::LeapArray {
public:
explicit SimpleErrorCounterLeapArray(int32_t sample_count,
int32_t interval_ms)
: LeapArray(sample_count, interval_ms) {}
virtual ~SimpleErrorCounterLeapArray() {}
std::shared_ptr NewEmptyBucket(
int64_t time_millis) override;
void ResetWindowTo(const Stat::WindowWrapSharedPtr& wrap,
int64_t start_time) override;
};
class ErrorCircuitBreaker : public AbstractCircuitBreaker {
public:
explicit ErrorCircuitBreaker(const Rule& rule)
: AbstractCircuitBreaker(rule),
sliding_counter_(std::make_unique(
1, rule.stat_interval_ms())) {
this->strategy_ = rule.strategy();
this->threshold_ = rule.threshold();
}
virtual ~ErrorCircuitBreaker() = default;
bool TryPass(Stat::NodeSharedPtr& node) override;
void Reset() override;
void RecordComplete(int64_t rt, const std::string& error) override;
private:
void RecordAndHandleStateChange(const std::string& err);
Strategy strategy_;
double threshold_;
const std::unique_ptr sliding_counter_;
};
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rt_circuit_breaker.cc
================================================
#include
#include "sentinel-core/circuitbreaker/rt_circuit_breaker.h"
namespace Sentinel {
namespace CircuitBreaker {
std::shared_ptr SlowRequestLeapArray::NewEmptyBucket(
int64_t) {
return std::make_shared();
}
void SlowRequestLeapArray::ResetWindowTo(
const Stat::WindowWrapSharedPtr& w,
int64_t start_time) {
// Update the start time and reset value.
w->ResetTo(start_time);
w->Value()->Reset();
}
bool ResponseTimeCircuitBreaker::TryPass(Stat::NodeSharedPtr&) {
State state = current_state_.load();
if (state == State::kClosed) {
return true;
}
if (state == State::kOpen) {
return RetryTimeoutArrived() && FromOpenToHalfOpen();
}
return false;
}
void ResponseTimeCircuitBreaker::Reset() {
sliding_counter_->CurrentWindow()->Value()->Reset();
}
void ResponseTimeCircuitBreaker::RecordComplete(int64_t rt,
const std::string&) {
auto counter = sliding_counter_->CurrentWindow()->Value();
if (rt > max_allowed_rt_) {
counter->AddSlowCount(1);
}
counter->AddTotalCount(1);
RecordAndHandleStateChange(rt);
}
void ResponseTimeCircuitBreaker::RecordAndHandleStateChange(int64_t rt) {
if (current_state_.load() == State::kOpen) {
return;
}
if (current_state_.load() == State::kHalfOpen) {
if (rt > max_allowed_rt_) {
FromHalfOpenToOpen(1.0);
} else {
FromHalfOpenToClose();
}
return;
}
auto counters = sliding_counter_->Values();
int64_t slow_count = 0, total_count = 0;
for (auto& c : counters) {
slow_count += c->slow_count();
total_count += c->total_count();
}
if (total_count < rule_.min_request_amount()) {
return;
}
double current_ratio = slow_count * 1.0 / total_count;
if (current_ratio > max_slow_request_ratio_) {
auto cs = current_state_.load();
switch (cs) {
case State::kClosed:
FromCloseToOpen(current_ratio);
break;
case State::kHalfOpen:
FromHalfOpenToOpen(current_ratio);
break;
default:
break;
}
}
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rt_circuit_breaker.h
================================================
#pragma once
#include
#include
#include "sentinel-core/circuitbreaker/circuit_breaker.h"
#include "sentinel-core/circuitbreaker/rule.h"
#include "sentinel-core/statistic/base/leap_array.h"
namespace Sentinel {
namespace CircuitBreaker {
class SlowRequestCounter {
public:
explicit SlowRequestCounter() = default;
virtual ~SlowRequestCounter() = default;
int64_t AddSlowCount(int64_t count) {
return slow_count_.fetch_add(count) + count;
};
int64_t AddTotalCount(int64_t count) {
return total_count_.fetch_add(count) + count;
};
void Reset() {
slow_count_ = 0;
total_count_ = 0;
};
int64_t slow_count() { return slow_count_.load(); };
int64_t total_count() { return total_count_.load(); };
private:
std::atomic slow_count_{0};
std::atomic total_count_{0};
};
class SlowRequestLeapArray : public Stat::LeapArray {
public:
explicit SlowRequestLeapArray(int32_t sample_count, int32_t interval_ms)
: LeapArray(sample_count, interval_ms) {}
virtual ~SlowRequestLeapArray() {}
std::shared_ptr NewEmptyBucket(
int64_t time_millis) override;
void ResetWindowTo(const Stat::WindowWrapSharedPtr& wrap,
int64_t start_time) override;
};
class ResponseTimeCircuitBreaker : public AbstractCircuitBreaker {
public:
explicit ResponseTimeCircuitBreaker(const Rule& rule)
: AbstractCircuitBreaker(rule),
sliding_counter_(std::make_unique(
1, rule.stat_interval_ms())) {
this->max_allowed_rt_ = rule.max_allowed_rt();
this->max_slow_request_ratio_ = rule.threshold();
}
virtual ~ResponseTimeCircuitBreaker() = default;
bool TryPass(Stat::NodeSharedPtr& node) override;
void Reset() override;
void RecordComplete(int64_t rt, const std::string& error) override;
private:
void RecordAndHandleStateChange(int64_t rt);
int32_t max_allowed_rt_;
double max_slow_request_ratio_;
const std::unique_ptr sliding_counter_;
};
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rule.cc
================================================
#include
#include "sentinel-core/circuitbreaker/rule.h"
#include "absl/strings/str_format.h"
namespace Sentinel {
namespace CircuitBreaker {
bool CircuitBreaker::Rule::operator==(const CircuitBreaker::Rule& rule) const {
return resource_ == rule.resource() && threshold_ == rule.threshold() &&
strategy_ == rule.strategy() &&
retry_timeout_sec_ == rule.retry_timeout_sec() &&
min_request_amount_ == rule.min_request_amount() &&
stat_interval_ms_ == rule.stat_interval_ms() &&
max_allowed_rt_ == rule.max_allowed_rt();
}
std::string CircuitBreaker::Rule::ToString() const {
return absl::StrFormat(
"CircuitBreakerRule{resource=%s, strategy=%d, threshold=%.2f, "
"retry_timeout_sec=%d, min_request_amount=%d, stat_interval_ms=%d, "
"max_allowed_rt=%d}",
resource_, static_cast(strategy_), threshold_, retry_timeout_sec_,
min_request_amount_, stat_interval_ms_, max_allowed_rt_);
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rule.h
================================================
#pragma once
#include
#include
#include
#include
#include
#include "sentinel-core/common/rule.h"
namespace Sentinel {
namespace CircuitBreaker {
enum class MetricType { kResponseTime = 0, kException = 1 };
enum class Strategy { kSlowRtRatio = 0, kErrorRatio = 1, kErrorCount = 2 };
struct Rule : public Sentinel::Rule {
public:
Rule() = default;
virtual ~Rule() = default;
explicit Rule(const std::string& resource) : resource_(resource) {}
const std::string& resource() const { return resource_; }
Strategy strategy() const { return strategy_; }
double threshold() const { return threshold_; }
int32_t retry_timeout_sec() const { return retry_timeout_sec_; }
int32_t min_request_amount() const { return min_request_amount_; }
int32_t stat_interval_ms() const { return stat_interval_ms_; }
int32_t max_allowed_rt() const { return max_allowed_rt_; }
void set_resource(const std::string& resource) { resource_ = resource; }
void set_strategy(Strategy s) { strategy_ = s; }
void set_threshold(double threshold) { threshold_ = threshold; }
void set_retry_timeout_sec(int32_t retry_timeout_sec) {
retry_timeout_sec_ = retry_timeout_sec;
}
void set_min_request_amount(int32_t min_request_amount) {
min_request_amount_ = min_request_amount;
}
void set_stat_interval_ms(int32_t stat_interval_ms) {
stat_interval_ms_ = stat_interval_ms;
}
void set_max_allowed_rt(int32_t max_allowed_rt) {
max_allowed_rt_ = max_allowed_rt;
}
bool operator==(const CircuitBreaker::Rule& rule) const;
std::string ToString() const;
private:
std::string resource_;
Strategy strategy_{Strategy::kSlowRtRatio};
double threshold_ = 0;
int32_t retry_timeout_sec_ = 0;
int32_t min_request_amount_{10};
int32_t stat_interval_ms_{1000};
int32_t max_allowed_rt_{0};
};
struct RuleHash {
std::size_t operator()(const CircuitBreaker::Rule& rule) const noexcept {
std::size_t result = std::hash{}(rule.resource());
result = 31 * result + static_cast(rule.strategy());
result = 31 * result + std::hash{}(rule.threshold());
result = 31 * result + rule.retry_timeout_sec();
result = 31 * result + rule.min_request_amount();
result = 31 * result + rule.stat_interval_ms();
result = 31 * result + rule.max_allowed_rt();
return result;
}
};
using RuleList = std::vector;
using RuleSet = std::unordered_set;
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rule_manager.cc
================================================
#include
#include
#include
#include
#include "sentinel-core/circuitbreaker/error_circuit_breaker.h"
#include "sentinel-core/circuitbreaker/rt_circuit_breaker.h"
#include "sentinel-core/circuitbreaker/rule_manager.h"
#include "sentinel-core/log/logger.h"
#include "sentinel-core/property/dynamic_sentinel_property.h"
namespace Sentinel {
namespace CircuitBreaker {
constexpr auto kPropertyListenerName = "CircuitBreakerRulePropertyListener";
bool IsValidRule(const Rule& rule) {
auto threshold = rule.threshold();
bool base_valid = !rule.resource().empty() && threshold >= 0;
if (!base_valid) {
return false;
}
if (rule.retry_timeout_sec() <= 0 || rule.stat_interval_ms() <= 0 ||
rule.min_request_amount() < 0) {
return false;
}
switch (rule.strategy()) {
case Strategy::kSlowRtRatio:
return rule.max_allowed_rt() >= 0 && threshold <= 1;
case Strategy::kErrorRatio:
return threshold <= 1;
case Strategy::kErrorCount:
return true;
default:
return false;
}
}
// RuleManager
RuleManager::RuleManager() {
cur_property_ =
std::make_shared>>();
cur_property_->AddListener(std::make_unique());
}
bool RuleManager::LoadRules(const RuleList& rules) {
return cur_property_->UpdateValue(rules);
}
bool RuleManager::HasRules(const std::string& resource) {
absl::ReaderMutexLock lck(&update_mtx_);
return cb_map_.find(resource) != cb_map_.end();
}
RuleList RuleManager::GetRules() const {
absl::ReaderMutexLock lck(&update_mtx_);
RuleList list{};
for (const auto& e : rule_map_) {
list.insert(std::end(list), std::begin(e.second), std::end(e.second));
}
return list;
}
RuleSet RuleManager::GetRulesOfResource(const std::string& resource) const {
absl::ReaderMutexLock lck(&update_mtx_);
auto it = rule_map_.find(resource);
if (it == rule_map_.end()) {
return {};
}
return it->second;
}
std::vector RuleManager::GetCircuitBreakers(
const std::string& resource) const {
absl::ReaderMutexLock lck(&update_mtx_);
auto it = cb_map_.find(resource);
if (it == cb_map_.end()) {
return {};
}
return it->second;
}
void RuleManager::RegisterToProperty(const RulePropertySharedPtr& property) {
if (property == nullptr) {
return;
}
std::lock_guard lck(property_mtx_);
SENTINEL_LOG(info, "Registering new property to CircuitBreakerRuleManager");
cur_property_->RemoveListener(kPropertyListenerName);
cur_property_ = property;
cur_property_->AddListener(std::make_unique());
}
// If the rule already exists, reuse the circuit breaker instance directly,
// otherwise generate a new instance.
CircuitBreakerSharedPtr RuleManager::GetExistingSameCbOrNew(const Rule& rule) {
auto it = cb_map_.find(rule.resource());
if (it == cb_map_.end()) {
return NewCircuitBreaker(rule);
}
auto cbs = it->second;
if (cbs.empty()) {
return NewCircuitBreaker(rule);
}
for (auto& cb : cbs) {
if (rule == cb->GetRule()) {
// Reuse the circuit breaker if the rule remains unchanged.
return cb;
}
}
return NewCircuitBreaker(rule);
}
CircuitBreakerSharedPtr RuleManager::NewCircuitBreaker(const Rule& rule) {
switch (rule.strategy()) {
case Strategy::kSlowRtRatio:
return std::make_shared(rule);
case Strategy::kErrorRatio:
return std::make_shared(rule);
default:
return nullptr;
}
}
void LogRuleMap(const std::unordered_map& map) {
std::string s("[");
for (const auto& e : map) {
for (const auto& rule : e.second) {
s += rule.ToString();
s += ",";
}
}
s[s.size() - 1] = ']';
SENTINEL_LOG(info, "[CircuitBreakerRuleManager] Rules received: {}", s);
}
// RulePropertyListener
CircuitBreakerMap RulePropertyListener::BuildCircuitBreakerMap(
const RuleList& list) {
CircuitBreakerMap m{};
if (list.empty()) {
return m;
}
for (auto& rule : list) {
if (!IsValidRule(rule)) {
SENTINEL_LOG(warn,
"[CircuitBreakerRuleManager] Ignoring invalid rule when "
"loading new circuit breaker rules: {}",
rule.ToString());
continue;
}
CircuitBreakerSharedPtr cb =
RuleManager::GetInstance().GetExistingSameCbOrNew(rule);
if (cb == nullptr) {
SENTINEL_LOG(warn,
"[CircuitBreakerRuleManager] Unknown circuit breaking "
"strategy, ignoring: {}",
rule.ToString());
continue;
}
auto it = m.find(rule.resource());
if (it == m.end()) {
m.insert({rule.resource(), {cb}});
} else {
auto& vec = it->second;
vec.push_back(std::move(cb));
}
}
return m;
}
void RulePropertyListener::ConfigUpdate(const RuleList& value, bool) {
RuleManager& m = RuleManager::GetInstance();
if (value.empty()) {
absl::WriterMutexLock lck(&(m.update_mtx_));
m.rule_map_.clear();
m.cb_map_.clear();
SENTINEL_LOG(info, "[CircuitBreakerRuleManager] Rules received: []");
return;
}
absl::WriterMutexLock lck(&(m.update_mtx_));
auto cbs = BuildCircuitBreakerMap(value);
std::unordered_map rule_map;
for (const auto& kv : cbs) {
if (kv.second.empty()) {
continue;
}
RuleSet rules{};
for (const auto& cb : kv.second) {
rules.insert(cb->GetRule());
}
rule_map.insert({kv.first, rules});
}
m.rule_map_ = std::move(rule_map);
m.cb_map_ = std::move(cbs);
LogRuleMap(m.rule_map_);
}
const std::string RulePropertyListener::Name() const {
return kPropertyListenerName;
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/rule_manager.h
================================================
#pragma once
#include
#include
#include
#include
#include "absl/synchronization/mutex.h"
#include "sentinel-core/circuitbreaker/circuit_breaker.h"
#include "sentinel-core/circuitbreaker/rule.h"
#include "sentinel-core/property/property_listener.h"
#include "sentinel-core/property/sentinel_property.h"
namespace Sentinel {
namespace CircuitBreaker {
using CircuitBreakerMap =
std::unordered_map>;
using RulePropertySharedPtr = Property::SentinelPropertySharedPtr;
class RuleManager {
public:
static RuleManager& GetInstance() {
static RuleManager* instance = new RuleManager();
return *instance;
}
friend class RulePropertyListener;
void RegisterToProperty(const RulePropertySharedPtr& property);
bool LoadRules(const RuleList& rules);
bool HasRules(const std::string& resource);
RuleList GetRules() const;
RuleSet GetRulesOfResource(const std::string& resource) const;
std::vector GetCircuitBreakers(
const std::string& resource) const;
private:
RuleManager();
virtual ~RuleManager() = default;
RulePropertySharedPtr cur_property_;
std::unordered_map rule_map_{};
CircuitBreakerMap cb_map_{};
mutable std::mutex property_mtx_;
mutable absl::Mutex update_mtx_;
CircuitBreakerSharedPtr GetExistingSameCbOrNew(const Rule& rule);
CircuitBreakerSharedPtr NewCircuitBreaker(const Rule& rule);
};
class RulePropertyListener : public Property::PropertyListener {
public:
RulePropertyListener() = default;
~RulePropertyListener() = default;
void ConfigUpdate(const RuleList& value, bool first_load) override;
const std::string Name() const override;
private:
CircuitBreakerMap BuildCircuitBreakerMap(const RuleList& list);
};
bool IsValidRule(const Rule& rule);
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/slot.cc
================================================
#include "sentinel-core/circuitbreaker/slot.h"
#include "sentinel-core/circuitbreaker/rule_manager.h"
#include "sentinel-core/utils/time_utils.h"
namespace Sentinel {
namespace CircuitBreaker {
// CheckerSlot
Sentinel::Slot::TokenResultSharedPtr CheckerSlot::Entry(
const EntrySharedPtr& entry, Stat::NodeSharedPtr& node, int count, int flag,
const std::vector& params) {
auto cbs =
RuleManager::GetInstance().GetCircuitBreakers(entry->resource()->name());
if (cbs.empty()) {
return Sentinel::Slot::TokenResult::Ok();
}
for (auto& cb : cbs) {
if (!cb->TryPass(node)) {
return Sentinel::Slot::TokenResult::Blocked("DegradeException");
}
}
return Sentinel::Slot::TokenResult::Ok();
}
void CheckerSlot::Exit(const EntrySharedPtr& entry, int count,
const std::vector& params) {}
// CompleteStatSlot
Sentinel::Slot::TokenResultSharedPtr CompleteStatSlot::Entry(
const EntrySharedPtr& entry,
/*const*/ Stat::NodeSharedPtr& node, int count, int flag,
const std::vector& params) {
if (entry == nullptr || entry->context() == nullptr) {
return Sentinel::Slot::TokenResult::Ok();
}
auto prev_result = entry->context()->last_token_result();
if (prev_result == nullptr) {
return Sentinel::Slot::TokenResult::Ok();
}
return prev_result;
}
void CompleteStatSlot::Exit(const EntrySharedPtr& entry, int count,
const std::vector& params) {
if (entry == nullptr || entry->HasBlockError()) {
return;
}
auto cbs =
RuleManager::GetInstance().GetCircuitBreakers(entry->resource()->name());
if (cbs.empty()) {
return;
}
auto rt = entry->rt();
if (rt < 0) {
rt = std::max(int64_t(0), Utils::TimeUtils::CurrentTimeMillis().count() -
entry->create_time().count());
}
for (auto& cb : cbs) {
cb->RecordComplete(rt, entry->error());
}
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/slot.h
================================================
#pragma once
#include "sentinel-core/slot/base/rule_checker_slot.h"
#include "sentinel-core/slot/base/stats_slot.h"
namespace Sentinel {
namespace CircuitBreaker {
constexpr auto kCheckerSlotName = "CircuitBreakerCheckerSlot";
constexpr auto kCompleteStatSlotName = "CircuitBreakerCompleteStatSlot";
class CheckerSlot : public Slot::RuleCheckerSlot {
public:
CheckerSlot() = default;
virtual ~CheckerSlot() = default;
Sentinel::Slot::TokenResultSharedPtr Entry(
const EntrySharedPtr& entry, Stat::NodeSharedPtr& node, int count,
int flag, const std::vector& params) override;
void Exit(const EntrySharedPtr& entry, int count,
const std::vector& params) override;
const std::string& Name() const override { return name_; };
private:
const std::string name_{kCheckerSlotName};
};
class CompleteStatSlot : public Slot::StatsSlot {
public:
CompleteStatSlot() = default;
virtual ~CompleteStatSlot() = default;
const std::string& Name() const override { return name_; };
Sentinel::Slot::TokenResultSharedPtr Entry(
const EntrySharedPtr& entry,
/*const*/ Stat::NodeSharedPtr& node, int count, int flag,
const std::vector& params) override;
void Exit(const EntrySharedPtr& entry, int count,
const std::vector& params) override;
private:
const std::string name_{kCompleteStatSlotName};
};
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/circuitbreaker/slot_test.cc
================================================
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "sentinel-core/test/mock/statistic/node/mock.h"
#include "sentinel-core/circuitbreaker/rule.h"
#include "sentinel-core/circuitbreaker/rule_manager.h"
#include "sentinel-core/circuitbreaker/slot.h"
#include "sentinel-core/common/string_resource_wrapper.h"
using testing::_;
using testing::InSequence;
using testing::Mock;
using testing::Return;
namespace Sentinel {
namespace CircuitBreaker {
Sentinel::Slot::TokenResultSharedPtr Entry_And_Exit(
CheckerSlot& slot_checker, CompleteStatSlot& slot_complete,
const EntrySharedPtr& entry, const ResourceWrapperSharedPtr& resource,
Stat::NodeSharedPtr& node, int count, int flag,
const std::vector& params) {
auto result = slot_checker.Entry(entry, node, count, flag, params);
EXPECT_EQ(Sentinel::Slot::TokenStatus::RESULT_STATUS_OK, result->status());
slot_complete.Entry(entry, node, count, flag, params);
slot_complete.Exit(entry, count, params);
return result;
}
TEST(CircuitBreakerSlotTest, CircuitBreakerErrorRatioTest) {
std::string resource_name{"test_resource"};
EntryContextSharedPtr context =
std::make_shared("test_context");
Stat::NodeSharedPtr node = std::make_shared();
auto resource =
std::make_shared(resource_name, EntryType::OUT);
auto entry = std::make_shared(resource, context);
entry->set_cur_node(node);
auto entry_error = std::make_shared(resource, context);
entry_error->set_cur_node(node);
entry_error->set_error("test_error");
const std::vector myParams;
CheckerSlot slot_checker;
CompleteStatSlot slot_complete;
// Test breaker checking when no rule exists.
for (int i = 0; i < 10; i++) {
Entry_And_Exit(slot_checker, slot_complete, entry, resource, node, 1, 0,
myParams);
}
Rule rule{resource_name};
rule.set_strategy(Strategy::kErrorRatio);
rule.set_threshold(0.5);
rule.set_retry_timeout_sec(1);
rule.set_stat_interval_ms(2000);
rule.set_min_request_amount(10);
RuleList rules{rule};
RuleManager& m = RuleManager::GetInstance();
m.LoadRules(rules);
auto cbs = m.GetCircuitBreakers(resource->name());
EXPECT_EQ(cbs[0]->CurrentState(), State::kClosed);
// Test breaker checking when error entry happens.
for (int i = 0; i < 10; i++) {
Entry_And_Exit(slot_checker, slot_complete, entry_error, resource, node, 1,
0, myParams);
}
EXPECT_EQ(cbs[0]->CurrentState(), State::kOpen);
// skip break time span
sleep(1);
// Switch state to kHalfOpen
auto result = slot_checker.Entry(entry, node, 1, 0, myParams);
EXPECT_EQ(Sentinel::Slot::TokenStatus::RESULT_STATUS_OK, result->status());
EXPECT_EQ(cbs[0]->CurrentState(), State::kHalfOpen);
// Switch state to kClosed
slot_complete.Entry(entry, node, 1, 0, myParams);
slot_complete.Exit(entry, 1, myParams);
EXPECT_EQ(cbs[0]->CurrentState(), State::kClosed);
m.LoadRules({});
}
TEST(CircuitBreakerSlotTest, CircuitBreakerSlowRatioTest) {
std::string resource_name{"test_resource"};
EntryContextSharedPtr context =
std::make_shared("test_context");
Stat::NodeSharedPtr node = std::make_shared();
auto resource =
std::make_shared(resource_name, EntryType::OUT);
auto entry = std::make_shared(resource, context);
entry->set_cur_node(node);
entry->set_rt(10);
auto entry_slow = std::make_shared(resource, context);
entry_slow->set_cur_node(node);
entry_slow->set_rt(500);
const std::vector myParams;
CheckerSlot slot_checker;
CompleteStatSlot slot_complete;
// Test breaker checking when no rule exists.
for (int i = 0; i < 10; i++) {
Entry_And_Exit(slot_checker, slot_complete, entry, resource, node, 1, 0,
myParams);
}
Rule rule{resource_name};
rule.set_strategy(Strategy::kSlowRtRatio);
rule.set_threshold(0.5);
rule.set_retry_timeout_sec(1);
rule.set_stat_interval_ms(10000);
rule.set_min_request_amount(10);
rule.set_max_allowed_rt(200);
RuleList rules{rule};
RuleManager& m = RuleManager::GetInstance();
m.LoadRules(rules);
auto cbs = m.GetCircuitBreakers(resource->name());
EXPECT_EQ(cbs[0]->CurrentState(), State::kClosed);
// Test breaker checking when slow entry happens.
for (int i = 0; i < 10; i++) {
Entry_And_Exit(slot_checker, slot_complete, entry_slow, resource, node, 1,
0, myParams);
}
EXPECT_EQ(cbs[0]->CurrentState(), State::kOpen);
// skip break time span
sleep(1);
// Switch state to kHalfOpen
auto result = slot_checker.Entry(entry, node, 1, 0, myParams);
EXPECT_EQ(Sentinel::Slot::TokenStatus::RESULT_STATUS_OK, result->status());
EXPECT_EQ(cbs[0]->CurrentState(), State::kHalfOpen);
// Switch state to kClosed
slot_complete.Entry(entry, node, 1, 0, myParams);
slot_complete.Exit(entry, 1, myParams);
EXPECT_EQ(cbs[0]->CurrentState(), State::kClosed);
m.LoadRules({});
}
} // namespace CircuitBreaker
} // namespace Sentinel
================================================
FILE: sentinel-core/common/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_library(
name = "constants",
srcs = [
"constants.h",
],
copts = DEFAULT_COPTS,
)
cc_library(
name = "global_status",
srcs = [
"global_status.h",
"global_status.cc",
],
copts = DEFAULT_COPTS,
)
cc_library(
name = "entry_type_enum",
srcs = [
"entry_type.h",
],
copts = DEFAULT_COPTS,
)
cc_library(
name = "resource_wrapper_interface",
srcs = [
"resource_wrapper.h",
],
copts = DEFAULT_COPTS,
deps = [
":entry_type_enum",
"@com_google_absl//absl/types:any",
]
)
cc_library(
name = "string_resource_wrapper_lib",
srcs = [
"string_resource_wrapper.h",
],
copts = DEFAULT_COPTS,
deps = [
":resource_wrapper_interface",
"@com_google_absl//absl/types:any",
]
)
cc_library(
name = "entry_context_lib",
srcs = [
"entry_context.h",
"entry_context.cc",
],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/statistic/node:node_interface",
"//sentinel-core/slot/base:token_result_lib",
]
)
cc_library(
name = "entry_lib",
srcs = [
"entry.h",
],
copts = DEFAULT_COPTS,
deps = [
":entry_context_lib",
":resource_wrapper_interface",
"//sentinel-core/utils:utils_lib",
]
)
cc_library(
name = "rule_lib",
srcs = [
"rule.h",
],
copts = DEFAULT_COPTS,
deps = [
":constants",
]
)
cc_library(
name = "entry_result_lib",
srcs = [
"entry_result.h",
"entry_result.cc",
],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/slot:global_slot_chain_header",
"@com_google_absl//absl/types:optional",
]
)
cc_library(
name = "tracer_lib",
srcs = [
"tracer.h",
"tracer.cc",
],
copts = DEFAULT_COPTS,
deps = [
":entry_lib",
]
)
cc_test(
name = "tracer_lib_unittests",
srcs = [
"tracer_test.cc",
],
copts = TEST_COPTS,
deps = [
":tracer_lib",
":entry_lib",
":string_resource_wrapper_lib",
"//sentinel-core/test/mock/statistic/node:mock_lib",
"@com_google_googletest//:gtest_main",
]
)
================================================
FILE: sentinel-core/common/constants.h
================================================
#pragma once
namespace Sentinel {
namespace Constants {
static constexpr int kDefaultSampleCount = 2;
static constexpr int kDefaultIntervalMs = 1000;
static constexpr const char* kLimitOriginDefault = "default";
static constexpr const char* kLimitOriginOther = "other";
static constexpr const char* kDefaultContextName = "sentinel_default_context";
static constexpr int kMaxAllowedRt = 4900;
static constexpr int kMaxResourceSize = 10000;
static constexpr int kMaxTagSize = 1000;
}; // namespace Constants
} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry.h
================================================
#pragma once
#include
#include
#include
#include "sentinel-core/common/entry_context.h"
#include "sentinel-core/common/resource_wrapper.h"
#include "sentinel-core/utils/time_utils.h"
namespace Sentinel {
class Entry {
public:
explicit Entry(const ResourceWrapperSharedPtr& resource,
const EntryContextSharedPtr& context)
: resource_(resource),
context_(context),
create_time_(Utils::TimeUtils::CurrentTimeMillis()) {}
virtual ~Entry() = default;
friend class EntryResult;
friend class SphU;
ResourceWrapperSharedPtr resource() const { return resource_; }
std::chrono::milliseconds create_time() const { return create_time_; }
EntryContextSharedPtr context() const { return context_; }
Stat::NodeSharedPtr cur_node() const { return cur_node_; }
int64_t rt() const { return rt_; }
std::vector params() const { return params_; }
bool exited() const { return exited_; }
std::string error() const { return error_; }
bool HasError() const { return !error_.empty(); };
bool HasBlockError() const { return !block_error_.empty(); };
void set_rt(int64_t rt) { rt_ = rt; }
void set_error(const std::string& message) { error_ = message; }
void set_block_error(const std::string& message) { block_error_ = message; }
void set_cur_node(const Stat::NodeSharedPtr& node) { cur_node_ = node; }
void set_params(const std::vector&& params) { params_ = params; }
private:
const ResourceWrapperSharedPtr resource_;
EntryContextSharedPtr context_;
const std::chrono::milliseconds create_time_;
bool exited_{false};
int64_t rt_{-1};
std::string error_{};
std::string block_error_{};
Stat::NodeSharedPtr cur_node_;
std::vector params_;
};
using EntrySharedPtr = std::shared_ptr;
} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry_context.cc
================================================
#include
#include "sentinel-core/common/entry_context.h"
namespace Sentinel {} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry_context.h
================================================
#pragma once
#include
#include
#include "sentinel-core/slot/base/token_result.h"
#include "sentinel-core/statistic/node/node.h"
namespace Sentinel {
class EntryContext {
public:
explicit EntryContext(const std::string& name) : EntryContext(name, "") {}
EntryContext(const std::string& name, const std::string& tag)
: name_(name), tag_(tag) {}
const std::string& name() const { return name_; };
const std::string& tag() const { return tag_; };
Stat::NodeSharedPtr tag_node() { return tag_node_; }
const Slot::TokenResultSharedPtr& last_token_result() const {
return last_token_result_;
}
/**
* Users should not invoke this.
*/
void set_last_token_result(const Slot::TokenResultSharedPtr& r) {
last_token_result_ = r;
}
void set_tag_node(Stat::NodeSharedPtr& node) { tag_node_ = node; }
private:
const std::string name_;
// Maybe multiple tags in the future, using vector instead of string.
const std::string tag_;
Stat::NodeSharedPtr tag_node_;
Slot::TokenResultSharedPtr last_token_result_;
};
using EntryContextSharedPtr = std::shared_ptr;
} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry_node.h
================================================
================================================
FILE: sentinel-core/common/entry_result.cc
================================================
#include "sentinel-core/common/entry_result.h"
#include "sentinel-core/slot/global_slot_chain.h"
namespace Sentinel {
bool EntryResult::IsBlocked() const { return blocked_reason_.has_value(); }
bool EntryResult::Exit() { return Exit(1); }
bool EntryResult::Exit(int count) {
if (entry_ == nullptr) {
return false;
}
const std::vector params = entry_->params();
if (!entry_->exited()) {
Slot::SlotChainSharedPtr chain = Slot::GetGlobalSlotChain();
if (chain != nullptr) {
// NOTE: keep consistent with exit operation in SphU::Entry when blocked.
chain->Exit(entry_, count, params);
}
entry_->exited_ = true;
return true;
}
return false;
}
void EntryResult::SetError(const std::string& err) {
if (entry_ != nullptr) {
entry_->set_error(err);
}
}
} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry_result.h
================================================
#pragma once
#include
#include
#include
#include "absl/types/optional.h"
#include "sentinel-core/common/entry_result.h"
#include "sentinel-core/slot/global_slot_chain.h"
namespace Sentinel {
class EntryResult {
public:
explicit EntryResult(const EntrySharedPtr& entry) : entry_(entry) {}
explicit EntryResult(const std::string& reason)
: entry_(nullptr), blocked_reason_(reason) {}
~EntryResult() = default;
EntrySharedPtr entry() const { return entry_; };
absl::optional blocked_reason() const {
return blocked_reason_;
};
bool IsBlocked() const;
bool Exit();
bool Exit(int count);
bool Exit(int count, const std::vector& params);
void SetError(const std::string& err);
private:
const EntrySharedPtr entry_;
const absl::optional blocked_reason_;
};
using EntryResultPtr = std::unique_ptr;
} // namespace Sentinel
================================================
FILE: sentinel-core/common/entry_type.h
================================================
#pragma once
namespace Sentinel {
enum class EntryType { IN, OUT };
} // namespace Sentinel
================================================
FILE: sentinel-core/common/global_status.cc
================================================
namespace Sentinel {
namespace GlobalStatus {
bool activated = true;
}; // namespace GlobalStatus
} // namespace Sentinel
================================================
FILE: sentinel-core/common/global_status.h
================================================
#pragma once
namespace Sentinel {
namespace GlobalStatus {
extern bool activated;
}; // namespace GlobalStatus
} // namespace Sentinel
================================================
FILE: sentinel-core/common/resource_wrapper.h
================================================
#pragma once
#include
#include
#include "absl/types/any.h"
#include "sentinel-core/common/entry_type.h"
namespace Sentinel {
class ResourceWrapper;
using ResourceWrapperSharedPtr = std::shared_ptr;
class ResourceWrapper {
public:
virtual ~ResourceWrapper() = default;
virtual const std::string& name() const = 0;
virtual EntryType entry_type() const = 0;
};
} // namespace Sentinel
================================================
FILE: sentinel-core/common/rule.h
================================================
#pragma once
#include
#include "sentinel-core/common/constants.h"
namespace Sentinel {
class Rule {
public:
Rule() = default;
virtual ~Rule() = default;
static bool LimitOriginEquals(const std::string& lhs,
const std::string& rhs) {
if (lhs.empty()) {
return rhs.empty() || rhs == Constants::kLimitOriginDefault;
} else if (lhs == Constants::kLimitOriginDefault) {
return rhs.empty() || rhs == Constants::kLimitOriginDefault;
}
return lhs == rhs;
}
};
} // namespace Sentinel
================================================
FILE: sentinel-core/common/string_resource_wrapper.h
================================================
#pragma once
#include
#include "sentinel-core/common/resource_wrapper.h"
namespace Sentinel {
class StringResourceWrapper : public ResourceWrapper {
public:
StringResourceWrapper(const std::string& name, EntryType type)
: name_(name), entry_type_(type) {}
virtual ~StringResourceWrapper() = default;
const std::string& name() const override { return name_; }
EntryType entry_type() const override { return entry_type_; }
private:
std::string name_;
EntryType entry_type_;
};
} // namespace Sentinel
================================================
FILE: sentinel-core/common/tracer.cc
================================================
#include
#include "sentinel-core/common/tracer.h"
#include "sentinel-core/statistic/node/node.h"
namespace Sentinel {
void Tracer::Trace(const EntrySharedPtr entry, const std::string&, int count) {
if (entry == nullptr || count <= 0) {
return;
}
// TODO: check BlockException?
Stat::NodeSharedPtr node = entry->cur_node();
if (node != nullptr) {
node->AddExceptionRequest(count);
}
}
void Tracer::Trace(const EntrySharedPtr entry, const std::string& message) {
Tracer::Trace(entry, message, 1);
}
} // namespace Sentinel
================================================
FILE: sentinel-core/common/tracer.h
================================================
#pragma once
#include
#include "sentinel-core/common/entry.h"
namespace Sentinel {
class Tracer {
public:
Tracer() = delete;
static void Trace(const EntrySharedPtr entry, const std::string& message);
static void Trace(const EntrySharedPtr entry, const std::string& message,
int count);
};
} // namespace Sentinel
================================================
FILE: sentinel-core/common/tracer_test.cc
================================================
#include
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "sentinel-core/common/entry.h"
#include "sentinel-core/common/string_resource_wrapper.h"
#include "sentinel-core/common/tracer.h"
#include "sentinel-core/test/mock/statistic/node/mock.h"
using testing::_;
using testing::InSequence;
using testing::Return;
namespace Sentinel {
TEST(TracerTest, TraceExceptionTest) {
{
auto node = std::make_shared();
int n = 10;
EXPECT_CALL(*node.get(), AddExceptionRequest(n)).Times(1);
EntrySharedPtr entry = std::make_shared(
std::make_shared("abc", EntryType::IN), nullptr);
entry->set_cur_node(node);
Tracer::Trace(entry, "some_exception", n);
}
}
} // namespace Sentinel
================================================
FILE: sentinel-core/config/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_library(
name = "config_constants",
srcs = [
"config_constants.h",
],
copts = DEFAULT_COPTS,
)
cc_library(
name = "local_config_lib",
srcs = [
"local_config.h",
"local_config.cc",
],
copts = DEFAULT_COPTS,
deps = [
":config_constants",
"//sentinel-core/init:init_target_interface",
"//sentinel-core/log:logger_lib",
"@com_google_absl//absl/strings",
]
)
cc_test(
name = "local_config_unittests",
srcs = [
"local_config_test.cc",
],
copts = TEST_COPTS,
deps = [
":local_config_lib",
"@com_google_googletest//:gtest_main",
]
)
================================================
FILE: sentinel-core/config/config_constants.h
================================================
#pragma once
#include
namespace Sentinel {
namespace Config {
const auto kUnknownAppName = "unknown_cpp_service";
constexpr uint32_t kDefaultAppType = 0;
constexpr auto kDefaultCharset = "UTF-8";
constexpr uint32_t kDefaultSingleMetricFileSize = 1024 * 1024 * 50;
const uint32_t kDefaultTotalMetricFileCount = 6;
const uint32_t kDefaultWarmUpColdFactor = 3;
const uint32_t kDefaultStatisticMaxRt = 4900;
namespace Env {
constexpr auto kAppNameKey = "CSP_SENTINEL_APP_NAME";
constexpr auto kAppTypeKey = "CSP_SENTINEL_APP_TYPE";
constexpr auto kCharsetKey = "CSP_SENTINEL_CHARSET";
constexpr auto kSingleMetricFileSizeKey =
"CSP_SENTINEL_METRIC_FILE_SINGLE_SIZE";
constexpr auto kTotalMetricFileCountKey =
"CSP_SENTINEL_METRIC_FILE_TOTAL_COUNT";
constexpr auto kWarmUpColdFactorKey = "CSP_SENTINEL_FLOW_WARMUP_COLD_FACTOR";
constexpr auto kStatisticMaxRtKey = "CSP_SENTINEL_STATISTIC_MAX_RT";
} // namespace Env
namespace Args {
constexpr auto kAppNameKey = "project.name";
constexpr auto kAppTypeKey = "csp.sentinel.app.type";
constexpr auto kCharsetKey = "csp.sentinel.charset";
constexpr auto kSingleMetricFileSizeKey =
"csp.sentinel.metric.file.single.size";
constexpr auto kTotalMetricFileCountKey =
"csp.sentinel.metric.file.total.count";
constexpr auto kWarmUpColdFactorKey = "csp.sentinel.flow.cold.factor";
constexpr auto kStatisticMaxRtKey = "csp.sentinel.statistic.max.rt";
} // namespace Args
} // namespace Config
} // namespace Sentinel
================================================
FILE: sentinel-core/config/local_config.cc
================================================
#include
#include "absl/strings/numbers.h"
#include "sentinel-core/config/config_constants.h"
#include "sentinel-core/config/local_config.h"
#include "sentinel-core/log/logger.h"
namespace Sentinel {
namespace Config {
LocalConfig::LocalConfig() {
// Initialize on create.
this->Initialize();
}
void LocalConfig::SetConfig(const std::string& key, const std::string& value) {
config_map_.emplace(std::make_pair(key, value));
}
void LocalConfig::SetConfigIfNotExists(const std::string& key,
const std::string& value) {
auto iter = config_map_.find(key);
if (iter != config_map_.end()) {
return;
}
SetConfig(key, value);
}
void LocalConfig::RemoveConfig(const std::string& key) {
config_map_.erase(key);
}
const std::string LocalConfig::GetConfig(const std::string& key) const {
auto iter = config_map_.find(key);
if (iter == config_map_.end()) {
return std::string();
}
return iter->second;
}
int32_t LocalConfig::GetInt32(const std::string& key,
int32_t default_value) const {
auto v = GetConfig(key);
if (v.empty()) {
return default_value;
}
int32_t x;
if (!absl::SimpleAtoi(v, &x)) {
x = default_value;
}
return x;
}
int64_t LocalConfig::GetInt64(const std::string& key,
int64_t default_value) const {
auto v = GetConfig(key);
if (v.empty()) {
return default_value;
}
int64_t x;
if (!absl::SimpleAtoi(v, &x)) {
x = default_value;
}
return x;
}
int32_t LocalConfig::WarmUpColdFactor() const {
int cold_factor =
GetInt32(Env::kWarmUpColdFactorKey, kDefaultWarmUpColdFactor);
if (cold_factor <= 1) {
SENTINEL_LOG(
info,
"Invalid cold_factor <{}>, fallback with the default cold factor <{}>",
cold_factor, kDefaultWarmUpColdFactor);
cold_factor = kDefaultWarmUpColdFactor;
}
return cold_factor;
}
int32_t LocalConfig::StatisticMaxRt() const {
int max_rt = GetInt32(Env::kStatisticMaxRtKey, kDefaultStatisticMaxRt);
if (max_rt < 0) {
max_rt = kDefaultStatisticMaxRt;
}
return max_rt;
}
int32_t LocalConfig::TotalMetricFileCount() const {
return GetInt32(Env::kTotalMetricFileCountKey, kDefaultTotalMetricFileCount);
}
int64_t LocalConfig::SingleMetricFileSize() const {
return GetInt64(Env::kSingleMetricFileSizeKey, kDefaultSingleMetricFileSize);
}
const std::string LocalConfig::Charset() const {
return GetConfig(Env::kCharsetKey);
}
void LocalConfig::ResolveAppName() {
const char* app_name_env = std::getenv(Env::kAppNameKey);
if (app_name_env) {
app_name_ = app_name_env;
SENTINEL_LOG(info, "App name resolved: {}", app_name_);
} else {
app_name_ = kUnknownAppName;
SENTINEL_LOG(warn, "No {} configured, using the fallback app name: {}",
Env::kAppNameKey, kUnknownAppName);
}
}
void LocalConfig::Initialize() {
ResolveAppName();
config_map_.emplace(std::make_pair(Env::kCharsetKey, kDefaultCharset));
config_map_.emplace(
std::make_pair(Env::kSingleMetricFileSizeKey,
std::to_string(kDefaultSingleMetricFileSize)));
config_map_.emplace(
std::make_pair(Env::kTotalMetricFileCountKey,
std::to_string(kDefaultTotalMetricFileCount)));
config_map_.emplace(std::make_pair(Env::kWarmUpColdFactorKey,
std::to_string(kDefaultWarmUpColdFactor)));
config_map_.emplace(std::make_pair(Env::kStatisticMaxRtKey,
std::to_string(kDefaultStatisticMaxRt)));
}
} // namespace Config
} // namespace Sentinel
================================================
FILE: sentinel-core/config/local_config.h
================================================
#pragma once
#include
#include
#include "sentinel-core/init/init_target.h"
namespace Sentinel {
namespace Config {
class LocalConfig {
public:
~LocalConfig() = default;
static LocalConfig& GetInstance() {
static LocalConfig* instance = new LocalConfig();
return *instance;
}
const std::string GetConfig(const std::string& key) const;
void SetConfig(const std::string& key, const std::string& value);
void SetConfigIfNotExists(const std::string& key, const std::string& value);
void RemoveConfig(const std::string& key);
int32_t GetInt32(const std::string& key, int32_t default_value) const;
int64_t GetInt64(const std::string& key, int64_t default_value) const;
const std::string& app_name() const { return app_name_; }
void set_app_name(const std::string& app_name) { app_name_ = app_name; }
int32_t WarmUpColdFactor() const;
int32_t StatisticMaxRt() const;
int32_t TotalMetricFileCount() const;
int64_t SingleMetricFileSize() const;
const std::string Charset() const;
private:
std::unordered_map config_map_;
std::string app_name_;
LocalConfig();
void ResolveAppName();
void Initialize();
};
} // namespace Config
} // namespace Sentinel
================================================
FILE: sentinel-core/config/local_config_test.cc
================================================
#include
#include
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "sentinel-core/config/config_constants.h"
#include "sentinel-core/config/local_config.h"
namespace Sentinel {
namespace Config {
TEST(LocalConfigTest, TestResolveNormalAppName) {
auto app_name = "test_app";
setenv(Env::kAppNameKey, app_name, 1);
LocalConfig config = LocalConfig::GetInstance();
EXPECT_EQ(app_name, config.app_name());
unsetenv(Env::kAppNameKey);
}
TEST(LocalConfigTest, TestGetIntOfInvalidValue) {
LocalConfig config = LocalConfig::GetInstance();
constexpr auto key = "some_key_bad";
config.SetConfig(key, "a32");
constexpr int32_t d = 32;
EXPECT_EQ(d, config.GetInt32(key, d));
}
TEST(LocalConfigTest, TestGetIntOfNormalValue) {
LocalConfig config = LocalConfig::GetInstance();
constexpr auto key = "some_key_good";
config.SetConfig(key, "64");
constexpr int32_t d = 32;
EXPECT_EQ(64, config.GetInt32(key, d));
}
} // namespace Config
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/BUILD
================================================
load("//bazel:copts.bzl", "DEFAULT_COPTS", "TEST_COPTS")
package(default_visibility = ["//visibility:public"])
cc_library(
name = "flow_rule_constants_lib",
srcs = [
"flow_rule_constants.h",
],
copts = DEFAULT_COPTS
)
cc_library(
name = "flow_rule_lib",
srcs = [
"flow_rule.h",
"flow_rule.cc",
],
copts = DEFAULT_COPTS,
deps = [
"@com_google_absl//absl/strings:str_format",
":flow_rule_constants_lib",
"//sentinel-core/common:rule_lib",
]
)
cc_library(
name = "flow_rule_manager_lib",
srcs = [
"flow_rule_manager.h",
"flow_rule_manager.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_constants_lib",
":flow_rule_lib",
":traffic_shaping_controller_lib",
":default_traffic_shaping_calculator_lib",
":default_traffic_shaping_checker_lib",
"//sentinel-core/property:property_listener_interface",
"//sentinel-core/property:dynamic_sentinel_property_lib",
"@com_google_absl//absl/synchronization",
]
)
cc_library(
name = "flow_rule_checker_lib",
srcs = [
"flow_rule_checker.h",
"flow_rule_checker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_manager_lib",
"//sentinel-core/common:entry_lib",
"//sentinel-core/statistic/node:resource_node_storage_lib",
]
)
cc_library(
name = "flow_slot_lib",
srcs = [
"flow_slot.h",
"flow_slot.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_checker_lib",
"//sentinel-core/slot/base:rule_checker_slot_interface",
]
)
cc_library(
name = "traffic_shaping_calculator_interface",
srcs = [
"traffic_shaping_calculator.h",
],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/statistic/node:node_interface",
]
)
cc_library(
name = "traffic_shaping_checker_interface",
srcs = [
"traffic_shaping_checker.h",
],
copts = DEFAULT_COPTS,
deps = [
"//sentinel-core/statistic/node:node_interface",
"//sentinel-core/slot/base:token_result_lib"
]
)
cc_library(
name = "default_traffic_shaping_calculator_lib",
srcs = [
"default_traffic_shaping_calculator.h",
"default_traffic_shaping_calculator.cc",
],
copts = DEFAULT_COPTS,
deps = [
":traffic_shaping_calculator_interface",
]
)
cc_library(
name = "default_traffic_shaping_checker_lib",
srcs = [
"default_traffic_shaping_checker.h",
"default_traffic_shaping_checker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_constants_lib",
":traffic_shaping_checker_interface",
]
)
cc_library(
name = "throttling_traffic_shaping_checker_lib",
srcs = [
"throttling_traffic_shaping_checker.h",
"throttling_traffic_shaping_checker.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_constants_lib",
":traffic_shaping_checker_interface",
"//sentinel-core/utils:utils_lib",
]
)
cc_library(
name = "traffic_shaping_controller_lib",
srcs = [
"traffic_shaping_controller.h",
"traffic_shaping_controller.cc",
],
copts = DEFAULT_COPTS,
deps = [
":flow_rule_constants_lib",
":traffic_shaping_checker_interface",
":traffic_shaping_calculator_interface",
]
)
cc_test(
name = "traffic_shaping_controller_unittests",
srcs = [
"traffic_shaping_controller_test.cc",
],
copts = TEST_COPTS,
deps = [
":default_traffic_shaping_calculator_lib",
":default_traffic_shaping_checker_lib",
":traffic_shaping_controller_lib",
"//sentinel-core/test/mock/flow:flow_mock_lib",
"//sentinel-core/test/mock/statistic/node:mock_lib",
"@com_google_googletest//:gtest_main",
]
)
cc_test(
name = "flow_slot_unittests",
srcs = [
"flow_slot_test.cc",
],
copts = TEST_COPTS,
deps = [
":flow_slot_lib",
"//sentinel-core/common:string_resource_wrapper_lib",
"//sentinel-core/test/mock/statistic/node:mock_lib",
"@com_google_googletest//:gtest_main",
]
)
================================================
FILE: sentinel-core/flow/default_traffic_shaping_calculator.cc
================================================
#include "sentinel-core/flow/default_traffic_shaping_calculator.h"
namespace Sentinel {
namespace Flow {
double DefaultTrafficShapingCalculator::CalculateAllowedTokens(
const Stat::NodeSharedPtr&, int, int) {
return threshold_;
}
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/default_traffic_shaping_calculator.h
================================================
#pragma once
#include "sentinel-core/flow/traffic_shaping_calculator.h"
namespace Sentinel {
namespace Flow {
class DefaultTrafficShapingCalculator : public TrafficShapingCalculator {
public:
DefaultTrafficShapingCalculator(double t) : threshold_(t) {}
virtual ~DefaultTrafficShapingCalculator() = default;
double CalculateAllowedTokens(const Stat::NodeSharedPtr& node,
int acquire_count, int flag) override;
private:
const double threshold_;
};
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/default_traffic_shaping_checker.cc
================================================
#include "sentinel-core/flow/default_traffic_shaping_checker.h"
#include "sentinel-core/flow/flow_rule_constants.h"
namespace Sentinel {
namespace Flow {
Slot::TokenResultSharedPtr DefaultTrafficShapingChecker::DoCheck(
const Stat::NodeSharedPtr& node, int acquire_count, double threshold) {
double cur_pass = 0;
if (node != nullptr) {
cur_pass = mode_ == (int)FlowMetricType::kThreadCount ? node->CurThreadNum()
: node->PassQps();
}
if (cur_pass + acquire_count > threshold) {
return Slot::TokenResult::Blocked("FlowException");
}
return Slot::TokenResult::Ok();
}
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/default_traffic_shaping_checker.h
================================================
#pragma once
#include "sentinel-core/flow/flow_rule_constants.h"
#include "sentinel-core/flow/traffic_shaping_checker.h"
namespace Sentinel {
namespace Flow {
class DefaultTrafficShapingChecker : public TrafficShapingChecker {
public:
explicit DefaultTrafficShapingChecker(int mode) : mode_(mode) {}
explicit DefaultTrafficShapingChecker(FlowMetricType mode)
: mode_((int)mode) {}
virtual ~DefaultTrafficShapingChecker() = default;
Slot::TokenResultSharedPtr DoCheck(const Stat::NodeSharedPtr& node,
int acquire_count,
double threshold) override;
private:
const int mode_;
};
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule.cc
================================================
#include
#include "sentinel-core/flow/flow_rule.h"
#include "absl/strings/str_format.h"
namespace Sentinel {
namespace Flow {
bool FlowRule::operator==(const FlowRule& rule) const {
return resource_ == rule.resource() &&
Rule::LimitOriginEquals(limit_origin_, rule.limit_origin()) &&
metric_type_ == rule.metric_type() && count_ == rule.count() &&
strategy_ == rule.strategy() && ref_resource_ == rule.ref_resource() &&
control_behavior_ == rule.control_behavior() &&
warm_up_period_sec_ == rule.warm_up_period_sec() &&
max_queueing_time_ms_ == rule.max_queueing_time_ms() &&
cluster_mode_ == rule.cluster_mode();
}
std::string FlowRule::ToString() const {
return absl::StrFormat(
"FlowRule{resource=%s, limit_origin=%s, metric_type=%d, count=%.2f, "
"strategy=%d, ref_resource=%s, control_behavior=%d, "
"warm_up_period_sec=%d, max_queueing_time_ms=%d, cluster_mode=%d}",
resource_, limit_origin_, static_cast(metric_type_), count_,
static_cast(strategy_), ref_resource_,
static_cast(control_behavior_), warm_up_period_sec_,
max_queueing_time_ms_, cluster_mode_);
}
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule.h
================================================
#pragma once
#include
#include
#include
#include
#include "sentinel-core/common/constants.h"
#include "sentinel-core/common/rule.h"
#include "sentinel-core/flow/flow_rule_constants.h"
namespace Sentinel {
namespace Flow {
struct FlowRule : public Rule {
public:
FlowRule() = default;
virtual ~FlowRule() = default;
explicit FlowRule(const std::string& resource)
: resource_(resource), limit_origin_(Constants::kLimitOriginDefault) {}
FlowRule(const std::string& resource, const std::string& limit_origin)
: resource_(resource), limit_origin_(limit_origin) {}
const std::string& resource() const { return resource_; }
const std::string& limit_origin() const { return limit_origin_; }
FlowMetricType metric_type() const { return metric_type_; }
double count() const { return count_; }
FlowRelationStrategy strategy() const { return strategy_; }
const std::string& ref_resource() const { return ref_resource_; }
FlowControlBehavior control_behavior() const { return control_behavior_; }
int32_t warm_up_period_sec() const { return warm_up_period_sec_; }
int32_t max_queueing_time_ms() const { return max_queueing_time_ms_; }
bool cluster_mode() const { return cluster_mode_; }
void set_resource(const std::string& resource) { resource_ = resource; }
void set_limit_origin(const std::string& limit_origin) {
limit_origin_ = limit_origin;
}
void set_limit_origin(const char* limit_origin) {
if (limit_origin != nullptr) {
limit_origin_ = limit_origin;
}
}
void set_metric_type(FlowMetricType metric_type) {
metric_type_ = metric_type;
}
void set_count(double count) { count_ = count; }
void set_strategy(FlowRelationStrategy strategy) { strategy_ = strategy; }
void set_ref_resource(const std::string& r) { ref_resource_ = r; }
void set_control_behavior(FlowControlBehavior cb) { control_behavior_ = cb; }
void set_warm_up_period_sec(int32_t w) { warm_up_period_sec_ = w; }
void set_max_queueing_time_ms(int32_t q) { max_queueing_time_ms_ = q; }
void set_cluster_mode(bool cluster_mode) { cluster_mode_ = cluster_mode; }
bool operator==(const FlowRule& rule) const;
std::string ToString() const;
private:
std::string resource_; // resource
std::string limit_origin_{Constants::kLimitOriginDefault}; // limitApp
FlowMetricType metric_type_{FlowMetricType::kQps}; // grade
double count_ = 0; // count
FlowRelationStrategy strategy_{FlowRelationStrategy::kDirect}; // strategy
FlowControlBehavior control_behavior_{
FlowControlBehavior::kReject}; // controlBehavior
std::string ref_resource_{}; // refResource
int32_t warm_up_period_sec_ = 10; // warmUpPeriodSec
int32_t max_queueing_time_ms_ = 500; // maxQueueingTimeMs
bool cluster_mode_ = false; // clusterMode
};
using FlowRulePtr = std::shared_ptr;
using FlowRuleList = std::vector;
struct FlowRuleHash {
std::size_t operator()(const FlowRule& rule) const noexcept {
std::size_t result = std::hash{}(rule.resource());
const std::string& limit_origin = rule.limit_origin();
if (!limit_origin.empty() &&
limit_origin != Constants::kLimitOriginDefault) {
result = 31 * result + std::hash{}(limit_origin);
}
result = 31 * result + static_cast(rule.metric_type());
result = 31 * result + std::hash{}(rule.count());
result = 31 * result + static_cast(rule.strategy());
result = 31 * result + static_cast(rule.control_behavior());
result = 31 * result + std::hash{}(rule.ref_resource());
result = 31 * result + rule.warm_up_period_sec();
result = 31 * result + rule.max_queueing_time_ms();
result = 31 * result + std::hash{}(rule.cluster_mode());
return result;
}
};
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule_checker.cc
================================================
#include "sentinel-core/flow/flow_rule_checker.h"
#include "sentinel-core/flow/flow_rule_constants.h"
#include "sentinel-core/flow/flow_rule_manager.h"
#include "sentinel-core/statistic/node/resource_node_storage.h"
namespace Sentinel {
namespace Flow {
Slot::TokenResultSharedPtr FlowRuleChecker::CanPassCheck(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node, int count, int flag) {
if (rule.limit_origin().empty()) {
return Slot::TokenResult::Ok();
}
// if (rule.cluster_mode()) {
// return PassClusterCheck();
// }
return PassLocalCheck(rule, entry, node, count, flag);
}
Slot::TokenResultSharedPtr FlowRuleChecker::CanPassCheck(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node, int count) {
return CanPassCheck(rule, entry, node, count, 0);
}
Slot::TokenResultSharedPtr FlowRuleChecker::PassLocalCheck(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node, int count, int flag) {
Stat::NodeSharedPtr selected_node =
selectNodeByRequesterAndStrategy(rule, entry, node);
if (selected_node == nullptr) {
return Slot::TokenResult::Ok();
}
auto controller =
FlowRuleManager::GetInstance().GetTrafficControllerFor(rule);
if (controller == nullptr) {
return Slot::TokenResult::Ok();
}
return controller->CanPass(selected_node, count, flag);
}
Stat::NodeSharedPtr FlowRuleChecker::selectNodeByRequesterAndStrategy(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node) {
FlowRuleManager& m = FlowRuleManager::GetInstance();
std::string tag = entry->context()->tag();
std::string limit_origin = rule.limit_origin();
FlowRelationStrategy strategy = rule.strategy();
Stat::NodeSharedPtr tag_node = entry->context()->tag_node();
if ((tag == limit_origin) && IsValidTag(tag)) {
if (strategy == FlowRelationStrategy::kDirect) {
// When tag matches, return tag node.
return tag_node;
}
return SelectNodeByRelStrategy(rule, entry, node);
} else if (limit_origin == Constants::kLimitOriginDefault) {
if (strategy == FlowRelationStrategy::kDirect) {
// When rule contains default tag, which means all request should follow
// rule's limit count.
return node;
}
return SelectNodeByRelStrategy(rule, entry, node);
} else if ((limit_origin == Constants::kLimitOriginOther) &&
m.IsTagNotInFlowRuleList(rule.resource(), tag)) {
if (strategy == FlowRelationStrategy::kDirect) {
// When rule contains other tag, which means all request except this tag
// should follow this rule.
return tag_node;
}
return SelectNodeByRelStrategy(rule, entry, node);
}
return nullptr;
}
Stat::NodeSharedPtr FlowRuleChecker::SelectNodeByRelStrategy(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node) {
const std::string& ref_resource = rule.ref_resource();
auto rel_strategy = rule.strategy();
if (!ref_resource.empty() &&
rel_strategy == FlowRelationStrategy::kAssociatedResource) {
return Stat::ResourceNodeStorage::GetInstance().GetClusterNode(
ref_resource);
}
return node;
}
bool FlowRuleChecker::IsValidTag(const std::string& tag) {
return !tag.empty() && (tag != Constants::kLimitOriginDefault) &&
(tag != Constants::kLimitOriginOther);
}
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule_checker.h
================================================
#pragma once
#include "sentinel-core/common/entry.h"
#include "sentinel-core/flow/flow_rule.h"
#include "sentinel-core/slot/base/token_result.h"
namespace Sentinel {
namespace Flow {
class FlowRuleChecker {
public:
FlowRuleChecker() = default;
~FlowRuleChecker() = default;
Slot::TokenResultSharedPtr CanPassCheck(const FlowRule& rule,
const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node,
int count);
Slot::TokenResultSharedPtr CanPassCheck(const FlowRule& rule,
const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node,
int count, int flag);
private:
Slot::TokenResultSharedPtr PassLocalCheck(const FlowRule& rule,
const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node,
int count, int flag);
Stat::NodeSharedPtr selectNodeByRequesterAndStrategy(
const FlowRule& rule, const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node);
Stat::NodeSharedPtr SelectNodeByRelStrategy(const FlowRule& rule,
const EntrySharedPtr& entry,
const Stat::NodeSharedPtr& node);
bool IsValidTag(const std::string& tag);
};
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule_constants.h
================================================
#pragma once
namespace Sentinel {
namespace Flow {
enum class FlowMetricType {
kThreadCount = 0,
kQps = 1 // default mode
};
enum class FlowRelationStrategy {
kDirect = 0, // default relation strategy
kAssociatedResource = 1,
kInvocationChainEntrance = 2
};
enum class FlowControlBehavior {
kReject = 0, // default behavior
kWarmUp = 1,
kThrotting = 2,
kWarmUpThrottling = 3
};
} // namespace Flow
} // namespace Sentinel
================================================
FILE: sentinel-core/flow/flow_rule_manager.cc
================================================
#include
#include
#include
#include
#include "sentinel-core/flow/default_traffic_shaping_calculator.h"
#include "sentinel-core/flow/default_traffic_shaping_checker.h"
#include "sentinel-core/flow/flow_rule_manager.h"
#include "sentinel-core/log/logger.h"
#include "sentinel-core/property/dynamic_sentinel_property.h"
namespace Sentinel {
namespace Flow {
constexpr auto kFlowPropertyListenerName = "FlowPropertyListener";
bool IsValidRule(const FlowRule& rule) {
bool base_valid = !rule.resource().empty() && rule.count() >= 0;
if (!base_valid) {
return false;
}
// Check rel strategy.
bool rel = rule.strategy() == FlowRelationStrategy::kAssociatedResource ||
rule.strategy() == FlowRelationStrategy::kInvocationChainEntrance;
bool rel_valid = !rel || !rule.ref_resource().empty();
// Check control behavior.
bool cb_valid;
switch (rule.control_behavior()) {
case FlowControlBehavior::kWarmUp:
cb_valid = rule.warm_up_period_sec() > 0;
break;
case FlowControlBehavior::kThrotting:
cb_valid = rule.max_queueing_time_ms() > 0;
break;
default:
cb_valid = true;
}
return rel_valid && cb_valid;
}
std::shared_ptr CreateDefaultController(
const FlowRule& rule) {
return std::make_shared(
std::make_unique(rule.count()),
std::make_unique(rule.metric_type()));
}
// FlowRuleManager
FlowRuleManager::FlowRuleManager() {
cur_property_ = std::make_shared<
Property::DynamicSentinelProperty>>();
cur_property_->AddListener(std::make_unique());
}
bool FlowRuleManager::LoadRules(const FlowRuleList& rules) {
return cur_property_->UpdateValue(rules);
}
bool FlowRuleManager::HasRules(const std::string& resource) {
absl::ReaderMutexLock lck(&update_mtx_);
return rule_map_.find(resource) != rule_map_.end();
}
FlowRuleList FlowRuleManager::GetRules() const {
absl::ReaderMutexLock lck(&update_mtx_);
FlowRuleList list{};
for (const auto& e : rule_map_) {
list.insert(std::end(list), std::begin(e.second), std::end(e.second));
}
return list;
}
FlowRuleList FlowRuleManager::GetRulesForResource(
const std::string& resource) const {
absl::ReaderMutexLock lck(&update_mtx_);
auto it = rule_map_.find(resource);
if (it == rule_map_.end()) {
return {};
}
return it->second;
}
std::shared_ptr