Merge pull request #2 from doocs/master

1
This commit is contained in:
Jone 2019-06-15 18:58:38 +08:00 committed by GitHub
commit 280c5bd674
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
308 changed files with 2420 additions and 343 deletions

12
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: advanced-java
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with a single custom sponsorship URL

431
LICENSE
View File

@ -1,22 +1,427 @@
# Creative Commons Attribution-NonCommercial 4.0 International License
Attribution-ShareAlike 4.0 International
Disclaimer: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-nc/4.0/legalcode).
=======================================================================
You are free to:
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material
Using Creative Commons Public Licenses
The licensor cannot revoke these freedoms as long as you follow the license terms.
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Under the following terms:
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
- NonCommercial — You may not use the material for commercial purposes.
- No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
Notices:
=======================================================================
You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
Creative Commons Attribution-ShareAlike 4.0 International Public
License
No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or hereafter created,
and to make technical modifications necessary to do so. The
Licensor waives and/or agrees not to assert any right or
authority to forbid You from making technical modifications
necessary to exercise the Licensed Rights, including
technical modifications necessary to circumvent Effective
Technological Measures. For purposes of this Public License,
simply making modifications authorized by this Section 2(a)
(4) never produces Adapted Material.
5. Downstream recipients.
a. Offer from the Licensor -- Licensed Material. Every
recipient of the Licensed Material automatically
receives an offer from the Licensor to exercise the
Licensed Rights under the terms and conditions of this
Public License.
b. Additional offer from the Licensor -- Adapted Material.
Every recipient of Adapted Material from You
automatically receives an offer from the Licensor to
exercise the Licensed Rights in the Adapted Material
under the conditions of the Adapter's License You apply.
c. No downstream restrictions. You may not offer or impose
any additional or different terms or conditions on, or
apply any Effective Technological Measures to, the
Licensed Material if doing so restricts exercise of the
Licensed Rights by any recipient of the Licensed
Material.
6. No endorsement. Nothing in this Public License constitutes or
may be construed as permission to assert or imply that You
are, or that Your use of the Licensed Material is, connected
with, or sponsored, endorsed, or granted official status by,
the Licensor or others designated to receive attribution as
provided in Section 3(a)(1)(A)(i).
b. Other rights.
1. Moral rights, such as the right of integrity, are not
licensed under this Public License, nor are publicity,
privacy, and/or other similar personality rights; however, to
the extent possible, the Licensor waives and/or agrees not to
assert any such rights held by the Licensor to the limited
extent necessary to allow You to exercise the Licensed
Rights, but not otherwise.
2. Patent and trademark rights are not licensed under this
Public License.
3. To the extent possible, the Licensor waives any right to
collect royalties from You for the exercise of the Licensed
Rights, whether directly or through a collecting society
under any voluntary or waivable statutory or compulsory
licensing scheme. In all other cases the Licensor expressly
reserves any right to collect such royalties.
Section 3 -- License Conditions.
Your exercise of the Licensed Rights is expressly made subject to the
following conditions.
a. Attribution.
1. If You Share the Licensed Material (including in modified
form), You must:
a. retain the following if it is supplied by the Licensor
with the Licensed Material:
i. identification of the creator(s) of the Licensed
Material and any others designated to receive
attribution, in any reasonable manner requested by
the Licensor (including by pseudonym if
designated);
ii. a copyright notice;
iii. a notice that refers to this Public License;
iv. a notice that refers to the disclaimer of
warranties;
v. a URI or hyperlink to the Licensed Material to the
extent reasonably practicable;
b. indicate if You modified the Licensed Material and
retain an indication of any previous modifications; and
c. indicate the Licensed Material is licensed under this
Public License, and include the text of, or the URI or
hyperlink to, this Public License.
2. You may satisfy the conditions in Section 3(a)(1) in any
reasonable manner based on the medium, means, and context in
which You Share the Licensed Material. For example, it may be
reasonable to satisfy the conditions by providing a URI or
hyperlink to a resource that includes the required
information.
3. If requested by the Licensor, You must remove any of the
information required by Section 3(a)(1)(A) to the extent
reasonably practicable.
b. ShareAlike.
In addition to the conditions in Section 3(a), if You Share
Adapted Material You produce, the following conditions also apply.
1. The Adapter's License You apply must be a Creative Commons
license with the same License Elements, this version or
later, or a BY-SA Compatible License.
2. You must include the text of, or the URI or hyperlink to, the
Adapter's License You apply. You may satisfy this condition
in any reasonable manner based on the medium, means, and
context in which You Share Adapted Material.
3. You may not offer or impose any additional or different terms
or conditions on, or apply any Effective Technological
Measures to, Adapted Material that restrict exercise of the
rights granted under the Adapter's License You apply.
Section 4 -- Sui Generis Database Rights.
Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
to extract, reuse, reproduce, and Share all or a substantial
portion of the contents of the database;
b. if You include all or a substantial portion of the database
contents in a database in which You have Sui Generis Database
Rights, then the database in which You have Sui Generis Database
Rights (but not its individual contents) is Adapted Material,
including for purposes of Section 3(b); and
c. You must comply with the conditions in Section 3(a) if You Share
all or a substantial portion of the contents of the database.
For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
c. The disclaimer of warranties and limitation of liability provided
above shall be interpreted in a manner that, to the extent
possible, most closely approximates an absolute disclaimer and
waiver of all liability.
Section 6 -- Term and Termination.
a. This Public License applies for the term of the Copyright and
Similar Rights licensed here. However, if You fail to comply with
this Public License, then Your rights under this Public License
terminate automatically.
b. Where Your right to use the Licensed Material has terminated under
Section 6(a), it reinstates:
1. automatically as of the date the violation is cured, provided
it is cured within 30 days of Your discovery of the
violation; or
2. upon express reinstatement by the Licensor.
For the avoidance of doubt, this Section 6(b) does not affect any
right the Licensor may have to seek remedies for Your violations
of this Public License.
c. For the avoidance of doubt, the Licensor may also offer the
Licensed Material under separate terms or conditions or stop
distributing the Licensed Material at any time; however, doing so
will not terminate this Public License.
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
License.
Section 7 -- Other Terms and Conditions.
a. The Licensor shall not be bound by any additional or different
terms or conditions communicated by You unless expressly agreed.
b. Any arrangements, understandings, or agreements regarding the
Licensed Material not stated herein are separate from and
independent of the terms and conditions of this Public License.
Section 8 -- Interpretation.
a. For the avoidance of doubt, this Public License does not, and
shall not be interpreted to, reduce, limit, restrict, or impose
conditions on any use of the Licensed Material that could lawfully
be made without permission under this Public License.
b. To the extent possible, if any provision of this Public License is
deemed unenforceable, it shall be automatically reformed to the
minimum extent necessary to make it enforceable. If the provision
cannot be reformed, it shall be severed from this Public License
without affecting the enforceability of the remaining terms and
conditions.
c. No term or condition of this Public License will be waived and no
failure to comply consented to unless expressly agreed to by the
Licensor.
d. Nothing in this Public License constitutes or may be interpreted
as a limitation upon, or waiver of, any privileges and immunities
that apply to the Licensor or You, including from the legal
processes of any jurisdiction or authority.
=======================================================================
Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.
Creative Commons may be contacted at creativecommons.org.

View File

