init
This commit is contained in:
53
sentinel/.circleci/config.yml
Normal file
53
sentinel/.circleci/config.yml
Normal file
@@ -0,0 +1,53 @@
|
||||
version: 2
|
||||
jobs:
|
||||
integration-test:
|
||||
docker:
|
||||
- image: circleci/openjdk:8-jdk
|
||||
working_directory: ~/sentinel
|
||||
environment:
|
||||
MAVEN_OPTS: -Xmx3200m
|
||||
steps:
|
||||
- checkout
|
||||
# Run tests
|
||||
- run: mvn integration-test
|
||||
|
||||
document-lint:
|
||||
docker:
|
||||
# this image is build from Dockerfile
|
||||
# https://github.com/pouchcontainer/pouchlinter/blob/master/Dockerfile
|
||||
- image: pouchcontainer/pouchlinter:v0.1.2
|
||||
working_directory: ~/sentinel
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: use markdownlint v0.5.0 to lint markdown file (https://github.com/markdownlint/markdownlint)
|
||||
command: |
|
||||
find ./ -name "*.md" | grep -v vendor | grep -v commandline | grep -v .github | grep -v swagger | grep -v api | xargs mdl -r ~MD010,~MD013,~MD024,~MD029,~MD033,~MD036
|
||||
# - run:
|
||||
# name: use markdown-link-check(https://github.com/tcort/markdown-link-check) to check links in markdown files
|
||||
# command: |
|
||||
# set +e
|
||||
# for name in $(find . -name \*.md | grep -v vendor | grep -v CHANGELOG); do
|
||||
# if [ -f $name ]; then
|
||||
# markdown-link-check -q $name;
|
||||
# if [ $? -ne 0 ]; then
|
||||
# code=1
|
||||
# fi
|
||||
# fi
|
||||
# done
|
||||
# bash -c "exit $code";
|
||||
# - run:
|
||||
# name: use opensource tool client9/misspell to correct commonly misspelled English words
|
||||
# command: |
|
||||
# find ./* -name "*" | grep -v vendor | xargs misspell -error
|
||||
# - run:
|
||||
# name: use ShellCheck (https://github.com/koalaman/shellcheck) to check the validateness of shell scripts in pouch repo
|
||||
# command: |
|
||||
# find ./ -name "*.sh" | grep -v vendor | xargs shellcheck
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
ci:
|
||||
jobs:
|
||||
- integration-test
|
||||
- document-lint
|
||||
8
sentinel/.codecov.yml
Normal file
8
sentinel/.codecov.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
ignore:
|
||||
- "sentinel-demo/.*"
|
||||
- "sentinel-dashboard/.*"
|
||||
- "sentinel-benchmark/.*"
|
||||
- "sentinel-transport/.*"
|
||||
- "sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LongAdder.java"
|
||||
- "sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Striped64.java"
|
||||
- "sentinel-core/src/main/java/com/alibaba/csp/sentinel/eagleeye/*"
|
||||
30
sentinel/.github/ISSUE_TEMPLATE.md
vendored
Normal file
30
sentinel/.github/ISSUE_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
<!-- Here is for bug reports and feature requests ONLY!
|
||||
|
||||
If you're looking for help, please check our mail list and the Gitter room.
|
||||
|
||||
Please try to use English to describe your issue, or at least provide a snippet of English translation.
|
||||
-->
|
||||
|
||||
## 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?
|
||||
|
||||
|
||||
|
||||
19
sentinel/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
19
sentinel/.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
<!-- Thanks for submitting a pull request! Here are some tips for you:
|
||||
1. Please make sure you have read and understood the contributing guidelines: https://github.com/alibaba/Sentinel/blob/master/CONTRIBUTING.md
|
||||
2. Please make sure the PR has a corresponding issue.
|
||||
-->
|
||||
|
||||
### Describe what this PR does / why we need it
|
||||
|
||||
|
||||
### Does this pull request fix one issue?
|
||||
|
||||
<!--If that, add "Fixes #xxxx" below in the next line. For example, Fixes #15. Otherwise, add "NONE" -->
|
||||
|
||||
### Describe how you did it
|
||||
|
||||
|
||||
### Describe how to verify it
|
||||
|
||||
|
||||
### Special notes for reviews
|
||||
32
sentinel/.github/workflows/ci.yml
vendored
Normal file
32
sentinel/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
name: Sentinel CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
strategy:
|
||||
matrix:
|
||||
java: [8, 11]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: ${{ matrix.java }}
|
||||
architecture: x64
|
||||
|
||||
- name: Test with Maven
|
||||
run: mvn test
|
||||
|
||||
- name: Build with Maven
|
||||
run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -DminimumPriority=1
|
||||
|
||||
- name: Run Codecov
|
||||
run: bash <(curl -s https://codecov.io/bash)
|
||||
30
sentinel/.gitignore
vendored
Normal file
30
sentinel/.gitignore
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
# IntelliJ project files
|
||||
.idea/
|
||||
*.iml
|
||||
out
|
||||
gen
|
||||
|
||||
# Visual Studio Code
|
||||
.history/
|
||||
|
||||
# Maven
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
!/.mvn/wrapper/maven-wrapper.jar
|
||||
|
||||
# Eclipse
|
||||
.classpath
|
||||
.settings/
|
||||
.project
|
||||
bin/
|
||||
|
||||
# System related
|
||||
*.DS_Store
|
||||
Thumbs.db
|
||||
73
sentinel/CODE_OF_CONDUCT.md
Normal file
73
sentinel/CODE_OF_CONDUCT.md
Normal file
@@ -0,0 +1,73 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
education, socio-economic status, nationality, personal appearance, race,
|
||||
religion, or sexual identity and orientation.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Our Responsibilities
|
||||
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at sentinel@linux.alibaba.com. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [https://www.contributor-covenant.org/version/1/4/code-of-conduct.html](https://www.contributor-covenant.org/version/1/4/code-of-conduct.html)
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
80
sentinel/CONTRIBUTING.md
Normal file
80
sentinel/CONTRIBUTING.md
Normal file
@@ -0,0 +1,80 @@
|
||||
# Contributing to Sentinel
|
||||
|
||||
Welcome to Sentinel! This document is a guideline about how to contribute to Sentinel.
|
||||
If you find something incorrect or missing, please leave comments / suggestions.
|
||||
|
||||
## Before you get started
|
||||
|
||||
### Code of Conduct
|
||||
|
||||
Please make sure to read and observe our [Code of Conduct](./CODE_OF_CONDUCT.md).
|
||||
|
||||
### Setting up your development environment
|
||||
|
||||
You should have JDK 1.8 or later installed in your system.
|
||||
|
||||
## Contributing
|
||||
|
||||
We are always very happy to have contributions, whether for typo fix, bug fix or big new features.
|
||||
Please do not ever hesitate to ask a question or send a pull request.
|
||||
|
||||
We strongly value documentation and integration with other projects.
|
||||
We are very glad to accept improvements for these aspects.
|
||||
|
||||
### GitHub workflow
|
||||
|
||||
We use the `master` branch as the development branch, which indicates that this is a unstable branch.
|
||||
|
||||
Here are the workflow for contributors:
|
||||
|
||||
1. Fork to your own
|
||||
2. Clone fork to local repository
|
||||
3. Create a new branch and work on it
|
||||
4. Keep your branch in sync
|
||||
5. Commit your changes (make sure your commit message concise)
|
||||
6. Push your commits to your forked repository
|
||||
7. Create a pull request
|
||||
|
||||
Please follow [the pull request template](./.github/PULL_REQUEST_TEMPLATE.md).
|
||||
Please make sure the PR has a corresponding issue.
|
||||
|
||||
After creating a PR, one or more reviewers will be assigned to the pull request.
|
||||
The reviewers will review the code.
|
||||
|
||||
Before merging a PR, squash any fix review feedback, typo, merged, and rebased sorts of commits.
|
||||
The final commit message should be clear and concise.
|
||||
|
||||
### Open an issue / PR
|
||||
|
||||
We use [GitHub Issues](https://github.com/alibaba/Sentinel/issues) and [Pull Requests](https://github.com/alibaba/Sentinel/pulls) for trackers.
|
||||
|
||||
If you find a typo in document, find a bug in code, or want new features, or want to give suggestions,
|
||||
you can [open an issue on GitHub](https://github.com/alibaba/Sentinel/issues/new) to report it.
|
||||
Please follow the guideline message in the issue template.
|
||||
|
||||
If you want to contribute, please follow the [contribution workflow](#github-workflow) and create a new pull request.
|
||||
If your PR contains large changes, e.g. component refactor or new components, please write detailed documents
|
||||
about its design and usage.
|
||||
|
||||
Note that a single PR should not be too large. If heavy changes are required, it's better to separate the changes
|
||||
to a few individual PRs.
|
||||
|
||||
### Code review
|
||||
|
||||
All code should be well reviewed by one or more committers. Some principles:
|
||||
|
||||
- Readability: Important code should be well-documented. Comply with our code style.
|
||||
- Elegance: New functions, classes or components should be well designed.
|
||||
- Testability: Important code should be well-tested (high unit test coverage).
|
||||
|
||||
## Community
|
||||
|
||||
### Contact us
|
||||
|
||||
#### Mailing list
|
||||
|
||||
If you have any questions or advice, please contact sentinel@linux.alibaba.com.
|
||||
|
||||
#### Gitter
|
||||
|
||||
Our Gitter room: [https://gitter.im/alibaba/Sentinel](https://gitter.im/alibaba/Sentinel).
|
||||
201
sentinel/LICENSE
Normal file
201
sentinel/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
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.
|
||||
179
sentinel/README.md
Normal file
179
sentinel/README.md
Normal file
@@ -0,0 +1,179 @@
|
||||
<img src="https://user-images.githubusercontent.com/9434884/43697219-3cb4ef3a-9975-11e8-9a9c-73f4f537442d.png" alt="Sentinel Logo" width="50%">
|
||||
|
||||
# Sentinel: The Sentinel of Your Microservices
|
||||
|
||||
[](https://github.com/alibaba/Sentinel/actions/workflows/ci.yml)
|
||||
[](https://codecov.io/gh/alibaba/Sentinel)
|
||||
[](https://search.maven.org/search?q=g:com.alibaba.csp%20AND%20a:sentinel-core)
|
||||
[](https://www.apache.org/licenses/LICENSE-2.0.html)
|
||||
[](https://gitter.im/alibaba/Sentinel)
|
||||
|
||||
## Introduction
|
||||
|
||||
As distributed systems become increasingly popular, the reliability between services is becoming more important than ever before.
|
||||
Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**,
|
||||
**traffic shaping**, **circuit breaking** and **system adaptive protection**, to guarantee reliability and resilience for microservices.
|
||||
|
||||
Sentinel has the following features:
|
||||
|
||||
- **Rich applicable scenarios**: Sentinel has been wildly used in Alibaba, and has covered almost all the core-scenarios in Double-11 (11.11) Shopping Festivals in the past 10 years, such as “Second Kill” which needs to limit burst flow traffic to meet the system capacity, message peak clipping and valley fills, circuit breaking for unreliable downstream services, cluster flow control, etc.
|
||||
- **Real-time monitoring**: Sentinel also provides real-time monitoring ability. You can see the runtime information of a single machine in real-time, and the aggregated runtime info of a cluster with less than 500 nodes.
|
||||
- **Widespread open-source ecosystem**: Sentinel provides out-of-box integrations with commonly-used frameworks and libraries such as Spring Cloud, Dubbo and gRPC. You can easily use Sentinel by simply add the adapter dependency to your services.
|
||||
- **Polyglot support**: Sentinel has provided native support for Java, [Go](https://github.com/alibaba/sentinel-golang) and [C++](https://github.com/alibaba/sentinel-cpp).
|
||||
- **Various SPI extensions**: Sentinel provides easy-to-use SPI extension interfaces that allow you to quickly customize your logic, for example, custom rule management, adapting data sources, and so on.
|
||||
|
||||
Features overview:
|
||||
|
||||

|
||||
|
||||
## Documentation
|
||||
|
||||
See the [Sentinel](https://sentinelguard.io/) for the document website.
|
||||
|
||||
See the [中文文档](https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D) for document in Chinese.
|
||||
|
||||
See the [Wiki](https://github.com/alibaba/Sentinel/wiki) for full documentation, examples, blog posts, operational details and other information.
|
||||
|
||||
Sentinel provides integration modules for various open-source frameworks
|
||||
(e.g. Spring Cloud, Apache Dubbo, gRPC, Spring WebFlux, Reactor) and service mesh.
|
||||
You can refer to [the document](https://github.com/alibaba/Sentinel/wiki/Adapters-to-Popular-Framework) for more information.
|
||||
|
||||
If you are using Sentinel, please [**leave a comment here**](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better.
|
||||
It's also encouraged to add the link of your blog post, tutorial, demo or customized components to [**Awesome Sentinel**](./doc/awesome-sentinel.md).
|
||||
|
||||
## Ecosystem Landscape
|
||||
|
||||

|
||||
|
||||
## Quick Start
|
||||
|
||||
Below is a simple demo that guides new users to use Sentinel in just 3 steps. It also shows how to monitor this demo using the dashboard.
|
||||
|
||||
### 1. Add Dependency
|
||||
|
||||
**Note:** Sentinel requires JDK 1.8 or later.
|
||||
|
||||
If you're using Maven, just add the following dependency in `pom.xml`.
|
||||
|
||||
```xml
|
||||
<!-- replace here with the latest version -->
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
<version>1.8.3</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
If not, you can download JAR in [Maven Center Repository](https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-core).
|
||||
|
||||
### 2. Define Resource
|
||||
|
||||
Wrap your code snippet via Sentinel API: `SphU.entry(resourceName)`.
|
||||
In below example, it is `System.out.println("hello world");`:
|
||||
|
||||
```java
|
||||
try (Entry entry = SphU.entry("HelloWorld")) {
|
||||
// Your business logic here.
|
||||
System.out.println("hello world");
|
||||
} catch (BlockException e) {
|
||||
// Handle rejected request.
|
||||
e.printStackTrace();
|
||||
}
|
||||
// try-with-resources auto exit
|
||||
```
|
||||
|
||||
So far the code modification is done. We've also provided [annotation support module](https://github.com/alibaba/Sentinel/blob/master/sentinel-extension/sentinel-annotation-aspectj/README.md) to define resource easier.
|
||||
|
||||
### 3. Define Rules
|
||||
|
||||
If we want to limit the access times of the resource, we can **set rules to the resource**.
|
||||
The following code defines a rule that limits access to the resource to 20 times per second at the maximum.
|
||||
|
||||
```java
|
||||
List<FlowRule> rules = new ArrayList<>();
|
||||
FlowRule rule = new FlowRule();
|
||||
rule.setResource("HelloWorld");
|
||||
// set limit qps to 20
|
||||
rule.setCount(20);
|
||||
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||
rules.add(rule);
|
||||
FlowRuleManager.loadRules(rules);
|
||||
```
|
||||
|
||||
For more information, please refer to [How To Use](https://github.com/alibaba/Sentinel/wiki/How-to-Use).
|
||||
|
||||
### 4. Check the Result
|
||||
|
||||
After running the demo for a while, you can see the following records in `~/logs/csp/${appName}-metrics.log.{date}` (When using the default `DateFileLogHandler`).
|
||||
|
||||
```
|
||||
|--timestamp-|------date time----|-resource-|p |block|s |e|rt |occupied
|
||||
1529998904000|2018-06-26 15:41:44|HelloWorld|20|0 |20|0|0 |0
|
||||
1529998905000|2018-06-26 15:41:45|HelloWorld|20|5579 |20|0|728 |0
|
||||
1529998906000|2018-06-26 15:41:46|HelloWorld|20|15698|20|0|0 |0
|
||||
1529998907000|2018-06-26 15:41:47|HelloWorld|20|19262|20|0|0 |0
|
||||
1529998908000|2018-06-26 15:41:48|HelloWorld|20|19502|20|0|0 |0
|
||||
1529998909000|2018-06-26 15:41:49|HelloWorld|20|18386|20|0|0 |0
|
||||
|
||||
p stands for incoming request, block for blocked by rules, s for success handled by Sentinel, e for exception count, rt for average response time (ms), occupied stands for occupiedPassQps since 1.5.0 which enable us booking more than 1 shot when entering.
|
||||
```
|
||||
|
||||
This shows that the demo can print "hello world" 20 times per second.
|
||||
|
||||
More examples and information can be found in the [How To Use](https://github.com/alibaba/Sentinel/wiki/How-to-Use) section.
|
||||
|
||||
The working principles of Sentinel can be found in [How it works](https://github.com/alibaba/Sentinel/wiki/How-it-works) section.
|
||||
|
||||
Samples can be found in the [sentinel-demo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo) module.
|
||||
|
||||
### 5. Start Dashboard
|
||||
|
||||
> Note: Java 8 is required for building or running the dashboard.
|
||||
|
||||
Sentinel also provides a simple dashboard application, on which you can monitor the clients and configure the rules in real time.
|
||||
|
||||

|
||||
|
||||
For details please refer to [Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard).
|
||||
|
||||
## Trouble Shooting and Logs
|
||||
|
||||
Sentinel will generate logs for troubleshooting and real-time monitoring.
|
||||
All the information can be found in [logs](https://github.com/alibaba/Sentinel/wiki/Logs).
|
||||
|
||||
## Bugs and Feedback
|
||||
|
||||
For bug report, questions and discussions please submit [GitHub Issues](https://github.com/alibaba/sentinel/issues).
|
||||
|
||||
Contact us via [Gitter](https://gitter.im/alibaba/Sentinel) or [Email](mailto:sentinel@linux.alibaba.com).
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are always welcomed! Please refer to [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
|
||||
|
||||
You can start with the issues labeled with [`good first issue`](https://github.com/alibaba/Sentinel/issues?q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22).
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks [Guava](https://github.com/google/guava), which provides some inspiration on rate limiting.
|
||||
|
||||
And thanks for all [contributors](https://github.com/alibaba/Sentinel/graphs/contributors) of Sentinel!
|
||||
|
||||
## Who is using
|
||||
|
||||
These are only part of the companies using Sentinel, for reference only.
|
||||
If you are using Sentinel, please [add your company here](https://github.com/alibaba/Sentinel/issues/18) to tell us your scenario to make Sentinel better :)
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
3
sentinel/doc/README.md
Normal file
3
sentinel/doc/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Sentinel related documents
|
||||
|
||||
- [Awesome Sentinel](./awesome-sentinel.md)
|
||||
76
sentinel/doc/awesome-sentinel.md
Normal file
76
sentinel/doc/awesome-sentinel.md
Normal file
@@ -0,0 +1,76 @@
|
||||
# Awesome Sentinel
|
||||
|
||||
[](https://awesome.re)
|
||||
|
||||
A curated list of awesome things (e.g. samples, third-party extensions, blog posts) for [Sentinel](https://github.com/alibaba/Sentinel).
|
||||
|
||||
If you want your component to appear here, feel free to submit a pull request to this repository to add it.
|
||||
You can refer to the [awesome contribution guidelines](https://github.com/sentinel-group/sentinel-awesome/blob/master/CONTRIBUTING.md).
|
||||
|
||||
You can also add to [sentinel-group/sentinel-awesome](https://github.com/sentinel-group/sentinel-awesome).
|
||||
|
||||
## Contents
|
||||
|
||||
- [Presentations](#presentations)
|
||||
- [Tutorials](#tutorials)
|
||||
- [Demos](#demos)
|
||||
- [Extensions / Integrations](#extensions--integrations)
|
||||
- [Blog Posts](#blog-posts)
|
||||
|
||||
## Presentations
|
||||
|
||||
- Sentinel 1.6.0 网关流控新特性介绍-Eric Zhao (Dubbo Tech Day-201905-Beijing): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%201.6.0%20网关流控新特性介绍-Eric%20Zhao-DTED-201905.pdf)
|
||||
- Sentinel 微服务流控降级实践-Eric Zhao (Dubbo Tech Day-201907-Shenzhen): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%20微服务流控降级实践-Eric%20Zhao-DTED-201907.pdf)
|
||||
- Sentinel 1.7.0 新特性展望-Eric Zhao (Dubbo Tech Day-201910-Chengdu): [PDF](https://github.com/sentinel-group/sentinel-awesome/blob/master/slides/Sentinel%201.7.0%20新特性展望-Eric%20Zhao-DTED-201910.pdf)
|
||||
|
||||
## Tutorials
|
||||
|
||||
- [Sentinel Guides](https://github.com/sentinel-group/sentinel-guides)
|
||||
|
||||
## Demos
|
||||
|
||||
- [sentinel-zuul-example](https://github.com/tigerMoon/sentinel-zuul-sample): A simple project integration Sentinel to Spring Cloud Zuul which provides Service and API Path level flow control management by [tiger](https://github.com/tigerMoon)
|
||||
|
||||
## Extensions / Integrations
|
||||
|
||||
- [sentinel-support](https://github.com/cdfive/sentinel-support): A support project for convenient Sentinel integration including properties file configuration, ActiveMQ integration and a JdbcDataSource implementation by [cdfive](https://github.com/cdfive)
|
||||
- [Sentinel dashboard multi-data-source adapter](https://github.com/finefuture/sentinel-dashboard-X): Sentinel dashboard multi-data-source adapter has integrated Apollo and Nacos configuration center for bidirectional modification persistence. Implemented by [finefuture](https://github.com/finefuture)
|
||||
- [Sentinel Rule Annotation Support](https://github.com/code1986/sentinel-lib): A third-party library that supports configuring flow rule and degrade rule using annotation. Implemented by [code1986](https://github.com/code1986)
|
||||
- [sentinel-pigeon-adapter](https://github.com/wchswchs/sentinel-pigeon): A RPC framework Pigeon adapter for Sentinel including provider and invoker rate limiting implementation by [wchswchs](https://github.com/wchswchs)
|
||||
|
||||
## Blog Posts
|
||||
|
||||
- [Sentinel 为 Dubbo 服务保驾护航](https://dubbo.apache.org/zh/blog/2018/07/27/sentinel-为-dubbo-服务保驾护航) by [Eric Zhao](https://github.com/sczyh30)
|
||||
- [在生产环境中使用 Sentinel 控制台](https://github.com/alibaba/Sentinel/wiki/在生产环境中使用-Sentinel) by [Eric Zhao](https://github.com/sczyh30)
|
||||
- [Sentinel 与 Hystrix 的对比](https://sentinelguard.io/zh-cn/blog/sentinel-vs-hystrix.html) by [Eric Zhao](https://github.com/sczyh30)
|
||||
- [Guideline: 从 Hystrix 迁移到 Sentinel](https://sentinelguard.io/zh-cn/blog/guideline-migrate-from-hystrix-to-sentinel.html) by [Eric Zhao](https://github.com/sczyh30)
|
||||
- [Sentinel 控制台监控数据持久化【MySQL】](https://www.cnblogs.com/cdfive2018/p/9838577.html) by [cdfive](https://github.com/cdfive)
|
||||
- [Sentinel 控制台监控数据持久化【InfluxDB】](https://www.cnblogs.com/cdfive2018/p/9914838.html) by [cdfive](https://github.com/cdfive)
|
||||
- [Sentinel 控制台监控数据持久化【Apollo】](https://blog.csdn.net/caodegao/article/details/100009618) by [cookiejoo](https://github.com/cookiejoo)
|
||||
- [Sentinel一体化监控解决方案 CrateDB + Grafana](https://blog.csdn.net/huyong1990/article/details/82392386) by [Young Hu](https://github.com/YoungHu)
|
||||
- Sentinel 源码解析系列 by [houyi](https://github.com/all4you)
|
||||
- [Sentinel 原理-全解析](https://mp.weixin.qq.com/s/7_pCkamNv0269e5l9_Wz7w)
|
||||
- [Sentinel 原理-调用链](https://mp.weixin.qq.com/s/UEzwD22YC6jpp02foNSXnw)
|
||||
- [Sentinel 原理-滑动窗口](https://mp.weixin.qq.com/s/B1_7Kb_CxeKEAv43kdCWOA)
|
||||
- [Sentinel 实战-限流](https://mp.weixin.qq.com/s/rjyU37Dm-sxNln7GUD8tOw)
|
||||
- [Sentinel 实战-控制台](https://mp.weixin.qq.com/s/23EDFHMXLwsDqw-4O5dR5A)
|
||||
- [Sentinel 实战-规则持久化](https://mp.weixin.qq.com/s/twMFiBfRawKLR-1-N-f1yw)
|
||||
- Sentinel 学习笔记 by [ro9er](https://github.com/ro9er)
|
||||
- [Sentinel 学习笔记(1)-- 流量统计代码解析](https://www.jianshu.com/p/7936d7a57924)
|
||||
- [Sentinel 学习笔记(2)-- 流量控制代码分析](https://www.jianshu.com/p/938709e94e43)
|
||||
- [Sentinel 学习笔记(3)-- 上下文统计Node建立分析](https://www.jianshu.com/p/cfdf525248c1)
|
||||
- [大流量下的服务质量治理 Dubbo Sentinel 初涉](https://mp.weixin.qq.com/s/ergr_siI07VwwSRPFgsLvQ) by [RyuGrade](https://github.com/RyuGrade)
|
||||
- Sentinel 深入浅出系列 by [shxz130](https://github.com/shxz130)
|
||||
- [Sentinel 深入浅出之原理篇 SlotChain](https://www.jianshu.com/p/a7a405de3a12)
|
||||
- [Sentinel 深入浅出之原理篇 Context初始化 & Entry初始化](https://www.jianshu.com/p/e39ac47cd893)
|
||||
- [Sentinel 深入浅出之原理篇 NodeSelectorSlot](https://www.jianshu.com/p/9a380ba188ab)
|
||||
- [Sentinel 深入浅出之原理篇 ClusterBuilderSlot](https://www.jianshu.com/p/0b0b5d8888a2)
|
||||
- [Sentinel 深入浅出之原理篇 StatisticSlot&滑动窗口](https://www.jianshu.com/p/9620298fd15a)
|
||||
- [Sentinel 深入浅出之原理篇 SystemSlot](https://www.jianshu.com/p/bfad1b7d0cde)
|
||||
- [Sentinel 深入浅出之原理篇 AuthoritySlot](https://www.jianshu.com/p/c5312c2242b3)
|
||||
- [Sentinel 深入浅出之原理篇 FlowSlot](https://www.jianshu.com/p/53218d0d273e)
|
||||
- [Sentinel 深入浅出之原理篇 DegradeSlot](https://www.jianshu.com/p/e910d4840e4a)
|
||||
- [Alibaba Sentinel RESTful 接口流控处理优化](https://www.jianshu.com/p/96f5980d9798) by [luanlouis](https://github.com/luanlouis)
|
||||
- [Sentinel 控制台前端开发环境搭建](https://www.cnblogs.com/cdfive2018/p/11084001.html) by [cdfive](https://github.com/cdfive)
|
||||
- [阿里 Sentinel 源码解析](https://www.javadoop.com/post/sentinel) by [Javadoop](https://www.javadoop.com)
|
||||
- [Introduction to Alibaba Sentinel](https://www.baeldung.com/java-sentinel-intro) by [Amit Bhave](https://github.com/Amitbhave)
|
||||
BIN
sentinel/doc/image/sentinel-features-overview-en.png
Normal file
BIN
sentinel/doc/image/sentinel-features-overview-en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 134 KiB |
BIN
sentinel/doc/image/sentinel-opensource-eco-landscape-en.png
Normal file
BIN
sentinel/doc/image/sentinel-opensource-eco-landscape-en.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 902 KiB |
BIN
sentinel/doc/image/slots.gif
Normal file
BIN
sentinel/doc/image/slots.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 154 KiB |
399
sentinel/pom.xml
Normal file
399
sentinel/pom.xml
Normal file
@@ -0,0 +1,399 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parent</artifactId>
|
||||
<version>1.8.3</version>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The parent project of Sentinel</description>
|
||||
<url>https://github.com/alibaba/Sentinel</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>Apache License, Version 2.0</name>
|
||||
<url>http://www.apache.org/licenses/LICENSE-2.0</url>
|
||||
<distribution>repo</distribution>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<url>https://github.com/alibaba/Sentinel</url>
|
||||
<connection>scm:git:https://github.com/alibaba/Sentinel.git</connection>
|
||||
<developerConnection>scm:git:https://github.com/alibaba/Sentinel.git</developerConnection>
|
||||
</scm>
|
||||
<developers>
|
||||
<developer>
|
||||
<name>The Sentinel Project Contributors</name>
|
||||
<email>sentinel-dev@linux.alibaba.com</email>
|
||||
<url>https://github.com/alibaba/Sentinel</url>
|
||||
</developer>
|
||||
</developers>
|
||||
<organization>
|
||||
<name>Alibaba Group</name>
|
||||
<url>https://github.com/alibaba</url>
|
||||
</organization>
|
||||
<issueManagement>
|
||||
<system>github</system>
|
||||
<url>https://github.com/alibaba/Sentinel/issues</url>
|
||||
</issueManagement>
|
||||
|
||||
<properties>
|
||||
<!-- Compile libs -->
|
||||
<fastjson.version>1.2.75</fastjson.version>
|
||||
<javax.annotation-api.version>1.3.2</javax.annotation-api.version>
|
||||
|
||||
<!-- Test libs -->
|
||||
<junit.version>4.12</junit.version>
|
||||
<mockito.version>2.28.2</mockito.version>
|
||||
<assertj.version>3.12.1</assertj.version>
|
||||
<awaitility.version>3.1.5</awaitility.version>
|
||||
<powermock.version>2.0.0</powermock.version>
|
||||
|
||||
<!-- Build -->
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.source.version>1.8</java.source.version>
|
||||
<java.target.version>1.8</java.target.version>
|
||||
<java.encoding>UTF-8</java.encoding>
|
||||
<maven.compiler.version>3.8.0</maven.compiler.version>
|
||||
<maven.surefire.version>2.22.1</maven.surefire.version>
|
||||
<maven.source.version>3.0.1</maven.source.version>
|
||||
<maven.javadoc.version>3.0.1</maven.javadoc.version>
|
||||
<maven.deploy.version>2.8.2</maven.deploy.version>
|
||||
<maven.gpg.version>1.6</maven.gpg.version>
|
||||
<maven.jacoco.version>0.8.3</maven.jacoco.version>
|
||||
<maven.jar.version>3.1.0</maven.jar.version>
|
||||
<maven.pmd.version>3.8</maven.pmd.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
<module>sentinel-core</module>
|
||||
<module>sentinel-extension</module>
|
||||
<module>sentinel-transport</module>
|
||||
<module>sentinel-adapter</module>
|
||||
<module>sentinel-cluster</module>
|
||||
<module>sentinel-logging</module>
|
||||
|
||||
<module>sentinel-dashboard</module>
|
||||
|
||||
<module>sentinel-demo</module>
|
||||
<module>sentinel-benchmark</module>
|
||||
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-annotation-aspectj</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-annotation-cdi-interceptor</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-nacos</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-zookeeper</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-apollo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-datasource-etcd</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-simple-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-netty-http</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-spring-mvc</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-cluster-common-default</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-metric-exporter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<version>${fastjson.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<version>${assertj.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.awaitility</groupId>
|
||||
<artifactId>awaitility</artifactId>
|
||||
<version>${awaitility.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hamcrest</groupId>
|
||||
<artifactId>java-hamcrest</artifactId>
|
||||
<version>2.0.0.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-module-junit4</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.powermock</groupId>
|
||||
<artifactId>powermock-api-mockito2</artifactId>
|
||||
<version>${powermock.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-pmd-plugin</artifactId>
|
||||
<version>${maven.pmd.version}</version>
|
||||
<configuration>
|
||||
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
|
||||
<minimumPriority>1</minimumPriority>
|
||||
<excludeRoots>
|
||||
<excludeRoot>com/alibaba/csp/sentinel/benchmark</excludeRoot>
|
||||
</excludeRoots>
|
||||
<excludes>
|
||||
<exclude>**/*_jmhTest.java</exclude>
|
||||
</excludes>
|
||||
<printFailingErrors>true</printFailingErrors>
|
||||
<rulesets>
|
||||
<ruleset>rulesets/java/ali-comment.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-concurrent.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-constant.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-exception.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-flowcontrol.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-naming.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-oop.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-orm.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-other.xml</ruleset>
|
||||
<ruleset>rulesets/java/ali-set.xml</ruleset>
|
||||
</rulesets>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.p3c</groupId>
|
||||
<artifactId>p3c-pmd</artifactId>
|
||||
<version>1.3.6</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>${maven.compiler.version}</version>
|
||||
<configuration>
|
||||
<source>${java.source.version}</source>
|
||||
<target>${java.target.version}</target>
|
||||
<encoding>${java.encoding}</encoding>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<!-- CircleCI build workaround -->
|
||||
<argLine>@{argLine} -Xms1024m -Xmx2048m</argLine>
|
||||
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||
<useSystemClassLoader>false</useSystemClassLoader>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
<version>${maven.jacoco.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>prepare-agent</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>report</id>
|
||||
<phase>test</phase>
|
||||
<goals>
|
||||
<goal>report</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<version>${maven.surefire.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${maven.jar.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven.gpg.version}</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>oss</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<!-- Source -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
<version>${maven.source.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar-no-fork</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- Javadoc -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>${maven.javadoc.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>jar</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<locale>en_US</locale>
|
||||
<encoding>UTF-8</encoding>
|
||||
<charset>UTF-8</charset>
|
||||
<doclint>none</doclint>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<!-- GPG -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
<version>${maven.gpg.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>sign</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
|
||||
</snapshotRepository>
|
||||
<repository>
|
||||
<id>oss</id>
|
||||
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
79
sentinel/sentinel-adapter/pom.xml
Normal file
79
sentinel/sentinel-adapter/pom.xml
Normal file
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parent</artifactId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>sentinel-adapter</name>
|
||||
<description>The adapters of Sentinel</description>
|
||||
|
||||
<modules>
|
||||
<module>sentinel-web-servlet</module>
|
||||
<module>sentinel-dubbo-adapter</module>
|
||||
<module>sentinel-apache-dubbo-adapter</module>
|
||||
<module>sentinel-apache-httpclient-adapter</module>
|
||||
<module>sentinel-sofa-rpc-adapter</module>
|
||||
<module>sentinel-grpc-adapter</module>
|
||||
<module>sentinel-zuul-adapter</module>
|
||||
<module>sentinel-reactor-adapter</module>
|
||||
<module>sentinel-spring-webflux-adapter</module>
|
||||
<module>sentinel-api-gateway-adapter-common</module>
|
||||
<module>sentinel-spring-cloud-gateway-adapter</module>
|
||||
<module>sentinel-spring-webmvc-adapter</module>
|
||||
<module>sentinel-zuul2-adapter</module>
|
||||
<module>sentinel-okhttp-adapter</module>
|
||||
<module>sentinel-jax-rs-adapter</module>
|
||||
<module>sentinel-quarkus-adapter</module>
|
||||
<module>sentinel-motan-adapter</module>
|
||||
</modules>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-extension</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-web-servlet</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-reactor-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
@@ -0,0 +1,72 @@
|
||||
# Sentinel Apache Dubbo Adapter (for 2.7.x+)
|
||||
|
||||
> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
|
||||
|
||||
Sentinel Dubbo Adapter provides service consumer filter and provider filter
|
||||
for [Apache Dubbo](https://dubbo.apache.org/en-us/) services.
|
||||
|
||||
**Note: This adapter only supports Apache Dubbo 2.7.x and above.** For legacy `com.alibaba:dubbo` 2.6.x,
|
||||
please use `sentinel-dubbo-adapter` module instead.
|
||||
|
||||
To use Sentinel Dubbo Adapter, you can simply add the following dependency to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
The Sentinel filters are **enabled by default**. Once you add the dependency,
|
||||
the Dubbo services and methods will become protected resources in Sentinel,
|
||||
which can leverage Sentinel's flow control and guard ability when rules are configured.
|
||||
Demos can be found in [sentinel-demo-apache-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-apache-dubbo).
|
||||
|
||||
If you don't want the filters enabled, you can manually disable them. For example:
|
||||
|
||||
```xml
|
||||
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
|
||||
<dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
```
|
||||
|
||||
For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/dev/impls/filter.html).
|
||||
|
||||
## Dubbo resources
|
||||
|
||||
The resource for Dubbo services has two granularities: service interface and service method.
|
||||
|
||||
- Service interface: resourceName format is `interfaceName`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
|
||||
- Service method: resourceName format is `interfaceName:methodSignature`, e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
|
||||
|
||||
## Flow control based on caller
|
||||
|
||||
In many circumstances, it's also significant to control traffic flow based on the **caller**.
|
||||
For example, assuming that there are two services A and B, both of them initiate remote call requests to the service provider.
|
||||
If we want to limit the calls from service B only, we can set the `limitApp` of flow rule as the identifier of service B (e.g. service name).
|
||||
|
||||
Sentinel Dubbo Adapter will automatically resolve the Dubbo consumer's *application name* as the caller's name (`origin`),
|
||||
and will bring the caller's name when doing resource protection.
|
||||
If `limitApp` of flow rules is not configured (`default`), flow control will take effects on all callers.
|
||||
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
|
||||
|
||||
> Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
|
||||
> so developers should manually put the application name into *attachment* at consumer side,
|
||||
> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
|
||||
> where consumer can carry application name information to provider automatically.
|
||||
> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
|
||||
> developers can manually put the application name into attachment with the key `dubboApplication`.
|
||||
>
|
||||
> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
|
||||
> implementation to `DubboAdapterGlobalConfig`.
|
||||
|
||||
## Global fallback
|
||||
|
||||
Sentinel Dubbo Adapter supports global fallback configuration.
|
||||
The global fallback will handle exceptions and give replacement result when blocked by
|
||||
flow control, degrade or system load protection. You can implement your own `DubboFallback` interface
|
||||
and then register to `DubboAdapterGlobalConfig`.
|
||||
If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
|
||||
|
||||
Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-apache-dubbo-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<java.source.version>1.8</java.source.version>
|
||||
<java.target.version>1.8</java.target.version>
|
||||
<apache.dubbo.version>2.7.13</apache.dubbo.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
<version>${apache.dubbo.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Base class of the {@link SentinelDubboProviderFilter} and {@link SentinelDubboConsumerFilter}.
|
||||
*
|
||||
* @author Zechao Zheng
|
||||
*/
|
||||
public abstract class BaseSentinelDubboFilter implements Filter {
|
||||
|
||||
|
||||
/**
|
||||
* Get method name of dubbo rpc
|
||||
*
|
||||
* @param invoker
|
||||
* @param invocation
|
||||
* @return
|
||||
*/
|
||||
abstract String getMethodName(Invoker invoker, Invocation invocation, String prefix);
|
||||
|
||||
/**
|
||||
* Get interface name of dubbo rpc
|
||||
*
|
||||
* @param invoker
|
||||
* @return
|
||||
*/
|
||||
abstract String getInterfaceName(Invoker invoker, String prefix);
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.Filter;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
|
||||
/**
|
||||
* Puts current consumer's application name in the attachment of each invocation.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class DubboAppContextFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
String application = invoker.getUrl().getParameter(CommonConstants.APPLICATION_KEY);
|
||||
if (application != null) {
|
||||
RpcContext.getContext().setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, application);
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public final class DubboUtils {
|
||||
|
||||
public static final String SENTINEL_DUBBO_APPLICATION_KEY = "dubboApplication";
|
||||
|
||||
public static String getApplication(Invocation invocation, String defaultValue) {
|
||||
if (invocation == null || invocation.getAttachments() == null) {
|
||||
throw new IllegalArgumentException("Bad invocation instance");
|
||||
}
|
||||
return invocation.getAttachment(SENTINEL_DUBBO_APPLICATION_KEY, defaultValue);
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation){
|
||||
return getMethodResourceName(invoker, invocation, false);
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, Boolean useGroupAndVersion) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
String interfaceResource = useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
|
||||
buf.append(interfaceResource)
|
||||
.append(":")
|
||||
.append(invocation.getMethodName())
|
||||
.append("(");
|
||||
boolean isFirst = true;
|
||||
for (Class<?> clazz : invocation.getParameterTypes()) {
|
||||
if (!isFirst) {
|
||||
buf.append(",");
|
||||
}
|
||||
buf.append(clazz.getName());
|
||||
isFirst = false;
|
||||
}
|
||||
buf.append(")");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
public static String getMethodResourceName(Invoker<?> invoker, Invocation invocation, String prefix) {
|
||||
if (StringUtil.isNotBlank(prefix)) {
|
||||
return new StringBuilder(64)
|
||||
.append(prefix)
|
||||
.append(getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
|
||||
.toString();
|
||||
} else {
|
||||
return getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getInterfaceName(Invoker invoker) {
|
||||
return getInterfaceName(invoker, false);
|
||||
}
|
||||
|
||||
public static String getInterfaceName(Invoker<?> invoker, Boolean useGroupAndVersion) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
return useGroupAndVersion ? invoker.getUrl().getColonSeparatedKey() : invoker.getInterface().getName();
|
||||
}
|
||||
|
||||
public static String getInterfaceName(Invoker<?> invoker, String prefix) {
|
||||
if (StringUtil.isNotBlank(prefix)) {
|
||||
return new StringBuilder(64)
|
||||
.append(prefix)
|
||||
.append(getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled()))
|
||||
.toString();
|
||||
} else {
|
||||
return getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboInterfaceGroupAndVersionEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private DubboUtils() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.*;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.*;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
|
||||
|
||||
/**
|
||||
* <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
|
||||
* <p>
|
||||
* If you want to disable the consumer filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
* @author Lin Liang
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class SentinelDubboConsumerFilter extends BaseSentinelDubboFilter {
|
||||
|
||||
public SentinelDubboConsumerFilter() {
|
||||
RecordLog.info("Sentinel Apache Dubbo consumer filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
|
||||
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getInterfaceName(Invoker invoker, String prefix) {
|
||||
return DubboUtils.getInterfaceName(invoker, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
|
||||
if (InvokeMode.SYNC == invokeMode) {
|
||||
return syncInvoke(invoker, invocation);
|
||||
} else {
|
||||
return asyncInvoke(invoker, invocation);
|
||||
}
|
||||
}
|
||||
|
||||
private Result syncInvoke(Invoker<?> invoker, Invocation invocation) {
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT,
|
||||
invocation.getArguments());
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Tracer.traceEntry(result.getException(), interfaceEntry);
|
||||
Tracer.traceEntry(result.getException(), methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Result asyncInvoke(Invoker<?> invoker, Invocation invocation) {
|
||||
LinkedList<EntryHolder> queue = new LinkedList<>();
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
queue.push(new EntryHolder(
|
||||
SphU.asyncEntry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT), null));
|
||||
queue.push(new EntryHolder(
|
||||
SphU.asyncEntry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
|
||||
EntryType.OUT, 1, invocation.getArguments()), invocation.getArguments()));
|
||||
Result result = invoker.invoke(invocation);
|
||||
result.whenCompleteWithContext((r, throwable) -> {
|
||||
Throwable error = throwable;
|
||||
if (error == null) {
|
||||
error = Optional.ofNullable(r).map(Result::getException).orElse(null);
|
||||
}
|
||||
while (!queue.isEmpty()) {
|
||||
EntryHolder holder = queue.pop();
|
||||
Tracer.traceEntry(error, holder.entry);
|
||||
exitEntry(holder);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
while (!queue.isEmpty()) {
|
||||
exitEntry(queue.pop());
|
||||
}
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
|
||||
}
|
||||
}
|
||||
|
||||
static class EntryHolder {
|
||||
|
||||
final private Entry entry;
|
||||
final private Object[] params;
|
||||
|
||||
public EntryHolder(Entry entry, Object[] params) {
|
||||
this.entry = entry;
|
||||
this.params = params;
|
||||
}
|
||||
}
|
||||
|
||||
private void exitEntry(EntryHolder holder) {
|
||||
if (holder.params != null) {
|
||||
holder.entry.exit(1, holder.params);
|
||||
} else {
|
||||
holder.entry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.*;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.common.extension.Activate;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.apache.dubbo.rpc.RpcException;
|
||||
|
||||
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
|
||||
|
||||
/**
|
||||
* <p>Apache Dubbo service provider filter that enables integration with Sentinel. Auto activated by default.</p>
|
||||
* <p>Note: this only works for Apache Dubbo 2.7.x or above version.</p>
|
||||
* <p>
|
||||
* If you want to disable the provider filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author Carpenter Lee
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = PROVIDER)
|
||||
public class SentinelDubboProviderFilter extends BaseSentinelDubboFilter {
|
||||
|
||||
public SentinelDubboProviderFilter() {
|
||||
RecordLog.info("Sentinel Apache Dubbo provider filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
String getMethodName(Invoker invoker, Invocation invocation, String prefix) {
|
||||
return DubboUtils.getMethodResourceName(invoker, invocation, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
String getInterfaceName(Invoker invoker, String prefix) {
|
||||
return DubboUtils.getInterfaceName(invoker, prefix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
// Get origin caller.
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
|
||||
if (null == origin) {
|
||||
origin = "";
|
||||
}
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodName(invoker, invocation, prefix);
|
||||
try {
|
||||
// Only need to create entrance context at provider side, as context will take effect
|
||||
// at entrance of invocation chain only (for inbound traffic).
|
||||
ContextUtil.enter(methodResourceName, origin);
|
||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN,
|
||||
invocation.getArguments());
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Tracer.traceEntry(result.getException(), interfaceEntry);
|
||||
Tracer.traceEntry(result.getException(), methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DefaultDubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Responsible for dubbo service provider, consumer attribute configuration
|
||||
* </p>
|
||||
*
|
||||
* @author lianglin
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public final class DubboAdapterGlobalConfig {
|
||||
|
||||
private static final String TRUE_STR = "true";
|
||||
|
||||
public static final String DUBBO_RES_NAME_WITH_PREFIX_KEY = "csp.sentinel.dubbo.resource.use.prefix";
|
||||
public static final String DUBBO_PROVIDER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.provider.prefix";
|
||||
public static final String DUBBO_CONSUMER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.consumer.prefix";
|
||||
|
||||
private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
|
||||
private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
|
||||
|
||||
public static final String DUBBO_INTERFACE_GROUP_VERSION_ENABLED = "csp.sentinel.dubbo.interface.group.version.enabled";
|
||||
|
||||
private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser();
|
||||
|
||||
public static boolean isUsePrefix() {
|
||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY));
|
||||
}
|
||||
|
||||
public static String getDubboProviderResNamePrefixKey() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_PROVIDER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDubboConsumerResNamePrefixKey() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_CONSUMER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Boolean getDubboInterfaceGroupAndVersionEnabled() {
|
||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED));
|
||||
}
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return consumerFallback;
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.consumerFallback = consumerFallback;
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return providerFallback;
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.providerFallback = providerFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @return the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static DubboOriginParser getOriginParser() {
|
||||
return originParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @param originParser the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void setOriginParser(DubboOriginParser originParser) {
|
||||
AssertUtil.notNull(originParser, "originParser cannot be null");
|
||||
DubboAdapterGlobalConfig.originParser = originParser;
|
||||
}
|
||||
|
||||
private DubboAdapterGlobalConfig() {}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class DefaultDubboFallback implements DubboFallback {
|
||||
|
||||
@Override
|
||||
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
|
||||
// Just wrap the exception.
|
||||
return AsyncRpcResult.newDefaultAsyncResult(ex.toRuntimeException(), invocation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
|
||||
/**
|
||||
* Fallback handler for Dubbo services.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface DubboFallback {
|
||||
|
||||
/**
|
||||
* Handle the block exception and provide fallback result.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @param ex block exception
|
||||
* @return fallback result
|
||||
*/
|
||||
Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
|
||||
/**
|
||||
* <p>Global fallback registry for Dubbo.</p>
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @deprecated use {@link DubboAdapterGlobalConfig} instead since 1.8.0.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class DubboFallbackRegistry {
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback();
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback();
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
|
||||
}
|
||||
|
||||
private DubboFallbackRegistry() {}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Default Dubbo origin parser.
|
||||
*
|
||||
* @author jingzian
|
||||
*/
|
||||
public class DefaultDubboOriginParser implements DubboOriginParser {
|
||||
|
||||
@Override
|
||||
public String parse(Invoker<?> invoker, Invocation invocation) {
|
||||
return DubboUtils.getApplication(invocation, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Customized origin parser for Dubbo provider filter.{@link Context#getOrigin()}
|
||||
*
|
||||
* @author jingzian
|
||||
*/
|
||||
public interface DubboOriginParser {
|
||||
|
||||
/**
|
||||
* Parses the origin (caller) from Dubbo invocation.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @return the parsed origin
|
||||
*/
|
||||
String parse(Invoker<?> invoker, Invocation invocation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
sentinel.dubbo.provider.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
|
||||
sentinel.dubbo.consumer.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
|
||||
dubbo.application.context.name.filter=com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Base test class, provide common methods for subClass
|
||||
* The package is same as CtSph, to call CtSph.resetChainMap() method for test
|
||||
* <p>
|
||||
* Note: Only for test. DO NOT USE IN PRODUCTION!
|
||||
*
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class BaseTest {
|
||||
|
||||
|
||||
/**
|
||||
* Clean up resources for context, clusterNodeMap, processorSlotChainMap
|
||||
*/
|
||||
public void cleanUpAll() {
|
||||
try {
|
||||
clearDubboContext();
|
||||
cleanUpCstContext();
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpCstContext() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
CtSph.resetChainMap();
|
||||
Method method = ContextUtil.class.getDeclaredMethod("resetContextMap");
|
||||
method.setAccessible(true);
|
||||
method.invoke(null, null);
|
||||
ContextUtil.exit();
|
||||
FlowRuleManager.loadRules(new ArrayList<>());
|
||||
DegradeRuleManager.loadRules(new ArrayList<>());
|
||||
}
|
||||
|
||||
private void clearDubboContext() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
RpcContext.removeContext();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author lianglin
|
||||
*/
|
||||
public class DubboTestUtil {
|
||||
|
||||
|
||||
public static Class<?> DEFAULT_TEST_SERVICE = DemoService.class;
|
||||
public static Method DEFAULT_TEST_METHOD_ONE = DEFAULT_TEST_SERVICE.getMethods()[0];
|
||||
public static Method DEFAULT_TEST_METHOD_TWO = DEFAULT_TEST_SERVICE.getMethods()[1];
|
||||
|
||||
public static Invoker getMockInvoker(URL url, Class<?> cls) {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(cls);
|
||||
return invoker;
|
||||
}
|
||||
|
||||
public static Invoker getDefaultMockInvoker() {
|
||||
return getMockInvoker(getDefaultTestURL(), DEFAULT_TEST_SERVICE);
|
||||
}
|
||||
|
||||
public static Invocation getMockInvocation(Method method) {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static Invocation getDefaultMockInvocationOne() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_ONE.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_ONE.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static Invocation getDefaultMockInvocationTwo() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getMethodName()).thenReturn(DEFAULT_TEST_METHOD_TWO.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(DEFAULT_TEST_METHOD_TWO.getParameterTypes());
|
||||
return invocation;
|
||||
}
|
||||
|
||||
public static URL getDefaultTestURL() {
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DEFAULT_TEST_SERVICE.getName());
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public class DubboAppContextFilterTest extends BaseTest {
|
||||
|
||||
private DubboAppContextFilter filter = new DubboAppContextFilter();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeApplicationKey() {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
URL url = URL.valueOf("test://test:111/test?application=serviceA");
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
|
||||
assertEquals("serviceA", application);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeNullApplicationKey() {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
URL url = URL.valueOf("test://test:111/test?application=");
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
String application = RpcContext.getContext().getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY);
|
||||
assertEquals(application, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig.DUBBO_INTERFACE_GROUP_VERSION_ENABLED;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
*/
|
||||
public class DubboUtilsTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "true");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
SentinelConfig.setConfig("csp.sentinel.dubbo.resource.use.prefix", "false");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "");
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetApplication() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getAttachments()).thenReturn(new HashMap<>());
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn("consumerA");
|
||||
|
||||
String application = DubboUtils.getApplication(invocation, "");
|
||||
verify(invocation).getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "");
|
||||
|
||||
assertEquals("consumerA", application);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetApplicationNoAttachments() {
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
when(invocation.getAttachments()).thenReturn(null);
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn("consumerA");
|
||||
|
||||
DubboUtils.getApplication(invocation, "");
|
||||
|
||||
fail("No attachments in invocation, IllegalArgumentException should be thrown!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceName() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
String resourceName = DubboUtils.getMethodResourceName(invoker, invocation);
|
||||
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetResourceNameWithGroupAndVersion() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
String resourceNameUseGroupAndVersion = DubboUtils.getMethodResourceName(invoker, invocation, true);
|
||||
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1:sayHello(java.lang.String,int)", resourceNameUseGroupAndVersion);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testGetResourceNameWithPrefix() throws NoSuchMethodException {
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
Invocation invocation = mock(Invocation.class);
|
||||
Method method = DemoService.class.getDeclaredMethod("sayHello", String.class, int.class);
|
||||
when(invocation.getMethodName()).thenReturn(method.getName());
|
||||
when(invocation.getParameterTypes()).thenReturn(method.getParameterTypes());
|
||||
|
||||
//test with default prefix
|
||||
String resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
|
||||
//test with custom prefix
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
resourceName = DubboUtils.getMethodResourceName(invoker, invocation, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:sayHello(java.lang.String,int)", resourceName);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceName() {
|
||||
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "false");
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceNameWithGroupAndVersion() throws NoSuchMethodException {
|
||||
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
SentinelConfig.setConfig(DUBBO_INTERFACE_GROUP_VERSION_ENABLED, "true");
|
||||
assertEquals("com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService:1.0.0:grp1", DubboUtils.getInterfaceName(invoker, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInterfaceNameWithPrefix() throws NoSuchMethodException {
|
||||
URL url = URL.valueOf("dubbo://127.0.0.1:2181")
|
||||
.addParameter(CommonConstants.VERSION_KEY, "1.0.0")
|
||||
.addParameter(CommonConstants.GROUP_KEY, "grp1")
|
||||
.addParameter(CommonConstants.INTERFACE_KEY, DemoService.class.getName());
|
||||
Invoker invoker = mock(Invoker.class);
|
||||
when(invoker.getUrl()).thenReturn(url);
|
||||
when(invoker.getInterface()).thenReturn(DemoService.class);
|
||||
|
||||
|
||||
//test with default prefix
|
||||
String resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
|
||||
|
||||
//test with custom prefix
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_PROVIDER_RES_NAME_PREFIX_KEY, "my:dubbo:provider:");
|
||||
SentinelConfig.setConfig(DubboAdapterGlobalConfig.DUBBO_CONSUMER_RES_NAME_PREFIX_KEY, "my:dubbo:consumer:");
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboProviderResNamePrefixKey());
|
||||
assertEquals("my:dubbo:provider:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
resourceName = DubboUtils.getInterfaceName(invoker, DubboAdapterGlobalConfig.getDubboConsumerResNamePrefixKey());
|
||||
assertEquals("my:dubbo:consumer:com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService", resourceName);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,394 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
import com.alibaba.csp.sentinel.DubboTestUtil;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallbackRegistry;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.node.Node;
|
||||
import com.alibaba.csp.sentinel.node.StatisticNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
|
||||
import org.apache.dubbo.rpc.*;
|
||||
import org.apache.dubbo.rpc.support.RpcUtils;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO;
|
||||
import static org.apache.dubbo.rpc.Constants.ASYNC_KEY;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class SentinelDubboConsumerFilterTest extends BaseTest {
|
||||
|
||||
private final SentinelDubboConsumerFilter consumerFilter = new SentinelDubboConsumerFilter();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
initFallback();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterfaceLevelFollowControlAsync() throws InterruptedException {
|
||||
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initFlowRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result1 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result1.getValue());
|
||||
|
||||
// should fallback because the qps > 1
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset qps
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDegradeAsync() throws InterruptedException {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initDegradeRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result = invokeDubboRpc(false, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
assertEquals("normal", result.getValue());
|
||||
|
||||
// inc the clusterNode's exception to trigger the fallback
|
||||
for (int i = 0; i < 5; i++) {
|
||||
invokeDubboRpc(true, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset exception
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDegradeSync() throws InterruptedException {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
initDegradeRule(DubboUtils.getInterfaceName(invoker));
|
||||
|
||||
Result result = invokeDubboRpc(false, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
assertEquals("normal", result.getValue());
|
||||
|
||||
// inc the clusterNode's exception to trigger the fallback
|
||||
for (int i = 0; i < 5; i++) {
|
||||
invokeDubboRpc(true, invoker, invocation);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
}
|
||||
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", result2.getValue());
|
||||
|
||||
// sleeping 1000 ms to reset exception
|
||||
Thread.sleep(1000);
|
||||
Result result3 = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("normal", result3.getValue());
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMethodFlowControlAsync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
initFlowRule(consumerFilter.getMethodName(invoker, invocation, null));
|
||||
invokeDubboRpc(false, invoker, invocation);
|
||||
invokeDubboRpc(false, invoker, invocation);
|
||||
|
||||
Invocation invocation2 = DubboTestUtil.getDefaultMockInvocationTwo();
|
||||
Result result2 = invokeDubboRpc(false, invoker, invocation2);
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation2);
|
||||
assertEquals("normal", result2.getValue());
|
||||
|
||||
// the method of invocation should be blocked
|
||||
Result fallback = invokeDubboRpc(false, invoker, invocation);
|
||||
assertEquals("fallback", fallback.getValue());
|
||||
verifyInvocationStructureForCallFinish(invoker, invocation);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeAsync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
when(invocation.getAttachment(ASYNC_KEY)).thenReturn(Boolean.TRUE.toString());
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructureForAsyncCall(invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
consumerFilter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvokeSync() {
|
||||
|
||||
Invocation invocation = DubboTestUtil.getDefaultMockInvocationOne();
|
||||
Invoker invoker = DubboTestUtil.getDefaultMockInvoker();
|
||||
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(result.getException()).thenReturn(new Exception());
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructure(invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
|
||||
consumerFilter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply verify invocation structure in memory:
|
||||
* EntranceNode(defaultContextName)
|
||||
* --InterfaceNode(interfaceName)
|
||||
* ----MethodNode(resourceName)
|
||||
*/
|
||||
private void verifyInvocationStructure(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
|
||||
// In actual project, a consumer is usually also a provider, the context will be created by
|
||||
//SentinelDubboProviderFilter
|
||||
// If consumer is on the top of Dubbo RPC invocation chain, use default context
|
||||
String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
|
||||
assertEquals("", context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.OUT);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
|
||||
assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
|
||||
assertSame(EntryType.OUT, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.OUT);
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode methodNode = getNode(resourceName, entranceNode);
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(resourceName, methodResource.getName());
|
||||
assertSame(EntryType.OUT, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertSame(methodNode, curEntry.getCurNode());
|
||||
assertSame(interfaceNode, curEntry.getLastNode());
|
||||
assertNull(curEntry.getOriginNode());// As context origin is not "", no originNode should be created in curEntry
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode,
|
||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(0, methodOriginCountMap.size());
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(0, interfaceOriginCountMap.size());
|
||||
}
|
||||
|
||||
private void verifyInvocationStructureForAsyncCall(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
|
||||
// As not call ContextUtil.enter(resourceName, application) in SentinelDubboConsumerFilter, use default context
|
||||
// In actual project, a consumer is usually also a provider, the context will be created by
|
||||
//SentinelDubboProviderFilter
|
||||
// If consumer is on the top of Dubbo RPC invocation chain, use default context
|
||||
String resourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, context.getName());
|
||||
assertEquals("", context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
assertEquals(com.alibaba.csp.sentinel.Constants.CONTEXT_DEFAULT_NAME, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.OUT);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(2, childList.size());
|
||||
DefaultNode interfaceNode = getNode(DubboUtils.getInterfaceName(invoker), entranceNode);
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
assertEquals(DubboUtils.getInterfaceName(invoker), interfaceResource.getName());
|
||||
assertSame(EntryType.OUT, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.OUT);
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(0, childList.size());
|
||||
DefaultNode methodNode = getNode(resourceName, entranceNode);
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(resourceName, methodResource.getName());
|
||||
assertSame(EntryType.OUT, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
// nothing will bind to local context when use the AsyncEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertNull(curEntry);
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode,
|
||||
interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is "", the StatisticNode should not be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(0, methodOriginCountMap.size());
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(0, interfaceOriginCountMap.size());
|
||||
}
|
||||
|
||||
private void verifyInvocationStructureForCallFinish(Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
String methodResourceName = consumerFilter.getMethodName(invoker, invocation, null);
|
||||
Entry[] entries = (Entry[]) RpcContext.getContext().get(methodResourceName);
|
||||
assertNull(entries);
|
||||
}
|
||||
|
||||
private DefaultNode getNode(String resourceName, DefaultNode root) {
|
||||
|
||||
Queue<DefaultNode> queue = new LinkedList<>();
|
||||
queue.offer(root);
|
||||
while (!queue.isEmpty()) {
|
||||
DefaultNode temp = queue.poll();
|
||||
if (temp.getId().getName().equals(resourceName)) {
|
||||
return temp;
|
||||
}
|
||||
for (Node node : temp.getChildList()) {
|
||||
queue.offer((DefaultNode) node);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void initFlowRule(String resource) {
|
||||
FlowRule flowRule = new FlowRule(resource);
|
||||
flowRule.setCount(1);
|
||||
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
|
||||
List<FlowRule> flowRules = new ArrayList<>();
|
||||
flowRules.add(flowRule);
|
||||
FlowRuleManager.loadRules(flowRules);
|
||||
}
|
||||
|
||||
private void initDegradeRule(String resource) {
|
||||
DegradeRule degradeRule = new DegradeRule(resource)
|
||||
.setCount(0.5)
|
||||
.setGrade(DEGRADE_GRADE_EXCEPTION_RATIO);
|
||||
List<DegradeRule> degradeRules = new ArrayList<>();
|
||||
degradeRules.add(degradeRule);
|
||||
degradeRule.setTimeWindow(1);
|
||||
DegradeRuleManager.loadRules(degradeRules);
|
||||
}
|
||||
|
||||
private void initFallback() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback((invoker, invocation, ex) -> {
|
||||
// boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
|
||||
return AsyncRpcResult.newDefaultAsyncResult("fallback", invocation);
|
||||
});
|
||||
}
|
||||
|
||||
private Result invokeDubboRpc(boolean exception, Invoker invoker, Invocation invocation) {
|
||||
Result result = null;
|
||||
InvokeMode invokeMode = RpcUtils.getInvokeMode(invoker.getUrl(), invocation);
|
||||
if (InvokeMode.SYNC == invokeMode) {
|
||||
result = exception ? new AppResponse(new Exception("error")) : new AppResponse("normal");
|
||||
} else {
|
||||
result = exception ? AsyncRpcResult.newDefaultAsyncResult(new Exception("error"), invocation)
|
||||
: AsyncRpcResult.newDefaultAsyncResult("normal", invocation);
|
||||
}
|
||||
when(invoker.invoke(invocation)).thenReturn(result);
|
||||
return consumerFilter.invoke(invoker, invocation);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.BaseTest;
|
||||
import com.alibaba.csp.sentinel.DubboTestUtil;
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.node.Node;
|
||||
import com.alibaba.csp.sentinel.node.StatisticNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.constants.CommonConstants;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* @author cdfive
|
||||
* @author lianglin
|
||||
*/
|
||||
public class SentinelDubboProviderFilterTest extends BaseTest {
|
||||
|
||||
|
||||
private SentinelDubboProviderFilter filter = new SentinelDubboProviderFilter();
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() {
|
||||
cleanUpAll();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvoke() {
|
||||
|
||||
final String originApplication = "consumerA";
|
||||
|
||||
URL url = DubboTestUtil.getDefaultTestURL();
|
||||
url = url.addParameter(CommonConstants.SIDE_KEY, CommonConstants.PROVIDER_SIDE);
|
||||
Invoker invoker = DubboTestUtil.getMockInvoker(url, DemoService.class);
|
||||
|
||||
Invocation invocation = DubboTestUtil.getMockInvocation(DemoService.class.getMethods()[0]);
|
||||
when(invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, ""))
|
||||
.thenReturn(originApplication);
|
||||
|
||||
final Result result = mock(Result.class);
|
||||
when(result.hasException()).thenReturn(false);
|
||||
when(result.getException()).thenReturn(new Exception());
|
||||
|
||||
when(invoker.invoke(invocation)).thenAnswer(invocationOnMock -> {
|
||||
verifyInvocationStructure(originApplication, invoker, invocation);
|
||||
return result;
|
||||
});
|
||||
|
||||
filter.invoke(invoker, invocation);
|
||||
verify(invoker).invoke(invocation);
|
||||
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNull(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply verify invocation structure in memory:
|
||||
* EntranceNode(methodResourceName)
|
||||
* --InterfaceNode(interfaceName)
|
||||
* ----MethodNode(methodResourceName)
|
||||
*/
|
||||
private void verifyInvocationStructure(String originApplication, Invoker invoker, Invocation invocation) {
|
||||
Context context = ContextUtil.getContext();
|
||||
assertNotNull(context);
|
||||
|
||||
// As ContextUtil.enter(resourceName, application) in SentinelDubboProviderFilter
|
||||
String methodResourceName = filter.getMethodName(invoker, invocation, null);
|
||||
assertEquals(methodResourceName, context.getName());
|
||||
assertEquals(originApplication, context.getOrigin());
|
||||
|
||||
DefaultNode entranceNode = context.getEntranceNode();
|
||||
ResourceWrapper entranceResource = entranceNode.getId();
|
||||
assertEquals(methodResourceName, entranceResource.getName());
|
||||
assertSame(EntryType.IN, entranceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(interfaceName, EntryType.IN);
|
||||
Set<Node> childList = entranceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode interfaceNode = (DefaultNode) childList.iterator().next();
|
||||
ResourceWrapper interfaceResource = interfaceNode.getId();
|
||||
|
||||
assertEquals(filter.getInterfaceName(invoker, null), interfaceResource.getName());
|
||||
assertSame(EntryType.IN, interfaceResource.getEntryType());
|
||||
|
||||
// As SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
|
||||
childList = interfaceNode.getChildList();
|
||||
assertEquals(1, childList.size());
|
||||
DefaultNode methodNode = (DefaultNode) childList.iterator().next();
|
||||
ResourceWrapper methodResource = methodNode.getId();
|
||||
assertEquals(methodResourceName, methodResource.getName());
|
||||
assertSame(EntryType.IN, methodResource.getEntryType());
|
||||
|
||||
// Verify curEntry
|
||||
Entry curEntry = context.getCurEntry();
|
||||
assertSame(methodNode, curEntry.getCurNode());
|
||||
assertSame(interfaceNode, curEntry.getLastNode());
|
||||
assertNotNull(curEntry.getOriginNode());// As context origin is not "", originNode should be created
|
||||
|
||||
// Verify clusterNode
|
||||
ClusterNode methodClusterNode = methodNode.getClusterNode();
|
||||
ClusterNode interfaceClusterNode = interfaceNode.getClusterNode();
|
||||
assertNotSame(methodClusterNode, interfaceClusterNode);// Different resource->Different ProcessorSlot->Different ClusterNode
|
||||
|
||||
// As context origin is not "", the StatisticNode should be created in originCountMap of ClusterNode
|
||||
Map<String, StatisticNode> methodOriginCountMap = methodClusterNode.getOriginCountMap();
|
||||
assertEquals(1, methodOriginCountMap.size());
|
||||
assertTrue(methodOriginCountMap.containsKey(originApplication));
|
||||
|
||||
Map<String, StatisticNode> interfaceOriginCountMap = interfaceClusterNode.getOriginCountMap();
|
||||
assertEquals(1, interfaceOriginCountMap.size());
|
||||
assertTrue(interfaceOriginCountMap.containsKey(originApplication));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
|
||||
import org.apache.dubbo.rpc.AsyncRpcResult;
|
||||
import org.apache.dubbo.rpc.Result;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class DubboFallbackRegistryTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(new DefaultDubboFallback());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultFallback() {
|
||||
// Test for default fallback.
|
||||
BlockException ex = new FlowException("xxx");
|
||||
Result result = new DefaultDubboFallback().handle(null, null, ex);
|
||||
Assert.assertTrue("The result should carry exception", result.hasException());
|
||||
Assert.assertTrue(BlockException.isBlockException(result.getException()));
|
||||
Assert.assertTrue(result.getException().getMessage().contains(ex.getClass().getSimpleName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomFallback() {
|
||||
BlockException ex = new FlowException("xxx");
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(
|
||||
(invoker, invocation, e) -> AsyncRpcResult
|
||||
.newDefaultAsyncResult("Error: " + e.getClass().getName(), invocation));
|
||||
Result result = DubboAdapterGlobalConfig.getConsumerFallback()
|
||||
.handle(null, null, ex);
|
||||
Assert.assertFalse("The invocation should not fail", result.hasException());
|
||||
Assert.assertEquals("Error: " + ex.getClass().getName(), result.getValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.config.DubboAdapterGlobalConfig;
|
||||
import com.alibaba.dubbo.rpc.RpcInvocation;
|
||||
import org.apache.dubbo.rpc.Invocation;
|
||||
import org.apache.dubbo.rpc.Invoker;
|
||||
import org.junit.After;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author tiecheng
|
||||
*/
|
||||
public class DubboOriginRegistryTest {
|
||||
|
||||
@After
|
||||
public void cleanUp() {
|
||||
DubboAdapterGlobalConfig.setOriginParser(new DefaultDubboOriginParser());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testDefaultOriginParserFail() {
|
||||
DubboAdapterGlobalConfig.getOriginParser().parse(null, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultOriginParserSuccess() {
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String dubboName = "sentinel";
|
||||
invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName, origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomOriginParser() {
|
||||
DubboAdapterGlobalConfig.setOriginParser(new DubboOriginParser() {
|
||||
@Override
|
||||
public String parse(Invoker<?> invoker, Invocation invocation) {
|
||||
return invocation.getAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, "default") + "_" + invocation
|
||||
.getMethodName();
|
||||
}
|
||||
});
|
||||
|
||||
RpcInvocation invocation = new RpcInvocation();
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals("default_null", origin);
|
||||
|
||||
String dubboName = "sentinel";
|
||||
invocation.setAttachment(DubboUtils.SENTINEL_DUBBO_APPLICATION_KEY, dubboName);
|
||||
origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName + "_null", origin);
|
||||
|
||||
invocation.setMethodName("hello");
|
||||
origin = DubboAdapterGlobalConfig.getOriginParser().parse(null, invocation);
|
||||
Assert.assertEquals(dubboName + "_hello", origin);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.provider;
|
||||
|
||||
/**
|
||||
* @author leyou
|
||||
*/
|
||||
public interface DemoService {
|
||||
String sayHello(String name, int n);
|
||||
String sayHi(String name,int n);
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.provider.impl;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService;
|
||||
|
||||
/**
|
||||
* @author leyou
|
||||
*/
|
||||
public class DemoServiceImpl implements DemoService {
|
||||
public String sayHello(String name, int n) {
|
||||
return "Hello " + name + ", " + n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sayHi(String name, int n) {
|
||||
return "Hi " + name + ", " + n;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://code.alibabatech.com/schema/dubbo
|
||||
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="demo-consumer"/>
|
||||
<dubbo:registry address="multicast://224.5.6.7:1234"/>
|
||||
<dubbo:protocol name="dubbo" port="20880"/>
|
||||
<dubbo:service interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService" ref="demoServiceImp" />
|
||||
<bean id="demoServiceImp" class="com.alibaba.csp.sentinel.adapter.dubbo.provider.impl.DemoServiceImpl"/>
|
||||
|
||||
<dubbo:reference id="demoService" interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://code.alibabatech.com/schema/dubbo
|
||||
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="demo-provider"/>
|
||||
<dubbo:registry address="multicast://224.5.6.7:1234"/>
|
||||
<dubbo:protocol name="dubbo" port="20880"/>
|
||||
<dubbo:service interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService" ref="demoServiceImp" />
|
||||
<bean id="demoServiceImp" class="com.alibaba.csp.sentinel.adapter.dubbo.provider.impl.DemoServiceImpl"/>
|
||||
|
||||
<dubbo:reference id="demoService" interface="com.alibaba.csp.sentinel.adapter.dubbo.provider.DemoService"/>
|
||||
|
||||
</beans>
|
||||
@@ -0,0 +1,75 @@
|
||||
# Sentinel Apache Httpclient Adapter
|
||||
|
||||
## Introduction
|
||||
|
||||
Sentinel provides integration for OkHttp client to enable flow control for web requests.
|
||||
|
||||
Add the following dependency in `pom.xml` (if you are using Maven):
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
We can use the `SentinelApacheHttpClientBuilder` when `CloseableHttpClient` at initialization, for example:
|
||||
|
||||
```java
|
||||
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
|
||||
```
|
||||
|
||||
If we want to add some additional configurations, we can refer to the following code
|
||||
|
||||
```java
|
||||
HttpClientBuilder builder = new SentinelApacheHttpClientBuilder();
|
||||
//builder Other Definitions
|
||||
CloseableHttpClient httpclient = builder.build();
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
- `SentinelApacheHttpClientConfig` configuration:
|
||||
|
||||
| name | description | type | default value |
|
||||
|------|------------|------|-------|
|
||||
| prefix | customize resource prefix | `String` | `httpclient:` |
|
||||
| extractor | customize resource extractor | `ApacheHttpClientResourceExtractor` | `DefaultApacheHttpClientResourceExtractor` |
|
||||
| fallback | handle request when it is blocked | `ApacheHttpClientFallback` | `DefaultApacheHttpClientFallback` |
|
||||
|
||||
### extractor (resource extractor)
|
||||
|
||||
We can define `ApacheHttpClientResourceExtractor` to customize resource extractor replace `DefaultApacheHttpClientResourceExtractor` at `SentinelApacheHttpClientBuilder` default config, for example: httpclient:GET:/httpclient/back/1 ==> httpclient:GET:/httpclient/back/{id}
|
||||
|
||||
```java
|
||||
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||
|
||||
@Override
|
||||
public String extractor(HttpRequestWrapper request) {
|
||||
String contains = "/httpclient/back/";
|
||||
String uri = request.getRequestLine().getUri();
|
||||
if (uri.startsWith(contains)) {
|
||||
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||
}
|
||||
return request.getMethod() + ":" + uri;
|
||||
}
|
||||
});
|
||||
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||
```
|
||||
|
||||
### fallback (Block handling)
|
||||
|
||||
We can define `ApacheHttpClientFallback` at `SentinelApacheHttpClientBuilder` default config, to handle request is blocked according to the actual scenario, for example:
|
||||
|
||||
```java
|
||||
public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
|
||||
|
||||
@Override
|
||||
public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
|
||||
// Just wrap and throw the exception.
|
||||
throw new SentinelRpcException(e);
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-apache-httpclient-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<apache.httpclient.version>4.5.6</apache.httpclient.version>
|
||||
<spring.boot.version>2.1.3.RELEASE</spring.boot.version>
|
||||
<spring-test.version>5.1.5.RELEASE</spring-test.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>${apache.httpclient.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<version>${spring-test.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient;
|
||||
|
||||
import com.alibaba.csp.sentinel.*;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpExecutionAware;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.client.protocol.HttpClientContext;
|
||||
import org.apache.http.conn.routing.HttpRoute;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.execchain.ClientExecChain;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class SentinelApacheHttpClientBuilder extends HttpClientBuilder {
|
||||
|
||||
private final SentinelApacheHttpClientConfig config;
|
||||
|
||||
public SentinelApacheHttpClientBuilder(){
|
||||
this.config = new SentinelApacheHttpClientConfig();
|
||||
}
|
||||
|
||||
public SentinelApacheHttpClientBuilder(SentinelApacheHttpClientConfig config){
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) {
|
||||
return new ClientExecChain() {
|
||||
@Override
|
||||
public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request,
|
||||
HttpClientContext clientContext, HttpExecutionAware execAware)
|
||||
throws IOException, HttpException {
|
||||
Entry entry = null;
|
||||
try {
|
||||
String name = config.getExtractor().extractor(request);
|
||||
if (!StringUtil.isEmpty(config.getPrefix())) {
|
||||
name = config.getPrefix() + name;
|
||||
}
|
||||
entry = SphU.entry(name, ResourceTypeConstants.COMMON_WEB, EntryType.OUT);
|
||||
return mainExec.execute(route, request, clientContext, execAware);
|
||||
} catch (BlockException e) {
|
||||
return config.getFallback().handle(request, e);
|
||||
} catch (Throwable t) {
|
||||
Tracer.traceEntry(t, entry);
|
||||
throw t;
|
||||
} finally {
|
||||
if (entry != null) {
|
||||
entry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.config;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.DefaultApacheHttpClientResourceExtractor;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.ApacheHttpClientFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback.DefaultApacheHttpClientFallback;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class SentinelApacheHttpClientConfig {
|
||||
|
||||
private String prefix = "httpclient:";
|
||||
private ApacheHttpClientResourceExtractor extractor = new DefaultApacheHttpClientResourceExtractor();
|
||||
private ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix) {
|
||||
AssertUtil.notNull(prefix, "prefix cannot be null");
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
public ApacheHttpClientResourceExtractor getExtractor() {
|
||||
return extractor;
|
||||
}
|
||||
|
||||
public void setExtractor(ApacheHttpClientResourceExtractor extractor) {
|
||||
AssertUtil.notNull(extractor, "extractor cannot be null");
|
||||
this.extractor = extractor;
|
||||
}
|
||||
|
||||
public ApacheHttpClientFallback getFallback() {
|
||||
return fallback;
|
||||
}
|
||||
|
||||
public void setFallback(ApacheHttpClientFallback fallback) {
|
||||
AssertUtil.notNull(fallback, "fallback cannot be null");
|
||||
this.fallback = fallback;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor;
|
||||
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public interface ApacheHttpClientResourceExtractor {
|
||||
|
||||
String extractor(HttpRequestWrapper request);
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor;
|
||||
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class DefaultApacheHttpClientResourceExtractor implements ApacheHttpClientResourceExtractor {
|
||||
|
||||
@Override
|
||||
public String extractor(HttpRequestWrapper request) {
|
||||
return request.getRequestLine().getUri();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public interface ApacheHttpClientFallback {
|
||||
|
||||
CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
|
||||
import org.apache.http.HttpException;
|
||||
import org.apache.http.HttpRequest;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class DefaultApacheHttpClientFallback implements ApacheHttpClientFallback {
|
||||
|
||||
@Override
|
||||
public CloseableHttpResponse handle(HttpRequestWrapper request, BlockException e) {
|
||||
// Just wrap and throw the exception.
|
||||
throw new SentinelRpcException(e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient;
|
||||
|
||||
import com.alibaba.csp.sentinel.Constants;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.app.TestApplication;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.config.SentinelApacheHttpClientConfig;
|
||||
import com.alibaba.csp.sentinel.adapter.apache.httpclient.extractor.ApacheHttpClientResourceExtractor;
|
||||
import com.alibaba.csp.sentinel.node.ClusterNode;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpRequestWrapper;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.protocol.BasicHttpContext;
|
||||
import org.apache.http.protocol.HttpContext;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes = TestApplication.class,
|
||||
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
|
||||
properties = {
|
||||
"server.port=8084"
|
||||
})
|
||||
public class SentinelApacheHttpClientTest {
|
||||
|
||||
@Value("${server.port}")
|
||||
private Integer port;
|
||||
|
||||
@Test
|
||||
public void testSentinelOkHttpInterceptor0() throws Exception {
|
||||
|
||||
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder().build();
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back");
|
||||
System.out.println(getRemoteString(httpclient, httpGet));
|
||||
ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:/httpclient/back");
|
||||
assertNotNull(cn);
|
||||
Constants.ROOT.removeChildList();
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSentinelOkHttpInterceptor1() throws Exception {
|
||||
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||
config.setExtractor(new ApacheHttpClientResourceExtractor() {
|
||||
|
||||
@Override
|
||||
public String extractor(HttpRequestWrapper request) {
|
||||
String contains = "/httpclient/back/";
|
||||
String uri = request.getRequestLine().getUri();
|
||||
if (uri.startsWith(contains)) {
|
||||
uri = uri.substring(0, uri.indexOf(contains) + contains.length()) + "{id}";
|
||||
}
|
||||
return request.getMethod() + ":" + uri;
|
||||
}
|
||||
});
|
||||
CloseableHttpClient httpclient = new SentinelApacheHttpClientBuilder(config).build();
|
||||
|
||||
HttpGet httpGet = new HttpGet("http://localhost:" + port + "/httpclient/back/1");
|
||||
System.out.println(getRemoteString(httpclient, httpGet));
|
||||
ClusterNode cn = ClusterBuilderSlot.getClusterNode("httpclient:GET:/httpclient/back/{id}");
|
||||
assertNotNull(cn);
|
||||
Constants.ROOT.removeChildList();
|
||||
ClusterBuilderSlot.getClusterNodeMap().clear();
|
||||
}
|
||||
|
||||
private String getRemoteString(CloseableHttpClient httpclient, HttpGet httpGet) throws IOException {
|
||||
String result;
|
||||
HttpContext context = new BasicHttpContext();
|
||||
CloseableHttpResponse response;
|
||||
response = httpclient.execute(httpGet, context);
|
||||
try {
|
||||
HttpEntity entity = response.getEntity();
|
||||
result = EntityUtils.toString(entity, "utf-8");
|
||||
EntityUtils.consume(entity);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
httpclient.close();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.app;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class TestApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(TestApplication.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.app.controller;
|
||||
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
@RestController
|
||||
public class TestController {
|
||||
|
||||
@RequestMapping("/httpclient/back")
|
||||
public String back() {
|
||||
return "Welcome Back!";
|
||||
}
|
||||
|
||||
@RequestMapping("/httpclient/back/{id}")
|
||||
public String back(@PathVariable String id) {
|
||||
return "Welcome Back! " + id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.config;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class SentinelApacheHttpClientConfigTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testConfigSetPrefix() {
|
||||
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||
config.setPrefix(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testConfigSetCleaner() {
|
||||
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||
config.setExtractor(null);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testConfigSetFallback() {
|
||||
SentinelApacheHttpClientConfig config = new SentinelApacheHttpClientConfig();
|
||||
config.setFallback(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.apache.httpclient.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* @author zhaoyuguang
|
||||
*/
|
||||
public class ApacheHttpClientFallbackTest {
|
||||
|
||||
@Test(expected = SentinelRpcException.class)
|
||||
public void testDefaultOkHttpFallback() {
|
||||
BlockException e = new FlowException("xxx");
|
||||
ApacheHttpClientFallback fallback = new DefaultApacheHttpClientFallback();
|
||||
fallback.handle(null, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Sentinel API Gateway Adapter Common
|
||||
|
||||
The `sentinel-api-gateway-adapter-common` module provides common abstraction for
|
||||
API gateway flow control:
|
||||
|
||||
- `GatewayFlowRule`: flow control rule specific for route or API defined in API gateway.
|
||||
This can be automatically converted to `FlowRule` or `ParamFlowRule`.
|
||||
- `ApiDefinition`: gateway API definition with a group of predicates
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>sentinel-api-gateway-adapter-common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-parameter-flow-control</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-transport-common</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class SentinelGatewayConstants {
|
||||
|
||||
public static final int APP_TYPE_GATEWAY = 1;
|
||||
|
||||
public static final int RESOURCE_MODE_ROUTE_ID = 0;
|
||||
public static final int RESOURCE_MODE_CUSTOM_API_NAME = 1;
|
||||
|
||||
public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
|
||||
public static final int PARAM_PARSE_STRATEGY_HOST = 1;
|
||||
public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
|
||||
public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
|
||||
public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;
|
||||
|
||||
public static final int URL_MATCH_STRATEGY_EXACT = 0;
|
||||
public static final int URL_MATCH_STRATEGY_PREFIX = 1;
|
||||
public static final int URL_MATCH_STRATEGY_REGEX = 2;
|
||||
|
||||
public static final int PARAM_MATCH_STRATEGY_EXACT = 0;
|
||||
public static final int PARAM_MATCH_STRATEGY_PREFIX = 1;
|
||||
public static final int PARAM_MATCH_STRATEGY_REGEX = 2;
|
||||
public static final int PARAM_MATCH_STRATEGY_CONTAINS = 3;
|
||||
|
||||
public static final String GATEWAY_CONTEXT_DEFAULT = "sentinel_gateway_context_default";
|
||||
public static final String GATEWAY_CONTEXT_PREFIX = "sentinel_gateway_context$$";
|
||||
public static final String GATEWAY_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$";
|
||||
|
||||
public static final String GATEWAY_NOT_MATCH_PARAM = "$NM";
|
||||
public static final String GATEWAY_DEFAULT_PARAM = "$D";
|
||||
|
||||
private SentinelGatewayConstants() {}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A group of HTTP API patterns.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiDefinition {
|
||||
|
||||
private String apiName;
|
||||
private Set<ApiPredicateItem> predicateItems;
|
||||
|
||||
public ApiDefinition() {}
|
||||
|
||||
public ApiDefinition(String apiName) {
|
||||
this.apiName = apiName;
|
||||
}
|
||||
|
||||
public String getApiName() {
|
||||
return apiName;
|
||||
}
|
||||
|
||||
public ApiDefinition setApiName(String apiName) {
|
||||
this.apiName = apiName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<ApiPredicateItem> getPredicateItems() {
|
||||
return predicateItems;
|
||||
}
|
||||
|
||||
public ApiDefinition setPredicateItems(Set<ApiPredicateItem> predicateItems) {
|
||||
this.predicateItems = predicateItems;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
ApiDefinition that = (ApiDefinition)o;
|
||||
|
||||
if (!Objects.equals(apiName, that.apiName)) { return false; }
|
||||
return Objects.equals(predicateItems, that.predicateItems);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = apiName != null ? apiName.hashCode() : 0;
|
||||
result = 31 * result + (predicateItems != null ? predicateItems.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiDefinition{" +
|
||||
"apiName='" + apiName + '\'' +
|
||||
", predicateItems=" + predicateItems +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface ApiDefinitionChangeObserver {
|
||||
|
||||
/**
|
||||
* Notify the observer about the new gateway API definitions.
|
||||
*
|
||||
* @param apiDefinitions new set of gateway API definition
|
||||
*/
|
||||
void onChange(Set<ApiDefinition> apiDefinitions);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiPathPredicateItem implements ApiPredicateItem {
|
||||
|
||||
private String pattern;
|
||||
private int matchStrategy = SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT;
|
||||
|
||||
public ApiPathPredicateItem setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ApiPathPredicateItem setMatchStrategy(int matchStrategy) {
|
||||
this.matchStrategy = matchStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public int getMatchStrategy() {
|
||||
return matchStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
ApiPathPredicateItem that = (ApiPathPredicateItem)o;
|
||||
|
||||
if (matchStrategy != that.matchStrategy) { return false; }
|
||||
return Objects.equals(pattern, that.pattern);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = pattern != null ? pattern.hashCode() : 0;
|
||||
result = 31 * result + matchStrategy;
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ApiPathPredicateItem{" +
|
||||
"pattern='" + pattern + '\'' +
|
||||
", matchStrategy=" + matchStrategy +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class ApiPredicateGroupItem implements ApiPredicateItem {
|
||||
|
||||
private final Set<ApiPredicateItem> items = new HashSet<>();
|
||||
|
||||
public ApiPredicateGroupItem addItem(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
items.add(item);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Set<ApiPredicateItem> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
/*@Override
|
||||
public ApiPredicateItem and(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
return this.addItem(item);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface ApiPredicateItem {
|
||||
|
||||
/**
|
||||
* Combine two {@link ApiPredicateItem}.
|
||||
*
|
||||
* @param item another predicate item
|
||||
* @return combined predicate group item
|
||||
*/
|
||||
/*default ApiPredicateItem and(ApiPredicateItem item) {
|
||||
AssertUtil.notNull(item, "item cannot be null");
|
||||
return new ApiPredicateGroupItem()
|
||||
.addItem(this).addItem(item);
|
||||
}*/
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.spi.SpiLoader;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* Manager for gateway API definitions.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class GatewayApiDefinitionManager {
|
||||
|
||||
private static final Map<String, ApiDefinition> API_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final ApiDefinitionPropertyListener LISTENER = new ApiDefinitionPropertyListener();
|
||||
private static SentinelProperty<Set<ApiDefinition>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
||||
/**
|
||||
* The map keeps all found ApiDefinitionChangeObserver (class name as key).
|
||||
*/
|
||||
private static final Map<String, ApiDefinitionChangeObserver> API_CHANGE_OBSERVERS = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
try {
|
||||
currentProperty.addListener(LISTENER);
|
||||
initializeApiChangeObserverSpi();
|
||||
} catch (Throwable ex) {
|
||||
RecordLog.warn("[GatewayApiDefinitionManager] Failed to initialize", ex);
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeApiChangeObserverSpi() {
|
||||
List<ApiDefinitionChangeObserver> listeners = SpiLoader.of(ApiDefinitionChangeObserver.class).loadInstanceList();
|
||||
for (ApiDefinitionChangeObserver e : listeners) {
|
||||
API_CHANGE_OBSERVERS.put(e.getClass().getCanonicalName(), e);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] ApiDefinitionChangeObserver added: {}"
|
||||
, e.getClass().getCanonicalName());
|
||||
}
|
||||
}
|
||||
|
||||
public static void register2Property(SentinelProperty<Set<ApiDefinition>> property) {
|
||||
AssertUtil.notNull(property, "property cannot be null");
|
||||
synchronized (LISTENER) {
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Registering new property to gateway API definition manager");
|
||||
currentProperty.removeListener(LISTENER);
|
||||
property.addListener(LISTENER);
|
||||
currentProperty = property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load given gateway API definitions and apply to downstream observers.
|
||||
*
|
||||
* @param apiDefinitions set of gateway API definitions
|
||||
* @return true if updated, or else false
|
||||
*/
|
||||
public static boolean loadApiDefinitions(Set<ApiDefinition> apiDefinitions) {
|
||||
return currentProperty.updateValue(apiDefinitions);
|
||||
}
|
||||
|
||||
public static ApiDefinition getApiDefinition(final String apiName) {
|
||||
if (apiName == null) {
|
||||
return null;
|
||||
}
|
||||
return API_MAP.get(apiName);
|
||||
}
|
||||
|
||||
public static Set<ApiDefinition> getApiDefinitions() {
|
||||
return new HashSet<>(API_MAP.values());
|
||||
}
|
||||
|
||||
private static final class ApiDefinitionPropertyListener implements PropertyListener<Set<ApiDefinition>> {
|
||||
|
||||
@Override
|
||||
public void configUpdate(Set<ApiDefinition> set) {
|
||||
applyApiUpdateInternal(set);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Api definition updated: {}", API_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(Set<ApiDefinition> set) {
|
||||
applyApiUpdateInternal(set);
|
||||
RecordLog.info("[GatewayApiDefinitionManager] Api definition loaded: {}", API_MAP);
|
||||
}
|
||||
|
||||
private static synchronized void applyApiUpdateInternal(Set<ApiDefinition> set) {
|
||||
if (set == null || set.isEmpty()) {
|
||||
API_MAP.clear();
|
||||
notifyDownstreamListeners(new HashSet<ApiDefinition>());
|
||||
return;
|
||||
}
|
||||
Map<String, ApiDefinition> map = new HashMap<>(set.size());
|
||||
Set<ApiDefinition> validSet = new HashSet<>();
|
||||
for (ApiDefinition definition : set) {
|
||||
if (isValidApi(definition)) {
|
||||
map.put(definition.getApiName(), definition);
|
||||
validSet.add(definition);
|
||||
}
|
||||
}
|
||||
|
||||
API_MAP.clear();
|
||||
API_MAP.putAll(map);
|
||||
|
||||
// propagate to downstream.
|
||||
notifyDownstreamListeners(validSet);
|
||||
}
|
||||
}
|
||||
|
||||
private static void notifyDownstreamListeners(/*@Valid*/ final Set<ApiDefinition> definitions) {
|
||||
try {
|
||||
for (Map.Entry<?, ApiDefinitionChangeObserver> entry : API_CHANGE_OBSERVERS.entrySet()) {
|
||||
entry.getValue().onChange(definitions);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[GatewayApiDefinitionManager] WARN: failed to notify downstream api listeners", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isValidApi(ApiDefinition apiDefinition) {
|
||||
return apiDefinition != null && StringUtil.isNotBlank(apiDefinition.getApiName())
|
||||
&& apiDefinition.getPredicateItems() != null;
|
||||
}
|
||||
|
||||
static void addApiChangeListener(ApiDefinitionChangeObserver listener) {
|
||||
AssertUtil.notNull(listener, "listener cannot be null");
|
||||
API_CHANGE_OBSERVERS.put(listener.getClass().getCanonicalName(), listener);
|
||||
}
|
||||
|
||||
static void removeApiChangeListener(Class<?> clazz) {
|
||||
AssertUtil.notNull(clazz, "class cannot be null");
|
||||
API_CHANGE_OBSERVERS.remove(clazz.getCanonicalName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public abstract class AbstractApiMatcher<T> implements Predicate<T> {
|
||||
|
||||
protected final String apiName;
|
||||
protected final ApiDefinition apiDefinition;
|
||||
/**
|
||||
* We use {@link com.alibaba.csp.sentinel.util.function.Predicate} here as the min JDK version is 1.7.
|
||||
*/
|
||||
protected final Set<Predicate<T>> matchers = new HashSet<>();
|
||||
|
||||
public AbstractApiMatcher(ApiDefinition apiDefinition) {
|
||||
AssertUtil.notNull(apiDefinition, "apiDefinition cannot be null");
|
||||
AssertUtil.assertNotBlank(apiDefinition.getApiName(), "apiName cannot be empty");
|
||||
this.apiName = apiDefinition.getApiName();
|
||||
this.apiDefinition = apiDefinition;
|
||||
|
||||
try {
|
||||
initializeMatchers();
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[GatewayApiMatcher] Failed to initialize internal matchers", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the matchers.
|
||||
*/
|
||||
protected abstract void initializeMatchers();
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
for (Predicate<T> matcher : matchers) {
|
||||
if (matcher.test(t)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getApiName() {
|
||||
return apiName;
|
||||
}
|
||||
|
||||
public ApiDefinition getApiDefinition() {
|
||||
return apiDefinition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.command;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
|
||||
import com.alibaba.csp.sentinel.command.CommandHandler;
|
||||
import com.alibaba.csp.sentinel.command.CommandRequest;
|
||||
import com.alibaba.csp.sentinel.command.CommandResponse;
|
||||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@CommandMapping(name = "gateway/getApiDefinitions", desc = "Fetch all customized gateway API groups")
|
||||
public class GetGatewayApiDefinitionGroupCommandHandler implements CommandHandler<String> {
|
||||
|
||||
@Override
|
||||
public CommandResponse<String> handle(CommandRequest request) {
|
||||
return CommandResponse.ofSuccess(JSON.toJSONString(GatewayApiDefinitionManager.getApiDefinitions()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.command;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.command.CommandHandler;
|
||||
import com.alibaba.csp.sentinel.command.CommandRequest;
|
||||
import com.alibaba.csp.sentinel.command.CommandResponse;
|
||||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@CommandMapping(name = "gateway/getRules", desc = "Fetch all gateway rules")
|
||||
public class GetGatewayRuleCommandHandler implements CommandHandler<String> {
|
||||
|
||||
@Override
|
||||
public CommandResponse<String> handle(CommandRequest request) {
|
||||
return CommandResponse.ofSuccess(JSON.toJSONString(GatewayRuleManager.getRules()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.command;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPathPredicateItem;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
|
||||
import com.alibaba.csp.sentinel.command.CommandHandler;
|
||||
import com.alibaba.csp.sentinel.command.CommandRequest;
|
||||
import com.alibaba.csp.sentinel.command.CommandResponse;
|
||||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
|
||||
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@CommandMapping(name = "gateway/updateApiDefinitions", desc = "")
|
||||
public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler<String> {
|
||||
|
||||
private static WritableDataSource<Set<ApiDefinition>> apiDefinitionWds = null;
|
||||
|
||||
@Override
|
||||
public CommandResponse<String> handle(CommandRequest request) {
|
||||
String data = request.getParam("data");
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
|
||||
}
|
||||
try {
|
||||
data = URLDecoder.decode(data, "utf-8");
|
||||
} catch (Exception e) {
|
||||
RecordLog.info("Decode gateway API definition data error", e);
|
||||
return CommandResponse.ofFailure(e, "decode gateway API definition data error");
|
||||
}
|
||||
|
||||
RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {}", data);
|
||||
|
||||
String result = SUCCESS_MSG;
|
||||
|
||||
Set<ApiDefinition> apiDefinitions = parseJson(data);
|
||||
GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);
|
||||
if (!writeToDataSource(apiDefinitionWds, apiDefinitions)) {
|
||||
result = WRITE_DS_FAILURE_MSG;
|
||||
}
|
||||
return CommandResponse.ofSuccess(result);
|
||||
}
|
||||
|
||||
private static final String SUCCESS_MSG = "success";
|
||||
private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
|
||||
|
||||
/**
|
||||
* Parse json data to set of {@link ApiDefinition}.
|
||||
*
|
||||
* Since the predicateItems of {@link ApiDefinition} is set of interface,
|
||||
* here we parse predicateItems to {@link ApiPathPredicateItem} temporarily.
|
||||
*/
|
||||
private Set<ApiDefinition> parseJson(String data) {
|
||||
Set<ApiDefinition> apiDefinitions = new HashSet<>();
|
||||
JSONArray array = JSON.parseArray(data);
|
||||
for (Object obj : array) {
|
||||
JSONObject o = (JSONObject)obj;
|
||||
ApiDefinition apiDefinition = new ApiDefinition((o.getString("apiName")));
|
||||
Set<ApiPredicateItem> predicateItems = new HashSet<>();
|
||||
JSONArray itemArray = o.getJSONArray("predicateItems");
|
||||
if (itemArray != null) {
|
||||
predicateItems.addAll(itemArray.toJavaList(ApiPathPredicateItem.class));
|
||||
}
|
||||
apiDefinition.setPredicateItems(predicateItems);
|
||||
apiDefinitions.add(apiDefinition);
|
||||
}
|
||||
|
||||
return apiDefinitions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write target value to given data source.
|
||||
*
|
||||
* @param dataSource writable data source
|
||||
* @param value target value to save
|
||||
* @param <T> value type
|
||||
* @return true if write successful or data source is empty; false if error occurs
|
||||
*/
|
||||
private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
|
||||
if (dataSource != null) {
|
||||
try {
|
||||
dataSource.write(value);
|
||||
} catch (Exception e) {
|
||||
RecordLog.warn("Write data source failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized static WritableDataSource<Set<ApiDefinition>> getWritableDataSource() {
|
||||
return apiDefinitionWds;
|
||||
}
|
||||
|
||||
public synchronized static void setWritableDataSource(WritableDataSource<Set<ApiDefinition>> apiDefinitionWds) {
|
||||
UpdateGatewayApiDefinitionGroupCommandHandler.apiDefinitionWds = apiDefinitionWds;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.command;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.command.CommandHandler;
|
||||
import com.alibaba.csp.sentinel.command.CommandRequest;
|
||||
import com.alibaba.csp.sentinel.command.CommandResponse;
|
||||
import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
|
||||
import com.alibaba.csp.sentinel.datasource.WritableDataSource;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
@CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules")
|
||||
public class UpdateGatewayRuleCommandHandler implements CommandHandler<String> {
|
||||
private static WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds = null;
|
||||
|
||||
@Override
|
||||
public CommandResponse<String> handle(CommandRequest request) {
|
||||
String data = request.getParam("data");
|
||||
if (StringUtil.isBlank(data)) {
|
||||
return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
|
||||
}
|
||||
try {
|
||||
data = URLDecoder.decode(data, "utf-8");
|
||||
} catch (Exception e) {
|
||||
RecordLog.info("Decode gateway rule data error", e);
|
||||
return CommandResponse.ofFailure(e, "decode gateway rule data error");
|
||||
}
|
||||
|
||||
RecordLog.info("[API Server] Receiving rule change (type: gateway rule): {}", data);
|
||||
|
||||
String result = SUCCESS_MSG;
|
||||
Set<GatewayFlowRule> flowRules = JSON.parseObject(data, new TypeReference<Set<GatewayFlowRule>>() {
|
||||
});
|
||||
GatewayRuleManager.loadRules(flowRules);
|
||||
if (!writeToDataSource(gatewayFlowWds, flowRules)) {
|
||||
result = WRITE_DS_FAILURE_MSG;
|
||||
}
|
||||
return CommandResponse.ofSuccess(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write target value to given data source.
|
||||
*
|
||||
* @param dataSource writable data source
|
||||
* @param value target value to save
|
||||
* @param <T> value type
|
||||
* @return true if write successful or data source is empty; false if error occurs
|
||||
*/
|
||||
private <T> boolean writeToDataSource(WritableDataSource<T> dataSource, T value) {
|
||||
if (dataSource != null) {
|
||||
try {
|
||||
dataSource.write(value);
|
||||
} catch (Exception e) {
|
||||
RecordLog.warn("Write data source failed", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public synchronized static WritableDataSource<Set<GatewayFlowRule>> getWritableDataSource() {
|
||||
return gatewayFlowWds;
|
||||
}
|
||||
|
||||
public synchronized static void setWritableDataSource(WritableDataSource<Set<GatewayFlowRule>> gatewayFlowWds) {
|
||||
UpdateGatewayRuleCommandHandler.gatewayFlowWds = gatewayFlowWds;
|
||||
}
|
||||
|
||||
private static final String SUCCESS_MSG = "success";
|
||||
private static final String WRITE_DS_FAILURE_MSG = "partial success (write data source failed)";
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.param;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayParamParser<T> {
|
||||
|
||||
private final RequestItemParser<T> requestItemParser;
|
||||
|
||||
public GatewayParamParser(RequestItemParser<T> requestItemParser) {
|
||||
AssertUtil.notNull(requestItemParser, "requestItemParser cannot be null");
|
||||
this.requestItemParser = requestItemParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse parameters for given resource from the request entity on condition of the rule predicate.
|
||||
*
|
||||
* @param resource valid resource name
|
||||
* @param request valid request
|
||||
* @param rulePredicate rule predicate indicating the rules to refer
|
||||
* @return the parameter array
|
||||
*/
|
||||
public Object[] parseParameterFor(String resource, T request, Predicate<GatewayFlowRule> rulePredicate) {
|
||||
if (StringUtil.isEmpty(resource) || request == null || rulePredicate == null) {
|
||||
return new Object[0];
|
||||
}
|
||||
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
|
||||
Set<Boolean> predSet = new HashSet<>();
|
||||
boolean hasNonParamRule = false;
|
||||
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
|
||||
if (rule.getParamItem() != null) {
|
||||
gatewayRules.add(rule);
|
||||
predSet.add(rulePredicate.test(rule));
|
||||
} else {
|
||||
hasNonParamRule = true;
|
||||
}
|
||||
}
|
||||
if (!hasNonParamRule && gatewayRules.isEmpty()) {
|
||||
return new Object[0];
|
||||
}
|
||||
if (predSet.size() > 1 || predSet.contains(false)) {
|
||||
return new Object[0];
|
||||
}
|
||||
int size = hasNonParamRule ? gatewayRules.size() + 1 : gatewayRules.size();
|
||||
Object[] arr = new Object[size];
|
||||
for (GatewayFlowRule rule : gatewayRules) {
|
||||
GatewayParamFlowItem paramItem = rule.getParamItem();
|
||||
int idx = paramItem.getIndex();
|
||||
String param = parseInternal(paramItem, request);
|
||||
arr[idx] = param;
|
||||
}
|
||||
if (hasNonParamRule) {
|
||||
arr[size - 1] = SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
private String parseInternal(GatewayParamFlowItem item, T request) {
|
||||
switch (item.getParseStrategy()) {
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP:
|
||||
return parseClientIp(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST:
|
||||
return parseHost(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER:
|
||||
return parseHeader(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
|
||||
return parseUrlParameter(item, request);
|
||||
case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE:
|
||||
return parseCookie(item, request);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String clientIp = requestItemParser.getRemoteAddress(request);
|
||||
String pattern = item.getPattern();
|
||||
if (StringUtil.isEmpty(pattern)) {
|
||||
return clientIp;
|
||||
}
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
|
||||
}
|
||||
|
||||
private String parseHeader(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String headerKey = item.getFieldName();
|
||||
String pattern = item.getPattern();
|
||||
// TODO: what if the header has multiple values?
|
||||
String headerValue = requestItemParser.getHeader(request, headerKey);
|
||||
if (StringUtil.isEmpty(pattern)) {
|
||||
return headerValue;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), headerValue, pattern);
|
||||
}
|
||||
|
||||
private String parseHost(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String pattern = item.getPattern();
|
||||
String host = requestItemParser.getHeader(request, "Host");
|
||||
if (StringUtil.isEmpty(pattern)) {
|
||||
return host;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), host, pattern);
|
||||
}
|
||||
|
||||
private String parseUrlParameter(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String paramName = item.getFieldName();
|
||||
String pattern = item.getPattern();
|
||||
String param = requestItemParser.getUrlParam(request, paramName);
|
||||
if (StringUtil.isEmpty(pattern)) {
|
||||
return param;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
||||
}
|
||||
|
||||
private String parseCookie(/*@Valid*/ GatewayParamFlowItem item, T request) {
|
||||
String cookieName = item.getFieldName();
|
||||
String pattern = item.getPattern();
|
||||
String param = requestItemParser.getCookieValue(request, cookieName);
|
||||
if (StringUtil.isEmpty(pattern)) {
|
||||
return param;
|
||||
}
|
||||
// Match value according to regex pattern or exact mode.
|
||||
return parseWithMatchStrategyInternal(item.getMatchStrategy(), param, pattern);
|
||||
}
|
||||
|
||||
private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
switch (matchStrategy) {
|
||||
case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT:
|
||||
return value.equals(pattern) ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
|
||||
case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS:
|
||||
return value.contains(pattern) ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
|
||||
case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX:
|
||||
Pattern regex = GatewayRegexCache.getRegexPattern(pattern);
|
||||
if (regex == null) {
|
||||
return value;
|
||||
}
|
||||
return regex.matcher(value).matches() ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
|
||||
default:
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.param;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.2
|
||||
*/
|
||||
public final class GatewayRegexCache {
|
||||
|
||||
private static final Map<String, Pattern> REGEX_CACHE = new ConcurrentHashMap<>();
|
||||
|
||||
public static Pattern getRegexPattern(String pattern) {
|
||||
if (pattern == null) {
|
||||
return null;
|
||||
}
|
||||
return REGEX_CACHE.get(pattern);
|
||||
}
|
||||
|
||||
public static boolean addRegexPattern(String pattern) {
|
||||
if (pattern == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
Pattern regex = Pattern.compile(pattern);
|
||||
REGEX_CACHE.put(pattern, regex);
|
||||
return true;
|
||||
} catch (Exception ex) {
|
||||
RecordLog.warn("[GatewayRegexCache] Failed to compile the regex: " + pattern, ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
REGEX_CACHE.clear();
|
||||
}
|
||||
|
||||
private GatewayRegexCache() {}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.param;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public interface RequestItemParser<T> {
|
||||
|
||||
/**
|
||||
* Get API path from the request.
|
||||
*
|
||||
* @param request valid request
|
||||
* @return API path
|
||||
*/
|
||||
String getPath(T request);
|
||||
|
||||
/**
|
||||
* Get remote address from the request.
|
||||
*
|
||||
* @param request valid request
|
||||
* @return remote address
|
||||
*/
|
||||
String getRemoteAddress(T request);
|
||||
|
||||
/**
|
||||
* Get the header associated with the header key.
|
||||
*
|
||||
* @param request valid request
|
||||
* @param key valid header key
|
||||
* @return the header
|
||||
*/
|
||||
String getHeader(T request, String key);
|
||||
|
||||
/**
|
||||
* Get the parameter value associated with the parameter name.
|
||||
*
|
||||
* @param request valid request
|
||||
* @param paramName valid parameter name
|
||||
* @return the parameter value
|
||||
*/
|
||||
String getUrlParam(T request, String paramName);
|
||||
|
||||
/**
|
||||
* Get the cookie value associated with the cookie name.
|
||||
*
|
||||
* @param request valid request
|
||||
* @param cookieName valid cookie name
|
||||
* @return the cookie value
|
||||
* @since 1.7.0
|
||||
*/
|
||||
String getCookieValue(T request, String cookieName);
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayFlowRule {
|
||||
|
||||
private String resource;
|
||||
private int resourceMode = SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID;
|
||||
|
||||
private int grade = RuleConstant.FLOW_GRADE_QPS;
|
||||
private double count;
|
||||
private long intervalSec = 1;
|
||||
|
||||
private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
|
||||
private int burst;
|
||||
/**
|
||||
* For throttle (rate limiting with queueing).
|
||||
*/
|
||||
private int maxQueueingTimeoutMs = 500;
|
||||
|
||||
/**
|
||||
* For parameter flow control. If not set, the gateway rule will be
|
||||
* converted to normal flow rule.
|
||||
*/
|
||||
private GatewayParamFlowItem paramItem;
|
||||
|
||||
public GatewayFlowRule() {}
|
||||
|
||||
public GatewayFlowRule(String resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
public String getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setResource(String resource) {
|
||||
this.resource = resource;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getResourceMode() {
|
||||
return resourceMode;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setResourceMode(int resourceMode) {
|
||||
this.resourceMode = resourceMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getGrade() {
|
||||
return grade;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setGrade(int grade) {
|
||||
this.grade = grade;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getControlBehavior() {
|
||||
return controlBehavior;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setControlBehavior(int controlBehavior) {
|
||||
this.controlBehavior = controlBehavior;
|
||||
return this;
|
||||
}
|
||||
|
||||
public double getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setCount(double count) {
|
||||
this.count = count;
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getIntervalSec() {
|
||||
return intervalSec;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setIntervalSec(long intervalSec) {
|
||||
this.intervalSec = intervalSec;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getBurst() {
|
||||
return burst;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setBurst(int burst) {
|
||||
this.burst = burst;
|
||||
return this;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem getParamItem() {
|
||||
return paramItem;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setParamItem(GatewayParamFlowItem paramItem) {
|
||||
this.paramItem = paramItem;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMaxQueueingTimeoutMs() {
|
||||
return maxQueueingTimeoutMs;
|
||||
}
|
||||
|
||||
public GatewayFlowRule setMaxQueueingTimeoutMs(int maxQueueingTimeoutMs) {
|
||||
this.maxQueueingTimeoutMs = maxQueueingTimeoutMs;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) { return true; }
|
||||
if (o == null || getClass() != o.getClass()) { return false; }
|
||||
|
||||
GatewayFlowRule rule = (GatewayFlowRule)o;
|
||||
|
||||
if (resourceMode != rule.resourceMode) { return false; }
|
||||
if (grade != rule.grade) { return false; }
|
||||
if (Double.compare(rule.count, count) != 0) { return false; }
|
||||
if (intervalSec != rule.intervalSec) { return false; }
|
||||
if (controlBehavior != rule.controlBehavior) { return false; }
|
||||
if (burst != rule.burst) { return false; }
|
||||
if (maxQueueingTimeoutMs != rule.maxQueueingTimeoutMs) { return false; }
|
||||
if (!Objects.equals(resource, rule.resource)) { return false; }
|
||||
return Objects.equals(paramItem, rule.paramItem);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result;
|
||||
long temp;
|
||||
result = resource != null ? resource.hashCode() : 0;
|
||||
result = 31 * result + resourceMode;
|
||||
result = 31 * result + grade;
|
||||
temp = Double.doubleToLongBits(count);
|
||||
result = 31 * result + (int)(temp ^ (temp >>> 32));
|
||||
result = 31 * result + (int)(intervalSec ^ (intervalSec >>> 32));
|
||||
result = 31 * result + controlBehavior;
|
||||
result = 31 * result + burst;
|
||||
result = 31 * result + maxQueueingTimeoutMs;
|
||||
result = 31 * result + (paramItem != null ? paramItem.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GatewayFlowRule{" +
|
||||
"resource='" + resource + '\'' +
|
||||
", resourceMode=" + resourceMode +
|
||||
", grade=" + grade +
|
||||
", count=" + count +
|
||||
", intervalSec=" + intervalSec +
|
||||
", controlBehavior=" + controlBehavior +
|
||||
", burst=" + burst +
|
||||
", maxQueueingTimeoutMs=" + maxQueueingTimeoutMs +
|
||||
", paramItem=" + paramItem +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public class GatewayParamFlowItem {
|
||||
|
||||
/**
|
||||
* Should be set when applying to parameter flow rules.
|
||||
*/
|
||||
private Integer index;
|
||||
|
||||
/**
|
||||
* Strategy for parsing item (e.g. client IP, arbitrary headers and URL parameters).
|
||||
*/
|
||||
private int parseStrategy;
|
||||
/**
|
||||
* Field to get (only required for arbitrary headers or URL parameters mode).
|
||||
*/
|
||||
private String fieldName;
|
||||
/**
|
||||
* Matching pattern. If not set, all values will be kept in LRU map.
|
||||
*/
|
||||
private String pattern;
|
||||
/**
|
||||
* Matching strategy for item value.
|
||||
*/
|
||||
private int matchStrategy = SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT;
|
||||
|
||||
public Integer getIndex() {
|
||||
return index;
|
||||
}
|
||||
|
||||
GatewayParamFlowItem setIndex(Integer index) {
|
||||
this.index = index;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getParseStrategy() {
|
||||
return parseStrategy;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setParseStrategy(int parseStrategy) {
|
||||
this.parseStrategy = parseStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getFieldName() {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setFieldName(String fieldName) {
|
||||
this.fieldName = fieldName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPattern() {
|
||||
return pattern;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setPattern(String pattern) {
|
||||
this.pattern = pattern;
|
||||
return this;
|
||||
}
|
||||
|
||||
public int getMatchStrategy() {
|
||||
return matchStrategy;
|
||||
}
|
||||
|
||||
public GatewayParamFlowItem setMatchStrategy(int matchStrategy) {
|
||||
this.matchStrategy = matchStrategy;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GatewayParamFlowItem{" +
|
||||
"index=" + index +
|
||||
", parseStrategy=" + parseStrategy +
|
||||
", fieldName='" + fieldName + '\'' +
|
||||
", pattern='" + pattern + '\'' +
|
||||
", matchStrategy=" + matchStrategy +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
final class GatewayRuleConverter {
|
||||
|
||||
static FlowRule toFlowRule(/*@Valid*/ GatewayFlowRule rule) {
|
||||
return new FlowRule(rule.getResource())
|
||||
.setControlBehavior(rule.getControlBehavior())
|
||||
.setCount(rule.getCount())
|
||||
.setGrade(rule.getGrade())
|
||||
.setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs());
|
||||
}
|
||||
|
||||
static ParamFlowItem generateNonMatchPassParamItem() {
|
||||
return new ParamFlowItem().setClassType(String.class.getName())
|
||||
.setCount(1000_0000)
|
||||
.setObject(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
}
|
||||
|
||||
static ParamFlowItem generateNonMatchBlockParamItem() {
|
||||
return new ParamFlowItem().setClassType(String.class.getName())
|
||||
.setCount(0)
|
||||
.setObject(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
}
|
||||
|
||||
static ParamFlowRule applyNonParamToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
|
||||
return new ParamFlowRule(gatewayRule.getResource())
|
||||
.setCount(gatewayRule.getCount())
|
||||
.setGrade(gatewayRule.getGrade())
|
||||
.setDurationInSec(gatewayRule.getIntervalSec())
|
||||
.setBurstCount(gatewayRule.getBurst())
|
||||
.setControlBehavior(gatewayRule.getControlBehavior())
|
||||
.setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
|
||||
.setParamIdx(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gateway rule to parameter flow rule, then apply the generated
|
||||
* parameter index to {@link GatewayParamFlowItem} of the rule.
|
||||
*
|
||||
* @param gatewayRule a valid gateway rule that should contain valid parameter items
|
||||
* @param idx generated parameter index (callers should guarantee it's unique and incremental)
|
||||
* @return converted parameter flow rule
|
||||
*/
|
||||
static ParamFlowRule applyToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
|
||||
ParamFlowRule paramRule = new ParamFlowRule(gatewayRule.getResource())
|
||||
.setCount(gatewayRule.getCount())
|
||||
.setGrade(gatewayRule.getGrade())
|
||||
.setDurationInSec(gatewayRule.getIntervalSec())
|
||||
.setBurstCount(gatewayRule.getBurst())
|
||||
.setControlBehavior(gatewayRule.getControlBehavior())
|
||||
.setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
|
||||
.setParamIdx(idx);
|
||||
GatewayParamFlowItem gatewayItem = gatewayRule.getParamItem();
|
||||
// Apply the current idx to gateway rule item.
|
||||
gatewayItem.setIndex(idx);
|
||||
// Apply for pattern-based parameters.
|
||||
String valuePattern = gatewayItem.getPattern();
|
||||
if (valuePattern != null) {
|
||||
paramRule.getParamFlowItemList().add(generateNonMatchPassParamItem());
|
||||
}
|
||||
return paramRule;
|
||||
}
|
||||
|
||||
private GatewayRuleConverter() {}
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayRegexCache;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
||||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.0
|
||||
*/
|
||||
public final class GatewayRuleManager {
|
||||
|
||||
/**
|
||||
* Gateway flow rule map: (resource, [rules...])
|
||||
*/
|
||||
private static final Map<String, Set<GatewayFlowRule>> GATEWAY_RULE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<String, List<ParamFlowRule>> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
|
||||
private static final Set<Integer> FIELD_REQUIRED_SET = new HashSet<>(
|
||||
Arrays.asList(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM,
|
||||
SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER,
|
||||
SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
|
||||
);
|
||||
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
||||
static {
|
||||
currentProperty.addListener(LISTENER);
|
||||
}
|
||||
|
||||
private GatewayRuleManager() {
|
||||
}
|
||||
|
||||
public static void register2Property(SentinelProperty<Set<GatewayFlowRule>> property) {
|
||||
AssertUtil.notNull(property, "property cannot be null");
|
||||
synchronized (LISTENER) {
|
||||
RecordLog.info("[GatewayRuleManager] Registering new property to gateway flow rule manager");
|
||||
currentProperty.removeListener(LISTENER);
|
||||
property.addListener(LISTENER);
|
||||
currentProperty = property;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all provided gateway rules into memory, while
|
||||
* previous rules will be replaced.
|
||||
*
|
||||
* @param rules rule set
|
||||
* @return true if updated, otherwise false
|
||||
*/
|
||||
public static boolean loadRules(Set<GatewayFlowRule> rules) {
|
||||
return currentProperty.updateValue(rules);
|
||||
}
|
||||
|
||||
public static Set<GatewayFlowRule> getRules() {
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
for (Set<GatewayFlowRule> ruleSet : GATEWAY_RULE_MAP.values()) {
|
||||
rules.addAll(ruleSet);
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) {
|
||||
if (StringUtil.isBlank(resourceName)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<GatewayFlowRule> set = GATEWAY_RULE_MAP.get(resourceName);
|
||||
if (set == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return new HashSet<>(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get all converted parameter rules.</p>
|
||||
* <p>Note: caller SHOULD NOT modify the list and rules.</p>
|
||||
*
|
||||
* @param resourceName valid resource name
|
||||
* @return converted parameter rules
|
||||
*/
|
||||
public static List<ParamFlowRule> getConvertedParamRules(String resourceName) {
|
||||
if (StringUtil.isBlank(resourceName)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return CONVERTED_PARAM_RULE_MAP.get(resourceName);
|
||||
}
|
||||
|
||||
public static boolean isValidRule(GatewayFlowRule rule) {
|
||||
if (rule == null || StringUtil.isBlank(rule.getResource()) || rule.getResourceMode() < 0
|
||||
|| rule.getGrade() < 0 || rule.getCount() < 0 || rule.getBurst() < 0 || rule.getControlBehavior() < 0) {
|
||||
return false;
|
||||
}
|
||||
if (rule.getGrade() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
|
||||
&& rule.getMaxQueueingTimeoutMs() < 0) {
|
||||
return false;
|
||||
}
|
||||
if (rule.getIntervalSec() <= 0) {
|
||||
return false;
|
||||
}
|
||||
GatewayParamFlowItem item = rule.getParamItem();
|
||||
if (item != null) {
|
||||
return isValidParamItem(item);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isValidParamItem(/*@NonNull*/ GatewayParamFlowItem item) {
|
||||
if (item.getParseStrategy() < 0) {
|
||||
return false;
|
||||
}
|
||||
// Check required field name for item types.
|
||||
if (FIELD_REQUIRED_SET.contains(item.getParseStrategy()) && StringUtil.isBlank(item.getFieldName())) {
|
||||
return false;
|
||||
}
|
||||
return StringUtil.isEmpty(item.getPattern()) || item.getMatchStrategy() >= 0;
|
||||
}
|
||||
|
||||
private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> {
|
||||
|
||||
@Override
|
||||
public void configUpdate(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: {}", GATEWAY_RULE_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: {}", GATEWAY_RULE_MAP);
|
||||
}
|
||||
|
||||
private int getIdxInternal(Map<String, Integer> idxMap, String resourceName) {
|
||||
// Prepare index map.
|
||||
if (!idxMap.containsKey(resourceName)) {
|
||||
idxMap.put(resourceName, 0);
|
||||
}
|
||||
return idxMap.get(resourceName);
|
||||
}
|
||||
|
||||
private void cacheRegexPattern(/*@NonNull*/ GatewayParamFlowItem item) {
|
||||
String pattern = item.getPattern();
|
||||
if (StringUtil.isNotEmpty(pattern) &&
|
||||
item.getMatchStrategy() == SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX) {
|
||||
if (GatewayRegexCache.getRegexPattern(pattern) == null) {
|
||||
GatewayRegexCache.addRegexPattern(pattern);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) {
|
||||
if (conf == null || conf.isEmpty()) {
|
||||
applyToConvertedParamMap(new HashSet<ParamFlowRule>());
|
||||
GATEWAY_RULE_MAP.clear();
|
||||
return;
|
||||
}
|
||||
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
|
||||
Map<String, Integer> idxMap = new HashMap<>();
|
||||
Set<ParamFlowRule> paramFlowRules = new HashSet<>();
|
||||
Map<String, List<GatewayFlowRule>> noParamMap = new HashMap<>();
|
||||
|
||||
for (GatewayFlowRule rule : conf) {
|
||||
if (!isValidRule(rule)) {
|
||||
RecordLog.warn("[GatewayRuleManager] Ignoring invalid rule when loading new rules: " + rule);
|
||||
continue;
|
||||
}
|
||||
String resourceName = rule.getResource();
|
||||
if (rule.getParamItem() == null) {
|
||||
// Cache the rules with no parameter config, then skip.
|
||||
List<GatewayFlowRule> noParamList = noParamMap.get(resourceName);
|
||||
if (noParamList == null) {
|
||||
noParamList = new ArrayList<>();
|
||||
noParamMap.put(resourceName, noParamList);
|
||||
}
|
||||
noParamList.add(rule);
|
||||
} else {
|
||||
int idx = getIdxInternal(idxMap, resourceName);
|
||||
// Convert to parameter flow rule.
|
||||
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
|
||||
idxMap.put(rule.getResource(), idx + 1);
|
||||
}
|
||||
cacheRegexPattern(rule.getParamItem());
|
||||
}
|
||||
// Apply to the gateway rule map.
|
||||
Set<GatewayFlowRule> ruleSet = gatewayRuleMap.get(resourceName);
|
||||
if (ruleSet == null) {
|
||||
ruleSet = new HashSet<>();
|
||||
gatewayRuleMap.put(resourceName, ruleSet);
|
||||
}
|
||||
ruleSet.add(rule);
|
||||
}
|
||||
// Handle non-param mode rules.
|
||||
for (Map.Entry<String, List<GatewayFlowRule>> e : noParamMap.entrySet()) {
|
||||
List<GatewayFlowRule> rules = e.getValue();
|
||||
if (rules == null || rules.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (GatewayFlowRule rule : rules) {
|
||||
int idx = getIdxInternal(idxMap, e.getKey());
|
||||
// Always use the same index (the last position).
|
||||
paramFlowRules.add(GatewayRuleConverter.applyNonParamToParamRule(rule, idx));
|
||||
}
|
||||
}
|
||||
|
||||
applyToConvertedParamMap(paramFlowRules);
|
||||
|
||||
GATEWAY_RULE_MAP.clear();
|
||||
GATEWAY_RULE_MAP.putAll(gatewayRuleMap);
|
||||
}
|
||||
|
||||
private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
|
||||
Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
|
||||
new ArrayList<>(paramFlowRules));
|
||||
if (newRuleMap == null || newRuleMap.isEmpty()) {
|
||||
// No parameter flow rules, so clear all the metrics.
|
||||
for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
|
||||
ParameterMetricStorage.clearParamMetricForResource(resource);
|
||||
}
|
||||
RecordLog.info("[GatewayRuleManager] No gateway rules, clearing parameter metrics of previous rules");
|
||||
CONVERTED_PARAM_RULE_MAP.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear unused parameter metrics.
|
||||
for (Map.Entry<String, List<ParamFlowRule>> entry : CONVERTED_PARAM_RULE_MAP.entrySet()) {
|
||||
String resource = entry.getKey();
|
||||
if (!newRuleMap.containsKey(resource)) {
|
||||
ParameterMetricStorage.clearParamMetricForResource(resource);
|
||||
continue;
|
||||
}
|
||||
List<ParamFlowRule> newRuleList = newRuleMap.get(resource);
|
||||
List<ParamFlowRule> oldRuleList = new ArrayList<>(entry.getValue());
|
||||
oldRuleList.removeAll(newRuleList);
|
||||
for (ParamFlowRule rule : oldRuleList) {
|
||||
ParameterMetric metric = ParameterMetricStorage.getParamMetricForResource(resource);
|
||||
if (null != metric) {
|
||||
metric.clearForRule(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply to converted rule map.
|
||||
CONVERTED_PARAM_RULE_MAP.clear();
|
||||
CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap);
|
||||
|
||||
RecordLog.info("[GatewayRuleManager] Converted internal param rules: {}", CONVERTED_PARAM_RULE_MAP);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.slot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
|
||||
import com.alibaba.csp.sentinel.spi.Spi;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*/
|
||||
@Spi(order = -4000)
|
||||
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
public void entry(Context context, ResourceWrapper resource, DefaultNode node, int count,
|
||||
boolean prioritized, Object... args) throws Throwable {
|
||||
checkGatewayParamFlow(resource, count, args);
|
||||
|
||||
fireEntry(context, resource, node, count, prioritized, args);
|
||||
}
|
||||
|
||||
private void checkGatewayParamFlow(ResourceWrapper resourceWrapper, int count, Object... args)
|
||||
throws BlockException {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ParamFlowRule> rules = GatewayRuleManager.getConvertedParamRules(resourceWrapper.getName());
|
||||
if (rules == null || rules.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ParamFlowRule rule : rules) {
|
||||
// Initialize the parameter metrics.
|
||||
ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule);
|
||||
|
||||
if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) {
|
||||
String triggeredParam = "";
|
||||
if (args.length > rule.getParamIdx()) {
|
||||
Object value = args[rule.getParamIdx()];
|
||||
triggeredParam = String.valueOf(value);
|
||||
}
|
||||
throw new ParamFlowException(resourceWrapper.getName(), triggeredParam, rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
|
||||
fireExit(context, resourceWrapper, count, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.slot;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*
|
||||
* @deprecated since 1.7.2, we can use @Spi(order = -4000) to adjust the order of {@link GatewayFlowSlot},
|
||||
* this class is reserved for compatibility with older versions.
|
||||
*
|
||||
* @see GatewayFlowSlot
|
||||
* @see DefaultSlotChainBuilder
|
||||
*/
|
||||
@Deprecated
|
||||
public class GatewaySlotChainBuilder extends DefaultSlotChainBuilder {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayApiDefinitionGroupCommandHandler
|
||||
com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayRuleCommandHandler
|
||||
com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayApiDefinitionGroupCommandHandler
|
||||
com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayRuleCommandHandler
|
||||
@@ -0,0 +1 @@
|
||||
com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewayFlowSlot
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.api;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayApiDefinitionManagerTest {
|
||||
|
||||
@Test
|
||||
public void testIsValidApi() {
|
||||
ApiDefinition bad1 = new ApiDefinition();
|
||||
ApiDefinition bad2 = new ApiDefinition("foo");
|
||||
ApiDefinition good1 = new ApiDefinition("foo")
|
||||
.setPredicateItems(Collections.<ApiPredicateItem>singleton(new ApiPathPredicateItem()
|
||||
.setPattern("/abc")
|
||||
));
|
||||
|
||||
assertFalse(GatewayApiDefinitionManager.isValidApi(bad1));
|
||||
assertFalse(GatewayApiDefinitionManager.isValidApi(bad2));
|
||||
assertTrue(GatewayApiDefinitionManager.isValidApi(good1));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.param;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class GatewayParamParserTest {
|
||||
|
||||
private final Predicate<GatewayFlowRule> routeIdPredicate = new Predicate<GatewayFlowRule>() {
|
||||
@Override
|
||||
public boolean test(GatewayFlowRule e) {
|
||||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID;
|
||||
}
|
||||
};
|
||||
private final Predicate<GatewayFlowRule> apiNamePredicate = new Predicate<GatewayFlowRule>() {
|
||||
@Override
|
||||
public boolean test(GatewayFlowRule e) {
|
||||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testParseParametersNoParamItem() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> parser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
String routeId1 = "my_test_route_A";
|
||||
rules.add(new GatewayFlowRule(routeId1)
|
||||
.setCount(5)
|
||||
.setIntervalSec(1)
|
||||
);
|
||||
rules.add(new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setControlBehavior(2)
|
||||
.setMaxQueueingTimeoutMs(1000)
|
||||
);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
Object[] params = parser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseParametersWithItems() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
final String routeId1 = "my_test_route_A";
|
||||
final String api1 = "my_test_route_B";
|
||||
final String headerName = "X-Sentinel-Flag";
|
||||
final String paramName = "p";
|
||||
final String cookieName = "myCookie";
|
||||
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setIntervalSec(10);
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId1)
|
||||
.setCount(2)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(2)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
GatewayFlowRule routeRule2 = new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setIntervalSec(1)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(600)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName(headerName)
|
||||
);
|
||||
GatewayFlowRule routeRule3 = new GatewayFlowRule(routeId1)
|
||||
.setCount(20)
|
||||
.setIntervalSec(1)
|
||||
.setBurst(5)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
);
|
||||
GatewayFlowRule routeRule4 = new GatewayFlowRule(routeId1)
|
||||
.setCount(120)
|
||||
.setIntervalSec(10)
|
||||
.setBurst(30)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST)
|
||||
);
|
||||
GatewayFlowRule routeRule5 = new GatewayFlowRule(routeId1)
|
||||
.setCount(50)
|
||||
.setIntervalSec(30)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE)
|
||||
.setFieldName(cookieName)
|
||||
);
|
||||
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1)
|
||||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
|
||||
.setCount(5)
|
||||
.setIntervalSec(1)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
);
|
||||
rules.add(routeRule1);
|
||||
rules.add(routeRule2);
|
||||
rules.add(routeRule3);
|
||||
rules.add(routeRule4);
|
||||
rules.add(routeRule5);
|
||||
rules.add(routeRuleNoParam);
|
||||
rules.add(apiRule1);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
final String expectedHost = "hello.test.sentinel";
|
||||
final String expectedAddress = "66.77.88.99";
|
||||
final String expectedHeaderValue1 = "Sentinel";
|
||||
final String expectedUrlParamValue1 = "17";
|
||||
final String expectedCookieValue1 = "Sentinel-Foo";
|
||||
|
||||
mockClientHostAddress(itemParser, expectedAddress);
|
||||
Map<String, String> expectedHeaders = new HashMap<String, String>() {{
|
||||
put(headerName, expectedHeaderValue1); put("Host", expectedHost);
|
||||
}};
|
||||
mockHeaders(itemParser, expectedHeaders);
|
||||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1);
|
||||
mockSingleCookie(itemParser, cookieName, expectedCookieValue1);
|
||||
|
||||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
// Param length should be 6 (5 with parameters, 1 normal flow with generated constant)
|
||||
assertThat(params.length).isEqualTo(6);
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress);
|
||||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1);
|
||||
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1);
|
||||
assertThat(params[routeRule4.getParamItem().getIndex()]).isEqualTo(expectedHost);
|
||||
assertThat(params[routeRule5.getParamItem().getIndex()]).isEqualTo(expectedCookieValue1);
|
||||
assertThat(params[params.length - 1]).isEqualTo(SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM);
|
||||
|
||||
assertThat(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero();
|
||||
|
||||
String expectedUrlParamValue2 = "fs";
|
||||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue2);
|
||||
params = paramParser.parseParameterFor(api1, request, apiNamePredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
assertThat(params[apiRule1.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseParametersWithEmptyItemPattern() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
final String routeId = "my_test_route_DS(*H";
|
||||
final String headerName = "X-Sentinel-Flag";
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId)
|
||||
.setCount(10)
|
||||
.setIntervalSec(2)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName(headerName)
|
||||
.setPattern("")
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
|
||||
);
|
||||
rules.add(routeRule1);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
mockSingleHeader(itemParser, headerName, "Sent1nel");
|
||||
Object[] params = paramParser.parseParameterFor(routeId, request, routeIdPredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
// Empty pattern should not take effect.
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo("Sent1nel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseParametersWithItemPatternMatching() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
final String routeId1 = "my_test_route_F&@";
|
||||
final String api1 = "my_test_route_E5K";
|
||||
final String headerName = "X-Sentinel-Flag";
|
||||
final String paramName = "p";
|
||||
|
||||
String nameEquals = "Wow";
|
||||
String nameContains = "warn";
|
||||
String valueRegex = "\\d+";
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setIntervalSec(1)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(600)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName(headerName)
|
||||
.setPattern(nameEquals)
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
|
||||
);
|
||||
GatewayFlowRule routeRule2 = new GatewayFlowRule(routeId1)
|
||||
.setCount(20)
|
||||
.setIntervalSec(1)
|
||||
.setBurst(5)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
.setPattern(nameContains)
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS)
|
||||
);
|
||||
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1)
|
||||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
|
||||
.setCount(5)
|
||||
.setIntervalSec(1)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
.setPattern(valueRegex)
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX)
|
||||
);
|
||||
rules.add(routeRule1);
|
||||
rules.add(routeRule2);
|
||||
rules.add(apiRule1);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
mockSingleHeader(itemParser, headerName, nameEquals);
|
||||
mockSingleUrlParam(itemParser, paramName, nameContains);
|
||||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
assertThat(params.length).isEqualTo(2);
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(nameEquals);
|
||||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(nameContains);
|
||||
|
||||
mockSingleHeader(itemParser, headerName, nameEquals + "_foo");
|
||||
mockSingleUrlParam(itemParser, paramName, nameContains + "_foo");
|
||||
params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
assertThat(params.length).isEqualTo(2);
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()])
|
||||
.isEqualTo(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
assertThat(params[routeRule2.getParamItem().getIndex()])
|
||||
.isEqualTo(nameContains + "_foo");
|
||||
|
||||
mockSingleHeader(itemParser, headerName, "foo");
|
||||
mockSingleUrlParam(itemParser, paramName, "foo");
|
||||
params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
assertThat(params.length).isEqualTo(2);
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()])
|
||||
.isEqualTo(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
assertThat(params[routeRule2.getParamItem().getIndex()])
|
||||
.isEqualTo(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
|
||||
mockSingleUrlParam(itemParser, paramName, "23");
|
||||
params = paramParser.parseParameterFor(api1, request, apiNamePredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
assertThat(params[apiRule1.getParamItem().getIndex()]).isEqualTo("23");
|
||||
|
||||
mockSingleUrlParam(itemParser, paramName, "some233");
|
||||
params = paramParser.parseParameterFor(api1, request, apiNamePredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
assertThat(params[apiRule1.getParamItem().getIndex()])
|
||||
.isEqualTo(SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM);
|
||||
}
|
||||
|
||||
private void mockClientHostAddress(/*@Mock*/ RequestItemParser parser, String address) {
|
||||
when(parser.getRemoteAddress(any())).thenReturn(address);
|
||||
}
|
||||
|
||||
private void mockHeaders(/*@Mock*/ RequestItemParser parser, Map<String, String> headerMap) {
|
||||
for (Map.Entry<String, String> e : headerMap.entrySet()) {
|
||||
when(parser.getHeader(any(), eq(e.getKey()))).thenReturn(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void mockUrlParams(/*@Mock*/ RequestItemParser parser, Map<String, String> paramMap) {
|
||||
for (Map.Entry<String, String> e : paramMap.entrySet()) {
|
||||
when(parser.getUrlParam(any(), eq(e.getKey()))).thenReturn(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void mockSingleUrlParam(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||
when(parser.getUrlParam(any(), eq(key))).thenReturn(value);
|
||||
}
|
||||
|
||||
private void mockSingleHeader(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||
when(parser.getHeader(any(), eq(key))).thenReturn(value);
|
||||
}
|
||||
|
||||
private void mockSingleCookie(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||
when(parser.getCookieValue(any(), eq(key))).thenReturn(value);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
GatewayRegexCache.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
GatewayRegexCache.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.param;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayRegexCacheTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
GatewayRegexCache.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GatewayRegexCache.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddAndGetRegexPattern() {
|
||||
// Test for invalid pattern.
|
||||
assertThat(GatewayRegexCache.addRegexPattern("\\")).isFalse();
|
||||
assertThat(GatewayRegexCache.addRegexPattern(null)).isFalse();
|
||||
// Test for good pattern.
|
||||
String goodPattern = "\\d+";
|
||||
assertThat(GatewayRegexCache.addRegexPattern(goodPattern)).isTrue();
|
||||
assertThat(GatewayRegexCache.getRegexPattern(goodPattern)).isNotNull();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayRuleConverterTest {
|
||||
|
||||
@Test
|
||||
public void testConvertToFlowRule() {
|
||||
GatewayFlowRule rule = new GatewayFlowRule("routeId1")
|
||||
.setCount(10)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(1000);
|
||||
FlowRule flowRule = GatewayRuleConverter.toFlowRule(rule);
|
||||
assertEquals(rule.getResource(), flowRule.getResource());
|
||||
assertEquals(rule.getCount(), flowRule.getCount(), 0.01);
|
||||
assertEquals(rule.getControlBehavior(), flowRule.getControlBehavior());
|
||||
assertEquals(rule.getMaxQueueingTimeoutMs(), flowRule.getMaxQueueingTimeMs());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConvertAndApplyToParamRule() {
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule("routeId1")
|
||||
.setCount(2)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(2)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
int idx = 1;
|
||||
ParamFlowRule paramRule = GatewayRuleConverter.applyToParamRule(routeRule1, idx);
|
||||
assertEquals(routeRule1.getResource(), paramRule.getResource());
|
||||
assertEquals(routeRule1.getCount(), paramRule.getCount(), 0.01);
|
||||
assertEquals(routeRule1.getControlBehavior(), paramRule.getControlBehavior());
|
||||
assertEquals(routeRule1.getIntervalSec(), paramRule.getDurationInSec());
|
||||
assertEquals(routeRule1.getBurst(), paramRule.getBurstCount());
|
||||
assertEquals(idx, (int)paramRule.getParamIdx());
|
||||
assertEquals(idx, (int)routeRule1.getParamItem().getIndex());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright 1999-2019 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class GatewayRuleManagerTest {
|
||||
|
||||
@Test
|
||||
public void testLoadAndGetGatewayRules() {
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
String ahasRoute = "ahas_route";
|
||||
GatewayFlowRule rule1 = new GatewayFlowRule(ahasRoute)
|
||||
.setCount(500)
|
||||
.setIntervalSec(1);
|
||||
GatewayFlowRule rule2 = new GatewayFlowRule(ahasRoute)
|
||||
.setCount(20)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(5)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
GatewayFlowRule rule3 = new GatewayFlowRule("complex_route_ZZZ")
|
||||
.setCount(10)
|
||||
.setIntervalSec(1)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(600)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName("X-Sentinel-Flag")
|
||||
);
|
||||
rules.add(rule1);
|
||||
rules.add(rule2);
|
||||
rules.add(rule3);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
List<ParamFlowRule> convertedRules = GatewayRuleManager.getConvertedParamRules(ahasRoute);
|
||||
assertNotNull(convertedRules);
|
||||
assertEquals(0, (int)rule2.getParamItem().getIndex());
|
||||
assertEquals(0, (int)rule3.getParamItem().getIndex());
|
||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
|
||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsValidRule() {
|
||||
GatewayFlowRule bad1 = new GatewayFlowRule();
|
||||
GatewayFlowRule bad2 = new GatewayFlowRule("abc")
|
||||
.setIntervalSec(0);
|
||||
GatewayFlowRule bad3 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM));
|
||||
GatewayFlowRule bad4 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName("p")
|
||||
.setPattern("def")
|
||||
.setMatchStrategy(-1)
|
||||
);
|
||||
GatewayFlowRule good1 = new GatewayFlowRule("abc");
|
||||
GatewayFlowRule good2 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem().setParseStrategy(0));
|
||||
GatewayFlowRule good3 = new GatewayFlowRule("abc")
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setMatchStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName("Origin")
|
||||
.setPattern("def"));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad1));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad2));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad3));
|
||||
assertFalse(GatewayRuleManager.isValidRule(bad4));
|
||||
|
||||
assertTrue(GatewayRuleManager.isValidRule(good1));
|
||||
assertTrue(GatewayRuleManager.isValidRule(good2));
|
||||
assertTrue(GatewayRuleManager.isValidRule(good3));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
}
|
||||
72
sentinel/sentinel-adapter/sentinel-dubbo-adapter/README.md
Normal file
72
sentinel/sentinel-adapter/sentinel-dubbo-adapter/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Sentinel Dubbo Adapter
|
||||
|
||||
> Note: 中文文档请见[此处](https://github.com/alibaba/Sentinel/wiki/主流框架的适配#dubbo)。
|
||||
|
||||
Sentinel Dubbo Adapter provides service consumer filter and provider filter
|
||||
for [Dubbo](https://dubbo.apache.org/en-us/) services.
|
||||
|
||||
**Note: This adapter only supports legacy Dubbo 2.6.x version and below.**
|
||||
For new Apache Dubbo 2.7.x or above version, please use `sentinel-apache-dubbo-adapter` module instead.
|
||||
|
||||
To use Sentinel Dubbo Adapter, you can simply add the following dependency to your `pom.xml`:
|
||||
|
||||
```xml
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-dubbo-adapter</artifactId>
|
||||
<version>x.y.z</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
The Sentinel filters are **enabled by default**. Once you add the dependency,
|
||||
the Dubbo services and methods will become protected resources in Sentinel,
|
||||
which can leverage Sentinel's flow control and guard ability when rules are configured.
|
||||
Demos can be found in [sentinel-demo-dubbo](https://github.com/alibaba/Sentinel/tree/master/sentinel-demo/sentinel-demo-dubbo).
|
||||
|
||||
If you don't want the filters enabled, you can manually disable them. For example:
|
||||
|
||||
```xml
|
||||
<dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
|
||||
<dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
```
|
||||
|
||||
For more details of Dubbo filter, see [here](http://dubbo.apache.org/en-us/docs/dev/impls/filter.html).
|
||||
|
||||
## Dubbo resources
|
||||
|
||||
The resource for Dubbo services has two granularities: service interface and service method.
|
||||
|
||||
- Service interface:resourceName format is `interfaceName`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService`
|
||||
- Service method:resourceName format is `interfaceName:methodSignature`,e.g. `com.alibaba.csp.sentinel.demo.dubbo.FooService:sayHello(java.lang.String)`
|
||||
|
||||
## Flow control based on caller
|
||||
|
||||
In many circumstances, it's also significant to control traffic flow based on the **caller**.
|
||||
For example, assuming that there are two services A and B, both of them initiate remote call requests to the service provider.
|
||||
If we want to limit the calls from service B only, we can set the `limitApp` of flow rule as the identifier of service B (e.g. service name).
|
||||
|
||||
Sentinel Dubbo Adapter will automatically resolve the Dubbo consumer's *application name* as the caller's name (`origin`),
|
||||
and will bring the caller's name when doing resource protection.
|
||||
If `limitApp` of flow rules is not configured (`default`), flow control will take effects on all callers.
|
||||
If `limitApp` of a flow rule is configured with a caller, then the corresponding flow rule will only take effect on the specific caller.
|
||||
|
||||
> Note: Dubbo consumer does not provide its Dubbo application name when doing RPC,
|
||||
> so developers should manually put the application name into *attachment* at consumer side,
|
||||
> then extract it at provider side. Sentinel Dubbo Adapter has implemented a filter (`DubboAppContextFilter`)
|
||||
> where consumer can carry application name information to provider automatically.
|
||||
> If the consumer does not use Sentinel Dubbo Adapter but requires flow control based on caller,
|
||||
> developers can manually put the application name into attachment with the key `dubboApplication`.
|
||||
>
|
||||
> Since 1.8.0, the adapter provides support for customizing origin parsing logic. You may register your own `DubboOriginParser`
|
||||
> implementation to `DubboAdapterGlobalConfig`.
|
||||
|
||||
## Global fallback
|
||||
|
||||
Sentinel Dubbo Adapter supports global fallback configuration.
|
||||
The global fallback will handle exceptions and give replacement result when blocked by
|
||||
flow control, degrade or system load protection. You can implement your own `DubboFallback` interface
|
||||
and then register to `DubboAdapterGlobalConfig`.
|
||||
If no fallback is configured, Sentinel will wrap the `BlockException` as the fallback result.
|
||||
|
||||
Besides, we can also leverage [Dubbo mock mechanism](http://dubbo.apache.org/en-us/docs/user/demos/local-mock.html) to provide fallback implementation of degraded Dubbo services.
|
||||
48
sentinel/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
Normal file
48
sentinel/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
Normal file
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
|
||||
<parent>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-adapter</artifactId>
|
||||
<version>1.8.3</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>sentinel-dubbo-adapter</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<dubbo.version>2.6.6</dubbo.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.csp</groupId>
|
||||
<artifactId>sentinel-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>dubbo</artifactId>
|
||||
<version>${dubbo.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba</groupId>
|
||||
<artifactId>fastjson</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
import com.alibaba.dubbo.rpc.Filter;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* @author leyou
|
||||
*/
|
||||
abstract class AbstractDubboFilter implements Filter {
|
||||
|
||||
protected String getMethodResourceName(Invoker<?> invoker, Invocation invocation) {
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
buf.append(invoker.getInterface().getName())
|
||||
.append(":")
|
||||
.append(invocation.getMethodName())
|
||||
.append("(");
|
||||
boolean isFirst = true;
|
||||
for (Class<?> clazz : invocation.getParameterTypes()) {
|
||||
if (!isFirst) {
|
||||
buf.append(",");
|
||||
}
|
||||
buf.append(clazz.getName());
|
||||
isFirst = false;
|
||||
}
|
||||
buf.append(")");
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
protected String getMethodResourceName(Invoker<?> invoker, Invocation invocation, String prefix) {
|
||||
if (StringUtil.isBlank(prefix)) {
|
||||
return getMethodResourceName(invoker, invocation);
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(64);
|
||||
return buf.append(prefix)
|
||||
.append(getMethodResourceName(invoker, invocation))
|
||||
.toString();
|
||||
}
|
||||
|
||||
protected String getInterfaceName(Invoker<?> invoker) {
|
||||
return invoker.getInterface().getName();
|
||||
}
|
||||
|
||||
protected String getInterfaceName(Invoker<?> invoker, String prefix) {
|
||||
if (StringUtil.isBlank(prefix)) {
|
||||
return getInterfaceName(invoker);
|
||||
}
|
||||
return prefix + getInterfaceName(invoker);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DefaultDubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.fallback.DubboFallback;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DefaultDubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.origin.DubboOriginParser;
|
||||
import com.alibaba.csp.sentinel.config.SentinelConfig;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
/**
|
||||
* <p>Global config and callback registry of Dubbo legacy adapter.</p>
|
||||
*
|
||||
* @author lianglin
|
||||
* @author Eric Zhao
|
||||
* @since 1.7.0
|
||||
*/
|
||||
public final class DubboAdapterGlobalConfig {
|
||||
|
||||
private static final String TRUE_STR = "true";
|
||||
|
||||
public static final String DUBBO_RES_NAME_WITH_PREFIX_KEY = "csp.sentinel.dubbo.resource.use.prefix";
|
||||
public static final String DUBBO_PROVIDER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.provider.prefix";
|
||||
public static final String DUBBO_CONSUMER_RES_NAME_PREFIX_KEY = "csp.sentinel.dubbo.resource.consumer.prefix";
|
||||
|
||||
private static final String DEFAULT_DUBBO_PROVIDER_PREFIX = "dubbo:provider:";
|
||||
private static final String DEFAULT_DUBBO_CONSUMER_PREFIX = "dubbo:consumer:";
|
||||
|
||||
private static volatile DubboFallback consumerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboFallback providerFallback = new DefaultDubboFallback();
|
||||
private static volatile DubboOriginParser originParser = new DefaultDubboOriginParser();
|
||||
|
||||
public static boolean isUsePrefix() {
|
||||
return TRUE_STR.equalsIgnoreCase(SentinelConfig.getConfig(DUBBO_RES_NAME_WITH_PREFIX_KEY));
|
||||
}
|
||||
|
||||
public static String getDubboProviderPrefix() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_PROVIDER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_PROVIDER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getDubboConsumerPrefix() {
|
||||
if (isUsePrefix()) {
|
||||
String config = SentinelConfig.getConfig(DUBBO_CONSUMER_RES_NAME_PREFIX_KEY);
|
||||
return StringUtil.isNotBlank(config) ? config : DEFAULT_DUBBO_CONSUMER_PREFIX;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return consumerFallback;
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
AssertUtil.notNull(consumerFallback, "consumerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.consumerFallback = consumerFallback;
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return providerFallback;
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
AssertUtil.notNull(providerFallback, "providerFallback cannot be null");
|
||||
DubboAdapterGlobalConfig.providerFallback = providerFallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @return the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static DubboOriginParser getOriginParser() {
|
||||
return originParser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the origin parser of Dubbo adapter.
|
||||
*
|
||||
* @param originParser the origin parser
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public static void setOriginParser(DubboOriginParser originParser) {
|
||||
AssertUtil.notNull(originParser, "originParser cannot be null");
|
||||
DubboAdapterGlobalConfig.originParser = originParser;
|
||||
}
|
||||
|
||||
private DubboAdapterGlobalConfig() {}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.dubbo.common.Constants;
|
||||
import com.alibaba.dubbo.common.extension.Activate;
|
||||
import com.alibaba.dubbo.rpc.Filter;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
import com.alibaba.dubbo.rpc.Result;
|
||||
import com.alibaba.dubbo.rpc.RpcContext;
|
||||
import com.alibaba.dubbo.rpc.RpcException;
|
||||
|
||||
import static com.alibaba.dubbo.common.Constants.CONSUMER;
|
||||
|
||||
/**
|
||||
* Puts current consumer's application name in the attachment of each invocation.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class DubboAppContextFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
String application = invoker.getUrl().getParameter(Constants.APPLICATION_KEY);
|
||||
if (application != null) {
|
||||
RpcContext.getContext().setAttachment(DubboUtils.DUBBO_APPLICATION_KEY, application);
|
||||
}
|
||||
return invoker.invoke(invocation);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public final class DubboUtils {
|
||||
|
||||
public static final String DUBBO_APPLICATION_KEY = "dubboApplication";
|
||||
|
||||
public static String getApplication(Invocation invocation, String defaultValue) {
|
||||
if (invocation == null || invocation.getAttachments() == null) {
|
||||
throw new IllegalArgumentException("Bad invocation instance");
|
||||
}
|
||||
return invocation.getAttachment(DUBBO_APPLICATION_KEY, defaultValue);
|
||||
}
|
||||
|
||||
private DubboUtils() {}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.ResourceTypeConstants;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.dubbo.common.extension.Activate;
|
||||
import com.alibaba.dubbo.rpc.Filter;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
import com.alibaba.dubbo.rpc.Result;
|
||||
import com.alibaba.dubbo.rpc.RpcException;
|
||||
|
||||
import static com.alibaba.dubbo.common.Constants.CONSUMER;
|
||||
|
||||
/**
|
||||
* <p>Dubbo service consumer filter for Sentinel. Auto activated by default.</p>
|
||||
*
|
||||
* If you want to disable the consumer filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:consumer filter="-sentinel.dubbo.consumer.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author leyou
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = CONSUMER)
|
||||
public class SentinelDubboConsumerFilter extends AbstractDubboFilter implements Filter {
|
||||
|
||||
public SentinelDubboConsumerFilter() {
|
||||
RecordLog.info("Sentinel Dubbo consumer filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
try {
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboConsumerPrefix();
|
||||
String interfaceResourceName = getInterfaceName(invoker, prefix);
|
||||
String methodResourceName = getMethodResourceName(invoker, invocation, prefix);
|
||||
interfaceEntry = SphU.entry(interfaceResourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
|
||||
EntryType.OUT, invocation.getArguments());
|
||||
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Throwable e = result.getException();
|
||||
// Record common exception.
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo;
|
||||
|
||||
import com.alibaba.csp.sentinel.Entry;
|
||||
import com.alibaba.csp.sentinel.EntryType;
|
||||
import com.alibaba.csp.sentinel.ResourceTypeConstants;
|
||||
import com.alibaba.csp.sentinel.SphU;
|
||||
import com.alibaba.csp.sentinel.Tracer;
|
||||
import com.alibaba.csp.sentinel.context.ContextUtil;
|
||||
import com.alibaba.csp.sentinel.log.RecordLog;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.dubbo.common.extension.Activate;
|
||||
import com.alibaba.dubbo.rpc.Filter;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
import com.alibaba.dubbo.rpc.Result;
|
||||
import com.alibaba.dubbo.rpc.RpcException;
|
||||
|
||||
import static com.alibaba.dubbo.common.Constants.PROVIDER;
|
||||
|
||||
/**
|
||||
* <p>Dubbo service provider filter for Sentinel. Auto activated by default.</p>
|
||||
*
|
||||
* If you want to disable the provider filter, you can configure:
|
||||
* <pre>
|
||||
* <dubbo:provider filter="-sentinel.dubbo.provider.filter"/>
|
||||
* </pre>
|
||||
*
|
||||
* @author leyou
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@Activate(group = PROVIDER)
|
||||
public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
|
||||
|
||||
public SentinelDubboProviderFilter() {
|
||||
RecordLog.info("Sentinel Dubbo provider filter initialized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
|
||||
// Get origin caller.
|
||||
String origin = DubboAdapterGlobalConfig.getOriginParser().parse(invoker, invocation);
|
||||
if (null == origin) {
|
||||
origin = "";
|
||||
}
|
||||
|
||||
Entry interfaceEntry = null;
|
||||
Entry methodEntry = null;
|
||||
try {
|
||||
String prefix = DubboAdapterGlobalConfig.getDubboProviderPrefix();
|
||||
String methodResourceName = getMethodResourceName(invoker, invocation, prefix);
|
||||
String interfaceName = getInterfaceName(invoker, prefix);
|
||||
ContextUtil.enter(methodResourceName, origin);
|
||||
interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
|
||||
methodEntry = SphU.entry(methodResourceName, ResourceTypeConstants.COMMON_RPC,
|
||||
EntryType.IN, invocation.getArguments());
|
||||
|
||||
Result result = invoker.invoke(invocation);
|
||||
if (result.hasException()) {
|
||||
Throwable e = result.getException();
|
||||
// Record common exception.
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
}
|
||||
return result;
|
||||
} catch (BlockException e) {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback().handle(invoker, invocation, e);
|
||||
} catch (RpcException e) {
|
||||
Tracer.traceEntry(e, interfaceEntry);
|
||||
Tracer.traceEntry(e, methodEntry);
|
||||
throw e;
|
||||
} finally {
|
||||
if (methodEntry != null) {
|
||||
methodEntry.exit(1, invocation.getArguments());
|
||||
}
|
||||
if (interfaceEntry != null) {
|
||||
interfaceEntry.exit();
|
||||
}
|
||||
ContextUtil.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
import com.alibaba.dubbo.rpc.Result;
|
||||
import com.alibaba.dubbo.rpc.RpcResult;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public class DefaultDubboFallback implements DubboFallback {
|
||||
|
||||
@Override
|
||||
public Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex) {
|
||||
// Just wrap the exception. edit by wzg923 2020/9/23
|
||||
RpcResult result = new RpcResult();
|
||||
result.setException(new SentinelRpcException(ex.toRuntimeException()));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
import com.alibaba.dubbo.rpc.Result;
|
||||
|
||||
/**
|
||||
* Fallback handler for Dubbo services.
|
||||
*
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
public interface DubboFallback {
|
||||
|
||||
/**
|
||||
* Handle the block exception and provide fallback result.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @param ex block exception
|
||||
* @return fallback result
|
||||
*/
|
||||
Result handle(Invoker<?> invoker, Invocation invocation, BlockException ex);
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 1999-2018 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.fallback;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboAdapterGlobalConfig;
|
||||
|
||||
/**
|
||||
* <p>Global fallback registry for Dubbo.</p>
|
||||
*
|
||||
* @author Eric Zhao
|
||||
* @deprecated use {@link DubboAdapterGlobalConfig} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class DubboFallbackRegistry {
|
||||
|
||||
public static DubboFallback getConsumerFallback() {
|
||||
return DubboAdapterGlobalConfig.getConsumerFallback();
|
||||
}
|
||||
|
||||
public static void setConsumerFallback(DubboFallback consumerFallback) {
|
||||
DubboAdapterGlobalConfig.setConsumerFallback(consumerFallback);
|
||||
}
|
||||
|
||||
public static DubboFallback getProviderFallback() {
|
||||
return DubboAdapterGlobalConfig.getProviderFallback();
|
||||
}
|
||||
|
||||
public static void setProviderFallback(DubboFallback providerFallback) {
|
||||
DubboAdapterGlobalConfig.setProviderFallback(providerFallback);
|
||||
}
|
||||
|
||||
private DubboFallbackRegistry() {}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.dubbo.DubboUtils;
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Default Dubbo origin parser.
|
||||
*
|
||||
* @author tiecheng
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public class DefaultDubboOriginParser implements DubboOriginParser {
|
||||
|
||||
@Override
|
||||
public String parse(Invoker<?> invoker, Invocation invocation) {
|
||||
return DubboUtils.getApplication(invocation, "");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 1999-2020 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.csp.sentinel.adapter.dubbo.origin;
|
||||
|
||||
import com.alibaba.dubbo.rpc.Invocation;
|
||||
import com.alibaba.dubbo.rpc.Invoker;
|
||||
|
||||
/**
|
||||
* Customized origin parser for Dubbo provider filter.
|
||||
*
|
||||
* @author tiecheng
|
||||
* @since 1.8.0
|
||||
*/
|
||||
public interface DubboOriginParser {
|
||||
|
||||
/**
|
||||
* Parses the origin (caller) from Dubbo invocation.
|
||||
*
|
||||
* @param invoker Dubbo invoker
|
||||
* @param invocation Dubbo invocation
|
||||
* @return the parsed origin
|
||||
*/
|
||||
String parse(Invoker<?> invoker, Invocation invocation);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
sentinel.dubbo.provider.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboProviderFilter
|
||||
sentinel.dubbo.consumer.filter=com.alibaba.csp.sentinel.adapter.dubbo.SentinelDubboConsumerFilter
|
||||
dubbo.application.context.name.filter=com.alibaba.csp.sentinel.adapter.dubbo.DubboAppContextFilter
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user