commit c92fea5d46f8c7968498c6e817757f64043cc43d
Author: Eric Zhao
Date: Mon Jul 23 15:51:05 2018 +0800
Welcome to the world, Sentinel
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100755
index 00000000..7da6d4b0
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,16 @@
+version: 2
+jobs:
+ build:
+ docker:
+ - image: circleci/openjdk:8-jdk
+
+ working_directory: ~/sentinel
+
+ environment:
+ MAVEN_OPTS: -Xmx3200m
+
+ steps:
+ - checkout
+
+ # Run tests
+ - run: mvn integration-test
\ No newline at end of file
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100755
index 00000000..f04e94d7
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,28 @@
+
+
+## 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?
+
+
+
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100755
index 00000000..6c009260
--- /dev/null
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,19 @@
+
+
+### Describe what this PR does / why we need it
+
+
+### Does this pull request fix one issue?
+
+
+
+### Describe how you did it
+
+
+### Describe how to verify it
+
+
+### Special notes for reviews
diff --git a/.gitignore b/.gitignore
new file mode 100755
index 00000000..84c94f69
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,27 @@
+# IntelliJ project files
+.idea/
+*.iml
+out
+gen
+
+# 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
diff --git a/.travis.yml b/.travis.yml
new file mode 100755
index 00000000..32dc91db
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,10 @@
+language: java
+
+sudo: required
+dist: trusty
+
+jdk:
+ - oraclejdk8
+
+after_success:
+ - bash <(curl -s https://codecov.io/bash)
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100755
index 00000000..ddd80669
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -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
+
+[homepage]: https://www.contributor-covenant.org
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100755
index 00000000..4618d955
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -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).
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100755
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/README.md b/README.md
new file mode 100755
index 00000000..0d52c69e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,131 @@
+# Sentinel: Sentinel of Your Application
+
+[![Travis Build Status](https://travis-ci.org/alibaba/Sentinel.svg?branch=master)](https://travis-ci.org/alibaba/Sentinel)
+[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
+[![Gitter](https://badges.gitter.im/alibaba/Sentinel.svg)](https://gitter.im/alibaba/Sentinel)
+
+## What Does It Do?
+
+As distributed systems become increasingly popular, the stability between services is becoming more important than ever before. Sentinel takes "flow" as breakthrough point, and works on multiple fields including **flow control**, **concurrency**, **circuit breaking** and **load protection**, to protect service stability.
+
+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 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, degrading unreliable downstream applications, etc.
+
+* **Integrated monitor module**:
+Sentinel also provides real-time monitoring function. You can see the runtime information of a single machine in real-time, and the summary runtime info of a cluster with less than 500 nodes.
+
+* **Easy extension point**:
+Sentinel provides easy-to-use extension points that allow you to quickly customize your logic, for example, custom rule management, adapting data sources, and so on.
+
+## Documentation
+
+See the [中文文档](https://github.com/alibaba/Sentinel/wiki/%E4%BB%8B%E7%BB%8D) for Chinese readme.
+
+See the [Wiki](https://github.com/alibaba/Sentinel/wiki) for full documentation, examples, operational details and other information.
+
+See the [Javadoc](https://github.com/alibaba/Sentinel/tree/master/doc) for the API.
+
+## 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.Download Library
+
+**Note:** Sentinel requires Java 6 or later.
+
+If your application is build in maven, just add the following code in pom.xml.
+
+```xml
+
+ com.alibaba.csp
+ sentinel-core
+ x.y.z
+
+```
+
+If not, you can download JAR in [Maven Center Repository](https://mvnrepository.com/artifact/com.alibaba.csp/sentinel-core).
+
+
+### 2.Define Resource
+
+Wrap code snippet via Sentinel API: `SphU.entry("RESOURCENAME")` and `entry.exit()`. In below example, it is `System.out.println("hello world");`:
+
+```java
+Entry entry = null;
+
+try {
+ entry = SphU.entry("HelloWorld");
+
+ // BIZ logic being protected
+ System.out.println("hello world");
+} catch (BlockException e) {
+ // handle block logic
+} finally {
+ // make sure that the exit() logic is called
+ if (entry != null) {
+ entry.exit();
+ }
+}
+```
+
+So far the code modification is done.
+
+### 3.Define Rules
+
+If we want to limit the access times of the resource, we can define rules. The following code defines a rule that limits access to the reource to 20 times per second at the maximum.
+
+```java
+List rules = new ArrayList();
+FlowRule rule = new FlowRule();
+rule.setResource("hello world");
+// set limit qps to 20
+rule.setCount(20);
+rules.add(rule);
+FlowRuleManager.loadRules(rules);
+```
+
+### 4. Check the Result
+
+After running the demo for a while, you can see the following records in `~/logs/csp/${appName}-metrics.log.xxx`.
+
+```
+|--timestamp-|------date time----|--resource-|p |block|s |e|rt
+1529998904000|2018-06-26 15:41:44|hello world|20|0 |20|0|0
+1529998905000|2018-06-26 15:41:45|hello world|20|5579 |20|0|728
+1529998906000|2018-06-26 15:41:46|hello world|20|15698|20|0|0
+1529998907000|2018-06-26 15:41:47|hello world|20|19262|20|0|0
+1529998908000|2018-06-26 15:41:48|hello world|20|19502|20|0|0
+1529998909000|2018-06-26 15:41:49|hello world|20|18386|20|0|0
+
+p stands for incoming request, block for intercepted by rules, success for success handled, e for exception, rt for average response time (ms)
+```
+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
+
+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. 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: sentinel@linux.alibaba.com
+
+## Contributing
+
+Contributions are always welcomed! Please see [CONTRIBUTING](./CONTRIBUTING.md) for detailed guidelines.
+
diff --git a/doc/image.gif b/doc/image.gif
new file mode 100755
index 00000000..51be223d
Binary files /dev/null and b/doc/image.gif differ
diff --git a/doc/image/nolockcirclearray.gif b/doc/image/nolockcirclearray.gif
new file mode 100755
index 00000000..1a65fc98
Binary files /dev/null and b/doc/image/nolockcirclearray.gif differ
diff --git a/doc/image/slots.gif b/doc/image/slots.gif
new file mode 100755
index 00000000..9d04b4c7
Binary files /dev/null and b/doc/image/slots.gif differ
diff --git a/pom.xml b/pom.xml
new file mode 100755
index 00000000..1351d8c0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,218 @@
+
+
+ 4.0.0
+
+ com.alibaba.csp
+ sentinel-parent
+ 0.1.0
+ pom
+
+ ${project.artifactId}
+ The parent project of Sentinel
+ https://github.com/alibaba/Sentinel
+
+
+ Apache License, Version 2.0
+ http://www.apache.org/licenses/LICENSE-2.0
+ repo
+
+
+
+ https://github.com/alibaba/Sentinel
+ scm:git:https://github.com/alibaba/Sentinel.git
+ scm:git:https://github.com/alibaba/Sentinel.git
+
+
+
+ The Sentinel Project Contributors
+ sentinel-dev@linux.alibaba.com
+ https://github.com/alibaba/Sentinel
+
+
+
+ Alibaba Group
+ https://github.com/alibaba
+
+
+ github
+ https://github.com/alibaba/Sentinel/issues
+
+
+
+
+ 1.2.47
+
+
+ 4.12
+ 2.18.0
+
+
+ UTF-8
+ 1.6
+ 1.6
+ UTF-8
+ 3.7.0
+ 3.0.1
+ 3.0.1
+ 1.6
+ 0.8.1
+
+
+
+ sentinel-core
+ sentinel-extension
+ sentinel-transport
+ sentinel-adapter
+ sentinel-dashboard
+
+ sentinel-demo
+
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-extension
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-datasource-extension
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-adapter
+ ${project.version}
+
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven.compiler.version}
+
+
+ ${java.target.version}
+ ${java.encoding}
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ ${maven.jacoco.version}
+
+
+
+ prepare-agent
+
+
+
+ report
+ test
+
+ report
+
+
+
+
+
+
+
+
+
+ oss
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ ${maven.source.version}
+
+
+ package
+
+ jar-no-fork
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ ${maven.javadoc.version}
+
+
+ package
+
+ jar
+
+
+ en_US
+ UTF-8
+ UTF-8
+ none
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ ${maven.gpg.version}
+
+
+ verify
+
+ sign
+
+
+
+
+
+
+
+
+ oss
+ https://oss.sonatype.org/content/repositories/snapshots/
+
+
+ oss
+ https://oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml
new file mode 100755
index 00000000..bd1acec8
--- /dev/null
+++ b/sentinel-adapter/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+ 4.0.0
+
+
+ com.alibaba.csp
+ sentinel-parent
+ 0.1.0
+
+ sentinel-adapter
+ pom
+ sentinel-adapter
+ The adapters of Sentinel
+
+
+ sentinel-web-servlet
+ sentinel-dubbo-adapter
+ sentinel-grpc-adapter
+ sentinel-spring-boot-starter
+
+
+
+ 3.1.0
+ 2.5.8
+ 1.13.1
+
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-extension
+ ${project.version}
+
+
+ javax.servlet
+ javax.servlet-api
+ ${servlet.api.version}
+ provided
+
+
+
+ com.alibaba
+ dubbo
+ ${dubbo.version}
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+ org.mockito
+ mockito-core
+ ${mockito.version}
+ test
+
+
+
+
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/README.md b/sentinel-adapter/sentinel-dubbo-adapter/README.md
new file mode 100755
index 00000000..a897de5b
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/README.md
@@ -0,0 +1,53 @@
+# Sentinel Dubbo Adapter
+
+Sentinel Dubbo Adapter provides service consumer filter and provider filter
+for [Dubbo](http://dubbo.io/) services.
+
+To use Sentinel Dubbo Adapter, you can simply add the following dependency to your `pom.xml`:
+
+```xml
+
+ com.alibaba.csp
+ sentinel-dubbo-adapter
+ x.y.z
+
+```
+
+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
+
+
+
+```
+
+For more details of Dubbo filter, see [here](https://dubbo.incubator.apache.org/#/docs/dev/impls/filter.md?lang=en-us).
+
+## 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`.
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/pom.xml b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
new file mode 100755
index 00000000..a9913e66
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+ com.alibaba.csp
+ sentinel-adapter
+ 0.1.0
+
+ 4.0.0
+ sentinel-dubbo-adapter
+ jar
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ com.alibaba
+ dubbo
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ com.alibaba
+ fastjson
+ 1.2.47
+ test
+
+
+
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
new file mode 100755
index 00000000..2c5b0779
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/AbstractDubboFilter.java
@@ -0,0 +1,44 @@
+/*
+ * 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.Filter;
+import com.alibaba.dubbo.rpc.Invocation;
+import com.alibaba.dubbo.rpc.Invoker;
+
+/**
+ * @author leyou
+ */
+abstract class AbstractDubboFilter implements Filter {
+
+ protected String getResourceName(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();
+ }
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
new file mode 100644
index 00000000..ceae979d
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboAppContextFilter.java
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+/**
+ * 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);
+ }
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
new file mode 100644
index 00000000..f633e9e0
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/DubboUtils.java
@@ -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() {}
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
new file mode 100755
index 00000000..2e388545
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/dubbo/SentinelDubboConsumerFilter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.SentinelRpcException;
+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;
+
+/**
+ *
Dubbo service consumer filter for Sentinel. Auto activated by default.
+ *
+ * If you want to disable the consumer filter, you can configure:
+ *
+ *
+ * @author leyou
+ */
+@Activate(group = "provider")
+public class SentinelDubboProviderFilter extends AbstractDubboFilter implements Filter {
+
+ @Override
+ public Result invoke(Invoker> invoker, Invocation invocation) throws RpcException {
+ // Get origin caller.
+ String application = DubboUtils.getApplication(invocation, "");
+
+ Entry interfaceEntry = null;
+ Entry methodEntry = null;
+ try {
+ String resourceName = getResourceName(invoker, invocation);
+ String interfaceName = invoker.getInterface().getName();
+ ContextUtil.enter(resourceName, application);
+ interfaceEntry = SphU.entry(interfaceName, EntryType.IN);
+ methodEntry = SphU.entry(resourceName, EntryType.IN, 1, invocation.getArguments());
+
+ return invoker.invoke(invocation);
+ } catch (BlockException e) {
+ throw new SentinelRpcException(e);
+ } catch (RpcException e) {
+ Tracer.trace(e);
+ throw e;
+ } finally {
+ if (methodEntry != null) {
+ methodEntry.exit(1, invocation.getArguments());
+ }
+ if (interfaceEntry != null) {
+ interfaceEntry.exit();
+ }
+ ContextUtil.exit();
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/sentinel-adapter/sentinel-dubbo-adapter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
new file mode 100755
index 00000000..292a4b25
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter
@@ -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
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DemoService.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DemoService.java
new file mode 100755
index 00000000..882de9ef
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/DemoService.java
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/**
+ * @author leyou
+ */
+public interface DemoService {
+ String sayHello(String name, int n);
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoServiceImpl.java b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoServiceImpl.java
new file mode 100755
index 00000000..b5dfd930
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/dubbo/provider/DemoServiceImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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;
+
+import com.alibaba.csp.sentinel.adapter.dubbo.DemoService;
+
+/**
+ * @author leyou
+ */
+public class DemoServiceImpl implements DemoService {
+ public String sayHello(String name, int n) {
+ return "Hello " + name + ", " + n;
+ }
+}
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-consumer-filter.xml b/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-consumer-filter.xml
new file mode 100755
index 00000000..aef82044
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-consumer-filter.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-provider-filter.xml b/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-provider-filter.xml
new file mode 100755
index 00000000..95e3eca5
--- /dev/null
+++ b/sentinel-adapter/sentinel-dubbo-adapter/src/test/resources/spring-dubbo-provider-filter.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-grpc-adapter/README.md b/sentinel-adapter/sentinel-grpc-adapter/README.md
new file mode 100755
index 00000000..103f9927
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/README.md
@@ -0,0 +1,38 @@
+# Sentinel gRPC Adapter
+
+Sentinel gRPC Adapter provides client and server interceptor for gRPC services.
+
+> Note that currently the interceptor only supports unary methods in gRPC.
+ In some circumstances (e.g. asynchronous call), the RT metrics might not be accurate.
+
+## Client Interceptor
+
+Example:
+
+```java
+public class ServiceClient {
+
+ private final ManagedChannel channel;
+
+ ServiceClient(String host, int port) {
+ this.channel = ManagedChannelBuilder.forAddress(host, port)
+ .intercept(new SentinelGrpcClientInterceptor()) // Add the client interceptor.
+ .build();
+ // Init your stub here.
+ }
+}
+```
+
+## Server Interceptor
+
+Example:
+
+```java
+import io.grpc.Server;
+
+Server server = ServerBuilder.forPort(port)
+ .addService(new MyServiceImpl()) // Add your service.
+ .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
+ .build();
+```
+
diff --git a/sentinel-adapter/sentinel-grpc-adapter/pom.xml b/sentinel-adapter/sentinel-grpc-adapter/pom.xml
new file mode 100755
index 00000000..e5928c60
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/pom.xml
@@ -0,0 +1,87 @@
+
+
+
+ sentinel-adapter
+ com.alibaba.csp
+ 0.1.0
+
+ 4.0.0
+ sentinel-grpc-adapter
+ jar
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+
+ io.grpc
+ grpc-netty
+ ${grpc.version}
+ provided
+
+
+ io.grpc
+ grpc-protobuf
+ ${grpc.version}
+ provided
+
+
+ io.grpc
+ grpc-stub
+ ${grpc.version}
+ provided
+
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+ com.alibaba
+ fastjson
+ ${fastjson.version}
+ test
+
+
+
+
+
+
+ kr.motd.maven
+ os-maven-plugin
+ 1.5.0.Final
+
+
+
+
+ org.xolstice.maven.plugins
+ protobuf-maven-plugin
+ 0.5.1
+
+ com.google.protobuf:protoc:3.5.1-1:exe:${os.detected.classifier}
+ grpc-java
+ io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
+
+
+
+
+
+ test-compile
+ test-compile-custom
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptor.java b/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptor.java
new file mode 100755
index 00000000..47771388
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptor.java
@@ -0,0 +1,141 @@
+/*
+ * 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.grpc;
+
+import javax.annotation.Nullable;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+import io.grpc.CallOptions;
+import io.grpc.Channel;
+import io.grpc.ClientCall;
+import io.grpc.ClientInterceptor;
+import io.grpc.ForwardingClientCall;
+import io.grpc.ForwardingClientCallListener.SimpleForwardingClientCallListener;
+import io.grpc.Metadata;
+import io.grpc.MethodDescriptor;
+import io.grpc.Status;
+
+/**
+ *
gRPC client interceptor for Sentinel. Currently it only works with unary methods.
+ *
+ * Example code:
+ *
+ * public class ServiceClient {
+ *
+ * private final ManagedChannel channel;
+ *
+ * ServiceClient(String host, int port) {
+ * this.channel = ManagedChannelBuilder.forAddress(host, port)
+ * .intercept(new SentinelGrpcClientInterceptor()) // Add the client interceptor.
+ * .build();
+ * // Init your stub here.
+ * }
+ *
+ * }
+ *
+ *
+ * For server interceptor, see {@link SentinelGrpcServerInterceptor}.
+ *
+ * @author Eric Zhao
+ */
+public class SentinelGrpcClientInterceptor implements ClientInterceptor {
+
+ private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
+ "Flow control limit exceeded (client side)");
+
+ @Override
+ public ClientCall interceptCall(MethodDescriptor methodDescriptor,
+ CallOptions callOptions, Channel channel) {
+ String resourceName = methodDescriptor.getFullMethodName();
+ Entry entry = null;
+ try {
+ ContextUtil.enter(resourceName);
+ entry = SphU.entry(resourceName, EntryType.OUT);
+ // Allow access, forward the call.
+ return new ForwardingClientCall.SimpleForwardingClientCall(
+ channel.newCall(methodDescriptor, callOptions)) {
+ @Override
+ public void start(Listener responseListener, Metadata headers) {
+ super.start(new SimpleForwardingClientCallListener(responseListener) {
+ @Override
+ public void onReady() {
+ super.onReady();
+ }
+
+ @Override
+ public void onClose(Status status, Metadata trailers) {
+ super.onClose(status, trailers);
+ // Record the exception metrics.
+ if (!status.isOk()) {
+ recordException(status.asRuntimeException());
+ }
+ }
+ }, headers);
+ }
+
+ @Override
+ public void cancel(@Nullable String message, @Nullable Throwable cause) {
+ super.cancel(message, cause);
+ // Record the exception metrics.
+ recordException(cause);
+ }
+ };
+ } catch (BlockException e) {
+ // Flow control threshold exceeded, block the call.
+ return new ClientCall() {
+ @Override
+ public void start(Listener responseListener, Metadata headers) {
+ responseListener.onClose(FLOW_CONTROL_BLOCK, new Metadata());
+ }
+
+ @Override
+ public void request(int numMessages) {
+
+ }
+
+ @Override
+ public void cancel(@Nullable String message, @Nullable Throwable cause) {
+
+ }
+
+ @Override
+ public void halfClose() {
+
+ }
+
+ @Override
+ public void sendMessage(ReqT message) {
+
+ }
+ };
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ ContextUtil.exit();
+ }
+ }
+
+ private void recordException(Throwable t) {
+ Tracer.trace(t);
+ }
+}
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptor.java b/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptor.java
new file mode 100755
index 00000000..1e0d9090
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptor.java
@@ -0,0 +1,90 @@
+/*
+ * 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.grpc;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+import io.grpc.ForwardingServerCall;
+import io.grpc.ForwardingServerCallListener;
+import io.grpc.Metadata;
+import io.grpc.ServerCall;
+import io.grpc.ServerCall.Listener;
+import io.grpc.ServerCallHandler;
+import io.grpc.ServerInterceptor;
+import io.grpc.Status;
+
+/**
+ *
gRPC server interceptor for Sentinel. Currently it only works with unary methods.
+ *
+ * Example code:
+ *
+ * Server server = ServerBuilder.forPort(port)
+ * .addService(new MyServiceImpl()) // Add your service.
+ * .intercept(new SentinelGrpcServerInterceptor()) // Add the server interceptor.
+ * .build();
+ *
+ *
+ * For client interceptor, see {@link SentinelGrpcClientInterceptor}.
+ *
+ * @author Eric Zhao
+ */
+public class SentinelGrpcServerInterceptor implements ServerInterceptor {
+
+ private static final Status FLOW_CONTROL_BLOCK = Status.UNAVAILABLE.withDescription(
+ "Flow control limit exceeded (server side)");
+
+ @Override
+ public Listener interceptCall(ServerCall serverCall, Metadata metadata,
+ ServerCallHandler serverCallHandler) {
+ String resourceName = serverCall.getMethodDescriptor().getFullMethodName();
+ // Remote address: serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR);
+ Entry entry = null;
+ try {
+ ContextUtil.enter(resourceName);
+ entry = SphU.entry(resourceName, EntryType.IN);
+ // Allow access, forward the call.
+ return new ForwardingServerCallListener.SimpleForwardingServerCallListener(
+ serverCallHandler.startCall(
+ new ForwardingServerCall.SimpleForwardingServerCall(serverCall) {
+ @Override
+ public void close(Status status, Metadata trailers) {
+ super.close(status, trailers);
+ // Record the exception metrics.
+ if (!status.isOk()) {
+ recordException(status.asRuntimeException());
+ }
+ }
+ }, metadata)) {};
+ } catch (BlockException e) {
+ serverCall.close(FLOW_CONTROL_BLOCK, new Metadata());
+ return new ServerCall.Listener() {};
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ ContextUtil.exit();
+ }
+ }
+
+ private void recordException(Throwable t) {
+ Tracer.trace(t);
+ }
+}
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceClient.java b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceClient.java
new file mode 100755
index 00000000..dab36678
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceClient.java
@@ -0,0 +1,70 @@
+/*
+ * 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.grpc;
+
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
+
+import io.grpc.ClientInterceptor;
+import io.grpc.ManagedChannel;
+import io.grpc.ManagedChannelBuilder;
+
+/**
+ * A simple wrapped gRPC client for FooService.
+ *
+ * @author Eric Zhao
+ */
+final class FooServiceClient {
+
+ private final ManagedChannel channel;
+ private final FooServiceGrpc.FooServiceBlockingStub blockingStub;
+
+ FooServiceClient(String host, int port) {
+ this.channel = ManagedChannelBuilder.forAddress(host, port)
+ .usePlaintext()
+ .build();
+ this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
+ }
+
+ FooServiceClient(String host, int port, ClientInterceptor interceptor) {
+ this.channel = ManagedChannelBuilder.forAddress(host, port)
+ .usePlaintext()
+ .intercept(interceptor)
+ .build();
+ this.blockingStub = FooServiceGrpc.newBlockingStub(this.channel);
+ }
+
+ FooResponse sayHello(FooRequest request) {
+ if (request == null) {
+ throw new IllegalArgumentException("Request cannot be null");
+ }
+ return blockingStub.sayHello(request);
+ }
+
+ FooResponse anotherHello(FooRequest request) {
+ if (request == null) {
+ throw new IllegalArgumentException("Request cannot be null");
+ }
+ return blockingStub.anotherHello(request);
+ }
+
+ void shutdown() throws InterruptedException {
+ channel.shutdown().awaitTermination(1, TimeUnit.SECONDS);
+ }
+}
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceImpl.java b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceImpl.java
new file mode 100755
index 00000000..adda5102
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/FooServiceImpl.java
@@ -0,0 +1,44 @@
+/*
+ * 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.grpc;
+
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooServiceGrpc;
+
+import io.grpc.stub.StreamObserver;
+
+/**
+ * Implementation of FooService defined in proto.
+ */
+class FooServiceImpl extends FooServiceGrpc.FooServiceImplBase {
+
+ @Override
+ public void sayHello(FooRequest request, StreamObserver responseObserver) {
+ String message = String.format("Hello %s! Your ID is %d.", request.getName(), request.getId());
+ FooResponse response = FooResponse.newBuilder().setMessage(message).build();
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+
+ @Override
+ public void anotherHello(FooRequest request, StreamObserver responseObserver) {
+ String message = String.format("Good day, %s (%d)", request.getName(), request.getId());
+ FooResponse response = FooResponse.newBuilder().setMessage(message).build();
+ responseObserver.onNext(response);
+ responseObserver.onCompleted();
+ }
+}
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptorTest.java b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptorTest.java
new file mode 100755
index 00000000..b7acc649
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcClientInterceptorTest.java
@@ -0,0 +1,113 @@
+/*
+ * 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.grpc;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+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.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.StatusRuntimeException;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test cases for {@link SentinelGrpcClientInterceptor}.
+ *
+ * @author Eric Zhao
+ */
+public class SentinelGrpcClientInterceptorTest {
+
+ private final String resourceName = "com.alibaba.sentinel.examples.FooService/sayHello";
+ private final int threshold = 2;
+
+ private Server server;
+
+ private void configureFlowRule() {
+ FlowRule rule = new FlowRule()
+ .setCount(threshold)
+ .setGrade(RuleConstant.FLOW_GRADE_QPS)
+ .setResource(resourceName)
+ .setLimitApp("default")
+ .as(FlowRule.class);
+ FlowRuleManager.loadRules(Collections.singletonList(rule));
+ }
+
+ //@Test
+ public void testGrpcClientInterceptor() throws Exception {
+ final int port = 19328;
+
+ configureFlowRule();
+ prepareServer(port);
+
+ FooServiceClient client = new FooServiceClient("localhost", port, new SentinelGrpcClientInterceptor());
+ final int total = 8;
+ for (int i = 0; i < total; i++) {
+ sendRequest(client);
+ }
+ ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT);
+ assertNotNull(clusterNode);
+
+ assertEquals((total - threshold) / 2, clusterNode.blockedRequest());
+ assertEquals(total / 2, clusterNode.totalRequest());
+
+ long totalQps = clusterNode.totalQps();
+ long passQps = clusterNode.passQps();
+ long blockedQps = clusterNode.blockedQps();
+ assertEquals(total, totalQps);
+ assertEquals(total - threshold, blockedQps);
+ assertEquals(threshold, passQps);
+
+ stopServer();
+ }
+
+ private void sendRequest(FooServiceClient client) {
+ try {
+ FooResponse response = client.sayHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+ System.out.println(ClusterBuilderSlot.getClusterNode(resourceName, EntryType.OUT).avgRt());
+ System.out.println("Response: " + response);
+ } catch (StatusRuntimeException ex) {
+ System.out.println("Blocked, cause: " + ex.getMessage());
+ }
+ }
+
+ private void prepareServer(int port) throws IOException {
+ if (server != null) {
+ throw new IllegalStateException("Server already running!");
+ }
+ server = ServerBuilder.forPort(port)
+ .addService(new FooServiceImpl())
+ .build();
+ server.start();
+ }
+
+ private void stopServer() {
+ if (server != null) {
+ server.shutdown();
+ server = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptorTest.java b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptorTest.java
new file mode 100755
index 00000000..5be16eee
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/grpc/SentinelGrpcServerInterceptorTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.grpc;
+
+import java.io.IOException;
+import java.util.Collections;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooRequest;
+import com.alibaba.csp.sentinel.adapter.grpc.gen.FooResponse;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+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.FlowRuleManager;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+import io.grpc.Server;
+import io.grpc.ServerBuilder;
+import io.grpc.StatusRuntimeException;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test cases for {@link SentinelGrpcServerInterceptor}.
+ *
+ * @author Eric Zhao
+ */
+public class SentinelGrpcServerInterceptorTest {
+
+ private final String resourceName = "com.alibaba.sentinel.examples.FooService/anotherHello";
+ private final int threshold = 4;
+
+ private Server server;
+ private FooServiceClient client;
+
+ private void configureFlowRule() {
+ FlowRule rule = new FlowRule()
+ .setCount(threshold)
+ .setGrade(RuleConstant.FLOW_GRADE_QPS)
+ .setResource(resourceName)
+ .setLimitApp("default")
+ .as(FlowRule.class);
+ FlowRuleManager.loadRules(Collections.singletonList(rule));
+ }
+
+ //@Test
+ public void testGrpcServerInterceptor() throws Exception {
+ final int port = 19329;
+ client = new FooServiceClient("localhost", port);
+
+ configureFlowRule();
+ prepareServer(port);
+
+ final int total = 8;
+ for (int i = 0; i < total; i++) {
+ sendRequest();
+ }
+ ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(resourceName, EntryType.IN);
+ assertNotNull(clusterNode);
+
+ assertEquals((total - threshold) / 2, clusterNode.blockedRequest());
+ assertEquals(total / 2, clusterNode.totalRequest());
+
+ long totalQps = clusterNode.totalQps();
+ long passQps = clusterNode.passQps();
+ long blockedQps = clusterNode.blockedQps();
+ assertEquals(total, totalQps);
+ assertEquals(total - threshold, blockedQps);
+ assertEquals(threshold, passQps);
+
+ stopServer();
+ }
+
+ private void sendRequest() {
+ try {
+ FooResponse response = client.anotherHello(FooRequest.newBuilder().setName("Sentinel").setId(666).build());
+ System.out.println("Response: " + response);
+ } catch (StatusRuntimeException ex) {
+ System.out.println("Blocked, cause: " + ex.getMessage());
+ }
+ }
+
+ private void prepareServer(int port) throws IOException {
+ if (server != null) {
+ throw new IllegalStateException("Server already running!");
+ }
+ server = ServerBuilder.forPort(port)
+ .addService(new FooServiceImpl())
+ .intercept(new SentinelGrpcServerInterceptor())
+ .build();
+ server.start();
+ }
+
+ private void stopServer() {
+ if (server != null) {
+ server.shutdown();
+ server = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-grpc-adapter/src/test/proto/example.proto b/sentinel-adapter/sentinel-grpc-adapter/src/test/proto/example.proto
new file mode 100755
index 00000000..61858510
--- /dev/null
+++ b/sentinel-adapter/sentinel-grpc-adapter/src/test/proto/example.proto
@@ -0,0 +1,23 @@
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "com.alibaba.csp.sentinel.adapter.grpc.gen";
+option java_outer_classname = "FooProto";
+
+package com.alibaba.sentinel.examples;
+
+message FooRequest {
+ string name = 1;
+ int32 id = 2;
+}
+
+message FooResponse {
+ string message = 1;
+}
+
+// Example service definition.
+service FooService {
+ rpc sayHello(FooRequest) returns (FooResponse) {}
+
+ rpc anotherHello(FooRequest) returns (FooResponse) {}
+}
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/README.md b/sentinel-adapter/sentinel-spring-boot-starter/README.md
new file mode 100755
index 00000000..83560f54
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/README.md
@@ -0,0 +1,18 @@
+# Sentinel Spring Boot Starter
+
+Sentinel Spring Boot Starter provides out-of-box integration with Spring Boot applications
+(e.g. web applications, Dubbo services).
+
+## Web Servlet
+
+Web servlet integration is enabled by default. You need to configure URL patterns in your config file (e.g. properties file):
+
+```
+spring.sentinel.servletFilter.urlPatterns=/*
+```
+
+By default the URL pattern is `/*`.
+
+## Dubbo
+
+Dubbo integration is enabled by default. You need to disable the filters manually if you don't want them.
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/pom.xml b/sentinel-adapter/sentinel-spring-boot-starter/pom.xml
new file mode 100755
index 00000000..dc04ded5
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/pom.xml
@@ -0,0 +1,95 @@
+
+
+
+ sentinel-adapter
+ com.alibaba.csp
+ 0.1.0
+
+ 4.0.0
+ jar
+
+ sentinel-spring-boot-starter
+
+
+ 1.5.14.RELEASE
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-transport-simple-http
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-web-servlet
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-dubbo-adapter
+ ${project.version}
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.25
+ compile
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+ ${spring.boot.version}
+ compile
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+ ${spring.boot.version}
+ compile
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ ${spring.boot.version}
+ compile
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ ${spring.boot.version}
+ provided
+ true
+
+
+ org.springframework.boot
+ spring-boot-actuator
+ ${spring.boot.version}
+ compile
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ ${spring.boot.version}
+ test
+
+
+ junit
+ junit
+ ${junit.version}
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/Constants.java b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/Constants.java
new file mode 100755
index 00000000..bfb98e05
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/Constants.java
@@ -0,0 +1,31 @@
+/*
+ * 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.boot.sentinel;
+
+/**
+ * @author Eric Zhao
+ */
+public final class Constants {
+
+ /**
+ * Property prefix in application.properties.
+ */
+ public static final String PREFIX = "spring.sentinel";
+ public static final String SENTINEL_SERVLET_ENABLED = "spring.sentinel.servletFilter.enabled";
+ public static final String SENTINEL_ENABLED = "spring.sentinel.enabled";
+
+ private Constants() {}
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/config/SentinelWebServletAutoConfiguration.java b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/config/SentinelWebServletAutoConfiguration.java
new file mode 100755
index 00000000..f996fbeb
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/config/SentinelWebServletAutoConfiguration.java
@@ -0,0 +1,85 @@
+/*
+ * 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.boot.sentinel.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.servlet.Filter;
+
+import com.alibaba.boot.sentinel.Constants;
+import com.alibaba.boot.sentinel.property.SentinelProperties;
+import com.alibaba.boot.sentinel.property.SentinelProperties.ServletFilterConfig;
+import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.util.CollectionUtils;
+
+/**
+ * Auto configuration for Sentinel web servlet filter.
+ *
+ * @author Eric Zhao
+ */
+@Configuration
+@ConditionalOnWebApplication
+@ConditionalOnProperty(name = {Constants.SENTINEL_ENABLED, Constants.SENTINEL_SERVLET_ENABLED}, matchIfMissing = true)
+@EnableConfigurationProperties(SentinelProperties.class)
+public class SentinelWebServletAutoConfiguration {
+
+ private final Logger logger = LoggerFactory.getLogger(SentinelWebServletAutoConfiguration.class);
+
+ @Autowired
+ private SentinelProperties properties;
+
+ @Bean
+ @ConditionalOnWebApplication
+ public FilterRegistrationBean sentinelFilterRegistrationBean() {
+ FilterRegistrationBean registrationBean = new FilterRegistrationBean();
+ if (!properties.isEnabled()) {
+ return registrationBean;
+ }
+
+ ServletFilterConfig filterConfig = properties.getServletFilter();
+ if (null == filterConfig) {
+ filterConfig = new ServletFilterConfig();
+ properties.setServletFilter(filterConfig);
+ }
+
+ if (CollectionUtils.isEmpty(filterConfig.getUrlPatterns())) {
+ List defaultPatterns = new ArrayList();
+ defaultPatterns.add("/*");
+ filterConfig.setUrlPatterns(defaultPatterns);
+ logger.info("[Sentinel Starter] Using default patterns for web servlet filter: {}", defaultPatterns);
+ }
+ registrationBean.addUrlPatterns(filterConfig.getUrlPatterns().toArray(new String[0]));
+
+ Filter filter = new CommonFilter();
+ registrationBean.setFilter(filter);
+ registrationBean.setOrder(filterConfig.getOrder());
+
+ logger.info("[Sentinel Starter] Web servlet filter registered with urlPatterns: {}",
+ filterConfig.getUrlPatterns());
+ return registrationBean;
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelActuatorEndpoint.java b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelActuatorEndpoint.java
new file mode 100755
index 00000000..7dd14ae5
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelActuatorEndpoint.java
@@ -0,0 +1,68 @@
+/*
+ * 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.boot.sentinel.endpoint;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.boot.sentinel.property.SentinelProperties;
+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 com.alibaba.csp.sentinel.slots.system.SystemRule;
+import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.actuate.endpoint.mvc.AbstractMvcEndpoint;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+/**
+ * @author duanling
+ * @author Eric Zhao
+ */
+@ConfigurationProperties("endpoints.sentinel")
+public class SentinelActuatorEndpoint extends AbstractMvcEndpoint {
+
+ @Autowired
+ private SentinelProperties sentinelProperties;
+
+ public SentinelActuatorEndpoint() {
+ super("/sentinel", false);
+ }
+
+ @RequestMapping
+ @ResponseBody
+ public Map invoke() {
+ Map result = new HashMap();
+
+ result.put("version", this.getClass().getPackage().getImplementationVersion());
+ result.put("properties", sentinelProperties);
+
+ List flowRules = FlowRuleManager.getRules();
+ List degradeRules = DegradeRuleManager.getRules();
+ List systemRules = SystemRuleManager.getRules();
+
+ result.put("flowRules", flowRules);
+ result.put("degradeRules", degradeRules);
+ result.put("systemRules", systemRules);
+
+ return result;
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelEndpointManagementContextConfiguration.java b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelEndpointManagementContextConfiguration.java
new file mode 100755
index 00000000..3f1054ae
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/endpoint/SentinelEndpointManagementContextConfiguration.java
@@ -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.boot.sentinel.endpoint;
+
+import com.alibaba.boot.sentinel.property.SentinelProperties;
+
+import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration;
+import org.springframework.boot.actuate.condition.ConditionalOnEnabledEndpoint;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+
+/**
+ * @author duanling
+ */
+@ManagementContextConfiguration
+@EnableConfigurationProperties({SentinelProperties.class})
+public class SentinelEndpointManagementContextConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @ConditionalOnEnabledEndpoint("sentinel")
+ public SentinelActuatorEndpoint sentinelEndPoint() {
+ return new SentinelActuatorEndpoint();
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/property/SentinelProperties.java b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/property/SentinelProperties.java
new file mode 100755
index 00000000..bdd281d2
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/java/com/alibaba/boot/sentinel/property/SentinelProperties.java
@@ -0,0 +1,93 @@
+/*
+ * 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.boot.sentinel.property;
+
+import java.util.List;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.Ordered;
+
+/**
+ * @author Eric Zhao
+ */
+@ConfigurationProperties(prefix = "spring.sentinel")
+public class SentinelProperties {
+
+ private boolean enabled = true;
+
+ private ServletFilterConfig servletFilter;
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public SentinelProperties setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+
+ public ServletFilterConfig getServletFilter() {
+ return servletFilter;
+ }
+
+ public SentinelProperties setServletFilter(
+ ServletFilterConfig servletFilter) {
+ this.servletFilter = servletFilter;
+ return this;
+ }
+
+ public static class DubboFilterConfig {}
+
+ public static class ServletFilterConfig {
+
+ private boolean enabled = true;
+
+ /**
+ * Chain order for Sentinel servlet filter.
+ */
+ private int order = Ordered.HIGHEST_PRECEDENCE;
+
+ /**
+ * URL pattern for Sentinel servlet filter.
+ */
+ private List urlPatterns;
+
+ public int getOrder() {
+ return this.order;
+ }
+
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public List getUrlPatterns() {
+ return urlPatterns;
+ }
+
+ public void setUrlPatterns(List urlPatterns) {
+ this.urlPatterns = urlPatterns;
+ }
+
+ public boolean isEnabled() {
+ return enabled;
+ }
+
+ public ServletFilterConfig setEnabled(boolean enabled) {
+ this.enabled = enabled;
+ return this;
+ }
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.factories b/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.factories
new file mode 100755
index 00000000..15992e33
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,5 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.alibaba.boot.sentinel.config.SentinelWebServletAutoConfiguration
+
+org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration=\
+com.alibaba.boot.sentinel.endpoint.SentinelEndpointManagementContextConfiguration
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.provides b/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.provides
new file mode 100755
index 00000000..10a1f7ae
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/main/resources/META-INF/spring.provides
@@ -0,0 +1 @@
+provides: sentinel-core
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/test/java/com/alibaba/boot/sentinel/SimpleWebApplication.java b/sentinel-adapter/sentinel-spring-boot-starter/src/test/java/com/alibaba/boot/sentinel/SimpleWebApplication.java
new file mode 100755
index 00000000..572b02c2
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/test/java/com/alibaba/boot/sentinel/SimpleWebApplication.java
@@ -0,0 +1,53 @@
+/*
+ * 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.boot.sentinel;
+
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * @author Eric Zhao
+ */
+@SpringBootApplication
+@RestController
+@PropertySource("classpath:web-servlet.properties")
+public class SimpleWebApplication {
+
+ @RequestMapping("/foo")
+ public String foo() {
+ return "Hello!";
+ }
+
+ @RequestMapping("/baz")
+ public String baz() {
+ ClusterNode node = ClusterBuilderSlot.getClusterNode("/foo");
+ if (node == null) {
+ return "/foo has not been called!";
+ } else {
+ return "/foo total request in metrics: " + node.totalRequest();
+ }
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(SimpleWebApplication.class, args);
+ }
+}
diff --git a/sentinel-adapter/sentinel-spring-boot-starter/src/test/resources/web-servlet.properties b/sentinel-adapter/sentinel-spring-boot-starter/src/test/resources/web-servlet.properties
new file mode 100755
index 00000000..41eb21a2
--- /dev/null
+++ b/sentinel-adapter/sentinel-spring-boot-starter/src/test/resources/web-servlet.properties
@@ -0,0 +1,3 @@
+spring.sentinel.enabled=true
+spring.sentinel.servletFilter.enabled=true
+spring.sentinel.servletFilter.urlPatterns=/foo
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-web-servlet/README.md b/sentinel-adapter/sentinel-web-servlet/README.md
new file mode 100755
index 00000000..39bfefc2
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/README.md
@@ -0,0 +1,22 @@
+# Sentinel Web Servlet Filter
+
+Sentinel provides Servlet filter integration. To use the filter,
+you can simply configure your `web.xml` with:
+
+```xml
+
+ SentinelCommonFilter
+ com.alibaba.csp.sentinel.adapter.servlet.CommonFilter
+
+
+
+ SentinelCommonFilter
+ /*
+
+```
+
+When a request is blocked, Sentinel servlet filter will give a default page indicating the request blocked.
+If customized block page is set (via `WebServletConfig.setBlockPage(blockPage)` method),
+the filter will redirect the request to provided URL. You can also implement your own
+block handler (the `UrlBlockHandler` interface) and register to `WebCallbackManager`.
+
diff --git a/sentinel-adapter/sentinel-web-servlet/pom.xml b/sentinel-adapter/sentinel-web-servlet/pom.xml
new file mode 100755
index 00000000..5f253447
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+
+ com.alibaba.csp
+ sentinel-adapter
+ 0.1.0
+
+ sentinel-web-servlet
+ jar
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
+
+
+
\ No newline at end of file
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
new file mode 100755
index 00000000..7ca3fe27
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonFilter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
+import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/***
+ * Servlet filter that integrates with Sentinel.
+ *
+ * @author youji.zj
+ */
+public class CommonFilter implements Filter {
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+ throws IOException, ServletException {
+ HttpServletRequest sRequest = (HttpServletRequest)request;
+ Entry entry = null;
+
+ try {
+ String target = FilterUtil.filterTarget(sRequest);
+ target = WebCallbackManager.getUrlCleaner().clean(target);
+
+ ContextUtil.enter(target);
+ entry = SphU.entry(target, EntryType.IN);
+
+ chain.doFilter(request, response);
+ } catch (BlockException e) {
+ HttpServletResponse sResponse = (HttpServletResponse)response;
+ WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse);
+ } catch (IOException e2) {
+ Tracer.trace(e2);
+ throw e2;
+ } catch (ServletException e3) {
+ Tracer.trace(e3);
+ throw e3;
+ } catch (RuntimeException e4) {
+ Tracer.trace(e4);
+ throw e4;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ ContextUtil.exit();
+ }
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonTotalFilter.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonTotalFilter.java
new file mode 100755
index 00000000..4881df46
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/CommonTotalFilter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.servlet;
+
+import java.io.IOException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.adapter.servlet.callback.WebCallbackManager;
+import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/***
+ * Servlet filter for all requests.
+ *
+ * @author youji.zj
+ */
+public class CommonTotalFilter implements Filter {
+
+ public static final String TOTAL_URL_REQUEST = "total-url-request";
+
+ @Override
+ public void init(FilterConfig filterConfig) {
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest request, ServletResponse response,
+ FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest sRequest = (HttpServletRequest)request;
+ String target = FilterUtil.filterTarget(sRequest);
+ target = WebCallbackManager.getUrlCleaner().clean(target);
+
+ Entry entry = null;
+ try {
+ ContextUtil.enter(target);
+ entry = SphU.entry(TOTAL_URL_REQUEST);
+ chain.doFilter(request, response);
+ } catch (BlockException e) {
+ HttpServletResponse sResponse = (HttpServletResponse)response;
+ WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse);
+ } catch (IOException e2) {
+ Tracer.trace(e2);
+ throw e2;
+ } catch (ServletException e3) {
+ Tracer.trace(e3);
+ throw e3;
+ } catch (RuntimeException e4) {
+ Tracer.trace(e4);
+ throw e4;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ ContextUtil.exit();
+ }
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlBlockHandler.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlBlockHandler.java
new file mode 100755
index 00000000..cf14749f
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlBlockHandler.java
@@ -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.servlet.callback;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.csp.sentinel.adapter.servlet.util.FilterUtil;
+
+/***
+ * The default {@link UrlBlockHandler}.
+ *
+ * @author youji.zj
+ */
+public class DefaultUrlBlockHandler implements UrlBlockHandler {
+
+ @Override
+ public void blocked(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ // Directly redirect to the default flow control (blocked) page or customized block page.
+ FilterUtil.blockRequest(request, response);
+ }
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlCleaner.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlCleaner.java
new file mode 100755
index 00000000..2d80abd4
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/DefaultUrlCleaner.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servlet.callback;
+
+/***
+ * @author youji.zj
+ */
+public class DefaultUrlCleaner implements UrlCleaner {
+
+ @Override
+ public String clean(String originUrl) {
+ return originUrl;
+ }
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlBlockHandler.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlBlockHandler.java
new file mode 100755
index 00000000..dc6d1f86
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlBlockHandler.java
@@ -0,0 +1,38 @@
+/*
+ * 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.servlet.callback;
+
+import java.io.IOException;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/***
+ * The URL block handler handles requests when blocked.
+ *
+ * @author youji.zj
+ */
+public interface UrlBlockHandler {
+
+ /**
+ * Handle the request when blocked.
+ *
+ * @param request Servlet request
+ * @param response Servlet response
+ * @throws IOException some error occurs
+ */
+ void blocked(HttpServletRequest request, HttpServletResponse response) throws IOException;
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlCleaner.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlCleaner.java
new file mode 100755
index 00000000..c5883396
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/UrlCleaner.java
@@ -0,0 +1,31 @@
+/*
+ * 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.servlet.callback;
+
+/***
+ * @author youji.zj
+ */
+public interface UrlCleaner {
+
+ /***
+ *
Process the url. Some path variables should be handled and unified.
+ *
e.g. collect_item_relation--10200012121-.html will be converted to collect_item_relation.html
+ *
+ * @param originUrl original url
+ * @return processed url
+ */
+ String clean(String originUrl);
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/WebCallbackManager.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/WebCallbackManager.java
new file mode 100755
index 00000000..435dd9db
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/callback/WebCallbackManager.java
@@ -0,0 +1,50 @@
+/*
+ * 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.servlet.callback;
+
+/**
+ * Registry for URL cleaner and URL block handler.
+ *
+ * @author youji.zj
+ */
+public class WebCallbackManager {
+
+ /**
+ * URL cleaner
+ */
+ private static volatile UrlCleaner urlCleaner = new DefaultUrlCleaner();
+
+ /**
+ * URL block handler
+ */
+ private static volatile UrlBlockHandler urlBlockHandler = new DefaultUrlBlockHandler();
+
+ public static UrlCleaner getUrlCleaner() {
+ return urlCleaner;
+ }
+
+ public static void setUrlCleaner(UrlCleaner urlCleaner) {
+ WebCallbackManager.urlCleaner = urlCleaner;
+ }
+
+ public static UrlBlockHandler getUrlBlockHandler() {
+ return urlBlockHandler;
+ }
+
+ public static void setUrlBlockHandler(UrlBlockHandler urlBlockHandler) {
+ WebCallbackManager.urlBlockHandler = urlBlockHandler;
+ }
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/config/WebServletConfig.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/config/WebServletConfig.java
new file mode 100755
index 00000000..bcaafe07
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/config/WebServletConfig.java
@@ -0,0 +1,42 @@
+/*
+ * 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.servlet.config;
+
+import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
+import com.alibaba.csp.sentinel.adapter.servlet.CommonTotalFilter;
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+
+/**
+ * @author leyou
+ */
+public class WebServletConfig {
+
+ public static final String BLOCK_PAGE = "csp.sentinel.web.servlet.block.page";
+
+ /**
+ * Get redirecting page when Sentinel blocking for {@link CommonFilter} or
+ * {@link CommonTotalFilter} occurs.
+ *
+ * @return the block page URL, maybe null if not configured.
+ */
+ public static String getBlockPage() {
+ return SentinelConfig.getConfig(BLOCK_PAGE);
+ }
+
+ public static void setBlockPage(String blockPage) {
+ SentinelConfig.setConfig(BLOCK_PAGE, blockPage);
+ }
+}
diff --git a/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/util/FilterUtil.java b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/util/FilterUtil.java
new file mode 100755
index 00000000..ad3434c5
--- /dev/null
+++ b/sentinel-adapter/sentinel-web-servlet/src/main/java/com/alibaba/csp/sentinel/adapter/servlet/util/FilterUtil.java
@@ -0,0 +1,185 @@
+/*
+ * 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.servlet.util;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.alibaba.csp.sentinel.adapter.servlet.config.WebServletConfig;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * Util class for web servlet filter.
+ *
+ * @author youji.zj
+ * @author Eric Zhao
+ */
+public final class FilterUtil {
+
+ public static String filterTarget(HttpServletRequest request) {
+ String pathInfo = getResourcePath(request);
+ if (!pathInfo.startsWith("/")) {
+ pathInfo = "/" + pathInfo;
+ }
+
+ if ("/".equals(pathInfo)) {
+ return pathInfo;
+ }
+
+ // Note: pathInfo should be converted to camelCase style.
+ int lastSlashIndex = pathInfo.lastIndexOf("/");
+
+ if (lastSlashIndex >= 0) {
+ pathInfo = pathInfo.substring(0, lastSlashIndex) + "/"
+ + StringUtil.trim(pathInfo.substring(lastSlashIndex + 1));
+ } else {
+ pathInfo = "/" + StringUtil.trim(pathInfo);
+ }
+
+ return pathInfo;
+ }
+
+ public static void blockRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
+ StringBuffer url = request.getRequestURL();
+
+ if ("GET".equals(request.getMethod()) && StringUtil.isNotBlank(request.getQueryString())) {
+ url.append("?").append(request.getQueryString());
+ }
+
+ if (StringUtil.isEmpty(WebServletConfig.getBlockPage())) {
+ writeDefaultBlockedPage(response);
+ } else {
+ String redirectUrl = WebServletConfig.getBlockPage() + "?http_referer=" + url.toString();
+ // Redirect to the customized block page.
+ response.sendRedirect(redirectUrl);
+ }
+ }
+
+ private static void writeDefaultBlockedPage(HttpServletResponse response) throws IOException {
+ PrintWriter out = response.getWriter();
+ out.println("Blocked by Sentinel (flow limiting)");
+ out.flush();
+ out.close();
+ }
+
+ private static String getResourcePath(HttpServletRequest request) {
+ String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false);
+ String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0);
+
+ return servletPath + pathInfo;
+ }
+
+ private static String normalizeAbsolutePath(String path, boolean removeTrailingSlash) throws IllegalStateException {
+ return normalizePath(path, true, false, removeTrailingSlash);
+ }
+
+ private static String normalizePath(String path, boolean forceAbsolute, boolean forceRelative,
+ boolean removeTrailingSlash) throws IllegalStateException {
+ char[] pathChars = StringUtil.trimToEmpty(path).toCharArray();
+ int length = pathChars.length;
+
+ // Check path and slash.
+ boolean startsWithSlash = false;
+ boolean endsWithSlash = false;
+
+ if (length > 0) {
+ char firstChar = pathChars[0];
+ char lastChar = pathChars[length - 1];
+
+ startsWithSlash = firstChar == '/' || firstChar == '\\';
+ endsWithSlash = lastChar == '/' || lastChar == '\\';
+ }
+
+ StringBuilder buf = new StringBuilder(length);
+ boolean isAbsolutePath = forceAbsolute || !forceRelative && startsWithSlash;
+ int index = startsWithSlash ? 0 : -1;
+ int level = 0;
+
+ if (isAbsolutePath) {
+ buf.append("/");
+ }
+
+ while (index < length) {
+ index = indexOfSlash(pathChars, index + 1, false);
+
+ if (index == length) {
+ break;
+ }
+
+ int nextSlashIndex = indexOfSlash(pathChars, index, true);
+
+ String element = new String(pathChars, index, nextSlashIndex - index);
+ index = nextSlashIndex;
+
+ // Ignore "."
+ if (".".equals(element)) {
+ continue;
+ }
+
+ // Backtrack ".."
+ if ("..".equals(element)) {
+ if (level == 0) {
+ if (isAbsolutePath) {
+ throw new IllegalStateException(path);
+ } else {
+ buf.append("../");
+ }
+ } else {
+ buf.setLength(pathChars[--level]);
+ }
+
+ continue;
+ }
+
+ pathChars[level++] = (char)buf.length();
+ buf.append(element).append('/');
+ }
+
+ // remove the last "/"
+ if (buf.length() > 0) {
+ if (!endsWithSlash || removeTrailingSlash) {
+ buf.setLength(buf.length() - 1);
+ }
+ }
+
+ return buf.toString();
+ }
+
+ private static int indexOfSlash(char[] chars, int beginIndex, boolean slash) {
+ int i = beginIndex;
+
+ for (; i < chars.length; i++) {
+ char ch = chars[i];
+
+ if (slash) {
+ if (ch == '/' || ch == '\\') {
+ break; // if a slash
+ }
+ } else {
+ if (ch != '/' && ch != '\\') {
+ break; // if not a slash
+ }
+ }
+ }
+
+ return i;
+ }
+
+ private FilterUtil() {}
+}
diff --git a/sentinel-core/pom.xml b/sentinel-core/pom.xml
new file mode 100755
index 00000000..1bccb832
--- /dev/null
+++ b/sentinel-core/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+
+ com.alibaba.csp
+ sentinel-parent
+ 0.1.0
+
+ sentinel-core
+ jar
+ The core of Sentinel
+
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
new file mode 100755
index 00000000..311f932b
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java
@@ -0,0 +1,52 @@
+/*
+ * 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.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.EntranceNode;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
+
+/**
+ * @author qinan.qn
+ * @author youji.zj
+ * @author jialiang.linjl
+ */
+public class Constants {
+
+ public final static int MAX_CONTEXT_NAME_SIZE = 2000;
+ public final static int MAX_SLOT_CHAIN_SIZE = 6000;
+ public final static String ROOT_ID = "machine-root";
+ public final static String CONTEXT_DEFAULT_NAME = "default_context_name";
+
+ public final static DefaultNode ROOT = new EntranceNode(new StringResourceWrapper(ROOT_ID, EntryType.IN),
+ Env.nodeBuilder.buildClusterNode());
+
+ /**
+ * statistics for {@link SystemRule} checking.
+ */
+ public final static ClusterNode ENTRY_NODE = new ClusterNode();
+
+ /**
+ * 超过这个时间的请求不作为平均时间计算
+ */
+ public final static int TIME_DROP_VALVE = 4900;
+
+ /*** 框架功能打开或者关闭的开关 ***/
+ public static volatile boolean ON = true;
+
+}
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java
new file mode 100755
index 00000000..21d178d6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/CtSph.java
@@ -0,0 +1,279 @@
+/*
+ * 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 java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.context.NullContext;
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slotchain.MethodResourceWrapper;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.Rule;
+
+/**
+ * {@inheritDoc}
+ *
+ * @author jialiang.linjl
+ * @author leyou(lihao)
+ * @author Eric Zhao
+ * @see Sph
+ */
+public class CtSph implements Sph {
+
+ private static final Object[] OBJECTS0 = new Object[0];
+
+ /**
+ * Same resource({@link ResourceWrapper#equals(Object)}) will share the same
+ * {@link ProcessorSlotChain}, no matter in which {@link Context}.
+ */
+ private static Map chainMap
+ = new HashMap();
+
+ private static final Object LOCK = new Object();
+
+ /**
+ * Do all {@link Rule}s checking about the resource.
+ *
+ *
Each distinct resource will use a {@link ProcessorSlot} to do rules checking. Same resource will use
+ * same {@link ProcessorSlot} globally.
+ *
+ *
Note that total {@link ProcessorSlot} count must not exceed {@link Constants#MAX_SLOT_CHAIN_SIZE},
+ * otherwise no rules checking will do. In this condition, all requests will pass directly, with no checking
+ * or exception.
+ *
+ * @param resourceWrapper resource name
+ * @param count tokens needed
+ * @param args arguments of user method call
+ * @return {@link Entry} represents this call
+ * @throws BlockException if any rule's threshold is exceeded
+ */
+ public Entry entry(ResourceWrapper resourceWrapper, int count, Object... args) throws BlockException {
+ Context context = ContextUtil.getContext();
+ if (context instanceof NullContext) {
+ // Init the entry only. No rule checking will occur.
+ return new CtEntry(resourceWrapper, null, context);
+ }
+
+ if (context == null) {
+ context = MyContextUtil.myEnter(Constants.CONTEXT_DEFAULT_NAME, "", resourceWrapper.getType());
+ }
+
+ // Global switch is close, no rule checking will do.
+ if (!Constants.ON) {
+ return new CtEntry(resourceWrapper, null, context);
+ }
+
+ ProcessorSlot
+ */
+ private static final class MetricFileNameComparator implements Comparator {
+ @Override
+ public int compare(String o1, String o2) {
+ String name1 = new File(o1).getName();
+ String name2 = new File(o2).getName();
+ String dateStr1 = name1.split("\\.")[2];
+ String dateStr2 = name2.split("\\.")[2];
+
+ // compare date first
+ int t = dateStr1.compareTo(dateStr2);
+ if (t != 0) {
+ return t;
+ }
+
+ // same date, compare file number
+ t = name1.length() - name2.length();
+ if (t != 0) {
+ return t;
+ }
+ return name1.compareTo(name2);
+ }
+ }
+
+ /**
+ * Get all metric files' name in {@code baseDir}. The file name must contain {@code baseFileName}
+ * and not endsWith {@link #METRIC_FILE_INDEX_SUFFIX} or ".lck".
+ *
+ * @param baseDir the directory to search.
+ * @param baseFileName the file name pattern.
+ * @return the metric files' absolute path({@link File#getAbsolutePath()})
+ * @throws Exception
+ */
+ static List listMetricFiles(String baseDir, String baseFileName) throws Exception {
+ List list = new ArrayList();
+ File baseFile = new File(baseDir);
+ File[] files = baseFile.listFiles();
+ if (files == null) {
+ return list;
+ }
+ for (File file : files) {
+ if (file.isFile()
+ && file.getName().contains(baseFileName)
+ && !file.getName().endsWith(MetricWriter.METRIC_FILE_INDEX_SUFFIX)
+ && !file.getName().endsWith(".lck")) {
+ list.add(file.getAbsolutePath());
+ }
+ }
+ Collections.sort(list, MetricWriter.METRIC_FILENAME_CMP);
+ return list;
+ }
+
+ private void removeMoreFiles() throws Exception {
+ List list = listMetricFiles(baseDir, baseFileName);
+ if (list == null || list.isEmpty()) {
+ return;
+ }
+ for (int i = 0; i < list.size() - totalFileCount + 1; i++) {
+ String fileName = list.get(i);
+ String indexFile = formIndexFileName(fileName);
+ new File(fileName).delete();
+ RecordLog.info("remove metric file: " + fileName);
+ new File(indexFile).delete();
+ RecordLog.info("remove metric index file: " + indexFile);
+ }
+ }
+
+ private void closeAndNewFile(String fileName) throws Exception {
+ removeMoreFiles();
+ if (outMetricBuf != null) {
+ outMetricBuf.close();
+ }
+ if (outIndex != null) {
+ outIndex.close();
+ }
+ outMetric = new FileOutputStream(fileName, append);
+ outMetricBuf = new BufferedOutputStream(outMetric);
+ curMetricFile = new File(fileName);
+ String idxFile = formIndexFileName(fileName);
+ ;
+ curMetricIndexFile = new File(idxFile);
+ outIndex = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(idxFile, append)));
+ RecordLog.info("create new metric file: " + fileName);
+ RecordLog.info("create new metric index file: " + idxFile);
+ }
+
+ private boolean validSize() throws Exception {
+ long size = outMetric.getChannel().size();
+ return size < singleFileSize;
+ }
+
+ private boolean isNewDay(long lastSecond, long second) {
+ long lastDay = (lastSecond - timeSecondBase) / 86400;
+ long newDay = (second - timeSecondBase) / 86400;
+ return newDay > lastDay;
+ }
+
+ /**
+ * Form metric file name use the specific appName and pid. Not that only
+ * form the file name, not include path.
+ *
+ * @param appName
+ * @param pid
+ * @return metric file name.
+ */
+ public static String formMetricFileName(String appName, int pid) {
+ if (appName == null) {
+ appName = "";
+ }
+ return appName + "-" + pid + METRIC_FILE_SUFFIX;
+ }
+
+ /**
+ * Form index file name of the {@code metricFileName}
+ *
+ * @param metricFileName
+ * @return the index file name of the metricFileName
+ */
+ public static String formIndexFileName(String metricFileName) {
+ return metricFileName + METRIC_FILE_INDEX_SUFFIX;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java
new file mode 100755
index 00000000..50716397
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/DynamicSentinelProperty.java
@@ -0,0 +1,77 @@
+/*
+ * 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.property;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+
+public class DynamicSentinelProperty implements SentinelProperty {
+
+ protected Set> listeners = Collections.synchronizedSet(new HashSet>());
+ private T value = null;
+
+ public DynamicSentinelProperty() {
+ }
+
+ public DynamicSentinelProperty(T value) {
+ super();
+ this.value = value;
+ }
+
+ @Override
+ public void addListener(PropertyListener listener) {
+ listeners.add(listener);
+ listener.configLoad(value);
+ }
+
+ @Override
+ public void removeListener(PropertyListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public void updateValue(T newValue) {
+ if (isEqual(value, newValue)) {
+ return;
+ }
+ RecordLog.info("SentinelProperty, config is real updated to: " + newValue);
+
+ value = newValue;
+ for (PropertyListener listener : listeners) {
+ listener.configUpdate(newValue);
+ }
+
+ }
+
+ public boolean isEqual(T oldValue, T newValue) {
+ if (oldValue == null && newValue == null) {
+ return true;
+ }
+
+ if (oldValue == null && newValue != null) {
+ return false;
+ }
+
+ return oldValue.equals(newValue);
+ }
+
+ public void close() {
+ listeners.clear();
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/NoOpSentinelProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/NoOpSentinelProperty.java
new file mode 100755
index 00000000..c1a704fc
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/NoOpSentinelProperty.java
@@ -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.property;
+
+/**
+ * A {@link SentinelProperty} that will never inform the {@link PropertyListener} on it.
+ *
+ * @author leyou
+ */
+public final class NoOpSentinelProperty implements SentinelProperty {
+ @Override
+ public void addListener(PropertyListener listener) { }
+
+ @Override
+ public void removeListener(PropertyListener listener) { }
+
+ @Override
+ public void updateValue(Object newValue) { }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/PropertyListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/PropertyListener.java
new file mode 100755
index 00000000..69e4c960
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/PropertyListener.java
@@ -0,0 +1,38 @@
+/*
+ * 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.property;
+
+/**
+ * This class holds callback method when {@link SentinelProperty#updateValue(Object)} need inform the listener
+ *
+ * @author jialiang.linjl
+ */
+public interface PropertyListener {
+
+ /**
+ * Callback method when {@link SentinelProperty#updateValue(Object)} need inform the listener.
+ *
+ * @param value updated value.
+ */
+ void configUpdate(T value);
+
+ /**
+ * The first time of the {@code value}'s load.
+ *
+ * @param value the value loaded.
+ */
+ void configLoad(T value);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SentinelProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SentinelProperty.java
new file mode 100755
index 00000000..c71d9eb5
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SentinelProperty.java
@@ -0,0 +1,61 @@
+/*
+ * 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.property;
+
+/**
+ *
+ * This class holds current value of the config, and is responsible for informing all {@link PropertyListener}s
+ * added on this when the config is updated.
+ *
+ *
+ * Note that not every {@link #updateValue(Object newValue)} invocation should inform the listeners, only when
+ * {@code newValue} is not Equals to the old value, informing is needed.
+ *
+ * Add a {@link PropertyListener} to this {@link SentinelProperty}. After the listener is added,
+ * {@link #updateValue(Object)} will inform the listener if needed.
+ *
+ *
+ * This method can invoke multi times to add more than one listeners.
+ *
+ *
+ * @param listener listener to add.
+ */
+ void addListener(PropertyListener listener);
+
+ /**
+ * Remove the {@link PropertyListener} on this. After removing, {@link #updateValue(Object)}
+ * will not inform the listener.
+ *
+ * @param listener the listener to remove.
+ */
+ void removeListener(PropertyListener listener);
+
+ /**
+ * Update the {@code newValue} as the current value of this property and inform all {@link PropertyListener}s
+ * added on this only when new {@code newValue} is not Equals to the old value.
+ *
+ * @param newValue the new value.
+ */
+ void updateValue(T newValue);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SimplePropertyListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SimplePropertyListener.java
new file mode 100755
index 00000000..0e950db1
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/property/SimplePropertyListener.java
@@ -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.property;
+
+public abstract class SimplePropertyListener implements PropertyListener {
+
+ @Override
+ public void configLoad(T value) {
+ configUpdate(value);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/AbstractLinkedProcessorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/AbstractLinkedProcessorSlot.java
new file mode 100755
index 00000000..3d10186f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/AbstractLinkedProcessorSlot.java
@@ -0,0 +1,58 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.context.Context;
+
+/**
+ * @author qinan.qn
+ * @author jialiang.linjl
+ */
+public abstract class AbstractLinkedProcessorSlot implements ProcessorSlot {
+
+ private AbstractLinkedProcessorSlot> next = null;
+
+ @Override
+ public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
+ throws Throwable {
+ if (next != null) {
+ next.transformEntry(context, resourceWrapper, obj, count, args);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, Object... args)
+ throws Throwable {
+ T t = (T)o;
+ entry(context, resourceWrapper, t, count, args);
+ }
+
+ @Override
+ public void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ if (next != null) {
+ next.exit(context, resourceWrapper, count, args);
+ }
+ }
+
+ public AbstractLinkedProcessorSlot> getNext() {
+ return next;
+ }
+
+ public void setNext(AbstractLinkedProcessorSlot> next) {
+ this.next = next;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/DefaultProcessorSlotChain.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/DefaultProcessorSlotChain.java
new file mode 100755
index 00000000..b48301e2
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/DefaultProcessorSlotChain.java
@@ -0,0 +1,83 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.context.Context;
+
+/**
+ * @author qinan.qn
+ * @author jialiang.linjl
+ */
+public class DefaultProcessorSlotChain extends ProcessorSlotChain {
+
+ AbstractLinkedProcessorSlot> first = new AbstractLinkedProcessorSlot() {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, Object... args)
+ throws Throwable {
+ super.fireEntry(context, resourceWrapper, t, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ super.fireExit(context, resourceWrapper, count, args);
+ }
+
+ };
+ AbstractLinkedProcessorSlot> end = first;
+
+ @Override
+ public void addFirst(AbstractLinkedProcessorSlot> protocolProcessor) {
+ protocolProcessor.setNext(first.getNext());
+ first.setNext(protocolProcessor);
+ if (end == first) {
+ end = protocolProcessor;
+ }
+ }
+
+ @Override
+ public void addLast(AbstractLinkedProcessorSlot> protocolProcessor) {
+ end.setNext(protocolProcessor);
+ end = protocolProcessor;
+ }
+
+ /**
+ * Same as {@link #addLast(AbstractLinkedProcessorSlot)}.
+ *
+ * @param next processor to be added.
+ */
+ @Override
+ public void setNext(AbstractLinkedProcessorSlot> next) {
+ addLast(next);
+ }
+
+ @Override
+ public AbstractLinkedProcessorSlot> getNext() {
+ return first.getNext();
+ }
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, Object... args)
+ throws Throwable {
+ first.transformEntry(context, resourceWrapper, t, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ first.exit(context, resourceWrapper, count, args);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/MethodResourceWrapper.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/MethodResourceWrapper.java
new file mode 100755
index 00000000..4d8b4c33
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/MethodResourceWrapper.java
@@ -0,0 +1,58 @@
+/*
+ * 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.slotchain;
+
+import java.lang.reflect.Method;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.util.IdUtil;
+import com.alibaba.csp.sentinel.util.MethodUtil;
+
+/**
+ * Resource wrapper for method invocation.
+ *
+ * @author qinan.qn
+ */
+public class MethodResourceWrapper extends ResourceWrapper {
+
+ private transient Method method;
+
+ public MethodResourceWrapper(Method method, EntryType type) {
+ this.method = method;
+ this.name = MethodUtil.getMethodName(method);
+ this.type = type;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ public Method getMethod() {
+ return method;
+ }
+
+ @Override
+ public String getShowName() {
+ return IdUtil.truncate(this.name);
+ }
+
+ @Override
+ public EntryType getType() {
+ return type;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java
new file mode 100755
index 00000000..d4f0af5b
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java
@@ -0,0 +1,74 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.context.Context;
+
+/**
+ * A container of some process and ways of notification when the process is finished.
+ *
+ * @author qinan.qn
+ * @author jialiang.linjl
+ * @author leyou(lihao)
+ */
+public interface ProcessorSlot {
+
+ /**
+ * Entrance of this slot.
+ *
+ * @param context current {@link Context}
+ * @param resourceWrapper current resource
+ * @param param Generics parameter, usually is a {@link com.alibaba.csp.sentinel.node.Node}
+ * @param count tokens needed
+ * @param args parameters of the original call
+ * @throws Throwable
+ */
+ void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args)
+ throws Throwable;
+
+ /**
+ * Means finish of {@link #entry(Context, ResourceWrapper, Object, int, Object...)}.
+ *
+ * @param context current {@link Context}
+ * @param resourceWrapper current resource
+ * @param obj
+ * @param count tokens needed
+ * @param args parameters of the original call
+ * @throws Throwable
+ */
+ void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
+ throws Throwable;
+
+ /**
+ * Exit of this slot.
+ *
+ * @param context current {@link Context}
+ * @param resourceWrapper current resource
+ * @param count tokens needed
+ * @param args parameters of the original call
+ */
+ void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
+
+ /**
+ * Means finish of {@link #exit(Context, ResourceWrapper, int, Object...)}.
+ *
+ * @param context current {@link Context}
+ * @param resourceWrapper current resource
+ * @param count tokens needed
+ * @param args parameters of the original call
+ */
+ void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotChain.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotChain.java
new file mode 100755
index 00000000..5a17681e
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotChain.java
@@ -0,0 +1,38 @@
+/*
+ * 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.slotchain;
+
+/**
+ * Link all processor slots as a chain.
+ *
+ * @author qinan.qn
+ */
+public abstract class ProcessorSlotChain extends AbstractLinkedProcessorSlot {
+
+ /**
+ * Add a processor to the head of this slot chain.
+ *
+ * @param protocolProcessor processor to be added.
+ */
+ public abstract void addFirst(AbstractLinkedProcessorSlot> protocolProcessor);
+
+ /**
+ * Add a processor to the tail of this slot chain.
+ *
+ * @param protocolProcessor processor to be added.
+ */
+ public abstract void addLast(AbstractLinkedProcessorSlot> protocolProcessor);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ResourceWrapper.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ResourceWrapper.java
new file mode 100755
index 00000000..a796344c
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ResourceWrapper.java
@@ -0,0 +1,61 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.EntryType;
+
+/**
+ * A wrapper of resource name and {@link EntryType}.
+ *
+ * @author qinan.qn
+ * @author jialiang.linjl
+ */
+public abstract class ResourceWrapper {
+
+ protected String name;
+ protected EntryType type = EntryType.OUT;
+
+ public abstract String getName();
+
+ public abstract String getShowName();
+
+ /**
+ * Get {@link EntryType} of this wrapper.
+ *
+ * @return {@link EntryType} of this wrapper.
+ */
+ public abstract EntryType getType();
+
+ /**
+ * Only {@link #getName()} is considered.
+ */
+ @Override
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ /**
+ * Only {@link #getName()} is considered.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ResourceWrapper) {
+ ResourceWrapper rw = (ResourceWrapper)obj;
+ return rw.getName().equals(getName());
+ }
+ return false;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/StringResourceWrapper.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/StringResourceWrapper.java
new file mode 100755
index 00000000..a9096356
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/StringResourceWrapper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.EntryType;
+
+/**
+ * Common resource wrapper.
+ *
+ * @author qinan.qn
+ * @author jialiang.linjl
+ */
+public class StringResourceWrapper extends ResourceWrapper {
+
+ public StringResourceWrapper(String name, EntryType type) {
+ if (name == null) {
+ throw new IllegalArgumentException("Resource name cannot be null");
+ }
+ this.name = name;
+ this.type = type;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getShowName() {
+ return name;
+ }
+
+ @Override
+ public EntryType getType() {
+ return type;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotsChainBuilder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotsChainBuilder.java
new file mode 100755
index 00000000..5da3d5fa
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/DefaultSlotsChainBuilder.java
@@ -0,0 +1,52 @@
+/*
+ * 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.slots;
+
+import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
+import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+import com.alibaba.csp.sentinel.slots.logger.LogSlot;
+import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
+import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
+import com.alibaba.csp.sentinel.slots.system.SystemSlot;
+
+/**
+ * Helper class to create {@link ProcessorSlotChain}.
+ *
+ * @author qinan.qn
+ * @author leyou
+ */
+public class DefaultSlotsChainBuilder implements SlotsChainBuilder {
+
+ @Override
+ public ProcessorSlotChain build() {
+ ProcessorSlotChain chain = new DefaultProcessorSlotChain();
+ chain.addLast(new NodeSelectorSlot());
+ chain.addLast(new ClusterBuilderSlot());
+ chain.addLast(new LogSlot());
+ chain.addLast(new StatisticSlot());
+ chain.addLast(new SystemSlot());
+ chain.addLast(new AuthoritySlot());
+ chain.addLast(new FlowSlot());
+ chain.addLast(new DegradeSlot());
+
+ return chain;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/SlotsChainBuilder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/SlotsChainBuilder.java
new file mode 100755
index 00000000..59db01a6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/SlotsChainBuilder.java
@@ -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.slots;
+
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
+
+/**
+ * @author qinan.qn
+ * @author leyou
+ */
+public interface SlotsChainBuilder {
+
+ /**
+ * Helper method to create processor slot chain.
+ *
+ * @return a processor slot that chain some slots together.
+ */
+ ProcessorSlotChain build();
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java
new file mode 100755
index 00000000..ea7e7a3f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/AbstractRule.java
@@ -0,0 +1,94 @@
+/*
+ * 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.slots.block;
+
+/***
+ * @author youji.zj
+ */
+public abstract class AbstractRule implements Rule {
+
+ /*** 规则的资源描述 ***/
+ private String resource;
+
+ /*** 被限制的应用,授权时候为逗号分隔的应用集合,限流时为单个应用 ***/
+ private String limitApp;
+
+ public String getResource() {
+ return resource;
+ }
+
+ public AbstractRule setResource(String resource) {
+ this.resource = resource;
+ return this;
+ }
+
+ public String getLimitApp() {
+ return limitApp;
+ }
+
+ public AbstractRule setLimitApp(String limitApp) {
+ this.limitApp = limitApp;
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof AbstractRule)) {
+ return false;
+ }
+
+ AbstractRule that = (AbstractRule)o;
+
+ if (resource != null ? !resource.equals(that.resource) : that.resource != null) {
+ return false;
+ }
+ // if (limitApp != null ? !limitApp.equals(that.limitApp) :
+ // that.limitApp != null) { return false; }
+ if (!limitAppEquals(limitApp, that.limitApp)) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean limitAppEquals(String str1, String str2) {
+ if ("".equals(str1)) {
+ return "default".equals(str2);
+ } else if ("default".equals(str1)) {
+ return "".equals(str2) || str2 == null || str1.equals(str2);
+ }
+ if (str1 == null) {
+ return str2 == null || "default".equals(str2);
+ }
+ return str1.equals(str2);
+ }
+
+ public T as(Class clazz) {
+ return (T)this;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = resource != null ? resource.hashCode() : 0;
+ // result = 31 * result + (limitApp != null ? limitApp.hashCode() : 0);
+ if (!("".equals(limitApp) || "default".equals(limitApp) || limitApp == null)) {
+ result = 31 * result + limitApp.hashCode();
+ }
+ return result;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java
new file mode 100755
index 00000000..605b7b7c
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/BlockException.java
@@ -0,0 +1,101 @@
+/*
+ * 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.slots.block;
+
+/***
+ * Abstract exception indicating blocked by Sentinel due to flow control, degraded or system guard.
+ *
+ * @author youji.zj
+ */
+public abstract class BlockException extends Exception {
+
+ public static final String BLOCK_EXCEPTION_FLAG = "SentinelBlockException";
+ /**
+ *
this constant RuntimeException has no stack trace, just has a message
+ * {@link #BLOCK_EXCEPTION_FLAG} that marks its name.
+ *
+ *
+ * Use {@link #isBlockException(Throwable)} to check whether one Exception
+ * Sentinel Blocked Exception.
+ *
+ */
+ public static RuntimeException THROW_OUT_EXCEPTION = new RuntimeException(BLOCK_EXCEPTION_FLAG);
+
+ public static StackTraceElement[] sentinelStackTrace = new StackTraceElement[] {
+ new StackTraceElement(BlockException.class.getName(), "block", "BlockException", 0)
+ };
+
+ static {
+ THROW_OUT_EXCEPTION.setStackTrace(sentinelStackTrace);
+ }
+
+ private String ruleLimitApp;
+
+ public BlockException(String ruleLimitApp) {
+ super();
+ this.ruleLimitApp = ruleLimitApp;
+ }
+
+ public BlockException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public BlockException(String ruleLimitApp, String message) {
+ super(message);
+ this.ruleLimitApp = ruleLimitApp;
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+
+ public String getRuleLimitApp() {
+ return ruleLimitApp;
+ }
+
+ public void setRuleLimitApp(String ruleLimitApp) {
+ this.ruleLimitApp = ruleLimitApp;
+ }
+
+ /**
+ * Check whether the exception is sentinel blocked exception. One exception is sentinel blocked
+ * exception only when:
+ *
+ *
the exception or its (sub-)cause is {@link BlockException}, or
+ *
the exception's message is or any of its sub-cause's message equals to {@link #BLOCK_EXCEPTION_FLAG}
+ *
+ *
+ * @param t the exception.
+ * @return return true if the exception marks sentinel blocked exception.
+ */
+ public static boolean isBlockException(Throwable t) {
+ if (null == t) {
+ return false;
+ }
+
+ int counter = 0;
+ Throwable cause = t;
+ while (cause != null && counter++ < 50) {
+ if ((cause instanceof BlockException) || (BLOCK_EXCEPTION_FLAG.equals(cause.getMessage()))) {
+ return true;
+ }
+ cause = cause.getCause();
+ }
+
+ return false;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java
new file mode 100755
index 00000000..e429b2a5
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/Rule.java
@@ -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.slots.block;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+
+/**
+ * Base interface of all rules.
+ *
+ * @author youji.zj
+ */
+public interface Rule {
+
+ /**
+ * Check whether current statistical indicators meet this rule, which means not exceeding any threshold.
+ *
+ * @param context current {@link Context}
+ * @param node current {@link com.alibaba.csp.sentinel.node.Node}
+ * @param count tokens needed.
+ * @param args arguments of the original invocation.
+ * @return If current statistical indicators not exceeding any threshold return true, otherwise return false.
+ */
+ boolean passCheck(Context context, DefaultNode node, int count, Object... args);
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java
new file mode 100755
index 00000000..645b38a6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/RuleConstant.java
@@ -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.slots.block;
+
+/***
+ * @author youji.zj
+ * @author jialiang.linjl
+ */
+public class RuleConstant {
+
+ public static final int FLOW_GRADE_THREAD = 0;
+ public static final int FLOW_GRADE_QPS = 1;
+
+ public static final int DEGRADE_GRADE_RT = 0;
+ public static final int DEGRADE_GRADE_EXCEPTION = 1;
+
+ public static final int WHILE = 0;
+ public static final int BLACK = 1;
+
+ public static final int STRATEGY_DIRECT = 0;
+ public static final int STRATEGY_RELATE = 1;
+ public static final int STRATEGY_CHAIN = 2;
+
+ public static final int CONTROL_BEHAVIOR_DEFAULT = 0;
+ public static final int CONTROL_BEHAVIOR_WARM_UP = 1;
+ public static final int CONTROL_BEHAVIOR_RATE_LIMITER = 2;
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/SentinelRpcException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/SentinelRpcException.java
new file mode 100755
index 00000000..1025fee7
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/SentinelRpcException.java
@@ -0,0 +1,38 @@
+/*
+ * 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.slots.block;
+
+/**
+ * A {@link RuntimeException} marks sentinel RPC exception. The stack trace
+ * is removed for high performance.
+ *
+ * @author leyou
+ */
+public class SentinelRpcException extends RuntimeException {
+
+ public SentinelRpcException(String msg) {
+ super(msg);
+ }
+
+ public SentinelRpcException(Throwable e) {
+ super(e);
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityException.java
new file mode 100755
index 00000000..e007ec65
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.slots.block.authority;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/***
+ *
+ * @author youji.zj
+ */
+public class AuthorityException extends BlockException {
+
+ public AuthorityException(String ruleLimitApp) {
+ super(ruleLimitApp);
+ }
+
+ public AuthorityException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public AuthorityException(String ruleLimitApp, String message) {
+ super(ruleLimitApp, message);
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
new file mode 100755
index 00000000..ea97f6b8
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRule.java
@@ -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.slots.block.authority;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+
+/***
+ *
+ * @author youji.zj
+ */
+public class AuthorityRule extends AbstractRule {
+
+ /*** 0代表白名单;1代表黑名单 ***/
+ private int strategy;
+
+ public int getStrategy() {
+ return strategy;
+ }
+
+ public void setStrategy(int strategy) {
+ this.strategy = strategy;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) { return true; }
+ if (!(o instanceof AuthorityRule)) { return false; }
+ if (!super.equals(o)) { return false; }
+
+ AuthorityRule rule = (AuthorityRule)o;
+
+ return strategy == rule.strategy;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + strategy;
+ return result;
+ }
+
+ @Override
+ public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
+ String requester = context.getOrigin();
+
+ // 来源或者限流的应用为null直接通过
+ if (StringUtil.isEmpty(requester) || this.getLimitApp() == null) {
+ return true;
+ }
+
+ // 白名单、黑名单列表为逗号分隔的应用列表, indexOf还不行,需要精确匹配
+ int pos = this.getLimitApp().indexOf(requester);
+ boolean contain = pos > -1;
+
+ if (contain) {
+ boolean exactlyMatch = false;
+ String[] appArray = this.getLimitApp().split(",");
+ for (String app : appArray) {
+ if (requester.equals(app)) {
+ exactlyMatch = true;
+ break;
+ }
+ }
+
+ contain = exactlyMatch;
+ }
+
+ if (strategy == RuleConstant.BLACK && contain) {
+ return false;
+ }
+
+ if (strategy == RuleConstant.WHILE && !contain) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "AuthorityRule{" +
+ "resource=" + getResource() +
+ ", limitApp=" + getLimitApp() +
+ ", strategy=" + strategy +
+ "} " + super.toString();
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
new file mode 100755
index 00000000..2c762282
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthorityRuleManager.java
@@ -0,0 +1,156 @@
+/*
+ * 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.slots.block.authority;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+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.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+
+/***
+ * @author youji.zj
+ * @author jialiang.linjl
+ */
+public class AuthorityRuleManager {
+
+ private static Map> authorityRules
+ = new ConcurrentHashMap>();
+
+ final static RulePropertyListener listener = new RulePropertyListener();
+
+ private static SentinelProperty> currentProperty
+ = new DynamicSentinelProperty>();
+
+ static {
+ currentProperty.addListener(listener);
+ }
+
+ public static void register2Property(SentinelProperty> property) {
+ synchronized (listener) {
+ if (currentProperty != null) {
+ currentProperty.removeListener(listener);
+ }
+ property.addListener(listener);
+ currentProperty = property;
+ }
+ }
+
+ /**
+ * @param rules
+ */
+ public static void loadRules(List rules) {
+ currentProperty.updateValue(rules);
+ }
+
+ public static void checkAuthority(ResourceWrapper resource, Context context, DefaultNode node, int count)
+ throws BlockException {
+ if (authorityRules == null) {
+ return;
+ }
+
+ List rules = authorityRules.get(resource.getName());
+ if (rules == null) {
+ return;
+ }
+
+ for (AuthorityRule rule : rules) {
+ if (!rule.passCheck(context, node, count)) {
+ throw new AuthorityException(context.getOrigin());
+ }
+ }
+ }
+
+ public static boolean hasConfig(String resource) {
+ return authorityRules.containsKey(resource);
+ }
+
+ /**
+ * Get a copy of the rules.
+ *
+ * @return a new copy of the rules.
+ */
+ public static List getRules() {
+ List rules = new ArrayList();
+ if (authorityRules == null) {
+ return rules;
+ }
+ for (Map.Entry> entry : authorityRules.entrySet()) {
+ rules.addAll(entry.getValue());
+ }
+ return rules;
+ }
+
+ private static class RulePropertyListener implements PropertyListener> {
+
+ @Override
+ public void configUpdate(List conf) {
+ Map> rules = loadAuthorityConf(conf);
+
+ authorityRules.clear();
+ if (rules != null) {
+ authorityRules.putAll(rules);
+ }
+ RecordLog.info("receive authority config: " + authorityRules);
+ }
+
+ private Map> loadAuthorityConf(List list) {
+ if (list == null) {
+ return null;
+ }
+ Map> newRuleMap = new ConcurrentHashMap>();
+ for (AuthorityRule rule : list) {
+ if (StringUtil.isBlank(rule.getLimitApp())) {
+ rule.setLimitApp(FlowRule.DEFAULT);
+ }
+
+ String identity = rule.getResource();
+ List ruleM = newRuleMap.get(identity);
+ if (ruleM == null) {
+ ruleM = new ArrayList();
+ newRuleMap.put(identity, ruleM);
+ }
+ ruleM.add(rule);
+
+ }
+
+ return newRuleMap;
+ }
+
+ @Override
+ public void configLoad(List value) {
+ Map> rules = loadAuthorityConf(value);
+
+ authorityRules.clear();
+ if (rules != null) {
+ authorityRules.putAll(rules);
+ }
+ RecordLog.info("load authority config: " + authorityRules);
+ }
+
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
new file mode 100755
index 00000000..1101d891
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/authority/AuthoritySlot.java
@@ -0,0 +1,42 @@
+/*
+ * 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.slots.block.authority;
+
+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.ProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+
+/**
+ * A {@link ProcessorSlot} that dedicates to {@link AuthorityRule} checking.
+ *
+ * @author leyou
+ */
+public class AuthoritySlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+ AuthorityRuleManager.checkAuthority(resourceWrapper, context, node, count);
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeException.java
new file mode 100755
index 00000000..1538373d
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeException.java
@@ -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.slots.block.degrade;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/***
+ * @author youji.zj
+ */
+public class DegradeException extends BlockException {
+
+ public DegradeException(String ruleLimitApp) {
+ super(ruleLimitApp);
+ }
+
+ public DegradeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public DegradeException(String ruleLimitApp, String message) {
+ super(ruleLimitApp, message);
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java
new file mode 100755
index 00000000..48b9a810
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRule.java
@@ -0,0 +1,238 @@
+/*
+ * 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.slots.block.degrade;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+/**
+ *
+ * Degrade is used when the resources are in an unstable state, these resources
+ * will be degraded within the next defined time window. There are two ways to
+ * measure whether a resource is stable or not:
+ *
+ *
+ *
+ * Average response time ({@code DEGRADE_GRADE_RT}): When
+ * the average RT exceeds the threshold ('count' in 'DegradeRule', in milliseconds), the
+ * resource enters a quasi-degraded state. If the RT of next coming 5
+ * requests still exceed this threshold, this resource will be downgraded, which
+ * means that in the next time window (defined in 'timeWindow', in seconds) all the
+ * access to this resource will be blocked.
+ *
+ *
+ * Exception ratio: When the ratio of exception count per second and the
+ * success qps exceeds the threshold, access to the resource will be blocked in
+ * the coming window.
+ *
+ *
+ *
+ * @author jialiang.linjl
+ */
+public class DegradeRule extends AbstractRule {
+
+ private static final int RT_MAX_EXCEED_N = 5;
+
+ private static ScheduledExecutorService pool = Executors.newScheduledThreadPool(
+ Runtime.getRuntime().availableProcessors());
+
+ /**
+ * RT threshold or exception ratio threshold count.
+ */
+ private double count;
+
+ /**
+ * Degrade recover timeout (in seconds) when degradation occurs.
+ */
+ private int timeWindow;
+
+ /**
+ * Degrade strategy (0: average RT, 1: exception ratio).
+ */
+ private int grade = RuleConstant.DEGRADE_GRADE_RT;
+
+ private volatile boolean cut = false;
+
+ public int getGrade() {
+ return grade;
+ }
+
+ public void setGrade(int grade) {
+ this.grade = grade;
+ }
+
+ private AtomicLong passCount = new AtomicLong(0);
+
+ private final Object lock = new Object();
+
+ public double getCount() {
+ return count;
+ }
+
+ public void setCount(double count) {
+ this.count = count;
+ }
+
+ public boolean isCut() {
+ return cut;
+ }
+
+ public void setCut(boolean cut) {
+ this.cut = cut;
+ }
+
+ public AtomicLong getPassCount() {
+ return passCount;
+ }
+
+ public void setPassCount(AtomicLong passCount) {
+ this.passCount = passCount;
+ }
+
+ public int getTimeWindow() {
+ return timeWindow;
+ }
+
+ public void setTimeWindow(int timeWindow) {
+ this.timeWindow = timeWindow;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof DegradeRule)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ DegradeRule that = (DegradeRule)o;
+
+ if (count != that.count) {
+ return false;
+ }
+ if (timeWindow != that.timeWindow) {
+ return false;
+ }
+ if (grade != that.grade) {
+ return false;
+ }
+ // if (cut != that.cut) { return false; }
+ //// AtomicLong dose not Override equals()
+ // if ((passCount == null && that.passCount != null)
+ // || (passCount.get() != that.passCount.get())) {
+ // return false;
+ // }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + new Double(count).hashCode();
+ result = 31 * result + timeWindow;
+ result = 31 * result + grade;
+ // result = 31 * result + (cut ? 1 : 0);
+ // result = 31 * result + (passCount != null ? (int)passCount.get() :
+ // 0);
+ return result;
+ }
+
+ @Override
+ public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
+
+ if (cut) {
+ return false;
+ }
+
+ ClusterNode clusterNode = ClusterBuilderSlot.getClusterNode(this.getResource());
+ if (clusterNode == null) {
+ return true;
+ }
+
+ if (grade == RuleConstant.DEGRADE_GRADE_RT) {
+ double rt = clusterNode.avgRt();
+ if (rt < this.count) {
+ return true;
+ }
+
+ // Sentinel will degrade the service only if count exceeds.
+ if (passCount.incrementAndGet() < RT_MAX_EXCEED_N) {
+ return true;
+ }
+ } else {
+ double exception = clusterNode.exceptionQps();
+ double success = clusterNode.successQps();
+ if (success == 0) {
+ return true;
+ }
+
+ if (exception / success < count) {
+ return true;
+ }
+ }
+
+ synchronized (lock) {
+ if (!cut) {
+ // Automatically degrade.
+ cut = true;
+ ResetTask resetTask = new ResetTask(this);
+ pool.schedule(resetTask, timeWindow, TimeUnit.SECONDS);
+ }
+
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "DegradeRule{" +
+ "resource=" + getResource() +
+ ", grade=" + grade +
+ ", count=" + count +
+ ", limitApp=" + getLimitApp() +
+ ", timeWindow=" + timeWindow +
+ "}";
+ }
+
+ private static final class ResetTask implements Runnable {
+
+ private DegradeRule rule;
+
+ ResetTask(DegradeRule rule) {
+ this.rule = rule;
+ }
+
+ @Override
+ public void run() {
+ rule.getPassCount().set(0);
+ rule.setCut(false);
+ }
+ }
+}
+
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java
new file mode 100755
index 00000000..fbc43fff
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeRuleManager.java
@@ -0,0 +1,163 @@
+/*
+ * 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.slots.block.degrade;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+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.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/***
+ * @author youji.zj
+ * @author jialiang.linjl
+ */
+public class DegradeRuleManager {
+
+ private static volatile Map> degradeRules
+ = new ConcurrentHashMap>();
+
+ final static RulePropertyListener listener = new RulePropertyListener();
+ private static SentinelProperty> currentProperty
+ = new DynamicSentinelProperty>();
+
+ static {
+ currentProperty.addListener(listener);
+ }
+
+ /**
+ * Listen to the {@link SentinelProperty} for {@link DegradeRule}s. The property is the source
+ * of {@link DegradeRule}s. Degrade rules can also be set by {@link #loadRules(List)} directly.
+ *
+ * @param property the property to listen.
+ */
+ public static void register2Property(SentinelProperty> property) {
+ synchronized (listener) {
+ currentProperty.removeListener(listener);
+ property.addListener(listener);
+ currentProperty = property;
+ }
+ }
+
+ public static void checkDegrade(ResourceWrapper resource, Context context, DefaultNode node, int count)
+ throws BlockException {
+ if (degradeRules == null) {
+ return;
+ }
+
+ List rules = degradeRules.get(resource.getName());
+ if (rules == null) {
+ return;
+ }
+
+ for (DegradeRule rule : rules) {
+ if (!rule.passCheck(context, node, count)) {
+ throw new DegradeException(rule.getLimitApp());
+ }
+ }
+ }
+
+ public static boolean hasConfig(String resource) {
+ return degradeRules.containsKey(resource);
+ }
+
+ /**
+ * Get a copy of the rules.
+ *
+ * @return a new copy of the rules.
+ */
+ public static List getRules() {
+ List rules = new ArrayList();
+ if (degradeRules == null) {
+ return rules;
+ }
+ for (Map.Entry> entry : degradeRules.entrySet()) {
+ rules.addAll(entry.getValue());
+ }
+ return rules;
+ }
+
+ /**
+ * Load {@link DegradeRule}s, former rules will be replaced.
+ *
+ * @param rules new rules to load.
+ */
+ public static void loadRules(List rules) {
+ try {
+ currentProperty.updateValue(rules);
+ } catch (Throwable e) {
+ RecordLog.info(e.getMessage(), e);
+ }
+ }
+
+ private static class RulePropertyListener implements PropertyListener> {
+
+ @Override
+ public void configUpdate(List conf) {
+ Map> rules = loadDegradeConf(conf);
+ if (rules != null) {
+ degradeRules.clear();
+ degradeRules.putAll(rules);
+ }
+ RecordLog.info("receive degrade config: " + degradeRules);
+ }
+
+ @Override
+ public void configLoad(List conf) {
+ Map> rules = loadDegradeConf(conf);
+ if (rules != null) {
+ degradeRules.clear();
+ degradeRules.putAll(rules);
+ }
+ RecordLog.info("init degrade config: " + degradeRules);
+ }
+
+ private Map> loadDegradeConf(List list) {
+ if (list == null) {
+ return null;
+ }
+ Map> newRuleMap = new ConcurrentHashMap>();
+
+ for (DegradeRule rule : list) {
+ if (StringUtil.isBlank(rule.getLimitApp())) {
+ rule.setLimitApp(FlowRule.DEFAULT);
+ }
+
+ String identity = rule.getResource();
+ List ruleM = newRuleMap.get(identity);
+ if (ruleM == null) {
+ ruleM = new ArrayList();
+ newRuleMap.put(identity, ruleM);
+ }
+ ruleM.add(rule);
+ }
+
+ return newRuleMap;
+ }
+
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
new file mode 100755
index 00000000..533a9350
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeSlot.java
@@ -0,0 +1,42 @@
+/*
+ * 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.slots.block.degrade;
+
+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.ProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+
+/**
+ * A {@link ProcessorSlot} dedicates to {@link DegradeRule} checking.
+ *
+ * @author leyou
+ */
+public class DegradeSlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+ DegradeRuleManager.checkDegrade(resourceWrapper, context, node, count);
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ColdFactorProperty.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ColdFactorProperty.java
new file mode 100755
index 00000000..679d86af
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ColdFactorProperty.java
@@ -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.slots.block.flow;
+
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+/**
+ * @author jialiang.linjl
+ */
+class ColdFactorProperty {
+ public static volatile int coldFactor = 3;
+
+ static {
+ String strConfig = SentinelConfig.getConfig(SentinelConfig.COLD_FACTOR);
+ if (StringUtil.isBlank(strConfig)) {
+ coldFactor = 3;
+ } else {
+ try {
+ coldFactor = Integer.valueOf(strConfig);
+ } catch (NumberFormatException e) {
+ RecordLog.info(e.getMessage(), e);
+ }
+
+ if (coldFactor <= 1) {
+ coldFactor = 3;
+ RecordLog.info("cold factor should be larger than 3");
+ }
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/Controller.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/Controller.java
new file mode 100755
index 00000000..ab58e751
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/Controller.java
@@ -0,0 +1,27 @@
+/*
+ * 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.slots.block.flow;
+
+import com.alibaba.csp.sentinel.node.Node;
+
+/**
+ * @author jialiang.linjl
+ */
+public interface Controller {
+
+ boolean canPass(Node node, int acquireCount);
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowException.java
new file mode 100755
index 00000000..81c2a0e3
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowException.java
@@ -0,0 +1,43 @@
+/*
+ * 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.slots.block.flow;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/***
+ * @author youji.zj
+ */
+public class FlowException extends BlockException {
+
+ public FlowException(String ruleLimitApp) {
+
+ super(ruleLimitApp);
+ }
+
+ public FlowException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public FlowException(String ruleLimitApp, String message) {
+ super(ruleLimitApp, message);
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java
new file mode 100755
index 00000000..87977251
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRule.java
@@ -0,0 +1,297 @@
+/*
+ * 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.slots.block.flow;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+/***
+ *
+ * Each flow rule is mainly composed of three factors: grade,
+ * strategy and controlBehavior.
+ *
+ *
+ *
The {@link #grade} represents the threshold type of flow control (by QPS or thread count).
+ *
The {@link #strategy} represents the strategy based on invocation relation.
+ *
The {@link #controlBehavior} represents the QPS shaping behavior (actions on incoming request when QPS
+ * exceeds the threshold).
+ *
+ *
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class FlowRule extends AbstractRule {
+
+ public static final String DEFAULT = "default";
+ public static final String OTHER = "other";
+
+ /**
+ * The threshold type of flow control (0: thread count, 1: QPS).
+ */
+ private int grade;
+
+ private double count;
+
+ /**
+ * 0为直接限流;1为关联限流;2为链路限流
+ */
+ private int strategy;
+
+ private String refResource;
+
+ /**
+ * Rate limiter control behavior.
+ * 0. default, 1. warm up, 2. rate limiter
+ */
+ private int controlBehavior = RuleConstant.CONTROL_BEHAVIOR_DEFAULT;
+
+ private int warmUpPeriodSec = 10;
+
+ /**
+ * Max queueing time in rate limiter behavior.
+ */
+ private int maxQueueingTimeMs = 500;
+
+ private Controller controller;
+
+ public int getControlBehavior() {
+ return controlBehavior;
+ }
+
+ public FlowRule setControlBehavior(int controlBehavior) {
+ this.controlBehavior = controlBehavior;
+ return this;
+ }
+
+ public int getMaxQueueingTimeMs() {
+ return maxQueueingTimeMs;
+ }
+
+ public FlowRule setMaxQueueingTimeMs(int maxQueueingTimeMs) {
+ this.maxQueueingTimeMs = maxQueueingTimeMs;
+ return this;
+ }
+
+ public FlowRule setRater(Controller rater) {
+ this.controller = rater;
+ return this;
+ }
+
+ public int getWarmUpPeriodSec() {
+ return warmUpPeriodSec;
+ }
+
+ public FlowRule setWarmUpPeriodSec(int warmUpPeriodSec) {
+ this.warmUpPeriodSec = warmUpPeriodSec;
+ return this;
+ }
+
+ public int getGrade() {
+ return grade;
+ }
+
+ public FlowRule setGrade(int grade) {
+ this.grade = grade;
+ return this;
+ }
+
+ public double getCount() {
+ return count;
+ }
+
+ public FlowRule setCount(double count) {
+ this.count = count;
+ return this;
+ }
+
+ public int getStrategy() {
+ return strategy;
+ }
+
+ public FlowRule setStrategy(int strategy) {
+ this.strategy = strategy;
+ return this;
+ }
+
+ public String getRefResource() {
+ return refResource;
+ }
+
+ public FlowRule setRefResource(String refResource) {
+ this.refResource = refResource;
+ return this;
+ }
+
+ @Override
+ public boolean passCheck(Context context, DefaultNode node, int acquireCount, Object... args) {
+ String limitApp = this.getLimitApp();
+ if (limitApp == null) {
+ return true;
+ }
+
+ String origin = context.getOrigin();
+ Node selectedNode = selectNodeByRequesterAndStrategy(origin, context, node);
+ if (selectedNode == null) {
+ return true;
+ }
+
+ return controller.canPass(selectedNode, acquireCount);
+ }
+
+ private Node selectNodeByRequesterAndStrategy(String origin, Context context, DefaultNode node) {
+ // The limit app should not be empty.
+ String limitApp = this.getLimitApp();
+
+ if (limitApp.equals(origin)) {
+ if (strategy == RuleConstant.STRATEGY_DIRECT) {
+ return context.getOriginNode();
+ }
+
+ String refResource = this.getRefResource();
+ if (StringUtil.isEmpty(refResource)) {
+ return null;
+ }
+
+ if (strategy == RuleConstant.STRATEGY_RELATE) {
+ return ClusterBuilderSlot.getClusterNode(refResource);
+ }
+
+ if (strategy == RuleConstant.STRATEGY_CHAIN) {
+ if (!refResource.equals(context.getName())) {
+ return null;
+ }
+ return node;
+ }
+
+ } else if (limitApp.equals(DEFAULT)) {
+ if (strategy == RuleConstant.STRATEGY_DIRECT) {
+ return node.getClusterNode();
+ }
+ String refResource = this.getRefResource();
+ if (StringUtil.isEmpty(refResource)) {
+ return null;
+ }
+
+ if (strategy == RuleConstant.STRATEGY_RELATE) {
+ return ClusterBuilderSlot.getClusterNode(refResource);
+ }
+
+ if (strategy == RuleConstant.STRATEGY_CHAIN) {
+ if (!refResource.equals(context.getName())) {
+ return null;
+ }
+ return node;
+ }
+
+ } else if (limitApp.equals(OTHER) && FlowRuleManager.isOtherOrigin(origin, getResource())) {
+ if (strategy == RuleConstant.STRATEGY_DIRECT) {
+ return context.getOriginNode();
+ }
+
+ String refResource = this.getRefResource();
+ if (StringUtil.isEmpty(refResource)) {
+ return null;
+ }
+ if (strategy == RuleConstant.STRATEGY_RELATE) {
+ return ClusterBuilderSlot.getClusterNode(refResource);
+ }
+
+ if (strategy == RuleConstant.STRATEGY_CHAIN) {
+ if (!refResource.equals(context.getName())) {
+ return null;
+ }
+ if (node != null) {
+ return node;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof FlowRule)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ FlowRule flowRule = (FlowRule)o;
+
+ if (grade != flowRule.grade) {
+ return false;
+ }
+ if (Double.compare(flowRule.count, count) != 0) {
+ return false;
+ }
+ if (strategy != flowRule.strategy) {
+ return false;
+ }
+ if (refResource != null ? !refResource.equals(flowRule.refResource) : flowRule.refResource != null) {
+ return false;
+ }
+ if (this.controlBehavior != flowRule.controlBehavior) {
+ return false;
+ }
+
+ if (warmUpPeriodSec != flowRule.warmUpPeriodSec) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ long temp;
+ result = 31 * result + grade;
+ temp = Double.doubleToLongBits(count);
+ result = 31 * result + (int)(temp ^ (temp >>> 32));
+ result = 31 * result + strategy;
+ result = 31 * result + (refResource != null ? refResource.hashCode() : 0);
+ result = 31 * result + (int)(temp ^ (temp >>> 32));
+ result = 31 * result + warmUpPeriodSec;
+ result = 31 * result + controlBehavior;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "FlowRule{" +
+ "resource=" + getResource() +
+ ", limitApp=" + getLimitApp() +
+ ", grade=" + grade +
+ ", count=" + count +
+ ", strategy=" + strategy +
+ ", refResource=" + refResource +
+ ", controlBehavior=" + controlBehavior +
+ ", warmUpPeriodSec=" + warmUpPeriodSec +
+ ", maxQueueingTimeMs=" + maxQueueingTimeMs +
+ ", controller=" + controller +
+ "}";
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleComparator.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleComparator.java
new file mode 100755
index 00000000..2af7378a
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleComparator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.slots.block.flow;
+
+import java.util.Comparator;
+
+public class FlowRuleComparator implements Comparator {
+
+ @Override
+ public int compare(FlowRule o1, FlowRule o2) {
+
+ if (o1.getLimitApp() == null) {
+ return 0;
+ }
+
+ if (o1.getLimitApp().equals(o2.getLimitApp())) {
+ return 0;
+ }
+
+ if (FlowRule.DEFAULT.equals(o1.getLimitApp())) {
+ return 1;
+ } else if (FlowRule.DEFAULT.equals(o2.getLimitApp())) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
new file mode 100755
index 00000000..b734fa91
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleManager.java
@@ -0,0 +1,206 @@
+/*
+ * 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.slots.block.flow;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.metric.MetricTimerListener;
+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.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.flow.controller.DefaultController;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.PaceController;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
+
+/**
+ *
+ * One resources can have multiple rules. And these rules take effects in the
+ * following order:
+ *
+ *
requests from specified caller
+ *
no specified caller
+ *
+ *
+ *
+ * @author jialiang.linjl
+ */
+
+public class FlowRuleManager {
+
+ private static final Map> flowRules = new ConcurrentHashMap>();
+ private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+ private final static FlowPropertyListener listener = new FlowPropertyListener();
+ private static SentinelProperty> currentProperty = new DynamicSentinelProperty>();
+
+ static {
+ currentProperty.addListener(listener);
+ scheduler.scheduleAtFixedRate(new MetricTimerListener(), 0, 1, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Listen to the {@link SentinelProperty} for {@link FlowRule}s. The property is the source of {@link FlowRule}s.
+ * Flow rules can also be set by {@link #loadRules(List)} directly.
+ *
+ * @param property the property to listen.
+ */
+ public static void register2Property(SentinelProperty> property) {
+ synchronized (listener) {
+ currentProperty.removeListener(listener);
+ property.addListener(listener);
+ currentProperty = property;
+ }
+ }
+
+ /**
+ * Get a copy of the rules.
+ *
+ * @return a new copy of the rules.
+ */
+ public static List getRules() {
+ List rules = new ArrayList();
+ if (flowRules == null) {
+ return rules;
+ }
+ for (Map.Entry> entry : flowRules.entrySet()) {
+ rules.addAll(entry.getValue());
+ }
+ return rules;
+ }
+
+ /**
+ * Load {@link FlowRule}s, former rules will be replaced.
+ *
+ * @param rules new rules to load.
+ */
+ public static void loadRules(List rules) {
+ currentProperty.updateValue(rules);
+ }
+
+ private static Map> loadFlowConf(List list) {
+ Map> newRuleMap = new ConcurrentHashMap>();
+
+ if (list == null) {
+ return newRuleMap;
+ }
+
+ for (FlowRule rule : list) {
+ if (StringUtil.isBlank(rule.getLimitApp())) {
+ rule.setLimitApp(FlowRule.DEFAULT);
+ }
+
+ Controller rater = new DefaultController(rule.getCount(), rule.getGrade());
+ if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
+ && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_WARM_UP
+ && rule.getWarmUpPeriodSec() > 0) {
+ rater = new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(), ColdFactorProperty.coldFactor);
+
+ } else if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
+ && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
+ && rule.getMaxQueueingTimeMs() > 0) {
+ rater = new PaceController(rule.getMaxQueueingTimeMs(), rule.getCount());
+ }
+ rule.setRater(rater);
+
+ String identity = rule.getResource();
+ List ruleM = newRuleMap.get(identity);
+
+ if (ruleM == null) {
+ ruleM = new ArrayList();
+ newRuleMap.put(identity, ruleM);
+ }
+
+ ruleM.add(rule);
+
+ }
+ return newRuleMap;
+ }
+
+ public static void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count)
+ throws BlockException {
+ if (flowRules != null) {
+ List rules = flowRules.get(resource.getName());
+ if (rules != null) {
+ for (FlowRule rule : rules) {
+ if (!rule.passCheck(context, node, count)) {
+ throw new FlowException(rule.getLimitApp());
+ }
+ }
+ }
+ }
+ }
+
+ public static boolean hasConfig(String resource) {
+ return flowRules.containsKey(resource);
+ }
+
+ public static boolean isOtherOrigin(String origin, String resourceName) {
+ if (StringUtil.isEmpty(origin)) {
+ return false;
+ }
+
+ if (flowRules != null) {
+ List rules = flowRules.get(resourceName);
+
+ if (rules != null) {
+ for (FlowRule rule : rules) {
+ if (origin.equals(rule.getLimitApp())) {
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private static final class FlowPropertyListener implements PropertyListener> {
+
+ @Override
+ public void configUpdate(List value) {
+ Map> rules = loadFlowConf(value);
+ if (rules != null) {
+ flowRules.clear();
+ flowRules.putAll(rules);
+ }
+ RecordLog.info("receive flow config: " + flowRules);
+ }
+
+ @Override
+ public void configLoad(List conf) {
+ Map> rules = loadFlowConf(conf);
+ if (rules != null) {
+ flowRules.clear();
+ flowRules.putAll(rules);
+ }
+ RecordLog.info("load flow config: " + flowRules);
+ }
+
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
new file mode 100755
index 00000000..d01856df
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
@@ -0,0 +1,127 @@
+/*
+ * 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.slots.block.flow;
+
+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;
+
+/**
+ *
+ * Combined the runtime statistics collected from the previous
+ * slots(NodeSelectorSlot, ClusterNodeBuilderSlot, and StatistcSlot), FlowSlot
+ * will use pre-set rules to decide whether the incoming requests should be
+ * blocked.
+ *
+ * {@code SphU.entry (resourceName) }will throw FlowException if any rule is
+ * triggered. user can customize his own logic by catching FlowException.
+ *
+ * One resource can have multiple flow rules. FlowSlot traverses these rules
+ * until one of them is triggered or all rules have been traversed.
+ *
+ * Each FlowRule is mainly composed of the 2 factors: grade, strategy, path; we
+ * can combine these factors to achieve different effects.
+ *
+ * The grade is defined by the grade field in FlowRule. Here, 0 for thread
+ * isolation and 1 for request count shaping. Both thread count and request
+ * count are collected in real runtime, and we can view these statistics by
+ * following command: {@code
+ * curl http:// localhost:8719 / tree?type = root`
+ * idx id thread pass blocked success total aRt 1m-pass 1m-block 1m-all exeption
+ * 2 abc647 0 460 46 46 1 27 630 276 897 0
+ * }
+ *
+ * Thread for the count of threads that is currently processing the resource;
+ * pass for the count of incoming request within one second; blocked for the
+ * count of requests blocked within one second; success for the count of the
+ * requests successfully within one second; RT for the average response time of
+ * the requests within a second; total for the sum of incoming requests and
+ * blocked requests within one second; 1m-pass is for the count of incoming
+ * requests within one minute; 1m-block is for the count of a request blocked
+ * within one minute; 1m -all is the total of incoming and blocked requests
+ * within 1 minute; exception is for the count of exceptions in one second.
+ *
+ * This stage is usually used to protect resources from occupying. If a resource
+ * takes long time to finish, threads will begin to occupy. The longer the
+ * response takes, the more threads occupy.
+ *
+ * Besides counter, thread pool or semaphore can also be used to achieve this.
+ *
+ * - Thread pool: Allocate a thread pool to handle these resource. When there is
+ * no more idle thread in the pool, the request is rejected without affecting
+ * other resources.
+ *
+ * - Semaphore: Use semaphore to control the concurrent count of the threads in
+ * this resource.
+ *
+ * The benefit of using thread pool is that, it can walk away gracefully when
+ * time out. But it also bring us the cost of context switch and additional
+ * threads. If the incoming requests is already served in a separated thread,
+ * for instance, a servelet request, it will almost double the threads count if
+ * using thread pool.
+ *
+ * ### QPS Shaping ### When qps exceeds the threshold, we will take actions to
+ * control the incoming request, and is configured by "controlBehavior" field in
+ * flowrule
+ *
+ * 1. immediately reject(RuleConstant.CONTROL_BEHAVIOR_DEFAULT)
+ *
+ * This is the default behavior. The exceeded request is rejected immediately
+ * and the FlowException is thrown
+ *
+ * 2. Warmup(RuleConstant.CONTROL_BEHAVIOR_WARM_UP)
+ *
+ * If the usage of system has been low for a while, and a large amount of
+ * requests comes, the system might not be able to handle all these requests at
+ * once. However if we steady increase the incoming request, the system can warm
+ * up and finally be able to handle all the requests.If the usage of system has
+ * been low for a while, and a large amount of requests comes, the system might
+ * not be able to handle all these requests at once. However if we steady
+ * increase the incoming request, the system can warm up and finally be able to
+ * handle all the requests. This warmup period can be configured by setting the
+ * field "warmUpPeriodSec" in flow rule.
+ *
+ * 3.Rate limiter(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) This strategy
+ * strictly controls the interval between requests. In other words, it allows
+ * requests to pass at a stable rate.
+ * This strategy is an implement of leaky bucket
+ * (https://en.wikipedia.org/wiki/Leaky_bucket). It is used to handle the
+ * request at a stable rate and is often used in burst traffic. For instance,
+ * Message. When a large number of requests beyond the system’s capacity arrive
+ * at the same time, the system using this strategy will handle requests and its
+ * fixed rate until all the requests have been processed or time out.
+ *
+ * @author jialiang.linjl
+ */
+public class FlowSlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+
+ FlowRuleManager.checkFlow(resourceWrapper, context, node, count);
+
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/DefaultController.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/DefaultController.java
new file mode 100755
index 00000000..d02d85a8
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/DefaultController.java
@@ -0,0 +1,53 @@
+/*
+ * 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.slots.block.flow.controller;
+
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.Controller;
+
+/**
+ * @author jialiang.linjl
+ */
+public class DefaultController implements Controller {
+
+ double count = 0;
+ int grade = 0;
+
+ public DefaultController(double count, int grade) {
+ super();
+ this.count = count;
+ this.grade = grade;
+ }
+
+ @Override
+ public boolean canPass(Node node, int acquireCount) {
+ int curCount = avgUsedTokens(node);
+ if (curCount + acquireCount > count) {
+ return false;
+ }
+
+ return true;
+ }
+
+ private int avgUsedTokens(Node node) {
+ if (node == null) {
+ return -1;
+ }
+ return grade == RuleConstant.FLOW_GRADE_THREAD ? node.curThreadNum() : (int)node.passQps();
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/PaceController.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/PaceController.java
new file mode 100755
index 00000000..da56d439
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/PaceController.java
@@ -0,0 +1,77 @@
+/*
+ * 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.slots.block.flow.controller;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.csp.sentinel.slots.block.flow.Controller;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.node.Node;
+
+/**
+ * @author jialiang.linjl
+ */
+public class PaceController implements Controller {
+
+ private final int maxQueueingTimeMs;
+ private final double count;
+ private final AtomicLong latestPassedTime = new AtomicLong(-1);
+
+ public PaceController(int timeOut, double count) {
+ this.maxQueueingTimeMs = timeOut;
+ this.count = count;
+ }
+
+ @Override
+ public boolean canPass(Node node, int acquireCount) {
+
+ // 按照斜率来计算计划中应该什么时候通过
+ long currentTime = TimeUtil.currentTimeMillis();
+
+ long costTime = Math.round(1.0 * (acquireCount) / count * 1000);
+
+ //期待时间
+ long expectedTime = costTime + latestPassedTime.get();
+
+ if (expectedTime <= currentTime) {
+ //这里会有冲突,然而冲突就冲突吧.
+ latestPassedTime.set(currentTime);
+ return true;
+ } else {
+ // 计算自己需要的等待时间
+ long waitTime = costTime + latestPassedTime.get() - TimeUtil.currentTimeMillis();
+ if (waitTime >= maxQueueingTimeMs) {
+ return false;
+ } else {
+ long oldTime = latestPassedTime.addAndGet(costTime);
+ try {
+ waitTime = oldTime - TimeUtil.currentTimeMillis();
+ if (waitTime >= maxQueueingTimeMs) {
+ latestPassedTime.addAndGet(-costTime);
+ return false;
+ }
+ Thread.sleep(waitTime);
+ return true;
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/WarmUpController.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/WarmUpController.java
new file mode 100755
index 00000000..9037d1e9
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/controller/WarmUpController.java
@@ -0,0 +1,172 @@
+/*
+ * 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.slots.block.flow.controller;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slots.block.flow.Controller;
+
+/**
+ * The principle idea comes from guava. However, the calculation of guava is
+ * rate-based, which means that we need to translate rate to qps.
+ *
+ * https://github.com/google/guava/blob/master/guava/src/com/google/common/util/concurrent/SmoothRateLimiter.java
+ *
+ * Requests arriving at the pulse may drag down long idle systems even though it
+ * has a much larger handling capability in stable period. It usually happens in
+ * scenarios that require extra time for initialization, for example, db
+ * establishes a connection; connects to a remote service, and so on.
+ *
+ * That’s why we need “warm up”.
+ *
+ * Sentinel’s “warm up” implementation is based on Guava's algorithm. However,
+ * unlike Guava's scenario, which is a “leaky bucket”, and is mainly used to
+ * adjust the request interval, Sentinel is more focus on controlling the count
+ * of incoming requests per second without calculating its interval.
+ *
+ * Sentinel's "warm-up" implementation is based on the guava-based algorithm.
+ * However, Guava’s implementation focus on adjusting the request interval, in
+ * other words, a Leaky bucket. Sentinel pays more attention to controlling the
+ * count of incoming requests per second without calculating its interval, it is
+ * more like a “Token bucket.”
+ *
+ *
+ * The remaining tokens in the bucket is used to measure the system utility.
+ * Suppose a system can handle b requests per second. Every second b tokens will
+ * be added into the bucket until the bucket is full. And when system processes
+ * a request, it takes a token from the bucket. The more tokens left in the
+ * bucket, the lower the utilization of the system; when the token in the token
+ * bucket is above a certain threshold, we call it in a "saturation" state.
+ *
+ * Base on Guava’s theory, there is a linear equation we can write this in the
+ * form y = m * x + b where y (a.k.a y(x)), or qps(q)), is our expected QPS
+ * given a saturated period (e.g. 3 minutes in), m is the rate of change from
+ * our cold (minimum) rate to our stable (maximum) rate, x (or q) is the
+ * occupied token.
+ *
+ * @author jialiang.linjl
+ */
+public class WarmUpController implements Controller {
+
+ private double count;
+ private int coldFactor;
+ private int warningToken = 0;
+ private int maxToken;
+ private double slope;
+
+ AtomicLong storedTokens = new AtomicLong(0);
+ AtomicLong lastFilledTime = new AtomicLong(0);
+
+ public WarmUpController(double count, int warmupPeriodInSec, int coldFactor) {
+ construct(count, warmupPeriodInSec, coldFactor);
+ }
+
+ public WarmUpController(double count, int warmUpPeriodInMic) {
+ construct(count, warmUpPeriodInMic, 3);
+ }
+
+ private void construct(double count, int warmUpPeriodInSec, int coldFactor) {
+
+ if (coldFactor <= 1) {
+ throw new RuntimeException("cold factor should be larget than 1");
+ }
+
+ this.count = count;
+
+ this.coldFactor = coldFactor;
+
+ // thresholdPermits = 0.5 * warmupPeriod / stableInterval.
+ // warningToken = 100;
+ warningToken = (int)(warmUpPeriodInSec * count) / (coldFactor - 1);
+ // / maxPermits = thresholdPermits + 2 * warmupPeriod /
+ // (stableInterval + coldInterval)
+ // maxToken = 200
+ maxToken = warningToken + (int)(2 * warmUpPeriodInSec * count / (1.0 + coldFactor));
+
+ // slope
+ // slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits
+ // - thresholdPermits);
+ slope = (coldFactor - 1.0) / count / (maxToken - warningToken);
+
+ }
+
+ @Override
+ public boolean canPass(Node node, int acquireCount) {
+ long passQps = node.passQps();
+
+ long previousQps = node.previousPassQps();
+ syncToken(previousQps);
+
+ // 开始计算它的斜率
+ // 如果进入了警戒线,开始调整他的qps
+ long restToken = storedTokens.get();
+ if (restToken >= warningToken) {
+ long aboveToken = restToken - warningToken;
+ // 消耗的速度要比warning快,但是要比慢
+ // current interval = restToken*slope+1/count
+ double warningQps = Math.nextUp(1.0 / (aboveToken * slope + 1.0 / count));
+ if (passQps + acquireCount <= warningQps) {
+ return true;
+ }
+ } else {
+ if (passQps + acquireCount <= count) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void syncToken(long passQps) {
+ long currentTime = TimeUtil.currentTimeMillis();
+ currentTime = currentTime - currentTime % 1000;
+ long oldLastFillTime = lastFilledTime.get();
+ if (currentTime <= oldLastFillTime) {
+ return;
+ }
+
+ long oldValue = storedTokens.get();
+ long newValue = coolDownTokens(currentTime, passQps);
+
+ if (storedTokens.compareAndSet(oldValue, newValue)) {
+ long currentValue = storedTokens.addAndGet(0 - passQps);
+ if (currentValue < 0) {
+ storedTokens.set(0l);
+ }
+ lastFilledTime.set(currentTime);
+ }
+
+ }
+
+ private long coolDownTokens(long currentTime, long passQps) {
+ long oldValue = storedTokens.get();
+ long newValue = oldValue;
+
+ // 添加令牌的判断前提条件:
+ // 当令牌的消耗程度远远低于警戒线的时候
+ if (oldValue < warningToken) {
+ newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);
+ } else if (oldValue > warningToken) {
+ if (passQps < (int)count / coldFactor) {
+ newValue = (long)(oldValue + (currentTime - lastFilledTime.get()) * count / 1000);
+ }
+ }
+ return Math.min(newValue, maxToken);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
new file mode 100755
index 00000000..92940b46
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterBuilderSlot.java
@@ -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.slots.clusterbuilder;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.Env;
+import com.alibaba.csp.sentinel.EntryType;
+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.slotchain.AbstractLinkedProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+
+/**
+ *
+ * This slot maintains resource running statistics (response time, qps, thread
+ * count, exception), and a list of callers as well which is marked by
+ * {@link ContextUtil#enter(String origin)}
+ *
+ *
+ * One resource has only one cluster node, while one resource can have multiple
+ * default node.
+ *
+ * Remember that same resource({@link ResourceWrapper#equals(Object)}) will share
+ * the same {@link ProcessorSlotChain} globally, no matter in witch context. So if
+ * code goes into {@link #entry(Context, ResourceWrapper, DefaultNode, int, Object...)},
+ * the resource name must be same but context name may not.
+ *
+ *
+ * To get total statistics of the same resource in different context, same resource
+ * shares the same {@link ClusterNode} globally. All {@link ClusterNode}s are cached
+ * in this map.
+ *
+ *
+ * The longer the application runs, the more stable this mapping will
+ * become. so we don't concurrent map but a lock. as this lock only happens
+ * at the very beginning while concurrent map will hold the lock all the
+ * time
+ *
+ */
+ private static volatile Map clusterNodeMap
+ = new HashMap();
+
+ private static final Object lock = new Object();
+
+ private ClusterNode clusterNode = null;
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+ if (clusterNode == null) {
+ synchronized (lock) {
+ if (clusterNode == null) {
+ // Create the cluster node.
+ clusterNode = Env.nodeBuilder.buildClusterNode();
+ HashMap newMap = new HashMap(16);
+ newMap.putAll(clusterNodeMap);
+ newMap.put(node.getId(), clusterNode);
+
+ clusterNodeMap = newMap;
+ }
+ }
+ }
+ node.setClusterNode(clusterNode);
+
+ /*
+ * if context origin is set, we should get or create a new {@link Node} of
+ * the specific origin.
+ */
+ if (!"".equals(context.getOrigin())) {
+ Node originNode = node.getClusterNode().getOriginNode(context.getOrigin());
+ context.getCurEntry().setOriginNode(originNode);
+ }
+
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+
+ /**
+ * Get {@link ClusterNode} of the resource of the specific type.
+ *
+ * @param id resource name.
+ * @param type invoke type.
+ * @return the {@link ClusterNode}
+ */
+ public static ClusterNode getClusterNode(String id, EntryType type) {
+ return clusterNodeMap.get(new StringResourceWrapper(id, type));
+ }
+
+ /**
+ * Get {@link ClusterNode} of the resource name.
+ *
+ * @param id resource name.
+ * @return the {@link ClusterNode}.
+ */
+ public static ClusterNode getClusterNode(String id) {
+ if (id == null) {
+ return null;
+ }
+ ClusterNode clusterNode = null;
+
+ for (EntryType nodeType : EntryType.values()) {
+ clusterNode = clusterNodeMap.get(new StringResourceWrapper(id, nodeType));
+ if (clusterNode != null) {
+ break;
+ }
+ }
+
+ return clusterNode;
+ }
+
+ /**
+ * Get {@link ClusterNode}s map, this map holds all {@link ClusterNode}s, it's key is resource name,
+ * value is the related {@link ClusterNode}.
+ * DO NOT MODIFY the map returned.
+ *
+ * @return all {@link ClusterNode}s
+ */
+ public static Map getClusterNodeMap() {
+ return clusterNodeMap;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/EagleEyeLogUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/EagleEyeLogUtil.java
new file mode 100755
index 00000000..7cca5c17
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/EagleEyeLogUtil.java
@@ -0,0 +1,48 @@
+/*
+ * 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.slots.logger;
+
+import java.io.File;
+
+import com.alibaba.csp.sentinel.eagleeye.EagleEye;
+import com.alibaba.csp.sentinel.eagleeye.StatLogger;
+
+public class EagleEyeLogUtil {
+
+ private static final String DIR_NAME = "csp";
+ private static final String FILE_NAME = "sentinel-block.log";
+
+ private static StatLogger statLogger;
+
+ static {
+ String path = DIR_NAME + File.separator + FILE_NAME;
+
+ statLogger = EagleEye.statLoggerBuilder("sentinel-block-record")
+ .intervalSeconds(1)
+ .entryDelimiter('|')
+ .keyDelimiter(',')
+ .valueDelimiter(',')
+ .maxEntryCount(6000)
+ .baseLogFilePath(path)
+ .maxFileSizeMB(300)
+ .maxBackupIndex(3)
+ .buildSingleton();
+ }
+
+ public static void log(String resource, String exceptionName, String ruleLimitApp, String origin, int count) {
+ statLogger.stat(resource, exceptionName, ruleLimitApp, origin).count(count);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java
new file mode 100755
index 00000000..e2bdff32
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/logger/LogSlot.java
@@ -0,0 +1,54 @@
+/*
+ * 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.slots.logger;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+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;
+
+/**
+ * A {@link com.alibaba.csp.sentinel.slotchain.ProcessorSlot} that is response for logging block exceptions
+ * to provide concrete logs for troubleshooting.
+ */
+public class LogSlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode obj, int count, Object... args)
+ throws Throwable {
+ try {
+ fireEntry(context, resourceWrapper, obj, count, args);
+ } catch (BlockException e) {
+ EagleEyeLogUtil.log(resourceWrapper.getName(), e.getClass().getSimpleName(), e.getRuleLimitApp(),
+ context.getOrigin(), count);
+ throw e;
+ } catch (Throwable e) {
+ RecordLog.info("Entry exception", e);
+ }
+
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ try {
+ fireExit(context, resourceWrapper, count, args);
+ } catch (Throwable e) {
+ RecordLog.info("Entry exit exception", e);
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java
new file mode 100755
index 00000000..144780e6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java
@@ -0,0 +1,178 @@
+/*
+ * 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.slots.nodeselector;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.Env;
+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.EntranceNode;
+import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+
+/**
+ *
+ * This class will try to build the calling traces via
+ *
+ *
adding a new {@link DefaultNode} if needed as the last child in the context.
+ * The context's last node is the current node or the parent node of the context.
+ * Here the {@link EntranceNode} represents "entrance1" given by
+ * {@code ContextUtil.enter("entrance1", "appA")}.
+ *
+ *
+ * Both DefaultNode(nodeA) and ClusterNode(nodeA) holds statistics of "nodeA", which is given
+ * by {@code SphU.entry("nodeA")}
+ *
+ *
+ * The {@link ClusterNode} is uniquely identified by the ResourceId; the {@link DefaultNode}
+ * is identified by both the resource id and {@link Context}. In other words, one resource
+ * id will generate multiple {@link DefaultNode} for each distinct context, but only one
+ * {@link ClusterNode}.
+ *
+ *
+ * the following code shows one resource id in two different context:
+ *
+ * As we can see, two {@link DefaultNode} are created for "nodeA" in two context, but only one
+ * {@link ClusterNode} is created.
+ *
+ *
+ *
+ * We can also check this structure by calling:
+ * {@code curl http://localhost:8719/tree?type=root}
+ *
+ *
+ * @author jialiang.linjl
+ * @see EntranceNode
+ * @see ContextUtil
+ */
+public class NodeSelectorSlot extends AbstractLinkedProcessorSlot {
+
+ /**
+ * {@link DefaultNode}s of the same resource in different context.
+ */
+ private Map map = new HashMap(10);
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
+ throws Throwable {
+ /*
+ * It's interesting that we use context name rather resource name as the map key.
+ *
+ * Remember that same resource({@link ResourceWrapper#equals(Object)}) will share
+ * the same {@link ProcessorSlotChain} globally, no matter in which context. So if
+ * code goes into {@link #entry(Context, ResourceWrapper, DefaultNode, int, Object...)},
+ * the resource name must be same but context name may not.
+ *
+ * If we use {@link com.alibaba.csp.sentinel.SphU#entry(String resource)} to
+ * enter same resource in different context, using context name as map key can
+ * distinguish the same resource. In this case, multiple {@link DefaultNode}s will be created
+ * of the same resource name, for every distinct context (different context name) each.
+ *
+ * Consider another question. One resource may have multiple {@link DefaultNode},
+ * so what is the fastest way to get total statistics of the same resource?
+ * The answer is all {@link DefaultNode}s with same resource name share one
+ * {@link ClusterNode}. See {@link ClusterBuilderSlot} for detail.
+ */
+ DefaultNode node = map.get(context.getName());
+ if (node == null) {
+ synchronized (this) {
+ node = map.get(context.getName());
+ if (node == null) {
+ node = Env.nodeBuilder.buildTreeNode(resourceWrapper, null);
+ HashMap cacheMap = new HashMap(map.size());
+ cacheMap.putAll(map);
+ cacheMap.put(context.getName(), node);
+ map = cacheMap;
+ }
+ // Build invocation tree
+ ((DefaultNode)context.getLastNode()).addChild(node);
+ }
+ }
+
+ context.setCurNode(node);
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
new file mode 100755
index 00000000..c4e4d8f6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
@@ -0,0 +1,127 @@
+/*
+ * 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.slots.statistic;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+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;
+
+/**
+ *
+ * A processor slot that dedicates to real time statistics.
+ * When entering this slot, we need to separately count the following
+ * information:
+ *
+ *
{@link ClusterNode}: total statistics of a cluster node of the resource id
+ *
origin node: statistics of a cluster node from different callers/origins.
+ *
{@link DefaultNode}: statistics for specific resource name in the specific context.
+ *
Finally, the sum statistics of all entrances.
+ *
+ *
+ *
+ * @author jialiang.linjl
+ */
+public class StatisticSlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+
+ try {
+ fireEntry(context, resourceWrapper, node, count, args);
+ node.increaseThreadNum();
+ node.addPassRequest();
+
+ if (context.getCurEntry().getOriginNode() != null) {
+ context.getCurEntry().getOriginNode().increaseThreadNum();
+ context.getCurEntry().getOriginNode().addPassRequest();
+ }
+
+ if (resourceWrapper.getType() == EntryType.IN) {
+ Constants.ENTRY_NODE.increaseThreadNum();
+ Constants.ENTRY_NODE.addPassRequest();
+ }
+
+ } catch (BlockException e) {
+ context.getCurEntry().setError(e);
+
+ // Add block count.
+ node.increaseBlockedQps();
+ if (context.getCurEntry().getOriginNode() != null) {
+ context.getCurEntry().getOriginNode().increaseBlockedQps();
+ }
+
+ if (resourceWrapper.getType() == EntryType.IN) {
+ Constants.ENTRY_NODE.increaseBlockedQps();
+ }
+
+ throw e;
+ } catch (Throwable e) {
+ context.getCurEntry().setError(e);
+
+ // Should not happen
+ node.increaseExceptionQps();
+ if (context.getCurEntry().getOriginNode() != null) {
+ context.getCurEntry().getOriginNode().increaseExceptionQps();
+ }
+
+ if (resourceWrapper.getType() == EntryType.IN) {
+ Constants.ENTRY_NODE.increaseExceptionQps();
+ }
+ throw e;
+ }
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ DefaultNode node = (DefaultNode)context.getCurNode();
+
+ if (context.getCurEntry().getError() == null) {
+ long rt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime();
+ if (rt > Constants.TIME_DROP_VALVE) {
+ rt = Constants.TIME_DROP_VALVE;
+ }
+
+ node.rt(rt);
+ if (context.getCurEntry().getOriginNode() != null) {
+ context.getCurEntry().getOriginNode().rt(rt);
+ }
+
+ node.decreaseThreadNum();
+
+ if (context.getCurEntry().getOriginNode() != null) {
+ context.getCurEntry().getOriginNode().decreaseThreadNum();
+ }
+
+ if (resourceWrapper.getType() == EntryType.IN) {
+ Constants.ENTRY_NODE.rt(rt);
+ Constants.ENTRY_NODE.decreaseThreadNum();
+ }
+ } else {
+ // error may happen
+ // node.rt(-2);
+ }
+
+ fireExit(context, resourceWrapper, count);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
new file mode 100755
index 00000000..78f5d627
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java
@@ -0,0 +1,113 @@
+/*
+ * 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.slots.statistic.base;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+
+/**
+ * @param type of data wrapper
+ * @author jialiang.linjl
+ */
+public abstract class LeapArray {
+
+ protected int windowLength;
+ protected int sampleCount;
+ protected int intervalInMs;
+
+ protected AtomicReferenceArray> array;
+
+ public LeapArray(int windowLength, int intervalInSec) {
+ this.windowLength = windowLength;
+ this.sampleCount = intervalInSec * 1000 / windowLength;
+ this.intervalInMs = intervalInSec * 1000;
+
+ this.array = new AtomicReferenceArray>(sampleCount);
+ }
+
+ public WindowWrap currentWindow() {
+ return currentWindow(TimeUtil.currentTimeMillis());
+ }
+
+ abstract public WindowWrap currentWindow(long time);
+
+ public WindowWrap getPreviousWindow(long time) {
+ long timeId = (time - windowLength) / windowLength;
+ int idx = (int)(timeId % array.length());
+ time = time - windowLength;
+ WindowWrap wrap = array.get(idx);
+
+ if (wrap == null) {
+ return wrap;
+ }
+
+ if (wrap.windowStart() + windowLength < (time)) {
+ return null;
+ }
+
+ return wrap;
+ }
+
+ public WindowWrap getPreviousWindow() {
+ return getPreviousWindow(System.currentTimeMillis());
+ }
+
+ public T getWindowValue(long time) {
+ long timeId = time / windowLength;
+ int idx = (int)(timeId % array.length());
+
+ WindowWrap old = array.get(idx);
+ if (old == null) {
+ return null;
+ }
+
+ return old.value();
+ }
+
+ public AtomicReferenceArray> array() {
+ return array;
+ }
+
+ public List> list() {
+ ArrayList> result = new ArrayList>();
+
+ for (int i = 0; i < array.length(); i++) {
+ WindowWrap windowWrap = array.get(i);
+ if (windowWrap == null) {
+ continue;
+ }
+ result.add(windowWrap);
+ }
+
+ return result;
+ }
+
+ public List values() {
+ ArrayList result = new ArrayList();
+
+ for (int i = 0; i < array.length(); i++) {
+ WindowWrap windowWrap = array.get(i);
+ if (windowWrap == null) {
+ continue;
+ }
+ result.add(windowWrap.value());
+ }
+ return result;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LongAdder.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LongAdder.java
new file mode 100755
index 00000000..beeaedcf
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LongAdder.java
@@ -0,0 +1,207 @@
+package com.alibaba.csp.sentinel.slots.statistic.base;
+
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * From http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/
+ */
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * One or more variables that together maintain an initially zero
+ * {@code long} sum. When updates (method {@link #add}) are contended
+ * across threads, the set of variables may grow dynamically to reduce
+ * contention. Method {@link #sum} (or, equivalently, {@link
+ * #longValue}) returns the current total combined across the
+ * variables maintaining the sum.
+ *
+ *
This class is usually preferable to {@link AtomicLong} when
+ * multiple threads update a common sum that is used for purposes such
+ * as collecting statistics, not for fine-grained synchronization
+ * control. Under low update contention, the two classes have similar
+ * characteristics. But under high contention, expected throughput of
+ * this class is significantly higher, at the expense of higher space
+ * consumption.
+ *
+ *
This class extends {@link Number}, but does not define
+ * methods such as {@code hashCode} and {@code compareTo} because
+ * instances are expected to be mutated, and so are not useful as
+ * collection keys.
+ *
+ *
jsr166e note: This class is targeted to be placed in
+ * java.util.concurrent.atomic
+ *
+ * @author Doug Lea
+ * @since 1.8
+ */
+public class LongAdder extends Striped64 implements Serializable {
+ private static final long serialVersionUID = 7249069246863182397L;
+
+ /**
+ * Version of plus for use in retryUpdate
+ */
+ final long fn(long v, long x) { return v + x; }
+
+ /**
+ * Creates a new adder with initial sum of zero.
+ */
+ public LongAdder() {
+ }
+
+ /**
+ * Adds the given value.
+ *
+ * @param x the value to add
+ */
+ public void add(long x) {
+ Cell[] as;
+ long b, v;
+ HashCode hc;
+ Cell a;
+ int n;
+ if ((as = cells) != null || !casBase(b = base, b + x)) {
+ boolean uncontended = true;
+ int h = (hc = threadHashCode.get()).code;
+ if (as == null || (n = as.length) < 1 ||
+ (a = as[(n - 1) & h]) == null ||
+ !(uncontended = a.cas(v = a.value, v + x))) { retryUpdate(x, hc, uncontended); }
+ }
+ }
+
+ /**
+ * Equivalent to {@code add(1)}.
+ */
+ public void increment() {
+ add(1L);
+ }
+
+ /**
+ * Equivalent to {@code add(-1)}.
+ */
+ public void decrement() {
+ add(-1L);
+ }
+
+ /**
+ * Returns the current sum. The returned value is NOT an
+ * atomic snapshot: Invocation in the absence of concurrent
+ * updates returns an accurate result, but concurrent updates that
+ * occur while the sum is being calculated might not be
+ * incorporated.
+ *
+ * @return the sum
+ */
+ public long sum() {
+ long sum = base;
+ Cell[] as = cells;
+ if (as != null) {
+ int n = as.length;
+ for (int i = 0; i < n; ++i) {
+ Cell a = as[i];
+ if (a != null) { sum += a.value; }
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Resets variables maintaining the sum to zero. This method may
+ * be a useful alternative to creating a new adder, but is only
+ * effective if there are no concurrent updates. Because this
+ * method is intrinsically racy, it should only be used when it is
+ * known that no threads are concurrently updating.
+ */
+ public void reset() {
+ internalReset(0L);
+ }
+
+ /**
+ * Equivalent in effect to {@link #sum} followed by {@link
+ * #reset}. This method may apply for example during quiescent
+ * points between multithreaded computations. If there are
+ * updates concurrent with this method, the returned value is
+ * not guaranteed to be the final value occurring before
+ * the reset.
+ *
+ * @return the sum
+ */
+ public long sumThenReset() {
+ long sum = base;
+ Cell[] as = cells;
+ base = 0L;
+ if (as != null) {
+ int n = as.length;
+ for (int i = 0; i < n; ++i) {
+ Cell a = as[i];
+ if (a != null) {
+ sum += a.value;
+ a.value = 0L;
+ }
+ }
+ }
+ return sum;
+ }
+
+ /**
+ * Returns the String representation of the {@link #sum}.
+ *
+ * @return the String representation of the {@link #sum}
+ */
+ public String toString() {
+ return Long.toString(sum());
+ }
+
+ /**
+ * Equivalent to {@link #sum}.
+ *
+ * @return the sum
+ */
+ public long longValue() {
+ return sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as an {@code int} after a narrowing
+ * primitive conversion.
+ */
+ public int intValue() {
+ return (int)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code float}
+ * after a widening primitive conversion.
+ */
+ public float floatValue() {
+ return (float)sum();
+ }
+
+ /**
+ * Returns the {@link #sum} as a {@code double} after a widening
+ * primitive conversion.
+ */
+ public double doubleValue() {
+ return (double)sum();
+ }
+
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ s.defaultWriteObject();
+ s.writeLong(sum());
+ }
+
+ private void readObject(ObjectInputStream s)
+ throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ busy = 0;
+ cells = null;
+ base = s.readLong();
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Stripe64.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Stripe64.java
new file mode 100755
index 00000000..e4489e8c
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Stripe64.java
@@ -0,0 +1,347 @@
+
+package com.alibaba.csp.sentinel.slots.statistic.base;
+
+/*
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ *
+ * From http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/jsr166e/
+ */
+
+import java.util.Random;
+
+/**
+ * A package-local class holding common representation and mechanics
+ * for classes supporting dynamic striping on 64bit values. The class
+ * extends Number so that concrete subclasses must publicly do so.
+ */
+abstract class Striped64 extends Number {
+ /*
+ * This class maintains a lazily-initialized table of atomically
+ * updated variables, plus an extra "base" field. The table size
+ * is a power of two. Indexing uses masked per-thread hash codes.
+ * Nearly all declarations in this class are package-private,
+ * accessed directly by subclasses.
+ *
+ * Table entries are of class Cell; a variant of AtomicLong padded
+ * to reduce cache contention on most processors. Padding is
+ * overkill for most Atomics because they are usually irregularly
+ * scattered in memory and thus don't interfere much with each
+ * other. But Atomic objects residing in arrays will tend to be
+ * placed adjacent to each other, and so will most often share
+ * cache lines (with a huge negative performance impact) without
+ * this precaution.
+ *
+ * In part because Cells are relatively large, we avoid creating
+ * them until they are needed. When there is no contention, all
+ * updates are made to the base field. Upon first contention (a
+ * failed CAS on base update), the table is initialized to size 2.
+ * The table size is doubled upon further contention until
+ * reaching the nearest power of two greater than or equal to the
+ * number of CPUS. Table slots remain empty (null) until they are
+ * needed.
+ *
+ * A single spinlock ("busy") is used for initializing and
+ * resizing the table, as well as populating slots with new Cells.
+ * There is no need for a blocking lock: When the lock is not
+ * available, threads try other slots (or the base). During these
+ * retries, there is increased contention and reduced locality,
+ * which is still better than alternatives.
+ *
+ * Per-thread hash codes are initialized to random values.
+ * Contention and/or table collisions are indicated by failed
+ * CASes when performing an update operation (see method
+ * retryUpdate). Upon a collision, if the table size is less than
+ * the capacity, it is doubled in size unless some other thread
+ * holds the lock. If a hashed slot is empty, and lock is
+ * available, a new Cell is created. Otherwise, if the slot
+ * exists, a CAS is tried. Retries proceed by "double hashing",
+ * using a secondary hash (Marsaglia XorShift) to try to find a
+ * free slot.
+ *
+ * The table size is capped because, when there are more threads
+ * than CPUs, supposing that each thread were bound to a CPU,
+ * there would exist a perfect hash function mapping threads to
+ * slots that eliminates collisions. When we reach capacity, we
+ * search for this mapping by randomly varying the hash codes of
+ * colliding threads. Because search is random, and collisions
+ * only become known via CAS failures, convergence can be slow,
+ * and because threads are typically not bound to CPUS forever,
+ * may not occur at all. However, despite these limitations,
+ * observed contention rates are typically low in these cases.
+ *
+ * It is possible for a Cell to become unused when threads that
+ * once hashed to it terminate, as well as in the case where
+ * doubling the table causes no thread to hash to it under
+ * expanded mask. We do not try to detect or remove such cells,
+ * under the assumption that for long-running instances, observed
+ * contention levels will recur, so the cells will eventually be
+ * needed again; and for short-lived ones, it does not matter.
+ */
+
+ private static final long serialVersionUID = -3403386352761423917L;
+
+ /**
+ * Padded variant of AtomicLong supporting only raw accesses plus CAS.
+ * The value field is placed between pads, hoping that the JVM doesn't
+ * reorder them.
+ *
+ * JVM intrinsics note: It would be possible to use a release-only
+ * form of CAS here, if it were provided.
+ */
+ static final class Cell {
+ volatile long p0, p1, p2, p3, p4, p5, p6;
+ volatile long value;
+ volatile long q0, q1, q2, q3, q4, q5, q6;
+
+ Cell(long x) { value = x; }
+
+ final boolean cas(long cmp, long val) {
+ return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val);
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long valueOffset;
+
+ static {
+ try {
+ UNSAFE = getUnsafe();
+ Class> ak = Cell.class;
+ valueOffset = UNSAFE.objectFieldOffset
+ (ak.getDeclaredField("value"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ }
+
+ /**
+ * Holder for the thread-local hash code. The code is initially
+ * random, but may be set to a different value upon collisions.
+ */
+ static final class HashCode {
+ static final Random rng = new Random();
+ int code;
+
+ HashCode() {
+ int h = rng.nextInt(); // Avoid zero to allow xorShift rehash
+ code = (h == 0) ? 1 : h;
+ }
+ }
+
+ /**
+ * The corresponding ThreadLocal class
+ */
+ static final class ThreadHashCode extends ThreadLocal {
+ public HashCode initialValue() { return new HashCode(); }
+ }
+
+ /**
+ * Static per-thread hash codes. Shared across all instances to
+ * reduce ThreadLocal pollution and because adjustments due to
+ * collisions in one table are likely to be appropriate for
+ * others.
+ */
+ static final ThreadHashCode threadHashCode = new ThreadHashCode();
+
+ /**
+ * Number of CPUS, to place bound on table size
+ */
+ static final int NCPU = Runtime.getRuntime().availableProcessors();
+
+ /**
+ * Table of cells. When non-null, size is a power of 2.
+ */
+ transient volatile Cell[] cells;
+
+ /**
+ * Base value, used mainly when there is no contention, but also as
+ * a fallback during table initialization races. Updated via CAS.
+ */
+ transient volatile long base;
+
+ /**
+ * Spinlock (locked via CAS) used when resizing and/or creating Cells.
+ */
+ transient volatile int busy;
+
+ /**
+ * Package-private default constructor
+ */
+ Striped64() {
+ }
+
+ /**
+ * CASes the base field.
+ */
+ final boolean casBase(long cmp, long val) {
+ return UNSAFE.compareAndSwapLong(this, baseOffset, cmp, val);
+ }
+
+ /**
+ * CASes the busy field from 0 to 1 to acquire lock.
+ */
+ final boolean casBusy() {
+ return UNSAFE.compareAndSwapInt(this, busyOffset, 0, 1);
+ }
+
+ /**
+ * Computes the function of current and new value. Subclasses
+ * should open-code this update function for most uses, but the
+ * virtualized form is needed within retryUpdate.
+ *
+ * @param currentValue the current value (of either base or a cell)
+ * @param newValue the argument from a user update call
+ * @return result of the update function
+ */
+ abstract long fn(long currentValue, long newValue);
+
+ /**
+ * Handles cases of updates involving initialization, resizing,
+ * creating new Cells, and/or contention. See above for
+ * explanation. This method suffers the usual non-modularity
+ * problems of optimistic retry code, relying on rechecked sets of
+ * reads.
+ *
+ * @param x the value
+ * @param hc the hash code holder
+ * @param wasUncontended false if CAS failed before call
+ */
+ final void retryUpdate(long x, HashCode hc, boolean wasUncontended) {
+ int h = hc.code;
+ boolean collide = false; // True if last slot nonempty
+ for (; ; ) {
+ Cell[] as;
+ Cell a;
+ int n;
+ long v;
+ if ((as = cells) != null && (n = as.length) > 0) {
+ if ((a = as[(n - 1) & h]) == null) {
+ if (busy == 0) { // Try to attach new Cell
+ Cell r = new Cell(x); // Optimistically create
+ if (busy == 0 && casBusy()) {
+ boolean created = false;
+ try { // Recheck under lock
+ Cell[] rs;
+ int m, j;
+ if ((rs = cells) != null &&
+ (m = rs.length) > 0 &&
+ rs[j = (m - 1) & h] == null) {
+ rs[j] = r;
+ created = true;
+ }
+ } finally {
+ busy = 0;
+ }
+ if (created) { break; }
+ continue; // Slot is now non-empty
+ }
+ }
+ collide = false;
+ } else if (!wasUncontended) // CAS already known to fail
+ {
+ wasUncontended = true; // Continue after rehash
+ } else if (a.cas(v = a.value, fn(v, x))) { break; } else if (n >= NCPU || cells != as) {
+ collide = false; // At max size or stale
+ } else if (!collide) { collide = true; } else if (busy == 0 && casBusy()) {
+ try {
+ if (cells == as) { // Expand table unless stale
+ Cell[] rs = new Cell[n << 1];
+ for (int i = 0; i < n; ++i) { rs[i] = as[i]; }
+ cells = rs;
+ }
+ } finally {
+ busy = 0;
+ }
+ collide = false;
+ continue; // Retry with expanded table
+ }
+ h ^= h << 13; // Rehash
+ h ^= h >>> 17;
+ h ^= h << 5;
+ } else if (busy == 0 && cells == as && casBusy()) {
+ boolean init = false;
+ try { // Initialize table
+ if (cells == as) {
+ Cell[] rs = new Cell[2];
+ rs[h & 1] = new Cell(x);
+ cells = rs;
+ init = true;
+ }
+ } finally {
+ busy = 0;
+ }
+ if (init) { break; }
+ } else if (casBase(v = base, fn(v, x))) {
+ break; // Fall back on using base
+ }
+ }
+ hc.code = h; // Record index for next time
+ }
+
+ /**
+ * Sets base and all cells to the given value.
+ */
+ final void internalReset(long initialValue) {
+ Cell[] as = cells;
+ base = initialValue;
+ if (as != null) {
+ int n = as.length;
+ for (int i = 0; i < n; ++i) {
+ Cell a = as[i];
+ if (a != null) { a.value = initialValue; }
+ }
+ }
+ }
+
+ // Unsafe mechanics
+ private static final sun.misc.Unsafe UNSAFE;
+ private static final long baseOffset;
+ private static final long busyOffset;
+
+ static {
+ try {
+ UNSAFE = getUnsafe();
+ Class> sk = Striped64.class;
+ baseOffset = UNSAFE.objectFieldOffset
+ (sk.getDeclaredField("base"));
+ busyOffset = UNSAFE.objectFieldOffset
+ (sk.getDeclaredField("busy"));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ /**
+ * Returns a sun.misc.Unsafe. Suitable for use in a 3rd party package.
+ * Replace with a simple call to Unsafe.getUnsafe when integrating
+ * into a jdk.
+ *
+ * @return a sun.misc.Unsafe
+ */
+ private static sun.misc.Unsafe getUnsafe() {
+ try {
+ return sun.misc.Unsafe.getUnsafe();
+ } catch (SecurityException se) {
+ try {
+ return java.security.AccessController.doPrivileged
+ (new java.security
+ .PrivilegedExceptionAction() {
+ public sun.misc.Unsafe run() throws Exception {
+ java.lang.reflect.Field f = sun.misc
+ .Unsafe.class.getDeclaredField("theUnsafe");
+ f.setAccessible(true);
+ return (sun.misc.Unsafe)f.get(null);
+ }
+ });
+ } catch (java.security.PrivilegedActionException e) {
+ throw new RuntimeException("Could not initialize intrinsics",
+ e.getCause());
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java
new file mode 100755
index 00000000..da31e8e6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/Window.java
@@ -0,0 +1,100 @@
+/*
+ * 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.slots.statistic.base;
+
+/**
+ * Represents metrics data in a period of time window.
+ *
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class Window {
+
+ private final LongAdder pass = new LongAdder();
+ private final LongAdder block = new LongAdder();
+ private final LongAdder exception = new LongAdder();
+ private final LongAdder rt = new LongAdder();
+ private final LongAdder success = new LongAdder();
+ private final LongAdder minRt = new LongAdder();
+
+ public Window() {
+ minRt.add(4900);
+ }
+
+ /**
+ * Clean the adders and reset window to provided start time.
+ *
+ * @param startTime the start time of the window
+ * @return new clean window
+ */
+ Window resetTo(long startTime) {
+ pass.reset();
+ block.reset();
+ exception.reset();
+ rt.reset();
+ success.reset();
+ minRt.reset();
+ return this;
+ }
+
+ public long pass() {
+ return pass.sum();
+ }
+
+ public long block() {
+ return block.sum();
+ }
+
+ public long exception() {
+ return exception.sum();
+ }
+
+ public long rt() {
+ return rt.sum();
+ }
+
+ public long minRt() {
+ return minRt.longValue();
+ }
+
+ public long success() {
+ return success.sum();
+ }
+
+ public void addPass() {
+ pass.add(1L);
+ }
+
+ public void addException() {
+ exception.add(1L);
+ }
+
+ public void addBlock() {
+ block.add(1L);
+ }
+
+ public void addSuccess() {
+ success.add(1L);
+ }
+
+ public void addRT(long rt) {
+ this.rt.add(rt);
+
+ if (minRt.longValue() > rt) {
+ minRt.internalReset(rt);
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
new file mode 100755
index 00000000..7d7a0b51
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/WindowWrap.java
@@ -0,0 +1,68 @@
+/*
+ * 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.slots.statistic.base;
+
+/**
+ * Wrapper entity class for a period of time window.
+ *
+ * @param data type
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class WindowWrap {
+
+ /**
+ * The length of the window.
+ */
+ private final long windowLength;
+
+ /**
+ * Start time of the window in milliseconds.
+ */
+ private long windowStart;
+
+ /**
+ * Statistic value.
+ */
+ private T value;
+
+ /**
+ * @param windowLength the time length of the window
+ * @param windowStart the start timestamp of the window
+ * @param value window data
+ */
+ public WindowWrap(long windowLength, long windowStart, T value) {
+ this.windowLength = windowLength;
+ this.windowStart = windowStart;
+ this.value = value;
+ }
+
+ public long windowLength() {
+ return windowLength;
+ }
+
+ public long windowStart() {
+ return windowStart;
+ }
+
+ public T value() {
+ return value;
+ }
+
+ public void setValue(T value) {
+ this.value = value;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
new file mode 100755
index 00000000..6eb7df7e
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java
@@ -0,0 +1,228 @@
+/*
+ * 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.slots.statistic.metric;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.alibaba.csp.sentinel.node.metric.MetricNode;
+import com.alibaba.csp.sentinel.slots.statistic.base.Window;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+
+/**
+ * The basic metric class in Sentinel using a {@link WindowLeapArray} internal.
+ *
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class ArrayMetric implements Metric {
+
+ private final WindowLeapArray data;
+
+ public ArrayMetric(int windowLength, int interval) {
+ this.data = new WindowLeapArray(windowLength, interval);
+ }
+
+ /**
+ * For unit test.
+ */
+ public ArrayMetric(WindowLeapArray array) {
+ this.data = array;
+ }
+
+ @Override
+ public long success() {
+ data.currentWindow();
+ long success = 0;
+
+ List list = data.values();
+ for (Window window : list) {
+ success += window.success();
+ }
+ return success;
+ }
+
+ @Override
+ public long maxSuccess() {
+ data.currentWindow();
+ long success = 0;
+
+ List list = data.values();
+ for (Window window : list) {
+ if (window.success() > success) {
+ success = window.success();
+ }
+ }
+ return Math.max(success, 1);
+ }
+
+ @Override
+ public long exception() {
+ data.currentWindow();
+ long exception = 0;
+ List list = data.values();
+ for (Window window : list) {
+ exception += window.exception();
+ }
+ return exception;
+ }
+
+ @Override
+ public long block() {
+ data.currentWindow();
+ long block = 0;
+ List list = data.values();
+ for (Window window : list) {
+ block += window.block();
+ }
+ return block;
+ }
+
+ @Override
+ public long pass() {
+ data.currentWindow();
+ long pass = 0;
+ List list = data.values();
+
+ for (Window window : list) {
+ pass += window.pass();
+ }
+ return pass;
+ }
+
+ @Override
+ public long rt() {
+ data.currentWindow();
+ long rt = 0;
+ List list = data.values();
+ for (Window window : list) {
+ rt += window.rt();
+ }
+ return rt;
+ }
+
+ @Override
+ public long minRt() {
+ data.currentWindow();
+ long rt = 4900;
+ List list = data.values();
+ for (Window window : list) {
+ if (window.minRt() < rt) {
+ rt = window.minRt();
+ }
+ }
+
+ return Math.max(1, rt);
+ }
+
+ @Override
+ public List details() {
+ List details = new ArrayList();
+ data.currentWindow();
+ for (WindowWrap window : data.list()) {
+ if (window == null) {
+ continue;
+ }
+ MetricNode node = new MetricNode();
+ node.setBlockedQps(window.value().block());
+ node.setException(window.value().exception());
+ node.setPassedQps(window.value().pass());
+ long passQps = window.value().success();
+ node.setSuccessQps(passQps);
+ if (passQps != 0) {
+ node.setRt(window.value().rt() / passQps);
+ } else {
+ node.setRt(window.value().rt());
+ }
+ node.setTimestamp(window.windowStart());
+ details.add(node);
+ }
+
+ return details;
+ }
+
+ @Override
+ public Window[] windows() {
+ data.currentWindow();
+ return data.values().toArray(new Window[data.values().size()]);
+ }
+
+ @Override
+ public void addException() {
+ WindowWrap wrap = data.currentWindow();
+ wrap.value().addException();
+ }
+
+ @Override
+ public void addBlock() {
+ WindowWrap wrap = data.currentWindow();
+ wrap.value().addBlock();
+ }
+
+ @Override
+ public void addSuccess() {
+ WindowWrap wrap = data.currentWindow();
+ wrap.value().addSuccess();
+ }
+
+ @Override
+ public void addPass() {
+ WindowWrap wrap = data.currentWindow();
+ wrap.value().addPass();
+ }
+
+ @Override
+ public void addRT(long rt) {
+ WindowWrap wrap = data.currentWindow();
+ wrap.value().addRT(rt);
+ }
+
+ @Override
+ public void debugQps() {
+ data.currentWindow();
+ StringBuilder sb = new StringBuilder();
+ sb.append(Thread.currentThread().getId()).append("_");
+ for (WindowWrap windowWrap : data.list()) {
+
+ sb.append(windowWrap.windowStart()).append(":").append(windowWrap.value().pass()).append(":")
+ .append(windowWrap.value().block());
+ sb.append(",");
+
+ }
+ System.out.println(sb);
+ }
+
+ @Override
+ public long previousWindowBlock() {
+ WindowWrap wrap = data.currentWindow();
+ wrap = data.getPreviousWindow();
+ if (wrap == null) {
+ return 0;
+ }
+ return wrap.value().block();
+ }
+
+ @Override
+ public long previousWindowPass() {
+ WindowWrap wrap = data.currentWindow();
+ wrap = data.getPreviousWindow();
+ if (wrap == null) {
+ return 0;
+ }
+ return wrap.value().pass();
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java
new file mode 100755
index 00000000..fc56bf9e
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/Metric.java
@@ -0,0 +1,118 @@
+/*
+ * 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.slots.statistic.metric;
+
+import java.util.List;
+
+import com.alibaba.csp.sentinel.node.metric.MetricNode;
+import com.alibaba.csp.sentinel.slots.statistic.base.Window;
+
+/**
+ * Represents a basic structure recording invocation metrics of protected resources.
+ *
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public interface Metric {
+
+ /**
+ * Get total success count.
+ *
+ * @return success count
+ */
+ long success();
+
+ long maxSuccess();
+
+ /**
+ * Get total exception count.
+ *
+ * @return exception count
+ */
+ long exception();
+
+ /**
+ * Get total block count.
+ *
+ * @return block count
+ */
+ long block();
+
+ /**
+ * Get total pass count.
+ *
+ * @return pass count
+ */
+ long pass();
+
+ /**
+ * Get total RT.
+ *
+ * @return total RT
+ */
+ long rt();
+
+ /**
+ * Get the minimal RT.
+ *
+ * @return minimal RT
+ */
+ long minRt();
+
+ List details();
+
+ /**
+ * Get the raw window array.
+ *
+ * @return window metric array
+ */
+ Window[] windows();
+
+ /**
+ * Increment by one the current exception count.
+ */
+ void addException();
+
+ /**
+ * Increment by one the current blovk count.
+ */
+ void addBlock();
+
+ /**
+ * Increment by one the current success count.
+ */
+ void addSuccess();
+
+ /**
+ * Increment by one the current pass count.
+ */
+ void addPass();
+
+ /**
+ * Add given RT to current total RT.
+ *
+ * @param rt RT
+ */
+ void addRT(long rt);
+
+ // Tool methods.
+
+ void debugQps();
+
+ long previousWindowBlock();
+
+ long previousWindowPass();
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java
new file mode 100755
index 00000000..70f60a92
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/WindowLeapArray.java
@@ -0,0 +1,92 @@
+/*
+ * 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.slots.statistic.metric;
+
+import java.util.concurrent.locks.ReentrantLock;
+
+import com.alibaba.csp.sentinel.slots.statistic.base.LeapArray;
+import com.alibaba.csp.sentinel.slots.statistic.base.Window;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+
+/**
+ * The fundamental data structure for metric statistics in a time window.
+ *
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class WindowLeapArray extends LeapArray {
+
+ private final int timeLength;
+
+ public WindowLeapArray(int windowLengthInMs, int intervalInSec) {
+ super(windowLengthInMs, intervalInSec);
+ timeLength = intervalInSec * 1000;
+ }
+
+ private ReentrantLock addLock = new ReentrantLock();
+
+ @Override
+ public WindowWrap currentWindow(long time) {
+
+ long timeId = time / windowLength;
+ // Calculate current index.
+ int idx = (int)(timeId % array.length());
+
+ // Cut the time to current window start.
+ time = time - time % windowLength;
+
+ while (true) {
+ WindowWrap old = array.get(idx);
+ if (old == null) {
+ WindowWrap window = new WindowWrap(windowLength, time, new Window());
+ if (array.compareAndSet(idx, null, window)) {
+ return window;
+ } else {
+ Thread.yield();
+ }
+ } else if (time == old.windowStart()) {
+ return old;
+ } else if (time > old.windowStart()) {
+ if (addLock.tryLock()) {
+ try {
+ WindowWrap window = new WindowWrap(windowLength, time, new Window());
+ if (array.compareAndSet(idx, old, window)) {
+ for (int i = 0; i < array.length(); i++) {
+ WindowWrap tmp = array.get(i);
+ if (tmp == null) {
+ continue;
+ } else {
+ if (tmp.windowStart() < time - timeLength) {
+ array.set(i, null);
+ }
+ }
+ }
+ return window;
+ }
+ } finally {
+ addLock.unlock();
+ }
+
+ } else {
+ Thread.yield();
+ }
+
+ } else if (time < old.windowStart()) {
+ return new WindowWrap(windowLength, time, new Window());
+ }
+ }
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemBlockException.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemBlockException.java
new file mode 100755
index 00000000..a6d6a835
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemBlockException.java
@@ -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.slots.system;
+
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * @author jialiang.linjl
+ */
+public class SystemBlockException extends BlockException {
+
+ String resourceName;
+
+ public String getResourceName() {
+ return resourceName;
+ }
+
+ public SystemBlockException(String resourceName, String message, Throwable cause) {
+ super(message, cause);
+ this.resourceName = resourceName;
+ }
+
+ public SystemBlockException(String resourceName, String ruleLimitApp) {
+ super(ruleLimitApp);
+ this.resourceName = resourceName;
+ }
+
+ @Override
+ public Throwable fillInStackTrace() {
+ return this;
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRule.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRule.java
new file mode 100755
index 00000000..318f6bef
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRule.java
@@ -0,0 +1,170 @@
+/*
+ * 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.slots.system;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slots.block.AbstractRule;
+
+/**
+ *
+ * Sentinel System Rule makes the inbound traffic and capacity meet. It takes
+ * average RT, QPS and thread count of requests into account. And it also
+ * provides a measurement of system's load, but only available on Linux.
+ *
+ *
+ * We recommend to coordinate {@link #highestSystemLoad}, {@link #qps}, {@link #avgRt}
+ * and {@link #maxThread} to make sure your system run in safety level.
+ *
+ *
+ * To set the threshold appropriately, performance test may be needed.
+ *
+ *
+ * @author jialiang.linjl
+ * @see SystemRuleManager
+ */
+public class SystemRule extends AbstractRule {
+
+ /**
+ * negative value means no threshold checking.
+ */
+ private double highestSystemLoad = -1;
+ private double qps = -1;
+ private long avgRt = -1;
+ private long maxThread = -1;
+
+ public double getQps() {
+ return qps;
+ }
+
+ /**
+ * Set max total QPS. In a high concurrency condition, real passed QPS may be greater than max QPS set.
+ * The real passed QPS will nearly satisfy the following formula:
+ *
+ *
real passed QPS = QPS set + concurrent thread number
+ *
+ * @param qps max total QOS, values <= 0 are special for clearing the threshold.
+ */
+ public void setQps(double qps) {
+ this.qps = qps;
+ }
+
+ public long getMaxThread() {
+ return maxThread;
+ }
+
+ /**
+ * Set max PARALLEL working thread. When concurrent thread number is greater than {@code maxThread} only
+ * maxThread will run in parallel.
+ *
+ * @param maxThread max parallel thread number, values <= 0 are special for clearing the threshold.
+ */
+ public void setMaxThread(long maxThread) {
+ this.maxThread = maxThread;
+ }
+
+ public long getAvgRt() {
+ return avgRt;
+ }
+
+ /**
+ * Set max average RT(response time) of all passed requests.
+ *
+ * @param avgRt max average response time, values <= 0 are special for clearing the threshold.
+ */
+ public void setAvgRt(long avgRt) {
+ this.avgRt = avgRt;
+ }
+
+ public double getHighestSystemLoad() {
+ return highestSystemLoad;
+ }
+
+ /**
+ *
+ * Set highest load. The load is not same as Linux system load, which is not sensitive enough.
+ * To calculate the load, both Linux system load, current global response time and global QPS will be considered,
+ * which means that we need to coordinate with {@link #setAvgRt(long)} and {@link #setQps(double)}
+ *
+ *
+ * Note that this parameter is only available on Unix like system.
+ *
+ *
+ * @param highestSystemLoad highest system load, values <= 0 are special for clearing the threshold.
+ * @see SystemRuleManager
+ */
+ public void setHighestSystemLoad(double highestSystemLoad) {
+ this.highestSystemLoad = highestSystemLoad;
+ }
+
+ @Override
+ public boolean passCheck(Context context, DefaultNode node, int count, Object... args) {
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof SystemRule)) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+
+ SystemRule that = (SystemRule)o;
+
+ if (Double.compare(that.highestSystemLoad, highestSystemLoad) != 0) {
+ return false;
+ }
+
+ if (Double.compare(that.qps, qps) != 0) {
+ return false;
+ }
+
+ if (avgRt != that.avgRt) {
+ return false;
+ }
+ return maxThread == that.maxThread;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ long temp;
+ temp = Double.doubleToLongBits(highestSystemLoad);
+ result = 31 * result + (int)(temp ^ (temp >>> 32));
+
+ temp = Double.doubleToLongBits(qps);
+ result = 31 * result + (int)(temp ^ (temp >>> 32));
+
+ result = 31 * result + (int)(avgRt ^ (avgRt >>> 32));
+ result = 31 * result + (int)(maxThread ^ (maxThread >>> 32));
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "SystemRule{" +
+ "highestSystemLoad=" + highestSystemLoad +
+ ", qps=" + qps +
+ ", avgRt=" + avgRt +
+ ", maxThread=" + maxThread +
+ "}";
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java
new file mode 100755
index 00000000..c8e6378b
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java
@@ -0,0 +1,303 @@
+/*
+ * 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.slots.system;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
+import com.alibaba.csp.sentinel.property.SentinelProperty;
+import com.alibaba.csp.sentinel.property.SimplePropertyListener;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ *
+ * Sentinel System Rule makes the inbound traffic and capacity meet. It takes
+ * average rt, qps, thread count of incoming requests into account. And it also
+ * provides a measurement of system's load, but only available on Linux.
+ *
+ *
+ * rt, qps, thread count is easy to understand. If the incoming requests'
+ * rt,qps, thread count exceeds its threshold, the requests will be
+ * rejected.however, we use a different method to calculate the load.
+ *
+ *
+ * Consider the system as a pipeline,transitions between constraints result in
+ * three different regions (traffic-limited, capacity-limited and danger area)
+ * with qualitatively different behavior. When there isn’t enough request in
+ * flight to fill the pipe, RTprop determines behavior; otherwise, the system
+ * capacity dominates. Constraint lines intersect at inflight = Capacity ×
+ * RTprop. Since the pipe is full past this point, the inflight –capacity excess
+ * creates a queue, which results in the linear dependence of RTT on inflight
+ * traffic and an increase in system load.In danger area, system will stop
+ * responding.
+ * Referring to BBR algorithm to learn more.
+ *
+ *
+ * Note that {@link SystemRule} only effect on inbound requests, outbound traffic
+ * will not limit by {@link SystemRule}
+ *
+ *
+ * @author jialiang.linjl
+ * @author leyou
+ */
+public class SystemRuleManager {
+
+ private static volatile double highestSystemLoad = Double.MAX_VALUE;
+ private static volatile double qps = Double.MAX_VALUE;
+ private static volatile long maxRt = Long.MAX_VALUE;
+ private static volatile long maxThread = Long.MAX_VALUE;
+ /**
+ * mark whether the threshold are set by user.
+ */
+ private static volatile boolean highestSystemLoadIsSet = false;
+ private static volatile boolean qpsIsSet = false;
+ private static volatile boolean maxRtIsSet = false;
+ private static volatile boolean maxThreadIsSet = false;
+
+ static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);
+
+ private static SystemStatusListener statusListener = null;
+ private final static SystemPropertyListener listener = new SystemPropertyListener();
+ private static SentinelProperty> currentProperty = new DynamicSentinelProperty>();
+
+ private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
+
+ static {
+ checkSystemStatus.set(false);
+ statusListener = new SystemStatusListener();
+ scheduler.scheduleAtFixedRate(statusListener, 5, 1, TimeUnit.SECONDS);
+ currentProperty.addListener(listener);
+ }
+
+ /**
+ * Listen to the {@link SentinelProperty} for {@link SystemRule}s. The property is the source
+ * of {@link SystemRule}s. System rules can also be set by {@link #loadRules(List)} directly.
+ *
+ * @param property the property to listen.
+ */
+ public static void register2Property(SentinelProperty> property) {
+ synchronized (listener) {
+ currentProperty.removeListener(listener);
+ property.addListener(listener);
+ currentProperty = property;
+ }
+ }
+
+ /**
+ * Load {@link SystemRule}s, former rules will be replaced.
+ *
+ * @param rules new rules to load.
+ */
+ public static void loadRules(List rules) {
+ currentProperty.updateValue(rules);
+ }
+
+ /**
+ * Get a copy of the rules.
+ *
+ * @return a new copy of the rules.
+ */
+ public static List getRules() {
+
+ List result = new ArrayList();
+ if (!checkSystemStatus.get()) {
+ return result;
+ }
+
+ if (highestSystemLoadIsSet) {
+ SystemRule loadRule = new SystemRule();
+ loadRule.setHighestSystemLoad(highestSystemLoad);
+ result.add(loadRule);
+ }
+
+ if (maxRtIsSet) {
+ SystemRule rtRule = new SystemRule();
+ rtRule.setAvgRt(maxRt);
+ result.add(rtRule);
+ }
+
+ if (maxThreadIsSet) {
+ SystemRule threadRule = new SystemRule();
+ threadRule.setMaxThread(maxThread);
+ result.add(threadRule);
+ }
+
+ if (qpsIsSet) {
+ SystemRule qpsRule = new SystemRule();
+ qpsRule.setQps(qps);
+ result.add(qpsRule);
+ }
+
+ return result;
+ }
+
+ public static double getQps() {
+ return qps;
+ }
+
+ public static void setQps(double qps) {
+ SystemRuleManager.qps = qps;
+ }
+
+ public static long getMaxRt() {
+ return maxRt;
+ }
+
+ public static long getMaxThread() {
+ return maxThread;
+ }
+
+ static class SystemPropertyListener extends SimplePropertyListener> {
+
+ @Override
+ public void configUpdate(List rules) {
+ restoreSetting();
+ // systemRules = rules;
+ if (rules != null && rules.size() >= 1) {
+ for (SystemRule rule : rules) {
+ loadSystemConf(rule);
+ }
+ } else {
+ checkSystemStatus.set(false);
+ }
+
+ RecordLog.info("current system system status : " + checkSystemStatus.get());
+ RecordLog.info("current highestSystemLoad status : " + highestSystemLoad);
+ RecordLog.info("current maxRt : " + maxRt);
+ RecordLog.info("current maxThread : " + maxThread);
+ RecordLog.info("current qps : " + qps);
+ }
+
+ protected void restoreSetting() {
+ checkSystemStatus.set(false);
+
+ // should restore changes
+ highestSystemLoad = Double.MAX_VALUE;
+ maxRt = Long.MAX_VALUE;
+ maxThread = Long.MAX_VALUE;
+ qps = Double.MAX_VALUE;
+
+ highestSystemLoadIsSet = false;
+ maxRtIsSet = false;
+ maxThreadIsSet = false;
+ qpsIsSet = false;
+ }
+
+ }
+
+ public static Boolean getCheckSystemStatus() {
+ return checkSystemStatus.get();
+ }
+
+ public static double getHighestSystemLoad() {
+ return highestSystemLoad;
+ }
+
+ public static void setHighestSystemLoad(double highestSystemLoad) {
+ SystemRuleManager.highestSystemLoad = highestSystemLoad;
+ }
+
+ public static void loadSystemConf(SystemRule rule) {
+
+ boolean checkStatus = false;
+ // 首先判断是否有效
+
+ if (rule.getHighestSystemLoad() > 0) {
+ highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());
+ highestSystemLoadIsSet = true;
+ checkStatus = true;
+ }
+
+ if (rule.getAvgRt() > 0) {
+ maxRt = Math.min(maxRt, rule.getAvgRt());
+ maxRtIsSet = true;
+ checkStatus = true;
+ }
+ if (rule.getMaxThread() > 0) {
+ maxThread = Math.min(maxThread, rule.getMaxThread());
+ maxThreadIsSet = true;
+ checkStatus = true;
+ }
+
+ if (rule.getQps() > 0) {
+ qps = Math.min(qps, rule.getQps());
+ qpsIsSet = true;
+ checkStatus = true;
+ }
+
+ checkSystemStatus.set(checkStatus);
+
+ }
+
+ /**
+ * Apply {@link SystemRule} to the resource. Only inbound traffic will be checked.
+ *
+ * @param resourceWrapper the resource.
+ * @throws BlockException when any system rule's threshold is exceeded.
+ */
+ public static void checkSystem(ResourceWrapper resourceWrapper) throws BlockException {
+
+ // 确定开关开了
+ if (checkSystemStatus.get() == false) {
+ return;
+ }
+
+ // for inbound traffic only
+ if (resourceWrapper.getType() != EntryType.IN) {
+ return;
+ }
+
+ // total qps
+ double currentQps = Constants.ENTRY_NODE == null ? 0.0 : Constants.ENTRY_NODE.successQps();
+ if (currentQps > qps) {
+ throw new SystemBlockException(resourceWrapper.getName(), "qps");
+ }
+
+ // total thread
+ int currentThread = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.curThreadNum();
+ if (currentThread > maxThread) {
+ throw new SystemBlockException(resourceWrapper.getName(), "thread");
+ }
+
+ double rt = Constants.ENTRY_NODE == null ? 0 : Constants.ENTRY_NODE.avgRt();
+ if (rt > maxRt) {
+ throw new SystemBlockException(resourceWrapper.getName(), "rt");
+ }
+
+ // 完全按照RT,BBR算法来
+ if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {
+ if (currentThread > 1 &&
+ currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {
+ throw new SystemBlockException(resourceWrapper.getName(), "load");
+ }
+ }
+
+ }
+
+ public static double getCurrentSystemAvgLoad() {
+ return statusListener.getSystemAverageLoad();
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java
new file mode 100755
index 00000000..e7519270
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemSlot.java
@@ -0,0 +1,44 @@
+/*
+ * 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.slots.system;
+
+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.ProcessorSlot;
+import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
+
+/**
+ * A {@link ProcessorSlot} that dedicates to {@link SystemRule} checking.
+ *
+ * @author jialiang.linjl
+ * @author leyou
+ */
+public class SystemSlot extends AbstractLinkedProcessorSlot {
+
+ @Override
+ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
+ throws Throwable {
+ SystemRuleManager.checkSystem(resourceWrapper);
+ fireEntry(context, resourceWrapper, node, count, args);
+ }
+
+ @Override
+ public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
+ fireExit(context, resourceWrapper, count, args);
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java
new file mode 100755
index 00000000..2ccf6c63
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java
@@ -0,0 +1,68 @@
+/*
+ * 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.slots.system;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.OperatingSystemMXBean;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.Constants;
+
+/**
+ * @author jialiang.linjl
+ */
+public class SystemStatusListener implements Runnable {
+
+ volatile double currentLoad = -1;
+
+ volatile String reason = StringUtil.EMPTY;
+
+ static final int processor = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
+
+ public double getSystemAverageLoad() {
+ return currentLoad;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (!SystemRuleManager.getCheckSystemStatus()) {
+ return;
+ }
+
+ // system average load
+ OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
+ currentLoad = operatingSystemMXBean.getSystemLoadAverage();
+
+ StringBuilder sb = new StringBuilder();
+ if (currentLoad > SystemRuleManager.getHighestSystemLoad()) {
+ sb.append("load:").append(currentLoad).append(";");
+ sb.append("qps:").append(Constants.ENTRY_NODE.passQps()).append(";");
+ sb.append("rt:").append(Constants.ENTRY_NODE.avgRt()).append(";");
+ sb.append("thread:").append(Constants.ENTRY_NODE.curThreadNum()).append(";");
+ sb.append("success:").append(Constants.ENTRY_NODE.successQps()).append(";");
+ sb.append("minRt:").append(Constants.ENTRY_NODE.minRt()).append(";");
+ sb.append("maxSuccess:").append(Constants.ENTRY_NODE.maxSuccessQps()).append(";");
+ RecordLog.info(sb.toString());
+ }
+
+ } catch (Throwable e) {
+ RecordLog.info("could not get system error ", e);
+ }
+ }
+
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/AppNameUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/AppNameUtil.java
new file mode 100755
index 00000000..d616f29f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/AppNameUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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.util;
+
+import java.io.File;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+
+/**
+ * Util class for getting application name. This class uses the flowing order to get app's name:
+ *
+ *
+ *
get {@code project.name} from System Properties, if not null, use the value as app name;
+ *
get {@code sun.java.command} from System properties, remove path, arguments and ".jar" or ".JAR"
+ * suffix, use the result as app name. Note that whitespace in file name or path is not allowed, or a
+ * wrong app name may be gotten, For example:
+ *
+ *
+ * "test.Main" -> test.Main
+ * "/target/test.Main" -> test.Main
+ * "/target/test.Main args1" -> test.Main
+ * "Main.jar" -> Main
+ * "/target/Main.JAR args1" -> Main
+ * "Mai n.jar" -> Mai // whitespace in file name is not allowed
+ *
+ *
+ *
+ *
+ *
+ * @author Eric Zhao
+ * @author leyou
+ */
+public final class AppNameUtil {
+
+ public static final String APP_NAME = "project.name";
+ public static final String SUN_JAVA_COMMAND = "sun.java.command";
+
+ private static String appName;
+
+ private AppNameUtil() {
+ }
+
+ static {
+ resolveAppName();
+ RecordLog.info("app name resolved: " + appName);
+ }
+
+ public static void resolveAppName() {
+ String app = System.getProperty(APP_NAME);
+ // use -Dproject.name first
+ if (!isEmpty(app)) {
+ appName = app;
+ return;
+ }
+
+ // parse sun.java.command property
+ String command = System.getProperty(SUN_JAVA_COMMAND);
+ if (isEmpty(command)) {
+ return;
+ }
+ command = command.split("\\s")[0];
+ if (command.contains(File.separator)) {
+ String[] strs = command.split(File.separator);
+ command = strs[strs.length - 1];
+ }
+ if (command.endsWith(".jar") || command.endsWith(".JAR")) {
+ command = command.substring(0, command.length() - 4);
+ }
+ appName = command;
+ }
+
+ public static String getAppName() {
+ return appName;
+ }
+
+ private static boolean isEmpty(String str) {
+ return str == null || "".equals(str);
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/HostNameUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/HostNameUtil.java
new file mode 100755
index 00000000..fb0c3581
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/HostNameUtil.java
@@ -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.util;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.util.Enumeration;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+
+/**
+ * Get host name and ip of the host.
+ *
+ * @author leyou
+ */
+public final class HostNameUtil {
+
+ private static String ip;
+ private static String hostName;
+
+ static {
+ try {
+ // Init the host information.
+ resolveHost();
+ } catch (Exception e) {
+ RecordLog.info("Failed to get local host", e);
+ }
+ }
+
+ private static void resolveHost() throws Exception {
+ InetAddress addr = InetAddress.getLocalHost();
+ hostName = addr.getHostName();
+ ip = addr.getHostAddress();
+ if (addr.isLoopbackAddress()) {
+ // find the first IPv4 Address that not loopback
+ Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface in = interfaces.nextElement();
+ Enumeration addrs = in.getInetAddresses();
+ while (addrs.hasMoreElements()) {
+ InetAddress address = addrs.nextElement();
+ if (!address.isLoopbackAddress() && address instanceof Inet4Address) {
+ ip = address.getHostAddress();
+ }
+ }
+ }
+ }
+ }
+
+ public static String getIp() {
+ return ip;
+ }
+
+ public static String getHostName() {
+ return hostName;
+ }
+
+ public static String getConfigString() {
+ return "{\n"
+ + "\t\"machine\": \"" + hostName + "\",\n"
+ + "\t\"ip\": \"" + ip + "\"\n"
+ + "}";
+ }
+
+ private HostNameUtil() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/IdUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/IdUtil.java
new file mode 100755
index 00000000..0dfaded4
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/IdUtil.java
@@ -0,0 +1,70 @@
+/*
+ * 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.util;
+
+/**
+ * @author qinan.qn
+ */
+public final class IdUtil {
+
+ public static String truncate(String id) {
+ IdLexer lexer = new IdLexer(id);
+ StringBuilder sb = new StringBuilder();
+ String r;
+ String temp = "";
+ while ((r = lexer.nextToken()) != null) {
+ if ("(".equals(r) || ")".equals(r) || ",".equals(r)) {
+ sb.append(temp).append(r);
+ temp = "";
+ } else if (!".".equals(r)) {
+ temp = r;
+ }
+ }
+
+ return sb.toString();
+ }
+
+ private static class IdLexer {
+ private String id;
+ private int idx = 0;
+
+ IdLexer(String id) {
+ this.id = id;
+ }
+
+ String nextToken() {
+ int oldIdx = idx;
+ String result = null;
+ while (idx != id.length()) {
+ char curChar = id.charAt(idx);
+ if (curChar == '.' || curChar == '(' || curChar == ')' || curChar == ',') {
+ if (idx == oldIdx) {
+ result = String.valueOf(curChar);
+ ++idx;
+ break;
+ } else {
+ result = id.substring(oldIdx, idx);
+ break;
+ }
+ }
+ ++idx;
+ }
+ return result;
+ }
+ }
+
+ private IdUtil() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/MethodUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/MethodUtil.java
new file mode 100755
index 00000000..e4195da1
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/MethodUtil.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/***
+ * Util class for processing {@link Method}.
+ *
+ * @author youji.zj
+ */
+public final class MethodUtil {
+
+ private static Map methodNameMap = new HashMap();
+
+ private static final Object LOCK = new Object();
+
+ /**
+ * Parse and get the method name.
+ */
+ public static String getMethodName(Method method) {
+ String methodName = methodNameMap.get(method);
+ if (methodName == null) {
+ synchronized (LOCK) {
+ methodName = methodNameMap.get(method);
+ if (methodName == null) {
+ StringBuilder sb = new StringBuilder();
+
+ String className = method.getDeclaringClass().getName();
+ String name = method.getName();
+ Class>[] params = method.getParameterTypes();
+ sb.append(className).append(":").append(name);
+ sb.append("(");
+
+ int paramPos = 0;
+ for (Class> clazz : params) {
+ sb.append(clazz.getName());
+ if (++paramPos < params.length) {
+ sb.append(",");
+ }
+ }
+ sb.append(")");
+ methodName = sb.toString();
+
+ HashMap newMap = new HashMap(methodNameMap);
+ newMap.put(method, methodName);
+ methodNameMap = newMap;
+ }
+ }
+ }
+ return methodName;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/PidUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/PidUtil.java
new file mode 100755
index 00000000..31efefb7
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/PidUtil.java
@@ -0,0 +1,31 @@
+/*
+ * 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.util;
+
+import java.lang.management.ManagementFactory;
+
+/**
+ * Util class providing pid of current process.
+ */
+public final class PidUtil {
+
+ public static int getPid() {
+ String name = ManagementFactory.getRuntimeMXBean().getName();
+ return Integer.parseInt(name.split("@")[0]);
+ }
+
+ private PidUtil() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/StringUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/StringUtil.java
new file mode 100755
index 00000000..a355ef56
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/StringUtil.java
@@ -0,0 +1,120 @@
+/*
+ * 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.util;
+
+/***
+ * Util class providing operations on {@link String}.
+ *
+ * @author youji.zj
+ */
+public final class StringUtil {
+
+ public static final String EMPTY = "";
+
+ public static boolean equalsIgnoreCase(final CharSequence str1, final CharSequence str2) {
+ if (str1 == null || str2 == null) {
+ return str1 == str2;
+ } else if (str1 == str2) {
+ return true;
+ } else if (str1.length() != str2.length()) {
+ return false;
+ } else {
+ return regionMatches(str1, true, 0, str2, 0, str1.length());
+ }
+ }
+
+ public static boolean equals(String str1, String str2) {
+ return str1 == null ? str2 == null : str1.equals(str2);
+ }
+
+ public static boolean isBlank(String str) {
+ int strLen;
+ if (str == null || (strLen = str.length()) == 0) {
+ return true;
+ }
+ for (int i = 0; i < strLen; i++) {
+ if ((!Character.isWhitespace(str.charAt(i)))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static boolean isNotBlank(String str) {
+ return !isBlank(str);
+ }
+
+ public static boolean isEmpty(String str) {
+ return str == null || str.length() == 0;
+ }
+
+ public static boolean isNotEmpty(String str) {
+ return !isEmpty(str);
+ }
+
+ public static String trimToEmpty(String str) {
+ return str == null ? EMPTY : str.trim();
+ }
+
+ public static String trim(String str) {
+ return str == null ? null : str.trim();
+ }
+
+ private static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart,
+ final CharSequence substring, final int start, final int length) {
+ if (cs instanceof String && substring instanceof String) {
+ return ((String)cs).regionMatches(ignoreCase, thisStart, (String)substring, start, length);
+ }
+ int index1 = thisStart;
+ int index2 = start;
+ int tmpLen = length;
+
+ // Extract these first so we detect NPEs the same as the java.lang.String version
+ final int srcLen = cs.length() - thisStart;
+ final int otherLen = substring.length() - start;
+
+ // Check for invalid parameters
+ if (thisStart < 0 || start < 0 || length < 0) {
+ return false;
+ }
+
+ // Check that the regions are long enough
+ if (srcLen < length || otherLen < length) {
+ return false;
+ }
+
+ while (tmpLen-- > 0) {
+ final char c1 = cs.charAt(index1++);
+ final char c2 = substring.charAt(index2++);
+
+ if (c1 == c2) {
+ continue;
+ }
+
+ if (!ignoreCase) {
+ return false;
+ }
+
+ // The same check as in String.regionMatches():
+ if (Character.toUpperCase(c1) != Character.toUpperCase(c2)
+ && Character.toLowerCase(c1) != Character.toLowerCase(c2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/TimeUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/TimeUtil.java
new file mode 100755
index 00000000..a51276a0
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/util/TimeUtil.java
@@ -0,0 +1,52 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provides millisecond-level time of OS.
+ *
+ * @author qinan.qn
+ */
+public final class TimeUtil {
+
+ private static volatile long currentTimeMillis;
+
+ static {
+ currentTimeMillis = System.currentTimeMillis();
+ Thread daemon = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ currentTimeMillis = System.currentTimeMillis();
+ try {
+ TimeUnit.MILLISECONDS.sleep(1);
+ } catch (Throwable e) {
+
+ }
+ }
+ }
+ });
+ daemon.setDaemon(true);
+ daemon.setName("sentinel-time-tick-thread");
+ daemon.start();
+ }
+
+ public static long currentTimeMillis() {
+ return currentTimeMillis;
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ConfigPropertyHelper.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ConfigPropertyHelper.java
new file mode 100755
index 00000000..773722e9
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ConfigPropertyHelper.java
@@ -0,0 +1,69 @@
+/*
+ * 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 java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.Properties;
+
+import com.alibaba.csp.sentinel.log.LogBase;
+import com.alibaba.csp.sentinel.util.AppNameUtil;
+
+/**
+ * Helper class for executing a task within a config context via properties file.
+ *
+ * @author Eric Zhao
+ */
+public final class ConfigPropertyHelper {
+
+ public static void setAppNameProperty(String appName) {
+ System.setProperty(AppNameUtil.APP_NAME, appName);
+ }
+
+ public static void clearAppNameProperty() {
+ System.clearProperty(AppNameUtil.APP_NAME);
+ }
+
+ public static void runWithConfig(Properties prop, String appName, Task task) throws Exception {
+ if (prop == null || appName == null || "".equals(appName)) {
+ throw new IllegalArgumentException("Prop and appName cannot be empty");
+ }
+ // Set application name property.
+ setAppNameProperty(appName);
+ // Save the config.
+ String path = LogBase.getLogBaseDir() + appName + ".properties";
+ File file = new File(path);
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ OutputStream outputStream = new FileOutputStream(file);
+ prop.store(outputStream,"");
+ outputStream.close();
+ // Run the procedure.
+ task.run();
+ // Clean-up.
+ file.delete();
+ // Clear application name property.
+ clearAppNameProperty();
+ }
+
+ public interface Task {
+ void run() throws Exception;
+ }
+
+ private ConfigPropertyHelper() {}
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ContextTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ContextTest.java
new file mode 100755
index 00000000..cb09f44a
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/ContextTest.java
@@ -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;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.context.ContextUtil;
+
+/**
+ * @author jialiang.linjl
+ */
+public class ContextTest {
+
+ @Test
+ public void testEnterContext() {
+ // TODO: rewrite this unit test
+ ContextUtil.enter("entry", "origin");
+
+ ContextUtil.exit();
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java
new file mode 100755
index 00000000..e6c9935f
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/RecordLogTest.java
@@ -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;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+
+import org.junit.Test;
+
+/**
+ * @author xuyue
+ */
+public class RecordLogTest {
+
+ @Test
+ public void testLogException() {
+ Exception e = new Exception("ex");
+ RecordLog.info("Error", e);
+ }
+
+ @Test
+ public void testLogRolling() {
+ int count = 1000;
+ while (--count > 0) {
+ RecordLog.info("Count " + count);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphOTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphOTest.java
new file mode 100755
index 00000000..7f8787a5
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphOTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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 static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+
+/**
+ * Test cases for {@link SphO}.
+ *
+ * @author jialiang.linjl
+ */
+public class SphOTest {
+
+ @Test
+ public void testStringEntryNormal() {
+ if (SphO.entry("resourceName")) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(), "resourceName"));
+ } finally {
+ SphO.exit();
+ }
+ }
+ }
+
+ @Test
+ public void testMethodEntryNormal() throws NoSuchMethodException, SecurityException {
+ Method method = SphOTest.class.getMethod("testMethodEntryNormal");
+ if (SphO.entry(method)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(),
+ "com.alibaba.csp.sentinel.SphOTest:testMethodEntryNormal()"));
+ } finally {
+ SphO.exit();
+ }
+ }
+ }
+
+ @Test
+ public void testStringEntryCount() {
+ if (SphO.entry("resourceName", 2)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(), "resourceName"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.OUT);
+ } finally {
+ SphO.exit(2);
+ }
+ }
+ }
+
+ @Test
+ public void testMethodEntryCount() throws NoSuchMethodException, SecurityException {
+ Method method = SphOTest.class.getMethod("testMethodEntryCount");
+ if (SphO.entry(method, 2)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(),
+ "com.alibaba.csp.sentinel.SphOTest:testMethodEntryCount()"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.OUT);
+ } finally {
+ SphO.exit(2);
+ }
+ }
+ }
+
+ @Test
+ public void testStringEntryType() {
+ if (SphO.entry("resourceName", EntryType.IN)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(), "resourceName"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit();
+ }
+ }
+ }
+
+ @Test
+ public void testMethodEntryType() throws NoSuchMethodException, SecurityException {
+ Method method = SphOTest.class.getMethod("testMethodEntryType");
+ if (SphO.entry(method, EntryType.IN)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(),
+ "com.alibaba.csp.sentinel.SphOTest:testMethodEntryType()"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit();
+ }
+ }
+ }
+
+ @Test
+ public void testStringEntryTypeCount() {
+ if (SphO.entry("resourceName", EntryType.IN, 2)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(), "resourceName"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit(2);
+ }
+ }
+ }
+
+ @Test
+ public void testMethodEntryTypeCount() throws NoSuchMethodException, SecurityException {
+ Method method = SphOTest.class.getMethod("testMethodEntryTypeCount");
+ if (SphO.entry(method, EntryType.IN, 2)) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(),
+ "com.alibaba.csp.sentinel.SphOTest:testMethodEntryTypeCount()"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit(2);
+ }
+ }
+ }
+
+ @Test
+ public void testStringEntryAll() {
+ if (SphO.entry("resourceName", EntryType.IN, 2, "hello1", "hello2")) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(), "resourceName"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit(2, "hello1", "hello2");
+ }
+ }
+ }
+
+ @Test
+ public void testMethodEntryAll() throws NoSuchMethodException, SecurityException {
+ Method method = SphOTest.class.getMethod("testMethodEntryAll");
+ if (SphO.entry(method, EntryType.IN, 2, "hello1", "hello2")) {
+ try {
+ assertTrue(StringUtil.equalsIgnoreCase(
+ ContextUtil.getContext().getCurEntry().getResourceWrapper().getName(),
+ "com.alibaba.csp.sentinel.SphOTest:testMethodEntryAll()"));
+ assertSame(ContextUtil.getContext().getCurEntry().getResourceWrapper().getType(), EntryType.IN);
+ } finally {
+ SphO.exit(2, "hello1", "hello2");
+ }
+ }
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphUTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphUTest.java
new file mode 100755
index 00000000..a1a58a1a
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/SphUTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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 static org.junit.Assert.*;
+
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * Test cases for {@link SphU}.
+ *
+ * @author jialiang.linjl
+ */
+public class SphUTest {
+
+ @Test
+ public void testStringEntryNormal() throws BlockException {
+ Entry e = SphU.entry("resourceName");
+
+ assertNotNull(e);
+ assertEquals(e.resourceWrapper.getName(), "resourceName");
+ assertEquals(e.resourceWrapper.getType(), EntryType.OUT);
+ assertEquals(ContextUtil.getContext().getName(), Constants.CONTEXT_DEFAULT_NAME);
+
+ e.exit();
+ }
+
+ @Test
+ public void testMethodEntryNormal() throws BlockException, NoSuchMethodException, SecurityException {
+ Method method = SphUTest.class.getMethod("testMethodEntryNormal");
+ Entry e = SphU.entry(method);
+
+ assertNotNull(e);
+ assertTrue(StringUtil
+ .equalsIgnoreCase(e.resourceWrapper.getName(),
+ "com.alibaba.csp.sentinel.SphUTest:testMethodEntryNormal()"));
+ assertEquals(e.resourceWrapper.getType(), EntryType.OUT);
+ assertEquals(ContextUtil.getContext().getName(), Constants.CONTEXT_DEFAULT_NAME);
+
+ e.exit();
+ }
+
+ @Test(expected = ErrorEntryFreeException.class)
+ public void testStringEntryNotPairedException() throws BlockException {
+ Entry e = SphU.entry("resourceName");
+ Entry e1 = SphU.entry("resourceName");
+
+ if (e != null) {
+ e.exit();
+ }
+ if (e1 != null) {
+ e1.exit();
+ }
+ }
+
+ @Test
+ public void testStringEntryCount() throws BlockException {
+ Entry e = SphU.entry("resourceName", 2);
+
+ assertNotNull(e);
+ assertEquals("resourceName", e.resourceWrapper.getName());
+ assertEquals(e.resourceWrapper.getType(), EntryType.OUT);
+ assertEquals(ContextUtil.getContext().getName(), Constants.CONTEXT_DEFAULT_NAME);
+
+ e.exit(2);
+ }
+
+ @Test
+ public void testMethodEntryCount() throws BlockException, NoSuchMethodException, SecurityException {
+ Method method = SphUTest.class.getMethod("testMethodEntryNormal");
+ Entry e = SphU.entry(method, 2);
+
+ assertNotNull(e);
+ assertTrue(StringUtil
+ .equalsIgnoreCase(e.resourceWrapper.getName(),
+ "com.alibaba.csp.sentinel.SphUTest:testMethodEntryNormal()"));
+ assertEquals(e.resourceWrapper.getType(), EntryType.OUT);
+
+ e.exit(2);
+ }
+
+ @Test
+ public void testStringEntryType() throws BlockException {
+ Entry e = SphU.entry("resourceName", EntryType.IN);
+
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit();
+ }
+
+ @Test
+ public void testMethodEntryType() throws BlockException, NoSuchMethodException, SecurityException {
+ Method method = SphUTest.class.getMethod("testMethodEntryNormal");
+ Entry e = SphU.entry(method, EntryType.IN);
+
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit();
+ }
+
+ @Test
+ public void testStringEntryCountType() throws BlockException {
+ Entry e = SphU.entry("resourceName", EntryType.IN, 2);
+
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit(2);
+ }
+
+ @Test
+ public void testMethodEntryCountType() throws BlockException, NoSuchMethodException, SecurityException {
+ Method method = SphUTest.class.getMethod("testMethodEntryNormal");
+ Entry e = SphU.entry(method, EntryType.IN, 2);
+
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit();
+ }
+
+ @Test
+ public void testStringEntryAll() throws BlockException {
+ final String arg0 = "foo";
+ final String arg1 = "baz";
+ Entry e = SphU.entry("resourceName", EntryType.IN, 2, arg0, arg1);
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit(2, arg0, arg1);
+ }
+
+ @Test
+ public void testMethodEntryAll() throws BlockException, NoSuchMethodException, SecurityException {
+ final String arg0 = "foo";
+ final String arg1 = "baz";
+ Method method = SphUTest.class.getMethod("testMethodEntryNormal");
+ Entry e = SphU.entry(method, EntryType.IN, 2, arg0, arg1);
+
+ assertSame(e.resourceWrapper.getType(), EntryType.IN);
+
+ e.exit(2, arg0, arg1);
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/ArrayMetricTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/ArrayMetricTest.java
new file mode 100755
index 00000000..8bfbff4c
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/ArrayMetricTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.base.metric;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.slots.statistic.base.Window;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric;
+import com.alibaba.csp.sentinel.slots.statistic.metric.WindowLeapArray;
+
+import static org.junit.Assert.*;
+
+import static org.mockito.Mockito.*;
+
+/**
+ * Test cases for {@link ArrayMetric}.
+ *
+ * @author Eric Zhao
+ */
+public class ArrayMetricTest {
+
+ private final int windowLengthInMs = 500;
+ private final int intervalInSec = 1;
+
+ @Test
+ public void testOperateArrayMetric() {
+ WindowLeapArray leapArray = mock(WindowLeapArray.class);
+ final WindowWrap windowWrap = new WindowWrap(windowLengthInMs, 0, new Window());
+ when(leapArray.currentWindow()).thenReturn(windowWrap);
+ when(leapArray.values()).thenReturn(new ArrayList() {{ add(windowWrap.value()); }});
+
+ ArrayMetric metric = new ArrayMetric(leapArray);
+
+ final int expectedPass = 9;
+ final int expectedBlock = 2;
+ final int expectedSuccess = 9;
+ final int expectedException = 6;
+ final int expectedRt = 21;
+
+ metric.addRT(expectedRt);
+ for (int i = 0; i < expectedPass; i++) {
+ metric.addPass();
+ }
+ for (int i = 0; i < expectedBlock; i++) {
+ metric.addBlock();
+ }
+ for (int i = 0; i < expectedSuccess; i++) {
+ metric.addSuccess();
+ }
+ for (int i = 0; i < expectedException; i++) {
+ metric.addException();
+ }
+
+ assertEquals(expectedPass, metric.pass());
+ assertEquals(expectedBlock, metric.block());
+ assertEquals(expectedSuccess, metric.success());
+ assertEquals(expectedException, metric.exception());
+ assertEquals(expectedRt, metric.rt());
+ }
+}
\ No newline at end of file
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java
new file mode 100755
index 00000000..84390c57
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/base/metric/WindowLeapArrayTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.base.metric;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.slots.statistic.base.Window;
+import com.alibaba.csp.sentinel.slots.statistic.base.WindowWrap;
+import com.alibaba.csp.sentinel.slots.statistic.metric.WindowLeapArray;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Test cases for {@link WindowLeapArray}.
+ *
+ * @author Eric Zhao
+ */
+public class WindowLeapArrayTest {
+
+ private final int windowLengthInMs = 1000;
+ private final int intervalInSec = 2;
+
+ @Test
+ public void testNewWindow() {
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ long time = TimeUtil.currentTimeMillis();
+ WindowWrap window = leapArray.currentWindow(time);
+
+ assertEquals(window.windowLength(), windowLengthInMs);
+ assertEquals(window.windowStart(), time - time % windowLengthInMs);
+ assertNotNull(window.value());
+ assertEquals(0L, window.value().pass());
+ }
+
+ @Test
+ public void testLeapArrayWindowStart() {
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ long firstTime = TimeUtil.currentTimeMillis();
+ long previousWindowStart = firstTime - firstTime % windowLengthInMs;
+
+ WindowWrap window = leapArray.currentWindow(firstTime);
+
+ assertEquals(windowLengthInMs, window.windowLength());
+ assertEquals(previousWindowStart, window.windowStart());
+ }
+
+ @Test
+ public void testWindowAfterOneInterval() {
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ long firstTime = TimeUtil.currentTimeMillis();
+ long previousWindowStart = firstTime - firstTime % windowLengthInMs;
+ WindowWrap window = leapArray.currentWindow(previousWindowStart);
+
+ assertEquals(windowLengthInMs, window.windowLength());
+ assertEquals(previousWindowStart, window.windowStart());
+
+ Window currentWindow = window.value();
+ assertNotNull(currentWindow);
+
+ currentWindow.addPass();
+ currentWindow.addBlock();
+ assertEquals(1L, currentWindow.pass());
+ assertEquals(1L, currentWindow.block());
+
+ long middleTime = previousWindowStart + windowLengthInMs / 2;
+
+ window = leapArray.currentWindow(middleTime);
+ assertEquals(previousWindowStart, window.windowStart());
+
+ Window middleWindow = window.value();
+ middleWindow.addPass();
+ assertSame(currentWindow, middleWindow);
+ assertEquals(2L, middleWindow.pass());
+ assertEquals(1L, middleWindow.block());
+
+ long nextTime = middleTime + windowLengthInMs / 2;
+ window = leapArray.currentWindow(nextTime);
+ assertEquals(windowLengthInMs, window.windowLength());
+ assertEquals(windowLengthInMs, window.windowStart() - previousWindowStart);
+
+ currentWindow = window.value();
+ assertNotNull(currentWindow);
+ assertEquals(0L, currentWindow.pass());
+ assertEquals(0L, currentWindow.block());
+ }
+
+ @Test
+ public void testWindowDeprecatedRefresh() {
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ final int len = intervalInSec * 1000 / windowLengthInMs;
+ long firstTime = TimeUtil.currentTimeMillis();
+ List> firstIterWindowList = new ArrayList>(len);
+ for (int i = 0; i < len; i++) {
+ WindowWrap w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
+ w.value().addPass();
+ firstIterWindowList.add(i, w);
+ }
+
+ for (int i = len; i < len * 2; i++) {
+ WindowWrap w = leapArray.currentWindow(firstTime + windowLengthInMs * i);
+ assertNotSame(w, firstIterWindowList.get(i - len));
+ }
+ }
+
+ @Test
+ public void testMultiThreadUpdateEmptyWindow() throws Exception {
+ final long time = TimeUtil.currentTimeMillis();
+ final int nThreads = 16;
+ final WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ final CountDownLatch latch = new CountDownLatch(nThreads);
+ Runnable task = new Runnable() {
+ @Override
+ public void run() {
+ leapArray.currentWindow(time).value().addPass();
+ latch.countDown();
+ }
+ };
+
+ for (int i = 0; i < nThreads; i++) {
+ new Thread(task).start();
+ }
+
+ latch.await();
+
+ assertEquals(nThreads, leapArray.currentWindow(time).value().pass());
+ }
+
+ @Test
+ public void testGetPreviousWindow() {
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ long time = TimeUtil.currentTimeMillis();
+ WindowWrap previousWindow = leapArray.currentWindow(time);
+ assertNull(leapArray.getPreviousWindow(time));
+
+ long nextTime = time + windowLengthInMs;
+ assertSame(previousWindow, leapArray.getPreviousWindow(nextTime));
+
+ long longTime = time + 11 * windowLengthInMs;
+ assertNull(leapArray.getPreviousWindow(longTime));
+ }
+
+ @Test
+ public void testListWindows() {
+ final int windowLengthInMs = 100;
+ final int intervalInSec = 1;
+
+ WindowLeapArray leapArray = new WindowLeapArray(windowLengthInMs, intervalInSec);
+ long time = TimeUtil.currentTimeMillis();
+
+ Set> windowWraps = new HashSet>();
+
+ windowWraps.add(leapArray.currentWindow(time));
+ windowWraps.add(leapArray.currentWindow(time + windowLengthInMs));
+
+ List> list = leapArray.list();
+ for (WindowWrap wrap : list) {
+ assertTrue(windowWraps.contains(wrap));
+ }
+
+ leapArray.currentWindow(time + windowLengthInMs * 20 + intervalInSec * 1000).value().addPass();
+
+ assertEquals(1, leapArray.list().size());
+ }
+}
\ No newline at end of file
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java
new file mode 100755
index 00000000..968cb5c4
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/degrade/DegradeTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.slots.block.degrade;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
+
+/**
+ * @author jialiang.linjl
+ */
+public class DegradeTest {
+
+ @Test
+ public void testAverageRtDegrade() throws InterruptedException {
+ String key = "test_degrade_average_rt";
+ ClusterNode cn = mock(ClusterNode.class);
+ ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn);
+
+ Context context = mock(Context.class);
+ DefaultNode node = mock(DefaultNode.class);
+ when(node.getClusterNode()).thenReturn(cn);
+ when(cn.avgRt()).thenReturn(2L);
+
+ DegradeRule rule = new DegradeRule();
+ rule.setCount(1);
+ rule.setResource(key);
+ rule.setTimeWindow(5);
+
+ for (int i = 0; i < 4; i++) {
+ assertTrue(rule.passCheck(context, node, 1));
+ }
+
+ // The third time will fail.
+ assertFalse(rule.passCheck(context, node, 1));
+ assertFalse(rule.passCheck(context, node, 1));
+
+ // Restore.
+ TimeUnit.SECONDS.sleep(6);
+ assertTrue(rule.passCheck(context, node, 1));
+ }
+
+ @Test
+ public void testExceptionRatioModeDegrade() throws Throwable {
+ String key = "test_degrade_exception_ratio";
+ ClusterNode cn = mock(ClusterNode.class);
+ when(cn.exceptionQps()).thenReturn(2L);
+ ClusterBuilderSlot.getClusterNodeMap().put(new StringResourceWrapper(key, EntryType.IN), cn);
+
+ Context context = mock(Context.class);
+ DefaultNode node = mock(DefaultNode.class);
+ when(node.getClusterNode()).thenReturn(cn);
+
+ DegradeRule rule = new DegradeRule();
+ rule.setCount(0.5);
+ rule.setResource(key);
+ rule.setTimeWindow(5);
+ rule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION);
+
+ when(cn.successQps()).thenReturn(4L);
+
+ // Will fail.
+ assertFalse(rule.passCheck(context, node, 1));
+
+ // Restore from the degrade timeout.
+ TimeUnit.SECONDS.sleep(6);
+
+ when(cn.successQps()).thenReturn(7L);
+ // Will pass.
+ assertTrue(rule.passCheck(context, node, 1));
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowPartialIntegrationTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowPartialIntegrationTest.java
new file mode 100755
index 00000000..5da8f30b
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowPartialIntegrationTest.java
@@ -0,0 +1,262 @@
+/*
+ * 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.slots.block.flow;
+
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+
+/**
+ * @author jialiang.linjl
+ */
+public class FlowPartialIntegrationTest {
+
+ @Test
+ public void testQPSGrade() {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testQPSGrade");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(1);
+ FlowRuleManager.loadRules(Collections.singletonList(flowRule));
+
+ Entry e = null;
+ try {
+ e = SphU.entry("testQPSGrade");
+ } catch (BlockException e1) {
+ assertTrue(false);
+ }
+ e.exit();
+
+ try {
+ e = SphU.entry("testQPSGrade");
+ assertTrue(false);
+ } catch (BlockException e1) {
+ assertTrue(true);
+ }
+ }
+
+ @Test
+ public void testThreadGrade() throws InterruptedException {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testThreadGrade");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
+ flowRule.setCount(1);
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+
+ final Object sequence = new Object();
+
+ Runnable runnable = new Runnable() {
+ @Override
+ public void run() {
+ Entry e = null;
+ try {
+ e = SphU.entry("testThreadGrade");
+ assertTrue(true);
+ synchronized (sequence) {
+ System.out.println("notify up");
+ sequence.notify();
+ }
+ Thread.sleep(1000);
+ } catch (BlockException e1) {
+ assertTrue(false);
+ } catch (InterruptedException e1) {
+ assertTrue(false);
+ e1.printStackTrace();
+ }
+ e.exit();
+ }
+ };
+
+ Thread thread = new Thread(runnable);
+ thread.start();
+
+ Entry e = null;
+ synchronized (sequence) {
+ System.out.println("sleep");
+ sequence.wait();
+ System.out.println("wake up");
+ }
+ try {
+ e = SphU.entry("testThreadGrade");
+ assertTrue(false);
+ } catch (BlockException e1) {
+ assertTrue(true);
+ }
+ System.out.println("done");
+
+ }
+
+ @Test
+ public void testOriginFlowRule() {
+ // normal
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testOriginFlowRule");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(0);
+ flowRule.setLimitApp("other");
+
+ FlowRule flowRule2 = new FlowRule();
+ flowRule2.setResource("testOriginFlowRule");
+ flowRule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule2.setCount(1);
+ flowRule2.setLimitApp("app2");
+
+ FlowRuleManager.loadRules(Arrays.asList(flowRule, flowRule2));
+
+ ContextUtil.enter("node1", "app1");
+ Entry e = null;
+ try {
+ e = SphU.entry("testOriginFlowRule");
+ assertTrue(false);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(true);
+ }
+ assertTrue(e == null);
+
+ ContextUtil.exit();
+
+ ContextUtil.enter("node1", "app2");
+ e = null;
+ try {
+ e = SphU.entry("testOriginFlowRule");
+ assertTrue(true);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(false);
+ }
+
+ e.exit();
+ ContextUtil.exit();
+ }
+
+ @Test
+ public void testFlowRule_other() {
+
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testOther");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(0);
+ flowRule.setLimitApp("other");
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+
+ Entry e = null;
+ try {
+ e = SphU.entry("testOther");
+ assertTrue(true);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(false);
+ }
+
+ if (e != null) {
+ assertTrue(true);
+ e.exit();
+ } else {
+ assertTrue(false);
+ }
+ }
+
+ @Test
+ public void testStrategy() {
+
+ // normal
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testStrategy");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(0);
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+
+ ContextUtil.enter("testStrategy");
+ Entry e = null;
+ try {
+ e = SphU.entry("testStrategy");
+ assertTrue(false);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(true);
+ }
+
+ ContextUtil.exit();
+
+ flowRule = new FlowRule();
+ flowRule.setResource("testStrategy");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(0);
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ flowRule.setResource("entry2");
+
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+
+ e = null;
+ ContextUtil.enter("entry1");
+ try {
+ e = SphU.entry("testStrategy");
+ assertTrue(true);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(false);
+ }
+ e.exit();
+ ContextUtil.exit();
+ }
+
+ @Test
+ public void testStrategy_chain() {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("entry2");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(0);
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ flowRule.setRefResource("entry1");
+
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+ Entry e = null;
+ ContextUtil.enter("entry1");
+ try {
+ e = SphU.entry("entry2");
+ assertTrue(false);
+ } catch (BlockException e1) {
+ e1.printStackTrace();
+ assertTrue(true);
+ }
+
+ ContextUtil.exit();
+
+ e = null;
+ ContextUtil.enter("entry3");
+ try {
+ e = SphU.entry("entry2");
+ assertTrue(true);
+ } catch (BlockException e1) {
+ assertTrue(false);
+ }
+ e.exit();
+
+ ContextUtil.exit();
+
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleTest.java
new file mode 100755
index 00000000..a2439c51
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleTest.java
@@ -0,0 +1,177 @@
+/*
+ * 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.slots.block.flow;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
+
+/**
+ * @author jialiang.linjl
+ */
+public class FlowRuleTest {
+
+ @Test
+ public void testFlowRule_grade() {
+
+ FlowRule flowRule = new FlowRule();
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(1);
+ flowRule.setLimitApp("default");
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+
+ DefaultController defaultController = new DefaultController(1, flowRule.getGrade());
+ flowRule.setRater(defaultController);
+
+ Context context = mock(Context.class);
+ DefaultNode node = mock(DefaultNode.class);
+ ClusterNode cn = mock(ClusterNode.class);
+
+ when(context.getOrigin()).thenReturn("");
+ when(node.getClusterNode()).thenReturn(cn);
+ when(cn.passQps()).thenReturn(1l);
+
+ assertTrue(flowRule.passCheck(context, node, 1, new Object[0]) == false);
+
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_THREAD);
+ defaultController = new DefaultController(1, flowRule.getGrade());
+ flowRule.setRater(defaultController);
+ when(cn.curThreadNum()).thenReturn(1);
+ assertTrue(flowRule.passCheck(context, node, 1, new Object[0]) == false);
+ }
+
+ @Test
+ public void testFlowRule_strategy() {
+
+ FlowRule flowRule = new FlowRule();
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(1);
+ flowRule.setLimitApp("default");
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ DefaultController defaultController = new DefaultController(1, flowRule.getGrade());
+ flowRule.setRater(defaultController);
+ flowRule.setRefResource("entry1");
+
+ Context context = mock(Context.class);
+ DefaultNode dn = mock(DefaultNode.class);
+
+ when(context.getName()).thenReturn("entry1");
+ when(dn.passQps()).thenReturn(1l);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+
+ when(context.getName()).thenReturn("entry2");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ // Strategy == relate
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ ClusterNode cn = mock(ClusterNode.class);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == true);
+
+ }
+
+ @Test
+ public void testOrigin() {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(1);
+ flowRule.setLimitApp("default");
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ DefaultController defaultController = new DefaultController(1, flowRule.getGrade());
+ flowRule.setRater(defaultController);
+ flowRule.setRefResource("entry1");
+
+ Context context = mock(Context.class);
+ DefaultNode dn = mock(DefaultNode.class);
+ when(context.getOrigin()).thenReturn("origin1");
+ when(dn.passQps()).thenReturn(1l);
+ when(context.getOriginNode()).thenReturn(dn);
+
+ /*
+ * first scenario, limit app as default
+ *
+ */
+ ClusterNode cn = mock(ClusterNode.class);
+ when(dn.getClusterNode()).thenReturn(cn);
+ when(cn.passQps()).thenReturn(1l);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+ when(cn.passQps()).thenReturn(0l);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ flowRule.setResource("entry1");
+ when(context.getName()).thenReturn("entry1");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+ when(context.getName()).thenReturn("entry2");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ // relate node
+ flowRule.setStrategy(RuleConstant.STRATEGY_RELATE);
+ flowRule.setResource("worong");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ /*
+ * second scenario test a context with the same origin1
+ *
+ */
+ flowRule.setLimitApp("origin1");
+ when(context.getName()).thenReturn("entry1");
+ // direct node
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+
+ // chain node
+ flowRule.setResource("entry1");
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ when(context.getName()).thenReturn("entry1");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+ when(context.getName()).thenReturn("entry2");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ // relate node
+ flowRule.setStrategy(RuleConstant.STRATEGY_RELATE);
+ flowRule.setResource("not exits");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ when(context.getOrigin()).thenReturn("origin2");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+
+ /*
+ * limit app= other
+ */
+ flowRule.setLimitApp("other");
+ flowRule.setResource("hello world");
+
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+
+ flowRule.setStrategy(RuleConstant.STRATEGY_CHAIN);
+ flowRule.setResource("entry1");
+ when(context.getName()).thenReturn("entry1");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]) == false);
+
+ when(context.getName()).thenReturn("entry2");
+ assertTrue(flowRule.passCheck(context, dn, 1, new Object[0]));
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/PaceControllerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/PaceControllerTest.java
new file mode 100755
index 00000000..0fb4aaf0
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/PaceControllerTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.slots.block.flow;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.util.TimeUtil;
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.PaceController;
+
+/**
+ * @author jialiang.linjl
+ */
+public class PaceControllerTest {
+
+ @Test
+ public void testPaceController_normal() throws InterruptedException {
+ PaceController paceController = new PaceController(500, 10d);
+ Node node = mock(Node.class);
+
+ long start = TimeUtil.currentTimeMillis();
+ for (int i = 0; i < 6; i++) {
+ assertTrue(paceController.canPass(node, 1));
+ }
+ long end = TimeUtil.currentTimeMillis();
+ assertTrue((end - start) > 400);
+ }
+
+ @Test
+ public void testPaceController_timeout() throws InterruptedException {
+ final PaceController paceController = new PaceController(500, 10d);
+ final Node node = mock(Node.class);
+
+ final AtomicInteger passcount = new AtomicInteger();
+ final AtomicInteger blockcount = new AtomicInteger();
+ final CountDownLatch countDown = new CountDownLatch(1);
+
+ final AtomicInteger done = new AtomicInteger();
+ for (int i = 0; i < 10; i++) {
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ boolean pass = paceController.canPass(node, 1);
+
+ if (pass == true) {
+ passcount.incrementAndGet();
+ } else {
+ blockcount.incrementAndGet();
+ }
+ done.incrementAndGet();
+
+ if (done.get() >= 10) {
+ countDown.countDown();
+ }
+ }
+
+ }, "Thread " + i);
+ thread.start();
+ }
+
+ countDown.await();
+ System.out.println("pass:" + passcount.get());
+ System.out.println("block" + blockcount.get());
+ System.out.println("done" + done.get());
+ assertTrue(blockcount.get() > 0);
+
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpControllerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpControllerTest.java
new file mode 100755
index 00000000..340a31f9
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpControllerTest.java
@@ -0,0 +1,61 @@
+/*
+ * 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.slots.block.flow;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.node.Node;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
+
+/**
+ * @author jialiang.linjl
+ */
+public class WarmUpControllerTest {
+
+ @Test
+ public void testWarmUp() throws InterruptedException {
+ WarmUpController warmupController = new WarmUpController(10, 10, 3);
+
+ Node node = mock(Node.class);
+
+ when(node.passQps()).thenReturn(8L);
+ when(node.previousPassQps()).thenReturn(1L);
+
+ assertFalse(warmupController.canPass(node, 1));
+
+ when(node.passQps()).thenReturn(1L);
+ when(node.previousPassQps()).thenReturn(1L);
+
+ assertTrue(warmupController.canPass(node, 1));
+
+ when(node.previousPassQps()).thenReturn(10L);
+
+ for (int i = 0; i < 100; i++) {
+ Thread.sleep(1000);
+ warmupController.canPass(node, 1);
+ }
+ when(node.passQps()).thenReturn(8L);
+ assertTrue(warmupController.canPass(node, 1));
+
+ when(node.passQps()).thenReturn(10L);
+ assertFalse(warmupController.canPass(node, 1));
+ }
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpFlowTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpFlowTest.java
new file mode 100755
index 00000000..6cda853d
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/WarmUpFlowTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.slots.block.flow;
+
+import java.util.Arrays;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+
+/**
+ * @author jialiang.linjl
+ */
+public class WarmUpFlowTest {
+
+ @Test
+ public void testWarmupFlowControl() {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setResource("testWarmupFlowControl");
+ flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);
+ flowRule.setCount(10);
+ flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);
+ flowRule.setWarmUpPeriodSec(10);
+ flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
+
+ FlowRuleManager.loadRules(Arrays.asList(flowRule));
+
+ //ContextUtil.enter(null);
+
+ //when(flowRule.selectNodeByRequsterAndStrategy(null, null, null)).thenReturn(value)
+
+ // flowRule.passCheck(null, DefaultNode, acquireCount, args)
+ // when(leapArray.values()).thenReturn(new ArrayList() {{ add(windowWrap.value()); }});
+ ContextUtil.enter("test");
+
+ ContextUtil.exit();
+
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemGuardIntegrationTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemGuardIntegrationTest.java
new file mode 100755
index 00000000..16c47f8d
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemGuardIntegrationTest.java
@@ -0,0 +1,23 @@
+/*
+ * 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.slots.block.system;
+
+/**
+ * @author jialiang.linjl
+ */
+public class SystemGuardIntegrationTest {
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemRuleTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemRuleTest.java
new file mode 100755
index 00000000..f2c0a809
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/system/SystemRuleTest.java
@@ -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.slots.block.system;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.node.ClusterNode;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
+
+/**
+ * @author jialiang.linjl
+ */
+public class SystemRuleTest {
+
+ @Test
+ public void testSystemRule_load() {
+ SystemRule systemRule = new SystemRule();
+
+ systemRule.setAvgRt(4000L);
+
+ SystemRuleManager.loadRules(Collections.singletonList(systemRule));
+ }
+
+ @Test
+ public void testSystemRule_avgRt() throws BlockException {
+
+ SystemRule systemRule = new SystemRule();
+
+ systemRule.setAvgRt(4L);
+
+ Context context = mock(Context.class);
+ DefaultNode node = mock(DefaultNode.class);
+ ClusterNode cn = mock(ClusterNode.class);
+
+ when(context.getOrigin()).thenReturn("");
+ when(node.getClusterNode()).thenReturn(cn);
+
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterNodeBuilder.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterNodeBuilder.java
new file mode 100755
index 00000000..baa9530a
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/clusterbuilder/ClusterNodeBuilder.java
@@ -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.slots.clusterbuilder;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.Node;
+
+/**
+ * @author jialiang.linjl
+ */
+public class ClusterNodeBuilder {
+
+ @Test
+ public void clusterNodeBuilder_normal() throws Exception {
+ ContextUtil.enter("entry1", "caller1");
+
+ Entry nodeA = SphU.entry("nodeA");
+
+ Node curNode = nodeA.getCurNode();
+ assertTrue(curNode.getClass() == DefaultNode.class);
+ DefaultNode dN = (DefaultNode)curNode;
+ assertTrue(dN.getClusterNode().getOriginCountMap().containsKey("caller1"));
+ assertTrue(nodeA.getOriginNode() == dN.getClusterNode().getOriginNode("caller1"));
+
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+ ContextUtil.exit();
+
+ ContextUtil.enter("entry4", "caller2");
+
+ nodeA = SphU.entry("nodeA");
+
+ curNode = nodeA.getCurNode();
+ assertTrue(curNode.getClass() == DefaultNode.class);
+ DefaultNode dN1 = (DefaultNode)curNode;
+ assertTrue(dN1.getClusterNode().getOriginCountMap().containsKey("caller2"));
+ assertTrue(dN1 != dN);
+
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+ ContextUtil.exit();
+ }
+
+}
diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorTest.java
new file mode 100755
index 00000000..4eddcf0f
--- /dev/null
+++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorTest.java
@@ -0,0 +1,161 @@
+/*
+ * 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.slots.nodeselector;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+
+import com.alibaba.csp.sentinel.Constants;
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.context.ContextUtil;
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.node.EntranceNode;
+import com.alibaba.csp.sentinel.node.Node;
+
+/**
+ * @author jialiang.linjl
+ * @author Eric Zhao
+ */
+public class NodeSelectorTest {
+
+ @Test
+ public void testSingleEntrance() throws Exception {
+ final String contextName = "entry_SingleEntrance";
+ ContextUtil.enter(contextName);
+
+ EntranceNode entranceNode = null;
+ for (Node node : Constants.ROOT.getChildList()) {
+ entranceNode = (EntranceNode)node;
+ if (entranceNode.getId().getName().equals(contextName)) {
+ break;
+ } else {
+ System.out.println("Single entry: " + entranceNode.getId().getName());
+ }
+ }
+ assertNotNull(entranceNode);
+ assertTrue(entranceNode.getId().getName().equalsIgnoreCase(contextName));
+ final String resName = "nodeA";
+ Entry nodeA = SphU.entry(resName);
+
+ assertNotNull(ContextUtil.getContext().getCurNode());
+ assertEquals(resName, ((DefaultNode)ContextUtil.getContext().getCurNode()).getId().getName());
+ boolean hasNode = false;
+ for (Node node : entranceNode.getChildList()) {
+ if (((DefaultNode)node).getId().getName().equals(resName)) {
+ hasNode = true;
+ }
+ }
+ assertTrue(hasNode);
+
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+ ContextUtil.exit();
+ }
+
+ @Test
+ public void testMultipleEntrance() throws Exception {
+ final String firstEntry = "entry_multiple_one";
+ final String anotherEntry = "entry_multiple_another";
+ final String resName = "nodeA";
+
+ Node firstNode, anotherNode;
+ ContextUtil.enter(firstEntry);
+ Entry nodeA = SphU.entry(resName);
+ firstNode = ContextUtil.getContext().getCurNode();
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+ ContextUtil.exit();
+
+ ContextUtil.enter(anotherEntry);
+ nodeA = SphU.entry(resName);
+ anotherNode = ContextUtil.getContext().getCurNode();
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+
+ assertNotSame(firstNode, anotherNode);
+
+ for (Node node : Constants.ROOT.getChildList()) {
+ EntranceNode firstEntrance = (EntranceNode)node;
+ if (firstEntrance.getId().getName().equals(firstEntry)) {
+ assertEquals(1, firstEntrance.getChildList().size());
+ for (Node child : firstEntrance.getChildList()) {
+ assertEquals(resName, ((DefaultNode)child).getId().getName());
+ }
+ } else if (firstEntrance.getId().getName().equals(anotherEntry)) {
+ assertEquals(1, firstEntrance.getChildList().size());
+ for (Node child : firstEntrance.getChildList()) {
+ assertEquals(resName, ((DefaultNode)child).getId().getName());
+ }
+ } else {
+ System.out.println("Multiple entries: " + firstEntrance.getId().getName());
+ }
+ }
+ ContextUtil.exit();
+ }
+
+ //@Test
+ public void testMultipleLayer() throws Exception {
+ // TODO: fix this
+ ContextUtil.enter("entry1", "appA");
+
+ Entry nodeA = SphU.entry("nodeA");
+ assertSame(ContextUtil.getContext().getCurEntry(), nodeA);
+
+ DefaultNode dnA = (DefaultNode)nodeA.getCurNode();
+ assertNotNull(dnA);
+ assertSame("nodeA", dnA.getId().getName());
+
+ Entry nodeB = SphU.entry("nodeB");
+ assertSame(ContextUtil.getContext().getCurEntry(), nodeB);
+ DefaultNode dnB = (DefaultNode)nodeB.getCurNode();
+ assertNotNull(dnB);
+ assertTrue(dnA.getChildList().contains(dnB));
+
+ Entry nodeC = SphU.entry("nodeC");
+ assertSame(ContextUtil.getContext().getCurEntry(), nodeC);
+ DefaultNode dnC = (DefaultNode)nodeC.getCurNode();
+ assertNotNull(dnC);
+ assertTrue(dnB.getChildList().contains(dnC));
+
+ if (nodeC != null) {
+ nodeC.exit();
+ }
+ assertSame(ContextUtil.getContext().getCurEntry(), nodeB);
+
+ if (nodeB != null) {
+ nodeB.exit();
+ }
+ assertSame(ContextUtil.getContext().getCurEntry(), nodeA);
+
+ if (nodeA != null) {
+ nodeA.exit();
+ }
+ assertNull(ContextUtil.getContext().getCurEntry());
+ ContextUtil.exit();
+
+ }
+
+}
diff --git a/sentinel-dashboard/README.md b/sentinel-dashboard/README.md
new file mode 100755
index 00000000..e9079384
--- /dev/null
+++ b/sentinel-dashboard/README.md
@@ -0,0 +1,48 @@
+# Sentinel控制台
+
+## 0. 概述
+
+Sentinel控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在Sentinel控制台上,我们可以配置规则并实时查看流量控制效果。
+
+## 1. 编译和启动
+
+### 1.1 如何编译
+
+使用如下命令将代码打包成一个fat jar:
+
+```bash
+$ mvn clean package
+```
+
+### 1.2 如何启动
+
+使用如下命令启动编译后的控制台:
+
+```bash
+$ java -Dserver.port=8080 \
+-Dcsp.sentinel.dashboard.server=localhost:8080 \
+-Dproject.name=sentinel-dashboard \
+-jar target/sentinel-dashboard.jar
+```
+
+上述命令中我们指定几个JVM参数,其中`-Dserver.port=8080`用于指定spring boot启动端口为`8080`,其余几个是Sentinel客户端的参数。为便于演示,我们对控制台本身加入了流量控制功能,具体做法是引入`CommonFilter`这个Sentinel拦截器,上述JVM参数的含义是:
+
+| 参数 | 作用 |
+|--------|--------|
+|`Dcsp.sentinel.dashboard.server=localhost:8080`|向Sentinel客户端指定控制台的地址。|
+|`-Dproject.name=sentinel-dashboard`|向Sentinel指定本程序名称。|
+
+全部配置项参考[启动配置项](https://github.com/alibaba/Sentinel/wiki/%E5%90%AF%E5%8A%A8%E9%85%8D%E7%BD%AE%E9%A1%B9)
+
+经过上述配置,控制台启动后会自动向自己发送心跳。程序启动后浏览器访问`localhost:8080`即可访问Sentinel控制台。
+
+## 2. 客户端接入
+
+选择合适的方式接入Sentinel,然后在应用启动时加入JVM参数`-Dcsp.sentinel.dashboard.server=consoleIp:port`指定控制台地址和端口,Sentinel客户端会自动向控制台发送心跳包,将客户端纳入到控制台的管辖之下。
+
+## 3. 验证是否接入成功
+
+客户端正确配置并启动后,会主动向控制台发送心跳包,汇报自己的存在;控制台收到客户端心跳包之后,会在左侧导航栏中显示该客户端信息。控制台能够看到客户端的机器信息,则表明客户端接入成功了。
+
+
+更多:[控制台功能介绍](./Sentinel_Dashboard_Feature.md)。
\ No newline at end of file
diff --git a/sentinel-dashboard/Sentinel_Dashboard_Feature.md b/sentinel-dashboard/Sentinel_Dashboard_Feature.md
new file mode 100755
index 00000000..10e5744b
--- /dev/null
+++ b/sentinel-dashboard/Sentinel_Dashboard_Feature.md
@@ -0,0 +1,47 @@
+# Sentinel控制台功能介绍
+
+## 0. 概述
+
+Sentinel控制台是流量控制、熔断降级规则统一配置和管理的入口,它为用户提供了机器自发现、簇点链路自发现、监控、规则配置等功能。在Sentinel控制台上,我们可以配置规则并实时查看流量控制效果。使用Sentinel控制台的流程如下:
+
+```
+客户端接入 -> 机器自发现 -> 查看簇点链路 -> 配置流控规则 -> 查看流控效果
+```
+
+## 1. 功能介绍
+
+### 1.1 机器自发现
+
+Sentinel提供内置的机器自发现功能,无需依赖第三方服务发现组件即可实现客户端的发现。点击Sentinel控制台左侧导航栏的“机器列表”菜单,可查看集群机器数量和机器健康状况。
+
+### 1.2 簇点链路自发现
+
+Sentinel将每一个需要流控的URL或者接口称为一个资源,并使用URL地址或方法签名表示资源。Sentinel会自动发现所有潜在的资源和资源之间的调用链,并在“簇点链路”页面展示。资源是设置流控规则的载体,通过簇点链路自发现,可以方便的对重要资源设置流控规则、熔断降级规则等。
+
+> 注意:客户端有访问量之后,才能在簇点链路页面看到资源监控。
+
+### 1.3 实时监控
+
+Sentinel监控功能能够实时查看集群中每个资源的实时访问以及流控情况。控制台左侧导航栏的“实时监控”菜单对应该功能。
+
+### 1.4 流控降级规则设置
+
+Sentinel提供了多种规则来保护系统的不同部分。流量控制规则用于保护服务提供方,熔断降级规则用于保护服务消费方,系统保护规则用于保护整个系统。
+
+#### 1.4.1 流量控制规则
+
+流量控制规则用于保护服务提供方,服务提供方指任何可以对外提供服务的系统,比如向终端用户提供Web服务的Web应用,向微服务消费方提供服务的Dubbo Service Provider等。系统的服务能力是有限的,如果消费方请求速度过高,则采用相应的保护策略,或是直接拒绝,或是排队等待。通过“流控规则”页面可以查看和配置流量控制规则。
+
+#### 1.4.2 熔断降级规则
+
+降级规则用于保护服务消费方。在微服务架构中,业务系统通常要依赖多个服务,依赖的某个服务不可用将会影响业务系统的可用性。解决这个问题的方法是及时发现服务的“不可用”状态,在调用时快速失败而不是等待调动超时或者重试。Sentinel通过熔断降级来达到快速失败的目的。通过“降级规则”页面可以查看和配置降级规则。
+
+#### 1.4.3 系统保护规则
+
+系统保护规则(简称`系统规则`)用于保护整个系统指标处于安全水位。无论是服务提供方还是消费方,活跃线程数过多、系统LOAD等过高都是系统处于高水位的指标。当系统水位过高时,系统应拒绝对外提供服务以便迅速降低资源占用。通过“系统规则”页面可以查看和设置系统保护规则。
+
+## 2. 限制
+
+本控制台只是用于演示Sentinel的基本能力和工作流程,并没有依赖生产环境中所必需的组件,比如**持久化的后端数据库、可靠的配置中心**等。目前Sentinel采用内存态的方式存储监控和规则数据,监控最长存储时间为5分钟,控制台重启后数据丢失。
+
+更多:[Sentinel控制台启动和客户端接入](./README.md)。
\ No newline at end of file
diff --git a/sentinel-dashboard/pom.xml b/sentinel-dashboard/pom.xml
new file mode 100755
index 00000000..bacd451c
--- /dev/null
+++ b/sentinel-dashboard/pom.xml
@@ -0,0 +1,160 @@
+
+
+ 4.0.0
+
+
+ com.alibaba.csp
+ sentinel-parent
+ 0.1.0
+
+
+ sentinel-dashboard
+ 0.1.0
+ jar
+
+
+ 1.8
+ 1.8
+
+
+
+
+ com.alibaba.csp
+ sentinel-core
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-web-servlet
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-transport-simple-http
+ ${project.version}
+
+
+ com.alibaba.csp
+ sentinel-transport-common
+ ${project.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+ 1.5.9.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+ 1.5.9.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+ 1.5.9.RELEASE
+
+
+ org.springframework.boot
+ spring-boot-starter-tomcat
+ 1.5.9.RELEASE
+
+
+
+ org.springframework.boot
+ spring-boot-devtools
+ 1.5.9.RELEASE
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ 1.5.9.RELEASE
+ test
+
+
+
+ log4j
+ log4j
+ 1.2.14
+
+
+
+ commons-lang
+ commons-lang
+ 2.6
+
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.3
+
+
+ org.apache.httpcomponents
+ httpcore
+ 4.4.5
+
+
+ org.apache.httpcomponents
+ httpasyncclient
+ 4.1.3
+
+
+ org.apache.httpcomponents
+ httpcore-nio
+ 4.4.6
+
+
+ com.alibaba
+ fastjson
+ 1.2.47
+
+
+
+
+
+ sentinel-dashboard
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+ true
+ com.taobao.csp.sentinel.dashboard.Application
+
+
+
+
+ repackage
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+
+
+
+
+ src/main/resources
+
+
+
+ src/main/webapp/
+
+ resources/node_modules/**
+
+
+
+
+
+
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/Application.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/Application.java
new file mode 100755
index 00000000..80d2f3c0
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/Application.java
@@ -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.taobao.csp.sentinel.dashboard;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.support.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class Application extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+ return application.sources(Application.class);
+ }
+
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java
new file mode 100755
index 00000000..ba9b095d
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/config/WebConfig.java
@@ -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.taobao.csp.sentinel.dashboard.config;
+
+import com.alibaba.csp.sentinel.adapter.servlet.CommonFilter;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+/**
+ * @author leyou
+ */
+@Configuration
+public class WebConfig extends WebMvcConfigurerAdapter {
+ private static Logger logger = LoggerFactory.getLogger(WebConfig.class);
+
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/**").addResourceLocations("classpath:/resources/");
+ }
+
+ @Override
+ public void addViewControllers(ViewControllerRegistry registry) {
+ registry.addViewController("/").setViewName("forward:/index.htm");
+ }
+
+ /**
+ * Add {@link CommonFilter} to the server, this is the simplest way to use Sentinel
+ * for Web application.
+ *
+ * @return
+ */
+ @Bean
+ public FilterRegistrationBean sentinelFilterRegistration() {
+ logger.info("sentinelFilterRegistration(), add CommonFilter");
+ FilterRegistrationBean registration = new FilterRegistrationBean();
+ registration.setFilter(new CommonFilter());
+ registration.addUrlPatterns("/*");
+ registration.addInitParameter("paramName", "paramValue");
+ registration.setName("sentinelFilter");
+ registration.setOrder(1);
+
+ return registration;
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java
new file mode 100755
index 00000000..19dc4a3f
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/ApplicationEntity.java
@@ -0,0 +1,99 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+import com.taobao.csp.sentinel.dashboard.discovery.AppInfo;
+
+/**
+ * @author leyou
+ */
+public class ApplicationEntity {
+ private Long id;
+ private Date gmtCreate;
+ private Date gmtModified;
+ private String app;
+ private String activeConsole;
+ private Date lastFetch;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getActiveConsole() {
+ return activeConsole;
+ }
+
+ public Date getLastFetch() {
+ return lastFetch;
+ }
+
+ public void setLastFetch(Date lastFetch) {
+ this.lastFetch = lastFetch;
+ }
+
+ public void setActiveConsole(String activeConsole) {
+ this.activeConsole = activeConsole;
+ }
+
+ public AppInfo toAppInfo() {
+ AppInfo appInfo = new AppInfo();
+ appInfo.setApp(app);
+
+ return appInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "ApplicationEntity{" +
+ "id=" + id +
+ ", gmtCreate=" + gmtCreate +
+ ", gmtModified=" + gmtModified +
+ ", app='" + app + '\'' +
+ ", activeConsole='" + activeConsole + '\'' +
+ ", lastFetch=" + lastFetch +
+ '}';
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/DegradeRuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/DegradeRuleEntity.java
new file mode 100755
index 00000000..bc56742c
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/DegradeRuleEntity.java
@@ -0,0 +1,157 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+
+/**
+ * @author leyou
+ */
+public class DegradeRuleEntity implements RuleEntity {
+ private Long id;
+ private String app;
+ private String ip;
+ private Integer port;
+ private String resource;
+ private String limitApp;
+ private Double count;
+ private Integer timeWindow;
+ /**
+ * 0 rt 限流; 1为异常;
+ */
+ private Integer grade;
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public static DegradeRuleEntity fromDegradeRule(String app, String ip, Integer port, DegradeRule rule) {
+ DegradeRuleEntity entity = new DegradeRuleEntity();
+ entity.setApp(app);
+ entity.setIp(ip);
+ entity.setPort(port);
+ entity.setResource(rule.getResource());
+ entity.setLimitApp(rule.getLimitApp());
+ entity.setCount(rule.getCount());
+ entity.setTimeWindow(rule.getTimeWindow());
+ entity.setGrade(rule.getGrade());
+ return entity;
+ }
+
+ @Override
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ @Override
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Override
+ public Long getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public String getLimitApp() {
+ return limitApp;
+ }
+
+ public void setLimitApp(String limitApp) {
+ this.limitApp = limitApp;
+ }
+
+ public Double getCount() {
+ return count;
+ }
+
+ public void setCount(Double count) {
+ this.count = count;
+ }
+
+ public Integer getTimeWindow() {
+ return timeWindow;
+ }
+
+ public void setTimeWindow(Integer timeWindow) {
+ this.timeWindow = timeWindow;
+ }
+
+ public Integer getGrade() {
+ return grade;
+ }
+
+ public void setGrade(Integer grade) {
+ this.grade = grade;
+ }
+
+ @Override
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public DegradeRule toDegradeRule() {
+ DegradeRule rule = new DegradeRule();
+ rule.setResource(resource);
+ rule.setLimitApp(limitApp);
+ rule.setCount(count);
+ rule.setTimeWindow(timeWindow);
+ rule.setGrade(grade);
+ return rule;
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/FlowRuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/FlowRuleEntity.java
new file mode 100755
index 00000000..aa5a1238
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/FlowRuleEntity.java
@@ -0,0 +1,218 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+
+/**
+ * @author leyou
+ */
+public class FlowRuleEntity implements RuleEntity {
+
+ private Long id;
+ private String app;
+ private String ip;
+ private Integer port;
+ private String limitApp;
+ private String resource;
+ /**
+ * 0为线程数;1为qps
+ */
+ private Integer grade;
+ private Double count;
+ /**
+ * 0为直接限流;1为关联限流;2为链路限流
+ ***/
+ private Integer strategy;
+ private String refResource;
+ /**
+ * 0. default, 1. warm up, 2. rate limiter
+ */
+ private Integer controlBehavior;
+ private Integer warmUpPeriodSec;
+ /**
+ * max queueing time in rate limiter behavior
+ */
+ private Integer maxQueueingTimeMs;
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public static FlowRuleEntity fromFlowRule(String app, String ip, Integer port, FlowRule rule) {
+ FlowRuleEntity entity = new FlowRuleEntity();
+ entity.setApp(app);
+ entity.setIp(ip);
+ entity.setPort(port);
+ entity.setLimitApp(rule.getLimitApp());
+ entity.setResource(rule.getResource());
+ entity.setGrade(rule.getGrade());
+ entity.setCount(rule.getCount());
+ entity.setStrategy(rule.getStrategy());
+ entity.setRefResource(rule.getRefResource());
+ entity.setControlBehavior(rule.getControlBehavior());
+ entity.setWarmUpPeriodSec(rule.getWarmUpPeriodSec());
+ entity.setMaxQueueingTimeMs(rule.getMaxQueueingTimeMs());
+ return entity;
+ }
+
+ @Override
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ @Override
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Override
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ @Override
+ public Long getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public String getLimitApp() {
+ return limitApp;
+ }
+
+ public void setLimitApp(String limitApp) {
+ this.limitApp = limitApp;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public Integer getGrade() {
+ return grade;
+ }
+
+ public void setGrade(Integer grade) {
+ this.grade = grade;
+ }
+
+ public Double getCount() {
+ return count;
+ }
+
+ public void setCount(Double count) {
+ this.count = count;
+ }
+
+ public Integer getStrategy() {
+ return strategy;
+ }
+
+ public void setStrategy(Integer strategy) {
+ this.strategy = strategy;
+ }
+
+ public String getRefResource() {
+ return refResource;
+ }
+
+ public void setRefResource(String refResource) {
+ this.refResource = refResource;
+ }
+
+ public Integer getControlBehavior() {
+ return controlBehavior;
+ }
+
+ public void setControlBehavior(Integer controlBehavior) {
+ this.controlBehavior = controlBehavior;
+ }
+
+ public Integer getWarmUpPeriodSec() {
+ return warmUpPeriodSec;
+ }
+
+ public void setWarmUpPeriodSec(Integer warmUpPeriodSec) {
+ this.warmUpPeriodSec = warmUpPeriodSec;
+ }
+
+ public Integer getMaxQueueingTimeMs() {
+ return maxQueueingTimeMs;
+ }
+
+ public void setMaxQueueingTimeMs(Integer maxQueueingTimeMs) {
+ this.maxQueueingTimeMs = maxQueueingTimeMs;
+ }
+
+ @Override
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public FlowRule toFlowRule() {
+ FlowRule flowRule = new FlowRule();
+ flowRule.setCount(this.count);
+ flowRule.setGrade(this.grade);
+ flowRule.setResource(this.resource);
+ flowRule.setLimitApp(this.limitApp);
+ flowRule.setRefResource(this.refResource);
+ flowRule.setStrategy(this.strategy);
+ if (this.controlBehavior != null) {
+ flowRule.setControlBehavior(controlBehavior);
+ }
+ if (this.warmUpPeriodSec != null) {
+ flowRule.setWarmUpPeriodSec(warmUpPeriodSec);
+ }
+ if (this.maxQueueingTimeMs != null) {
+ flowRule.setMaxQueueingTimeMs(maxQueueingTimeMs);
+ }
+ return flowRule;
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MachineEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MachineEntity.java
new file mode 100755
index 00000000..f0cba232
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MachineEntity.java
@@ -0,0 +1,124 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
+
+/**
+ * @author leyou
+ */
+public class MachineEntity {
+ private Long id;
+ private Date gmtCreate;
+ private Date gmtModified;
+ private String app;
+ private String ip;
+ private String hostname;
+ private Date timestamp;
+ private Integer port;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public MachineInfo toMachineInfo() {
+ MachineInfo machineInfo = new MachineInfo();
+
+ machineInfo.setApp(app);
+ machineInfo.setHostname(hostname);
+ machineInfo.setIp(ip);
+ machineInfo.setPort(port);
+ machineInfo.setVersion(timestamp);
+
+ return machineInfo;
+ }
+
+ @Override
+ public String toString() {
+ return "MachineEntity{" +
+ "id=" + id +
+ ", gmtCreate=" + gmtCreate +
+ ", gmtModified=" + gmtModified +
+ ", app='" + app + '\'' +
+ ", ip='" + ip + '\'' +
+ ", hostname='" + hostname + '\'' +
+ ", timestamp=" + timestamp +
+ ", port=" + port +
+ '}';
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricEntity.java
new file mode 100755
index 00000000..c8c2806a
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricEntity.java
@@ -0,0 +1,223 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+/**
+ * @author leyou
+ */
+public class MetricEntity {
+ private Long id;
+ private Date gmtCreate;
+ private Date gmtModified;
+ private String app;
+ /**
+ * 监控信息的时间戳
+ */
+ private Date timestamp;
+ private String resource;
+ private Long passedQps;
+ private Long successQps;
+ private Long blockedQps;
+ /**
+ * 发生异常的次数
+ */
+ private Long exception;
+
+ /**
+ * 所有successQps的Rt的和。
+ */
+ private double rt;
+
+ /**
+ * 本次聚合的总条数
+ */
+ private int count;
+
+ private int resourceCode;
+
+ public static MetricEntity copyOf(MetricEntity oldEntity) {
+ MetricEntity entity = new MetricEntity();
+ entity.setId(oldEntity.getId());
+ entity.setGmtCreate(oldEntity.getGmtCreate());
+ entity.setGmtModified(oldEntity.getGmtModified());
+ entity.setApp(oldEntity.getApp());
+ entity.setTimestamp(oldEntity.getTimestamp());
+ entity.setResource(oldEntity.getResource());
+ entity.setPassedQps(oldEntity.getPassedQps());
+ entity.setBlockedQps(oldEntity.getBlockedQps());
+ entity.setSuccessQps(oldEntity.getSuccessQps());
+ entity.setException(oldEntity.getException());
+ entity.setRt(oldEntity.getRt());
+ entity.setCount(oldEntity.getCount());
+ entity.setResource(oldEntity.getResource());
+ return entity;
+ }
+
+ public synchronized void addPassedQps(Long passedQps) {
+ this.passedQps += passedQps;
+ }
+
+ public synchronized void addBlockedQps(Long blockedQps) {
+ this.blockedQps += blockedQps;
+ }
+
+ public synchronized void addException(Long exception) {
+ this.exception += exception;
+ }
+
+ public synchronized void addCount(int count) {
+ this.count += count;
+ }
+
+ public synchronized void addRtAndSuccessQps(double avgRt, Long successQps) {
+ this.rt += avgRt * successQps;
+ this.successQps += successQps;
+ }
+
+ /**
+ * {@link #rt} = {@code avgRt * successQps}
+ *
+ * @param avgRt average rt of {@code successQps}
+ * @param successQps
+ */
+ public synchronized void setRtAndSuccessQps(double avgRt, Long successQps) {
+ this.rt = avgRt * successQps;
+ this.successQps = successQps;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public Date getTimestamp() {
+ return timestamp;
+ }
+
+ public void setTimestamp(Date timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ this.resourceCode = resource.hashCode();
+ }
+
+ public Long getPassedQps() {
+ return passedQps;
+ }
+
+ public void setPassedQps(Long passedQps) {
+ this.passedQps = passedQps;
+ }
+
+ public Long getBlockedQps() {
+ return blockedQps;
+ }
+
+ public void setBlockedQps(Long blockedQps) {
+ this.blockedQps = blockedQps;
+ }
+
+ public Long getException() {
+ return exception;
+ }
+
+ public void setException(Long exception) {
+ this.exception = exception;
+ }
+
+ public double getRt() {
+ return rt;
+ }
+
+ public void setRt(double rt) {
+ this.rt = rt;
+ }
+
+ public int getCount() {
+ return count;
+ }
+
+ public void setCount(int count) {
+ this.count = count;
+ }
+
+ public int getResourceCode() {
+ return resourceCode;
+ }
+
+ public Long getSuccessQps() {
+ return successQps;
+ }
+
+ public void setSuccessQps(Long successQps) {
+ this.successQps = successQps;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricEntity{" +
+ "id=" + id +
+ ", gmtCreate=" + gmtCreate +
+ ", gmtModified=" + gmtModified +
+ ", app='" + app + '\'' +
+ ", timestamp=" + timestamp +
+ ", resource='" + resource + '\'' +
+ ", passedQps=" + passedQps +
+ ", blockedQps=" + blockedQps +
+ ", successQps=" + successQps +
+ ", exception=" + exception +
+ ", rt=" + rt +
+ ", count=" + count +
+ ", resourceCode=" + resourceCode +
+ '}';
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java
new file mode 100755
index 00000000..47fd7d33
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/MetricPositionEntity.java
@@ -0,0 +1,121 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+/**
+ * @author leyou
+ */
+public class MetricPositionEntity {
+ private long id;
+ private Date gmtCreate;
+ private Date gmtModified;
+ private String app;
+ private String ip;
+ /**
+ * Sentinel在该应用上使用的端口
+ */
+ private int port;
+
+ /**
+ * 机器名,冗余字段
+ */
+ private String hostname;
+
+ /**
+ * 上一次拉取的最晚时间戳
+ */
+ private Date lastFetch;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public Date getLastFetch() {
+ return lastFetch;
+ }
+
+ public void setLastFetch(Date lastFetch) {
+ this.lastFetch = lastFetch;
+ }
+
+ @Override
+ public String toString() {
+ return "MetricPositionEntity{" +
+ "id=" + id +
+ ", gmtCreate=" + gmtCreate +
+ ", gmtModified=" + gmtModified +
+ ", app='" + app + '\'' +
+ ", ip='" + ip + '\'' +
+ ", port=" + port +
+ ", hostname='" + hostname + '\'' +
+ ", lastFetch=" + lastFetch +
+ '}';
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/RuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/RuleEntity.java
new file mode 100755
index 00000000..2375f1fc
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/RuleEntity.java
@@ -0,0 +1,36 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+/**
+ * @author leyou
+ */
+public interface RuleEntity {
+
+ Long getId();
+
+ void setId(Long id);
+
+ String getApp();
+
+ String getIp();
+
+ Integer getPort();
+
+ Date getGmtCreate();
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/SystemRuleEntity.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/SystemRuleEntity.java
new file mode 100755
index 00000000..8041eda2
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/datasource/entity/SystemRuleEntity.java
@@ -0,0 +1,146 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.datasource.entity;
+
+import java.util.Date;
+
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
+
+/**
+ * @author leyou
+ */
+public class SystemRuleEntity implements RuleEntity {
+
+ private Long id;
+
+ private String app;
+ private String ip;
+ private Integer port;
+ private Double avgLoad;
+ private Long avgRt;
+ private Long maxThread;
+ private Double qps;
+
+ private Date gmtCreate;
+ private Date gmtModified;
+
+ public static SystemRuleEntity fromSystemRule(String app, String ip, Integer port, SystemRule rule) {
+ SystemRuleEntity entity = new SystemRuleEntity();
+ entity.setApp(app);
+ entity.setIp(ip);
+ entity.setPort(port);
+ entity.setAvgLoad(rule.getHighestSystemLoad());
+ entity.setAvgRt(rule.getAvgRt());
+ entity.setMaxThread(rule.getMaxThread());
+ entity.setQps(rule.getQps());
+ return entity;
+ }
+
+ @Override
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ @Override
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ @Override
+ public Long getId() {
+ return id;
+ }
+
+ @Override
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ @Override
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public Double getAvgLoad() {
+ return avgLoad;
+ }
+
+ public void setAvgLoad(Double avgLoad) {
+ this.avgLoad = avgLoad;
+ }
+
+ public Long getAvgRt() {
+ return avgRt;
+ }
+
+ public void setAvgRt(Long avgRt) {
+ this.avgRt = avgRt;
+ }
+
+ public Long getMaxThread() {
+ return maxThread;
+ }
+
+ public void setMaxThread(Long maxThread) {
+ this.maxThread = maxThread;
+ }
+
+ public Double getQps() {
+ return qps;
+ }
+
+ public void setQps(Double qps) {
+ this.qps = qps;
+ }
+
+ @Override
+ public Date getGmtCreate() {
+ return gmtCreate;
+ }
+
+ public void setGmtCreate(Date gmtCreate) {
+ this.gmtCreate = gmtCreate;
+ }
+
+ public Date getGmtModified() {
+ return gmtModified;
+ }
+
+ public void setGmtModified(Date gmtModified) {
+ this.gmtModified = gmtModified;
+ }
+
+ public SystemRule toSystemRule() {
+ SystemRule rule = new SystemRule();
+ rule.setHighestSystemLoad(avgLoad);
+ rule.setAvgRt(avgRt);
+ rule.setMaxThread(maxThread);
+ rule.setQps(qps);
+ return rule;
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppInfo.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppInfo.java
new file mode 100755
index 00000000..402d7c1a
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppInfo.java
@@ -0,0 +1,56 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.discovery;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+public class AppInfo {
+
+ private String app = "";
+
+ private Set machines = new TreeSet();
+
+ public AppInfo() {
+ }
+
+ public AppInfo(String app) {
+ this.app = app;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public synchronized Set getMachines() {
+ return machines;
+ }
+
+ @Override
+ public String toString() {
+ return "AppInfo{" + "app='" + app + ", machines=" + machines + '}';
+ }
+
+ public synchronized boolean addMachine(MachineInfo machineInfo) {
+ machines.remove(machineInfo);
+ return machines.add(machineInfo);
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppManagement.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppManagement.java
new file mode 100755
index 00000000..8002c659
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/AppManagement.java
@@ -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.taobao.csp.sentinel.dashboard.discovery;
+
+import java.util.List;
+import java.util.Set;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AppManagement implements MachineDiscovery {
+
+ //@Value("${appmanagement.maxnode}")
+ //private Integer maxNode;
+ //
+ //@Value("${discovery.type}")
+ //private String type;
+
+ @Autowired
+ private ApplicationContext context;
+
+ MachineDiscovery machineDiscovery;
+
+ @PostConstruct
+ public void init() {
+ machineDiscovery = context.getBean(SimpleMachineDiscovery.class);
+ }
+
+ @Override
+ public Set getBriefApps() {
+ return machineDiscovery.getBriefApps();
+ }
+
+ @Override
+ public long addMachine(MachineInfo machineInfo) {
+ return machineDiscovery.addMachine(machineInfo);
+ }
+
+ @Override
+ public List getAppNames() {
+ return machineDiscovery.getAppNames();
+ }
+
+ @Override
+ public AppInfo getDetailApp(String app) {
+ return machineDiscovery.getDetailApp(app);
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineDiscovery.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineDiscovery.java
new file mode 100755
index 00000000..989128bd
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineDiscovery.java
@@ -0,0 +1,33 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.discovery;
+
+import java.util.List;
+import java.util.Set;
+
+public interface MachineDiscovery {
+
+ long MAX_CLIENT_LIVE_TIME_MS = 1000 * 60 * 5;
+ String UNKNOWN_APP_NAME = "UNKNOWN";
+
+ List getAppNames();
+
+ Set getBriefApps();
+
+ AppInfo getDetailApp(String app);
+
+ long addMachine(MachineInfo machineInfo);
+}
\ No newline at end of file
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineInfo.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineInfo.java
new file mode 100755
index 00000000..b3aea6e2
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/MachineInfo.java
@@ -0,0 +1,129 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.discovery;
+
+import java.util.Date;
+
+import com.alibaba.csp.sentinel.util.StringUtil;
+
+public class MachineInfo implements Comparable {
+ private String app = "";
+ private String hostname = "";
+ private String ip = "";
+ private Integer port = -1;
+ private Date version;
+
+ public static MachineInfo of(String app, String ip, Integer port) {
+ MachineInfo machineInfo = new MachineInfo();
+ machineInfo.setApp(app);
+ machineInfo.setIp(ip);
+ machineInfo.setPort(port);
+ return machineInfo;
+ }
+
+ public Integer getPort() {
+ return port;
+ }
+
+ public void setPort(Integer port) {
+ this.port = port;
+ }
+
+ public String getApp() {
+ return app;
+ }
+
+ public void setApp(String app) {
+ this.app = app;
+ }
+
+ public String getHostname() {
+ return hostname;
+ }
+
+ public void setHostname(String hostname) {
+ this.hostname = hostname;
+ }
+
+ public String getIp() {
+ return ip;
+ }
+
+ public void setIp(String ip) {
+ this.ip = ip;
+ }
+
+ public Date getVersion() {
+ return version;
+ }
+
+ public void setVersion(Date version) {
+ this.version = version;
+ }
+
+ @Override
+ public int compareTo(MachineInfo o) {
+ if (this == o) {
+ return 0;
+ }
+ if (!port.equals(o.getPort())) {
+ return port.compareTo(o.getPort());
+ }
+ if (!StringUtil.equals(app, o.getApp())) {
+ return app.compareToIgnoreCase(o.getApp());
+ }
+ return ip.compareToIgnoreCase(o.getIp());
+ }
+
+ @Override
+ public String toString() {
+ return "MachineInfo{" +
+ "app='" + app + '\'' +
+ ", hostname='" + hostname + '\'' +
+ ", ip='" + ip + '\'' +
+ ", port=" + port +
+ ", version=" + version +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof MachineInfo)) {
+ return false;
+ }
+
+ MachineInfo that = (MachineInfo)o;
+
+ if (app != null ? !app.equals(that.app) : that.app != null) {
+ return false;
+ }
+ if (hostname != null ? !hostname.equals(that.hostname) : that.hostname != null) {
+ return false;
+ }
+ return ip != null ? ip.equals(that.ip) : that.ip == null;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = app != null ? app.hashCode() : 0;
+ result = 31 * result + (hostname != null ? hostname.hashCode() : 0);
+ result = 31 * result + (ip != null ? ip.hashCode() : 0);
+ return result;
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java
new file mode 100755
index 00000000..1148664f
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/discovery/SimpleMachineDiscovery.java
@@ -0,0 +1,55 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.discovery;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.springframework.stereotype.Component;
+
+/**
+ * @author leyou
+ */
+@Component
+public class SimpleMachineDiscovery implements MachineDiscovery {
+ protected ConcurrentHashMap apps = new ConcurrentHashMap<>();
+
+ @Override
+ public long addMachine(MachineInfo machineInfo) {
+ AppInfo appInfo = apps.computeIfAbsent(machineInfo.getApp(), app -> new AppInfo(app));
+ appInfo.addMachine(machineInfo);
+ return 1;
+ }
+
+ @Override
+ public List getAppNames() {
+ return new ArrayList<>(apps.keySet());
+ }
+
+ @Override
+ public AppInfo getDetailApp(String app) {
+ return apps.get(app);
+ }
+
+ @Override
+ public Set getBriefApps() {
+ return new HashSet<>(apps.values());
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/ResourceTreeNode.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/ResourceTreeNode.java
new file mode 100755
index 00000000..542a0814
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/domain/ResourceTreeNode.java
@@ -0,0 +1,242 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.domain;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.command.vo.NodeVo;
+
+/**
+ * @author leyou
+ */
+public class ResourceTreeNode {
+ private String id;
+ private String parentId;
+ private String resource;
+
+ private Integer threadNum;
+ private Long passQps;
+ private Long blockedQps;
+ private Long totalQps;
+ private Long averageRt;
+ private Long successQps;
+ private Long exceptionQps;
+ private Long oneMinutePassed;
+ private Long oneMinuteBlocked;
+ private Long oneMinuteException;
+ private Long oneMinuteTotal;
+
+ private boolean visible = true;
+
+ private List children = new ArrayList<>();
+
+ public static ResourceTreeNode fromNodeVoList(List nodeVos) {
+ if (nodeVos == null || nodeVos.isEmpty()) {
+ return null;
+ }
+ ResourceTreeNode root = null;
+ Map map = new HashMap<>();
+ for (NodeVo vo : nodeVos) {
+ ResourceTreeNode node = fromNodeVo(vo);
+ map.put(node.id, node);
+ // real root
+ if (node.parentId == null) {
+ root = node;
+ } else if (map.containsKey(node.parentId)) {
+ map.get(node.parentId).children.add(node);
+ } else {
+ // impossible
+ }
+ }
+ return root;
+ }
+
+ public static ResourceTreeNode fromNodeVo(NodeVo vo) {
+ ResourceTreeNode node = new ResourceTreeNode();
+ node.id = vo.getId();
+ node.parentId = vo.getParentId();
+ node.resource = vo.getResource();
+ node.threadNum = vo.getThreadNum();
+ node.passQps = vo.getPassQps();
+ node.blockedQps = vo.getBlockedQps();
+ node.totalQps = vo.getTotalQps();
+ node.averageRt = vo.getAverageRt();
+ node.successQps = vo.getSuccessQps();
+ node.exceptionQps = vo.getExceptionQps();
+ node.oneMinutePassed = vo.getOneMinutePassed();
+ node.oneMinuteBlocked = vo.getOneMinuteBlocked();
+ node.oneMinuteException = vo.getOneMinuteException();
+ node.oneMinuteTotal = vo.getOneMinuteTotal();
+ return node;
+ }
+
+ public void searchIgnoreCase(String searchKey) {
+ search(this, searchKey);
+ }
+
+ /**
+ * This node is visible only when searchKey matches this.resource or at least
+ * one of this's children is visible
+ */
+ private boolean search(ResourceTreeNode node, String searchKey) {
+ // empty matches all
+ if (searchKey == null || searchKey.isEmpty() ||
+ node.resource.toLowerCase().contains(searchKey.toLowerCase())) {
+ node.visible = true;
+ } else {
+ node.visible = false;
+ }
+
+ boolean found = false;
+ for (ResourceTreeNode c : node.children) {
+ found |= search(c, searchKey);
+ }
+ node.visible |= found;
+ return node.visible;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getParentId() {
+ return parentId;
+ }
+
+ public void setParentId(String parentId) {
+ this.parentId = parentId;
+ }
+
+ public String getResource() {
+ return resource;
+ }
+
+ public void setResource(String resource) {
+ this.resource = resource;
+ }
+
+ public Integer getThreadNum() {
+ return threadNum;
+ }
+
+ public void setThreadNum(Integer threadNum) {
+ this.threadNum = threadNum;
+ }
+
+ public Long getPassQps() {
+ return passQps;
+ }
+
+ public void setPassQps(Long passQps) {
+ this.passQps = passQps;
+ }
+
+ public Long getBlockedQps() {
+ return blockedQps;
+ }
+
+ public void setBlockedQps(Long blockedQps) {
+ this.blockedQps = blockedQps;
+ }
+
+ public Long getTotalQps() {
+ return totalQps;
+ }
+
+ public void setTotalQps(Long totalQps) {
+ this.totalQps = totalQps;
+ }
+
+ public Long getAverageRt() {
+ return averageRt;
+ }
+
+ public void setAverageRt(Long averageRt) {
+ this.averageRt = averageRt;
+ }
+
+ public Long getSuccessQps() {
+ return successQps;
+ }
+
+ public void setSuccessQps(Long successQps) {
+ this.successQps = successQps;
+ }
+
+ public Long getExceptionQps() {
+ return exceptionQps;
+ }
+
+ public void setExceptionQps(Long exceptionQps) {
+ this.exceptionQps = exceptionQps;
+ }
+
+ public Long getOneMinutePassed() {
+ return oneMinutePassed;
+ }
+
+ public void setOneMinutePassed(Long oneMinutePassed) {
+ this.oneMinutePassed = oneMinutePassed;
+ }
+
+ public Long getOneMinuteBlocked() {
+ return oneMinuteBlocked;
+ }
+
+ public void setOneMinuteBlocked(Long oneMinuteBlocked) {
+ this.oneMinuteBlocked = oneMinuteBlocked;
+ }
+
+ public Long getOneMinuteException() {
+ return oneMinuteException;
+ }
+
+ public void setOneMinuteException(Long oneMinuteException) {
+ this.oneMinuteException = oneMinuteException;
+ }
+
+ public Long getOneMinuteTotal() {
+ return oneMinuteTotal;
+ }
+
+ public void setOneMinuteTotal(Long oneMinuteTotal) {
+ this.oneMinuteTotal = oneMinuteTotal;
+ }
+
+ public boolean isVisible() {
+ return visible;
+ }
+
+ public void setVisible(boolean visible) {
+ this.visible = visible;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public void setChildren(List children) {
+ this.children = children;
+ }
+}
+
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/HttpHelper.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/HttpHelper.java
new file mode 100755
index 00000000..bde4ebfb
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/HttpHelper.java
@@ -0,0 +1,327 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.stream.Collectors;
+
+import com.alibaba.csp.sentinel.config.SentinelConfig;
+import com.alibaba.csp.sentinel.command.vo.NodeVo;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
+import com.alibaba.csp.sentinel.slots.system.SystemRule;
+import com.alibaba.fastjson.JSON;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.DegradeRuleEntity;
+import com.taobao.csp.sentinel.dashboard.datasource.entity.FlowRuleEntity;
+import com.taobao.csp.sentinel.dashboard.datasource.entity.SystemRuleEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.concurrent.FutureCallback;
+import org.apache.http.entity.ContentType;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
+import org.apache.http.impl.nio.client.HttpAsyncClients;
+import org.apache.http.impl.nio.reactor.IOReactorConfig;
+import org.apache.http.util.EntityUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+/**
+ * Communicate with Sentinel client.
+ *
+ * @author leyou
+ */
+@Component
+public class HttpHelper {
+
+ private static Logger logger = LoggerFactory.getLogger(HttpHelper.class);
+ private static final Charset defaultCharset = Charset.forName(SentinelConfig.charset());
+
+ private CloseableHttpAsyncClient httpclient;
+ private final String resourceUrlPath = "jsonTree";
+ private final String clusterNodePath = "clusterNode";
+
+ private final String getRulesPath = "getRules";
+ private final String setRulesPath = "setRules";
+ private final String flowRuleType = "flow";
+ private final String degradeRuleType = "degrade";
+ private final String systemRuleType = "system";
+
+ public HttpHelper() {
+ IOReactorConfig ioConfig = IOReactorConfig.custom().setConnectTimeout(3000).setSoTimeout(3000)
+ .setIoThreadCount(Runtime.getRuntime().availableProcessors() * 2).build();
+ httpclient = HttpAsyncClients.custom().setRedirectStrategy(new DefaultRedirectStrategy() {
+ @Override
+ protected boolean isRedirectable(final String method) {
+ return false;
+ }
+ }).setMaxConnTotal(4000).setMaxConnPerRoute(1000).setDefaultIOReactorConfig(ioConfig).build();
+ httpclient.start();
+ }
+
+ public List fetchResourceOfMachine(String ip, int port, String type) {
+ String url = "http://" + ip + ":" + port + "/" + resourceUrlPath + "?type=" + type;
+ String body = httpGetContent(url);
+ if (body == null) {
+ return null;
+ }
+ try {
+ return JSON.parseArray(body, NodeVo.class);
+ } catch (Exception e) {
+ logger.info("parse ResourceOfMachine error", e);
+ return null;
+ }
+ }
+
+ /**
+ * Fetch cluster node.
+ *
+ * @param ip ip to fetch
+ * @param port port of the ip
+ * @param includeZero whether zero value should in the result list.
+ * @return
+ */
+ public List fetchClusterNodeOfMachine(String ip, int port, boolean includeZero) {
+ String type = "noZero";
+ if (includeZero) {
+ type = "zero";
+ }
+ String url = "http://" + ip + ":" + port + "/" + clusterNodePath + "?type=" + type;
+ String body = httpGetContent(url);
+ if (body == null) {
+ return null;
+ }
+ try {
+ return JSON.parseArray(body, NodeVo.class);
+ } catch (Exception e) {
+ logger.info("parse ClusterNodeOfMachine error", e);
+ return null;
+ }
+ }
+
+ public List fetchFlowRuleOfMachine(String app, String ip, int port) {
+ String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + flowRuleType;
+ String body = httpGetContent(url);
+ logger.info("FlowRule Body:{}", body);
+ List rules = parseFlowRule(body);
+ if (rules != null) {
+ return rules.stream().map(rule -> FlowRuleEntity.fromFlowRule(app, ip, port, rule))
+ .collect(Collectors.toList());
+ } else {
+ return null;
+ }
+ }
+
+ public List fetchDegradeRuleOfMachine(String app, String ip, int port) {
+ String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + degradeRuleType;
+ String body = httpGetContent(url);
+ logger.info("Degrade Body:{}", body);
+ List rules = parseDegradeRule(body);
+ if (rules != null) {
+ return rules.stream().map(rule -> DegradeRuleEntity.fromDegradeRule(app, ip, port, rule))
+ .collect(Collectors.toList());
+ } else {
+ return null;
+ }
+ }
+
+ public List fetchSystemRuleOfMachine(String app, String ip, int port) {
+ String url = "http://" + ip + ":" + port + "/" + getRulesPath + "?type=" + systemRuleType;
+ String body = httpGetContent(url);
+ logger.info("SystemRule Body:{}", body);
+ List rules = parseSystemRule(body);
+ if (rules != null) {
+ return rules.stream().map(rule -> SystemRuleEntity.fromSystemRule(app, ip, port, rule))
+ .collect(Collectors.toList());
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * set rules of the machine. rules == null will return immediately;
+ * rules.isEmpty() means setting the rules to empty.
+ *
+ * @param app
+ * @param ip
+ * @param port
+ * @param rules
+ * @return whether successfully set the rules.
+ */
+ public boolean setFlowRuleOfMachine(String app, String ip, int port, List rules) {
+ if (rules == null) {
+ return true;
+ }
+ if (ip == null) {
+ throw new IllegalArgumentException("ip is null");
+ }
+ String data = JSON.toJSONString(rules.stream().map(FlowRuleEntity::toFlowRule).collect(Collectors.toList()));
+ try {
+ data = URLEncoder.encode(data, defaultCharset.name());
+ } catch (UnsupportedEncodingException e) {
+ logger.info("encode rule error", e);
+ return false;
+ }
+ String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + flowRuleType + "&data=" + data;
+ String result = httpGetContent(url);
+ logger.info("setFlowRule: " + result);
+ return true;
+ }
+
+ /**
+ * set rules of the machine. rules == null will return immediately;
+ * rules.isEmpty() means setting the rules to empty.
+ *
+ * @param app
+ * @param ip
+ * @param port
+ * @param rules
+ * @return whether successfully set the rules.
+ */
+ public boolean setDegradeRuleOfMachine(String app, String ip, int port, List rules) {
+ if (rules == null) {
+ return true;
+ }
+ if (ip == null) {
+ throw new IllegalArgumentException("ip is null");
+ }
+ String data = JSON.toJSONString(
+ rules.stream().map(DegradeRuleEntity::toDegradeRule).collect(Collectors.toList()));
+ try {
+ data = URLEncoder.encode(data, defaultCharset.name());
+ } catch (UnsupportedEncodingException e) {
+ logger.info("encode rule error", e);
+ return false;
+ }
+ String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + degradeRuleType + "&data=" + data;
+ String result = httpGetContent(url);
+ logger.info("setDegradeRule: " + result);
+ return true;
+ }
+
+ /**
+ * set rules of the machine. rules == null will return immediately;
+ * rules.isEmpty() means setting the rules to empty.
+ *
+ * @param app
+ * @param ip
+ * @param port
+ * @param rules
+ * @return whether successfully set the rules.
+ */
+ public boolean setSystemRuleOfMachine(String app, String ip, int port, List rules) {
+ if (rules == null) {
+ return true;
+ }
+ if (ip == null) {
+ throw new IllegalArgumentException("ip is null");
+ }
+ String data = JSON.toJSONString(
+ rules.stream().map(SystemRuleEntity::toSystemRule).collect(Collectors.toList()));
+ try {
+ data = URLEncoder.encode(data, defaultCharset.name());
+ } catch (UnsupportedEncodingException e) {
+ logger.info("encode rule error", e);
+ return false;
+ }
+ String url = "http://" + ip + ":" + port + "/" + setRulesPath + "?type=" + systemRuleType + "&data=" + data;
+ String result = httpGetContent(url);
+ logger.info("setSystemRule: " + result);
+ return true;
+ }
+
+ private List parseFlowRule(String body) {
+ try {
+ return JSON.parseArray(body, FlowRule.class);
+ } catch (Exception e) {
+ logger.info("parser FlowRule error: ", e);
+ return null;
+ }
+ }
+
+ private List parseDegradeRule(String body) {
+ try {
+ return JSON.parseArray(body, DegradeRule.class);
+ } catch (Exception e) {
+ logger.info("parser DegradeRule error: ", e);
+ return null;
+ }
+ }
+
+ private List parseSystemRule(String body) {
+ try {
+ return JSON.parseArray(body, SystemRule.class);
+ } catch (Exception e) {
+ logger.info("parser SystemRule error: ", e);
+ return null;
+ }
+ }
+
+ private String httpGetContent(String url) {
+ final HttpGet httpGet = new HttpGet(url);
+ final CountDownLatch latch = new CountDownLatch(1);
+ final AtomicReference reference = new AtomicReference<>();
+ httpclient.execute(httpGet, new FutureCallback() {
+ @Override
+ public void completed(final HttpResponse response) {
+ try {
+ reference.set(getBody(response));
+ } catch (Exception e) {
+ logger.info("httpGetContent " + url + " error:", e);
+ } finally {
+ latch.countDown();
+ }
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ latch.countDown();
+ logger.info("httpGetContent " + url + " failed:", ex);
+ }
+
+ @Override
+ public void cancelled() {
+ latch.countDown();
+ }
+ });
+ try {
+ latch.await(5, TimeUnit.SECONDS);
+ } catch (Exception e) {
+ logger.info("wait http client error:", e);
+ }
+ return reference.get();
+ }
+
+ private String getBody(HttpResponse response) throws Exception {
+ Charset charset = null;
+ try {
+ String contentTypeStr = response.getFirstHeader("Content-type").getValue();
+ ContentType contentType = ContentType.parse(contentTypeStr);
+ charset = contentType.getCharset();
+ } catch (Exception ignore) {
+ }
+ return EntityUtils.toString(response.getEntity(), charset != null ? charset : defaultCharset);
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemDegradeRuleStore.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemDegradeRuleStore.java
new file mode 100755
index 00000000..549c47ae
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemDegradeRuleStore.java
@@ -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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.DegradeRuleEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author leyou
+ */
+@Component
+public class InMemDegradeRuleStore extends InMemRepositoryAdapter {
+
+ private static AtomicLong ids = new AtomicLong(0);
+
+ @Override
+ protected long nextId() {
+ return ids.incrementAndGet();
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemFlowRuleStore.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemFlowRuleStore.java
new file mode 100755
index 00000000..220d64ef
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemFlowRuleStore.java
@@ -0,0 +1,36 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.FlowRuleEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * Store {@link FlowRuleEntity} in memory.
+ *
+ * @author leyou
+ */
+@Component
+public class InMemFlowRuleStore extends InMemRepositoryAdapter {
+ private static AtomicLong ids = new AtomicLong(0);
+
+ @Override
+ protected long nextId() {
+ return ids.incrementAndGet();
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemMetricStore.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemMetricStore.java
new file mode 100755
index 00000000..59409f58
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemMetricStore.java
@@ -0,0 +1,129 @@
+/*
+ * 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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.MetricEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * Store metrics in memory.
+ *
+ * @author leyou
+ */
+@Component
+public class InMemMetricStore {
+ public static final long MAX_METRIC_LIVE_TIME_MS = 1000 * 60 * 5;
+ /**
+ * {@code app -> resource -> timestamp -> metric}
+ */
+ private Map>> allMetrics = new ConcurrentHashMap<>();
+
+ /**
+ * Save all metrics in memory. Metric older than {@link #MAX_METRIC_LIVE_TIME_MS} will be removed.
+ *
+ * @param metrics metrics to be saved.
+ */
+ public synchronized void saveAll(Iterable metrics) {
+ if (metrics == null) {
+ return;
+ }
+ for (MetricEntity entity : metrics) {
+ allMetrics.computeIfAbsent(entity.getApp(), e -> new HashMap<>(16))
+ .computeIfAbsent(entity.getResource(), e -> new LinkedHashMap() {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return eldest.getKey() < System.currentTimeMillis() - MAX_METRIC_LIVE_TIME_MS;
+ }
+ }).put(entity.getTimestamp().getTime(), entity);
+ }
+ }
+
+ public synchronized List queryByAppAndResouce(String app,
+ String resource,
+ long startTime,
+ long endTime) {
+ List results = new ArrayList<>();
+ Map> resouceMap = allMetrics.get(app);
+ if (resouceMap == null) {
+ return results;
+ }
+ LinkedHashMap metricsMap = resouceMap.get(resource);
+ if (metricsMap == null) {
+ return results;
+ }
+ for (Map.Entry entry : metricsMap.entrySet()) {
+ if (entry.getKey() >= startTime && entry.getKey() <= endTime) {
+ results.add(entry.getValue());
+ }
+ }
+ return results;
+ }
+
+ /**
+ * Find resources of App order by last minute b_qps desc
+ *
+ * @param app app name
+ * @return Resources list, order by last minute b_qps desc.
+ */
+ public synchronized List findResourcesOfApp(String app) {
+ List results = new ArrayList<>();
+ // resource -> timestamp -> metric
+ Map> resourceMap = allMetrics.get(app);
+ if (resourceMap == null) {
+ return results;
+ }
+ final long minTimeMs = System.currentTimeMillis() - 1000 * 60;
+ Map resourceCount = new HashMap<>(32);
+
+ for (Map.Entry> resourceMetrics : resourceMap.entrySet()) {
+ for (Map.Entry metrics : resourceMetrics.getValue().entrySet()) {
+ if (metrics.getKey() < minTimeMs) {
+ continue;
+ }
+ MetricEntity newEntity = metrics.getValue();
+ if (resourceCount.containsKey(resourceMetrics.getKey())) {
+ MetricEntity oldEntity = resourceCount.get(resourceMetrics.getKey());
+ oldEntity.addPassedQps(newEntity.getPassedQps());
+ oldEntity.addRtAndSuccessQps(newEntity.getRt(), newEntity.getSuccessQps());
+ oldEntity.addBlockedQps(newEntity.getBlockedQps());
+ oldEntity.addException(newEntity.getException());
+ oldEntity.addCount(1);
+ } else {
+ resourceCount.put(resourceMetrics.getKey(), MetricEntity.copyOf(newEntity));
+ }
+ }
+ }
+ return resourceCount.entrySet().stream().sorted((o1, o2) -> {
+ MetricEntity e1 = o1.getValue();
+ MetricEntity e2 = o2.getValue();
+ int t = e2.getBlockedQps().compareTo(e1.getBlockedQps());
+ if (t != 0) {
+ return t;
+ }
+ return e2.getPassedQps().compareTo(e1.getPassedQps());
+ }).map(e -> e.getKey())
+ .collect(Collectors.toList());
+ }
+
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemRepositoryAdapter.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemRepositoryAdapter.java
new file mode 100755
index 00000000..bd43bea8
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemRepositoryAdapter.java
@@ -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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.RuleEntity;
+import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
+
+/**
+ * @author leyou
+ */
+public abstract class InMemRepositoryAdapter implements RuleRepository {
+ /**
+ * {@code >}
+ */
+ private Map> machineRules = new ConcurrentHashMap<>(16);
+ private Map allRules = new ConcurrentHashMap<>(16);
+
+ private static final int MAX_RULES_SIZE = 10000;
+
+ @Override
+ public T save(T entity) {
+ if (entity.getId() == null) {
+ entity.setId(nextId());
+ }
+ allRules.put(entity.getId(), entity);
+ machineRules.computeIfAbsent(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort()),
+ e -> new ConcurrentHashMap<>(32))
+ .put(entity.getId(), entity);
+ return entity;
+ }
+
+ @Override
+ public List saveAll(List rules) {
+ allRules.clear();
+ machineRules.clear();
+
+ if (rules == null) {
+ return null;
+ }
+ List savedRules = new ArrayList<>(rules.size());
+ for (T rule : rules) {
+ savedRules.add(save(rule));
+ }
+ return savedRules;
+ }
+
+ @Override
+ public T delete(Long id) {
+ T entity = allRules.remove(id);
+ if (entity != null) {
+ machineRules.get(MachineInfo.of(entity.getApp(), entity.getIp(), entity.getPort())).remove(id);
+ }
+ return entity;
+ }
+
+ @Override
+ public T findById(Long id) {
+ return allRules.get(id);
+ }
+
+ @Override
+ public List findAllByMachine(MachineInfo machineInfo) {
+ Map entities = machineRules.get(machineInfo);
+ if (entities == null) {
+ return new ArrayList<>();
+ }
+ return entities.values().stream()
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Get next unused id.
+ *
+ * @return
+ */
+ abstract protected long nextId();
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemSystemRuleStore.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemSystemRuleStore.java
new file mode 100755
index 00000000..1ed00bf3
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/InMemSystemRuleStore.java
@@ -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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.taobao.csp.sentinel.dashboard.datasource.entity.SystemRuleEntity;
+import org.springframework.stereotype.Component;
+
+/**
+ * @author leyou
+ */
+@Component
+public class InMemSystemRuleStore extends InMemRepositoryAdapter {
+
+ private static AtomicLong ids = new AtomicLong(0);
+
+ @Override
+ protected long nextId() {
+ return ids.incrementAndGet();
+ }
+}
diff --git a/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/RuleRepository.java b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/RuleRepository.java
new file mode 100755
index 00000000..65892087
--- /dev/null
+++ b/sentinel-dashboard/src/main/java/com/taobao/csp/sentinel/dashboard/inmem/RuleRepository.java
@@ -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.taobao.csp.sentinel.dashboard.inmem;
+
+import java.util.List;
+
+import com.taobao.csp.sentinel.dashboard.discovery.MachineInfo;
+
+/**
+ * Interface to store and find rules.
+ *
+ * @author leyou
+ */
+public interface RuleRepository {
+ /**
+ * Save one.
+ *
+ * @param entity
+ * @return
+ */
+ T save(T entity);
+
+ /**
+ * Save all.
+ *
+ * @param rules
+ * @return rules saved.
+ */
+ List saveAll(List rules);
+
+ /**
+ * Delete by id
+ *
+ * @param id
+ * @return entity deleted
+ */
+ T delete(ID id);
+
+ /**
+ * Find by id.
+ *
+ * @param id
+ * @return
+ */
+ T findById(ID id);
+
+ /**
+ * Find all by machine.
+ *
+ * @param machineInfo
+ * @return
+ */
+ List