@ -1,22 +1,26 @@
# 互联网 Java 工程师进阶知识完全扫盲
# 互联网 Java 工程师进阶知识完全扫盲<sup>[©](https://github.com/doocs/advanced-java)</sup>
[![license](https://badgen.net/badge/license/CC-BY-SA%204.0/green)](https://github.com/doocs/advanced-java/blob/master/LICENSE)
[![original](https://badgen.net/badge/original/%E4%B8%AD%E5%8D%8E%E7%9F%B3%E6%9D%89/orange)](https://github.com/doocs/advanced-java)
[![notice](https://badgen.net/badge/notice/%E7%BB%B4%E6%9D%83%E8%A1%8C%E5%8A%A8/red)](/docs/from-readers/rights-defending-movement.md)
[![wechat-group](https://badgen.net/badge/chat/%E5%BE%AE%E4%BF%A1%E4%BA%A4%E6%B5%81/138c7b)](https://github.com/doocs/advanced-java/issues/70)
[![reading](https://badgen.net/badge/books/read%20together/cyan)](https://github.com/doocs/technical-books)
[![coding](https://badgen.net/badge/leetcode/coding%20together/cyan)](https://github.com/doocs/leetcode)
[![stars](https://badgen.net/github/stars/doocs/advanced-java)](https://github.com/doocs/advanced-java/stargazers)
[![forks](https://badgen.net/github/forks/doocs/advanced-java)](https://github.com/doocs/advanced-java/network/members)
[![contributors](https://badgen.net/github/contributors/doocs/advanced-java)](https://github.com/doocs/advanced-java/tree/master/docs/from-readers#contributors)
[![help-wanted](https://badgen.net/github/label-issues/doocs/advanced-java/help%20wanted/open)](https://github.com/doocs/advanced-java/labels/help%20wanted)
[![issues](https://badgen.net/github/open-issues/doocs/advanced-java)](https://github.com/doocs/advanced-java/issues)
[![PRs Welcome](https://badgen.net/badge/PRs/welcome/green)](http://makeapullrequest.com)
[![license](https://img.shields.io/badge/license-Attribution--NonCommercial%204.0%20-brightgreen.svg)](https://github.com/doocs/advanced-java/blob/master/LICENSE)
[![original](https://img.shields.io/badge/original-%E4%B8%AD%E5%8D%8E%E7%9F%B3%E6%9D%89-orange.svg)](https://github.com/doocs/advanced-java)
[![stars](https://img.shields.io/github/stars/doocs/advanced-java.svg)](https://github.com/doocs/advanced-java/stargazers)
[![forks](https://img.shields.io/github/forks/doocs/advanced-java.svg)](https://github.com/doocs/advanced-java/network/members)
[![issues](https://img.shields.io/github/issues/doocs/advanced-java.svg)](https://github.com/doocs/advanced-java/issues)
[![PRs Welcome](https://img.shields.io/badge/PRs-Welcome-brightgreen.svg)](http://makeapullrequest.com)
本项目大部分内容来自中华石杉,版权归作者所有,内容涵盖[高并发](#高并发架构)、[分布式](#分布式系统)、[高可用](#高可用架构)、[微服务](#微服务架构)等领域知识。我对这部分知识做了一个系统的整理,方便学习查阅。配合《[大型网站技术架构](https://github.com/doocs/technical-books#architecture)——李智慧》、《[Redis 设计与实现](https://github.com/doocs/technical-books#database)——[黄健宏](https://github.com/huangz1990)》食用,[效果更佳](https://doocs.gitee.io/advanced-java/#/offer)。
本系列知识出自中华石杉我对这部分知识做了一个系统的整理方便学习查阅。By the way微信公众号**石杉的架构笔记**id:shishan100有其它很多架构知识墙裂推荐~
一点小建议:学习本系列知识之前,如果你完全没接触过 `MQ`、`ES`、`Redis`、`Dubbo`、`Hystrix` 等,那么我建议你可以先在网上搜一下每一块知识的快速入门,跟着入门 Demo 玩一下,然后再开始每一块知识的学习,这样效果更好噢~
学习之前,先来看看 [Issues 讨论区](https://github.com/doocs/advanced-java/issues/9#issue-394275038)的技术面试官是怎么说的吧。本项目也欢迎各位开发者朋友来[分享自己的一些想法和实践经验](/docs/from-readers/README.md)。
## 高并发架构
### [消息队列](/docs/high-concurrency/mq-interview.md)
- [为什么使用消息队列消息队列有什么优点和缺点Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](/docs/high-concurrency/why-mq.md)
- [如何保证消息队列的高可用?](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)
- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)
- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)
- [如何保证消息的可靠性传输?(如何处理消息丢失的问题)](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md)
- [如何保证消息的顺序性?](/docs/high-concurrency/how-to-ensure-the-order-of-messages.md)
- [如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](/docs/high-concurrency/mq-time-delay-and-expired-failure.md)
@ -36,7 +40,7 @@
- [如何保证 Redis 高并发、高可用Redis 的主从复制原理能介绍一下么Redis 的哨兵原理能介绍一下么?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md)
- [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](/docs/high-concurrency/redis-persistence.md)
- [Redis 集群模式的工作原理能说一下么在集群模式下Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](/docs/high-concurrency/redis-cluster.md)
- [了解什么是 Redis 的雪崩和穿透Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
- [了解什么是 redis 的雪崩、穿透和击穿Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
- [如何保证缓存与数据库的双写一致性?](/docs/high-concurrency/redis-consistence.md)
- [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](/docs/high-concurrency/redis-cas.md)
- [生产环境中的 Redis 是怎么部署的?](/docs/high-concurrency/redis-production-environment.md)
@ -54,7 +58,6 @@
- [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md)
## 分布式系统
### [面试连环炮](/docs/distributed-system/distributed-system-interview.md)
### 系统拆分
- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md)
@ -67,7 +70,7 @@
- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
- [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
- [如何自己设计一个类似 Dubbo 的 rpc 框架?](/docs/distributed-system/dubbo-rpc-design.md)
- [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md)
### 分布式锁
- [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md)
@ -84,6 +87,13 @@
- [电商网站详情页系统架构](/docs/high-availability/e-commerce-website-detail-page-architecture.md)
- [Hystrix 线程池技术实现资源隔离](/docs/high-availability/hystrix-thread-pool-isolation.md)
- [Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md)
- [Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md)
- [深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md)
- [基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md)
- [基于本地缓存的 fallback 降级机制](/docs/high-availability/hystrix-fallback.md)
- [深入 Hystrix 断路器执行原理](/docs/high-availability/hystrix-circuit-breaker.md)
- [深入 Hystrix 线程池隔离与接口限流](/docs/high-availability/hystrix-thread-pool-current-limiting.md)
- [基于 timeout 机制为服务接口调用超时提供安全保护](/docs/high-availability/hystrix-timeout.md)
### 高可用系统
- 如何设计一个高可用系统?
@ -96,4 +106,18 @@
- 熔断框架都有哪些?具体实现原理知道吗?
### 降级
- 如何进行降级?
- 如何进行降级?
## 微服务架构
- [微服务架构整个章节内容属额外新增,后续抽空更新,也欢迎读者们参与补充完善](https://github.com/doocs/advanced-java)
- [关于微服务架构的描述](/docs/micro-services/microservices-introduction.md)
### Spring Cloud 微服务架构
- 什么是微服务?微服务之间是如何独立通讯的?
- Spring Cloud 和 Dubbo 有哪些区别?
- Spring Boot 和 Spring Cloud谈谈你对它们的理解
- 什么是服务熔断?什么是服务降级?
- 微服务的优缺点分别是什么?说一下你在项目开发中碰到的坑?
- 你所知道的微服务技术栈都有哪些?
- Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别?
- ......

90
SUMMARY.md Normal file
View File

@ -0,0 +1,90 @@
# 目录
* Part1 高并发架构
* [1.1 消息队列](/docs/high-concurrency/mq-interview.md)
* [1.1.1 为什么使用消息队列消息队列有什么优点和缺点Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](/docs/high-concurrency/why-mq.md)
* [1.1.2 如何保证消息队列的高可用?](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)
* [1.1.3 如何保证消息不被重复消费?(如何保证消息消费的幂等性)](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)
* [1.1.4 如何保证消息的可靠性传输?(如何处理消息丢失的问题)](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md)
* [1.1.5 如何保证消息的顺序性?](/docs/high-concurrency/how-to-ensure-the-order-of-messages.md)
* [1.1.6 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](/docs/high-concurrency/mq-time-delay-and-expired-failure.md)
* [1.1.7 如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。](/docs/high-concurrency/mq-design.md)
* [1.2 搜索引擎](/docs/high-concurrency/es-introduction.md)
* [1.2.1 es 的分布式架构原理能说一下么es 是如何实现分布式的啊)?](/docs/high-concurrency/es-architecture.md)
* [1.2.2 es 写入数据的工作原理是什么啊es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?](/docs/high-concurrency/es-write-query-search.md)
* [1.2.3 es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](/docs/high-concurrency/es-optimizing-query-performance.md)
* [1.2.4 es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](/docs/high-concurrency/es-production-cluster.md)
* 1.3 缓存
* [1.3.1 在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?](/docs/high-concurrency/why-cache.md)
* [1.3.2 Redis 和 Memcached 有什么区别Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?](/docs/high-concurrency/redis-single-thread-model.md)
* [1.3.3 Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?](/docs/high-concurrency/redis-data-types.md)
* [1.3.4 Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](/docs/high-concurrency/redis-expiration-policies-and-lru.md)
* [1.3.5 如何保证 Redis 高并发、高可用Redis 的主从复制原理能介绍一下么Redis 的哨兵原理能介绍一下么?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md)
* [1.3.6 Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](/docs/high-concurrency/redis-persistence.md)
* [1.3.7 Redis 集群模式的工作原理能说一下么在集群模式下Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](/docs/high-concurrency/redis-cluster.md)
* [1.3.8 了解什么是 redis 的雪崩、穿透和击穿Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
* [1.3.9 如何保证缓存与数据库的双写一致性?](/docs/high-concurrency/redis-consistence.md)
* [1.3.10 Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](/docs/high-concurrency/redis-cas.md)
* [1.3.11 生产环境中的 Redis 是怎么部署的?](/docs/high-concurrency/redis-production-environment.md)
* 1.4 分库分表
* [1.4.1 为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?](/docs/high-concurrency/database-shard.md)
* [1.4.2 现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?](/docs/high-concurrency/database-shard-method.md)
* [1.4.3 如何设计可以动态扩容缩容的分库分表方案?](/docs/high-concurrency/database-shard-dynamic-expand.md)
* [1.4.4 分库分表之后id 主键如何处理?](/docs/high-concurrency/database-shard-global-id-generate.md)
* 1.5 读写分离
* [1.5.1 如何实现 MySQL 的读写分离MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](/docs/high-concurrency/mysql-read-write-separation.md)
* 1.6 高并发系统
* [1.6.1 如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md)
* Part2 分布式系统
* [2.1 面试连环炮](/docs/distributed-system/distributed-system-interview.md)
* 2.2 系统拆分
* [2.2.1 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md)
* 2.3 分布式服务框架
* [2.3.1 说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](/docs/distributed-system/dubbo-operating-principle.md)
* [2.3.2 Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构PB 知道吗?为什么 PB 的效率是最高的?](/docs/distributed-system/dubbo-serialization-protocol.md)
* [2.3.3 Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](/docs/distributed-system/dubbo-load-balancing.md)
* [2.3.4 Dubbo 的 spi 思想是什么?](/docs/distributed-system/dubbo-spi.md)
* [2.3.5 如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
* [2.3.6 分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
* [2.3.7 分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
* [2.3.8 如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md)
* 2.4 分布式锁
* [2.4.1 Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md)
* [2.4.2 使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md)
* 2.5 分布式事务
* [2.5.1 分布式事务了解吗你们如何解决分布式事务问题的TCC 如果出现网络连不通怎么办XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md)
* 2.6 分布式会话
* [2.6.1 集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
* Part3 高可用架构
* 3.1 Hystrix
* [3.1.1 Hystrix 介绍](/docs/high-availability/hystrix-introduction.md)
* [3.1.2 电商网站详情页系统架构](/docs/high-availability/e-commerce-website-detail-page-architecture.md)
* [3.1.3 Hystrix 线程池技术实现资源隔离](/docs/high-availability/hystrix-thread-pool-isolation.md)
* [3.1.4 Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md)
* [3.1.5 Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md)
* [3.1.6 深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md)
* [3.1.7 基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md)
* [3.1.8 基于本地缓存的 fallback 降级机制](/docs/high-availability/hystrix-fallback.md)
* [3.1.9 深入 Hystrix 断路器执行原理](/docs/high-availability/hystrix-circuit-breaker.md)
* [3.1.10 深入 Hystrix 线程池隔离与接口限流](/docs/high-availability/hystrix-thread-pool-current-limiting.md)
* [3.1.11 基于 timeout 机制为服务接口调用超时提供安全保护](/docs/high-availability/hystrix-timeout.md)
* 3.2 高可用系统
* 3.2.1 如何设计一个高可用系统?
* 3.3 限流
* 3.3.1 如何限流?在工作中是怎么做的?说一下具体的实现?
* 3.4 熔断
* 3.4.1 如何进行熔断?
* 3.4.2 熔断框架都有哪些?具体实现原理知道吗?
* 3.5 降级
* 3.5.1 如何进行降级?
* Part4 微服务架构
* [4.1 关于微服务架构的描述](/docs/micro-services/microservices-introduction.md)
* 4.2 Spring Cloud 微服务架构
* 4.2.1 什么是微服务?微服务之间是如何独立通讯的?
* 4.2.2 Spring Cloud 和 Dubbo 有哪些区别?
* 4.2.3 Spring Boot 和 Spring Cloud谈谈你对它们的理解
* 4.2.4 什么是服务熔断?什么是服务降级?
* 4.2.5 微服务的优缺点分别是什么?说一下你在项目开发中碰到的坑?
* 4.2.6 你所知道的微服务技术栈都有哪些?
* 4.2.7 Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别?
* 4.2.8 ......

View File

@ -1,8 +1,9 @@
![logo](img/icon.png)
[![logo](images/icon.png)](https://github.com/doocs/advanced-java)
# Java 进阶面试
# Java 进阶扫盲
> 高并发 分布式 高可用
> 高并发 分布式 高可用 微服务
[GitHub](https://github.com/doocs/advanced-java/)
[Doocs](https://github.com/doocs/intro)
[GitHub](https://github.com/yanglbme)
[Get Started](#互联网-java-工程师进阶知识完全扫盲)

View File

@ -2,9 +2,13 @@
* [高并发](/README#高并发架构)
* [分布式](/README#分布式系统)
* [高可用](/README#高可用架构)
* [微服务](/README#微服务架构)
* [读者分享](/docs/from-readers/README)
* 页面
* [封面]()
* [首页](README)
* [GitHub](https://github.com/yanglbme)
* [Offer](offer)
* [进阶](advanced)
* [Offer](offer)
* [Doocs](https://github.com/doocs/intro)
* [GitHub](https://github.com/yanglbme)

72
advanced.md Normal file
View File

@ -0,0 +1,72 @@
<p align="center"><iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width=330 height=86 src="//music.163.com/outchain/player?type=2&id=1334849028&auto=1&height=66"></iframe></p>
<p align="center">本单曲受版权保护,可<a href="https://music.163.com/#/mv?id=10859500">点击观看 MV</a></p>
> 受伤的得到疗愈,挣扎的得到出口<br>
*Let those who hurt heal, let those who struggle find hope*.
```
告别的时刻已到了
随身的行囊 装些快乐
前方被乌云笼罩了
心中的拉扯 困兽在低吼
隐隐约约的沉默
透露一丝丝苦涩
愿你不再被伤透
别再带着泪入睡
曲曲折折的世界
答案不一定绝对
伤口因为爱壮烈
让我们同步进阶
重生的力量来自真我
战胜可敬的对手 yeah~
坚持信念是我的所有
抵达心中的辽阔 yeah~
有失去的 该进阶的
有遗憾的 该进阶的
放不下的 该进阶的 yeah~
圣所的光芒
指引方向 oh~
跨越了浩劫
曙光渐亮 oh~
希望因挣扎破灭了
再别无所求 于是自由
逆着风孤独的英雄
也渴望停泊 可说不出口
隐隐约约的沉默
透露一丝丝苦涩
愿你不再被伤透
别再带着泪入睡
曲曲折折的世界
答案不一定绝对
伤口因为爱壮烈
让我们同步进阶
重生的力量来自真我
战胜可敬的对手 yeah~
坚持信念是我的所有
抵达心中的辽阔 yeah~
有失去的 该进阶的
有遗憾的 该进阶的
放不下的 该进阶的 yeah~
圣所的光芒
指引方向 oh~
跨越了浩劫
曙光渐亮 oh~
```

View File

@ -0,0 +1 @@
# 分布式系统

View File

@ -2,7 +2,7 @@
一般实现分布式锁都有哪些方式?使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
## 面试官心理分析
其实一般问问题,都是这么问的,先问问你 zk然后其实是要过度到 zk 关联的一些问题里去,比如分布式锁。因为在分布式系统开发中,分布式锁的使用场景还是很常见的。
其实一般问问题,都是这么问的,先问问你 zk然后其实是要过渡到 zk 相关的一些问题里去,比如分布式锁。因为在分布式系统开发中,分布式锁的使用场景还是很常见的。
## 面试题剖析
### redis 分布式锁
@ -17,10 +17,11 @@
#### redis 最普通的分布式锁
第一个最普通的实现方式,就是在 redis 里创建一个 key这样就算加锁。
第一个最普通的实现方式,就是在 redis 里使用 `setnx` 命令创建一个 key这样就算加锁。
```r
SET my:lock 随机值 NX PX 30000
SET resource_name my_random_value NX PX 30000
```
执行这个命令就 ok。
@ -39,7 +40,7 @@ else
end
```
为啥要用随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s此时可能已经自动释放锁了此时可能别的客户端已经获取到了这个锁要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 `lua` 脚本来释放锁。
为啥要用 `random_value` 随机值呢?因为如果某个客户端获取到了锁,但是阻塞了很长时间才执行完,比如说超过了 30s此时可能已经自动释放锁了此时可能别的客户端已经获取到了这个锁要是你这个时候直接删除 key 的话会有问题,所以得用随机值加上面的 `lua` 脚本来释放锁。
但是这样是肯定不行的。因为如果是普通的 redis 单实例,那就是单点故障。或者是 redis 普通主从,那 redis 主从异步复制如果主节点挂了key 就没有了key 还没同步到从节点,此时从节点切换为主节点,别人就可以 set key从而拿到锁。
@ -53,7 +54,9 @@ end
5. 要是锁建立失败了,那么就依次之前建立过的锁删除;
6. 只要别人建立了一把分布式锁,你就得**不断轮询去尝试获取锁**。
![redis-redlock](/img/redis-redlock.png)
![redis-redlock](/images/redis-redlock.png)
[Redis 官方](https://redis.io/)给出了以上两种基于 Redis 实现分布式锁的方法详细说明可以查看https://redis.io/topics/distlock 。
### zk 分布式锁

View File

@ -2,23 +2,26 @@
集群部署时的分布式 session 如何实现?
## 面试官心理分析
面试官问了你一堆 dubbo 是怎么玩儿的,你会玩儿 dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式session**。
面试官问了你一堆 dubbo 是怎么玩儿的,你会玩儿 dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式 session**。
当然了,分布式系统中的问题何止这么一点,非常之多,复杂度很高,但是这里就是说下常见的几个,也是面试的时候常问的几个。
当然了,分布式系统中的问题何止这么一点,非常之多,复杂度很高,这里只是说一下常见的几个问题,也是面试的时候常问的几个。
## 面试题剖析
session 是啥?浏览器有个 cookie在一段时间内这个 cookie 都存在,然后每次发请求过来都带上一个特殊的 `jsessionid cookie`,就根据这个东西,在服务端可以维护一个对应的 session 域,里面可以放点数据。
一般只要你没关掉浏览器cookie 还在,那么对应的那个 session 就在,但是如果 cookie 没了session 也就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。
一般的话只要你没关掉浏览器cookie 还在,那么对应的那个 session 就在,但是如果 cookie 没了session 也就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。
这个不多说了,懂 Java 的都该知道这个。
单块系统的时候这么玩儿 session 没问题但是你要是分布式系统呢那么多的服务session 状态在哪儿维护啊?
其实方法很多,但是常见常用的是两种:
其实方法很多,但是常见常用的是以下几种:
### 完全不用 session
使用 JWT Token 储存用户身份,然后再从数据库或者 cache 中获取其他的信息。这样无论请求分配到哪个服务器都无所谓。
### tomcat + redis
这个其实还挺方便的,就是使用 session 的代码跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署得 tomcat 都将 session 数据存储到 redis 即可。
这个其实还挺方便的,就是使用 session 的代码跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署 tomcat 都将 session 数据存储到 redis 即可。
在 tomcat 的配置文件中配置:
@ -45,12 +48,11 @@ session 是啥?浏览器有个 cookie在一段时间内这个 cookie 都存
还可以用上面这种方式基于 redis 哨兵支持的 redis 高可用集群来保存 session 数据,都是 ok 的。
### spring session + redis
第一种方式会与 tomcat 容器重耦合,如果我要将 web 容器迁移成 jetty难道还要重新把 jetty 都配置一遍?
上面所说的第二种方式会与 tomcat 容器重耦合,如果我要将 web 容器迁移成 jetty难道还要重新把 jetty 都配置一遍?
因为上面那种 tomcat + redis 的方式好用,但是会**严重依赖于web容器**,不好将代码移植到其他 web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 spring cloud 或者是 spring boot 之类的呢?
所以现在比较好的还是基于 Java 一站式解决方案,也就是 spring。人家 spring 基本上包了大部分我们需要使用的框架spirng cloud 做微服务spring boot 做脚手架,所以用 sping session 是一个很好的选择。
所以现在比较好的还是基于 Java 一站式解决方案,也就是 spring。人家 spring 基本上包了大部分我们需要使用的框架spirng cloud 做微服务spring boot 做脚手架,所以用 sping session 是一个很好的选择。
在 pom.xml 中配置:
```xml
@ -103,20 +105,17 @@ session 是啥?浏览器有个 cookie在一段时间内这个 cookie 都存
示例代码:
```java
@Controller
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/putIntoSession")
@ResponseBody
public String putIntoSession(HttpServletRequest request, String username) {
request.getSession().setAttribute("name", “leo”);
request.getSession().setAttribute("name", "leo");
return "ok";
}
@RequestMapping("/getFromSession")
@ResponseBody
public String getFromSession(HttpServletRequest request, Model model){
String name = request.getSession().getAttribute("name");
return name;
@ -127,4 +126,4 @@ public class TestController {
上面的代码就是 ok 的,给 sping session 配置基于 redis 来存储 session 数据,然后配置了一个 spring session 的过滤器这样的话session 相关操作都会交给 spring session 来管了。接着在代码中,就用原生的 session 操作,就是直接基于 spring sesion 从 redis 中获取数据了。
实现分布式的会话,有很多种很多种方式,我说的只不过比较常见的两种方式tomcat + redis 早期比较常用,但是会重耦合到 tomcat 中;近些年,通过 spring session 来实现。
实现分布式的会话有很多种方式,我说的只不过是比较常见的几种方式tomcat + redis 早期比较常用,但是会重耦合到 tomcat 中;近些年,通过 spring session 来实现。

View File

@ -6,7 +6,7 @@
一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?
你看,假如你有个服务提供一个接口,结果这服务部署在了 5 台机器上,接着有个接口就是**付款接口**。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单**不小心发起了两次支付请求**,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。
你看,假如你有个服务提供一些接口供外部调用,这个服务部署在了 5 台机器上,接着有个接口就是**付款接口**。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单**不小心发起了两次支付请求**,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。
或者是订单系统调用支付系统进行支付,结果不小心因为**网络超时**了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。

View File

@ -3,7 +3,7 @@
分布式业务系统,就是把原来用 Java 开发的一个大块系统,给拆分成**多个子系统**,多个子系统之间互相调用,形成一个大系统的整体。假设原来你做了一个 OA 系统里面包含了权限模块、员工模块、请假模块、财务模块一个工程里面包含了一堆模块模块与模块之间会互相去调用1 台机器部署。现在如果你把这个系统给拆开,权限系统、员工系统、请假系统、财务系统 4 个系统4 个工程,分别在 4 台机器上部署。一个请求过来完成这个请求这个员工系统调用权限系统调用请假系统调用财务系统4 个系统分别完成了一部分的事情,最后 4 个系统都干完了以后,才认为是这个请求已经完成了。
![simple-distributed-system-oa](/img/simple-distributed-system-oa.png)
![simple-distributed-system-oa](/images/simple-distributed-system-oa.png)
> 这两年开始兴起和流行 Spring Cloud刚流行还没开始普及目前普及的是 dubbo因此这里也主要讲 dubbo。

View File

@ -8,13 +8,12 @@
所以这都是分布式系统一些很常见的问题。
## 面试题剖析
首先,一般来说,个人建议是,你们从业务逻辑上设计的这个系统最好是不需要这种顺序性的保证,因为一旦引入顺序性保障,比如使用**分布式锁**,会**导致系统复杂度上升**,而且会带来**效率低下**,热点数据压力过大等问题。
下面我给个我们用过的方案吧,简单来说,首先你得用 dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个**内存队列**里去,强制排队,这样来确保他们的顺序性。
![distributed-system-request-sequence](/img/distributed-system-request-sequence.png)
![distributed-system-request-sequence](/images/distributed-system-request-sequence.png)
但是这样引发的后续问题就很多,比如说要是某个订单对应的请求特别多,造成某台机器成**热点**怎么办?解决这些问题又要开启后续一连串的复杂技术方案......曾经这类问题弄的我们头疼不已,所以,还是建议什么呢?

View File

@ -18,18 +18,18 @@
### 两阶段提交方案/XA方案
所谓的 XA 方案,即:两阶段提交,有一个**事务管理器**的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok那么就正式提交事务在各个数据库上执行操作如果任何其中一个数据库回答不 ok那么就回滚事务。
这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。如果要玩儿,那么基于 `spring + JTA` 就可以搞定,自己随便搜个 demo 看看就知道了。
这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。如果要玩儿,那么基于 `Spring + JTA` 就可以搞定,自己随便搜个 demo 看看就知道了。
这个方案,我们很少用,一般来说**某个系统内部如果出现跨多个库**的这么一个操作,是**不合规**的。我可以给大家介绍一下, 现在微服务,一个大的系统分成几百个服务,几十个服务。一般来说,我们的规定和规范,是要求**每个服务只能操作自己对应的一个数据库**。
这个方案,我们很少用,一般来说**某个系统内部如果出现跨多个库**的这么一个操作,是**不合规**的。我可以给大家介绍一下, 现在微服务,一个大的系统分成几十个甚至几百个服务。一般来说,我们的规定和规范,是要求**每个服务只能操作自己对应的一个数据库**。
如果你要操作别的服务对应的库,不允许直连别的服务的库,违反微服务架构的规范,你随便交叉胡乱访问,几百个服务的话,全体乱套,这样的一套服务是没法管理的,没法治理的,可能会出现数据被别人改错,自己的库被别人写挂等情况。
如果你要操作别人的服务的库,你必须是通过**调用别的服务的接口**来实现,绝对不允许交叉访问别人的数据库。
![distributed-transacion-XA](/img/distributed-transaction-XA.png)
![distributed-transacion-XA](/images/distributed-transaction-XA.png)
### TCC 方案
TCC 的全称是Try、Confirm、Cancel。
TCC 的全称是:`Try``Confirm``Cancel`
- Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行**锁定或者预留**。
- Confirm 阶段:这个阶段说的是在各个服务中**执行实际的操作**。
@ -41,9 +41,9 @@ TCC 的全称是Try、Confirm、Cancel。
而且最好是你的各个业务执行的时间都比较短。
但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护。
但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码很难维护
![distributed-transacion-TCC](/img/distributed-transaction-TCC.png)
![distributed-transacion-TCC](/images/distributed-transaction-TCC.png)
### 本地消息表
本地消息表其实是国外的 ebay 搞出来的这么一套思想。
@ -57,9 +57,9 @@ TCC 的全称是Try、Confirm、Cancel。
5. 如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
6. 这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。
这个方案说实话最大的问题就在于**严重依赖于数据库的消息表来管理事务**啥的,会导致如果是高并发场景咋办呢?咋扩展呢?所以一般确实很少用。
这个方案说实话最大的问题就在于**严重依赖于数据库的消息表来管理事务**啥的,如果是高并发场景咋办呢?咋扩展呢?所以一般确实很少用。
![distributed-transaction-local-message-table](/img/distributed-transaction-local-message-table.png)
![distributed-transaction-local-message-table](/images/distributed-transaction-local-message-table.png)
### 可靠消息最终一致性方案
这个的意思,就是干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。
@ -73,7 +73,7 @@ TCC 的全称是Try、Confirm、Cancel。
5. 这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。
6. 这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQRabbitMQ自己封装一套类似的逻辑出来总之思路就是这样子的。
![distributed-transaction-reliable-message](/img/distributed-transaction-reliable-message.png)
![distributed-transaction-reliable-message](/images/distributed-transaction-reliable-message.png)
### 最大努力通知方案
这个方案的大致意思就是:
@ -83,7 +83,7 @@ TCC 的全称是Try、Confirm、Cancel。
3. 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B反复 N 次,最后还是不行就放弃。
### 你们公司是如何处理分布式事务的?
如果你真的被问到,可以这么说,我们某某特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。
如果你真的被问到,可以这么说,我们某某特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。
你找一个严格资金要求绝对不能错的场景,你可以说你是用的 TCC 方案;如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。

View File

@ -5,54 +5,81 @@ dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略
继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议,还得知道具体用 dubbo 的时候,如何负载均衡,如何高可用,如何动态代理。
说白了,就是看你对 dubbo 熟悉不熟悉:
- dubbo 工作原理:服务注册,注册中心,消费者,代理通信,负载均衡
- 网络通信、序列化dubbo协议长连接NIOhessian 序列化协议
- 负载均衡策略集群容错策略动态代理策略dubbo 跑起来的时候一些功能是如何运转的,怎么做负载均衡?怎么做集群容错?怎么生成动态代理?
- dubbo SPI机制你了解不了解 dubbo 的 SPI 机制?如何基于 SPI 机制对 dubbo 进行扩展?
- dubbo 工作原理:服务注册、注册中心、消费者、代理通信、负载均衡;
- 网络通信、序列化dubbo 协议、长连接、NIO、hessian 序列化协议;
- 负载均衡策略、集群容错策略、动态代理策略dubbo 跑起来的时候一些功能是如何运转的?怎么做负载均衡?怎么做集群容错?怎么生成动态代理?
- dubbo SPI 机制:你了解不了解 dubbo 的 SPI 机制?如何基于 SPI 机制对 dubbo 进行扩展?
## 面试题剖析
### dubbo 负载均衡策略
#### random loadbalance
默认情况下dubbo 是 random load balance **随机**调用实现负载均衡,可以对 provider 不同实例**设置不同的权重**,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
默认情况下dubbo 是 random load balance ,即**随机**调用实现负载均衡,可以对 provider 不同实例**设置不同的权重**,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
#### roundrobin loadbalance
这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。
举个栗子。
跟运维同学申请机器有的时候我们运气好正好公司资源比较充足刚刚有一批热气腾腾、刚刚做好的一批虚拟机新鲜出炉配置都比较高。8核+16g机器2 台。过了一段时间,我感觉 2 台机器有点不太够,我去找运维同学,哥儿们,你能不能再给我 1 台机器4核+8G的机器。我还是得要。
这个时候,可以给两台 8核16g 的机器设置权重 4给剩余 1 台 4核8G 的机器设置权重 2。
跟运维同学申请机器有的时候我们运气好正好公司资源比较充足刚刚有一批热气腾腾、刚刚做好的虚拟机新鲜出炉配置都比较高8 核 + 16G 机器,申请到 2 台。过了一段时间,我们感觉 2 台机器有点不太够,我就去找运维同学说,“哥儿们,你能不能再给我一台机器”,但是这时只剩下一台 4 核 + 8G 的机器。我要还是得要。
这个时候,可以给两台 8 核 16G 的机器设置权重 4给剩余 1 台 4 核 8G 的机器设置权重 2。
#### leastactive loadbalance
这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给**不活跃的性能差的机器更少的请求**。
#### consistanthash loadbalance
一致性 Hash 算法,相同参数的请求一定分发到一个 provider 上去provider 挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。**如果你需要的不是随机负载均衡**,是要一类请求都到一个节点,那就走这个一致性 hash 策略。
一致性 Hash 算法,相同参数的请求一定分发到一个 provider 上去provider 挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。**如果你需要的不是随机负载均衡**,是要一类请求都到一个节点,那就走这个一致性 Hash 策略。
### dubbo 集群容错策略
#### failover cluster 模式
失败自动切换,自动重试其他机器,默认就是这个,常见于读操作。(失败重试其它机器)
失败自动切换,自动重试其他机器,**默认**就是这个,常见于读操作。(失败重试其它机器)
#### failfast cluster模式
一次调用失败就立即失败,常见于写操作。(调用失败就立即失败)
可以通过以下几种方式配置重试次数:
```xml
<dubbo:service retries="2" />
```
或者
```xml
<dubbo:reference retries="2" />
```
或者
```xml
<dubbo:reference>
<dubbo:method name="findFoo" retries="2" />
</dubbo:reference>
```
#### failfast cluster 模式
一次调用失败就立即失败,常见于非幂等性的写操作,比如新增一条记录(调用失败就立即失败)
#### failsafe cluster 模式
出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。
配置示例如下:
```xml
<dubbo:service cluster="failsafe" />
```
或者
```xml
<dubbo:reference cluster="failsafe" />
```
#### failback cluster 模式
失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。
#### forking cluster 模式
**并行调用**多个 provider只要一个成功就立即返回。
**并行调用**多个 provider只要一个成功就立即返回。常用于实时性要求比较高的读操作,但是会浪费更多的服务资源,可通过 `forks="2"` 来设置最大并行数。
#### broadcacst cluster
逐个调用所有的 provider。
逐个调用所有的 provider。任何一个 provider 出错则报错(从`2.1.0` 版本开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
### dubbo动态代理策略
默认使用 javassist 动态字节码生成,创建代理类。
但是可以通过 spi 扩展机制配置自己的动态代理策略。
默认使用 javassist 动态字节码生成,创建代理类。但是可以通过 spi 扩展机制配置自己的动态代理策略。

View File

@ -2,20 +2,20 @@
说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程?
## 面试官心理分析
MQ、ES、Redis、Dubbo上来先问你一些思考的问题,原理(kafka 高可用架构原理、es 分布式架构原理、redis 线程模型原理、Dubbo 工作原理生产环境里可能会碰到的一些问题每种技术引入之后生产环境都可能会碰到一些问题系统设计设计MQ设计搜索引擎设计一个缓存设计 rpc 框架)
MQ、ES、Redis、Dubbo上来先问你一些**思考性的问题**、**原理**,比如 kafka 高可用架构原理、es 分布式架构原理、redis 线程模型原理、Dubbo 工作原理;之后就是生产环境里可能会碰到的一些问题,因为每种技术引入之后生产环境都可能会碰到一些问题;再来点综合的,就是系统设计,比如让你设计一个 MQ、设计一个搜索引擎、设计一个缓存、设计一个 rpc 框架等等。
那既然开始聊分布式系统了,自然重点先聊聊 dubbo 了,毕竟 dubbo 是目前事实上大部分公司的分布式系统的 rpc 框架标准,基于 dubbo 也可以构建一整套的微服务架构。但是需要自己大量开发。
当然去年开始 spring cloud 非常火现在大量的公司开始转向spring cloud了spring cloud 人家毕竟是微服务架构的全家桶式的这么一个东西。但是因为很多公司还在用 dubbo所以 dubbo 肯定会是目前面试的重点,何况人家 dubbo 现在重启开源社区维护了,未来应该也还是有一定市场和地位的。
当然去年开始 spring cloud 非常火,现在大量的公司开始转向 spring cloud spring cloud 人家毕竟是微服务架构的全家桶式的这么一个东西。但是因为很多公司还在用 dubbo所以 dubbo 肯定会是目前面试的重点,何况人家 dubbo 现在重启开源社区维护了,捐献给了 apache,未来应该也还是有一定市场和地位的。
既然聊 dubbo那肯定是先从 dubbo 原理开始聊了,你先说说 dubbo 支撑 rpc分布式调用的架构啥的然后说说一次 rpc 请求 dubbo 是怎么给你完成的,对吧。
既然聊 dubbo那肯定是先从 dubbo 原理开始聊了,你先说说 dubbo 支撑 rpc 分布式调用的架构啥的,然后说说一次 rpc 请求 dubbo 是怎么给你完成的,对吧。
## 面试题剖析
### dubbo 工作原理
- 第一层service 层,接口层,给服务提供者和消费者来实现的
- 第二层config 层,配置层,主要是对 dubbo 进行各种配置的
- 第三层proxy 层,服务代理层,无论是 consumer 还是 providerdubbo 都会给你生成代理,代理之间进行网络通信
- 第四层register 层,服务注册层,负责服务的注册与发现
- 第四层registry 层,服务注册层,负责服务的注册与发现
- 第五层cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
- 第六层monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
- 第七层protocal 层,远程调用层,封装 rpc 调用
@ -29,7 +29,7 @@ MQ、ES、Redis、Dubbo上来先问你一些思考的问题原理kafka
- 第三步consumer 调用 provider
- 第四步consumer 和 provider 都异步通知监控中心
![dubbo-operating-principle](/img/dubbo-operating-principle.png)
![dubbo-operating-principle](/images/dubbo-operating-principle.png)
### 注册中心挂了可以继续通信吗?
可以,因为刚开始初始化的时候,消费者会将提供者的地址等信息**拉取到本地缓存**,所以注册中心挂了可以继续通信。

View File

@ -1,5 +1,5 @@
## 面试题
如何自己设计一个类似dubbo的rpc框架?
如何自己设计一个类似 Dubbo 的 RPC 框架?
## 面试官心理分析
说实话,就这问题,其实就跟问你如何自己设计一个 MQ 一样的道理,就考两个:
@ -12,7 +12,7 @@
所以我给大家一个建议,遇到这类问题,起码从你了解的类似框架的原理入手,自己说说参照 dubbo 的原理你来设计一下举个例子dubbo 不是有那么多分层么?而且每个分层是干啥的,你大概是不是知道?那就按照这个思路大致说一下吧,起码你不能懵逼,要比那些上来就懵,啥也说不出来的人要好一些。
举个栗子,我给大家说个最简单的回答思路:
- 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信,可以用 zookeeper 来做,对吧。
- 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信,可以用 zookeeper 来做,对吧。
- 然后你的消费者需要去注册中心拿对应的服务信息吧,对吧,而且每个服务可能会存在于多台机器上。
- 接着你就该发起一次请求了,咋发起?当然是基于动态代理了,你面向接口获取到一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址。
- 然后找哪个机器发送请求?那肯定得有个负载均衡算法了,比如最简单的可以随机轮询是不是。

View File

@ -9,7 +9,7 @@ dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian
## 面试题剖析
**序列化**,就是把数据结构或者是一些对象,转换为二进制串的过程,而**反序列化**是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
![serialize-deserialize](/img/serialize-deserialize.png)
![serialize-deserialize](/images/serialize-deserialize.png)
### dubbo 支持不同的通信协议
- dubbo 协议
@ -20,11 +20,11 @@ dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian
长连接,通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。
![dubbo-keep-connection](/img/dubbo-keep-connection.png)
![dubbo-keep-connection](/images/dubbo-keep-connection.png)
而短连接,每次要发送请求之前,需要先重新建立一次连接。
![dubbo-not-keep-connection](/img/dubbo-not-keep-connection.png)
![dubbo-not-keep-connection](/images/dubbo-not-keep-connection.png)
- rmi 协议
@ -68,6 +68,6 @@ Hessian 的对象序列化机制有 8 种原始类型:
- ref用来表示对共享对象的引用。
### 为什么 PB 的效率是最高的?
可能有一些同学比较习惯于 `JSON` or `XML` 数据存储格式,对于 `Protocal Buffer` 还比较陌生。`Protocal Buffer` 其实是 Google 出品的一种轻量并且高效的结构化数据存储格式,性能比 `JSON`、`XML` 要高很多。
可能有一些同学比较习惯于 `JSON` or `XML` 数据存储格式,对于 `Protocol Buffer` 还比较陌生。`Protocol Buffer` 其实是 Google 出品的一种轻量并且高效的结构化数据存储格式,性能比 `JSON`、`XML` 要高很多。
其实 PB 之所以性能如此好,主要得益于两个:**第一**,它使用 proto 编译器,自动进行序列化和反序列化,速度非常快,应该比 `XML``JSON` 快上了 `20~100` 倍;**第二**,它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。

View File

@ -8,7 +8,7 @@
**失败重试**,分布式系统中网络请求如此频繁,要是因为网络问题不小心失败了一次,是不是要重试?
**超时重试**同上,如果不小心网络慢一点,超时了,如何重试?
**超时重试**跟上面一样,如果不小心网络慢一点,超时了,如何重试?
## 面试题剖析
### 服务治理
@ -17,7 +17,7 @@
那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将**各个服务之间的依赖关系和调用链路生成出来**,做成一张图,显示出来,大家才可以看到对吧。
![dubbo-service-invoke-road](/img/dubbo-service-invoke-road.png)
![dubbo-service-invoke-road](/images/dubbo-service-invoke-road.png)
#### 2. 服务访问压力以及时长统计
需要自动统计**各个接口和服务之间的调用次数以及访问延时**,而且要分成两个级别。
@ -31,28 +31,23 @@
- 服务分层(避免循环依赖)
- 调用链路失败监控和报警
- 服务鉴权
- 每个服务的可用性的监控接口调用成功率几个999.99%99.9%99%
- 每个服务的可用性的监控(接口调用成功率?几个 999.99%99.9%99%
### 服务降级
比如说服务 A调用服务 B结果服务 B 挂掉了,服务 A 重试几次调用服务 B还是不行那么直接降级走一个备用的逻辑给用户返回响应。
比如说服务 A 调用服务 B结果服务 B 挂掉了,服务 A 重试几次调用服务 B还是不行那么直接降级走一个备用的逻辑给用户返回响应。
举个栗子,我们有接口 `HelloService`。`HelloServiceImpl` 有该接口的具体实现。
```java
public interface HelloService {
void sayHello();
}
public class HelloServiceImpl implements HelloService {
public void sayHello() {
System.out.println("hello world......");
}
}
```
```xml
@ -88,16 +83,14 @@ public class HelloServiceImpl implements HelloService {
我们调用接口失败的时候,可以通过 `mock` 统一返回 null。
mock 的值也可以修改为 true然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 `接口名称+Mock` 后缀。然后在 Mock 类里实现自己的降级逻辑。
mock 的值也可以修改为 true然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+`Mock`” 后缀。然后在 Mock 类里实现自己的降级逻辑。
```java
public class HelloServiceMock implements HelloService {
public void sayHello() {
// 降级逻辑
}
}
```
### 失败重试和超时重试
@ -109,9 +102,9 @@ public class HelloServiceMock implements HelloService {
举个栗子。
某个服务的接口,要耗费 5s你这边不能干等着你这边配置了 timeout之后我等待2s还没返回我直接就撤了不能干等你。
某个服务的接口,要耗费 5s你这边不能干等着你这边配置了 timeout 之后,我等待 2s还没返回我直接就撤了不能干等你。
可以结合你们公司具体的场景来说说你是怎么设置这些参数的:
- `timeout`:一般设置为 `200ms`,我们认为不能超过 `200ms`还没返回。
- `timeout`:一般设置为 `200ms`,我们认为不能超过 `200ms` 还没返回。
- `retries`:设置 retries一般是在读请求的时候比如你要查询个数据你可以设置个 retries如果第一次没读到报错重试指定的次数尝试再次读取。

View File

@ -12,7 +12,7 @@ spi简单来说就是 `service provider interface`,说白了是什么意
举个栗子。
你有一个接口A。A1/A2/A3 分别是接口A的不同实现。你通过配置 `接口A=实现A2`那么在系统实际运行的时候会加载你的配置用实现A2实例化一个对象来提供服务。
你有一个接口 A。A1/A2/A3 分别是接口A的不同实现。你通过配置 `接口 A = 实现 A2`,那么在系统实际运行的时候,会加载你的配置,用实现 A2 实例化一个对象来提供服务。
spi 机制一般用在哪儿?**插件扩展的场景**,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里面,从而扩展某个功能,这个时候 spi 思想就用上了。
@ -33,7 +33,7 @@ Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdapti
Protocol 接口在系统运行的时候dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。
它会去找一个你配置的 Protocol将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了
它会去找一个你配置的 Protocol将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了
上面那行代码就是 dubbo 里大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现好了,没问题。
@ -61,7 +61,7 @@ http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
```
所以说,这就看到了 dubbo 的 spi 机制默认是怎么玩儿的了,其实就是 Protocol 接口,`@SPI(“dubbo”)` 说的是,通过 SPI 机制来提供实现类,实现类是通过 dubbo 作为默认 key 去配置文件里找到的,配置文件名称与接口全限定名一样的,通过 dubbo 作为 key 可以找到默认的实现类就是 `com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol`
所以说,这就看到了 dubbo 的 spi 机制默认是怎么玩儿的了,其实就是 Protocol 接口,`@SPI("dubbo")` 说的是,通过 SPI 机制来提供实现类,实现类是通过 dubbo 作为默认 key 去配置文件里找到的,配置文件名称与接口全限定名一样的,通过 dubbo 作为 key 可以找到默认的实现类就是 `com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol`
如果想要动态替换掉默认的实现类,需要使用 `@Adaptive` 接口Protocol 接口中,有两个方法加了 `@Adaptive` 注解,就是说那俩接口会被代理实现。
@ -77,12 +77,13 @@ hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
然后自己搞一个 `dubbo provider` 工程,在这个工程里面依赖你自己搞的那个 jar然后在 spring 配置文件里给个配置:
```xml
<dubbo:protocol name=”my” port=”20000” />
```
provider 启动的时候,就会加载到我们 jar 包里的`my=com.bingo.MyProtocol` 这行配置里,接着会根据你的配置使用你定义好的 MyProtocol 了,这个就是简单说明一下,你通过上述方式,可以替换掉大量的 dubbo 内部的组件,就是扔个你自己的 jar 包,然后配置一下即可。
![dubbo-spi](/img/dubbo-spi.png)
![dubbo-spi](/images/dubbo-spi.png)
dubbo 里面提供了大量的类似上面的扩展点,就是说,你如果要扩展一个东西,只要自己写个 jar让你的 consumer 或者是 provider 工程,依赖你的那个 jar在你的 jar 里指定目录下配置好接口名称对应的文件,里面通过 `key=实现类`
然后对对应的组件,类似 `<dubbo:protocol>` 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。
然后对对应的组件,类似 `<dubbo:protocol>` 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -8,7 +8,7 @@
早些年,印象中在 2010 年初的时候,整个 IT 行业,很少有人谈分布式,更不用说微服务,虽然很多 BAT 等大型公司,因为系统的复杂性,很早就是分布式架构,大量的服务,只不过微服务大多基于自己搞的一套框架来实现而已。
但是确实,那个年代,大家很重视 ssh2很多中小型公司几乎大部分都是玩儿 struts2、spring、hibernate稍晚一些才进入了spring mvc、spring、mybatis 的组合。那个时候整个行业的技术水平就是那样,当年 oracle 很火oracle 管理员很吃香oracle 性能优化啥的都是 IT 男的大杀招啊。连大数据都没人提,当年 OCP、OCM 等认证培训机构,火的不行。
但是确实,那个年代,大家很重视 ssh2很多中小型公司几乎大部分都是玩儿 struts2、spring、hibernate稍晚一些才进入了 spring mvc、spring、mybatis 的组合。那个时候整个行业的技术水平就是那样,当年 oracle 很火oracle 管理员很吃香oracle 性能优化啥的都是 IT 男的大杀招啊。连大数据都没人提,当年 OCP、OCM 等认证培训机构,火的不行。
但是确实随着时代的发展,慢慢的,很多公司开始接受分布式系统架构了,这里面尤为对行业有至关重要影响的,是阿里的 dubbo**某种程度上而言,阿里在这里推动了行业技术的前进**。
@ -22,20 +22,19 @@
要是**不拆分**一个大系统几十万行代码20 个人维护一份代码,简直是悲剧啊。代码经常改着改着就冲突了,各种代码冲突和合并要处理,非常耗费时间;经常我改动了我的代码,你调用了我的,导致你的代码也得重新测试,麻烦的要死;然后每次发布都是几十万行代码的系统一起发布,大家得一起提心吊胆准备上线,几十万行代码的上线,可能每次上线都要做很多的检查,很多异常问题的处理,简直是又麻烦又痛苦;而且如果我现在打算把技术升级到最新的 spring 版本,还不行,因为这可能导致你的代码报错,我不敢随意乱改技术。
假设一个系统是 20 万行代码,其中 A 在里面改了 1000 行代码,但是此时发布的时候是这个 20 万行代码的大系统一块儿发布。就意味着 20 万上代码在线上就可能出现各种变化20 个人,每个人都要紧张地等在电脑面前,上线之后,检查日志,看自己负责的那一块儿有没有什么问题。
假设一个系统是 20 万行代码,其中 A 在里面改了 1000 行代码,但是此时发布的时候是这个 20 万行代码的大系统一块儿发布。就意味着 20 万上代码在线上就可能出现各种变化20 个人,每个人都要紧张地等在电脑面前,上线之后,检查日志,看自己负责的那一块儿有没有什么问题。
A 就检查了自己负责的 1 万行代码对应的功能确保ok就闪人了结果不巧的是A 上线的时候不小心修改了线上机器的某个配置,导致另外 B 和 C 负责的 2 万行代码对应的一些功能,出错了。
A 就检查了自己负责的 1 万行代码对应的功能,确保 ok 就闪人了结果不巧的是A 上线的时候不小心修改了线上机器的某个配置,导致另外 B 和 C 负责的 2 万行代码对应的一些功能,出错了。
几十个人负责维护一个几十万行代码的单块应用,每次上线,准备几个礼拜,上线 -> 部署 -> 检查自己负责的功能。
**拆分了以后**,整个世界清爽了,几十万行代码的系统,拆分成 20 个服务,平均每个服务就 1~2 万行代码每个服务部署到单独的机器上。20 个工程20 个 git 代码仓库20 个码农,每个人维护自己的那个服务就可以了,是自己独立的代码,跟别人没关系。再也没有代码冲突了,爽。每次就测试我自己的代码就可以了,爽。每次就发布我自己的一个小服务就可以了,爽。技术上想怎么升级就怎么升级,保持接口不变就可以了,爽。
**拆分了以后**,整个世界清爽了,几十万行代码的系统,拆分成 20 个服务,平均每个服务就 1~2 万行代码每个服务部署到单独的机器上。20 个工程20 个 git 代码仓库20 个开发人员,每个人维护自己的那个服务就可以了,是自己独立的代码,跟别人没关系。再也没有代码冲突了,爽。每次就测试我自己的代码就可以了,爽。每次就发布我自己的一个小服务就可以了,爽。技术上想怎么升级就怎么升级,保持接口不变就可以了,爽。
所以简单来说,一句话总结,如果是那种代码量多达几十万行的中大型项目,团队里有几十个人,那么如果不拆分系统,**开发效率极其低下**,问题很多。但是拆分系统之后,每个人就负责自己的一小部分就好了,可以随便玩儿随便弄。分布式系统拆分之后,可以大幅度提升复杂系统大型团队的开发效率。
但是同时,也要**提醒**的一点是,系统拆分成分布式系统之后,大量的分布式系统面临的问题也是接踵而来,所以后面的问题都是在**围绕分布式系统带来的复杂技术挑战**在说。
### 如何进行系统拆分?
这个问题说大可以很大,可以扯到领域驱动模型设计上去,说小了也很小,我不太想给大家太过于学术的说法,因为你也不可能背这个答案,过去了直接说吧。还是说的简单一点,大家自己到时候知道怎么回答就行了。
系统拆分为分布式系统,拆成多个服务,拆成微服务的架构,是需要拆很多轮的。并不是说上来一个架构师一次就给拆好了,而以后都不用拆。
@ -44,7 +43,7 @@
如果是多人维护一个服务最理想的情况下几十个人1 个人负责 1 个或 2~3 个服务;某个服务工作量变大了,代码量越来越多,某个同学,负责一个服务,代码量变成了 10 万行了他自己不堪重负他现在一个人拆开5 个服务1 个人顶着,负责 5 个人接着招人2 个人给那个同学带着3 个人负责 5 个服务,其中 2 个人每个人负责 2 个服务1 个人负责 1 个服务。
个人建议一个服务的代码不要太多1万行左右两三万撑死了吧。
个人建议一个服务的代码不要太多1 万行左右,两三万撑死了吧。
大部分的系统,是要进行**多轮拆分**的,第一次拆分,可能就是将以前的多个模块该拆分开来了,比如说将电商系统拆分成订单系统、商品系统、采购系统、仓储系统、用户系统,等等吧。
@ -53,7 +52,6 @@
扯深了实在很深,所以这里先给大家举个例子,你自己感受一下,**核心意思就是根据情况,先拆分一轮,后面如果系统更复杂了,可以继续分拆**。你根据自己负责系统的例子,来考虑一下就好了。
### 拆分后不用 dubbo 可以吗?
当然可以了,大不了最次,就是各个系统之间,直接基于 spring mvc就纯 http 接口互相通信呗,还能咋样。但是这个肯定是有问题的,因为 http 接口通信维护起来成本很高,你要考虑**超时重试**、**负载均衡**等等各种乱七八糟的问题,比如说你的订单系统调用商品系统,商品系统部署了 5 台机器,你怎么把请求均匀地甩给那 5 台机器?这不就是负载均衡?你要是都自己搞那是可以的,但是确实很痛苦。
所以 dubbo 说白了,是一种 rpc 框架,就是说本地就是进行接口调用,但是 dubbo 会代理这个调用请求,跟远程机器网络通信,给你处理掉负载均衡、服务实例上下线自动感知、超时重试了,等等乱七八糟的问题。那你就不用自己做了,用 dubbo 就可以了。
所以 dubbo 说白了,是一种 rpc 框架,就是说本地就是进行接口调用,但是 dubbo 会代理这个调用请求,跟远程机器网络通信,给你处理掉负载均衡、服务实例上下线自动感知、超时重试等等乱七八糟的问题。那你就不用自己做了,用 dubbo 就可以了。

View File

@ -4,12 +4,12 @@ zookeeper 都有哪些使用场景?
## 面试官心理分析
现在聊的 topic 是分布式系统,面试官跟你聊完了 dubbo 相关的一些问题之后,已经确认你对分布式服务框架/RPC框架基本都有一些认知了。那么他可能开始要跟你聊分布式相关的其它问题了。
分布式锁这个东西,很常用的,你做 Java系统开发分布式系统可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
分布式锁这个东西,很常用的,你做 Java 系统开发,分布式系统,可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
其实说实话,问这个问题,一般就是看看你是否了解 zookeeper因为 zk 是分布式系统中很常见的一个基础系统。而且问的话常问的就是说 zk 的使用场景是什么?看你知道不知道一些基本的使用场景。但是其实 zk 挖深了自然是可以问的很深很深的。
其实说实话,问这个问题,一般就是看看你是否了解 zookeeper因为 zookeeper 是分布式系统中很常见的一个基础系统。而且问的话常问的就是说 zookeeper 的使用场景是什么?看你知道不知道一些基本的使用场景。但是其实 zookeeper 挖深了自然是可以问的很深很深的。
## 面试题剖析
大致来说zk 的使用场景如下,我就举几个简单的,大家能说几个就好了:
大致来说zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:
- 分布式协调
- 分布式锁
@ -17,21 +17,21 @@ zookeeper 都有哪些使用场景?
- HA高可用性
### 分布式协调
这个其实是 zk 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zk 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zk 上**对某个节点的值注册个监听器**,一旦 B 系统处理完了就修改 zk 那个节点的值A 立马就可以收到通知,完美解决。
这个其实是 zookeeper 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zookeeper 上**对某个节点的值注册个监听器**,一旦 B 系统处理完了就修改 zookeeper 那个节点的值A 系统立马就可以收到通知,完美解决。
![zookeeper-distributed-coordination](/img/zookeeper-distributed-coordination.png)
![zookeeper-distributed-coordination](/images/zookeeper-distributed-coordination.png)
### 分布式锁
举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zk 分布式锁,一个机器接收到了请求之后先获取 zk 上的一把分布式锁,就是可以去创建一个 znode接着执行操作然后另外一个机器也**尝试去创建**那个 znode结果发现自己创建不了因为被别人创建了那只能等着等第一个机器执行完了自己再执行。
举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zookeeper 分布式锁,一个机器接收到了请求之后先获取 zookeeper 上的一把分布式锁,就是可以去创建一个 znode接着执行操作然后另外一个机器也**尝试去创建**那个 znode结果发现自己创建不了因为被别人创建了那只能等着等第一个机器执行完了自己再执行。
![zookeeper-distributed-lock-demo](/img/zookeeper-distributed-lock-demo.png)
![zookeeper-distributed-lock-demo](/images/zookeeper-distributed-lock-demo.png)
### 元数据/配置信息管理
zk 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zk 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zk 么?
zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zookeeper 么?
![zookeeper-meta-data-manage](/img/zookeeper-meta-data-manage.png)
![zookeeper-meta-data-manage](/images/zookeeper-meta-data-manage.png)
### HA高可用性
这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zk 来开发 HA 高可用机制,就是一个**重要进程一般会做主备**两个,主进程挂了立马通过 zk 感知到切换到备用进程。
这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制,就是一个**重要进程一般会做主备**两个,主进程挂了立马通过 zookeeper 感知到切换到备用进程。
![zookeeper-active-standby](/img/zookeeper-active-standby.png)
![zookeeper-active-standby](/images/zookeeper-active-standby.png)

View File

@ -0,0 +1,15 @@
# GitHub 开发者参与专区
[Doocs/advanced-java](https://github.com/doocs/advanced-java) 欢迎各位开发朋友们分享自己或他人的实践经验与总结。如果你想参与,请参考[提交注意事项](/docs/from-readers/doocs-advanced-java-attention.md)。感谢 [@jerryldh](https://github.com/jerryldh), [@BigBlackSheep](https://github.com/BigBlackSheep), [@sunyuanpinggithub](https://github.com/sunyuanpinggithub) 等多位朋友的反馈,具体请参考 [#46](https://github.com/doocs/advanced-java/issues/46)。
## Articles
- [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
- [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
## Contributors
This project exists thanks to all the people who contribute.
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<a href="https://github.com/doocs/leetcode/graphs/contributors"><img src="https://opencollective.com/advanced-java/contributors.svg?width=890&button=false" /></a>
<!-- ALL-CONTRIBUTORS-LIST:END -->

View File

@ -0,0 +1,52 @@
# 提交注意事项
项目需要有一个统一的内容提交规范,没有规范的项目将会是一团乱麻,维护起来也会很费劲儿。以下列出了几个小点,看似很多,实际上都非常容易做到,供朋友们参考。
> 如果你有好的 idea欢迎 issues 交流。
## 关于提交形式
本项目**不希望以外链的形式引入内容**。如果你有好的内容推荐,请在此项目基础上创建新的文件,完善内容后再提交。
## 关于文件命名与存放位置
文件请以 “`GitHub ID` + 文章主题” 命名,确保每位朋友的提交内容不会冲突。文章主题统一采用**英文**命名,请勿使用中文或者汉语拼音,文件类型统一选择 `.md`
给个示例。某位朋友的 GitHub ID 是 [SnailClimb](https://github.com/snailclimb),想分享一篇关于 Kafka 实践相关的文章,那么文件名可以是 `snailclimb-kafka-in-action.md`
最终文件存放于 `docs/from-readers/` 目录下,即与[本文件](/docs/from-readers/doocs-advanced-java-attention.md)处于同一级别。**文件命名、存放位置不规范的文章将不予采纳**。
## 关于文章内容
仅收录与此项目主题相关的优质文章,可以是[高并发](https://github.com/doocs/advanced-java#高并发架构)、[分布式](https://github.com/doocs/advanced-java#分布式系统)、[高可用](https://github.com/doocs/advanced-java#高可用架构)、[微服务](https://github.com/doocs/advanced-java#高并发架构微服务架构)等相关领域的内容。**其它主题的文章将不会被采纳**。
## 关于文章排版
文章排版保持整洁美观。中英文之间、中文与数字之间用空格隔开是最基本的。
> 有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦,有七成的比例会在 34 岁的时候跟自己不爱的人结婚,而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。
图片统一使用 `![](/images/xxx.png)` 进行相对路径的引用,并同时存放于根目录 `images/` 和本专区目录 `docs/from-readers/images/` **两个位置**之下(这是为了确保在 GitHub 和 GitHub Page 都能正常显示图片;图片并不限定 `.png` 格式),作图推荐使用在线工具 [ProcessOn](https://www.processon.com/i/594a16f7e4b0e1bb14fe2fac)。具体文章书写规范请参考《[中文技术文档的写作规范](https://github.com/ruanyf/document-style-guide)》。
以下是文章基本的结构,供朋友们参考。
```markdown
# 这是文章标题
- Author: [GitHub ID](https://github.com/your-github-id)
- Description: 文章的简单描述信息。
- ...
## 这是一级索引
...
### 这是二级索引
...
## 这是一级索引
...
### 这是二级索引
...
```
## 关于 Git 提交信息
Git 提交信息统一使用英文,本项目遵从 [Angular JS Git 提交规范](https://github.com/angular/angular.js/commits/master)。e.g.
```bash
git commit -m "docs(from-readers): add an article about Kafka"
```
Git 提交信息不规范的 PR 将不予合并。

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -0,0 +1,39 @@
<p align="center">
<a href="https://github.com/doocs/advanced-java"><img src="./images/advanced-java-doocs-shishan.png" alt="维权行动"></a>
</p>
## 声明
读者朋友们,你们好。[advanced-java](https://github.com/doocs/advanced-java) 项目自创建以来,一直收到很多读者的反馈,也在不断改进、完善内容,只希望可以用心做得更好。然而,网上抄袭、侵权的现象普遍存在,我想,不能任由这种恶劣行为肆虐。
因此,在此说明,除了 [doocs/advanced-java](https://github.com/doocs/advanced-java)、“**石杉的架构笔记**”,网上其它平台上如若出现了与本项目内容雷同甚至完全一致的文章,**不注明出处、甚至打着原创的标签忽悠读者**的,欢迎举报,也欢迎在此提供侵权名单,曝光抄袭者,谢谢。
希望各位朋友都注重**维护他人知识产权,尊重他人劳动成果**,我们共同构建一个健康的知识分享生态圈。
## 抄袭名单列表
### 博客
| # | 文章 | 抄袭者 |
|---|---|---|
| 1 | [MySQL 面试题](https://jsbintask.cn/2019/02/17/interview/interview-high-concurrency-design/) | jsbintask |
| 2 | [消息队列面试题](https://blog.51cto.com/13904503/2351522) | Java邵先生 |
| 3 | [高并发架构消息队列面试题解析](https://www.cnblogs.com/yuxiang1/p/10542569.html) | 手留余香-博客园 |
| 4 | [消息中间件面试题:消息中间件的高可用](https://www.jianshu.com/p/92862edc7c51) | jsbintask-简书 |
| 5 | [深入 Hystrix 执行时内部原理](https://www.jianshu.com/p/1a14401e219f) | kevin0016-简书 |
### 公众号
| # | 文章 | 抄袭者 |
|---|---|---|
### 头条号
| # | 文章 | 抄袭者 |
|---|---|---|
### 掘金
| # | 文章 | 抄袭者 |
|---|---|---|
### 知乎
| # | 文章 | 抄袭者 | 备注 |
|---|---|---|---|
| 1 | [Java消息队列三道面试题详解](https://zhuanlan.zhihu.com/p/62739616) | Java高级架构解析 | 严重抄袭 |

View File

@ -0,0 +1 @@
# 高可用架构

View File

@ -3,20 +3,35 @@
### 小型电商网站的商品详情页系统架构
小型电商网站的页面展示采用页面全量静态化的思想。数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入 Nginx 服务器。用户浏览网站页面时,取用一个已经静态化好的 html 页面,直接返回回去,不涉及任何的业务逻辑处理。
![e-commerce-website-detail-page-architecture-1](/img/e-commerce-website-detail-page-architecture-1.png)
![e-commerce-website-detail-page-architecture-1](/images/e-commerce-website-detail-page-architecture-1.png)
- 好处:用户每次浏览一个页面,不需要进行任何的跟数据库的交互逻辑,也不需要执行任何的代码,直接返回一个 html 页面就可以了,速度和性能非常高。
- 坏处:仅仅适用于一些小型的网站,比如页面的规模在几十到几万不等。对于一些大型的电商网站,亿级数量的页面,你说你每次页面模板修改了,都需要将这么多页面全量静态化,靠谱吗?
下面是页面模板的简单 Demo 。
```html
<html>
<body>
商品名称:#{productName}<br>
商品价格:#{productPrice}<br>
商品描述:#{productDesc}
</body>
</html>
```
这样做,**好处**在于,用户每次浏览一个页面,不需要进行任何的跟数据库的交互逻辑,也不需要执行任何的代码,直接返回一个 html 页面就可以了,速度和性能非常高。
对于小网站页面很少很实用非常简单Java 中可以使用 velocity、freemarker、thymeleaf 等等,然后做个 cms 页面内容管理系统,模板变更的时候,点击按钮或者系统自动化重新进行全量渲染。
**坏处**在于,仅仅适用于一些小型的网站,比如页面的规模在几十到几万不等。对于一些大型的电商网站,亿级数量的页面,你说你每次页面模板修改了,都需要将这么多页面全量静态化,靠谱吗?每次渲染花个好几天时间,那你整个网站就废掉了。
### 大型电商网站的商品详情页系统架构
大型电商网站商品详情页的系统设计中,当商品信息发生变更时,会将变更消息压入消息队列中。**缓存服务**从消息队列中消费此消息时,发现有信息发生变更,便通过调用接口,获取变更后的数据。将整合好的数据推送至 redis 中。Nginx 获取到最新的缓存数据,并且缓存到 Nginx 自己本地中。
大型电商网站商品详情页的系统设计中,当商品数据发生变更时,会将变更消息压入 MQ 消息队列中。**缓存服务**从消息队列中消费这条消息时,感知到有数据发生变更,便通过调用数据服务接口,获取变更后的数据,然后将整合好的数据推送至 redis 中。Nginx 本地缓存的数据是有一定的时间期限的,比如说 10 分钟,当数据过期之后,它就会从 redis 获取到最新的缓存数据,并且缓存到自己本地
用户浏览网页时,动态将 Nginx 本地数据渲染到本地 html 模板并返回给用户。
![e-commerce-website-detail-page-architecture-2](/img/e-commerce-website-detail-page-architecture-2.png)
![e-commerce-website-detail-page-architecture-2](/images/e-commerce-website-detail-page-architecture-2.png)
虽然没有直接返回 html 页面那么快,但是因为数据在本地缓存,所以也很快,其实耗费的也就是动态渲染一个 html 页面的性能。如果 html 模板发生了变更,不需要将所有的页面重新静态化,直接将数据渲染进最新的 html 页面模板后响应即可。
虽然没有直接返回 html 页面那么快,但是因为数据在本地缓存,所以也很快,其实耗费的也就是动态渲染一个 html 页面的性能。如果 html 模板发生了变更,不需要将所有的页面重新静态化,也不需要发送请求,没有网络请求的开销,直接将数据渲染进最新的 html 页面模板后响应即可。
在这种架构下,我们需要**保证系统的高可用性**。

View File

@ -0,0 +1,181 @@
## 深入 Hystrix 断路器执行原理
### RequestVolumeThreshold
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(int)
```
表示在滑动窗口中,至少有多少个请求,才可能触发断路。
Hystrix 经过断路器的流量超过了一定的阈值,才有可能触发断路。比如说,要求在 10s 内经过断路器的流量必须达到 20 个,而实际经过断路器的流量才 10 个,那么根本不会去判断要不要断路。
### ErrorThresholdPercentage
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerErrorThresholdPercentage(int)
```
表示异常比例达到多少,才会触发断路,默认值是 50(%)。
如果断路器统计到的异常调用的占比超过了一定的阈值,比如说在 10s 内,经过断路器的流量达到了 30 个,同时其中异常访问的数量也达到了一定的比例,比如 60% 的请求都是异常(报错 / 超时 / reject就会开启断路。
### SleepWindowInMilliseconds
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerSleepWindowInMilliseconds(int)
```
断路开启,也就是由 close 转换到 open 状态close -> open。那么之后在 `SleepWindowInMilliseconds` 时间内,所有经过该断路器的请求全部都会被断路,不调用后端服务,直接走 fallback 降级机制。
而在该参数时间过后,断路器会变为 `half-open` 半开闭状态,尝试让一条请求经过断路器,看能不能正常调用。如果调用成功了,那么就自动恢复,断路器转为 close 状态。
### Enabled
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerEnabled(boolean)
```
控制是否允许断路器工作,包括跟踪依赖服务调用的健康状况,以及对异常情况过多时是否允许触发断路。默认值是 `true`
### ForceOpen
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerForceOpen(boolean)
```
如果设置为 true 的话,直接强迫打开断路器,相当于是手动断路了,手动降级,默认值是 `false`
### ForceClosed
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerForceClosed(boolean)
```
如果设置为 true直接强迫关闭断路器相当于手动停止断路了手动升级默认值是 `false`
## 实例 Demo
### HystrixCommand 配置参数
在 GetProductInfoCommand 中配置 Setter 断路器相关参数。
- 滑动窗口中,最少 20 个请求,才可能触发断路。
- 异常比例达到 40% 时,才触发断路。
- 断路后 3000ms 内,所有请求都被 reject直接走 fallback 降级,不会调用 run() 方法。3000ms 过后,变为 half-open 状态。
run() 方法中,我们判断一下 productId 是否为 -1是的话直接抛出异常。这么写我们之后测试的时候就可以传入 productId=-1**模拟服务执行异常**了。
在降级逻辑中,我们直接给它返回降级商品就好了。
```java
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
public GetProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY)
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// 是否允许断路器工作
.withCircuitBreakerEnabled(true)
// 滑动窗口中,最少有多少个请求,才可能触发断路
.withCircuitBreakerRequestVolumeThreshold(20)
// 异常比例达到多少才触发断路默认50%
.withCircuitBreakerErrorThresholdPercentage(40)
// 断路后多少时间内直接reject请求之后进入half-open状态默认5000ms
.withCircuitBreakerSleepWindowInMilliseconds(3000)));
this.productId = productId;
}
@Override
protected ProductInfo run() throws Exception {
System.out.println("调用接口查询商品数据productId=" + productId);
if (productId == -1L) {
throw new Exception();
}
String url = "http://localhost:8081/getProductInfo?productId=" + productId;
String response = HttpClientUtils.sendGetRequest(url);
return JSONObject.parseObject(response, ProductInfo.class);
}
@Override
protected ProductInfo getFallback() {
ProductInfo productInfo = new ProductInfo();
productInfo.setName("降级商品");
return productInfo;
}
}
```
### 断路测试类
我们在测试类中,前 30 次请求,传入 productId=-1然后休眠 3s之后 70 次请求,传入 productId=1。
```java
@SpringBootTest
@RunWith(SpringRunner.class)
public class CircuitBreakerTest {
@Test
public void testCircuitBreaker() {
String baseURL = "http://localhost:8080/getProductInfo?productId=";
for (int i = 0; i < 30; ++i) {
// 传入-1会抛出异常然后走降级逻辑
HttpClientUtils.sendGetRequest(baseURL + "-1");
}
TimeUtils.sleep(3);
System.out.println("After sleeping...");
for (int i = 31; i < 100; ++i) {
// 传入1走服务正常调用
HttpClientUtils.sendGetRequest(baseURL + "1");
}
}
}
```
### 测试结果
测试结果,我们可以明显看出系统断路与恢复的整个过程。
```c
调用接口查询商品数据productId=-1
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
// ...
// 这里重复打印了 20 次上面的结果
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
// ...
// 这里重复打印了 8 次上面的结果
// 休眠 3s 后
调用接口查询商品数据productId=1
ProductInfo(id=1, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, specification=iphone7的规格, service=iphone7的售后服务, color=红色,白色,黑色, size=5.5, shopId=1, modifiedTime=2017-01-01 12:00:00, cityId=1, cityName=null, brandId=1, brandName=null)
// ...
// 这里重复打印了 69 次上面的结果
```
前 30 次请求,我们传入的 productId 为 -1所以服务执行过程中会抛出异常。我们设置了最少 20 次请求通过断路器并且异常比例超出 40% 就触发断路。因此执行了 21 次接口调用每次都抛异常并且走降级21 次过后,断路器就被打开了。
之后的 9 次请求,都不会执行 run() 方法,也就不会打印以下信息。
```c
调用接口查询商品数据productId=-1
```
而是直接走降级逻辑,调用 getFallback() 执行。
休眠了 3s 后,我们在之后的 70 次请求中,都传入 productId 为 1。由于我们前面设置了 3000ms 过后断路器变为 `half-open` 状态。因此 Hystrix 会尝试执行请求,发现成功了,那么断路器关闭,之后的所有请求也都能正常调用了。

View File

@ -0,0 +1,106 @@
## Hystrix 隔离策略细粒度控制
Hystrix 实现资源隔离,有两种策略:
- 线程池隔离
- 信号量隔离
对资源隔离这一块东西,其实可以做一定细粒度的一些控制。
### execution.isolation.strategy
指定了 HystrixCommand.run() 的资源隔离策略:`THREAD` or `SEMAPHORE`,一种基于线程池,一种基于信号量。
```java
// to use thread isolation
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD)
// to use semaphore isolation
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
```
线程池机制,每个 command 运行在一个线程中限流是通过线程池的大小来控制的信号量机制command 是运行在调用线程中,通过信号量的容量来进行限流。
如何在线程池和信号量之间做选择?
**默认的策略**就是线程池。
**线程池**其实最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住。
而使用信号量的场景,通常是针对超大并发量的场景下,每个服务实例每秒都几百的 `QPS`,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护。一般用信号量常见于那种基于纯内存的一些业务逻辑服务,而不涉及到任何网络访问请求。
### command key & command group
我们使用线程池隔离,要怎么对**依赖服务**、**依赖服务接口**、**线程池**三者做划分呢?
每一个 command都可以设置一个自己的名称 command key同时可以设置一个自己的组 command group。
```java
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
public CommandHelloWorld(String name) {
super(cachedSetter);
this.name = name;
}
```
command group 是一个非常重要的概念,默认情况下,就是通过 command group 来定义一个线程池的,而且还会通过 command group 来聚合一些监控和报警信息。同一个 command group 中的请求,都会进入同一个线程池中。
### command thread pool
ThreadPoolKey 代表了一个 HystrixThreadPool用来进行统一监控、统计、缓存。默认的 ThreadPoolKey 就是 command group 的名称。每个 command 都会跟它的 ThreadPoolKey 对应的 ThreadPool 绑定在一起。
如果不想直接用 command group也可以手动设置 ThreadPool 的名称。
```java
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"));
public CommandHelloWorld(String name) {
super(cachedSetter);
this.name = name;
}
```
### command key & command group & command thread pool
**command key** ,代表了一类 command一般来说代表了底层的依赖服务的一个接口。
**command group** ,代表了某一个底层的依赖服务,这是很合理的,一个依赖服务可能会暴露出来多个接口,每个接口就是一个 command key。command group 在逻辑上去组织起来一堆 command key 的调用、统计信息、成功次数、timeout 超时次数、失败次数等,可以看到某一个服务整体的一些访问情况。一般来说,**推荐**根据一个服务区划分出一个线程池command key 默认都是属于同一个线程池的。
比如说你以一个服务为粒度,估算出来这个服务每秒的所有接口加起来的整体 `QPS` 在 100 左右,你调用这个服务,当前这个服务部署了 10 个服务实例,每个服务实例上,其实用这个 command group 对应这个服务,给一个线程池,量大概在 10 个左右就可以了,你对整个服务的整体的访问 QPS 就大概在每秒 100 左右。
但是,如果说 command group 对应了一个服务,而这个服务暴露出来的几个接口,访问量很不一样,差异非常之大。你可能就希望在这个服务 command group 内部,包含的对应多个接口的 command key做一些细粒度的资源隔离。就是说对同一个服务的不同接口使用不同的线程池。
```
command key -> command group
command key -> 自己的 thread pool key
```
逻辑上来说,多个 command key 属于一个command group在做统计的时候会放在一起统计。每个 command key 有自己的线程池,每个接口有自己的线程池,去做资源隔离和限流。
说白点,就是说如果你的 command key 要用自己的线程池,可以定义自己的 thread pool key就 ok 了。
### coreSize
设置线程池的大小,默认是 10。一般来说用这个默认的 10 个线程大小就够了。
```java
HystrixThreadPoolProperties.Setter().withCoreSize(int value);
```
### queueSizeRejectionThreshold
如果说线程池中的 10 个线程都在工作中,没有空闲的线程来做其它的事情,此时再有请求过来,会先进入队列积压。如果说队列积压满了,再有请求过来,就直接 reject拒绝请求执行 fallback 降级的逻辑,快速返回。
![hystrix-thread-pool-queue](/images/hystrix-thread-pool-queue.png)
控制 queue 满了之后 reject 的 threshold因为 maxQueueSize 不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小。
```java
HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value);
```
### execution.isolation.semaphore.maxConcurrentRequests
设置使用 SEMAPHORE 隔离策略的时候允许访问的最大并发量,超过这个最大并发量,请求直接被 reject。
这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且 Hystrix 框架本身的开销会小很多。
默认值是 10尽量设置的小一些因为一旦设置的太大而且有延时发生可能瞬间导致 tomcat 本身的线程资源被占满。
```java
HystrixCommandProperties.Setter().withExecutionIsolationSemaphoreMaxConcurrentRequests(int value);
```

View File

@ -0,0 +1,125 @@
## 基于本地缓存的 fallback 降级机制
Hystrix 出现以下四种情况,都会去调用 fallback 降级机制:
- 断路器处于打开的状态。
- 资源池已满(线程池+队列 / 信号量)。
- Hystrix 调用各种接口,或者访问外部依赖,比如 MySQL、Redis、Zookeeper、Kafka 等等,出现了任何异常的情况。
- 访问外部依赖的时候,访问时间过长,报了 TimeoutException 异常。
### 两种最经典的降级机制
- 纯内存数据<br>
在降级逻辑中,你可以在内存中维护一个 ehcache作为一个纯内存的基于 LRU 自动清理的缓存让数据放在缓存内。如果说外部依赖有异常fallback 这里直接尝试从 ehcache 中获取数据。
- 默认值<br>
fallback 降级逻辑中,也可以直接返回一个默认值。
`HystrixCommand`,降级逻辑的书写,是通过实现 getFallback() 接口;而在 `HystrixObservableCommand` 中,则是实现 resumeWithFallback() 方法。
现在,我们用一个简单的栗子,来演示 fallback 降级是怎么做的。
比如,有这么个**场景**。我们现在有个包含 brandId 的商品数据,假设正常的逻辑是这样:拿到一个商品数据,根据 brandId 去调用品牌服务的接口,获取品牌的最新名称 brandName。
假如说,品牌服务接口挂掉了,那么我们可以尝试从本地内存中,获取一份稍过期的数据,先凑合着用。
### 步骤一:本地缓存获取数据
本地获取品牌名称的代码大致如下。
```java
/**
* 品牌名称本地缓存
*
*/
public class BrandCache {
private static Map<Long, String> brandMap = new HashMap<>();
static {
brandMap.put(1L, "Nike");
}
/**
* brandId 获取 brandName
*
* @param brandId 品牌id
* @return 品牌名
*/
public static String getBrandName(Long brandId) {
return brandMap.get(brandId);
}
```
### 步骤二:实现 GetBrandNameCommand
在 GetBrandNameCommand 中run() 方法的正常逻辑是去调用品牌服务的接口获取到品牌名称,如果调用失败,报错了,那么就会去调用 fallback 降级机制。
这里,我们直接**模拟接口调用报错**,给它抛出个异常。
而在 getFallback() 方法中,就是我们的**降级逻辑**,我们直接从本地的缓存中,**获取到品牌名称**的数据。
```java
/**
* 获取品牌名称的command
*
*/
public class GetBrandNameCommand extends HystrixCommand<String> {
private Long brandId;
public GetBrandNameCommand(Long brandId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("BrandService"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetBrandNameCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
// 设置降级机制最大并发请求数
.withFallbackIsolationSemaphoreMaxConcurrentRequests(15)));
this.brandId = brandId;
}
@Override
protected String run() throws Exception {
// 这里正常的逻辑应该是去调用一个品牌服务的接口获取名称
// 如果调用失败报错了那么就会去调用fallback降级机制
// 这里我们直接模拟调用报错,抛出异常
throw new Exception();
}
@Override
protected String getFallback() {
return BrandCache.getBrandName(brandId);
}
}
```
`FallbackIsolationSemaphoreMaxConcurrentRequests` 用于设置 fallback 最大允许的并发请求量,默认值是 10是通过 semaphore 信号量的机制去限流的。如果超出了这个最大值,那么直接 reject。
### 步骤三CacheController 调用接口
在 CacheController 中,我们通过 productInfo 获取 brandId然后创建 GetBrandNameCommand 并执行,去尝试获取 brandName。这里执行会报错因为我们在 run() 方法中直接抛出异常Hystrix 就会去调用 getFallback() 方法走降级逻辑。
```java
@Controller
public class CacheController {
@RequestMapping("/getProductInfo")
@ResponseBody
public String getProductInfo(Long productId) {
HystrixCommand<ProductInfo> getProductInfoCommand = new GetProductInfoCommand(productId);
ProductInfo productInfo = getProductInfoCommand.execute();
Long brandId = productInfo.getBrandId();
HystrixCommand<String> getBrandNameCommand = new GetBrandNameCommand(brandId);
// 执行会抛异常报错,然后走降级
String brandName = getBrandNameCommand.execute();
productInfo.setBrandName(brandName);
System.out.println(productInfo);
return "success";
}
}
```
关于降级逻辑的演示,基本上就结束了。

View File

@ -1,4 +1,5 @@
## 用 Hystrix 构建高可用服务架构
参考 [Hystrix Home](https://github.com/Netflix/Hystrix/wiki#what)。
### Hystrix 是什么?
在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是**依赖服务**,有的时候某些依赖服务出现故障也是很正常的。
@ -16,6 +17,8 @@ Hystrix 是高可用性保障的一个框架。Netflix可以认为是国外
时至今日Netflix 中每天都有数十亿次的服务间调用,通过 Hystrix 框架在进行,而 Hystrix 也帮助 Netflix 网站提升了整体的可用性和稳定性。
[2018 年 11 月Hystrix 在其 Github 主页宣布,不再开放新功能,推荐开发者使用其他仍然活跃的开源项目](https://github.com/Netflix/Hystrix/blob/master/README.md#hystrix-status)。维护模式的转变绝不意味着 Hystrix 不再有价值。相反Hystrix 激发了很多伟大的想法和项目,我们高可用的这一块知识还是会针对 Hystrix 进行讲解。
### Hystrix 的设计原则
- 对依赖服务调用时出现的调用延迟和调用失败进行**控制和容错保护**。
- 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障。
@ -30,7 +33,7 @@ Hystrix 是高可用性保障的一个框架。Netflix可以认为是国外
调用服务 C只需要 20ms现在因为服务 C 故障了,比如延迟,或者挂了,此时线程会 hang 住 2s 左右。40 个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C同样也会被卡住。这样导致服务 B 的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。服务 A 也挂。
![service-invoke-road](/img/service-invoke-road.png)
![service-invoke-road](/images/service-invoke-road.png)
Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线程调用服务 C。当此 40 个线程被 hang 住时,其它 60 个线程依然能正常调用工作。从而确保整个系统不会被拖垮。
@ -39,7 +42,7 @@ Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线
- 阻止任何一个依赖服务耗尽所有的资源,比如 tomcat 中的所有线程资源。
- 避免请求排队和积压,采用限流和 `fail fast` 来控制故障。
- 提供 fallback 降级机制来应对故障。
- 使用资源隔离技术,比如 `bulkhead`(舱壁隔离技术)、`swimlane`(泳道技术)、`circuit breaker`路技术)来限制任何一个依赖服务的故障的影响。
- 使用资源隔离技术,比如 `bulkhead`(舱壁隔离技术)、`swimlane`(泳道技术)、`circuit breaker`路技术)来限制任何一个依赖服务的故障的影响。
- 通过近实时的统计/监控/报警功能,来提高故障发现的速度。
- 通过近实时的属性和配置**热修改**功能,来提高故障处理和恢复的速度。
- 保护依赖服务调用的所有故障情况,而不仅仅只是网络故障情况。

View File

@ -0,0 +1,160 @@
## 深入 Hystrix 执行时内部原理
前面我们了解了 Hystrix 最基本的支持高可用的技术:**资源隔离** + **限流**
- 创建 command
- 执行这个 command
- 配置这个 command 对应的 group 和线程池。
这里,我们要讲一下,你开始执行这个 command调用了这个 command 的 execute() 方法之后Hystrix 底层的执行流程和步骤以及原理是什么。
在讲解这个流程的过程中,我会带出来 Hystrix 其他的一些核心以及重要的功能。
这里是整个 8 大步骤的流程图,我会对每个步骤进行细致的讲解。学习的过程中,对照着这个流程图,相信思路会比较清晰。
![hystrix-process](/images/hystrix-process.png)
### 步骤一:创建 command
一个 HystrixCommand 或 HystrixObservableCommand 对象,代表了对某个依赖服务发起的一次请求或者调用。创建的时候,可以在构造函数中传入任何需要的参数。
- HystrixCommand 主要用于仅仅会返回一个结果的调用。
- HystrixObservableCommand 主要用于可能会返回多条结果的调用。
```java
// 创建 HystrixCommand
HystrixCommand hystrixCommand = new HystrixCommand(arg1, arg2);
// 创建 HystrixObservableCommand
HystrixObservableCommand hystrixObservableCommand = new HystrixObservableCommand(arg1, arg2);
```
### 步骤二:调用 command 执行方法
执行 command就可以发起一次对依赖服务的调用。
要执行 command可以在 4 个方法中选择其中的一个execute()、queue()、observe()、toObservable()。
其中 execute() 和 queue() 方法仅仅对 HystrixCommand 适用。
- execute():调用后直接 block 住,属于同步调用,直到依赖服务返回单条结果,或者抛出异常。
- queue():返回一个 Future属于异步调用后面可以通过 Future 获取单条结果。
- observe():订阅一个 Observable 对象Observable 代表的是依赖服务返回的结果,获取到一个那个代表结果的 Observable 对象的拷贝对象。
- toObservable():返回一个 Observable 对象,如果我们订阅这个对象,就会执行 command 并且获取返回结果。
```java
K value = hystrixCommand.execute();
Future<K> fValue = hystrixCommand.queue();
Observable<K> oValue = hystrixObservableCommand.observe();
Observable<K> toOValue = hystrixObservableCommand.toObservable();
```
execute() 实际上会调用 queue().get() 方法,可以看一下 Hystrix 源码。
```java
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
```
而在 queue() 方法中,会调用 toObservable().toBlocking().toFuture()。
```java
final Future<R> delegate = toObservable().toBlocking().toFuture();
```
也就是说,先通过 toObservable() 获得 Future 对象,然后调用 Future 的 get() 方法。那么,其实无论是哪种方式执行 command最终都是依赖于 toObservable() 去执行的。
![hystrix-process](/images/hystrix-process.png)
### 步骤三:检查是否开启缓存
从这一步开始,就进入到 Hystrix 底层运行原理啦,看一下 Hystrix 一些更高级的功能和特性。
如果这个 command 开启了请求缓存 Request Cache而且这个调用的结果在缓存中存在那么直接从缓存中返回结果。否则继续往后的步骤。
### 步骤四:检查是否开启了断路器
检查这个 command 对应的依赖服务是否开启了断路器。如果断路器被打开了,那么 Hystrix 就不会执行这个 command而是直接去执行 fallback 降级机制,返回降级结果。
### 步骤五:检查线程池/队列/信号量是否已满
如果这个 command 线程池和队列已满,或者 semaphore 信号量已满,那么也不会执行 command而是直接去调用 fallback 降级机制,同时发送 reject 信息给断路器统计。
### 步骤六:执行 command
调用 HystrixObservableCommand 对象的 construct() 方法,或者 HystrixCommand 的 run() 方法来实际执行这个 command。
- HystrixCommand.run() 返回单条结果,或者抛出异常。
```java
// 通过command执行获取最新一条商品数据
ProductInfo productInfo = getProductInfoCommand.execute();
```
- HystrixObservableCommand.construct() 返回一个 Observable 对象,可以获取多条结果。
```java
Observable<ProductInfo> observable = getProductInfosCommand.observe();
// 订阅获取多条结果
observable.subscribe(new Observer<ProductInfo>() {
@Override
public void onCompleted() {
System.out.println("获取完了所有的商品数据");
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
/**
* 获取完一条数据,就回调一次这个方法
*
* @param productInfo 商品信息
*/
@Override
public void onNext(ProductInfo productInfo) {
System.out.println(productInfo);
}
});
```
如果是采用线程池方式,并且 HystrixCommand.run() 或者 HystrixObservableCommand.construct() 的执行时间超过了 timeout 时长的话,那么 command 所在的线程会抛出一个 TimeoutException这时会执行 fallback 降级机制,不会去管 run() 或 construct() 返回的值了。另一种情况,如果 command 执行出错抛出了其它异常,那么也会走 fallback 降级。这两种情况下Hystrix 都会发送异常事件给断路器统计。
**注意**,我们是不可能终止掉一个调用严重延迟的依赖服务的线程的,只能说给你抛出来一个 TimeoutException。
如果没有 timeout也正常执行的话那么调用线程就会拿到一些调用依赖服务获取到的结果然后 Hystrix 也会做一些 logging 记录和 metric 度量统计。
![hystrix-process](/images/hystrix-process.png)
### 步骤七:断路健康检查
Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在接下来一段时间内,会直接断路,返回降级结果。
如果在之后,断路器尝试执行 command调用没有出错返回了正常结果那么 Hystrix 就会把断路器关闭。
### 步骤八:调用 fallback 降级机制
在以下几种情况中Hystrix 会调用 fallback 降级机制。
- 断路器处于打开状态;
- 线程池/队列/semaphore满了
- command 执行超时;
- run() 或者 construct() 抛出异常。
一般在降级机制中,都建议给出一些默认的返回值,比如静态的一些代码逻辑,或者从内存中的缓存中提取一些数据,在这里尽量不要再进行网络请求了。
在降级中,如果一定要进行网络调用的话,也应该将那个调用放在一个 HystrixCommand 中进行隔离。
- HystrixCommand 中,实现 getFallback() 方法,可以提供降级机制。
- HystrixObservableCommand 中,实现 resumeWithFallback() 方法,返回一个 Observable 对象,可以提供降级结果。
如果没有实现 fallback或者 fallback 抛出了异常Hystrix 会返回一个 Observable但是不会返回任何数据。
不同的 command 执行方式,其 fallback 为空或者异常时的返回结果不同。
- 对于 execute(),直接抛出异常。
- 对于 queue(),返回一个 Future调用 get() 时抛出异常。
- 对于 observe(),返回一个 Observable 对象,但是调用 subscribe() 方法订阅它时,立即抛出调用者的 onError() 方法。
- 对于 toObservable(),返回一个 Observable 对象,但是调用 subscribe() 方法订阅它时,立即抛出调用者的 onError() 方法。
### 不同的执行方式
- execute(),获取一个 Future.get(),然后拿到单个结果。
- queue(),返回一个 Future。
- observe(),立即订阅 Observable然后启动 8 大执行步骤,返回一个拷贝的 Observable订阅时立即回调给你结果。
- toObservable(),返回一个原始的 Observable必须手动订阅才会去执行 8 大步骤。

View File

@ -0,0 +1,200 @@
## 基于 request cache 请求缓存技术优化批量商品数据查询接口
Hystrix command 执行时 8 大步骤第三步,就是检查 Request cache 是否有缓存。
首先,有一个概念,叫做 Request Context 请求上下文,一般来说,在一个 web 应用中,如果我们用到了 Hystrix我们会在一个 filter 里面,对每一个请求都施加一个请求上下文。就是说,每一次请求,就是一次请求上下文。然后在这次请求上下文中,我们会去执行 N 多代码,调用 N 多依赖服务,有的依赖服务可能还会调用好几次。
在一次请求上下文中,如果有多个 command参数都是一样的调用的接口也是一样的而结果可以认为也是一样的。那么这个时候我们可以让第一个 command 执行返回的结果缓存在内存中,然后这个请求上下文后续的其它对这个依赖的调用全部从内存中取出缓存结果就可以了。
这样的话,好处在于不用在一次请求上下文中反复多次执行一样的 command**避免重复执行网络请求,提升整个请求的性能**。
举个栗子。比如说我们在一次请求上下文中,请求获取 productId 为 1 的数据,第一次缓存中没有,那么会从商品服务中获取数据,返回最新数据结果,同时将数据缓存在内存中。后续同一次请求上下文中,如果还有获取 productId 为 1 的数据的请求,直接从缓存中取就好了。
![hystrix-request-cache](/images/hystrix-request-cache.png)
HystrixCommand 和 HystrixObservableCommand 都可以指定一个缓存 key然后 Hystrix 会自动进行缓存,接着在同一个 request context 内,再次访问的话,就会直接取用缓存。
下面,我们结合一个具体的**业务场景**,来看一下如何使用 request cache 请求缓存技术。当然,以下代码只作为一个基本的 Demo 而已。
现在,假设我们要做一个**批量查询商品数据**的接口,在这个里面,我们是用 HystrixCommand 一次性批量查询多个商品 id 的数据。但是这里有个问题,如果说 Nginx 在本地缓存失效了,重新获取一批缓存,传递过来的 productIds 都没有进行去重,比如 `productIds=1,1,1,2,2`,那么可能说,商品 id 出现了重复,如果按照我们之前的业务逻辑,可能就会重复对 productId=1 的商品查询三次productId=2 的商品查询两次。
我们对批量查询商品数据的接口,可以用 request cache 做一个优化,就是说一次请求,就是一次 request context对相同的商品查询只执行一次其余重复的都走 request cache。
### 实现 Hystrix 请求上下文过滤器并注册
定义 HystrixRequestContextFilter 类,实现 Filter 接口。
```java
/**
* Hystrix 请求上下文过滤器
*/
public class HystrixRequestContextFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
filterChain.doFilter(servletRequest, servletResponse);
} catch (IOException | ServletException e) {
e.printStackTrace();
} finally {
context.shutdown();
}
}
@Override
public void destroy() {
}
}
```
然后将该 filter 对象注册到 SpringBoot Application 中。
```java
@SpringBootApplication
public class EshopApplication {
public static void main(String[] args) {
SpringApplication.run(EshopApplication.class, args);
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new HystrixRequestContextFilter());
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
}
```
### command 重写 getCacheKey() 方法
在 GetProductInfoCommand 中,重写 getCacheKey() 方法,这样的话,每一次请求的结果,都会放在 Hystrix 请求上下文中。下一次同一个 productId 的数据请求,直接取缓存,无须再调用 run() 方法。
```java
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
public GetProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY));
this.productId = productId;
}
@Override
protected ProductInfo run() {
String url = "http://localhost:8081/getProductInfo?productId=" + productId;
String response = HttpClientUtils.sendGetRequest(url);
System.out.println("调用接口查询商品数据productId=" + productId);
return JSONObject.parseObject(response, ProductInfo.class);
}
/**
* 每次请求的结果都会放在Hystrix绑定的请求上下文上
*
* @return cacheKey 缓存key
*/
@Override
public String getCacheKey() {
return "product_info_" + productId;
}
/**
* 将某个商品id的缓存清空
*
* @param productId 商品id
*/
public static void flushCache(Long productId) {
HystrixRequestCache.getInstance(KEY,
HystrixConcurrencyStrategyDefault.getInstance()).clear("product_info_" + productId);
}
}
```
这里写了一个 flushCache() 方法,用于我们开发手动删除缓存。
### controller 调用 command 查询商品信息
在一次 web 请求上下文中,传入商品 id 列表,查询多条商品数据信息。对于每个 productId都创建一个 command。
如果 id 列表没有去重,那么重复的 id第二次查询的时候就会直接走缓存。
```java
@Controller
public class CacheController {
/**
* 一次性批量查询多条商品数据的请求
*
* @param productIds 以,分隔的商品id列表
* @return 响应状态
*/
@RequestMapping("/getProductInfos")
@ResponseBody
public String getProductInfos(String productIds) {
for (String productId : productIds.split(",")) {
// 对每个productId都创建一个command
GetProductInfoCommand getProductInfoCommand = new GetProductInfoCommand(Long.valueOf(productId));
ProductInfo productInfo = getProductInfoCommand.execute();
System.out.println("是否是从缓存中取的结果:" + getProductInfoCommand.isResponseFromCache());
}
return "success";
}
}
```
### 发起请求
调用接口,查询多个商品的信息。
```
http://localhost:8080/getProductInfos?productIds=1,1,1,2,2,5
```
在控制台,我们可以看到以下结果。
```
调用接口查询商品数据productId=1
是否是从缓存中取的结果false
是否是从缓存中取的结果true
是否是从缓存中取的结果true
调用接口查询商品数据productId=2
是否是从缓存中取的结果false
是否是从缓存中取的结果true
调用接口查询商品数据productId=5
是否是从缓存中取的结果false
```
第一次查询 productId=1 的数据,会调用接口进行查询,不是从缓存中取结果。而随后再出现查询 productId=1 的请求,就直接取缓存了,这样的话,效率明显高很多。
### 删除缓存
我们写一个 UpdateProductInfoCommand在更新商品信息之后手动调用之前写的 flushCache(),手动将缓存删除。
```java
public class UpdateProductInfoCommand extends HystrixCommand<Boolean> {
private Long productId;
public UpdateProductInfoCommand(Long productId) {
super(HystrixCommandGroupKey.Factory.asKey("UpdateProductInfoGroup"));
this.productId = productId;
}
@Override
protected Boolean run() throws Exception {
// 这里执行一次商品信息的更新
// ...
// 然后清空缓存
GetProductInfoCommand.flushCache(productId);
return true;
}
}
```
这样,以后查询该商品的请求,第一次就会走接口调用去查询最新的商品信息。

View File

@ -13,14 +13,14 @@ Hystrix 实现资源隔离,主要有两种技术:
### 信号量机制
信号量的资源隔离只是起到一个开关的作用,比如,服务 A 的信号量大小为 10那么就是说它同时只允许有 10 个 tomcat 线程来访问服务 A其它的请求都会被拒绝从而达到资源隔离和限流保护的作用。
![hystrix-semphore](/img/hystrix-semphore.png)
![hystrix-semphore](/images/hystrix-semphore.png)
### 线程池与信号量区别
线程池隔离技术,并不是说去控制类似 tomcat 这种 web 容器的线程。更加严格的意义上来说Hystrix 的线程池隔离技术,控制的是 tomcat 线程的执行。Hystrix 线程池满后会确保说tomcat 的线程不会因为依赖服务的接口调用延迟或故障而被 hang 住tomcat 其它的线程不会卡死,可以快速返回,然后支撑其它的事情。
线程池隔离技术,是用 Hystrix 自己的线程去执行调用;而信号量隔离技术,是直接让 tomcat 线程去调用依赖服务。信号量隔离,只是一道关卡,信号量有多少,就允许多少个 tomcat 线程通过它,然后去执行。
![hystrix-semphore-thread-pool](/img/hystrix-semphore-thread-pool.png)
![hystrix-semphore-thread-pool](/images/hystrix-semphore-thread-pool.png)
**适用场景**
- **线程池技术**,适合绝大多数场景,比如说我们对依赖服务的网络请求的调用和访问、需要对调用的 timeout 进行控制(捕捉 timeout 超时异常)。
@ -58,11 +58,6 @@ public class LocationCache {
写一个 GetCityNameCommand策略设置为**信号量**。run() 方法中获取本地缓存。我们目的就是对获取本地缓存的代码进行资源隔离。
```java
/**
* @author bingo
* @since 2018/12/29
*/
public class GetCityNameCommand extends HystrixCommand<String> {
private Long cityId;

View File

@ -0,0 +1,163 @@
## 深入 Hystrix 线程池隔离与接口限流
前面讲了 Hystrix 的 request cache 请求缓存、fallback 优雅降级、circuit breaker 断路器快速熔断,这一讲,我们来详细说说 Hystrix 的线程池隔离与接口限流。
![hystrix-process](/images/hystrix-process.png)
Hystrix 通过判断线程池或者信号量是否已满,超出容量的请求,直接 Reject 走降级,从而达到限流的作用。
限流是限制对后端的服务的访问量,比如说你对 MySQL、Redis、Zookeeper 以及其它各种后端中间件的资源的访问的限制,其实是为了避免过大的流量直接打死后端的服务。
### 线程池隔离技术的设计
Hystrix 采用了 Bulkhead Partition 舱壁隔离技术,来将外部依赖进行资源隔离,进而避免任何外部依赖的故障导致本服务崩溃。
**舱壁隔离**,是说将船体内部空间区隔划分成若干个隔舱,一旦某几个隔舱发生破损进水,水流不会在其间相互流动,如此一来船舶在受损时,依然能具有足够的浮力和稳定性,进而减低立即沉船的危险。
![bulkhead-partition](/images/bulkhead-partition.jpg)
Hystrix 对每个外部依赖用一个单独的线程池,这样的话,如果对那个外部依赖调用延迟很严重,最多就是耗尽那个依赖自己的线程池而已,不会影响其他的依赖调用。
### Hystrix 应用线程池机制的场景
- 每个服务都会调用几十个后端依赖服务,那些后端依赖服务通常是由很多不同的团队开发的。
- 每个后端依赖服务都会提供它自己的 client 调用库,比如说用 thrift 的话,就会提供对应的 thrift 依赖。
- client 调用库随时会变更。
- client 调用库随时可能会增加新的网络请求的逻辑。
- client 调用库可能会包含诸如自动重试、数据解析、内存中缓存等逻辑。
- client 调用库一般都对调用者来说是个黑盒,包括实现细节、网络访问、默认配置等等。
- 在真实的生产环境中经常会出现调用者突然间惊讶的发现client 调用库发生了某些变化。
- 即使 client 调用库没有改变,依赖服务本身可能有会发生逻辑上的变化。
- 有些依赖的 client 调用库可能还会拉取其他的依赖库,而且可能那些依赖库配置的不正确。
- 大多数网络请求都是同步调用的。
- 调用失败和延迟,也有可能会发生在 client 调用库本身的代码中,不一定就是发生在网络请求中。
简单来说,就是你必须默认 client 调用库很不靠谱,而且随时可能发生各种变化,所以就要用强制隔离的方式来确保任何服务的故障不会影响当前服务。
### 线程池机制的优点
- 任何一个依赖服务都可以被隔离在自己的线程池内,即使自己的线程池资源填满了,也不会影响任何其他的服务调用。
- 服务可以随时引入一个新的依赖服务,因为即使这个新的依赖服务有问题,也不会影响其他任何服务的调用。
- 当一个故障的依赖服务重新变好的时候,可以通过清理掉线程池,瞬间恢复该服务的调用,而如果是 tomcat 线程池被占满,再恢复就很麻烦。
- 如果一个 client 调用库配置有问题,线程池的健康状况随时会报告,比如成功/失败/拒绝/超时的次数统计,然后可以近实时热修改依赖服务的调用配置,而不用停机。
- 基于线程池的异步本质,可以在同步的调用之上,构建一层异步调用层。
简单来说,最大的好处,就是资源隔离,确保说任何一个依赖服务故障,不会拖垮当前的这个服务。
### 线程池机制的缺点
- 线程池机制最大的缺点就是增加了 CPU 的开销。<br>
除了 tomcat 本身的调用线程之外,还有 Hystrix 自己管理的线程池。
- 每个 command 的执行都依托一个独立的线程,会进行排队,调度,还有上下文切换。
- Hystrix 官方自己做了一个多线程异步带来的额外开销统计,通过对比多线程异步调用+同步调用得出Netflix API 每天通过 Hystrix 执行 10 亿次调用,每个服务实例有 40 个以上的线程池,每个线程池有 10 个左右的线程。)最后发现说,用 Hystrix 的额外开销,就是给请求带来了 3ms 左右的延时,最多延时在 10ms 以内,相比于可用性和稳定性的提升,这是可以接受的。
我们可以用 Hystrix semaphore 技术来实现对某个依赖服务的并发访问量的限制,而不是通过线程池/队列的大小来限制流量。
semaphore 技术可以用来限流和削峰,但是不能用来对调研延迟的服务进行 timeout 和隔离。
`execution.isolation.strategy` 设置为 `SEMAPHORE`,那么 Hystrix 就会用 semaphore 机制来替代线程池机制,来对依赖服务的访问进行限流。如果通过 semaphore 调用的时候,底层的网络调用延迟很严重,那么是无法 timeout 的,只能一直 block 住。一旦请求数量超过了 semaphore 限定的数量之后,就会立即开启限流。
### 接口限流 Demo
假设一个线程池大小为 8等待队列的大小为 10。timeout 时长我们设置长一些20s。
在 command 内部,写死代码,做一个 sleep比如 sleep 3s。
- withCoreSize设置线程池大小。
- withMaxQueueSize设置等待队列大小。
- withQueueSizeRejectionThreshold这个与 withMaxQueueSize 配合使用,等待队列的大小,取得是这两个参数的较小值。
如果只设置了线程池大小,另外两个 queue 相关参数没有设置的话,等待队列是处于关闭的状态。
```java
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
public GetProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY)
// 线程池相关配置信息
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
// 设置线程池大小为8
.withCoreSize(8)
// 设置等待队列大小为10
.withMaxQueueSize(10)
.withQueueSizeRejectionThreshold(12))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerEnabled(true)
.withCircuitBreakerRequestVolumeThreshold(20)
.withCircuitBreakerErrorThresholdPercentage(40)
.withCircuitBreakerSleepWindowInMilliseconds(3000)
// 设置超时时间
.withExecutionTimeoutInMilliseconds(20000)
// 设置fallback最大请求并发数
.withFallbackIsolationSemaphoreMaxConcurrentRequests(30)));
this.productId = productId;
}
@Override
protected ProductInfo run() throws Exception {
System.out.println("调用接口查询商品数据productId=" + productId);
if (productId == -1L) {
throw new Exception();
}
// 请求过来会在这里hang住3秒钟
if (productId == -2L) {
TimeUtils.sleep(3);
}
String url = "http://localhost:8081/getProductInfo?productId=" + productId;
String response = HttpClientUtils.sendGetRequest(url);
System.out.println(response);
return JSONObject.parseObject(response, ProductInfo.class);
}
@Override
protected ProductInfo getFallback() {
ProductInfo productInfo = new ProductInfo();
productInfo.setName("降级商品");
return productInfo;
}
}
```
我们模拟 25 个请求。前 8 个请求,调用接口时会直接被 hang 住 3s那么后面的 10 个请求会先进入等待队列中等待前面的请求执行完毕。最后的 7 个请求过来,会直接被 reject调用 fallback 降级逻辑。
```java
@SpringBootTest
@RunWith(SpringRunner.class)
public class RejectTest {
@Test
public void testReject() {
for (int i = 0; i < 25; ++i) {
new Thread(() -> HttpClientUtils.sendGetRequest("http://localhost:8080/getProductInfo?productId=-2")).start();
}
// 防止主线程提前结束执行
TimeUtils.sleep(50);
}
}
```
从执行结果中,我们可以明显看出一共打印出了 7 个降级商品。这也就是请求数超过线程池+队列的数量而直接被 reject 的结果。
```c
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
调用接口查询商品数据productId=-2
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
{"id": -2, "name": "iphone7手机", "price": 5599, "pictureList":"a.jpg,b.jpg", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1}
// 后面都是一些正常的商品信息,就不贴出来了
//...
```

View File

@ -1,5 +1,4 @@
## 基于 Hystrix 线程池技术实现资源隔离
上一讲提到,如果从 Nginx 开始缓存都失效了Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做个讨论),有可能出现调用延时而把缓存服务资源耗尽的情况。这里,我们就来说说,怎么通过 Hystrix 线程池技术实现资源隔离。
资源隔离,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内,不会去用其它资源了,这就叫资源隔离。哪怕对这个依赖服务,比如说商品服务,现在同时发起的调用量已经到了 1000但是线程池内就 10 个线程,最多就只会用这 10 个线程去执行,不会说,对商品服务的请求,因为接口调用延时,将 tomcat 内部所有的线程资源全部耗尽。
@ -111,6 +110,6 @@ public String getProductInfos(String productIds) {
我们回过头来,看看 Hystrix 线程池技术是如何实现资源隔离的。
![hystrix-thread-pool-isolation](/img/hystrix-thread-pool-isolation.png)
![hystrix-thread-pool-isolation](/images/hystrix-thread-pool-isolation.png)
从 Nginx 开始,缓存都失效了,那么 Nginx 通过缓存服务去调用商品服务。缓存服务默认的线程大小是 10 个,最多就只有 10 个线程去调用商品服务的接口。即使商品服务接口故障了,最多就只有 10 个线程会 hang 死在调用商品服务接口的路上,缓存服务的 tomcat 内其它的线程还是可以用来调用其它的服务,干其它的事情。

View File

@ -0,0 +1,103 @@
## 基于 timeout 机制为服务接口调用超时提供安全保护
一般来说,在调用依赖服务的接口的时候,比较常见的一个问题就是**超时**。超时是在一个复杂的分布式系统中,导致系统不稳定,或者系统抖动。出现大量超时,线程资源会被 hang 死,从而导致吞吐量大幅度下降,甚至服务崩溃。
你去调用各种各样的依赖服务,特别是在大公司,你甚至都不认识开发一个服务的人,你都不知道那个人的技术水平怎么样,对那个人根本不了解。
Peter Steiner 说过,"[On the Internet, nobody knows you're a dog](https://en.wikipedia.org/wiki/On_the_Internet,_nobody_knows_you%27re_a_dog)",也就是说在互联网的另外一头,你都不知道甚至坐着一条狗。
![220px-Internet_dog.jpg](/images/220px-Internet_dog.jpg)
像特别复杂的分布式系统,特别是在大公司里,多个团队、大型协作,你可能都不知道服务是谁的,很可能说开发服务的那个哥儿们甚至是一个实习生。依赖服务的接口性能可能很不稳定,有时候 2ms有时候 200ms甚至 2s都有可能。
如果你不对各种依赖服务接口的调用做超时控制,来给你的服务提供安全保护措施,那么很可能你的服务就被各种垃圾的依赖服务的性能给拖死了。大量的接口调用很慢,大量的线程被卡死。如果你做了资源的隔离,那么也就是线程池的线程被卡死,但其实我们可以做超时控制,没必要让它们全卡死。
### TimeoutMilliseconds
在 Hystrix 中,我们可以手动设置 timeout 时长,如果一个 command 运行时间超过了设定的时长,那么就被认为是 timeout然后 Hystrix command 标识为 timeout同时执行 fallback 降级逻辑。
`TimeoutMilliseconds` 默认值是 1000也就是 1000ms。
```java
HystrixCommandProperties.Setter()
..withExecutionTimeoutInMilliseconds(int)
```
### TimeoutEnabled
这个参数用于控制是否要打开 timeout 机制,默认值是 true。
```java
HystrixCommandProperties.Setter()
.withExecutionTimeoutEnabled(boolean)
```
## 实例 Demo
我们在 command 中,将超时时间设置为 500ms然后在 run() 方法中,设置休眠时间 1s这样一个请求过来直接休眠 1s结果就会因为超时而执行降级逻辑。
```java
public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
private Long productId;
private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
public GetProductInfoCommand(Long productId) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
.andCommandKey(KEY)
.andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
.withCoreSize(8)
.withMaxQueueSize(10)
.withQueueSizeRejectionThreshold(8))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerEnabled(true)
.withCircuitBreakerRequestVolumeThreshold(20)
.withCircuitBreakerErrorThresholdPercentage(40)
.withCircuitBreakerSleepWindowInMilliseconds(3000)
// 设置是否打开超时默认是true
.withExecutionTimeoutEnabled(true)
// 设置超时时间默认1000(ms)
.withExecutionTimeoutInMilliseconds(500)
.withFallbackIsolationSemaphoreMaxConcurrentRequests(30)));
this.productId = productId;
}
@Override
protected ProductInfo run() throws Exception {
System.out.println("调用接口查询商品数据productId=" + productId);
// 休眠1s
TimeUtils.sleep(1);
String url = "http://localhost:8081/getProductInfo?productId=" + productId;
String response = HttpClientUtils.sendGetRequest(url);
System.out.println(response);
return JSONObject.parseObject(response, ProductInfo.class);
}
@Override
protected ProductInfo getFallback() {
ProductInfo productInfo = new ProductInfo();
productInfo.setName("降级商品");
return productInfo;
}
}
```
在测试类中,我们直接发起请求。
```java
@SpringBootTest
@RunWith(SpringRunner.class)
public class TimeoutTest {
@Test
public void testTimeout() {
HttpClientUtils.sendGetRequest("http://localhost:8080/getProductInfo?productId=1");
}
}
```
结果中可以看到,打印出了降级商品相关信息。
```c
ProductInfo(id=null, name=降级商品, price=null, pictureList=null, specification=null, service=null, color=null, size=null, shopId=null, modifiedTime=null, cityId=null, cityName=null, brandId=null, brandName=null)
{"id": 1, "name": "iphone7手机", "price": 5599, "pictureList":"a.jpg,b.jpg", "specification": "iphone7的规格", "service": "iphone7的售后服务", "color": "红色,白色,黑色", "size": "5.5", "shopId": 1, "modifiedTime": "2017-01-01 12:00:00", "cityId": 1, "brandId": 1}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

Before

Width:  |  Height:  |  Size: 5.1 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Some files were not shown because too many files have changed in this diff Show More