├── .gitattributes
├── .github
└── FUNDING.yml
├── .gitignore
├── .nojekyll
├── LICENSE
├── README.md
├── SUMMARY.md
├── _coverpage.md
├── _navbar.md
├── advanced.md
├── docs
├── distributed-system
│ ├── README.md
│ ├── distributed-lock-redis-vs-zookeeper.md
│ ├── distributed-session.md
│ ├── distributed-system-idempotency.md
│ ├── distributed-system-interview.md
│ ├── distributed-system-request-sequence.md
│ ├── distributed-transaction.md
│ ├── dubbo-load-balancing.md
│ ├── dubbo-operating-principle.md
│ ├── dubbo-rpc-design.md
│ ├── dubbo-serialization-protocol.md
│ ├── dubbo-service-management.md
│ ├── dubbo-spi.md
│ ├── images
│ │ ├── async-replication-data-lose-case.png
│ │ ├── consistent-hashing-algorithm.png
│ │ ├── distributed-system-request-sequence.png
│ │ ├── distributed-transaction-TCC.png
│ │ ├── distributed-transaction-XA.png
│ │ ├── distributed-transaction-local-message-table.png
│ │ ├── distributed-transaction-reliable-message.png
│ │ ├── dubbo-keep-connection.png
│ │ ├── dubbo-not-keep-connection.png
│ │ ├── dubbo-operating-principle.png
│ │ ├── dubbo-service-invoke-road.png
│ │ ├── dubbo-spi.png
│ │ ├── e-commerce-website-detail-page-architecture-1.png
│ │ ├── e-commerce-website-detail-page-architecture-2.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── hash-slot.png
│ │ ├── hash.png
│ │ ├── icon.png
│ │ ├── serialize-deserialize.png
│ │ ├── service-invoke-road.png
│ │ ├── simple-distributed-system-oa.png
│ │ ├── zookeeper-active-standby.png
│ │ ├── zookeeper-centralized-storage.png
│ │ ├── zookeeper-distributed-coordination.png
│ │ ├── zookeeper-distributed-lock-demo.png
│ │ ├── zookeeper-distributed-lock.png
│ │ └── zookeeper-meta-data-manage.png
│ ├── why-dubbo.md
│ └── zookeeper-application-scenarios.md
├── from-readers
│ ├── README.md
│ ├── doocs-advanced-java-attention.md
│ ├── images
│ │ ├── advanced-java-doocs-shishan.png
│ │ └── icon.png
│ └── rights-defending-movement.md
├── high-availability
│ ├── README.md
│ ├── e-commerce-website-detail-page-architecture.md
│ ├── hystrix-circuit-breaker.md
│ ├── hystrix-execution-isolation.md
│ ├── hystrix-fallback.md
│ ├── hystrix-introduction.md
│ ├── hystrix-process.md
│ ├── hystrix-request-cache.md
│ ├── hystrix-semphore-isolation.md
│ ├── hystrix-thread-pool-current-limiting.md
│ ├── hystrix-thread-pool-isolation.md
│ ├── hystrix-timeout.md
│ └── images
│ │ ├── 220px-Internet_dog.jpg
│ │ ├── async-replication-data-lose-case.png
│ │ ├── bulkhead-partition.jpg
│ │ ├── consistent-hashing-algorithm.png
│ │ ├── distributed-system-request-sequence.png
│ │ ├── distributed-transaction-TCC.png
│ │ ├── distributed-transaction-XA.png
│ │ ├── distributed-transaction-local-message-table.png
│ │ ├── distributed-transaction-reliable-message.png
│ │ ├── dubbo-operating-principle.png
│ │ ├── dubbo-service-invoke-road.png
│ │ ├── dubbo-spi.png
│ │ ├── e-commerce-website-detail-page-architecture-1.png
│ │ ├── e-commerce-website-detail-page-architecture-2.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── hash-slot.png
│ │ ├── hash.png
│ │ ├── hystrix-process.png
│ │ ├── hystrix-request-cache.png
│ │ ├── hystrix-semphore-thread-pool.png
│ │ ├── hystrix-semphore.png
│ │ ├── hystrix-thread-pool-isolation.png
│ │ ├── hystrix-thread-pool-queue.png
│ │ ├── icon.png
│ │ ├── service-invoke-road.png
│ │ ├── simple-distributed-system-oa.png
│ │ ├── zookeeper-active-standby.png
│ │ ├── zookeeper-centralized-storage.png
│ │ ├── zookeeper-distributed-coordination.png
│ │ ├── zookeeper-distributed-lock-demo.png
│ │ ├── zookeeper-distributed-lock.png
│ │ └── zookeeper-meta-data-manage.png
├── high-concurrency
│ ├── README.md
│ ├── database-shard-dynamic-expand.md
│ ├── database-shard-global-id-generate.md
│ ├── database-shard-method.md
│ ├── database-shard.md
│ ├── es-architecture.md
│ ├── es-introduction.md
│ ├── es-optimizing-query-performance.md
│ ├── es-production-cluster.md
│ ├── es-write-query-search.md
│ ├── high-concurrency-design.md
│ ├── how-to-ensure-high-availability-of-message-queues.md
│ ├── how-to-ensure-high-concurrency-and-high-availability-of-redis.md
│ ├── how-to-ensure-that-messages-are-not-repeatedly-consumed.md
│ ├── how-to-ensure-the-order-of-messages.md
│ ├── how-to-ensure-the-reliable-transmission-of-messages.md
│ ├── images
│ │ ├── async-replication-data-lose-case.png
│ │ ├── consistent-hashing-algorithm.png
│ │ ├── database-id-sequence-step.png
│ │ ├── database-shard-method-1.png
│ │ ├── database-shard-method-2.png
│ │ ├── database-split-horizon.png
│ │ ├── database-split-vertically.png
│ │ ├── distributed-system-request-sequence.png
│ │ ├── distributed-transaction-TCC.png
│ │ ├── distributed-transaction-XA.png
│ │ ├── distributed-transaction-local-message-table.png
│ │ ├── distributed-transaction-reliable-message.png
│ │ ├── dubbo-operating-principle.png
│ │ ├── dubbo-service-invoke-road.png
│ │ ├── dubbo-spi.png
│ │ ├── e-commerce-website-detail-page-architecture-1.png
│ │ ├── e-commerce-website-detail-page-architecture-2.png
│ │ ├── es-cluster-0.png
│ │ ├── es-cluster.png
│ │ ├── es-index-type-mapping-document-field.png
│ │ ├── es-search-process.png
│ │ ├── es-write-detail.png
│ │ ├── es-write.png
│ │ ├── favicon-16x16.png
│ │ ├── favicon-32x32.png
│ │ ├── hash-slot.png
│ │ ├── hash.png
│ │ ├── high-concurrency-system-design.png
│ │ ├── icon.png
│ │ ├── kafka-after.png
│ │ ├── kafka-before.png
│ │ ├── kafka-order-01.png
│ │ ├── kafka-order-02.png
│ │ ├── mq-1.png
│ │ ├── mq-10.png
│ │ ├── mq-11.png
│ │ ├── mq-2.png
│ │ ├── mq-3.png
│ │ ├── mq-4.png
│ │ ├── mq-5.png
│ │ ├── mq-6.png
│ │ ├── mq-7.png
│ │ ├── mq-8.png
│ │ ├── mysql-master-slave.png
│ │ ├── rabbitmq-message-lose-solution.png
│ │ ├── rabbitmq-message-lose.png
│ │ ├── rabbitmq-order-01.png
│ │ ├── rabbitmq-order-02.png
│ │ ├── redis-caching-avalanche-solution.png
│ │ ├── redis-caching-avalanche.png
│ │ ├── redis-caching-penetration.png
│ │ ├── redis-cluster-split-brain.png
│ │ ├── redis-gossip.png
│ │ ├── redis-junior-inconsistent.png
│ │ ├── redis-master-slave-replication-detail.png
│ │ ├── redis-master-slave-replication.png
│ │ ├── redis-master-slave.png
│ │ ├── redis-redlock.png
│ │ ├── redis-single-thread-model.png
│ │ ├── service-invoke-road.png
│ │ ├── simple-distributed-system-oa.png
│ │ ├── zookeeper-active-standby.png
│ │ ├── zookeeper-centralized-storage.png
│ │ ├── zookeeper-distributed-coordination.png
│ │ ├── zookeeper-distributed-lock-demo.png
│ │ ├── zookeeper-distributed-lock.png
│ │ └── zookeeper-meta-data-manage.png
│ ├── mq-design.md
│ ├── mq-interview.md
│ ├── mq-time-delay-and-expired-failure.md
│ ├── mysql-read-write-separation.md
│ ├── redis-caching-avalanche-and-caching-penetration.md
│ ├── redis-cas.md
│ ├── redis-cluster.md
│ ├── redis-consistence.md
│ ├── redis-data-types.md
│ ├── redis-expiration-policies-and-lru.md
│ ├── redis-master-slave.md
│ ├── redis-persistence.md
│ ├── redis-production-environment.md
│ ├── redis-sentinel.md
│ ├── redis-single-thread-model.md
│ ├── why-cache.md
│ └── why-mq.md
└── micro-services
│ ├── README.md
│ ├── images
│ ├── PreferFunctionalStaffOrganization.png
│ ├── basic-pipeline.png
│ ├── conways-law.png
│ ├── decentralised-data.png
│ ├── micro-deployment.png
│ └── sketch.png
│ └── microservices-introduction.md
├── images
├── 220px-Internet_dog.jpg
├── PreferFunctionalStaffOrganization.png
├── advanced-java-doocs-shishan.png
├── async-replication-data-lose-case.png
├── basic-pipeline.png
├── bulkhead-partition.jpg
├── consistent-hashing-algorithm.png
├── conways-law.png
├── database-id-sequence-step.png
├── database-shard-method-1.png
├── database-shard-method-2.png
├── database-split-horizon.png
├── database-split-vertically.png
├── decentralised-data.png
├── distributed-system-request-sequence.png
├── distributed-transaction-TCC.png
├── distributed-transaction-XA.png
├── distributed-transaction-local-message-table.png
├── distributed-transaction-reliable-message.png
├── dubbo-keep-connection.png
├── dubbo-not-keep-connection.png
├── dubbo-operating-principle.png
├── dubbo-service-invoke-road.png
├── dubbo-spi.png
├── e-commerce-website-detail-page-architecture-1.png
├── e-commerce-website-detail-page-architecture-2.png
├── es-cluster-0.png
├── es-cluster.png
├── es-index-type-mapping-document-field.png
├── es-search-process.png
├── es-write-detail.png
├── es-write.png
├── favicon-16x16.png
├── favicon-32x32.png
├── get-up-and-study.png
├── hash-slot.png
├── hash.png
├── high-concurrency-system-design.png
├── hystrix-process.png
├── hystrix-request-cache.png
├── hystrix-semphore-thread-pool.png
├── hystrix-semphore.png
├── hystrix-thread-pool-isolation.png
├── hystrix-thread-pool-queue.png
├── icon.png
├── kafka-after.png
├── kafka-before.png
├── kafka-order-01.png
├── kafka-order-02.png
├── kafka-order-1.png
├── kafka-order-2.png
├── micro-deployment.png
├── mq-1.png
├── mq-10.png
├── mq-11.png
├── mq-2.png
├── mq-3.png
├── mq-4.png
├── mq-5.png
├── mq-6.png
├── mq-7.png
├── mq-8.png
├── mysql-master-slave.png
├── rabbitmq-message-lose-solution.png
├── rabbitmq-message-lose.png
├── rabbitmq-order-01.png
├── rabbitmq-order-02.png
├── rabbitmq-order-1.png
├── rabbitmq-order-2.png
├── redis-caching-avalanche-solution.png
├── redis-caching-avalanche.png
├── redis-caching-penetration.png
├── redis-cluster-split-brain.png
├── redis-gossip.png
├── redis-junior-inconsistent.png
├── redis-master-slave-replication-detail.png
├── redis-master-slave-replication.png
├── redis-master-slave.png
├── redis-redlock.png
├── redis-single-thread-model.png
├── serialize-deserialize.png
├── service-invoke-road.png
├── simple-distributed-system-oa.png
├── sketch.png
├── where-is-my-offer.png
├── zookeeper-active-standby.png
├── zookeeper-centralized-storage.png
├── zookeeper-distributed-coordination.png
├── zookeeper-distributed-lock-demo.png
├── zookeeper-distributed-lock.png
└── zookeeper-meta-data-manage.png
├── index.html
├── offer.md
└── sw.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | *.js linguist-language=java
3 | *.css linguist-language=java
4 | *.html linguist-language=java
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: advanced-java
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with a single custom sponsorship URL
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /gradle/wrapper/gradle-wrapper.properties
2 | ##----------Android----------
3 | # build
4 | *.apk
5 | *.ap_
6 | *.dex
7 | *.class
8 | bin/
9 | gen/
10 | build/
11 |
12 | # gradle
13 | .gradle/
14 | gradle-app.setting
15 | !gradle-wrapper.jar
16 | build/
17 |
18 | local.properties
19 |
20 | ##----------idea----------
21 | *.iml
22 | .idea/
23 | *.ipr
24 | *.iws
25 |
26 | # Android Studio Navigation editor temp files
27 | .navigation/
28 |
29 | ##----------Other----------
30 | # osx
31 | *~
32 | .DS_Store
33 | gradle.properties
34 |
35 | .vscode
--------------------------------------------------------------------------------
/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/.nojekyll
--------------------------------------------------------------------------------
/_coverpage.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/doocs/advanced-java)
2 |
3 | # Java 进阶扫盲
4 |
5 | > 高并发 分布式 高可用 微服务
6 |
7 | [Doocs](https://github.com/doocs/intro)
8 | [GitHub](https://github.com/yanglbme)
9 | [Get Started](#互联网-java-工程师进阶知识完全扫盲)
--------------------------------------------------------------------------------
/_navbar.md:
--------------------------------------------------------------------------------
1 | * 分类
2 | * [高并发](/README#高并发架构)
3 | * [分布式](/README#分布式系统)
4 | * [高可用](/README#高可用架构)
5 | * [微服务](/README#微服务架构)
6 | * [读者分享](/docs/from-readers/README)
7 |
8 | * 页面
9 | * [封面]()
10 | * [首页](README)
11 | * [进阶](advanced)
12 | * [Offer](offer)
13 | * [Doocs](https://github.com/doocs/intro)
14 | * [GitHub](https://github.com/yanglbme)
--------------------------------------------------------------------------------
/advanced.md:
--------------------------------------------------------------------------------
1 |
2 | 本单曲受版权保护,可点击观看 MV。
3 |
4 |
5 | > 受伤的得到疗愈,挣扎的得到出口
6 | *Let those who hurt heal, let those who struggle find hope*.
7 |
8 | ```
9 | 告别的时刻已到了
10 | 随身的行囊 装些快乐
11 |
12 | 前方被乌云笼罩了
13 | 心中的拉扯 困兽在低吼
14 |
15 | 隐隐约约的沉默
16 | 透露一丝丝苦涩
17 | 愿你不再被伤透
18 | 别再带着泪入睡
19 |
20 | 曲曲折折的世界
21 | 答案不一定绝对
22 | 伤口因为爱壮烈
23 | 让我们同步进阶
24 |
25 | 重生的力量来自真我
26 | 战胜可敬的对手 yeah~
27 |
28 | 坚持信念是我的所有
29 | 抵达心中的辽阔 yeah~
30 |
31 | 有失去的 该进阶的
32 | 有遗憾的 该进阶的
33 | 放不下的 该进阶的 yeah~
34 |
35 | 圣所的光芒
36 | 指引方向 oh~
37 |
38 | 跨越了浩劫
39 | 曙光渐亮 oh~
40 |
41 | 希望因挣扎破灭了
42 | 再别无所求 于是自由
43 |
44 | 逆着风孤独的英雄
45 | 也渴望停泊 可说不出口
46 |
47 | 隐隐约约的沉默
48 | 透露一丝丝苦涩
49 | 愿你不再被伤透
50 | 别再带着泪入睡
51 |
52 | 曲曲折折的世界
53 | 答案不一定绝对
54 | 伤口因为爱壮烈
55 | 让我们同步进阶
56 |
57 | 重生的力量来自真我
58 | 战胜可敬的对手 yeah~
59 |
60 | 坚持信念是我的所有
61 | 抵达心中的辽阔 yeah~
62 |
63 | 有失去的 该进阶的
64 | 有遗憾的 该进阶的
65 | 放不下的 该进阶的 yeah~
66 |
67 | 圣所的光芒
68 | 指引方向 oh~
69 |
70 | 跨越了浩劫
71 | 曙光渐亮 oh~
72 | ```
--------------------------------------------------------------------------------
/docs/distributed-system/README.md:
--------------------------------------------------------------------------------
1 | # 分布式系统
--------------------------------------------------------------------------------
/docs/distributed-system/distributed-session.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 集群部署时的分布式 session 如何实现?
3 |
4 | ## 面试官心理分析
5 | 面试官问了你一堆 dubbo 是怎么玩儿的,你会玩儿 dubbo 就可以把单块系统弄成分布式系统,然后分布式之后接踵而来的就是一堆问题,最大的问题就是**分布式事务**、**接口幂等性**、**分布式锁**,还有最后一个就是**分布式 session**。
6 |
7 | 当然了,分布式系统中的问题何止这么一点,非常之多,复杂度很高,这里只是说一下常见的几个问题,也是面试的时候常问的几个。
8 |
9 | ## 面试题剖析
10 | session 是啥?浏览器有个 cookie,在一段时间内这个 cookie 都存在,然后每次发请求过来都带上一个特殊的 `jsessionid cookie`,就根据这个东西,在服务端可以维护一个对应的 session 域,里面可以放点数据。
11 |
12 | 一般的话只要你没关掉浏览器,cookie 还在,那么对应的那个 session 就在,但是如果 cookie 没了,session 也就没了。常见于什么购物车之类的东西,还有登录状态保存之类的。
13 |
14 | 这个不多说了,懂 Java 的都该知道这个。
15 |
16 | 单块系统的时候这么玩儿 session 没问题,但是你要是分布式系统呢,那么多的服务,session 状态在哪儿维护啊?
17 |
18 | 其实方法很多,但是常见常用的是以下几种:
19 |
20 | ### 完全不用 session
21 | 使用 JWT Token 储存用户身份,然后再从数据库或者 cache 中获取其他的信息。这样无论请求分配到哪个服务器都无所谓。
22 |
23 | ### tomcat + redis
24 | 这个其实还挺方便的,就是使用 session 的代码,跟以前一样,还是基于 tomcat 原生的 session 支持即可,然后就是用一个叫做 `Tomcat RedisSessionManager` 的东西,让所有我们部署的 tomcat 都将 session 数据存储到 redis 即可。
25 |
26 | 在 tomcat 的配置文件中配置:
27 |
28 | ```xml
29 |
30 |
31 |
36 | ```
37 |
38 | 然后指定 redis 的 host 和 port 就 ok 了。
39 |
40 | ```xml
41 |
42 |
46 | ```
47 |
48 | 还可以用上面这种方式基于 redis 哨兵支持的 redis 高可用集群来保存 session 数据,都是 ok 的。
49 |
50 | ### spring session + redis
51 | 上面所说的第二种方式会与 tomcat 容器重耦合,如果我要将 web 容器迁移成 jetty,难道还要重新把 jetty 都配置一遍?
52 |
53 | 因为上面那种 tomcat + redis 的方式好用,但是会**严重依赖于web容器**,不好将代码移植到其他 web 容器上去,尤其是你要是换了技术栈咋整?比如换成了 spring cloud 或者是 spring boot 之类的呢?
54 |
55 | 所以现在比较好的还是基于 Java 一站式解决方案,也就是 spring。人家 spring 基本上承包了大部分我们需要使用的框架,spirng cloud 做微服务,spring boot 做脚手架,所以用 sping session 是一个很好的选择。
56 |
57 | 在 pom.xml 中配置:
58 | ```xml
59 |
60 | org.springframework.session
61 | spring-session-data-redis
62 | 1.2.1.RELEASE
63 |
64 |
65 | redis.clients
66 | jedis
67 | 2.8.1
68 |
69 | ```
70 |
71 | 在 spring 配置文件中配置:
72 | ```xml
73 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 | ```
93 |
94 | 在 web.xml 中配置:
95 | ```xml
96 |
97 | springSessionRepositoryFilter
98 | org.springframework.web.filter.DelegatingFilterProxy
99 |
100 |
101 | springSessionRepositoryFilter
102 | /*
103 |
104 | ```
105 |
106 | 示例代码:
107 | ```java
108 | @RestController
109 | @RequestMapping("/test")
110 | public class TestController {
111 |
112 | @RequestMapping("/putIntoSession")
113 | public String putIntoSession(HttpServletRequest request, String username) {
114 | request.getSession().setAttribute("name", "leo");
115 | return "ok";
116 | }
117 |
118 | @RequestMapping("/getFromSession")
119 | public String getFromSession(HttpServletRequest request, Model model){
120 | String name = request.getSession().getAttribute("name");
121 | return name;
122 | }
123 | }
124 | ```
125 |
126 |
127 | 上面的代码就是 ok 的,给 sping session 配置基于 redis 来存储 session 数据,然后配置了一个 spring session 的过滤器,这样的话,session 相关操作都会交给 spring session 来管了。接着在代码中,就用原生的 session 操作,就是直接基于 spring sesion 从 redis 中获取数据了。
128 |
129 | 实现分布式的会话有很多种方式,我说的只不过是比较常见的几种方式,tomcat + redis 早期比较常用,但是会重耦合到 tomcat 中;近些年,通过 spring session 来实现。
--------------------------------------------------------------------------------
/docs/distributed-system/distributed-system-idempotency.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
3 |
4 | ## 面试官心理分析
5 | 从这个问题开始,面试官就已经进入了**实际的生产问题**的面试了。
6 |
7 | 一个分布式系统中的某个接口,该如何保证幂等性?这个事儿其实是你做分布式系统的时候必须要考虑的一个生产环境的技术问题。啥意思呢?
8 |
9 | 你看,假如你有个服务提供一些接口供外部调用,这个服务部署在了 5 台机器上,接着有个接口就是**付款接口**。然后人家用户在前端上操作的时候,不知道为啥,总之就是一个订单**不小心发起了两次支付请求**,然后这俩请求分散在了这个服务部署的不同的机器上,好了,结果一个订单扣款扣两次。
10 |
11 | 或者是订单系统调用支付系统进行支付,结果不小心因为**网络超时**了,然后订单系统走了前面我们看到的那个重试机制,咔嚓给你重试了一把,好,支付系统收到一个支付请求两次,而且因为负载均衡算法落在了不同的机器上,尴尬了。。。
12 |
13 | 所以你肯定得知道这事儿,否则你做出来的分布式系统恐怕容易埋坑。
14 |
15 | ## 面试题剖析
16 | 这个不是技术问题,这个没有通用的一个方法,这个应该**结合业务**来保证幂等性。
17 |
18 | 所谓**幂等性**,就是说一个接口,多次发起同一个请求,你这个接口得保证结果是准确的,比如不能多扣款、不能多插入一条数据、不能将统计值多加了 1。这就是幂等性。
19 |
20 | 其实保证幂等性主要是三点:
21 | - 对于每个请求必须有一个唯一的标识,举个栗子:订单支付请求,肯定得包含订单 id,一个订单 id 最多支付一次,对吧。
22 | - 每次处理完请求之后,必须有一个记录标识这个请求处理过了。常见的方案是在 mysql 中记录个状态啥的,比如支付之前记录一条这个订单的支付流水。
23 | - 每次接收请求需要进行判断,判断之前是否处理过。比如说,如果有一个订单已经支付了,就已经有了一条支付流水,那么如果重复发送这个请求,则此时先插入支付流水,orderId 已经存在了,唯一键约束生效,报错插入不进去的。然后你就不用再扣款了。
24 |
25 | 实际运作过程中,你要结合自己的业务来,比如说利用 redis,用 orderId 作为唯一键。只有成功插入这个支付流水,才可以执行实际的支付扣款。
26 |
27 | 要求是支付一个订单,必须插入一条支付流水,order_id 建一个唯一键 `unique key`。你在支付一个订单之前,先插入一条支付流水,order_id 就已经进去了。你就可以写一个标识到 redis 里面去,`set order_id payed`,下一次重复请求过来了,先查 redis 的 order_id 对应的 value,如果是 `payed` 就说明已经支付过了,你就别重复支付了。
--------------------------------------------------------------------------------
/docs/distributed-system/distributed-system-interview.md:
--------------------------------------------------------------------------------
1 | ## 分布式系统面试连环炮
2 | 有一些同学,之前呢主要是做传统行业,或者外包项目,一直是在那种小的公司,技术一直都搞的比较简单。他们有共同的一个问题,就是都没怎么搞过分布式系统,现在互联网公司,一般都是做分布式的系统,大家都不是做底层的分布式系统、分布式存储系统 hadoop hdfs、分布式计算系统 hadoop mapreduce / spark、分布式流式计算系统 storm。
3 |
4 | 分布式业务系统,就是把原来用 Java 开发的一个大块系统,给拆分成**多个子系统**,多个子系统之间互相调用,形成一个大系统的整体。假设原来你做了一个 OA 系统,里面包含了权限模块、员工模块、请假模块、财务模块,一个工程,里面包含了一堆模块,模块与模块之间会互相去调用,1 台机器部署。现在如果你把这个系统给拆开,权限系统、员工系统、请假系统、财务系统 4 个系统,4 个工程,分别在 4 台机器上部署。一个请求过来,完成这个请求,这个员工系统,调用权限系统,调用请假系统,调用财务系统,4 个系统分别完成了一部分的事情,最后 4 个系统都干完了以后,才认为是这个请求已经完成了。
5 |
6 | 
7 |
8 | > 近几年开始兴起和流行 Spring Cloud,刚流行,还没开始普及,目前普及的是 dubbo,因此这里也主要讲 dubbo。
9 |
10 | 面试官可能会问你以下问题。
11 | ### 为什么要进行系统拆分?
12 | - 为什么要进行系统拆分?如何进行系统拆分?拆分后不用dubbo可以吗?dubbo和thrift有什么区别呢?
13 | ### 分布式服务框架
14 | - 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?
15 | - dubbo 支持哪些序列化协议?说一下 hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
16 | - dubbo 负载均衡策略和高可用策略都有哪些?动态代理策略呢?
17 | - dubbo 的 spi 思想是什么?
18 | - 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
19 | - 分布式服务接口的幂等性如何设计(比如不能重复扣款)?
20 | - 分布式服务接口请求的顺序性如何保证?
21 | - 如何自己设计一个类似 dubbo 的 rpc 框架?
22 |
23 | ### 分布式锁
24 | - 使用 redis 如何设计分布式锁?使用 zk 来设计分布式锁可以吗?这两种分布式锁的实现方式哪种效率比较高?
25 |
26 | ### 分布式事务
27 | - 分布式事务了解吗?你们如何解决分布式事务问题的?TCC 如果出现网络连不通怎么办?XA 的一致性如何保证?
28 |
29 | ### 分布式会话
30 | - 集群部署时的分布式 session 如何实现?
31 |
--------------------------------------------------------------------------------
/docs/distributed-system/distributed-system-request-sequence.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 分布式服务接口请求的顺序性如何保证?
3 |
4 | ## 面试官心理分析
5 | 其实分布式系统接口的调用顺序,也是个问题,一般来说是不用保证顺序的。但是**有时候**可能确实是需要**严格的顺序**保证。给大家举个例子,你服务 A 调用服务 B,先插入再删除。好,结果俩请求过去了,落在不同机器上,可能插入请求因为某些原因执行慢了一些,导致删除请求先执行了,此时因为没数据所以啥效果也没有;结果这个时候插入请求过来了,好,数据插入进去了,那就尴尬了。
6 |
7 | 本来应该是 “先插入 -> 再删除”,这条数据应该没了,结果现在 “先删除 -> 再插入”,数据还存在,最后你死都想不明白是怎么回事。
8 |
9 | 所以这都是分布式系统一些很常见的问题。
10 |
11 | ## 面试题剖析
12 | 首先,一般来说,个人建议是,你们从业务逻辑上设计的这个系统最好是不需要这种顺序性的保证,因为一旦引入顺序性保障,比如使用**分布式锁**,会**导致系统复杂度上升**,而且会带来**效率低下**,热点数据压力过大等问题。
13 |
14 | 下面我给个我们用过的方案吧,简单来说,首先你得用 dubbo 的一致性 hash 负载均衡策略,将比如某一个订单 id 对应的请求都给分发到某个机器上去,接着就是在那个机器上,因为可能还是多线程并发执行的,你可能得立即将某个订单 id 对应的请求扔一个**内存队列**里去,强制排队,这样来确保他们的顺序性。
15 |
16 | 
17 |
18 | 但是这样引发的后续问题就很多,比如说要是某个订单对应的请求特别多,造成某台机器成**热点**怎么办?解决这些问题又要开启后续一连串的复杂技术方案......曾经这类问题弄的我们头疼不已,所以,还是建议什么呢?
19 |
20 | 最好是比如说刚才那种,一个订单的插入和删除操作,能不能合并成一个操作,就是一个删除,或者是其它什么,避免这种问题的产生。
--------------------------------------------------------------------------------
/docs/distributed-system/distributed-transaction.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 分布式事务了解吗?你们是如何解决分布式事务问题的?
3 |
4 | ## 面试官心理分析
5 | 只要聊到你做了分布式系统,必问分布式事务,你对分布式事务一无所知的话,确实会很坑,你起码得知道有哪些方案,一般怎么来做,每个方案的优缺点是什么。
6 |
7 | 现在面试,分布式系统成了标配,而分布式系统带来的**分布式事务**也成了标配了。因为你做系统肯定要用事务吧,如果是分布式系统,肯定要用分布式事务吧。先不说你搞过没有,起码你得明白有哪几种方案,每种方案可能有啥坑?比如 TCC 方案的网络问题、XA 方案的一致性问题。
8 |
9 | ## 面试题剖析
10 | 分布式事务的实现主要有以下 5 种方案:
11 |
12 | - XA 方案
13 | - TCC 方案
14 | - 本地消息表
15 | - 可靠消息最终一致性方案
16 | - 最大努力通知方案
17 |
18 | ### 两阶段提交方案/XA方案
19 | 所谓的 XA 方案,即:两阶段提交,有一个**事务管理器**的概念,负责协调多个数据库(资源管理器)的事务,事务管理器先问问各个数据库你准备好了吗?如果每个数据库都回复 ok,那么就正式提交事务,在各个数据库上执行操作;如果任何其中一个数据库回答不 ok,那么就回滚事务。
20 |
21 | 这种分布式事务方案,比较适合单块应用里,跨多个库的分布式事务,而且因为严重依赖于数据库层面来搞定复杂的事务,效率很低,绝对不适合高并发的场景。如果要玩儿,那么基于 `Spring + JTA` 就可以搞定,自己随便搜个 demo 看看就知道了。
22 |
23 | 这个方案,我们很少用,一般来说**某个系统内部如果出现跨多个库**的这么一个操作,是**不合规**的。我可以给大家介绍一下, 现在微服务,一个大的系统分成几十个甚至几百个服务。一般来说,我们的规定和规范,是要求**每个服务只能操作自己对应的一个数据库**。
24 |
25 | 如果你要操作别的服务对应的库,不允许直连别的服务的库,违反微服务架构的规范,你随便交叉胡乱访问,几百个服务的话,全体乱套,这样的一套服务是没法管理的,没法治理的,可能会出现数据被别人改错,自己的库被别人写挂等情况。
26 |
27 | 如果你要操作别人的服务的库,你必须是通过**调用别的服务的接口**来实现,绝对不允许交叉访问别人的数据库。
28 |
29 | 
30 |
31 | ### TCC 方案
32 | TCC 的全称是:`Try`、`Confirm`、`Cancel`。
33 |
34 | - Try 阶段:这个阶段说的是对各个服务的资源做检测以及对资源进行**锁定或者预留**。
35 | - Confirm 阶段:这个阶段说的是在各个服务中**执行实际的操作**。
36 | - Cancel 阶段:如果任何一个服务的业务方法执行出错,那么这里就需要**进行补偿**,就是执行已经执行成功的业务逻辑的回滚操作。(把那些执行成功的回滚)
37 |
38 | 这种方案说实话几乎很少人使用,我们用的也比较少,但是也有使用的场景。因为这个**事务回滚**实际上是**严重依赖于你自己写代码来回滚和补偿**了,会造成补偿代码巨大,非常之恶心。
39 |
40 | 比如说我们,一般来说跟**钱**相关的,跟钱打交道的,**支付**、**交易**相关的场景,我们会用 TCC,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性,保证在资金上不会出现问题。
41 |
42 | 而且最好是你的各个业务执行的时间都比较短。
43 |
44 | 但是说实话,一般尽量别这么搞,自己手写回滚逻辑,或者是补偿逻辑,实在太恶心了,那个业务代码是很难维护的。
45 |
46 | 
47 |
48 | ### 本地消息表
49 | 本地消息表其实是国外的 ebay 搞出来的这么一套思想。
50 |
51 | 这个大概意思是这样的:
52 |
53 | 1. A 系统在自己本地一个事务里操作同时,插入一条数据到消息表;
54 | 2. 接着 A 系统将这个消息发送到 MQ 中去;
55 | 3. B 系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样**保证不会重复处理消息**;
56 | 4. B 系统执行成功之后,就会更新自己本地消息表的状态以及 A 系统消息表的状态;
57 | 5. 如果 B 系统处理失败了,那么就不会更新消息表状态,那么此时 A 系统会定时扫描自己的消息表,如果有未处理的消息,会再次发送到 MQ 中去,让 B 再次处理;
58 | 6. 这个方案保证了最终一致性,哪怕 B 事务失败了,但是 A 会不断重发消息,直到 B 那边成功为止。
59 |
60 | 这个方案说实话最大的问题就在于**严重依赖于数据库的消息表来管理事务**啥的,如果是高并发场景咋办呢?咋扩展呢?所以一般确实很少用。
61 |
62 | 
63 |
64 | ### 可靠消息最终一致性方案
65 | 这个的意思,就是干脆不要用本地的消息表了,直接基于 MQ 来实现事务。比如阿里的 RocketMQ 就支持消息事务。
66 |
67 | 大概的意思就是:
68 |
69 | 1. A 系统先发送一个 prepared 消息到 mq,如果这个 prepared 消息发送失败那么就直接取消操作别执行了;
70 | 2. 如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉 mq 发送确认消息,如果失败就告诉 mq 回滚消息;
71 | 3. 如果发送了确认消息,那么此时 B 系统会接收到确认消息,然后执行本地的事务;
72 | 4. mq 会自动**定时轮询**所有 prepared 消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认的消息,是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,而确认消息却发送失败了。
73 | 5. 这个方案里,要是系统 B 的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如 B 系统本地回滚后,想办法通知系统 A 也回滚;或者是发送报警由人工来手工回滚和补偿。
74 | 6. 这个还是比较合适的,目前国内互联网公司大都是这么玩儿的,要不你举用 RocketMQ 支持的,要不你就自己基于类似 ActiveMQ?RabbitMQ?自己封装一套类似的逻辑出来,总之思路就是这样子的。
75 |
76 | 
77 |
78 | ### 最大努力通知方案
79 | 这个方案的大致意思就是:
80 |
81 | 1. 系统 A 本地事务执行完之后,发送个消息到 MQ;
82 | 2. 这里会有个专门消费 MQ 的**最大努力通知服务**,这个服务会消费 MQ 然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统 B 的接口;
83 | 3. 要是系统 B 执行成功就 ok 了;要是系统 B 执行失败了,那么最大努力通知服务就定时尝试重新调用系统 B,反复 N 次,最后还是不行就放弃。
84 |
85 | ### 你们公司是如何处理分布式事务的?
86 | 如果你真的被问到,可以这么说,我们某某特别严格的场景,用的是 TCC 来保证强一致性;然后其他的一些场景基于阿里的 RocketMQ 来实现分布式事务。
87 |
88 | 你找一个严格资金要求绝对不能错的场景,你可以说你是用的 TCC 方案;如果是一般的分布式事务场景,订单插入之后要调用库存服务更新库存,库存数据没有资金那么的敏感,可以用可靠消息最终一致性方案。
89 |
90 | 友情提示一下,RocketMQ 3.2.6 之前的版本,是可以按照上面的思路来的,但是之后接口做了一些改变,我这里不再赘述了。
91 |
92 | 当然如果你愿意,你可以参考可靠消息最终一致性方案来自己实现一套分布式事务,比如基于 RocketMQ 来玩儿。
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-load-balancing.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?
3 |
4 | ## 面试官心理分析
5 | 继续深问吧,这些都是用 dubbo 必须知道的一些东西,你得知道基本原理,知道序列化是什么协议,还得知道具体用 dubbo 的时候,如何负载均衡,如何高可用,如何动态代理。
6 |
7 | 说白了,就是看你对 dubbo 熟悉不熟悉:
8 | - dubbo 工作原理:服务注册、注册中心、消费者、代理通信、负载均衡;
9 | - 网络通信、序列化:dubbo 协议、长连接、NIO、hessian 序列化协议;
10 | - 负载均衡策略、集群容错策略、动态代理策略:dubbo 跑起来的时候一些功能是如何运转的?怎么做负载均衡?怎么做集群容错?怎么生成动态代理?
11 | - dubbo SPI 机制:你了解不了解 dubbo 的 SPI 机制?如何基于 SPI 机制对 dubbo 进行扩展?
12 |
13 | ## 面试题剖析
14 | ### dubbo 负载均衡策略
15 | #### random loadbalance
16 | 默认情况下,dubbo 是 random load balance ,即**随机**调用实现负载均衡,可以对 provider 不同实例**设置不同的权重**,会按照权重来负载均衡,权重越大分配流量越高,一般就用这个默认的就可以了。
17 |
18 | #### roundrobin loadbalance
19 | 这个的话默认就是均匀地将流量打到各个机器上去,但是如果各个机器的性能不一样,容易导致性能差的机器负载过高。所以此时需要调整权重,让性能差的机器承载权重小一些,流量少一些。
20 |
21 | 举个栗子。
22 |
23 | 跟运维同学申请机器,有的时候,我们运气好,正好公司资源比较充足,刚刚有一批热气腾腾、刚刚做好的虚拟机新鲜出炉,配置都比较高:8 核 + 16G 机器,申请到 2 台。过了一段时间,我们感觉 2 台机器有点不太够,我就去找运维同学说,“哥儿们,你能不能再给我一台机器”,但是这时只剩下一台 4 核 + 8G 的机器。我要还是得要。
24 |
25 | 这个时候,可以给两台 8 核 16G 的机器设置权重 4,给剩余 1 台 4 核 8G 的机器设置权重 2。
26 |
27 | #### leastactive loadbalance
28 | 这个就是自动感知一下,如果某个机器性能越差,那么接收的请求越少,越不活跃,此时就会给**不活跃的性能差的机器更少的请求**。
29 |
30 | #### consistanthash loadbalance
31 | 一致性 Hash 算法,相同参数的请求一定分发到一个 provider 上去,provider 挂掉的时候,会基于虚拟节点均匀分配剩余的流量,抖动不会太大。**如果你需要的不是随机负载均衡**,是要一类请求都到一个节点,那就走这个一致性 Hash 策略。
32 |
33 | ### dubbo 集群容错策略
34 | #### failover cluster 模式
35 | 失败自动切换,自动重试其他机器,**默认**就是这个,常见于读操作。(失败重试其它机器)
36 |
37 | 可以通过以下几种方式配置重试次数:
38 |
39 | ```xml
40 |
41 | ```
42 |
43 | 或者
44 |
45 | ```xml
46 |
47 | ```
48 |
49 | 或者
50 |
51 | ```xml
52 |
53 |
54 |
55 | ```
56 |
57 | #### failfast cluster 模式
58 | 一次调用失败就立即失败,常见于非幂等性的写操作,比如新增一条记录(调用失败就立即失败)
59 |
60 | #### failsafe cluster 模式
61 | 出现异常时忽略掉,常用于不重要的接口调用,比如记录日志。
62 |
63 | 配置示例如下:
64 |
65 | ```xml
66 |
67 | ```
68 |
69 | 或者
70 |
71 | ```xml
72 |
73 | ```
74 |
75 | #### failback cluster 模式
76 | 失败了后台自动记录请求,然后定时重发,比较适合于写消息队列这种。
77 |
78 | #### forking cluster 模式
79 | **并行调用**多个 provider,只要一个成功就立即返回。常用于实时性要求比较高的读操作,但是会浪费更多的服务资源,可通过 `forks="2"` 来设置最大并行数。
80 |
81 | #### broadcacst cluster
82 | 逐个调用所有的 provider。任何一个 provider 出错则报错(从`2.1.0` 版本开始支持)。通常用于通知所有提供者更新缓存或日志等本地资源信息。
83 |
84 | ### dubbo动态代理策略
85 | 默认使用 javassist 动态字节码生成,创建代理类。但是可以通过 spi 扩展机制配置自己的动态代理策略。
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-operating-principle.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 说一下的 dubbo 的工作原理?注册中心挂了可以继续通信吗?说说一次 rpc 请求的流程?
3 |
4 | ## 面试官心理分析
5 | MQ、ES、Redis、Dubbo,上来先问你一些**思考性的问题**、**原理**,比如 kafka 高可用架构原理、es 分布式架构原理、redis 线程模型原理、Dubbo 工作原理;之后就是生产环境里可能会碰到的一些问题,因为每种技术引入之后生产环境都可能会碰到一些问题;再来点综合的,就是系统设计,比如让你设计一个 MQ、设计一个搜索引擎、设计一个缓存、设计一个 rpc 框架等等。
6 |
7 | 那既然开始聊分布式系统了,自然重点先聊聊 dubbo 了,毕竟 dubbo 是目前事实上大部分公司的分布式系统的 rpc 框架标准,基于 dubbo 也可以构建一整套的微服务架构。但是需要自己大量开发。
8 |
9 | 当然去年开始 spring cloud 非常火,现在大量的公司开始转向 spring cloud 了,spring cloud 人家毕竟是微服务架构的全家桶式的这么一个东西。但是因为很多公司还在用 dubbo,所以 dubbo 肯定会是目前面试的重点,何况人家 dubbo 现在重启开源社区维护了,捐献给了 apache,未来应该也还是有一定市场和地位的。
10 |
11 | 既然聊 dubbo,那肯定是先从 dubbo 原理开始聊了,你先说说 dubbo 支撑 rpc 分布式调用的架构啥的,然后说说一次 rpc 请求 dubbo 是怎么给你完成的,对吧。
12 |
13 | ## 面试题剖析
14 | ### dubbo 工作原理
15 | - 第一层:service 层,接口层,给服务提供者和消费者来实现的
16 | - 第二层:config 层,配置层,主要是对 dubbo 进行各种配置的
17 | - 第三层:proxy 层,服务代理层,无论是 consumer 还是 provider,dubbo 都会给你生成代理,代理之间进行网络通信
18 | - 第四层:registry 层,服务注册层,负责服务的注册与发现
19 | - 第五层:cluster 层,集群层,封装多个服务提供者的路由以及负载均衡,将多个实例组合成一个服务
20 | - 第六层:monitor 层,监控层,对 rpc 接口的调用次数和调用时间进行监控
21 | - 第七层:protocal 层,远程调用层,封装 rpc 调用
22 | - 第八层:exchange 层,信息交换层,封装请求响应模式,同步转异步
23 | - 第九层:transport 层,网络传输层,抽象 mina 和 netty 为统一接口
24 | - 第十层:serialize 层,数据序列化层
25 |
26 | ### 工作流程
27 | - 第一步:provider 向注册中心去注册
28 | - 第二步:consumer 从注册中心订阅服务,注册中心会通知 consumer 注册好的服务
29 | - 第三步:consumer 调用 provider
30 | - 第四步:consumer 和 provider 都异步通知监控中心
31 |
32 | 
33 |
34 | ### 注册中心挂了可以继续通信吗?
35 | 可以,因为刚开始初始化的时候,消费者会将提供者的地址等信息**拉取到本地缓存**,所以注册中心挂了可以继续通信。
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-rpc-design.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何自己设计一个类似 Dubbo 的 RPC 框架?
3 |
4 | ## 面试官心理分析
5 | 说实话,就这问题,其实就跟问你如何自己设计一个 MQ 一样的道理,就考两个:
6 | - 你有没有对某个 rpc 框架原理有非常深入的理解。
7 | - 你能不能从整体上来思考一下,如何设计一个 rpc 框架,考考你的系统设计能力。
8 |
9 | ## 面试题剖析
10 | 其实问到你这问题,你起码不能认怂,因为是知识的扫盲,那我不可能给你深入讲解什么 kafka 源码剖析,dubbo 源码剖析,何况我就算讲了,你要真的消化理解和吸收,起码个把月以后了。
11 |
12 | 所以我给大家一个建议,遇到这类问题,起码从你了解的类似框架的原理入手,自己说说参照 dubbo 的原理,你来设计一下,举个例子,dubbo 不是有那么多分层么?而且每个分层是干啥的,你大概是不是知道?那就按照这个思路大致说一下吧,起码你不能懵逼,要比那些上来就懵,啥也说不出来的人要好一些。
13 |
14 | 举个栗子,我给大家说个最简单的回答思路:
15 | - 上来你的服务就得去注册中心注册吧,你是不是得有个注册中心,保留各个服务的信息,可以用 zookeeper 来做,对吧。
16 | - 然后你的消费者需要去注册中心拿对应的服务信息吧,对吧,而且每个服务可能会存在于多台机器上。
17 | - 接着你就该发起一次请求了,咋发起?当然是基于动态代理了,你面向接口获取到一个动态代理,这个动态代理就是接口在本地的一个代理,然后这个代理会找到服务对应的机器地址。
18 | - 然后找哪个机器发送请求?那肯定得有个负载均衡算法了,比如最简单的可以随机轮询是不是。
19 | - 接着找到一台机器,就可以跟它发送请求了,第一个问题咋发送?你可以说用 netty 了,nio 方式;第二个问题发送啥格式数据?你可以说用 hessian 序列化协议了,或者是别的,对吧。然后请求过去了。
20 | - 服务器那边一样的,需要针对你自己的服务生成一个动态代理,监听某个网络端口了,然后代理你本地的服务代码。接收到请求的时候,就调用对应的服务代码,对吧。
21 |
22 | 这就是一个最最基本的 rpc 框架的思路,先不说你有多牛逼的技术功底,哪怕这个最简单的思路你先给出来行不行?
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-serialization-protocol.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的?
3 |
4 | ## 面试官心理分析
5 | 上一个问题,说说 dubbo 的基本工作原理,那是你必须知道的,至少要知道 dubbo 分成哪些层,然后平时怎么发起 rpc 请求的,注册、发现、调用,这些是基本的。
6 |
7 | 接着就可以针对底层进行深入的问问了,比如第一步就可以先问问序列化协议这块,就是平时 RPC 的时候怎么走的?
8 |
9 | ## 面试题剖析
10 | **序列化**,就是把数据结构或者是一些对象,转换为二进制串的过程,而**反序列化**是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程。
11 |
12 | 
13 |
14 | ### dubbo 支持不同的通信协议
15 | - dubbo 协议
16 |
17 | **默认**就是走 dubbo 协议,单一长连接,进行的是 NIO 异步通信,基于 hessian 作为序列化协议。使用的场景是:传输数据量小(每次请求在 100kb 以内),但是并发量很高。
18 |
19 | 为了要支持高并发场景,一般是服务提供者就几台机器,但是服务消费者有上百台,可能每天调用量达到上亿次!此时用长连接是最合适的,就是跟每个服务消费者维持一个长连接就可以,可能总共就 100 个连接。然后后面直接基于长连接 NIO 异步通信,可以支撑高并发请求。
20 |
21 | 长连接,通俗点说,就是建立连接过后可以持续发送请求,无须再建立连接。
22 |
23 | 
24 |
25 | 而短连接,每次要发送请求之前,需要先重新建立一次连接。
26 |
27 | 
28 |
29 | - rmi 协议
30 |
31 | 走 Java 二进制序列化,多个短连接,适合消费者和提供者数量差不多的情况,适用于文件的传输,一般较少用。
32 |
33 | - hessian 协议
34 |
35 | 走 hessian 序列化协议,多个短连接,适用于提供者数量比消费者数量还多的情况,适用于文件的传输,一般较少用。
36 |
37 | - http 协议
38 |
39 | 走 json 序列化。
40 |
41 | - webservice
42 |
43 | 走 SOAP 文本序列化。
44 |
45 | ### dubbo 支持的序列化协议
46 | dubbo 支持 hession、Java 二进制序列化、json、SOAP 文本序列化多种序列化协议。但是 hessian 是其默认的序列化协议。
47 |
48 | ### 说一下 Hessian 的数据结构
49 | Hessian 的对象序列化机制有 8 种原始类型:
50 |
51 | - 原始二进制数据
52 | - boolean
53 | - 64-bit date(64 位毫秒值的日期)
54 | - 64-bit double
55 | - 32-bit int
56 | - 64-bit long
57 | - null
58 | - UTF-8 编码的 string
59 |
60 | 另外还包括 3 种递归类型:
61 |
62 | - list for lists and arrays
63 | - map for maps and dictionaries
64 | - object for objects
65 |
66 | 还有一种特殊的类型:
67 |
68 | - ref:用来表示对共享对象的引用。
69 |
70 | ### 为什么 PB 的效率是最高的?
71 | 可能有一些同学比较习惯于 `JSON` or `XML` 数据存储格式,对于 `Protocol Buffer` 还比较陌生。`Protocol Buffer` 其实是 Google 出品的一种轻量并且高效的结构化数据存储格式,性能比 `JSON`、`XML` 要高很多。
72 |
73 | 其实 PB 之所以性能如此好,主要得益于两个:**第一**,它使用 proto 编译器,自动进行序列化和反序列化,速度非常快,应该比 `XML` 和 `JSON` 快上了 `20~100` 倍;**第二**,它的数据压缩效果好,就是说它序列化后的数据量体积小。因为体积小,传输起来带宽和速度上会有优化。
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-service-management.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何基于 dubbo 进行服务治理、服务降级、失败重试以及超时重试?
3 |
4 | ## 面试官心理分析
5 | 服务治理,这个问题如果问你,其实就是看看你有没有**服务治理**的思想,因为这个是做过复杂微服务的人肯定会遇到的一个问题。
6 |
7 | **服务降级**,这个是涉及到复杂分布式系统中必备的一个话题,因为分布式系统互相来回调用,任何一个系统故障了,你不降级,直接就全盘崩溃?那就太坑爹了吧。
8 |
9 | **失败重试**,分布式系统中网络请求如此频繁,要是因为网络问题不小心失败了一次,是不是要重试?
10 |
11 | **超时重试**,跟上面一样,如果不小心网络慢一点,超时了,如何重试?
12 |
13 | ## 面试题剖析
14 | ### 服务治理
15 | #### 1. 调用链路自动生成
16 | 一个大型的分布式系统,或者说是用现在流行的微服务架构来说吧,**分布式系统由大量的服务组成**。那么这些服务之间互相是如何调用的?调用链路是啥?说实话,几乎到后面没人搞的清楚了,因为服务实在太多了,可能几百个甚至几千个服务。
17 |
18 | 那就需要基于 dubbo 做的分布式系统中,对各个服务之间的调用自动记录下来,然后自动将**各个服务之间的依赖关系和调用链路生成出来**,做成一张图,显示出来,大家才可以看到对吧。
19 |
20 | 
21 |
22 | #### 2. 服务访问压力以及时长统计
23 | 需要自动统计**各个接口和服务之间的调用次数以及访问延时**,而且要分成两个级别。
24 |
25 | - 一个级别是接口粒度,就是每个服务的每个接口每天被调用多少次,TP50/TP90/TP99,三个档次的请求延时分别是多少;
26 | - 第二个级别是从源头入口开始,一个完整的请求链路经过几十个服务之后,完成一次请求,每天全链路走多少次,全链路请求延时的 TP50/TP90/TP99,分别是多少。
27 |
28 | 这些东西都搞定了之后,后面才可以来看当前系统的压力主要在哪里,如何来扩容和优化啊。
29 |
30 | #### 3. 其它
31 | - 服务分层(避免循环依赖)
32 | - 调用链路失败监控和报警
33 | - 服务鉴权
34 | - 每个服务的可用性的监控(接口调用成功率?几个 9?99.99%,99.9%,99%)
35 |
36 | ### 服务降级
37 | 比如说服务 A 调用服务 B,结果服务 B 挂掉了,服务 A 重试几次调用服务 B,还是不行,那么直接降级,走一个备用的逻辑,给用户返回响应。
38 |
39 | 举个栗子,我们有接口 `HelloService`。`HelloServiceImpl` 有该接口的具体实现。
40 |
41 | ```java
42 | public interface HelloService {
43 | void sayHello();
44 | }
45 |
46 | public class HelloServiceImpl implements HelloService {
47 | public void sayHello() {
48 | System.out.println("hello world......");
49 | }
50 | }
51 | ```
52 |
53 | ```xml
54 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 | ```
83 |
84 | 我们调用接口失败的时候,可以通过 `mock` 统一返回 null。
85 |
86 | mock 的值也可以修改为 true,然后再跟接口同一个路径下实现一个 Mock 类,命名规则是 “接口名称+`Mock`” 后缀。然后在 Mock 类里实现自己的降级逻辑。
87 |
88 | ```java
89 | public class HelloServiceMock implements HelloService {
90 | public void sayHello() {
91 | // 降级逻辑
92 | }
93 | }
94 | ```
95 |
96 | ### 失败重试和超时重试
97 | 所谓失败重试,就是 consumer 调用 provider 要是失败了,比如抛异常了,此时应该是可以重试的,或者调用超时了也可以重试。配置如下:
98 |
99 | ```xml
100 |
101 | ```
102 |
103 | 举个栗子。
104 |
105 | 某个服务的接口,要耗费 5s,你这边不能干等着,你这边配置了 timeout 之后,我等待 2s,还没返回,我直接就撤了,不能干等你。
106 |
107 | 可以结合你们公司具体的场景来说说你是怎么设置这些参数的:
108 |
109 | - `timeout`:一般设置为 `200ms`,我们认为不能超过 `200ms` 还没返回。
110 | - `retries`:设置 retries,一般是在读请求的时候,比如你要查询个数据,你可以设置个 retries,如果第一次没读到,报错,重试指定的次数,尝试再次读取。
--------------------------------------------------------------------------------
/docs/distributed-system/dubbo-spi.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | dubbo 的 spi 思想是什么?
3 |
4 | ## 面试官心理分析
5 | 继续深入问呗,前面一些基础性的东西问完了,确定你应该都 ok,了解 dubbo 的一些基本东西,那么问个稍微难一点点的问题,就是 spi,先问问你 spi 是啥?然后问问你 dubbo 的 spi 是怎么实现的?
6 |
7 | 其实就是看看你对 dubbo 的掌握如何。
8 |
9 | ## 面试题剖析
10 | ### spi 是啥?
11 | spi,简单来说,就是 `service provider interface`,说白了是什么意思呢,比如你有个接口,现在这个接口有 3 个实现类,那么在系统运行的时候对这个接口到底选择哪个实现类呢?这就需要 spi 了,需要**根据指定的配置**或者是**默认的配置**,去**找到对应的实现类**加载进来,然后用这个实现类的实例对象。
12 |
13 | 举个栗子。
14 |
15 | 你有一个接口 A。A1/A2/A3 分别是接口A的不同实现。你通过配置 `接口 A = 实现 A2`,那么在系统实际运行的时候,会加载你的配置,用实现 A2 实例化一个对象来提供服务。
16 |
17 | spi 机制一般用在哪儿?**插件扩展的场景**,比如说你开发了一个给别人使用的开源框架,如果你想让别人自己写个插件,插到你的开源框架里面,从而扩展某个功能,这个时候 spi 思想就用上了。
18 |
19 | ### Java spi 思想的体现
20 | spi 经典的思想体现,大家平时都在用,比如说 jdbc。
21 |
22 | Java 定义了一套 jdbc 的接口,但是 Java 并没有提供 jdbc 的实现类。
23 |
24 | 但是实际上项目跑的时候,要使用 jdbc 接口的哪些实现类呢?一般来说,我们要**根据自己使用的数据库**,比如 mysql,你就将 `mysql-jdbc-connector.jar` 引入进来;oracle,你就将 `oracle-jdbc-connector.jar` 引入进来。
25 |
26 | 在系统跑的时候,碰到你使用 jdbc 的接口,他会在底层使用你引入的那个 jar 中提供的实现类。
27 |
28 | ### dubbo 的 spi 思想
29 | dubbo 也用了 spi 思想,不过没有用 jdk 的 spi 机制,是自己实现的一套 spi 机制。
30 | ```java
31 | Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
32 | ```
33 |
34 | Protocol 接口,在系统运行的时候,,dubbo 会判断一下应该选用这个 Protocol 接口的哪个实现类来实例化对象来使用。
35 |
36 | 它会去找一个你配置的 Protocol,将你配置的 Protocol 实现类,加载到 jvm 中来,然后实例化对象,就用你的那个 Protocol 实现类就可以了。
37 |
38 |
39 | 上面那行代码就是 dubbo 里大量使用的,就是对很多组件,都是保留一个接口和多个实现,然后在系统运行的时候动态根据配置去找到对应的实现类。如果你没配置,那就走默认的实现好了,没问题。
40 | ```java
41 | @SPI("dubbo")
42 | public interface Protocol {
43 |
44 | int getDefaultPort();
45 |
46 | @Adaptive
47 | Exporter export(Invoker invoker) throws RpcException;
48 |
49 | @Adaptive
50 | Invoker refer(Class type, URL url) throws RpcException;
51 |
52 | void destroy();
53 |
54 | }
55 | ```
56 |
57 | 在 dubbo 自己的 jar 里,在`/META_INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol`文件中:
58 | ```xml
59 | dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
60 | http=com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
61 | hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
62 | ```
63 |
64 | 所以说,这就看到了 dubbo 的 spi 机制默认是怎么玩儿的了,其实就是 Protocol 接口,`@SPI("dubbo")` 说的是,通过 SPI 机制来提供实现类,实现类是通过 dubbo 作为默认 key 去配置文件里找到的,配置文件名称与接口全限定名一样的,通过 dubbo 作为 key 可以找到默认的实现类就是 `com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol`。
65 |
66 |
67 | 如果想要动态替换掉默认的实现类,需要使用 `@Adaptive` 接口,Protocol 接口中,有两个方法加了 `@Adaptive` 注解,就是说那俩接口会被代理实现。
68 |
69 | 啥意思呢?
70 |
71 | 比如这个 Protocol 接口搞了俩 `@Adaptive` 注解标注了方法,在运行的时候会针对 Protocol 生成代理类,这个代理类的那俩方法里面会有代理代码,代理代码会在运行的时候动态根据 url 中的 protocol 来获取那个 key,默认是 dubbo,你也可以自己指定,你如果指定了别的 key,那么就会获取别的实现类的实例了。
72 |
73 | ### 如何自己扩展 dubbo 中的组件
74 | 下面来说说怎么来自己扩展 dubbo 中的组件。
75 |
76 | 自己写个工程,要是那种可以打成 jar 包的,里面的 `src/main/resources` 目录下,搞一个 `META-INF/services`,里面放个文件叫:`com.alibaba.dubbo.rpc.Protocol`,文件里搞一个`my=com.bingo.MyProtocol`。自己把 jar 弄到 nexus 私服里去。
77 |
78 | 然后自己搞一个 `dubbo provider` 工程,在这个工程里面依赖你自己搞的那个 jar,然后在 spring 配置文件里给个配置:
79 |
80 | ```xml
81 |
82 | ```
83 | provider 启动的时候,就会加载到我们 jar 包里的`my=com.bingo.MyProtocol` 这行配置里,接着会根据你的配置使用你定义好的 MyProtocol 了,这个就是简单说明一下,你通过上述方式,可以替换掉大量的 dubbo 内部的组件,就是扔个你自己的 jar 包,然后配置一下即可。
84 |
85 | 
86 |
87 | dubbo 里面提供了大量的类似上面的扩展点,就是说,你如果要扩展一个东西,只要自己写个 jar,让你的 consumer 或者是 provider 工程,依赖你的那个 jar,在你的 jar 里指定目录下配置好接口名称对应的文件,里面通过 `key=实现类`。
88 |
89 | 然后对于对应的组件,类似 `` 用你的那个 key 对应的实现类来实现某个接口,你可以自己去扩展 dubbo 的各种功能,提供你自己的实现。
--------------------------------------------------------------------------------
/docs/distributed-system/images/async-replication-data-lose-case.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/async-replication-data-lose-case.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/consistent-hashing-algorithm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/consistent-hashing-algorithm.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/distributed-system-request-sequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/distributed-system-request-sequence.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/distributed-transaction-TCC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/distributed-transaction-TCC.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/distributed-transaction-XA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/distributed-transaction-XA.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/distributed-transaction-local-message-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/distributed-transaction-local-message-table.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/distributed-transaction-reliable-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/distributed-transaction-reliable-message.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/dubbo-keep-connection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/dubbo-keep-connection.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/dubbo-not-keep-connection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/dubbo-not-keep-connection.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/dubbo-operating-principle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/dubbo-operating-principle.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/dubbo-service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/dubbo-service-invoke-road.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/dubbo-spi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/dubbo-spi.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/e-commerce-website-detail-page-architecture-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/e-commerce-website-detail-page-architecture-1.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/e-commerce-website-detail-page-architecture-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/e-commerce-website-detail-page-architecture-2.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/hash-slot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/hash-slot.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/hash.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/icon.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/serialize-deserialize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/serialize-deserialize.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/service-invoke-road.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/simple-distributed-system-oa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/simple-distributed-system-oa.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-active-standby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-active-standby.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-centralized-storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-centralized-storage.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-distributed-coordination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-distributed-coordination.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-distributed-lock-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-distributed-lock-demo.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-distributed-lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-distributed-lock.png
--------------------------------------------------------------------------------
/docs/distributed-system/images/zookeeper-meta-data-manage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/distributed-system/images/zookeeper-meta-data-manage.png
--------------------------------------------------------------------------------
/docs/distributed-system/why-dubbo.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 为什么要进行系统拆分?如何进行系统拆分?拆分后不用 dubbo 可以吗?
3 |
4 | ## 面试官心理分析
5 | 从这个问题开始就进行分布式系统环节了,现在出去面试分布式都成标配了,没有哪个公司不问问你分布式的事儿。你要是不会分布式的东西,简直这简历没法看,没人会让你去面试。
6 |
7 | 其实为啥会这样呢?这就是因为整个大行业技术发展的原因。
8 |
9 | 早些年,印象中在 2010 年初的时候,整个 IT 行业,很少有人谈分布式,更不用说微服务,虽然很多 BAT 等大型公司,因为系统的复杂性,很早就是分布式架构,大量的服务,只不过微服务大多基于自己搞的一套框架来实现而已。
10 |
11 | 但是确实,那个年代,大家很重视 ssh2,很多中小型公司几乎大部分都是玩儿 struts2、spring、hibernate,稍晚一些,才进入了 spring mvc、spring、mybatis 的组合。那个时候整个行业的技术水平就是那样,当年 oracle 很火,oracle 管理员很吃香,oracle 性能优化啥的都是 IT 男的大杀招啊。连大数据都没人提,当年 OCP、OCM 等认证培训机构,火的不行。
12 |
13 | 但是确实随着时代的发展,慢慢的,很多公司开始接受分布式系统架构了,这里面尤为对行业有至关重要影响的,是阿里的 dubbo,**某种程度上而言,阿里在这里推动了行业技术的前进**。
14 |
15 | 正是因为有阿里的 dubbo,很多中小型公司才可以基于 dubbo,来把系统拆分成很多的服务,每个人负责一个服务,大家的代码都没有冲突,服务可以自治,自己选用什么技术都可以,每次发布如果就改动一个服务那就上线一个服务好了,不用所有人一起联调,每次发布都是几十万行代码,甚至几百万行代码了。
16 |
17 | 直到今日,很高兴看到分布式系统都成行业面试标配了,任何一个普通的程序员都该掌握这个东西,其实这是行业的进步,也是所有 IT 码农的技术进步。所以既然分布式都成标配了,那么面试官当然会问了,因为很多公司现在都是分布式、微服务的架构,那面试官当然得考察考察你了。
18 |
19 | ## 面试题剖析
20 | ### 为什么要将系统进行拆分?
21 | 网上查查,答案极度零散和复杂,很琐碎,原因一大坨。但是我这里给大家直观的感受:
22 |
23 | 要是**不拆分**,一个大系统几十万行代码,20 个人维护一份代码,简直是悲剧啊。代码经常改着改着就冲突了,各种代码冲突和合并要处理,非常耗费时间;经常我改动了我的代码,你调用了我的,导致你的代码也得重新测试,麻烦的要死;然后每次发布都是几十万行代码的系统一起发布,大家得一起提心吊胆准备上线,几十万行代码的上线,可能每次上线都要做很多的检查,很多异常问题的处理,简直是又麻烦又痛苦;而且如果我现在打算把技术升级到最新的 spring 版本,还不行,因为这可能导致你的代码报错,我不敢随意乱改技术。
24 |
25 | 假设一个系统是 20 万行代码,其中 A 在里面改了 1000 行代码,但是此时发布的时候是这个 20 万行代码的大系统一块儿发布。就意味着 20 万上代码在线上就可能出现各种变化,20 个人,每个人都要紧张地等在电脑面前,上线之后,检查日志,看自己负责的那一块儿有没有什么问题。
26 |
27 | A 就检查了自己负责的 1 万行代码对应的功能,确保 ok 就闪人了;结果不巧的是,A 上线的时候不小心修改了线上机器的某个配置,导致另外 B 和 C 负责的 2 万行代码对应的一些功能,出错了。
28 |
29 | 几十个人负责维护一个几十万行代码的单块应用,每次上线,准备几个礼拜,上线 -> 部署 -> 检查自己负责的功能。
30 |
31 | **拆分了以后**,整个世界清爽了,几十万行代码的系统,拆分成 20 个服务,平均每个服务就 1~2 万行代码,每个服务部署到单独的机器上。20 个工程,20 个 git 代码仓库,20 个开发人员,每个人维护自己的那个服务就可以了,是自己独立的代码,跟别人没关系。再也没有代码冲突了,爽。每次就测试我自己的代码就可以了,爽。每次就发布我自己的一个小服务就可以了,爽。技术上想怎么升级就怎么升级,保持接口不变就可以了,真爽。
32 |
33 | 所以简单来说,一句话总结,如果是那种代码量多达几十万行的中大型项目,团队里有几十个人,那么如果不拆分系统,**开发效率极其低下**,问题很多。但是拆分系统之后,每个人就负责自己的一小部分就好了,可以随便玩儿随便弄。分布式系统拆分之后,可以大幅度提升复杂系统大型团队的开发效率。
34 |
35 | 但是同时,也要**提醒**的一点是,系统拆分成分布式系统之后,大量的分布式系统面临的问题也是接踵而来,所以后面的问题都是在**围绕分布式系统带来的复杂技术挑战**在说。
36 |
37 | ### 如何进行系统拆分?
38 | 这个问题说大可以很大,可以扯到领域驱动模型设计上去,说小了也很小,我不太想给大家太过于学术的说法,因为你也不可能背这个答案,过去了直接说吧。还是说的简单一点,大家自己到时候知道怎么回答就行了。
39 |
40 | 系统拆分为分布式系统,拆成多个服务,拆成微服务的架构,是需要拆很多轮的。并不是说上来一个架构师一次就给拆好了,而以后都不用拆。
41 |
42 | 第一轮;团队继续扩大,拆好的某个服务,刚开始是 1 个人维护 1 万行代码,后来业务系统越来越复杂,这个服务是 10 万行代码,5 个人;第二轮,1个服务 -> 5个服务,每个服务 2 万行代码,每人负责一个服务。
43 |
44 | 如果是多人维护一个服务,最理想的情况下,几十个人,1 个人负责 1 个或 2~3 个服务;某个服务工作量变大了,代码量越来越多,某个同学,负责一个服务,代码量变成了 10 万行了,他自己不堪重负,他现在一个人拆开,5 个服务,1 个人顶着,负责 5 个人,接着招人,2 个人,给那个同学带着,3 个人负责 5 个服务,其中 2 个人每个人负责 2 个服务,1 个人负责 1 个服务。
45 |
46 | 个人建议,一个服务的代码不要太多,1 万行左右,两三万撑死了吧。
47 |
48 | 大部分的系统,是要进行**多轮拆分**的,第一次拆分,可能就是将以前的多个模块该拆分开来了,比如说将电商系统拆分成订单系统、商品系统、采购系统、仓储系统、用户系统,等等吧。
49 |
50 | 但是后面可能每个系统又变得越来越复杂了,比如说采购系统里面又分成了供应商管理系统、采购单管理系统,订单系统又拆分成了购物车系统、价格系统、订单管理系统。
51 |
52 | 扯深了实在很深,所以这里先给大家举个例子,你自己感受一下,**核心意思就是根据情况,先拆分一轮,后面如果系统更复杂了,可以继续分拆**。你根据自己负责系统的例子,来考虑一下就好了。
53 |
54 | ### 拆分后不用 dubbo 可以吗?
55 | 当然可以了,大不了最次,就是各个系统之间,直接基于 spring mvc,就纯 http 接口互相通信呗,还能咋样。但是这个肯定是有问题的,因为 http 接口通信维护起来成本很高,你要考虑**超时重试**、**负载均衡**等等各种乱七八糟的问题,比如说你的订单系统调用商品系统,商品系统部署了 5 台机器,你怎么把请求均匀地甩给那 5 台机器?这不就是负载均衡?你要是都自己搞那是可以的,但是确实很痛苦。
56 |
57 | 所以 dubbo 说白了,是一种 rpc 框架,就是说本地就是进行接口调用,但是 dubbo 会代理这个调用请求,跟远程机器网络通信,给你处理掉负载均衡、服务实例上下线自动感知、超时重试等等乱七八糟的问题。那你就不用自己做了,用 dubbo 就可以了。
--------------------------------------------------------------------------------
/docs/distributed-system/zookeeper-application-scenarios.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | zookeeper 都有哪些使用场景?
3 |
4 | ## 面试官心理分析
5 | 现在聊的 topic 是分布式系统,面试官跟你聊完了 dubbo 相关的一些问题之后,已经确认你对分布式服务框架/RPC框架基本都有一些认知了。那么他可能开始要跟你聊分布式相关的其它问题了。
6 |
7 | 分布式锁这个东西,很常用的,你做 Java 系统开发,分布式系统,可能会有一些场景会用到。最常用的分布式锁就是基于 zookeeper 来实现的。
8 |
9 | 其实说实话,问这个问题,一般就是看看你是否了解 zookeeper,因为 zookeeper 是分布式系统中很常见的一个基础系统。而且问的话常问的就是说 zookeeper 的使用场景是什么?看你知道不知道一些基本的使用场景。但是其实 zookeeper 挖深了自然是可以问的很深很深的。
10 |
11 | ## 面试题剖析
12 | 大致来说,zookeeper 的使用场景如下,我就举几个简单的,大家能说几个就好了:
13 |
14 | - 分布式协调
15 | - 分布式锁
16 | - 元数据/配置信息管理
17 | - HA高可用性
18 |
19 | ### 分布式协调
20 | 这个其实是 zookeeper 很经典的一个用法,简单来说,就好比,你 A 系统发送个请求到 mq,然后 B 系统消息消费之后处理了。那 A 系统如何知道 B 系统的处理结果?用 zookeeper 就可以实现分布式系统之间的协调工作。A 系统发送请求之后可以在 zookeeper 上**对某个节点的值注册个监听器**,一旦 B 系统处理完了就修改 zookeeper 那个节点的值,A 系统立马就可以收到通知,完美解决。
21 |
22 | 
23 |
24 | ### 分布式锁
25 | 举个栗子。对某一个数据连续发出两个修改操作,两台机器同时收到了请求,但是只能一台机器先执行完另外一个机器再执行。那么此时就可以使用 zookeeper 分布式锁,一个机器接收到了请求之后先获取 zookeeper 上的一把分布式锁,就是可以去创建一个 znode,接着执行操作;然后另外一个机器也**尝试去创建**那个 znode,结果发现自己创建不了,因为被别人创建了,那只能等着,等第一个机器执行完了自己再执行。
26 |
27 | 
28 |
29 | ### 元数据/配置信息管理
30 | zookeeper 可以用作很多系统的配置信息的管理,比如 kafka、storm 等等很多分布式系统都会选用 zookeeper 来做一些元数据、配置信息的管理,包括 dubbo 注册中心不也支持 zookeeper 么?
31 |
32 | 
33 |
34 | ### HA高可用性
35 | 这个应该是很常见的,比如 hadoop、hdfs、yarn 等很多大数据系统,都选择基于 zookeeper 来开发 HA 高可用机制,就是一个**重要进程一般会做主备**两个,主进程挂了立马通过 zookeeper 感知到切换到备用进程。
36 |
37 | 
--------------------------------------------------------------------------------
/docs/from-readers/README.md:
--------------------------------------------------------------------------------
1 | # GitHub 开发者参与专区
2 | [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)。
3 |
4 | ## Articles
5 | - [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
6 | - [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
7 |
8 | ## Contributors
9 | This project exists thanks to all the people who contribute.
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/docs/from-readers/doocs-advanced-java-attention.md:
--------------------------------------------------------------------------------
1 | # 提交注意事项
2 | 项目需要有一个统一的内容提交规范,没有规范的项目将会是一团乱麻,维护起来也会很费劲儿。以下列出了几个小点,看似很多,实际上都非常容易做到,供朋友们参考。
3 |
4 | > 如果你有好的 idea,欢迎 issues 交流。
5 |
6 | ## 关于提交形式
7 | 本项目**不希望以外链的形式引入内容**。如果你有好的内容推荐,请在此项目基础上创建新的文件,完善内容后再提交。
8 |
9 | ## 关于文件命名与存放位置
10 | 文件请以 “`GitHub ID` + 文章主题” 命名,确保每位朋友的提交内容不会冲突。文章主题统一采用**英文**命名,请勿使用中文或者汉语拼音,文件类型统一选择 `.md`。
11 |
12 | 给个示例。某位朋友的 GitHub ID 是 [SnailClimb](https://github.com/snailclimb),想分享一篇关于 Kafka 实践相关的文章,那么文件名可以是 `snailclimb-kafka-in-action.md`。
13 |
14 | 最终文件存放于 `docs/from-readers/` 目录下,即与[本文件](/docs/from-readers/doocs-advanced-java-attention.md)处于同一级别。**文件命名、存放位置不规范的文章将不予采纳**。
15 |
16 | ## 关于文章内容
17 | 仅收录与此项目主题相关的优质文章,可以是[高并发](https://github.com/doocs/advanced-java#高并发架构)、[分布式](https://github.com/doocs/advanced-java#分布式系统)、[高可用](https://github.com/doocs/advanced-java#高可用架构)、[微服务](https://github.com/doocs/advanced-java#高并发架构微服务架构)等相关领域的内容。**其它主题的文章将不会被采纳**。
18 |
19 | ## 关于文章排版
20 | 文章排版保持整洁美观。中英文之间、中文与数字之间用空格隔开是最基本的。
21 |
22 | > 有研究显示,打字的时候不喜欢在中文和英文之间加空格的人,感情路都走得很辛苦,有七成的比例会在 34 岁的时候跟自己不爱的人结婚,而其余三成的人最后只能把遗产留给自己的猫。毕竟爱情跟书写都需要适时地留白。
23 |
24 | 图片统一使用 `` 进行相对路径的引用,并同时存放于根目录 `images/` 和本专区目录 `docs/from-readers/images/` **两个位置**之下(这是为了确保在 GitHub 和 GitHub Page 都能正常显示图片;图片并不限定 `.png` 格式),作图推荐使用在线工具 [ProcessOn](https://www.processon.com/i/594a16f7e4b0e1bb14fe2fac)。具体文章书写规范请参考《[中文技术文档的写作规范](https://github.com/ruanyf/document-style-guide)》。
25 |
26 | 以下是文章基本的结构,供朋友们参考。
27 |
28 | ```markdown
29 | # 这是文章标题
30 | - Author: [GitHub ID](https://github.com/your-github-id)
31 | - Description: 文章的简单描述信息。
32 | - ...
33 |
34 | ## 这是一级索引
35 | ...
36 | ### 这是二级索引
37 | ...
38 |
39 | ## 这是一级索引
40 | ...
41 | ### 这是二级索引
42 | ...
43 | ```
44 |
45 | ## 关于 Git 提交信息
46 | Git 提交信息统一使用英文,本项目遵从 [Angular JS Git 提交规范](https://github.com/angular/angular.js/commits/master)。e.g.
47 |
48 | ```bash
49 | git commit -m "docs(from-readers): add an article about Kafka"
50 | ```
51 |
52 | Git 提交信息不规范的 PR 将不予合并。
--------------------------------------------------------------------------------
/docs/from-readers/images/advanced-java-doocs-shishan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/from-readers/images/advanced-java-doocs-shishan.png
--------------------------------------------------------------------------------
/docs/from-readers/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/from-readers/images/icon.png
--------------------------------------------------------------------------------
/docs/from-readers/rights-defending-movement.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## 声明
6 | 读者朋友们,你们好。[advanced-java](https://github.com/doocs/advanced-java) 项目自创建以来,一直收到很多读者的反馈,也在不断改进、完善内容,只希望可以用心做得更好。然而,网上抄袭、侵权的现象普遍存在,我想,不能任由这种恶劣行为肆虐。
7 |
8 | 因此,在此说明,除了 [doocs/advanced-java](https://github.com/doocs/advanced-java)、“**石杉的架构笔记**”,网上其它平台上如若出现了与本项目内容雷同甚至完全一致的文章,**不注明出处、甚至打着原创的标签忽悠读者**的,欢迎举报,也欢迎在此提供侵权名单,曝光抄袭者,谢谢。
9 |
10 | 希望各位朋友都注重**维护他人知识产权,尊重他人劳动成果**,我们共同构建一个健康的知识分享生态圈。
11 |
12 | ## 抄袭名单列表
13 |
14 | ### 博客
15 | | # | 文章 | 抄袭者 |
16 | |---|---|---|
17 | | 1 | [MySQL 面试题](https://jsbintask.cn/2019/02/17/interview/interview-high-concurrency-design/) | jsbintask |
18 | | 2 | [消息队列面试题](https://blog.51cto.com/13904503/2351522) | Java邵先生 |
19 | | 3 | [高并发架构消息队列面试题解析](https://www.cnblogs.com/yuxiang1/p/10542569.html) | 手留余香-博客园 |
20 | | 4 | [消息中间件面试题:消息中间件的高可用](https://www.jianshu.com/p/92862edc7c51) | jsbintask-简书 |
21 | | 5 | [深入 Hystrix 执行时内部原理](https://www.jianshu.com/p/1a14401e219f) | kevin0016-简书 |
22 |
23 |
24 | ### 公众号
25 | | # | 文章 | 抄袭者 |
26 | |---|---|---|
27 |
28 | ### 头条号
29 | | # | 文章 | 抄袭者 |
30 | |---|---|---|
31 |
32 | ### 掘金
33 | | # | 文章 | 抄袭者 |
34 | |---|---|---|
35 |
36 | ### 知乎
37 | | # | 文章 | 抄袭者 | 备注 |
38 | |---|---|---|---|
39 | | 1 | [Java消息队列三道面试题详解!](https://zhuanlan.zhihu.com/p/62739616) | Java高级架构解析 | 严重抄袭 |
--------------------------------------------------------------------------------
/docs/high-availability/README.md:
--------------------------------------------------------------------------------
1 | # 高可用架构
--------------------------------------------------------------------------------
/docs/high-availability/e-commerce-website-detail-page-architecture.md:
--------------------------------------------------------------------------------
1 | ## 电商网站的商品详情页系统架构
2 |
3 | ### 小型电商网站的商品详情页系统架构
4 | 小型电商网站的页面展示采用页面全量静态化的思想。数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入 Nginx 服务器。用户浏览网站页面时,取用一个已经静态化好的 html 页面,直接返回回去,不涉及任何的业务逻辑处理。
5 |
6 | 
7 |
8 | 下面是页面模板的简单 Demo 。
9 |
10 | ```html
11 |
12 |
13 | 商品名称:#{productName}
14 | 商品价格:#{productPrice}
15 | 商品描述:#{productDesc}
16 |
17 |
18 | ```
19 |
20 | 这样做,**好处**在于,用户每次浏览一个页面,不需要进行任何的跟数据库的交互逻辑,也不需要执行任何的代码,直接返回一个 html 页面就可以了,速度和性能非常高。
21 |
22 | 对于小网站,页面很少,很实用,非常简单,Java 中可以使用 velocity、freemarker、thymeleaf 等等,然后做个 cms 页面内容管理系统,模板变更的时候,点击按钮或者系统自动化重新进行全量渲染。
23 |
24 | **坏处**在于,仅仅适用于一些小型的网站,比如页面的规模在几十到几万不等。对于一些大型的电商网站,亿级数量的页面,你说你每次页面模板修改了,都需要将这么多页面全量静态化,靠谱吗?每次渲染花个好几天时间,那你整个网站就废掉了。
25 |
26 | ### 大型电商网站的商品详情页系统架构
27 | 大型电商网站商品详情页的系统设计中,当商品数据发生变更时,会将变更消息压入 MQ 消息队列中。**缓存服务**从消息队列中消费这条消息时,感知到有数据发生变更,便通过调用数据服务接口,获取变更后的数据,然后将整合好的数据推送至 redis 中。Nginx 本地缓存的数据是有一定的时间期限的,比如说 10 分钟,当数据过期之后,它就会从 redis 获取到最新的缓存数据,并且缓存到自己本地。
28 |
29 | 用户浏览网页时,动态将 Nginx 本地数据渲染到本地 html 模板并返回给用户。
30 |
31 | 
32 |
33 |
34 | 虽然没有直接返回 html 页面那么快,但是因为数据在本地缓存,所以也很快,其实耗费的也就是动态渲染一个 html 页面的性能。如果 html 模板发生了变更,不需要将所有的页面重新静态化,也不需要发送请求,没有网络请求的开销,直接将数据渲染进最新的 html 页面模板后响应即可。
35 |
36 | 在这种架构下,我们需要**保证系统的高可用性**。
37 |
38 | 如果系统访问量很高,Nginx 本地缓存过期失效了,redis 中的缓存也被 LRU 算法给清理掉了,那么会有较高的访问量,从缓存服务调用商品服务。但如果此时商品服务的接口发生故障,调用出现了延时,缓存服务全部的线程都被这个调用商品服务接口给耗尽了,每个线程去调用商品服务接口的时候,都会卡住很长时间,后面大量的请求过来都会卡在那儿,此时缓存服务没有足够的线程去调用其它一些服务的接口,从而导致整个大量的商品详情页无法正常显示。
39 |
40 | 这其实就是一个商品接口服务故障导致缓存服务资源耗尽的现象。
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-execution-isolation.md:
--------------------------------------------------------------------------------
1 | ## Hystrix 隔离策略细粒度控制
2 | Hystrix 实现资源隔离,有两种策略:
3 |
4 | - 线程池隔离
5 | - 信号量隔离
6 |
7 | 对资源隔离这一块东西,其实可以做一定细粒度的一些控制。
8 |
9 | ### execution.isolation.strategy
10 | 指定了 HystrixCommand.run() 的资源隔离策略:`THREAD` or `SEMAPHORE`,一种基于线程池,一种基于信号量。
11 |
12 | ```java
13 | // to use thread isolation
14 | HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD)
15 |
16 | // to use semaphore isolation
17 | HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
18 | ```
19 |
20 | 线程池机制,每个 command 运行在一个线程中,限流是通过线程池的大小来控制的;信号量机制,command 是运行在调用线程中,通过信号量的容量来进行限流。
21 |
22 | 如何在线程池和信号量之间做选择?
23 |
24 | **默认的策略**就是线程池。
25 |
26 | **线程池**其实最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住。
27 |
28 | 而使用信号量的场景,通常是针对超大并发量的场景下,每个服务实例每秒都几百的 `QPS`,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护。一般用信号量常见于那种基于纯内存的一些业务逻辑服务,而不涉及到任何网络访问请求。
29 |
30 | ### command key & command group
31 | 我们使用线程池隔离,要怎么对**依赖服务**、**依赖服务接口**、**线程池**三者做划分呢?
32 |
33 | 每一个 command,都可以设置一个自己的名称 command key,同时可以设置一个自己的组 command group。
34 | ```java
35 | private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
36 | .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
37 |
38 | public CommandHelloWorld(String name) {
39 | super(cachedSetter);
40 | this.name = name;
41 | }
42 | ```
43 |
44 | command group 是一个非常重要的概念,默认情况下,就是通过 command group 来定义一个线程池的,而且还会通过 command group 来聚合一些监控和报警信息。同一个 command group 中的请求,都会进入同一个线程池中。
45 |
46 | ### command thread pool
47 | ThreadPoolKey 代表了一个 HystrixThreadPool,用来进行统一监控、统计、缓存。默认的 ThreadPoolKey 就是 command group 的名称。每个 command 都会跟它的 ThreadPoolKey 对应的 ThreadPool 绑定在一起。
48 |
49 | 如果不想直接用 command group,也可以手动设置 ThreadPool 的名称。
50 | ```java
51 | private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
52 | .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
53 | .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"));
54 |
55 | public CommandHelloWorld(String name) {
56 | super(cachedSetter);
57 | this.name = name;
58 | }
59 | ```
60 |
61 | ### command key & command group & command thread pool
62 | **command key** ,代表了一类 command,一般来说,代表了底层的依赖服务的一个接口。
63 |
64 | **command group** ,代表了某一个底层的依赖服务,这是很合理的,一个依赖服务可能会暴露出来多个接口,每个接口就是一个 command key。command group 在逻辑上去组织起来一堆 command key 的调用、统计信息、成功次数、timeout 超时次数、失败次数等,可以看到某一个服务整体的一些访问情况。一般来说,**推荐**根据一个服务区划分出一个线程池,command key 默认都是属于同一个线程池的。
65 |
66 | 比如说你以一个服务为粒度,估算出来这个服务每秒的所有接口加起来的整体 `QPS` 在 100 左右,你调用这个服务,当前这个服务部署了 10 个服务实例,每个服务实例上,其实用这个 command group 对应这个服务,给一个线程池,量大概在 10 个左右就可以了,你对整个服务的整体的访问 QPS 就大概在每秒 100 左右。
67 |
68 | 但是,如果说 command group 对应了一个服务,而这个服务暴露出来的几个接口,访问量很不一样,差异非常之大。你可能就希望在这个服务 command group 内部,包含的对应多个接口的 command key,做一些细粒度的资源隔离。就是说,对同一个服务的不同接口,使用不同的线程池。
69 |
70 | ```
71 | command key -> command group
72 |
73 | command key -> 自己的 thread pool key
74 | ```
75 |
76 | 逻辑上来说,多个 command key 属于一个command group,在做统计的时候,会放在一起统计。每个 command key 有自己的线程池,每个接口有自己的线程池,去做资源隔离和限流。
77 |
78 | 说白点,就是说如果你的 command key 要用自己的线程池,可以定义自己的 thread pool key,就 ok 了。
79 |
80 | ### coreSize
81 | 设置线程池的大小,默认是 10。一般来说,用这个默认的 10 个线程大小就够了。
82 | ```java
83 | HystrixThreadPoolProperties.Setter().withCoreSize(int value);
84 | ```
85 |
86 | ### queueSizeRejectionThreshold
87 | 如果说线程池中的 10 个线程都在工作中,没有空闲的线程来做其它的事情,此时再有请求过来,会先进入队列积压。如果说队列积压满了,再有请求过来,就直接 reject,拒绝请求,执行 fallback 降级的逻辑,快速返回。
88 |
89 | 
90 |
91 | 控制 queue 满了之后 reject 的 threshold,因为 maxQueueSize 不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小。
92 |
93 | ```java
94 | HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value);
95 | ```
96 |
97 | ### execution.isolation.semaphore.maxConcurrentRequests
98 | 设置使用 SEMAPHORE 隔离策略的时候允许访问的最大并发量,超过这个最大并发量,请求直接被 reject。
99 |
100 | 这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且 Hystrix 框架本身的开销会小很多。
101 |
102 | 默认值是 10,尽量设置的小一些,因为一旦设置的太大,而且有延时发生,可能瞬间导致 tomcat 本身的线程资源被占满。
103 |
104 | ```java
105 | HystrixCommandProperties.Setter().withExecutionIsolationSemaphoreMaxConcurrentRequests(int value);
106 | ```
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-fallback.md:
--------------------------------------------------------------------------------
1 | ## 基于本地缓存的 fallback 降级机制
2 | Hystrix 出现以下四种情况,都会去调用 fallback 降级机制:
3 |
4 | - 断路器处于打开的状态。
5 | - 资源池已满(线程池+队列 / 信号量)。
6 | - Hystrix 调用各种接口,或者访问外部依赖,比如 MySQL、Redis、Zookeeper、Kafka 等等,出现了任何异常的情况。
7 | - 访问外部依赖的时候,访问时间过长,报了 TimeoutException 异常。
8 |
9 | ### 两种最经典的降级机制
10 |
11 | - 纯内存数据
12 | 在降级逻辑中,你可以在内存中维护一个 ehcache,作为一个纯内存的基于 LRU 自动清理的缓存,让数据放在缓存内。如果说外部依赖有异常,fallback 这里直接尝试从 ehcache 中获取数据。
13 |
14 | - 默认值
15 | fallback 降级逻辑中,也可以直接返回一个默认值。
16 |
17 | 在 `HystrixCommand`,降级逻辑的书写,是通过实现 getFallback() 接口;而在 `HystrixObservableCommand` 中,则是实现 resumeWithFallback() 方法。
18 |
19 |
20 | 现在,我们用一个简单的栗子,来演示 fallback 降级是怎么做的。
21 |
22 | 比如,有这么个**场景**。我们现在有个包含 brandId 的商品数据,假设正常的逻辑是这样:拿到一个商品数据,根据 brandId 去调用品牌服务的接口,获取品牌的最新名称 brandName。
23 |
24 | 假如说,品牌服务接口挂掉了,那么我们可以尝试从本地内存中,获取一份稍过期的数据,先凑合着用。
25 |
26 | ### 步骤一:本地缓存获取数据
27 | 本地获取品牌名称的代码大致如下。
28 |
29 | ```java
30 | /**
31 | * 品牌名称本地缓存
32 | *
33 | */
34 |
35 | public class BrandCache {
36 |
37 | private static Map brandMap = new HashMap<>();
38 |
39 | static {
40 | brandMap.put(1L, "Nike");
41 | }
42 |
43 | /**
44 | * brandId 获取 brandName
45 | *
46 | * @param brandId 品牌id
47 | * @return 品牌名
48 | */
49 | public static String getBrandName(Long brandId) {
50 | return brandMap.get(brandId);
51 | }
52 | ```
53 |
54 | ### 步骤二:实现 GetBrandNameCommand
55 | 在 GetBrandNameCommand 中,run() 方法的正常逻辑是去调用品牌服务的接口获取到品牌名称,如果调用失败,报错了,那么就会去调用 fallback 降级机制。
56 |
57 | 这里,我们直接**模拟接口调用报错**,给它抛出个异常。
58 |
59 | 而在 getFallback() 方法中,就是我们的**降级逻辑**,我们直接从本地的缓存中,**获取到品牌名称**的数据。
60 |
61 | ```java
62 | /**
63 | * 获取品牌名称的command
64 | *
65 | */
66 |
67 | public class GetBrandNameCommand extends HystrixCommand {
68 |
69 | private Long brandId;
70 |
71 | public GetBrandNameCommand(Long brandId) {
72 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("BrandService"))
73 | .andCommandKey(HystrixCommandKey.Factory.asKey("GetBrandNameCommand"))
74 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
75 | // 设置降级机制最大并发请求数
76 | .withFallbackIsolationSemaphoreMaxConcurrentRequests(15)));
77 | this.brandId = brandId;
78 | }
79 |
80 | @Override
81 | protected String run() throws Exception {
82 | // 这里正常的逻辑应该是去调用一个品牌服务的接口获取名称
83 | // 如果调用失败,报错了,那么就会去调用fallback降级机制
84 |
85 | // 这里我们直接模拟调用报错,抛出异常
86 | throw new Exception();
87 | }
88 |
89 | @Override
90 | protected String getFallback() {
91 | return BrandCache.getBrandName(brandId);
92 | }
93 | }
94 | ```
95 |
96 | `FallbackIsolationSemaphoreMaxConcurrentRequests` 用于设置 fallback 最大允许的并发请求量,默认值是 10,是通过 semaphore 信号量的机制去限流的。如果超出了这个最大值,那么直接 reject。
97 |
98 | ### 步骤三:CacheController 调用接口
99 | 在 CacheController 中,我们通过 productInfo 获取 brandId,然后创建 GetBrandNameCommand 并执行,去尝试获取 brandName。这里执行会报错,因为我们在 run() 方法中直接抛出异常,Hystrix 就会去调用 getFallback() 方法走降级逻辑。
100 |
101 | ```java
102 | @Controller
103 | public class CacheController {
104 |
105 | @RequestMapping("/getProductInfo")
106 | @ResponseBody
107 | public String getProductInfo(Long productId) {
108 | HystrixCommand getProductInfoCommand = new GetProductInfoCommand(productId);
109 |
110 | ProductInfo productInfo = getProductInfoCommand.execute();
111 | Long brandId = productInfo.getBrandId();
112 |
113 | HystrixCommand getBrandNameCommand = new GetBrandNameCommand(brandId);
114 |
115 | // 执行会抛异常报错,然后走降级
116 | String brandName = getBrandNameCommand.execute();
117 | productInfo.setBrandName(brandName);
118 |
119 | System.out.println(productInfo);
120 | return "success";
121 | }
122 | }
123 | ```
124 |
125 | 关于降级逻辑的演示,基本上就结束了。
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-introduction.md:
--------------------------------------------------------------------------------
1 | ## 用 Hystrix 构建高可用服务架构
2 | 参考 [Hystrix Home](https://github.com/Netflix/Hystrix/wiki#what)。
3 |
4 | ### Hystrix 是什么?
5 | 在分布式系统中,每个服务都可能会调用很多其他服务,被调用的那些服务就是**依赖服务**,有的时候某些依赖服务出现故障也是很正常的。
6 |
7 | Hystrix 可以让我们在分布式系统中对服务间的调用进行控制,加入一些**调用延迟**或者**依赖故障**的**容错机制**。
8 |
9 | Hystrix 通过将依赖服务进行**资源隔离**,进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;同时Hystrix 还提供故障时的 fallback 降级机制。
10 |
11 | 总而言之,Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。
12 |
13 | ### Hystrix 的历史
14 | Hystrix 是高可用性保障的一个框架。Netflix(可以认为是国外的优酷或者爱奇艺之类的视频网站)的 API 团队从 2011 年开始做一些提升系统可用性和稳定性的工作,Hystrix 就是从那时候开始发展出来的。
15 |
16 | 在 2012 年的时候,Hystrix 就变得比较成熟和稳定了,Netflix 中,除了 API 团队以外,很多其他的团队都开始使用 Hystrix。
17 |
18 | 时至今日,Netflix 中每天都有数十亿次的服务间调用,通过 Hystrix 框架在进行,而 Hystrix 也帮助 Netflix 网站提升了整体的可用性和稳定性。
19 |
20 | [2018 年 11 月,Hystrix 在其 Github 主页宣布,不再开放新功能,推荐开发者使用其他仍然活跃的开源项目](https://github.com/Netflix/Hystrix/blob/master/README.md#hystrix-status)。维护模式的转变绝不意味着 Hystrix 不再有价值。相反,Hystrix 激发了很多伟大的想法和项目,我们高可用的这一块知识还是会针对 Hystrix 进行讲解。
21 |
22 | ### Hystrix 的设计原则
23 | - 对依赖服务调用时出现的调用延迟和调用失败进行**控制和容错保护**。
24 | - 在复杂的分布式系统中,阻止某一个依赖服务的故障在整个系统中蔓延。比如某一个服务故障了,导致其它服务也跟着故障。
25 | - 提供 `fail-fast`(快速失败)和快速恢复的支持。
26 | - 提供 fallback 优雅降级的支持。
27 | - 支持近实时的监控、报警以及运维操作。
28 |
29 |
30 | 举个栗子。
31 |
32 | 有这样一个分布式系统,服务 A 依赖于服务 B,服务 B 依赖于服务 C/D/E。在这样一个成熟的系统内,比如说最多可能只有 100 个线程资源。正常情况下,40 个线程并发调用服务 C,各 30 个线程并发调用 D/E。
33 |
34 | 调用服务 C,只需要 20ms,现在因为服务 C 故障了,比如延迟,或者挂了,此时线程会 hang 住 2s 左右。40 个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C,同样也会被卡住。这样导致服务 B 的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。服务 A 也挂。
35 |
36 | 
37 |
38 | Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线程调用服务 C。当此 40 个线程被 hang 住时,其它 60 个线程依然能正常调用工作。从而确保整个系统不会被拖垮。
39 |
40 |
41 | ### Hystrix 更加细节的设计原则
42 | - 阻止任何一个依赖服务耗尽所有的资源,比如 tomcat 中的所有线程资源。
43 | - 避免请求排队和积压,采用限流和 `fail fast` 来控制故障。
44 | - 提供 fallback 降级机制来应对故障。
45 | - 使用资源隔离技术,比如 `bulkhead`(舱壁隔离技术)、`swimlane`(泳道技术)、`circuit breaker`(断路技术)来限制任何一个依赖服务的故障的影响。
46 | - 通过近实时的统计/监控/报警功能,来提高故障发现的速度。
47 | - 通过近实时的属性和配置**热修改**功能,来提高故障处理和恢复的速度。
48 | - 保护依赖服务调用的所有故障情况,而不仅仅只是网络故障情况。
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-semphore-isolation.md:
--------------------------------------------------------------------------------
1 | ## 基于 Hystrix 信号量机制实现资源隔离
2 | Hystrix 里面核心的一项功能,其实就是所谓的**资源隔离**,要解决的最最核心的问题,就是将多个依赖服务的调用分别隔离到各自的资源池内。避免说对某一个依赖服务的调用,因为依赖服务的接口调用的延迟或者失败,导致服务所有的线程资源全部耗费在这个服务的接口调用上。一旦说某个服务的线程资源全部耗尽的话,就可能导致服务崩溃,甚至说这种故障会不断蔓延。
3 |
4 | Hystrix 实现资源隔离,主要有两种技术:
5 |
6 | - 线程池
7 | - 信号量
8 |
9 | 默认情况下,Hystrix 使用线程池模式。
10 |
11 | 前面已经说过线程池技术了,这一小节就来说说信号量机制实现资源隔离,以及这两种技术的区别与具体应用场景。
12 |
13 | ### 信号量机制
14 | 信号量的资源隔离只是起到一个开关的作用,比如,服务 A 的信号量大小为 10,那么就是说它同时只允许有 10 个 tomcat 线程来访问服务 A,其它的请求都会被拒绝,从而达到资源隔离和限流保护的作用。
15 |
16 | 
17 |
18 | ### 线程池与信号量区别
19 | 线程池隔离技术,并不是说去控制类似 tomcat 这种 web 容器的线程。更加严格的意义上来说,Hystrix 的线程池隔离技术,控制的是 tomcat 线程的执行。Hystrix 线程池满后,会确保说,tomcat 的线程不会因为依赖服务的接口调用延迟或故障而被 hang 住,tomcat 其它的线程不会卡死,可以快速返回,然后支撑其它的事情。
20 |
21 | 线程池隔离技术,是用 Hystrix 自己的线程去执行调用;而信号量隔离技术,是直接让 tomcat 线程去调用依赖服务。信号量隔离,只是一道关卡,信号量有多少,就允许多少个 tomcat 线程通过它,然后去执行。
22 |
23 | 
24 |
25 | **适用场景**:
26 | - **线程池技术**,适合绝大多数场景,比如说我们对依赖服务的网络请求的调用和访问、需要对调用的 timeout 进行控制(捕捉 timeout 超时异常)。
27 | - **信号量技术**,适合说你的访问不是对外部依赖的访问,而是对内部的一些比较复杂的业务逻辑的访问,并且系统内部的代码,其实不涉及任何的网络请求,那么只要做信号量的普通限流就可以了,因为不需要去捕获 timeout 类似的问题。
28 |
29 | ### 信号量简单 Demo
30 | 业务背景里,比较适合信号量的是什么场景呢?
31 |
32 | 比如说,我们一般来说,缓存服务,可能会将一些量特别少、访问又特别频繁的数据,放在自己的纯内存中。
33 |
34 | 举个栗子。一般我们在获取到商品数据之后,都要去获取商品是属于哪个地理位置、省、市、卖家等,可能在自己的纯内存中,比如就一个 Map 去获取。对于这种直接访问本地内存的逻辑,比较适合用信号量做一下简单的隔离。
35 |
36 | 优点在于,不用自己管理线程池啦,不用 care timeout 超时啦,也不需要进行线程的上下文切换啦。信号量做隔离的话,性能相对来说会高一些。
37 |
38 | 假如这是本地缓存,我们可以通过 cityId,拿到 cityName。
39 | ```java
40 | public class LocationCache {
41 | private static Map cityMap = new HashMap<>();
42 |
43 | static {
44 | cityMap.put(1L, "北京");
45 | }
46 |
47 | /**
48 | * 通过cityId 获取 cityName
49 | *
50 | * @param cityId 城市id
51 | * @return 城市名
52 | */
53 | public static String getCityName(Long cityId) {
54 | return cityMap.get(cityId);
55 | }
56 | }
57 | ```
58 |
59 | 写一个 GetCityNameCommand,策略设置为**信号量**。run() 方法中获取本地缓存。我们目的就是对获取本地缓存的代码进行资源隔离。
60 | ```java
61 | public class GetCityNameCommand extends HystrixCommand {
62 |
63 | private Long cityId;
64 |
65 | public GetCityNameCommand(Long cityId) {
66 | // 设置信号量隔离策略
67 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("GetCityNameGroup"))
68 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
69 | .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)));
70 |
71 | this.cityId = cityId;
72 | }
73 |
74 | @Override
75 | protected String run() {
76 | // 需要进行信号量隔离的代码
77 | return LocationCache.getCityName(cityId);
78 | }
79 | }
80 | ```
81 |
82 | 在接口层,通过创建 GetCityNameCommand,传入 cityId,执行 execute() 方法,那么获取本地 cityName 缓存的代码将会进行信号量的资源隔离。
83 | ```java
84 | @RequestMapping("/getProductInfo")
85 | @ResponseBody
86 | public String getProductInfo(Long productId) {
87 | HystrixCommand getProductInfoCommand = new GetProductInfoCommand(productId);
88 |
89 | // 通过command执行,获取最新商品数据
90 | ProductInfo productInfo = getProductInfoCommand.execute();
91 |
92 | Long cityId = productInfo.getCityId();
93 |
94 | GetCityNameCommand getCityNameCommand = new GetCityNameCommand(cityId);
95 | // 获取本地内存(cityName)的代码会被信号量进行资源隔离
96 | String cityName = getCityNameCommand.execute();
97 |
98 | productInfo.setCityName(cityName);
99 |
100 | System.out.println(productInfo);
101 | return "success";
102 | }
103 | ```
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-thread-pool-isolation.md:
--------------------------------------------------------------------------------
1 | ## 基于 Hystrix 线程池技术实现资源隔离
2 | 上一讲提到,如果从 Nginx 开始,缓存都失效了,Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做个讨论),有可能出现调用延时而把缓存服务资源耗尽的情况。这里,我们就来说说,怎么通过 Hystrix 线程池技术实现资源隔离。
3 |
4 | 资源隔离,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内,不会去用其它资源了,这就叫资源隔离。哪怕对这个依赖服务,比如说商品服务,现在同时发起的调用量已经到了 1000,但是线程池内就 10 个线程,最多就只会用这 10 个线程去执行,不会说,对商品服务的请求,因为接口调用延时,将 tomcat 内部所有的线程资源全部耗尽。
5 |
6 | Hystrix 进行资源隔离,其实是提供了一个抽象,叫做 command。这也是 Hystrix 最最基本的资源隔离技术。
7 |
8 | ### 利用 HystrixCommand 获取单条数据
9 | 我们通过将调用商品服务的操作封装在 HystrixCommand 中,限定一个 key,比如下面的 `GetProductInfoCommandGroup`,在这里我们可以简单认为这是一个线程池,每次调用商品服务,就只会用该线程池中的资源,不会再去用其它线程资源了。
10 |
11 | ```java
12 | public class GetProductInfoCommand extends HystrixCommand {
13 |
14 | private Long productId;
15 |
16 | public GetProductInfoCommand(Long productId) {
17 | super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoCommandGroup"));
18 | this.productId = productId;
19 | }
20 |
21 | @Override
22 | protected ProductInfo run() {
23 | String url = "http://localhost:8081/getProductInfo?productId=" + productId;
24 | // 调用商品服务接口
25 | String response = HttpClientUtils.sendGetRequest(url);
26 | return JSONObject.parseObject(response, ProductInfo.class);
27 | }
28 | }
29 | ```
30 |
31 | 我们在缓存服务接口中,根据 productId 创建 command 并执行,获取到商品数据。
32 |
33 | ```java
34 | @RequestMapping("/getProductInfo")
35 | @ResponseBody
36 | public String getProductInfo(Long productId) {
37 | HystrixCommand getProductInfoCommand = new GetProductInfoCommand(productId);
38 |
39 | // 通过command执行,获取最新商品数据
40 | ProductInfo productInfo = getProductInfoCommand.execute();
41 | System.out.println(productInfo);
42 | return "success";
43 | }
44 | ```
45 |
46 | 上面执行的是 execute() 方法,其实是同步的。也可以对 command 调用 queue() 方法,它仅仅是将 command 放入线程池的一个等待队列,就立即返回,拿到一个 Future 对象,后面可以继续做其它一些事情,然后过一段时间对 Future 调用 get() 方法获取数据。这是异步的。
47 |
48 | ### 利用 HystrixObservableCommand 批量获取数据
49 | 只要是获取商品数据,全部都绑定到同一个线程池里面去,我们通过 HystrixObservableCommand 的一个线程去执行,而在这个线程里面,批量把多个 productId 的 productInfo 拉回来。
50 |
51 | ```java
52 | public class GetProductInfosCommand extends HystrixObservableCommand {
53 |
54 | private String[] productIds;
55 |
56 | public GetProductInfosCommand(String[] productIds) {
57 | // 还是绑定在同一个线程池
58 | super(HystrixCommandGroupKey.Factory.asKey("GetProductInfoGroup"));
59 | this.productIds = productIds;
60 | }
61 |
62 | @Override
63 | protected Observable construct() {
64 | return Observable.unsafeCreate((Observable.OnSubscribe) subscriber -> {
65 |
66 | for (String productId : productIds) {
67 | // 批量获取商品数据
68 | String url = "http://localhost:8081/getProductInfo?productId=" + productId;
69 | String response = HttpClientUtils.sendGetRequest(url);
70 | ProductInfo productInfo = JSONObject.parseObject(response, ProductInfo.class);
71 | subscriber.onNext(productInfo);
72 | }
73 | subscriber.onCompleted();
74 |
75 | }).subscribeOn(Schedulers.io());
76 | }
77 | }
78 | ```
79 |
80 | 在缓存服务接口中,根据传来的 id 列表,比如是以 `,` 分隔的 id 串,通过上面的 HystrixObservableCommand,执行 Hystrix 的一些 API 方法,获取到所有商品数据。
81 | ```java
82 | public String getProductInfos(String productIds) {
83 | String[] productIdArray = productIds.split(",");
84 | HystrixObservableCommand getProductInfosCommand = new GetProductInfosCommand(productIdArray);
85 | Observable observable = getProductInfosCommand.observe();
86 |
87 | observable.subscribe(new Observer() {
88 | @Override
89 | public void onCompleted() {
90 | System.out.println("获取完了所有的商品数据");
91 | }
92 |
93 | @Override
94 | public void onError(Throwable e) {
95 | e.printStackTrace();
96 | }
97 |
98 | /**
99 | * 获取完一条数据,就回调一次这个方法
100 | * @param productInfo
101 | */
102 | @Override
103 | public void onNext(ProductInfo productInfo) {
104 | System.out.println(productInfo);
105 | }
106 | });
107 | return "success";
108 | }
109 | ```
110 |
111 | 我们回过头来,看看 Hystrix 线程池技术是如何实现资源隔离的。
112 |
113 | 
114 |
115 | 从 Nginx 开始,缓存都失效了,那么 Nginx 通过缓存服务去调用商品服务。缓存服务默认的线程大小是 10 个,最多就只有 10 个线程去调用商品服务的接口。即使商品服务接口故障了,最多就只有 10 个线程会 hang 死在调用商品服务接口的路上,缓存服务的 tomcat 内其它的线程还是可以用来调用其它的服务,干其它的事情。
--------------------------------------------------------------------------------
/docs/high-availability/hystrix-timeout.md:
--------------------------------------------------------------------------------
1 | ## 基于 timeout 机制为服务接口调用超时提供安全保护
2 | 一般来说,在调用依赖服务的接口的时候,比较常见的一个问题就是**超时**。超时是在一个复杂的分布式系统中,导致系统不稳定,或者系统抖动。出现大量超时,线程资源会被 hang 死,从而导致吞吐量大幅度下降,甚至服务崩溃。
3 |
4 | 你去调用各种各样的依赖服务,特别是在大公司,你甚至都不认识开发一个服务的人,你都不知道那个人的技术水平怎么样,对那个人根本不了解。
5 |
6 | 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)",也就是说在互联网的另外一头,你都不知道甚至坐着一条狗。
7 |
8 | 
9 |
10 | 像特别复杂的分布式系统,特别是在大公司里,多个团队、大型协作,你可能都不知道服务是谁的,很可能说开发服务的那个哥儿们甚至是一个实习生。依赖服务的接口性能可能很不稳定,有时候 2ms,有时候 200ms,甚至 2s,都有可能。
11 |
12 | 如果你不对各种依赖服务接口的调用做超时控制,来给你的服务提供安全保护措施,那么很可能你的服务就被各种垃圾的依赖服务的性能给拖死了。大量的接口调用很慢,大量的线程被卡死。如果你做了资源的隔离,那么也就是线程池的线程被卡死,但其实我们可以做超时控制,没必要让它们全卡死。
13 |
14 | ### TimeoutMilliseconds
15 | 在 Hystrix 中,我们可以手动设置 timeout 时长,如果一个 command 运行时间超过了设定的时长,那么就被认为是 timeout,然后 Hystrix command 标识为 timeout,同时执行 fallback 降级逻辑。
16 |
17 | `TimeoutMilliseconds` 默认值是 1000,也就是 1000ms。
18 |
19 | ```java
20 | HystrixCommandProperties.Setter()
21 | ..withExecutionTimeoutInMilliseconds(int)
22 | ```
23 |
24 | ### TimeoutEnabled
25 | 这个参数用于控制是否要打开 timeout 机制,默认值是 true。
26 |
27 | ```java
28 | HystrixCommandProperties.Setter()
29 | .withExecutionTimeoutEnabled(boolean)
30 | ```
31 |
32 | ## 实例 Demo
33 | 我们在 command 中,将超时时间设置为 500ms,然后在 run() 方法中,设置休眠时间 1s,这样一个请求过来,直接休眠 1s,结果就会因为超时而执行降级逻辑。
34 |
35 | ```java
36 | public class GetProductInfoCommand extends HystrixCommand {
37 |
38 | private Long productId;
39 |
40 | private static final HystrixCommandKey KEY = HystrixCommandKey.Factory.asKey("GetProductInfoCommand");
41 |
42 | public GetProductInfoCommand(Long productId) {
43 | super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ProductInfoService"))
44 | .andCommandKey(KEY)
45 | .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter()
46 | .withCoreSize(8)
47 | .withMaxQueueSize(10)
48 | .withQueueSizeRejectionThreshold(8))
49 | .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
50 | .withCircuitBreakerEnabled(true)
51 | .withCircuitBreakerRequestVolumeThreshold(20)
52 | .withCircuitBreakerErrorThresholdPercentage(40)
53 | .withCircuitBreakerSleepWindowInMilliseconds(3000)
54 | // 设置是否打开超时,默认是true
55 | .withExecutionTimeoutEnabled(true)
56 | // 设置超时时间,默认1000(ms)
57 | .withExecutionTimeoutInMilliseconds(500)
58 | .withFallbackIsolationSemaphoreMaxConcurrentRequests(30)));
59 | this.productId = productId;
60 | }
61 |
62 | @Override
63 | protected ProductInfo run() throws Exception {
64 | System.out.println("调用接口查询商品数据,productId=" + productId);
65 |
66 | // 休眠1s
67 | TimeUtils.sleep(1);
68 |
69 | String url = "http://localhost:8081/getProductInfo?productId=" + productId;
70 | String response = HttpClientUtils.sendGetRequest(url);
71 | System.out.println(response);
72 | return JSONObject.parseObject(response, ProductInfo.class);
73 | }
74 |
75 | @Override
76 | protected ProductInfo getFallback() {
77 | ProductInfo productInfo = new ProductInfo();
78 | productInfo.setName("降级商品");
79 | return productInfo;
80 | }
81 | }
82 | ```
83 |
84 | 在测试类中,我们直接发起请求。
85 |
86 | ```java
87 | @SpringBootTest
88 | @RunWith(SpringRunner.class)
89 | public class TimeoutTest {
90 |
91 | @Test
92 | public void testTimeout() {
93 | HttpClientUtils.sendGetRequest("http://localhost:8080/getProductInfo?productId=1");
94 | }
95 | }
96 | ```
97 |
98 | 结果中可以看到,打印出了降级商品相关信息。
99 |
100 | ```c
101 | 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)
102 | {"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}
103 | ```
--------------------------------------------------------------------------------
/docs/high-availability/images/220px-Internet_dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/220px-Internet_dog.jpg
--------------------------------------------------------------------------------
/docs/high-availability/images/async-replication-data-lose-case.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/async-replication-data-lose-case.png
--------------------------------------------------------------------------------
/docs/high-availability/images/bulkhead-partition.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/bulkhead-partition.jpg
--------------------------------------------------------------------------------
/docs/high-availability/images/consistent-hashing-algorithm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/consistent-hashing-algorithm.png
--------------------------------------------------------------------------------
/docs/high-availability/images/distributed-system-request-sequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/distributed-system-request-sequence.png
--------------------------------------------------------------------------------
/docs/high-availability/images/distributed-transaction-TCC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/distributed-transaction-TCC.png
--------------------------------------------------------------------------------
/docs/high-availability/images/distributed-transaction-XA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/distributed-transaction-XA.png
--------------------------------------------------------------------------------
/docs/high-availability/images/distributed-transaction-local-message-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/distributed-transaction-local-message-table.png
--------------------------------------------------------------------------------
/docs/high-availability/images/distributed-transaction-reliable-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/distributed-transaction-reliable-message.png
--------------------------------------------------------------------------------
/docs/high-availability/images/dubbo-operating-principle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/dubbo-operating-principle.png
--------------------------------------------------------------------------------
/docs/high-availability/images/dubbo-service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/dubbo-service-invoke-road.png
--------------------------------------------------------------------------------
/docs/high-availability/images/dubbo-spi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/dubbo-spi.png
--------------------------------------------------------------------------------
/docs/high-availability/images/e-commerce-website-detail-page-architecture-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/e-commerce-website-detail-page-architecture-1.png
--------------------------------------------------------------------------------
/docs/high-availability/images/e-commerce-website-detail-page-architecture-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/e-commerce-website-detail-page-architecture-2.png
--------------------------------------------------------------------------------
/docs/high-availability/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/high-availability/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hash-slot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hash-slot.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hash.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-process.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-request-cache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-request-cache.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-semphore-thread-pool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-semphore-thread-pool.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-semphore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-semphore.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-thread-pool-isolation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-thread-pool-isolation.png
--------------------------------------------------------------------------------
/docs/high-availability/images/hystrix-thread-pool-queue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/hystrix-thread-pool-queue.png
--------------------------------------------------------------------------------
/docs/high-availability/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/icon.png
--------------------------------------------------------------------------------
/docs/high-availability/images/service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/service-invoke-road.png
--------------------------------------------------------------------------------
/docs/high-availability/images/simple-distributed-system-oa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/simple-distributed-system-oa.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-active-standby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-active-standby.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-centralized-storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-centralized-storage.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-distributed-coordination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-distributed-coordination.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-distributed-lock-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-distributed-lock-demo.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-distributed-lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-distributed-lock.png
--------------------------------------------------------------------------------
/docs/high-availability/images/zookeeper-meta-data-manage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-availability/images/zookeeper-meta-data-manage.png
--------------------------------------------------------------------------------
/docs/high-concurrency/README.md:
--------------------------------------------------------------------------------
1 | # 高并发架构
--------------------------------------------------------------------------------
/docs/high-concurrency/database-shard-dynamic-expand.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何设计可以动态扩容缩容的分库分表方案?
3 |
4 | ## 面试官心理分析
5 | 对于分库分表来说,主要是面对以下问题:
6 |
7 | - 选择一个数据库中间件,调研、学习、测试;
8 | - 设计你的分库分表的一个方案,你要分成多少个库,每个库分成多少个表,比如 3 个库,每个库 4 个表;
9 | - 基于选择好的数据库中间件,以及在测试环境建立好的分库分表的环境,然后测试一下能否正常进行分库分表的读写;
10 | - 完成单库单表到分库分表的**迁移**,双写方案;
11 | - 线上系统开始基于分库分表对外提供服务;
12 | - 扩容了,扩容成 6 个库,每个库需要 12 个表,你怎么来增加更多库和表呢?
13 |
14 | 这个是你必须面对的一个事儿,就是你已经弄好分库分表方案了,然后一堆库和表都建好了,基于分库分表中间件的代码开发啥的都好了,测试都 ok 了,数据能均匀分布到各个库和各个表里去,而且接着你还通过双写的方案咔嚓一下上了系统,已经直接基于分库分表方案在搞了。
15 |
16 | 那么现在问题来了,你现在这些库和表又支撑不住了,要继续扩容咋办?这个可能就是说你的每个库的容量又快满了,或者是你的表数据量又太大了,也可能是你每个库的写并发太高了,你得继续扩容。
17 |
18 | 这都是玩儿分库分表线上必须经历的事儿。
19 |
20 | ## 面试题剖析
21 | ### 停机扩容(不推荐)
22 | 这个方案就跟停机迁移一样,步骤几乎一致,唯一的一点就是那个导数的工具,是把现有库表的数据抽出来慢慢倒入到新的库和表里去。但是最好别这么玩儿,有点不太靠谱,因为既然**分库分表**就说明数据量实在是太大了,可能多达几亿条,甚至几十亿,你这么玩儿,可能会出问题。
23 |
24 | 从单库单表迁移到分库分表的时候,数据量并不是很大,单表最大也就两三千万。那么你写个工具,多弄几台机器并行跑,1小时数据就导完了。这没有问题。
25 |
26 | 如果 3 个库 + 12 个表,跑了一段时间了,数据量都 1~2 亿了。光是导 2 亿数据,都要导个几个小时,6 点,刚刚导完数据,还要搞后续的修改配置,重启系统,测试验证,10 点才可以搞完。所以不能这么搞。
27 |
28 | ### 优化后的方案
29 | 一开始上来就是 32 个库,每个库 32 个表,那么总共是 1024 张表。
30 |
31 | 我可以告诉各位同学,这个分法,第一,基本上国内的互联网肯定都是够用了,第二,无论是并发支撑还是数据量支撑都没问题。
32 |
33 | 每个库正常承载的写入并发量是 1000,那么 32 个库就可以承载 32 * 1000 = 32000 的写并发,如果每个库承载 1500 的写并发,32 * 1500 = 48000 的写并发,接近 5 万每秒的写入并发,前面再加一个MQ,削峰,每秒写入 MQ 8 万条数据,每秒消费 5 万条数据。
34 |
35 | 有些除非是国内排名非常靠前的这些公司,他们的最核心的系统的数据库,可能会出现几百台数据库的这么一个规模,128 个库,256 个库,512 个库。
36 |
37 | 1024 张表,假设每个表放 500 万数据,在 MySQL 里可以放 50 亿条数据。
38 |
39 | 每秒 5 万的写并发,总共 50 亿条数据,对于国内大部分的互联网公司来说,其实一般来说都够了。
40 |
41 | 谈分库分表的扩容,**第一次分库分表,就一次性给他分个够**,32 个库,1024 张表,可能对大部分的中小型互联网公司来说,已经可以支撑好几年了。
42 |
43 | 一个实践是利用 `32 * 32` 来分库分表,即分为 32 个库,每个库里一个表分为 32 张表。一共就是 1024 张表。根据某个 id 先根据 32 取模路由到库,再根据 32 取模路由到库里的表。
44 |
45 | | orderId | id % 32 (库) | id / 32 % 32 (表) |
46 | |---|---|---|
47 | | 259 | 3 | 8 |
48 | | 1189 | 5 | 5 |
49 | | 352 | 0 | 11 |
50 | | 4593 | 17 | 15 |
51 |
52 |
53 | 刚开始的时候,这个库可能就是逻辑库,建在一个数据库上的,就是一个 mysql 服务器可能建了 n 个库,比如 32 个库。后面如果要拆分,就是不断在库和 mysql 服务器之间做迁移就可以了。然后系统配合改一下配置即可。
54 |
55 | 比如说最多可以扩展到 32 个数据库服务器,每个数据库服务器是一个库。如果还是不够?最多可以扩展到 1024 个数据库服务器,每个数据库服务器上面一个库一个表。因为最多是 1024 个表。
56 |
57 | 这么搞,是不用自己写代码做数据迁移的,都交给 dba 来搞好了,但是 dba 确实是需要做一些库表迁移的工作,但是总比你自己写代码,然后抽数据导数据来的效率高得多吧。
58 |
59 | 哪怕是要减少库的数量,也很简单,其实说白了就是按倍数缩容就可以了,然后修改一下路由规则。
60 |
61 | 这里对步骤做一个总结:
62 |
63 | 1. 设定好几台数据库服务器,每台服务器上几个库,每个库多少个表,推荐是 32 库 * 32 表,对于大部分公司来说,可能几年都够了。
64 | 2. 路由的规则,orderId 模 32 = 库,orderId / 32 模 32 = 表
65 | 3. 扩容的时候,申请增加更多的数据库服务器,装好 mysql,呈倍数扩容,4 台服务器,扩到 8 台服务器,再到 16 台服务器。
66 | 4. 由 dba 负责将原先数据库服务器的库,迁移到新的数据库服务器上去,库迁移是有一些便捷的工具的。
67 | 5. 我们这边就是修改一下配置,调整迁移的库所在数据库服务器的地址。
68 | 6. 重新发布系统,上线,原先的路由规则变都不用变,直接可以基于 n 倍的数据库服务器的资源,继续进行线上系统的提供服务。
--------------------------------------------------------------------------------
/docs/high-concurrency/database-shard-method.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表**动态切换**到分库分表上?
3 |
4 | ## 面试官心理分析
5 | 你看看,你现在已经明白为啥要分库分表了,你也知道常用的分库分表中间件了,你也设计好你们如何分库分表的方案了(水平拆分、垂直拆分、分表),那问题来了,你接下来该怎么把你那个单库单表的系统给迁移到分库分表上去?
6 |
7 | 所以这都是一环扣一环的,就是看你有没有全流程经历过这个过程。
8 |
9 | ## 面试题剖析
10 | 这个其实从 low 到高大上有好几种方案,我们都玩儿过,我都给你说一下。
11 |
12 | ### 停机迁移方案
13 | 我先给你说一个最 low 的方案,就是很简单,大家伙儿凌晨 12 点开始运维,网站或者 app 挂个公告,说 0 点到早上 6 点进行运维,无法访问。
14 |
15 | 接着到 0 点停机,系统停掉,没有流量写入了,此时老的单库单表数据库静止了。然后你之前得写好一个**导数的一次性工具**,此时直接跑起来,然后将单库单表的数据哗哗哗读出来,写到分库分表里面去。
16 |
17 | 导数完了之后,就 ok 了,修改系统的数据库连接配置啥的,包括可能代码和 SQL 也许有修改,那你就用最新的代码,然后直接启动连到新的分库分表上去。
18 |
19 | 验证一下,ok了,完美,大家伸个懒腰,看看看凌晨 4 点钟的北京夜景,打个滴滴回家吧。
20 |
21 | 但是这个方案比较 low,谁都能干,我们来看看高大上一点的方案。
22 |
23 | 
24 |
25 | ### 双写迁移方案
26 | 这个是我们常用的一种迁移方案,比较靠谱一些,不用停机,不用看北京凌晨 4 点的风景。
27 |
28 | 简单来说,就是在线上系统里面,之前所有写库的地方,增删改操作,**除了对老库增删改,都加上对新库的增删改**,这就是所谓的**双写**,同时写俩库,老库和新库。
29 |
30 | 然后**系统部署**之后,新库数据差太远,用之前说的导数工具,跑起来读老库数据写新库,写的时候要根据 gmt_modified 这类字段判断这条数据最后修改的时间,除非是读出来的数据在新库里没有,或者是比新库的数据新才会写。简单来说,就是不允许用老数据覆盖新数据。
31 |
32 | 导完一轮之后,有可能数据还是存在不一致,那么就程序自动做一轮校验,比对新老库每个表的每条数据,接着如果有不一样的,就针对那些不一样的,从老库读数据再次写。反复循环,直到两个库每个表的数据都完全一致为止。
33 |
34 | 接着当数据完全一致了,就 ok 了,基于仅仅使用分库分表的最新代码,重新部署一次,不就仅仅基于分库分表在操作了么,还没有几个小时的停机时间,很稳。所以现在基本玩儿数据迁移之类的,都是这么干的。
35 |
36 | 
--------------------------------------------------------------------------------
/docs/high-concurrency/database-shard.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?
3 |
4 | ## 面试官心理分析
5 | 其实这块肯定是扯到**高并发**了,因为分库分表一定是为了**支撑高并发、数据量大**两个问题的。而且现在说实话,尤其是互联网类的公司面试,基本上都会来这么一下,分库分表如此普遍的技术问题,不问实在是不行,而如果你不知道那也实在是说不过去!
6 |
7 | ## 面试题剖析
8 | ### 为什么要分库分表?(设计高并发系统的时候,数据库层面该如何设计?)
9 | 说白了,分库分表是两回事儿,大家可别搞混了,可能是光分库不分表,也可能是光分表不分库,都有可能。
10 |
11 | 我先给大家抛出来一个场景。
12 |
13 | 假如我们现在是一个小创业公司(或者是一个 BAT 公司刚兴起的一个新部门),现在注册用户就 20 万,每天活跃用户就 1 万,每天单表数据量就 1000,然后高峰期每秒钟并发请求最多就 10。天,就这种系统,随便找一个有几年工作经验的,然后带几个刚培训出来的,随便干干都可以。
14 |
15 | 结果没想到我们运气居然这么好,碰上个 CEO 带着我们走上了康庄大道,业务发展迅猛,过了几个月,注册用户数达到了 2000 万!每天活跃用户数 100 万!每天单表数据量 10 万条!高峰期每秒最大请求达到 1000!同时公司还顺带着融资了两轮,进账了几个亿人民币啊!公司估值达到了惊人的几亿美金!这是小独角兽的节奏!
16 |
17 | 好吧,没事,现在大家感觉压力已经有点大了,为啥呢?因为每天多 10 万条数据,一个月就多 300 万条数据,现在咱们单表已经几百万数据了,马上就破千万了。但是勉强还能撑着。高峰期请求现在是 1000,咱们线上部署了几台机器,负载均衡搞了一下,数据库撑 1000QPS 也还凑合。但是大家现在开始感觉有点担心了,接下来咋整呢......
18 |
19 | 再接下来几个月,我的天,CEO 太牛逼了,公司用户数已经达到 1 亿,公司继续融资几十亿人民币啊!公司估值达到了惊人的几十亿美金,成为了国内今年最牛逼的明星创业公司!天,我们太幸运了。
20 |
21 | 但是我们同时也是不幸的,因为此时每天活跃用户数上千万,每天单表新增数据多达 50 万,目前一个表总数据量都已经达到了两三千万了!扛不住啊!数据库磁盘容量不断消耗掉!高峰期并发达到惊人的 `5000~8000`!别开玩笑了,哥。我跟你保证,你的系统支撑不到现在,已经挂掉了!
22 |
23 | 好吧,所以你看到这里差不多就理解分库分表是怎么回事儿了,实际上这是跟着你的公司业务发展走的,你公司业务发展越好,用户就越多,数据量越大,请求量越大,那你单个数据库一定扛不住。
24 |
25 | #### 分表
26 |
27 | 比如你单表都几千万数据了,你确定你能扛住么?绝对不行,**单表数据量太大**,会极大影响你的 sql **执行的性能**,到了后面你的 sql 可能就跑的很慢了。一般来说,就以我的经验来看,单表到几百万的时候,性能就会相对差一些了,你就得分表了。
28 |
29 | 分表是啥意思?就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。比如按照用户 id 来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在 200 万以内。
30 |
31 | #### 分库
32 |
33 | 分库是啥意思?就是你一个库一般我们经验而言,最多支撑到并发 2000,一定要扩容了,而且一个健康的单库并发值你最好保持在每秒 1000 左右,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。
34 |
35 | 这就是所谓的**分库分表**,为啥要分库分表?你明白了吧。
36 |
37 |
38 | | # | 分库分表前 | 分库分表后 |
39 | |---|---|---|
40 | | 并发支撑情况 | MySQL 单机部署,扛不住高并发 | MySQL从单机到多机,能承受的并发增加了多倍 |
41 | | 磁盘使用情况 | MySQL 单机磁盘容量几乎撑满 | 拆分为多个库,数据库服务器磁盘使用率大大降低 |
42 | | SQL 执行性能 | 单表数据量太大,SQL 越跑越慢 | 单表数据量减少,SQL 执行效率明显提升 |
43 |
44 | ### 用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?
45 | 这个其实就是看看你了解哪些分库分表的中间件,各个中间件的优缺点是啥?然后你用过哪些分库分表的中间件。
46 |
47 | 比较常见的包括:
48 |
49 | - Cobar
50 | - TDDL
51 | - Atlas
52 | - Sharding-jdbc
53 | - Mycat
54 |
55 | #### Cobar
56 | 阿里 b2b 团队开发和开源的,属于 proxy 层方案,就是介于应用服务器和数据库服务器之间。应用程序通过 JDBC 驱动访问 Cobar 集群,Cobar 根据 SQL 和分库规则对 SQL 做分解,然后分发到 MySQL 集群不同的数据库实例上执行。早些年还可以用,但是最近几年都没更新了,基本没啥人用,差不多算是被抛弃的状态吧。而且不支持读写分离、存储过程、跨库 join 和分页等操作。
57 |
58 | #### TDDL
59 | 淘宝团队开发的,属于 client 层方案。支持基本的 crud 语法和读写分离,但不支持 join、多表查询等语法。目前使用的也不多,因为还依赖淘宝的 diamond 配置管理系统。
60 |
61 | #### Atlas
62 | 360 开源的,属于 proxy 层方案,以前是有一些公司在用的,但是确实有一个很大的问题就是社区最新的维护都在 5 年前了。所以,现在用的公司基本也很少了。
63 |
64 | #### Sharding-jdbc
65 | 当当开源的,属于 client 层方案,目前已经更名为 [`ShardingSphere`](https://github.com/apache/incubator-shardingsphere)(后文所提到的 `Sharding-jdbc`,等同于 `ShardingSphere`)。确实之前用的还比较多一些,因为 SQL 语法支持也比较多,没有太多限制,而且截至 2019.4,已经推出到了 `4.0.0-RC1` 版本,支持分库分表、读写分离、分布式 id 生成、柔性事务(最大努力送达型事务、TCC 事务)。而且确实之前使用的公司会比较多一些(这个在官网有登记使用的公司,可以看到从 2017 年一直到现在,是有不少公司在用的),目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也**可以选择的方案**。
66 |
67 | #### Mycat
68 | 基于 Cobar 改造的,属于 proxy 层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于 Sharding jdbc 来说,年轻一些,经历的锤炼少一些。
69 |
70 | #### 总结
71 | 综上,现在其实建议考量的,就是 Sharding-jdbc 和 Mycat,这两个都可以去考虑使用。
72 |
73 | Sharding-jdbc 这种 client 层方案的**优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高**,但是如果遇到升级啥的需要各个系统都重新升级版本再发布,各个系统都需要**耦合** Sharding-jdbc 的依赖;
74 |
75 | Mycat 这种 proxy 层方案的**缺点在于需要部署**,自己运维一套中间件,运维成本高,但是**好处在于对于各个项目是透明的**,如果遇到升级之类的都是自己中间件那里搞就行了。
76 |
77 | 通常来说,这两个方案其实都可以选用,但是我个人建议中小型公司选用 Sharding-jdbc,client 层方案轻便,而且维护成本低,不需要额外增派人手,而且中小型公司系统复杂度会低一些,项目也没那么多;但是中大型公司最好还是选用 Mycat 这类 proxy 层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护 Mycat,然后大量项目直接透明使用即可。
78 |
79 | ### 你们具体是如何对数据库如何进行垂直拆分或水平拆分的?
80 |
81 | **水平拆分**的意思,就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来扛更高的并发,还有就是用多个库的存储容量来进行扩容。
82 |
83 | 
84 |
85 | **垂直拆分**的意思,就是**把一个有很多字段的表给拆分成多个表**,**或者是多个库上去**。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会**将较少的访问频率很高的字段放到一个表里去**,然后**将较多的访问频率很低的字段放到另外一个表里去**。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。
86 |
87 | 
88 |
89 | 这个其实挺常见的,不一定我说,大家很多同学可能自己都做过,把一个大表拆开,订单表、订单支付表、订单商品表。
90 |
91 | 还有**表层面的拆分**,就是分表,将一个表变成 N 个表,就是**让每个表的数据量控制在一定范围内**,保证 SQL 的性能。否则单表数据量越大,SQL 性能就越差。一般是 200 万行左右,不要太多,但是也得看具体你怎么操作,也可能是 500 万,或者是 100 万。你的SQL越复杂,就最好让单表行数越少。
92 |
93 | 好了,无论分库还是分表,上面说的那些数据库中间件都是可以支持的。就是基本上那些中间件可以做到你分库分表之后,**中间件可以根据你指定的某个字段值**,比如说 userid,**自动路由到对应的库上去,然后再自动路由到对应的表里去**。
94 |
95 | 你就得考虑一下,你的项目里该如何分库分表?一般来说,垂直拆分,你可以在表层面来做,对一些字段特别多的表做一下拆分;水平拆分,你可以说是并发承载不了,或者是数据量太大,容量承载不了,你给拆了,按什么字段来拆,你自己想好;分表,你考虑一下,你如果哪怕是拆到每个库里去,并发和容量都 ok 了,但是每个库的表还是太大了,那么你就分表,将这个表分开,保证每个表的数据量并不是很大。
96 |
97 |
98 | 而且这儿还有两种**分库分表的方式**:
99 |
100 | - 一种是按照 range 来分,就是每个库一段连续的数据,这个一般是按比如**时间范围**来的,但是这种一般较少用,因为很容易产生热点问题,大量的流量都打在最新的数据上了。
101 | - 或者是按照某个字段 hash 一下均匀分散,这个较为常用。
102 |
103 | range 来分,好处在于说,扩容的时候很简单,因为你只要预备好,给每个月都准备一个库就可以了,到了一个新的月份的时候,自然而然,就会写新的库了;缺点,但是大部分的请求,都是访问最新的数据。实际生产用 range,要看场景。
104 |
105 | hash 分发,好处在于说,可以平均分配每个库的数据量和请求压力;坏处在于说扩容起来比较麻烦,会有一个数据迁移的过程,之前的数据需要重新计算 hash 值重新分配到不同的库或表。
106 |
--------------------------------------------------------------------------------
/docs/high-concurrency/es-architecture.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | es 的分布式架构原理能说一下么(es 是如何实现分布式的啊)?
3 |
4 | ## 面试官心理分析
5 | 在搜索这块,lucene 是最流行的搜索库。几年前业内一般都问,你了解 lucene 吗?你知道倒排索引的原理吗?现在早已经 out 了,因为现在很多项目都是直接用基于 lucene 的分布式搜索引擎—— ElasticSearch,简称为 es。
6 |
7 | 而现在分布式搜索基本已经成为大部分互联网行业的 Java 系统的标配,其中尤为流行的就是 es,前几年 es 没火的时候,大家一般用 solr。但是这两年基本大部分企业和项目都开始转向 es 了。
8 |
9 | 所以互联网面试,肯定会跟你聊聊分布式搜索引擎,也就一定会聊聊 es,如果你确实不知道,那你真的就 out 了。
10 |
11 | 如果面试官问你第一个问题,确实一般都会问你 es 的分布式架构设计能介绍一下么?就看看你对分布式搜索引擎架构的一个基本理解。
12 |
13 | ## 面试题剖析
14 | ElasticSearch 设计的理念就是分布式搜索引擎,底层其实还是基于 lucene 的。核心思想就是在多台机器上启动多个 es 进程实例,组成了一个 es 集群。
15 |
16 | es 中存储数据的**基本单位是索引**,比如说你现在要在 es 中存储一些订单数据,你就应该在 es 中创建一个索引 `order_idx`,所有的订单数据就都写到这个索引里面去,一个索引差不多就是相当于是 mysql 里的一张表。
17 |
18 | ```
19 | index -> type -> mapping -> document -> field。
20 | ```
21 |
22 | 这样吧,为了做个更直白的介绍,我在这里做个类比。但是切记,不要划等号,类比只是为了便于理解。
23 |
24 | index 相当于 mysql 里的一张表。而 type 没法跟 mysql 里去对比,一个 index 里可以有多个 type,每个 type 的字段都是差不多的,但是有一些略微的差别。假设有一个 index,是订单 index,里面专门是放订单数据的。就好比说你在 mysql 中建表,有些订单是实物商品的订单,比如一件衣服、一双鞋子;有些订单是虚拟商品的订单,比如游戏点卡,话费充值。就两种订单大部分字段是一样的,但是少部分字段可能有略微的一些差别。
25 |
26 | 所以就会在订单 index 里,建两个 type,一个是实物商品订单 type,一个是虚拟商品订单 type,这两个 type 大部分字段是一样的,少部分字段是不一样的。
27 |
28 | 很多情况下,一个 index 里可能就一个 type,但是确实如果说是一个 index 里有多个 type 的情况(**注意**,`mapping types` 这个概念在 ElasticSearch 7.X 已被完全移除,详细说明可以参考[官方文档](https://github.com/elastic/elasticsearch/blob/6.5/docs/reference/mapping/removal_of_types.asciidoc)),你可以认为 index 是一个类别的表,具体的每个 type 代表了 mysql 中的一个表。每个 type 有一个 mapping,如果你认为一个 type 是具体的一个表,index 就代表多个 type 同属于的一个类型,而 mapping 就是这个 type 的**表结构定义**,你在 mysql 中创建一个表,肯定是要定义表结构的,里面有哪些字段,每个字段是什么类型。实际上你往 index 里的一个 type 里面写的一条数据,叫做一条 document,一条 document 就代表了 mysql 中某个表里的一行,每个 document 有多个 field,每个 field 就代表了这个 document 中的一个字段的值。
29 |
30 | 
31 |
32 | 你搞一个索引,这个索引可以拆分成多个 `shard`,每个 shard 存储部分数据。拆分多个 shard 是有好处的,一是**支持横向扩展**,比如你数据量是 3T,3 个 shard,每个 shard 就 1T 的数据,若现在数据量增加到 4T,怎么扩展,很简单,重新建一个有 4 个 shard 的索引,将数据导进去;二是**提高性能**,数据分布在多个 shard,即多台服务器上,所有的操作,都会在多台机器上并行分布式执行,提高了吞吐量和性能。
33 |
34 | 接着就是这个 shard 的数据实际是有多个备份,就是说每个 shard 都有一个 `primary shard`,负责写入数据,但是还有几个 `replica shard`。`primary shard` 写入数据之后,会将数据同步到其他几个 `replica shard` 上去。
35 |
36 | 
37 |
38 | 通过这个 replica 的方案,每个 shard 的数据都有多个备份,如果某个机器宕机了,没关系啊,还有别的数据副本在别的机器上呢。高可用了吧。
39 |
40 | es 集群多个节点,会自动选举一个节点为 master 节点,这个 master 节点其实就是干一些管理的工作的,比如维护索引元数据、负责切换 primary shard 和 replica shard 身份等。要是 master 节点宕机了,那么会重新选举一个节点为 master 节点。
41 |
42 | 如果是非 master节点宕机了,那么会由 master 节点,让那个宕机节点上的 primary shard 的身份转移到其他机器上的 replica shard。接着你要是修复了那个宕机机器,重启了之后,master 节点会控制将缺失的 replica shard 分配过去,同步后续修改的数据之类的,让集群恢复正常。
43 |
44 | 说得更简单一点,就是说如果某个非 master 节点宕机了。那么此节点上的 primary shard 不就没了。那好,master 会让 primary shard 对应的 replica shard(在其他机器上)切换为 primary shard。如果宕机的机器修复了,修复后的节点也不再是 primary shard,而是 replica shard。
45 |
46 | 其实上述就是 ElasticSearch 作为分布式搜索引擎最基本的一个架构设计。
--------------------------------------------------------------------------------
/docs/high-concurrency/es-introduction.md:
--------------------------------------------------------------------------------
1 | ## lucene 和 es 的前世今生
2 | lucene 是最先进、功能最强大的搜索库。如果直接基于 lucene 开发,非常复杂,即便写一些简单的功能,也要写大量的 Java 代码,需要深入理解原理。
3 |
4 | elasticsearch 基于 lucene,隐藏了 lucene 的复杂性,提供了简单易用的 restful api / Java api 接口(另外还有其他语言的 api 接口)。
5 |
6 | - 分布式的文档存储引擎
7 | - 分布式的搜索引擎和分析引擎
8 | - 分布式,支持 PB 级数据
9 |
10 | ## es 的核心概念
11 | ### Near Realtime
12 | 近实时,有两层意思:
13 |
14 | - 从写入数据到数据可以被搜索到有一个小延迟(大概是 1s)
15 | - 基于 es 执行搜索和分析可以达到秒级
16 |
17 | ### Cluster 集群
18 | 集群包含多个节点,每个节点属于哪个集群都是通过一个配置来决定的,对于中小型应用来说,刚开始一个集群就一个节点很正常。
19 |
20 | ### Node 节点
21 | Node 是集群中的一个节点,节点也有一个名称,默认是随机分配的。默认节点会去加入一个名称为 `elasticsearch` 的集群。如果直接启动一堆节点,那么它们会自动组成一个 elasticsearch 集群,当然一个节点也可以组成 elasticsearch 集群。
22 |
23 | ### Document & field
24 | 文档是 es 中最小的数据单元,一个 document 可以是一条客户数据、一条商品分类数据、一条订单数据,通常用 json 数据结构来表示。每个 index 下的 type,都可以存储多条 document。一个 document 里面有多个 field,每个 field 就是一个数据字段。
25 |
26 | ```json
27 | {
28 | "product_id": "1",
29 | "product_name": "iPhone X",
30 | "product_desc": "苹果手机",
31 | "category_id": "2",
32 | "category_name": "电子产品"
33 | }
34 | ```
35 |
36 | ### Index
37 | 索引包含了一堆有相似结构的文档数据,比如商品索引。一个索引包含很多 document,一个索引就代表了一类相似或者相同的 ducument。
38 |
39 | ### Type
40 | 类型,每个索引里可以有一个或者多个 type,type 是 index 的一个逻辑分类,比如商品 index 下有多个 type:日化商品 type、电器商品 type、生鲜商品 type。每个 type 下的 document 的 field 可能不太一样。
41 |
42 | ### shard
43 | 单台机器无法存储大量数据,es 可以将一个索引中的数据切分为多个 shard,分布在多台服务器上存储。有了 shard 就可以横向扩展,存储更多数据,让搜索和分析等操作分布到多台服务器上去执行,提升吞吐量和性能。每个 shard 都是一个 lucene index。
44 |
45 | ### replica
46 | 任何一个服务器随时可能故障或宕机,此时 shard 可能就会丢失,因此可以为每个 shard 创建多个 replica 副本。replica 可以在 shard 故障时提供备用服务,保证数据不丢失,多个 replica 还可以提升搜索操作的吞吐量和性能。primary shard(建立索引时一次设置,不能修改,默认 5 个),replica shard(随时修改数量,默认 1 个),默认每个索引 10 个 shard,5 个 primary shard,5个 replica shard,最小的高可用配置,是 2 台服务器。
47 |
48 | 这么说吧,shard 分为 primary shard 和 replica shard。而 primary shard 一般简称为 shard,而 replica shard 一般简称为 replica。
49 |
50 | 
51 |
52 | ## es 核心概念 vs. db 核心概念
53 | | es | db |
54 | |---|---|
55 | | index | 数据库 |
56 | | type | 数据表 |
57 | | docuemnt | 一行数据 |
58 |
59 | 以上是一个简单的类比。
--------------------------------------------------------------------------------
/docs/high-concurrency/es-optimizing-query-performance.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?
3 |
4 | ## 面试官心理分析
5 | 这个问题是肯定要问的,说白了,就是看你有没有实际干过 es,因为啥?其实 es 性能并没有你想象中那么好的。很多时候数据量大了,特别是有几亿条数据的时候,可能你会懵逼的发现,跑个搜索怎么一下 `5~10s`,坑爹了。第一次搜索的时候,是 `5~10s`,后面反而就快了,可能就几百毫秒。
6 |
7 | 你就很懵,每个用户第一次访问都会比较慢,比较卡么?所以你要是没玩儿过 es,或者就是自己玩玩儿 demo,被问到这个问题容易懵逼,显示出你对 es 确实玩儿的不怎么样?
8 |
9 | ## 面试题剖析
10 | 说实话,es 性能优化是没有什么银弹的,啥意思呢?就是**不要期待着随手调一个参数,就可以万能的应对所有的性能慢的场景**。也许有的场景是你换个参数,或者调整一下语法,就可以搞定,但是绝对不是所有场景都可以这样。
11 |
12 | ### 性能优化的杀手锏——filesystem cache
13 | 你往 es 里写的数据,实际上都写到磁盘文件里去了,**查询的时候**,操作系统会将磁盘文件里的数据自动缓存到 `filesystem cache` 里面去。
14 |
15 | 
16 |
17 | es 的搜索引擎严重依赖于底层的 `filesystem cache`,你如果给 `filesystem cache` 更多的内存,尽量让内存可以容纳所有的 `idx segment file ` 索引数据文件,那么你搜索的时候就基本都是走内存的,性能会非常高。
18 |
19 | 性能差距究竟可以有多大?我们之前很多的测试和压测,如果走磁盘一般肯定上秒,搜索性能绝对是秒级别的,1秒、5秒、10秒。但如果是走 `filesystem cache`,是走纯内存的,那么一般来说性能比走磁盘要高一个数量级,基本上就是毫秒级的,从几毫秒到几百毫秒不等。
20 |
21 | 这里有个真实的案例。某个公司 es 节点有 3 台机器,每台机器看起来内存很多,64G,总内存就是 `64 * 3 = 192G`。每台机器给 es jvm heap 是 `32G`,那么剩下来留给 `filesystem cache` 的就是每台机器才 `32G`,总共集群里给 `filesystem cache` 的就是 `32 * 3 = 96G` 内存。而此时,整个磁盘上索引数据文件,在 3 台机器上一共占用了 `1T` 的磁盘容量,es 数据量是 `1T`,那么每台机器的数据量是 `300G`。这样性能好吗? `filesystem cache` 的内存才 100G,十分之一的数据可以放内存,其他的都在磁盘,然后你执行搜索操作,大部分操作都是走磁盘,性能肯定差。
22 |
23 | 归根结底,你要让 es 性能要好,最佳的情况下,就是你的机器的内存,至少可以容纳你的总数据量的一半。
24 |
25 | 根据我们自己的生产环境实践经验,最佳的情况下,是仅仅在 es 中就存少量的数据,就是你要**用来搜索的那些索引**,如果内存留给 `filesystem cache` 的是 100G,那么你就将索引数据控制在 `100G` 以内,这样的话,你的数据几乎全部走内存来搜索,性能非常之高,一般可以在 1 秒以内。
26 |
27 | 比如说你现在有一行数据。`id,name,age ....` 30 个字段。但是你现在搜索,只需要根据 `id,name,age` 三个字段来搜索。如果你傻乎乎往 es 里写入一行数据所有的字段,就会导致说 `90%` 的数据是不用来搜索的,结果硬是占据了 es 机器上的 `filesystem cache` 的空间,单条数据的数据量越大,就会导致 `filesystem cahce` 能缓存的数据就越少。其实,仅仅写入 es 中要用来检索的**少数几个字段**就可以了,比如说就写入 es `id,name,age` 三个字段,然后你可以把其他的字段数据存在 mysql/hbase 里,我们一般是建议用 `es + hbase` 这么一个架构。
28 |
29 | hbase 的特点是**适用于海量数据的在线存储**,就是对 hbase 可以写入海量数据,但是不要做复杂的搜索,做很简单的一些根据 id 或者范围进行查询的这么一个操作就可以了。从 es 中根据 name 和 age 去搜索,拿到的结果可能就 20 个 `doc id`,然后根据 `doc id` 到 hbase 里去查询每个 `doc id` 对应的**完整的数据**,给查出来,再返回给前端。
30 |
31 | 写入 es 的数据最好小于等于,或者是略微大于 es 的 filesystem cache 的内存容量。然后你从 es 检索可能就花费 20ms,然后再根据 es 返回的 id 去 hbase 里查询,查 20 条数据,可能也就耗费个 30ms,可能你原来那么玩儿,1T 数据都放 es,会每次查询都是 5~10s,现在可能性能就会很高,每次查询就是 50ms。
32 |
33 | ### 数据预热
34 | 假如说,哪怕是你就按照上述的方案去做了,es 集群中每个机器写入的数据量还是超过了 `filesystem cache` 一倍,比如说你写入一台机器 60G 数据,结果 `filesystem cache` 就 30G,还是有 30G 数据留在了磁盘上。
35 |
36 | 其实可以做**数据预热**。
37 |
38 | 举个例子,拿微博来说,你可以把一些大V,平时看的人很多的数据,你自己提前后台搞个系统,每隔一会儿,自己的后台系统去搜索一下热数据,刷到 `filesystem cache` 里去,后面用户实际上来看这个热数据的时候,他们就是直接从内存里搜索了,很快。
39 |
40 | 或者是电商,你可以将平时查看最多的一些商品,比如说 iphone 8,热数据提前后台搞个程序,每隔 1 分钟自己主动访问一次,刷到 `filesystem cache` 里去。
41 |
42 | 对于那些你觉得比较热的、经常会有人访问的数据,最好**做一个专门的缓存预热子系统**,就是对热数据每隔一段时间,就提前访问一下,让数据进入 `filesystem cache` 里面去。这样下次别人访问的时候,性能一定会好很多。
43 |
44 | ### 冷热分离
45 | es 可以做类似于 mysql 的水平拆分,就是说将大量的访问很少、频率很低的数据,单独写一个索引,然后将访问很频繁的热数据单独写一个索引。最好是将**冷数据写入一个索引中,然后热数据写入另外一个索引中**,这样可以确保热数据在被预热之后,尽量都让他们留在 `filesystem os cache` 里,**别让冷数据给冲刷掉**。
46 |
47 | 你看,假设你有 6 台机器,2 个索引,一个放冷数据,一个放热数据,每个索引 3 个 shard。3 台机器放热数据 index,另外 3 台机器放冷数据 index。然后这样的话,你大量的时间是在访问热数据 index,热数据可能就占总数据量的 10%,此时数据量很少,几乎全都保留在 `filesystem cache` 里面了,就可以确保热数据的访问性能是很高的。但是对于冷数据而言,是在别的 index 里的,跟热数据 index 不在相同的机器上,大家互相之间都没什么联系了。如果有人访问冷数据,可能大量数据是在磁盘上的,此时性能差点,就 10% 的人去访问冷数据,90% 的人在访问热数据,也无所谓了。
48 |
49 | ### document 模型设计
50 | 对于 MySQL,我们经常有一些复杂的关联查询。在 es 里该怎么玩儿,es 里面的复杂的关联查询尽量别用,一旦用了性能一般都不太好。
51 |
52 | 最好是先在 Java 系统里就完成关联,将关联好的数据直接写入 es 中。搜索的时候,就不需要利用 es 的搜索语法来完成 join 之类的关联搜索了。
53 |
54 | document 模型设计是非常重要的,很多操作,不要在搜索的时候才想去执行各种复杂的乱七八糟的操作。es 能支持的操作就那么多,不要考虑用 es 做一些它不好操作的事情。如果真的有那种操作,尽量在 document 模型设计的时候,写入的时候就完成。另外对于一些太复杂的操作,比如 join/nested/parent-child 搜索都要尽量避免,性能都很差的。
55 |
56 | ### 分页性能优化
57 | es 的分页是较坑的,为啥呢?举个例子吧,假如你每页是 10 条数据,你现在要查询第 100 页,实际上是会把每个 shard 上存储的前 1000 条数据都查到一个协调节点上,如果你有个 5 个 shard,那么就有 5000 条数据,接着协调节点对这 5000 条数据进行一些合并、处理,再获取到最终第 100 页的 10 条数据。
58 |
59 | 分布式的,你要查第 100 页的 10 条数据,不可能说从 5 个 shard,每个 shard 就查 2 条数据,最后到协调节点合并成 10 条数据吧?你**必须**得从每个 shard 都查 1000 条数据过来,然后根据你的需求进行排序、筛选等等操作,最后再次分页,拿到里面第 100 页的数据。你翻页的时候,翻的越深,每个 shard 返回的数据就越多,而且协调节点处理的时间越长,非常坑爹。所以用 es 做分页的时候,你会发现越翻到后面,就越是慢。
60 |
61 | 我们之前也是遇到过这个问题,用 es 作分页,前几页就几十毫秒,翻到 10 页或者几十页的时候,基本上就要 5~10 秒才能查出来一页数据了。
62 |
63 | 有什么解决方案吗?
64 | #### 不允许深度分页(默认深度分页性能很差)
65 | 跟产品经理说,你系统不允许翻那么深的页,默认翻的越深,性能就越差。
66 |
67 | #### 类似于 app 里的推荐商品不断下拉出来一页一页的
68 | 类似于微博中,下拉刷微博,刷出来一页一页的,你可以用 `scroll api`,关于如何使用,自行上网搜索。
69 |
70 | scroll 会一次性给你生成**所有数据的一个快照**,然后每次滑动向后翻页就是通过**游标** `scroll_id` 移动,获取下一页下一页这样子,性能会比上面说的那种分页性能要高很多很多,基本上都是毫秒级的。
71 |
72 | 但是,唯一的一点就是,这个适合于那种类似微博下拉翻页的,**不能随意跳到任何一页的场景**。也就是说,你不能先进入第 10 页,然后去第 120 页,然后又回到第 58 页,不能随意乱跳页。所以现在很多产品,都是不允许你随意翻页的,app,也有一些网站,做的就是你只能往下拉,一页一页的翻。
73 |
74 | 初始化时必须指定 `scroll` 参数,告诉 es 要保存此次搜索的上下文多长时间。你需要确保用户不会持续不断翻页翻几个小时,否则可能因为超时而失败。
75 |
76 | 除了用 `scroll api`,你也可以用 `search_after` 来做,`search_after` 的思想是使用前一页的结果来帮助检索下一页的数据,显然,这种方式也不允许你随意翻页,你只能一页页往后翻。初始化时,需要使用一个唯一值的字段作为 sort 字段。
--------------------------------------------------------------------------------
/docs/high-concurrency/es-production-cluster.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?
3 |
4 | ## 面试官心理分析
5 | 这个问题,包括后面的 redis 什么的,谈到 es、redis、mysql 分库分表等等技术,面试必问!就是你生产环境咋部署的?说白了,这个问题没啥技术含量,就是看你有没有在真正的生产环境里干过这事儿!
6 |
7 | 有些同学可能是没在生产环境中干过的,没实际去拿线上机器部署过 es 集群,也没实际玩儿过,也没往 es 集群里面导入过几千万甚至是几亿的数据量,可能你就不太清楚这里面的一些生产项目中的细节。
8 |
9 | 如果你是自己就玩儿过 demo,没碰过真实的 es 集群,那你可能此时会懵。别懵,你一定要云淡风轻的回答出来这个问题,表示你确实干过这事儿。
10 |
11 | ## 面试题剖析
12 | 其实这个问题没啥,如果你确实干过 es,那你肯定了解你们生产 es 集群的实际情况,部署了几台机器?有多少个索引?每个索引有多大数据量?每个索引给了多少个分片?你肯定知道!
13 |
14 | 但是如果你确实没干过,也别虚,我给你说一个基本的版本,你到时候就简单说一下就好了。
15 |
16 | - es 生产集群我们部署了 5 台机器,每台机器是 6 核 64G 的,集群总内存是 320G。
17 | - 我们 es 集群的日增量数据大概是 2000 万条,每天日增量数据大概是 500MB,每月增量数据大概是 6 亿,15G。目前系统已经运行了几个月,现在 es 集群里数据总量大概是 100G 左右。
18 | - 目前线上有 5 个索引(这个结合你们自己业务来,看看自己有哪些数据可以放 es 的),每个索引的数据量大概是 20G,所以这个数据量之内,我们每个索引分配的是 8 个 shard,比默认的 5 个 shard 多了 3 个 shard。
19 |
20 | 大概就这么说一下就行了。
--------------------------------------------------------------------------------
/docs/high-concurrency/high-concurrency-design.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何设计一个高并发系统?
3 |
4 | ## 面试官心理分析
5 | 说实话,如果面试官问你这个题目,那么你必须要使出全身吃奶劲了。为啥?因为你没看到现在很多公司招聘的 JD 里都是说啥,有高并发就经验者优先。
6 |
7 | 如果你确实有真才实学,在互联网公司里干过高并发系统,那你确实拿 offer 基本如探囊取物,没啥问题。面试官也绝对不会这样来问你,否则他就是蠢。
8 |
9 | 假设你在某知名电商公司干过高并发系统,用户上亿,一天流量几十亿,高峰期并发量上万,甚至是十万。那么人家一定会仔细盘问你的系统架构,你们系统啥架构?怎么部署的?部署了多少台机器?缓存咋用的?MQ 咋用的?数据库咋用的?就是深挖你到底是如何扛住高并发的。
10 |
11 | 因为真正干过高并发的人一定知道,脱离了业务的系统架构都是在纸上谈兵,真正在复杂业务场景而且还高并发的时候,那系统架构一定不是那么简单的,用个 redis,用 mq 就能搞定?当然不是,真实的系统架构搭配上业务之后,会比这种简单的所谓“高并发架构”要复杂很多倍。
12 |
13 | 如果有面试官问你个问题说,如何设计一个高并发系统?那么不好意思,**一定是因为你实际上没干过高并发系统**。面试官看你简历就没啥出彩的,感觉就不咋地,所以就会问问你,如何设计一个高并发系统?其实说白了本质就是看看你有没有自己研究过,有没有一定的知识积累。
14 |
15 | 最好的当然是招聘个真正干过高并发的哥儿们咯,但是这种哥儿们人数稀缺,不好招。所以可能次一点的就是招一个自己研究过的哥儿们,总比招一个啥也不会的哥儿们好吧!
16 |
17 | 所以这个时候你必须得做一把个人秀了,秀出你所有关于高并发的知识!
18 |
19 |
20 | ## 面试题剖析
21 | 其实所谓的高并发,如果你要理解这个问题呢,其实就得从高并发的根源出发,为啥会有高并发?为啥高并发就很牛逼?
22 |
23 | 我说的浅显一点,很简单,就是因为刚开始系统都是连接数据库的,但是要知道数据库支撑到每秒并发两三千的时候,基本就快完了。所以才有说,很多公司,刚开始干的时候,技术比较 low,结果业务发展太快,有的时候系统扛不住压力就挂了。
24 |
25 | 当然会挂了,凭什么不挂?你数据库如果瞬间承载每秒 5000/8000,甚至上万的并发,一定会宕机,因为比如 mysql 就压根儿扛不住这么高的并发量。
26 |
27 | 所以为啥高并发牛逼?就是因为现在用互联网的人越来越多,很多 app、网站、系统承载的都是高并发请求,可能高峰期每秒并发量几千,很正常的。如果是什么双十一之类的,每秒并发几万几十万都有可能。
28 |
29 | 那么如此之高的并发量,加上原本就如此之复杂的业务,咋玩儿?真正厉害的,一定是在复杂业务系统里玩儿过高并发架构的人,但是你没有,那么我给你说一下你该怎么回答这个问题:
30 |
31 | 可以分为以下 6 点:
32 |
33 | - 系统拆分
34 | - 缓存
35 | - MQ
36 | - 分库分表
37 | - 读写分离
38 | - ElasticSearch
39 |
40 | 
41 |
42 | ### 系统拆分
43 | 将一个系统拆分为多个子系统,用 dubbo 来搞。然后每个系统连一个数据库,这样本来就一个库,现在多个数据库,不也可以扛高并发么。
44 |
45 | ### 缓存
46 | 缓存,必须得用缓存。大部分的高并发场景,都是**读多写少**,那你完全可以在数据库和缓存里都写一份,然后读的时候大量走缓存不就得了。毕竟人家 redis 轻轻松松单机几万的并发。所以你可以考虑考虑你的项目里,那些承载主要请求的**读场景,怎么用缓存来抗高并发**。
47 |
48 | ### MQ
49 | MQ,必须得用 MQ。可能你还是会出现高并发写的场景,比如说一个业务操作里要频繁搞数据库几十次,增删改增删改,疯了。那高并发绝对搞挂你的系统,你要是用 redis 来承载写那肯定不行,人家是缓存,数据随时就被 LRU 了,数据格式还无比简单,没有事务支持。所以该用 mysql 还得用 mysql 啊。那你咋办?用 MQ 吧,大量的写请求灌入 MQ 里,排队慢慢玩儿,**后边系统消费后慢慢写**,控制在 mysql 承载范围之内。所以你得考虑考虑你的项目里,那些承载复杂写业务逻辑的场景里,如何用 MQ 来异步写,提升并发性。MQ 单机抗几万并发也是 ok 的,这个之前还特意说过。
50 |
51 | ### 分库分表
52 | 分库分表,可能到了最后数据库层面还是免不了抗高并发的要求,好吧,那么就将一个数据库拆分为多个库,多个库来扛更高的并发;然后将一个表**拆分为多个表**,每个表的数据量保持少一点,提高 sql 跑的性能。
53 |
54 | ### 读写分离
55 | 读写分离,这个就是说大部分时候数据库可能也是读多写少,没必要所有请求都集中在一个库上吧,可以搞个主从架构,**主库写**入,**从库读**取,搞一个读写分离。**读流量太多**的时候,还可以**加更多的从库**。
56 |
57 | ### ElasticSearch
58 | Elasticsearch,简称 es。es 是分布式的,可以随便扩容,分布式天然就可以支撑高并发,因为动不动就可以扩容加机器来扛更高的并发。那么一些比较简单的查询、统计类的操作,可以考虑用 es 来承载,还有一些全文搜索类的操作,也可以考虑用 es 来承载。
59 |
60 |
61 | 上面的 6 点,基本就是高并发系统肯定要干的一些事儿,大家可以仔细结合之前讲过的知识考虑一下,到时候你可以系统的把这块阐述一下,然后每个部分要注意哪些问题,之前都讲过了,你都可以阐述阐述,表明你对这块是有点积累的。
62 |
63 | 说句实话,毕竟你真正厉害的一点,不是在于弄明白一些技术,或者大概知道一个高并发系统应该长什么样?其实实际上在真正的复杂的业务系统里,做高并发要远远比上面提到的点要复杂几十倍到上百倍。你需要考虑:哪些需要分库分表,哪些不需要分库分表,单库单表跟分库分表如何 join,哪些数据要放到缓存里去,放哪些数据才可以扛住高并发的请求,你需要完成对一个复杂业务系统的分析之后,然后逐步逐步的加入高并发的系统架构的改造,这个过程是无比复杂的,一旦做过一次,并且做好了,你在这个市场上就会非常的吃香。
64 |
65 | 其实大部分公司,真正看重的,不是说你掌握高并发相关的一些基本的架构知识,架构中的一些技术,RocketMQ、Kafka、Redis、Elasticsearch,高并发这一块,你了解了,也只能是次一等的人才。对一个有几十万行代码的复杂的分布式系统,一步一步架构、设计以及实践过高并发架构的人,这个经验是难能可贵的。
--------------------------------------------------------------------------------
/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证消息队列的高可用?
3 |
4 | ## 面试官心理分析
5 | 如果有人问到你 MQ 的知识,**高可用是必问的**。[上一讲](/docs/high-concurrency/why-mq.md)提到,MQ 会导致**系统可用性降低**。所以只要你用了 MQ,接下来问的一些要点肯定就是围绕着 MQ 的那些缺点怎么来解决了。
6 |
7 | 要是你傻乎乎的就干用了一个 MQ,各种问题从来没考虑过,那你就杯具了,面试官对你的感觉就是,只会简单使用一些技术,没任何思考,马上对你的印象就不太好了。这样的同学招进来要是做个 20k 薪资以内的普通小弟还凑合,要是做薪资 20k+ 的高工,那就惨了,让你设计个系统,里面肯定一堆坑,出了事故公司受损失,团队一起背锅。
8 |
9 | ## 面试题剖析
10 | 这个问题这么问是很好的,因为不能问你 Kafka 的高可用性怎么保证?ActiveMQ 的高可用性怎么保证?一个面试官要是这么问就显得很没水平,人家可能用的就是 RabbitMQ,没用过 Kafka,你上来问人家 Kafka 干什么?这不是摆明了刁难人么。
11 |
12 | 所以有水平的面试官,问的是 MQ 的高可用性怎么保证?这样就是你用过哪个 MQ,你就说说你对那个 MQ 的高可用性的理解。
13 |
14 | ### RabbitMQ 的高可用性
15 | RabbitMQ 是比较有代表性的,因为是**基于主从**(非分布式)做高可用性的,我们就以 RabbitMQ 为例子讲解第一种 MQ 的高可用性怎么实现。
16 |
17 | RabbitMQ 有三种模式:单机模式、普通集群模式、镜像集群模式。
18 |
19 | #### 单机模式
20 | 单机模式,就是 Demo 级别的,一般就是你本地启动了玩玩儿的😄,没人生产用单机模式。
21 |
22 | #### 普通集群模式(无高可用性)
23 | 普通集群模式,意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。你**创建的 queue,只会放在一个 RabbitMQ 实例上**,但是每个实例都同步 queue 的元数据(元数据可以认为是 queue 的一些配置信息,通过元数据,可以找到 queue 所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。
24 |
25 | 
26 |
27 | 这种方式确实很麻烦,也不怎么好,**没做到所谓的分布式**,就是个普通集群。因为这导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个 queue 所在实例消费数据,前者有**数据拉取的开销**,后者导致**单实例性能瓶颈**。
28 |
29 | 而且如果那个放 queue 的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你**开启了消息持久化**,让 RabbitMQ 落地存储消息的话,**消息不一定会丢**,得等这个实例恢复了,然后才可以继续从这个 queue 拉取数据。
30 |
31 | 所以这个事儿就比较尴尬了,这就**没有什么所谓的高可用性**,**这方案主要是提高吞吐量的**,就是说让集群中多个节点来服务某个 queue 的读写操作。
32 |
33 | #### 镜像集群模式(高可用性)
34 | 这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会**存在于多个实例上**,就是说,每个 RabbitMQ 节点都有这个 queue 的一个**完整镜像**,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把**消息同步**到多个实例的 queue 上。
35 |
36 | 
37 |
38 | 那么**如何开启这个镜像集群模式**呢?其实很简单,RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是**镜像集群模式的策略**,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。
39 |
40 | 这样的话,好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于,第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!第二,这么玩儿,不是分布式的,就**没有扩展性可言**了,如果某个 queue 负载很重,你加机器,新增的机器也包含了这个 queue 的所有数据,并**没有办法线性扩展**你的 queue。你想,如果这个 queue 的数据量很大,大到这个机器上的容量无法容纳了,此时该怎么办呢?
41 |
42 | ### Kafka 的高可用性
43 | Kafka 一个最基本的架构认识:由多个 broker 组成,每个 broker 是一个节点;你创建一个 topic,这个 topic 可以划分为多个 partition,每个 partition 可以存在于不同的 broker 上,每个 partition 就放一部分数据。
44 |
45 | 这就是**天然的分布式消息队列**,就是说一个 topic 的数据,是**分散放在多个机器上的,每个机器就放一部分数据**。
46 |
47 | 实际上 RabbmitMQ 之类的,并不是分布式消息队列,它就是传统的消息队列,只不过提供了一些集群、HA(High Availability, 高可用性) 的机制而已,因为无论怎么玩儿,RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。
48 |
49 | Kafka 0.8 以前,是没有 HA 机制的,就是任何一个 broker 宕机了,那个 broker 上的 partition 就废了,没法写也没法读,没有什么高可用性可言。
50 |
51 | 比如说,我们假设创建了一个 topic,指定其 partition 数量是 3 个,分别在三台机器上。但是,如果第二台机器宕机了,会导致这个 topic 的 1/3 的数据就丢了,因此这个是做不到高可用的。
52 |
53 | 
54 |
55 | Kafka 0.8 以后,提供了 HA 机制,就是 replica(复制品) 副本机制。每个 partition 的数据都会同步到其它机器上,形成自己的多个 replica 副本。所有 replica 会选举一个 leader 出来,那么生产和消费都跟这个 leader 打交道,然后其他 replica 就是 follower。写的时候,leader 会负责把数据同步到所有 follower 上去,读的时候就直接读 leader 上的数据即可。只能读写 leader?很简单,**要是你可以随意读写每个 follower,那么就要 care 数据一致性的问题**,系统复杂度太高,很容易出问题。Kafka 会均匀地将一个 partition 的所有 replica 分布在不同的机器上,这样才可以提高容错性。
56 |
57 | 
58 |
59 | 这么搞,就有所谓的**高可用性**了,因为如果某个 broker 宕机了,没事儿,那个 broker上面的 partition 在其他机器上都有副本的。如果这个宕机的 broker 上面有某个 partition 的 leader,那么此时会从 follower 中**重新选举**一个新的 leader 出来,大家继续读写那个新的 leader 即可。这就有所谓的高可用性了。
60 |
61 | **写数据**的时候,生产者就写 leader,然后 leader 将数据落地写本地磁盘,接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)
62 |
63 | **消费**的时候,只会从 leader 去读,但是只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候,这个消息才会被消费者读到。
64 |
65 | 看到这里,相信你大致明白了 Kafka 是如何保证高可用机制的了,对吧?不至于一无所知,现场还能给面试官画画图。要是遇上面试官确实是 Kafka 高手,深挖了问,那你只能说不好意思,太深入的你没研究过。
--------------------------------------------------------------------------------
/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证 redis 的高并发和高可用?redis 的主从复制原理能介绍一下么?redis 的哨兵原理能介绍一下么?
3 |
4 | ## 面试官心理分析
5 | 其实问这个问题,主要是考考你,redis 单机能承载多高并发?如果单机扛不住如何扩容扛更多的并发?redis 会不会挂?既然 redis 会挂那怎么保证 redis 是高可用的?
6 |
7 | 其实针对的都是项目中你肯定要考虑的一些问题,如果你没考虑过,那确实你对生产系统中的问题思考太少。
8 |
9 | ## 面试题剖析
10 | 如果你用 redis 缓存技术的话,肯定要考虑如何用 redis 来加多台机器,保证 redis 是高并发的,还有就是如何让 redis 保证自己不是挂掉以后就直接死掉了,即 redis 高可用。
11 |
12 | 由于此节内容较多,因此,会分为两个小节进行讲解。
13 | - [redis 主从架构](/docs/high-concurrency/redis-master-slave.md)
14 | - [redis 基于哨兵实现高可用](/docs/high-concurrency/redis-sentinel.md)
15 |
16 | redis 实现**高并发**主要依靠**主从架构**,一主多从,一般来说,很多项目其实就足够了,单主用来写入数据,单机几万 QPS,多从用来查询数据,多个从实例可以提供每秒 10w 的 QPS。
17 |
18 | 如果想要在实现高并发的同时,容纳大量的数据,那么就需要 redis 集群,使用 redis 集群之后,可以提供每秒几十万的读写并发。
19 |
20 | redis 高可用,如果是做主从架构部署,那么加上哨兵就可以了,就可以实现,任何一个实例宕机,可以进行主备切换。
--------------------------------------------------------------------------------
/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?
3 |
4 | ## 面试官心理分析
5 | 其实这是很常见的一个问题,这俩问题基本可以连起来问。既然是消费消息,那肯定要考虑会不会重复消费?能不能避免重复消费?或者重复消费了也别造成系统异常可以吗?这个是 MQ 领域的基本问题,其实本质上还是问你**使用消息队列如何保证幂等性**,这个是你架构里要考虑的一个问题。
6 |
7 | ## 面试题剖析
8 | 回答这个问题,首先你别听到重复消息这个事儿,就一无所知吧,你**先大概说一说可能会有哪些重复消费的问题**。
9 |
10 | 首先,比如 RabbitMQ、RocketMQ、Kafka,都有可能会出现消息重复消费的问题,正常。因为这问题通常不是 MQ 自己保证的,是由我们开发来保证的。挑一个 Kafka 来举个例子,说说怎么重复消费吧。
11 |
12 | Kafka 实际上有个 offset 的概念,就是每个消息写进去,都有一个 offset,代表消息的序号,然后 consumer 消费了数据之后,**每隔一段时间**(定时定期),会把自己消费过的消息的 offset 提交一下,表示“我已经消费过了,下次我要是重启啥的,你就让我继续从上次消费到的 offset 来继续消费吧”。
13 |
14 | 但是凡事总有意外,比如我们之前生产经常遇到的,就是你有时候重启系统,看你怎么重启了,如果碰到点着急的,直接 kill 进程了,再重启。这会导致 consumer 有些消息处理了,但是没来得及提交 offset,尴尬了。重启之后,少数消息会再次消费一次。
15 |
16 | 举个栗子。
17 |
18 | 有这么个场景。数据 1/2/3 依次进入 kafka,kafka 会给这三条数据每条分配一个 offset,代表这条数据的序号,我们就假设分配的 offset 依次是 152/153/154。消费者从 kafka 去消费的时候,也是按照这个顺序去消费。假如当消费者消费了 `offset=153` 的这条数据,刚准备去提交 offset 到 zookeeper,此时消费者进程被重启了。那么此时消费过的数据 1/2 的 offset 并没有提交,kafka 也就不知道你已经消费了 `offset=153` 这条数据。那么重启之后,消费者会找 kafka 说,嘿,哥儿们,你给我接着把上次我消费到的那个地方后面的数据继续给我传递过来。由于之前的 offset 没有提交成功,那么数据 1/2 会再次传过来,如果此时消费者没有去重的话,那么就会导致重复消费。
19 |
20 | 
21 |
22 | 如果消费者干的事儿是拿一条数据就往数据库里写一条,会导致说,你可能就把数据 1/2 在数据库里插入了 2 次,那么数据就错啦。
23 |
24 | 其实重复消费不可怕,可怕的是你没考虑到重复消费之后,**怎么保证幂等性**。
25 |
26 | 举个例子吧。假设你有个系统,消费一条消息就往数据库里插入一条数据,要是你一个消息重复两次,你不就插入了两条,这数据不就错了?但是你要是消费到第二次的时候,自己判断一下是否已经消费过了,若是就直接扔了,这样不就保留了一条数据,从而保证了数据的正确性。
27 |
28 | 一条数据重复出现两次,数据库里就只有一条数据,这就保证了系统的幂等性。
29 |
30 | 幂等性,通俗点说,就一个数据,或者一个请求,给你重复来多次,你得确保对应的数据是不会改变的,**不能出错**。
31 |
32 | 所以第二个问题来了,怎么保证消息队列消费的幂等性?
33 |
34 | 其实还是得结合业务来思考,我这里给几个思路:
35 |
36 | - 比如你拿个数据要写库,你先根据主键查一下,如果这数据都有了,你就别插入了,update 一下好吧。
37 | - 比如你是写 Redis,那没问题了,反正每次都是 set,天然幂等性。
38 | - 比如你不是上面两个场景,那做的稍微复杂一点,你需要让生产者发送每条数据的时候,里面加一个全局唯一的 id,类似订单 id 之类的东西,然后你这里消费到了之后,先根据这个 id 去比如 Redis 里查一下,之前消费过吗?如果没有消费过,你就处理,然后这个 id 写 Redis。如果消费过了,那你就别处理了,保证别重复处理相同的消息即可。
39 | - 比如基于数据库的唯一键来保证重复数据不会重复插入多条。因为有唯一键约束了,重复数据插入只会报错,不会导致数据库中出现脏数据。
40 |
41 | 
42 |
43 | 当然,如何保证 MQ 的消费是幂等性的,需要结合具体的业务来看。
--------------------------------------------------------------------------------
/docs/high-concurrency/how-to-ensure-the-order-of-messages.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证消息的顺序性?
3 |
4 | ## 面试官心理分析
5 | 其实这个也是用 MQ 的时候必问的话题,第一看看你了不了解顺序这个事儿?第二看看你有没有办法保证消息是有顺序的?这是生产系统中常见的问题。
6 |
7 | ## 面试题剖析
8 | 我举个例子,我们以前做过一个 mysql `binlog` 同步的系统,压力还是非常大的,日同步数据要达到上亿,就是说数据从一个 mysql 库原封不动地同步到另一个 mysql 库里面去(mysql -> mysql)。常见的一点在于说比如大数据 team,就需要同步一个 mysql 库过来,对公司的业务系统的数据做各种复杂的操作。
9 |
10 | 你在 mysql 里增删改一条数据,对应出来了增删改 3 条 `binlog` 日志,接着这三条 `binlog` 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你楞是换了顺序给执行成删除、修改、增加,不全错了么。
11 |
12 | 本来这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。
13 |
14 | 先看看顺序会错乱的俩场景:
15 | - **RabbitMQ**:一个 queue,多个 consumer。比如,生产者向 RabbitMQ 里发送了三条数据,顺序依次是 data1/data2/data3,压入的是 RabbitMQ 的一个内存队列。有三个消费者分别从 MQ 中消费这三条数据中的一条,结果消费者2先执行完操作,把 data2 存入数据库,然后是 data1/data3。这不明显乱了。
16 |
17 | 
18 |
19 | - **Kafka**:比如说我们建了一个 topic,有三个 partition。生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到同一个 partition 中去,而且这个 partition 中的数据一定是有顺序的。
消费者从 partition 中取出来数据的时候,也一定是有顺序的。到这里,顺序还是 ok 的,没有错乱。接着,我们在消费者里可能会搞**多个线程来并发处理消息**。因为如果消费者是单线程消费处理,而处理比较耗时的话,比如处理一条消息耗时几十 ms,那么 1 秒钟只能处理几十条消息,这吞吐量太低了。而多个线程并发跑的话,顺序可能就乱掉了。
20 |
21 | 
22 |
23 | ### 解决方案
24 | #### RabbitMQ
25 | 拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。
26 | 
27 |
28 | #### Kafka
29 | - 一个 topic,一个 partition,一个 consumer,内部单线程消费,单线程吞吐量太低,一般不会用这个。
30 | - 写 N 个内存 queue,具有相同 key 的数据都到同一个内存 queue;然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
31 |
32 | 
--------------------------------------------------------------------------------
/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证消息的可靠性传输?或者说,如何处理消息丢失的问题?
3 |
4 | ## 面试官心理分析
5 | 这个是肯定的,用 MQ 有个基本原则,就是**数据不能多一条,也不能少一条**,不能多,就是前面说的[重复消费和幂等性问题](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)。不能少,就是说这数据别搞丢了。那这个问题你必须得考虑一下。
6 |
7 | 如果说你这个是用 MQ 来传递非常核心的消息,比如说计费、扣费的一些消息,那必须确保这个 MQ 传递过程中**绝对不会把计费消息给弄丢**。
8 |
9 | ## 面试题剖析
10 | 数据的丢失问题,可能出现在生产者、MQ、消费者中,咱们从 RabbitMQ 和 Kafka 分别来分析一下吧。
11 |
12 | ### RabbitMQ
13 | 
14 |
15 | #### 生产者弄丢了数据
16 |
17 | 生产者将数据发送到 RabbitMQ 的时候,可能数据就在半路给搞丢了,因为网络问题啥的,都有可能。
18 |
19 | 此时可以选择用 RabbitMQ 提供的事务功能,就是生产者**发送数据之前**开启 RabbitMQ 事务`channel.txSelect`,然后发送消息,如果消息没有成功被 RabbitMQ 接收到,那么生产者会收到异常报错,此时就可以回滚事务`channel.txRollback`,然后重试发送消息;如果收到了消息,那么可以提交事务`channel.txCommit`。
20 | ```java
21 | // 开启事务
22 | channel.txSelect
23 | try {
24 | // 这里发送消息
25 | } catch (Exception e) {
26 | channel.txRollback
27 |
28 | // 这里再次重发这条消息
29 | }
30 |
31 | // 提交事务
32 | channel.txCommit
33 | ```
34 |
35 | 但是问题是,RabbitMQ 事务机制(同步)一搞,基本上**吞吐量会下来,因为太耗性能**。
36 |
37 | 所以一般来说,如果你要确保说写 RabbitMQ 的消息别丢,可以开启 `confirm` 模式,在生产者那里设置开启 `confirm` 模式之后,你每次写的消息都会分配一个唯一的 id,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 `ack` 消息,告诉你说这个消息 ok 了。如果 RabbitMQ 没能处理这个消息,会回调你的一个 `nack` 接口,告诉你这个消息接收失败,你可以重试。而且你可以结合这个机制自己在内存里维护每个消息 id 的状态,如果超过一定时间还没接收到这个消息的回调,那么你可以重发。
38 |
39 | 事务机制和 `confirm` 机制最大的不同在于,**事务机制是同步的**,你提交一个事务之后会**阻塞**在那儿,但是 `confirm` 机制是**异步**的,你发送个消息之后就可以发送下一个消息,然后那个消息 RabbitMQ 接收了之后会异步回调你的一个接口通知你这个消息接收到了。
40 |
41 | 所以一般在生产者这块**避免数据丢失**,都是用 `confirm` 机制的。
42 |
43 | #### RabbitMQ 弄丢了数据
44 | 就是 RabbitMQ 自己弄丢了数据,这个你必须**开启 RabbitMQ 的持久化**,就是消息写入之后会持久化到磁盘,哪怕是 RabbitMQ 自己挂了,**恢复之后会自动读取之前存储的数据**,一般数据不会丢。除非极其罕见的是,RabbitMQ 还没持久化,自己就挂了,**可能导致少量数据丢失**,但是这个概率较小。
45 |
46 | 设置持久化有**两个步骤**:
47 |
48 | - 创建 queue 的时候将其设置为持久化
49 | 这样就可以保证 RabbitMQ 持久化 queue 的元数据,但是它是不会持久化 queue 里的数据的。
50 | - 第二个是发送消息的时候将消息的 `deliveryMode` 设置为 2
51 | 就是将消息设置为持久化的,此时 RabbitMQ 就会将消息持久化到磁盘上去。
52 |
53 | 必须要同时设置这两个持久化才行,RabbitMQ 哪怕是挂了,再次重启,也会从磁盘上重启恢复 queue,恢复这个 queue 里的数据。
54 |
55 | 注意,哪怕是你给 RabbitMQ 开启了持久化机制,也有一种可能,就是这个消息写到了 RabbitMQ 中,但是还没来得及持久化到磁盘上,结果不巧,此时 RabbitMQ 挂了,就会导致内存里的一点点数据丢失。
56 |
57 | 所以,持久化可以跟生产者那边的 `confirm` 机制配合起来,只有消息被持久化到磁盘之后,才会通知生产者 `ack` 了,所以哪怕是在持久化到磁盘之前,RabbitMQ 挂了,数据丢了,生产者收不到 `ack`,你也是可以自己重发的。
58 |
59 | #### 消费端弄丢了数据
60 | RabbitMQ 如果丢失了数据,主要是因为你消费的时候,**刚消费到,还没处理,结果进程挂了**,比如重启了,那么就尴尬了,RabbitMQ 认为你都消费了,这数据就丢了。
61 |
62 | 这个时候得用 RabbitMQ 提供的 `ack` 机制,简单来说,就是你必须关闭 RabbitMQ 的自动 `ack`,可以通过一个 api 来调用就行,然后每次你自己代码里确保处理完的时候,再在程序里 `ack` 一把。这样的话,如果你还没处理完,不就没有 `ack` 了?那 RabbitMQ 就认为你还没处理完,这个时候 RabbitMQ 会把这个消费分配给别的 consumer 去处理,消息是不会丢的。
63 |
64 | 
65 |
66 | ### Kafka
67 |
68 | #### 消费端弄丢了数据
69 | 唯一可能导致消费者弄丢数据的情况,就是说,你消费到了这个消息,然后消费者那边**自动提交了 offset**,让 Kafka 以为你已经消费好了这个消息,但其实你才刚准备处理这个消息,你还没处理,你自己就挂了,此时这条消息就丢咯。
70 |
71 | 这不是跟 RabbitMQ 差不多吗,大家都知道 Kafka 会自动提交 offset,那么只要**关闭自动提交** offset,在处理完之后自己手动提交 offset,就可以保证数据不会丢。但是此时确实还是**可能会有重复消费**,比如你刚处理完,还没提交 offset,结果自己挂了,此时肯定会重复消费一次,自己保证幂等性就好了。
72 |
73 | 生产环境碰到的一个问题,就是说我们的 Kafka 消费者消费到了数据之后是写到一个内存的 queue 里先缓冲一下,结果有的时候,你刚把消息写入内存 queue,然后消费者会自动提交 offset。然后此时我们重启了系统,就会导致内存 queue 里还没来得及处理的数据就丢失了。
74 |
75 | #### Kafka 弄丢了数据
76 |
77 | 这块比较常见的一个场景,就是 Kafka 某个 broker 宕机,然后重新选举 partition 的 leader。大家想想,要是此时其他的 follower 刚好还有些数据没有同步,结果此时 leader 挂了,然后选举某个 follower 成 leader 之后,不就少了一些数据?这就丢了一些数据啊。
78 |
79 | 生产环境也遇到过,我们也是,之前 Kafka 的 leader 机器宕机了,将 follower 切换为 leader 之后,就会发现说这个数据就丢了。
80 |
81 | 所以此时一般是要求起码设置如下 4 个参数:
82 |
83 | - 给 topic 设置 `replication.factor` 参数:这个值必须大于 1,要求每个 partition 必须有至少 2 个副本。
84 | - 在 Kafka 服务端设置 `min.insync.replicas` 参数:这个值必须大于 1,这个是要求一个 leader 至少感知到有至少一个 follower 还跟自己保持联系,没掉队,这样才能确保 leader 挂了还有一个 follower 吧。
85 | - 在 producer 端设置 `acks=all`:这个是要求每条数据,必须是**写入所有 replica 之后,才能认为是写成功了**。
86 | - 在 producer 端设置 `retries=MAX`(很大很大很大的一个值,无限次重试的意思):这个是**要求一旦写入失败,就无限重试**,卡在这里了。
87 |
88 | 我们生产环境就是按照上述要求配置的,这样配置之后,至少在 Kafka broker 端就可以保证在 leader 所在 broker 发生故障,进行 leader 切换时,数据不会丢失。
89 |
90 | #### 生产者会不会弄丢数据?
91 | 如果按照上述的思路设置了 `acks=all`,一定不会丢,要求是,你的 leader 接收到消息,所有的 follower 都同步到了消息之后,才认为本次写成功了。如果没满足这个条件,生产者会自动不断的重试,重试无限次。
92 |
--------------------------------------------------------------------------------
/docs/high-concurrency/images/async-replication-data-lose-case.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/async-replication-data-lose-case.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/consistent-hashing-algorithm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/consistent-hashing-algorithm.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/database-id-sequence-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/database-id-sequence-step.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/database-shard-method-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/database-shard-method-1.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/database-shard-method-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/database-shard-method-2.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/database-split-horizon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/database-split-horizon.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/database-split-vertically.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/database-split-vertically.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/distributed-system-request-sequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/distributed-system-request-sequence.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/distributed-transaction-TCC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/distributed-transaction-TCC.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/distributed-transaction-XA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/distributed-transaction-XA.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/distributed-transaction-local-message-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/distributed-transaction-local-message-table.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/distributed-transaction-reliable-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/distributed-transaction-reliable-message.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/dubbo-operating-principle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/dubbo-operating-principle.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/dubbo-service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/dubbo-service-invoke-road.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/dubbo-spi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/dubbo-spi.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/e-commerce-website-detail-page-architecture-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/e-commerce-website-detail-page-architecture-1.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/e-commerce-website-detail-page-architecture-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/e-commerce-website-detail-page-architecture-2.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-cluster-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-cluster-0.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-cluster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-cluster.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-index-type-mapping-document-field.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-index-type-mapping-document-field.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-search-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-search-process.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-write-detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-write-detail.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/es-write.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/es-write.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/hash-slot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/hash-slot.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/hash.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/high-concurrency-system-design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/high-concurrency-system-design.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/icon.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/kafka-after.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/kafka-after.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/kafka-before.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/kafka-before.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/kafka-order-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/kafka-order-01.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/kafka-order-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/kafka-order-02.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-1.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-10.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-11.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-2.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-3.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-4.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-5.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-6.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-7.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mq-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mq-8.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/mysql-master-slave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/mysql-master-slave.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/rabbitmq-message-lose-solution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/rabbitmq-message-lose-solution.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/rabbitmq-message-lose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/rabbitmq-message-lose.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/rabbitmq-order-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/rabbitmq-order-01.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/rabbitmq-order-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/rabbitmq-order-02.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-caching-avalanche-solution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-caching-avalanche-solution.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-caching-avalanche.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-caching-avalanche.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-caching-penetration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-caching-penetration.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-cluster-split-brain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-cluster-split-brain.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-gossip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-gossip.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-junior-inconsistent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-junior-inconsistent.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-master-slave-replication-detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-master-slave-replication-detail.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-master-slave-replication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-master-slave-replication.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-master-slave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-master-slave.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-redlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-redlock.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/redis-single-thread-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/redis-single-thread-model.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/service-invoke-road.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/simple-distributed-system-oa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/simple-distributed-system-oa.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-active-standby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-active-standby.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-centralized-storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-centralized-storage.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-distributed-coordination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-distributed-coordination.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-distributed-lock-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-distributed-lock-demo.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-distributed-lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-distributed-lock.png
--------------------------------------------------------------------------------
/docs/high-concurrency/images/zookeeper-meta-data-manage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/high-concurrency/images/zookeeper-meta-data-manage.png
--------------------------------------------------------------------------------
/docs/high-concurrency/mq-design.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如果让你写一个消息队列,该如何进行架构设计?说一下你的思路。
3 |
4 | ## 面试官心理分析
5 | 其实聊到这个问题,一般面试官要考察两块:
6 |
7 | - 你有没有对某一个消息队列做过较为深入的原理的了解,或者从整体了解把握住一个消息队列的架构原理。
8 | - 看看你的设计能力,给你一个常见的系统,就是消息队列系统,看看你能不能从全局把握一下整体架构设计,给出一些关键点出来。
9 |
10 | 说实话,问类似问题的时候,大部分人基本都会蒙,因为平时从来没有思考过类似的问题,大多数人就是平时埋头用,从来不去思考背后的一些东西。类似的问题,比如,如果让你来设计一个 Spring 框架你会怎么做?如果让你来设计一个 Dubbo 框架你会怎么做?如果让你来设计一个 MyBatis 框架你会怎么做?
11 |
12 |
13 | ## 面试题剖析
14 | 其实回答这类问题,说白了,不求你看过那技术的源码,起码你要大概知道那个技术的基本原理、核心组成部分、基本架构构成,然后参照一些开源的技术把一个系统设计出来的思路说一下就好。
15 |
16 | 比如说这个消息队列系统,我们从以下几个角度来考虑一下:
17 |
18 | - 首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
19 |
20 | - 其次你得考虑一下这个 mq 的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。
21 |
22 | - 其次你考虑一下你的 mq 的可用性啊?这个事儿,具体参考之前可用性那个环节讲解的 kafka 的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。
23 |
24 | - 能不能支持数据 0 丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案。
25 |
26 | mq 肯定是很复杂的,面试官问你这个问题,其实是个开放题,他就是看看你有没有从架构角度整体构思和设计的思维以及能力。确实这个问题可以刷掉一大批人,因为大部分人平时不思考这些东西。
--------------------------------------------------------------------------------
/docs/high-concurrency/mq-interview.md:
--------------------------------------------------------------------------------
1 | ## 消息队列面试场景
2 |
3 | **面试官**:你好。
4 |
5 | **候选人**:你好。
6 |
7 | (面试官在你的简历上面看到了,呦,有个亮点,你在项目里用过 `MQ`,比如说你用过 `ActiveMQ`)
8 |
9 | **面试官**:你在系统里用过消息队列吗?(面试官在随和的语气中展开了面试)
10 |
11 | **候选人**:用过的(此时感觉没啥)
12 |
13 | **面试官**:那你说一下你们在项目里是怎么用消息队列的?
14 |
15 | **候选人**:巴拉巴拉,“我们啥啥系统发送个啥啥消息到队列,别的系统来消费啥啥的。比如我们有个订单系统,订单系统每次下一个新的订单的时候,就会发送一条消息到 `ActiveMQ` 里面去,后台有个库存系统负责获取消息然后更新库存。”
16 |
17 | (部分同学在这里会进入一个误区,就是你仅仅就是知道以及回答你们是怎么用这个消息队列的,用这个消息队列来干了个什么事情?)
18 |
19 | **面试官**:那你们为什么使用消息队列啊?你的订单系统不发送消息到 `MQ`,直接订单系统调用库存系统一个接口,咔嚓一下,直接就调用成功,库存不也就更新了。
20 |
21 | **候选人**:额。。。(楞了一下,为什么?我没怎么仔细想过啊,老大让用就用了),硬着头皮胡言乱语了几句。
22 |
23 | (面试官此时听你楞了一下,然后听你胡言乱语了几句,开始心里觉得有点儿那什么了,怀疑你之前就压根儿没思考过这问题)
24 |
25 | **面试官**:那你说说用消息队列都有什么优点和缺点?
26 |
27 | (面试官此时心里想的是,你的 `MQ` 在项目里为啥要用,你没怎么考虑过,那我稍微简单点儿,我问问你消息队列你之前有没有考虑过如果用的话,优点和缺点分别是啥?)
28 |
29 | **候选人**:这个。。。(确实平时没怎么考虑过这个问题啊。。。胡言乱语了)
30 |
31 | (面试官此时心里已经更觉得你这哥儿们不行,平时都没什么思考)
32 |
33 | **面试官**:`Kafka`、`ActiveMQ`、`RabbitMQ`、`RocketMQ` 都有什么区别?
34 |
35 | (面试官问你这个问题,就是说,绕过比较虚的话题,直接看看你对各种 `MQ` 中间件是否了解,是否做过功课,是否做过调研)
36 |
37 | **候选人**:我们就用过 `ActiveMQ`,所以别的没用过。。。区别,也不太清楚。。。
38 |
39 | (面试官此时更是觉得你这哥儿们平时就是瞎用,根本就没什么思考,觉得不行)
40 |
41 | **面试官**:那你们是如何保证消息队列的高可用啊?
42 |
43 | **候选人**:这个。。。我平时就是简单走 API 调用一下,不太清楚消息队列怎么部署的。。。
44 |
45 | **面试官**:如何保证消息不被重复消费啊?如何保证消费的时候是幂等的啊?
46 |
47 | **候选人**:啥?(`MQ` 不就是写入&消费就可以了,哪来这么多问题)
48 |
49 | **面试官**:如何保证消息的可靠性传输啊?要是消息丢失了怎么办啊?
50 |
51 | **候选人**:我们没怎么丢过消息啊。。。
52 |
53 | **面试官**:那如何保证消息的顺序性?
54 |
55 | **候选人**:顺序性?什么意思?我为什么要保证消息的顺序性?它不是本来就有顺序吗?
56 |
57 | **面试官**:如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
58 |
59 | **候选人**:不是,我这平时没遇到过这些问题啊,就是简单用用,知道 `MQ` 的一些功能。
60 |
61 | **面试官**:如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。
62 |
63 | **候选人**:。。。。。我还是走吧。。。。
64 |
65 | ---
66 |
67 | 这其实是面试官的一种面试风格,就是说面试官的问题不是发散的,而是从一个小点慢慢铺开。比如说面试官可能会跟你聊聊高并发话题,就这个话题里面跟你聊聊缓存、`MQ` 等等东西,**由浅入深,一步步深挖**。
68 |
69 | 其实上面是一个非常典型的关于消息队列的技术考察过程,好的面试官一定是从你做过的某一个点切入,然后层层展开深入考察,一个接一个问,直到把这个技术点刨根问底,问到最底层。
--------------------------------------------------------------------------------
/docs/high-concurrency/mq-time-delay-and-expired-failure.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
3 |
4 | ## 面试官心理分析
5 | 你看这问法,其实本质针对的场景,都是说,可能你的消费端出了问题,不消费了;或者消费的速度极其慢。接着就坑爹了,可能你的消息队列集群的磁盘都快写满了,都没人消费,这个时候怎么办?或者是这整个就积压了几个小时,你这个时候怎么办?或者是你积压的时间太长了,导致比如 RabbitMQ 设置了消息过期时间后就没了怎么办?
6 |
7 | 所以就这事儿,其实线上挺常见的,一般不出,一出就是大 case。一般常见于,举个例子,消费端每次消费之后要写 mysql,结果 mysql 挂了,消费端 hang 那儿了,不动了;或者是消费端出了个什么岔子,导致消费速度极其慢。
8 |
9 | ## 面试题剖析
10 | 关于这个事儿,我们一个一个来梳理吧,先假设一个场景,我们现在消费端出故障了,然后大量消息在 mq 里积压,现在出事故了,慌了。
11 |
12 | ### 大量消息在 mq 里积压了几个小时了还没解决
13 | 几千万条数据在 MQ 里积压了七八个小时,从下午 4 点多,积压到了晚上 11 点多。这个是我们真实遇到过的一个场景,确实是线上故障了,这个时候要不然就是修复 consumer 的问题,让它恢复消费速度,然后傻傻的等待几个小时消费完毕。这个肯定不能在面试的时候说吧。
14 |
15 | 一个消费者一秒是 1000 条,一秒 3 个消费者是 3000 条,一分钟就是 18 万条。所以如果你积压了几百万到上千万的数据,即使消费者恢复了,也需要大概 1 小时的时间才能恢复过来。
16 |
17 | 一般这个时候,只能临时紧急扩容了,具体操作步骤和思路如下:
18 | - 先修复 consumer 的问题,确保其恢复消费速度,然后将现有 consumer 都停掉。
19 | - 新建一个 topic,partition 是原来的 10 倍,临时建立好原先 10 倍的 queue 数量。
20 | - 然后写一个临时的分发数据的 consumer 程序,这个程序部署上去消费积压的数据,**消费之后不做耗时的处理**,直接均匀轮询写入临时建立好的 10 倍数量的 queue。
21 | - 接着临时征用 10 倍的机器来部署 consumer,每一批 consumer 消费一个临时 queue 的数据。这种做法相当于是临时将 queue 资源和 consumer 资源扩大 10 倍,以正常的 10 倍速度来消费数据。
22 | - 等快速消费完积压数据之后,**得恢复原先部署的架构**,**重新**用原先的 consumer 机器来消费消息。
23 |
24 | ### mq 中的消息过期失效了
25 | 假设你用的是 RabbitMQ,RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是**大量的数据会直接搞丢**。
26 |
27 | 这个情况下,就不是说要增加 consumer 消费积压的消息,因为实际上没啥积压,而是丢了大量的消息。我们可以采取一个方案,就是**批量重导**,这个我们之前线上也有类似的场景干过。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,比如大家一起喝咖啡熬夜到晚上12点以后,用户都睡觉了。这个时候我们就开始写程序,将丢失的那批数据,写个临时程序,一点一点的查出来,然后重新灌入 mq 里面去,把白天丢的数据给他补回来。也只能是这样了。
28 |
29 | 假设 1 万个订单积压在 mq 里面,没有处理,其中 1000 个订单都丢了,你只能手动写程序把那 1000 个订单给查出来,手动发到 mq 里去再补一次。
30 |
31 | ### mq 都快写满了
32 | 如果消息积压在 mq 里,你很长时间都没有处理掉,此时导致 mq 都快写满了,咋办?这个还有别的办法吗?没有,谁让你第一个方案执行的太慢了,你临时写程序,接入数据来消费,**消费一个丢弃一个,都不要了**,快速消费掉所有的消息。然后走第二个方案,到了晚上再补数据吧。
33 |
--------------------------------------------------------------------------------
/docs/high-concurrency/mysql-read-write-separation.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 你们有没有做 MySQL 读写分离?如何实现 MySQL 的读写分离?MySQL 主从复制原理的是啥?如何解决 MySQL 主从同步的延时问题?
3 |
4 | ## 面试官心理分析
5 | 高并发这个阶段,肯定是需要做读写分离的,啥意思?因为实际上大部分的互联网公司,一些网站,或者是 app,其实都是读多写少。所以针对这个情况,就是写一个主库,但是主库挂多个从库,然后从多个从库来读,那不就可以支撑更高的读并发压力了吗?
6 |
7 | ## 面试题剖析
8 | ### 如何实现 MySQL 的读写分离?
9 | 其实很简单,就是基于主从复制架构,简单来说,就搞一个主库,挂多个从库,然后我们就单单只是写主库,然后主库会自动把数据给同步到从库上去。
10 |
11 | ### MySQL 主从复制原理的是啥?
12 | 主库将变更写入 binlog 日志,然后从库连接到主库之后,从库有一个 IO 线程,将主库的 binlog 日志拷贝到自己本地,写入一个 relay 中继日志中。接着从库中有一个 SQL 线程会从中继日志读取 binlog,然后执行 binlog 日志中的内容,也就是在自己本地再次执行一遍 SQL,这样就可以保证自己跟主库的数据是一样的。
13 |
14 | 
15 |
16 | 这里有一个非常重要的一点,就是从库同步主库数据的过程是串行化的,也就是说主库上并行的操作,在从库上会串行执行。所以这就是一个非常重要的点了,由于从库从主库拷贝日志以及串行执行 SQL 的特点,在高并发场景下,从库的数据一定会比主库慢一些,是**有延时**的。所以经常出现,刚写入主库的数据可能是读不到的,要过几十毫秒,甚至几百毫秒才能读取到。
17 |
18 | 而且这里还有另外一个问题,就是如果主库突然宕机,然后恰好数据还没同步到从库,那么有些数据可能在从库上是没有的,有些数据可能就丢失了。
19 |
20 | 所以 MySQL 实际上在这一块有两个机制,一个是**半同步复制**,用来解决主库数据丢失问题;一个是**并行复制**,用来解决主从同步延时问题。
21 |
22 | 这个所谓**半同步复制**,也叫 `semi-sync` 复制,指的就是主库写入 binlog 日志之后,就会将**强制**此时立即将数据同步到从库,从库将日志写入自己本地的 relay log 之后,接着会返回一个 ack 给主库,主库接收到**至少一个从库**的 ack 之后才会认为写操作完成了。
23 |
24 | 所谓**并行复制**,指的是从库开启多个线程,并行读取 relay log 中不同库的日志,然后**并行重放不同库的日志**,这是库级别的并行。
25 |
26 | ### MySQL 主从同步延时问题(精华)
27 | 以前线上确实处理过因为主从同步延时问题而导致的线上的 bug,属于小型的生产事故。
28 |
29 | 是这个么场景。有个同学是这样写代码逻辑的。先插入一条数据,再把它查出来,然后更新这条数据。在生产环境高峰期,写并发达到了 2000/s,这个时候,主从复制延时大概是在小几十毫秒。线上会发现,每天总有那么一些数据,我们期望更新一些重要的数据状态,但在高峰期时候却没更新。用户跟客服反馈,而客服就会反馈给我们。
30 |
31 | 我们通过 MySQL 命令:
32 | ```sql
33 | show status
34 | ```
35 | 查看 `Seconds_Behind_Master`,可以看到从库复制主库的数据落后了几 ms。
36 |
37 | 一般来说,如果主从延迟较为严重,有以下解决方案:
38 | - 分库,将一个主库拆分为多个主库,每个主库的写并发就减少了几倍,此时主从延迟可以忽略不计。
39 | - 打开 MySQL 支持的并行复制,多个库并行复制。如果说某个库的写入并发就是特别高,单库写并发达到了 2000/s,并行复制还是没意义。
40 | - 重写代码,写代码的同学,要慎重,插入数据时立马查询可能查不到。
41 | - 如果确实是存在必须先插入,立马要求就查询到,然后立马就要反过来执行一些操作,对这个查询**设置直连主库**。**不推荐**这种方法,你要是这么搞,读写分离的意义就丧失了。
42 |
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 了解什么是 redis 的雪崩、穿透和击穿?redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透?
3 |
4 | ## 面试官心理分析
5 | 其实这是问到缓存必问的,因为缓存雪崩和穿透,是缓存最大的两个问题,要么不出现,一旦出现就是致命性的问题,所以面试官一定会问你。
6 |
7 | ## 面试题剖析
8 | ### 缓存雪崩
9 | 对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了。此时,如果没有采用什么特别的方案来处理这个故障,DBA 很着急,重启数据库,但是数据库立马又被新的流量给打死了。
10 |
11 | 这就是缓存雪崩。
12 |
13 | 
14 |
15 | 大约在 3 年前,国内比较知名的一个互联网公司,曾因为缓存事故,导致雪崩,后台系统全部崩溃,事故从当天下午持续到晚上凌晨 3~4 点,公司损失了几千万。
16 |
17 | 缓存雪崩的事前事中事后的解决方案如下。
18 | - 事前:redis 高可用,主从+哨兵,redis cluster,避免全盘崩溃。
19 | - 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
20 | - 事后:redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
21 |
22 | 
23 |
24 | 用户发送一个请求,系统 A 收到请求后,先查本地 ehcache 缓存,如果没查到再查 redis。如果 ehcache 和 redis 都没有,再查数据库,将数据库中的结果,写入 ehcache 和 redis 中。
25 |
26 | 限流组件,可以设置每秒的请求,有多少能通过组件,剩余的未通过的请求,怎么办?**走降级**!可以返回一些默认的值,或者友情提示,或者空白的值。
27 |
28 | 好处:
29 | - 数据库绝对不会死,限流组件确保了每秒只有多少个请求能通过。
30 | - 只要数据库不死,就是说,对用户来说,2/5 的请求都是可以被处理的。
31 | - 只要有 2/5 的请求可以被处理,就意味着你的系统没死,对用户来说,可能就是点击几次刷不出来页面,但是多点几次,就可以刷出来一次。
32 |
33 | ### 缓存穿透
34 | 对于系统A,假设一秒 5000 个请求,结果其中 4000 个请求是黑客发出的恶意攻击。
35 |
36 | 黑客发出的那 4000 个攻击,缓存中查不到,每次你去数据库里查,也查不到。
37 |
38 | 举个栗子。数据库 id 是从 1 开始的,结果黑客发过来的请求 id 全部都是负数。这样的话,缓存中不会有,请求每次都“视缓存于无物”,直接查询数据库。这种恶意攻击场景的缓存穿透就会直接把数据库给打死。
39 |
40 | 
41 |
42 | 解决方式很简单,每次系统 A 从数据库中只要没查到,就写一个空值到缓存里去,比如 `set -999 UNKNOWN`。然后设置一个过期时间,这样的话,下次有相同的 key 来访问的时候,在缓存失效之前,都可以直接从缓存中取数据。
43 |
44 | ### 缓存击穿
45 | 缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
46 |
47 | 解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-cas.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | redis 的并发竞争问题是什么?如何解决这个问题?了解 redis 事务的 CAS 方案吗?
3 |
4 | ## 面试官心理分析
5 | 这个也是线上非常常见的一个问题,就是**多客户端同时并发写**一个 key,可能本来应该先到的数据后到了,导致数据版本错了;或者是多客户端同时获取一个 key,修改值之后再写回去,只要顺序错了,数据就错了。
6 |
7 | 而且 redis 自己就有天然解决这个问题的 CAS 类的乐观锁方案。
8 |
9 | ## 面试题剖析
10 | 某个时刻,多个系统实例都去更新某个 key。可以基于 zookeeper 实现分布式锁。每个系统通过 zookeeper 获取分布式锁,确保同一时间,只能有一个系统实例在操作某个 key,别人都不允许读和写。
11 |
12 | 
13 |
14 | 你要写入缓存的数据,都是从 mysql 里查出来的,都得写入 mysql 中,写入 mysql 中的时候必须保存一个时间戳,从 mysql 查出来的时候,时间戳也查出来。
15 |
16 | 每次要**写之前,先判断**一下当前这个 value 的时间戳是否比缓存里的 value 的时间戳要新。如果是的话,那么可以写,否则,就不能用旧的数据覆盖新的数据。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-consistence.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 如何保证缓存与数据库的双写一致性?
3 |
4 | ## 面试官心理分析
5 | 你只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题,那么你如何解决一致性问题?
6 |
7 | ## 面试题剖析
8 | 一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统**不是严格要求** “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:**读请求和写请求串行化**,串到一个**内存队列**里去。
9 |
10 | 串行化可以保证一定不会出现不一致的情况,但是它也会导致系统的吞吐量大幅度降低,用比正常情况下多几倍的机器去支撑线上的一个请求。
11 |
12 | ### Cache Aside Pattern
13 | 最经典的缓存+数据库读写的模式,就是 Cache Aside Pattern。
14 | - 读的时候,先读缓存,缓存没有的话,就读数据库,然后取出数据后放入缓存,同时返回响应。
15 | - 更新的时候,**先更新数据库,然后再删除缓存**。
16 |
17 | **为什么是删除缓存,而不是更新缓存?**
18 |
19 | 原因很简单,很多时候,在复杂点的缓存场景,缓存不单单是数据库中直接取出来的值。
20 |
21 | 比如可能更新了某个表的一个字段,然后其对应的缓存,是需要查询另外两个表的数据并进行运算,才能计算出缓存最新的值的。
22 |
23 | 另外更新缓存的代价有时候是很高的。是不是说,每次修改数据库的时候,都一定要将其对应的缓存更新一份?也许有的场景是这样,但是对于**比较复杂的缓存数据计算的场景**,就不是这样了。如果你频繁修改一个缓存涉及的多个表,缓存也频繁更新。但是问题在于,**这个缓存到底会不会被频繁访问到?**
24 |
25 | 举个栗子,一个缓存涉及的表的字段,在 1 分钟内就修改了 20 次,或者是 100 次,那么缓存更新 20 次、100 次;但是这个缓存在 1 分钟内只被读取了 1 次,有**大量的冷数据**。实际上,如果你只是删除缓存的话,那么在 1 分钟内,这个缓存不过就重新计算一次而已,开销大幅度降低。**用到缓存才去算缓存。**
26 |
27 | 其实删除缓存,而不是更新缓存,就是一个 lazy 计算的思想,不要每次都重新做复杂的计算,不管它会不会用到,而是让它到需要被使用的时候再重新计算。像 mybatis,hibernate,都有懒加载思想。查询一个部门,部门带了一个员工的 list,没有必要说每次查询部门,都里面的 1000 个员工的数据也同时查出来啊。80% 的情况,查这个部门,就只是要访问这个部门的信息就可以了。先查部门,同时要访问里面的员工,那么这个时候只有在你要访问里面的员工的时候,才会去数据库里面查询 1000 个员工。
28 |
29 | ### 最初级的缓存不一致问题及解决方案
30 | 问题:先更新数据库,再删除缓存。如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。
31 |
32 | 
33 |
34 | 解决思路:先删除缓存,再更新数据库。如果数据库更新失败了,那么数据库中是旧数据,缓存中是空的,那么数据不会不一致。因为读的时候缓存没有,所以去读了数据库中的旧数据,然后更新到缓存中。
35 |
36 | ### 比较复杂的数据不一致问题分析
37 | 数据发生了变更,先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,**查到了修改前的旧数据**,放到了缓存中。随后数据变更的程序完成了数据库的修改。完了,数据库和缓存中的数据不一样了...
38 |
39 | **为什么上亿流量高并发场景下,缓存会出现这个问题?**
40 |
41 | 只有在对一个数据在并发的进行读写的时候,才可能会出现这种问题。其实如果说你的并发量很低的话,特别是读并发很低,每天访问量就 1 万次,那么很少的情况下,会出现刚才描述的那种不一致的场景。但是问题是,如果每天的是上亿的流量,每秒并发读是几万,每秒只要有数据更新的请求,就**可能会出现上述的数据库+缓存不一致的情况**。
42 |
43 | **解决方案如下:**
44 |
45 | 更新数据的时候,根据**数据的唯一标识**,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。
46 |
47 | 一个队列对应一个工作线程,每个工作线程**串行**拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,没有读到缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。
48 |
49 | 这里有一个**优化点**,一个队列中,其实**多个更新缓存请求串在一起是没意义的**,因此可以做过滤,如果发现队列中已经有一个更新缓存的请求了,那么就不用再放个更新请求操作进去了,直接等待前面的更新操作请求完成即可。
50 |
51 | 待那个队列对应的工作线程完成了上一个操作的数据库的修改之后,才会去执行下一个操作,也就是缓存更新的操作,此时会从数据库中读取最新的值,然后写入缓存中。
52 |
53 | 如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。
54 |
55 | 高并发的场景下,该解决方案要注意的问题:
56 |
57 | - 读请求长时阻塞
58 |
59 | 由于读请求进行了非常轻度的异步化,所以一定要注意读超时的问题,每个读请求必须在超时时间范围内返回。
60 |
61 | 该解决方案,最大的风险点在于说,**可能数据更新很频繁**,导致队列中积压了大量更新操作在里面,然后**读请求会发生大量的超时**,最后导致大量的请求直接走数据库。务必通过一些模拟真实的测试,看看更新数据的频率是怎样的。
62 |
63 | 另外一点,因为一个队列中,可能会积压针对多个数据项的更新操作,因此需要根据自己的业务情况进行测试,可能需要**部署多个服务**,每个服务分摊一些数据的更新操作。如果一个内存队列里居然会挤压 100 个商品的库存修改操作,每隔库存修改操作要耗费 10ms 去完成,那么最后一个商品的读请求,可能等待 10 * 100 = 1000ms = 1s 后,才能得到数据,这个时候就导致**读请求的长时阻塞**。
64 |
65 | 一定要做根据实际业务系统的运行情况,去进行一些压力测试,和模拟线上环境,去看看最繁忙的时候,内存队列可能会挤压多少更新操作,可能会导致最后一个更新操作对应的读请求,会 hang 多少时间,如果读请求在 200ms 返回,如果你计算过后,哪怕是最繁忙的时候,积压 10 个更新操作,最多等待 200ms,那还可以的。
66 |
67 | **如果一个内存队列中可能积压的更新操作特别多**,那么你就要**加机器**,让每个机器上部署的服务实例处理更少的数据,那么每个内存队列中积压的更新操作就会越少。
68 |
69 | 其实根据之前的项目经验,一般来说,数据的写频率是很低的,因此实际上正常来说,在队列中积压的更新操作应该是很少的。像这种针对读高并发、读缓存架构的项目,一般来说写请求是非常少的,每秒的 QPS 能到几百就不错了。
70 |
71 | 我们来**实际粗略测算一下**。
72 |
73 | 如果一秒有 500 的写操作,如果分成 5 个时间片,每 200ms 就 100 个写操作,放到 20 个内存队列中,每个内存队列,可能就积压 5 个写操作。每个写操作性能测试后,一般是在 20ms 左右就完成,那么针对每个内存队列的数据的读请求,也就最多 hang 一会儿,200ms 以内肯定能返回了。
74 |
75 | 经过刚才简单的测算,我们知道,单机支撑的写 QPS 在几百是没问题的,如果写 QPS 扩大了 10 倍,那么就扩容机器,扩容 10 倍的机器,每个机器 20 个队列。
76 |
77 | - 读请求并发量过高
78 |
79 | 这里还必须做好压力测试,确保恰巧碰上上述情况的时候,还有一个风险,就是突然间大量读请求会在几十毫秒的延时 hang 在服务上,看服务能不能扛的住,需要多少机器才能扛住最大的极限情况的峰值。
80 |
81 | 但是因为并不是所有的数据都在同一时间更新,缓存也不会同一时间失效,所以每次可能也就是少数数据的缓存失效了,然后那些数据对应的读请求过来,并发量应该也不会特别大。
82 |
83 | - 多服务实例部署的请求路由
84 |
85 | 可能这个服务部署了多个实例,那么必须**保证**说,执行数据更新操作,以及执行缓存更新操作的请求,都通过 Nginx 服务器**路由到相同的服务实例上**。
86 |
87 | 比如说,对同一个商品的读写请求,全部路由到同一台机器上。可以自己去做服务间的按照某个请求参数的 hash 路由,也可以用 Nginx 的 hash 路由功能等等。
88 |
89 | - 热点商品的路由问题,导致请求的倾斜
90 |
91 | 万一某个商品的读写请求特别高,全部打到相同的机器的相同的队列里面去了,可能会造成某台机器的压力过大。就是说,因为只有在商品数据更新的时候才会清空缓存,然后才会导致读写并发,所以其实要根据业务系统去看,如果更新频率不是太高的话,这个问题的影响并不是特别大,但是的确可能某些机器的负载会高一些。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-data-types.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | redis 都有哪些数据类型?分别在哪些场景下使用比较合适?
3 |
4 | ## 面试官心理分析
5 | 除非是面试官感觉看你简历,是工作 3 年以内的比较初级的同学,可能对技术没有很深入的研究,面试官才会问这类问题。否则,在宝贵的面试时间里,面试官实在不想多问。
6 |
7 | 其实问这个问题,主要有两个原因:
8 | - 看看你到底有没有全面的了解 redis 有哪些功能,一般怎么来用,啥场景用什么,就怕你别就会最简单的 KV 操作;
9 | - 看看你在实际项目里都怎么玩儿过 redis。
10 |
11 | 要是你回答的不好,没说出几种数据类型,也没说什么场景,你完了,面试官对你印象肯定不好,觉得你平时就是做个简单的 set 和 get。
12 |
13 | ## 面试题剖析
14 | redis 主要有以下几种数据类型:
15 | - string
16 | - hash
17 | - list
18 | - set
19 | - sorted set
20 |
21 | ### string
22 | 这是最简单的类型,就是普通的 set 和 get,做简单的 KV 缓存。
23 | ```bash
24 | set college szu
25 | ```
26 |
27 | ### hash
28 | 这个是类似 map 的一种结构,这个一般就是可以将结构化的数据,比如一个对象(前提是**这个对象没嵌套其他的对象**)给缓存在 redis 里,然后每次读写缓存的时候,可以就操作 hash 里的**某个字段**。
29 |
30 | ```bash
31 | hset person name bingo
32 | hset person age 20
33 | hset person id 1
34 | hget person name
35 | ```
36 |
37 | ```json
38 | person = {
39 | "name": "bingo",
40 | "age": 20,
41 | "id": 1
42 | }
43 | ```
44 |
45 | ### list
46 | list 是有序列表,这个可以玩儿出很多花样。
47 |
48 | 比如可以通过 list 存储一些列表型的数据结构,类似粉丝列表、文章的评论列表之类的东西。
49 |
50 | 比如可以通过 lrange 命令,读取某个闭区间内的元素,可以基于 list 实现分页查询,这个是很棒的一个功能,基于 redis 实现简单的高性能分页,可以做类似微博那种下拉不断分页的东西,性能高,就一页一页走。
51 | ```bash
52 | # 0开始位置,-1结束位置,结束位置为-1时,表示列表的最后一个位置,即查看所有。
53 | lrange mylist 0 -1
54 | ```
55 |
56 | 比如可以搞个简单的消息队列,从 list 头怼进去,从 list 尾巴那里弄出来。
57 | ```bash
58 | lpush mylist 1
59 | lpush mylist 2
60 | lpush mylist 3 4 5
61 |
62 | # 1
63 | rpop mylist
64 | ```
65 |
66 | ### set
67 | set 是无序集合,自动去重。
68 |
69 | 直接基于 set 将系统里需要去重的数据扔进去,自动就给去重了,如果你需要对一些数据进行快速的全局去重,你当然也可以基于 jvm 内存里的 HashSet 进行去重,但是如果你的某个系统部署在多台机器上呢?得基于 redis 进行全局的 set 去重。
70 |
71 | 可以基于 set 玩儿交集、并集、差集的操作,比如交集吧,可以把两个人的粉丝列表整一个交集,看看俩人的共同好友是谁?对吧。
72 |
73 | 把两个大 V 的粉丝都放在两个 set 中,对两个 set 做交集。
74 | ```bash
75 | #-------操作一个set-------
76 | # 添加元素
77 | sadd mySet 1
78 |
79 | # 查看全部元素
80 | smembers mySet
81 |
82 | # 判断是否包含某个值
83 | sismember mySet 3
84 |
85 | # 删除某个/些元素
86 | srem mySet 1
87 | srem mySet 2 4
88 |
89 | # 查看元素个数
90 | scard mySet
91 |
92 | # 随机删除一个元素
93 | spop mySet
94 |
95 | #-------操作多个set-------
96 | # 将一个set的元素移动到另外一个set
97 | smove yourSet mySet 2
98 |
99 | # 求两set的交集
100 | sinter yourSet mySet
101 |
102 | # 求两set的并集
103 | sunion yourSet mySet
104 |
105 | # 求在yourSet中而不在mySet中的元素
106 | sdiff yourSet mySet
107 | ```
108 |
109 | ### sorted set
110 | sorted set 是排序的 set,去重但可以排序,写进去的时候给一个分数,自动根据分数排序。
111 | ```bash
112 | zadd board 85 zhangsan
113 | zadd board 72 lisi
114 | zadd board 96 wangwu
115 | zadd board 63 zhaoliu
116 |
117 | # 获取排名前三的用户(默认是升序,所以需要 rev 改为降序)
118 | zrevrange board 0 3
119 |
120 | # 获取某用户的排名
121 | zrank board zhaoliu
122 | ```
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-expiration-policies-and-lru.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?
3 |
4 | ## 面试官心理分析
5 | 如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当然的认为写进 redis 的数据就一定会存在,后面导致系统各种 bug,谁来负责?
6 |
7 | 常见的有两个问题:
8 | - 往 redis 写入的数据怎么没了?
9 |
10 | 可能有同学会遇到,在生产环境的 redis 经常会丢掉一些数据,写进去了,过一会儿可能就没了。我的天,同学,你问这个问题就说明 redis 你就没用对啊。redis 是缓存,你给当存储了是吧?
11 |
12 | 啥叫缓存?用内存当缓存。内存是无限的吗,内存是很宝贵而且是有限的,磁盘是廉价而且是大量的。可能一台机器就几十个 G 的内存,但是可以有几个 T 的硬盘空间。redis 主要是基于内存来进行高性能、高并发的读写操作的。
13 |
14 | 那既然内存是有限的,比如 redis 就只能用 10G,你要是往里面写了 20G 的数据,会咋办?当然会干掉 10G 的数据,然后就保留 10G 的数据了。那干掉哪些数据?保留哪些数据?当然是干掉不常用的数据,保留常用的数据了。
15 |
16 | - 数据明明过期了,怎么还占用着内存?
17 |
18 | 这是由 redis 的过期策略来决定。
19 |
20 | ## 面试题剖析
21 | ### redis 过期策略
22 | redis 过期策略是:**定期删除+惰性删除**。
23 |
24 | 所谓**定期删除**,指的是 redis 默认是每隔 100ms 就随机抽取一些设置了过期时间的 key,检查其是否过期,如果过期就删除。
25 |
26 | 假设 redis 里放了 10w 个 key,都设置了过期时间,你每隔几百毫秒,就检查 10w 个 key,那 redis 基本上就死了,cpu 负载会很高的,消耗在你的检查过期 key 上了。注意,这里可不是每隔 100ms 就遍历所有的设置过期时间的 key,那样就是一场性能上的**灾难**。实际上 redis 是每隔 100ms **随机抽取**一些 key 来检查和删除的。
27 |
28 | 但是问题是,定期删除可能会导致很多过期 key 到了时间并没有被删除掉,那咋整呢?所以就是惰性删除了。这就是说,在你获取某个 key 的时候,redis 会检查一下 ,这个 key 如果设置了过期时间那么是否过期了?如果过期了此时就会删除,不会给你返回任何东西。
29 |
30 | > 获取 key 的时候,如果此时 key 已经过期,就删除,不会返回任何东西。
31 |
32 | 但是实际上这还是有问题的,如果定期删除漏掉了很多过期 key,然后你也没及时去查,也就没走惰性删除,此时会怎么样?如果大量过期 key 堆积在内存里,导致 redis 内存块耗尽了,咋整?
33 |
34 | 答案是:**走内存淘汰机制**。
35 |
36 | ### 内存淘汰机制
37 | redis 内存淘汰机制有以下几个:
38 | - noeviction: 当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了。
39 | - **allkeys-lru**:当内存不足以容纳新写入数据时,在**键空间**中,移除最近最少使用的 key(这个是**最常用**的)。
40 | - allkeys-random:当内存不足以容纳新写入数据时,在**键空间**中,随机移除某个 key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的 key 给干掉啊。
41 | - volatile-lru:当内存不足以容纳新写入数据时,在**设置了过期时间的键空间**中,移除最近最少使用的 key(这个一般不太合适)。
42 | - volatile-random:当内存不足以容纳新写入数据时,在**设置了过期时间的键空间**中,**随机移除**某个 key。
43 | - volatile-ttl:当内存不足以容纳新写入数据时,在**设置了过期时间的键空间**中,有**更早过期时间**的 key 优先移除。
44 |
45 | ### 手写一个 LRU 算法
46 | 你可以现场手写最原始的 LRU 算法,那个代码量太大了,似乎不太现实。
47 |
48 | 不求自己纯手工从底层开始打造出自己的 LRU,但是起码要知道如何利用已有的 JDK 数据结构实现一个 Java 版的 LRU。
49 |
50 | ```java
51 | class LRUCache extends LinkedHashMap {
52 | private final int CACHE_SIZE;
53 |
54 | /**
55 | * 传递进来最多能缓存多少数据
56 | *
57 | * @param cacheSize 缓存大小
58 | */
59 | public LRUCache(int cacheSize) {
60 | // true 表示让 linkedHashMap 按照访问顺序来进行排序,最近访问的放在头部,最老访问的放在尾部。
61 | super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
62 | CACHE_SIZE = cacheSize;
63 | }
64 |
65 | @Override
66 | protected boolean removeEldestEntry(Map.Entry eldest) {
67 | // 当 map中的数据量大于指定的缓存个数的时候,就自动删除最老的数据。
68 | return size() > CACHE_SIZE;
69 | }
70 | }
71 | ```
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-master-slave.md:
--------------------------------------------------------------------------------
1 | # Redis 主从架构
2 |
3 | 单机的 redis,能够承载的 QPS 大概就在上万到几万不等。对于缓存来说,一般都是用来支撑**读高并发**的。因此架构做成主从(master-slave)架构,一主多从,主负责写,并且将数据复制到其它的 slave 节点,从节点负责读。所有的**读请求全部走从节点**。这样也可以很轻松实现水平扩容,**支撑读高并发**。
4 |
5 | 
6 |
7 | redis replication -> 主从架构 -> 读写分离 -> 水平扩容支撑读高并发
8 |
9 | ## redis replication 的核心机制
10 | - redis 采用**异步方式**复制数据到 slave 节点,不过 redis2.8 开始,slave node 会周期性地确认自己每次复制的数据量;
11 | - 一个 master node 是可以配置多个 slave node 的;
12 | - slave node 也可以连接其他的 slave node;
13 | - slave node 做复制的时候,不会 block master node 的正常工作;
14 | - slave node 在做复制的时候,也不会 block 对自己的查询操作,它会用旧的数据集来提供服务;但是复制完成的时候,需要删除旧数据集,加载新数据集,这个时候就会暂停对外服务了;
15 | - slave node 主要用来进行横向扩容,做读写分离,扩容的 slave node 可以提高读的吞吐量。
16 |
17 | 注意,如果采用了主从架构,那么建议必须**开启** master node 的[持久化](/docs/high-concurrency/redis-persistence.md),不建议用 slave node 作为 master node 的数据热备,因为那样的话,如果你关掉 master 的持久化,可能在 master 宕机重启的时候数据是空的,然后可能一经过复制, slave node 的数据也丢了。
18 |
19 | 另外,master 的各种备份方案,也需要做。万一本地的所有文件丢失了,从备份中挑选一份 rdb 去恢复 master,这样才能**确保启动的时候,是有数据的**,即使采用了后续讲解的[高可用机制](/docs/high-concurrency/redis-sentinel.md),slave node 可以自动接管 master node,但也可能 sentinel 还没检测到 master failure,master node 就自动重启了,还是可能导致上面所有的 slave node 数据被清空。
20 |
21 | ## redis 主从复制的核心原理
22 | 当启动一个 slave node 的时候,它会发送一个 `PSYNC` 命令给 master node。
23 |
24 | 如果这是 slave node 初次连接到 master node,那么会触发一次 `full resynchronization` 全量复制。此时 master 会启动一个后台线程,开始生成一份 `RDB` 快照文件,同时还会将从客户端 client 新收到的所有写命令缓存在内存中。`RDB` 文件生成完毕后, master 会将这个 `RDB` 发送给 slave,slave 会先**写入本地磁盘,然后再从本地磁盘加载到内存**中,接着 master 会将内存中缓存的写命令发送到 slave,slave 也会同步这些数据。slave node 如果跟 master node 有网络故障,断开了连接,会自动重连,连接之后 master node 仅会复制给 slave 部分缺少的数据。
25 |
26 | 
27 |
28 | ### 主从复制的断点续传
29 | 从 redis2.8 开始,就支持主从复制的断点续传,如果主从复制过程中,网络连接断掉了,那么可以接着上次复制的地方,继续复制下去,而不是从头开始复制一份。
30 |
31 | master node 会在内存中维护一个 backlog,master 和 slave 都会保存一个 replica offset 还有一个 master run id,offset 就是保存在 backlog 中的。如果 master 和 slave 网络连接断掉了,slave 会让 master 从上次 replica offset 开始继续复制,如果没有找到对应的 offset,那么就会执行一次 `resynchronization`。
32 |
33 | > 如果根据 host+ip 定位 master node,是不靠谱的,如果 master node 重启或者数据出现了变化,那么 slave node 应该根据不同的 run id 区分。
34 |
35 | ### 无磁盘化复制
36 | master 在内存中直接创建 `RDB`,然后发送给 slave,不会在自己本地落地磁盘了。只需要在配置文件中开启 `repl-diskless-sync yes` 即可。
37 | ```bash
38 | repl-diskless-sync yes
39 |
40 | # 等待 5s 后再开始复制,因为要等更多 slave 重新连接过来
41 | repl-diskless-sync-delay 5
42 | ```
43 |
44 | ### 过期 key 处理
45 | slave 不会过期 key,只会等待 master 过期 key。如果 master 过期了一个 key,或者通过 LRU 淘汰了一个 key,那么会模拟一条 del 命令发送给 slave。
46 |
47 | ## 复制的完整流程
48 | slave node 启动时,会在自己本地保存 master node 的信息,包括 master node 的`host`和`ip`,但是复制流程没开始。
49 |
50 | slave node 内部有个定时任务,每秒检查是否有新的 master node 要连接和复制,如果发现,就跟 master node 建立 socket 网络连接。然后 slave node 发送 `ping` 命令给 master node。如果 master 设置了 requirepass,那么 slave node 必须发送 masterauth 的口令过去进行认证。master node **第一次执行全量复制**,将所有数据发给 slave node。而在后续,master node 持续将写命令,异步复制给 slave node。
51 |
52 | 
53 |
54 | ### 全量复制
55 | - master 执行 bgsave ,在本地生成一份 rdb 快照文件。
56 | - master node 将 rdb 快照文件发送给 slave node,如果 rdb 复制时间超过 60秒(repl-timeout),那么 slave node 就会认为复制失败,可以适当调大这个参数(对于千兆网卡的机器,一般每秒传输 100MB,6G 文件,很可能超过 60s)
57 | - master node 在生成 rdb 时,会将所有新的写命令缓存在内存中,在 slave node 保存了 rdb 之后,再将新的写命令复制给 slave node。
58 | - 如果在复制期间,内存缓冲区持续消耗超过 64MB,或者一次性超过 256MB,那么停止复制,复制失败。
59 | ```bash
60 | client-output-buffer-limit slave 256MB 64MB 60
61 | ```
62 | - slave node 接收到 rdb 之后,清空自己的旧数据,然后重新加载 rdb 到自己的内存中,同时**基于旧的数据版本**对外提供服务。
63 | - 如果 slave node 开启了 AOF,那么会立即执行 BGREWRITEAOF,重写 AOF。
64 |
65 | ### 增量复制
66 | - 如果全量复制过程中,master-slave 网络连接断掉,那么 slave 重新连接 master 时,会触发增量复制。
67 | - master 直接从自己的 backlog 中获取部分丢失的数据,发送给 slave node,默认 backlog 就是 1MB。
68 | - master 就是根据 slave 发送的 psync 中的 offset 来从 backlog 中获取数据的。
69 |
70 | ### heartbeat
71 | 主从节点互相都会发送 heartbeat 信息。
72 |
73 | master 默认每隔 10秒 发送一次 heartbeat,slave node 每隔 1秒 发送一个 heartbeat。
74 |
75 | ### 异步复制
76 | master 每次接收到写命令之后,先在内部写入数据,然后异步发送给 slave node。
77 |
78 | ## redis 如何才能做到高可用
79 | 如果系统在 365 天内,有 99.99% 的时间,都是可以哗哗对外提供服务的,那么就说系统是高可用的。
80 |
81 | 一个 slave 挂掉了,是不会影响可用性的,还有其它的 slave 在提供相同数据下的相同的对外的查询服务。
82 |
83 | 但是,如果 master node 死掉了,会怎么样?没法写数据了,写缓存的时候,全部失效了。slave node 还有什么用呢,没有 master 给它们复制数据了,系统相当于不可用了。
84 |
85 | redis 的高可用架构,叫做 `failover` **故障转移**,也可以叫做主备切换。
86 |
87 | master node 在故障时,自动检测,并且将某个 slave node 自动切换为 master node 的过程,叫做主备切换。这个过程,实现了 redis 的主从架构下的高可用。
88 |
89 | 后面会详细说明 redis [基于哨兵的高可用性](/docs/high-concurrency/redis-sentinel.md)。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-persistence.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?
3 |
4 | ## 面试官心理分析
5 | redis 如果仅仅只是将数据缓存在内存里面,如果 redis 宕机了再重启,内存里的数据就全部都弄丢了啊。你必须得用 redis 的持久化机制,将数据写入内存的同时,异步的慢慢的将数据写入磁盘文件里,进行持久化。
6 |
7 | 如果 redis 宕机重启,自动从磁盘上加载之前持久化的一些数据就可以了,也许会丢失少许数据,但是至少不会将所有数据都弄丢。
8 |
9 | 这个其实一样,针对的都是 redis 的生产环境可能遇到的一些问题,就是 redis 要是挂了再重启,内存里的数据不就全丢了?能不能重启的时候把数据给恢复了?
10 |
11 | ## 面试题剖析
12 | 持久化主要是做灾难恢复、数据恢复,也可以归类到高可用的一个环节中去,比如你 redis 整个挂了,然后 redis 就不可用了,你要做的事情就是让 redis 变得可用,尽快变得可用。
13 |
14 | 重启 redis,尽快让它对外提供服务,如果没做数据备份,这时候 redis 启动了,也不可用啊,数据都没了。
15 |
16 | 很可能说,大量的请求过来,缓存全部无法命中,在 redis 里根本找不到数据,这个时候就死定了,出现**缓存雪崩**问题。所有请求没有在 redis 命中,就会去 mysql 数据库这种数据源头中去找,一下子 mysql 承接高并发,然后就挂了...
17 |
18 | 如果你把 redis 持久化做好,备份和恢复方案做到企业级的程度,那么即使你的 redis 故障了,也可以通过备份数据,快速恢复,一旦恢复立即对外提供服务。
19 |
20 | ### redis 持久化的两种方式
21 | - RDB:RDB 持久化机制,是对 redis 中的数据执行**周期性**的持久化。
22 | - AOF:AOF 机制对每条写入命令作为日志,以 `append-only` 的模式写入一个日志文件中,在 redis 重启的时候,可以通过**回放** AOF 日志中的写入指令来重新构建整个数据集。
23 |
24 | 通过 RDB 或 AOF,都可以将 redis 内存中的数据给持久化到磁盘上面来,然后可以将这些数据备份到别的地方去,比如说阿里云等云服务。
25 |
26 | 如果 redis 挂了,服务器上的内存和磁盘上的数据都丢了,可以从云服务上拷贝回来之前的数据,放到指定的目录中,然后重新启动 redis,redis 就会自动根据持久化数据文件中的数据,去恢复内存中的数据,继续对外提供服务。
27 |
28 | 如果同时使用 RDB 和 AOF 两种持久化机制,那么在 redis 重启的时候,会使用 **AOF** 来重新构建数据,因为 AOF 中的**数据更加完整**。
29 |
30 | #### RDB 优缺点
31 | - RDB 会生成多个数据文件,每个数据文件都代表了某一个时刻中 redis 的数据,这种多个数据文件的方式,**非常适合做冷备**,可以将这种完整的数据文件发送到一些远程的安全存储上去,比如说 Amazon 的 S3 云服务上去,在国内可以是阿里云的 ODPS 分布式存储上,以预定好的备份策略来定期备份 redis 中的数据。
32 | - RDB 对 redis 对外提供的读写服务,影响非常小,可以让 redis **保持高性能**,因为 redis 主进程只需要 fork 一个子进程,让子进程执行磁盘 IO 操作来进行 RDB 持久化即可。
33 | - 相对于 AOF 持久化机制来说,直接基于 RDB 数据文件来重启和恢复 redis 进程,更加快速。
34 |
35 | - 如果想要在 redis 故障时,尽可能少的丢失数据,那么 RDB 没有 AOF 好。一般来说,RDB 数据快照文件,都是每隔 5 分钟,或者更长时间生成一次,这个时候就得接受一旦 redis 进程宕机,那么会丢失最近 5 分钟的数据。
36 | - RDB 每次在 fork 子进程来执行 RDB 快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒。
37 |
38 | #### AOF 优缺点
39 | - AOF 可以更好的保护数据不丢失,一般 AOF 会每隔 1 秒,通过一个后台线程执行一次`fsync`操作,最多丢失 1 秒钟的数据。
40 | - AOF 日志文件以 `append-only` 模式写入,所以没有任何磁盘寻址的开销,写入性能非常高,而且文件不容易破损,即使文件尾部破损,也很容易修复。
41 | - AOF 日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。因为在 `rewrite` log 的时候,会对其中的指令进行压缩,创建出一份需要恢复数据的最小日志出来。在创建新日志文件的时候,老的日志文件还是照常写入。当新的 merge 后的日志文件 ready 的时候,再交换新老日志文件即可。
42 | - AOF 日志文件的命令通过非常可读的方式进行记录,这个特性非常**适合做灾难性的误删除的紧急恢复**。比如某人不小心用 `flushall` 命令清空了所有数据,只要这个时候后台 `rewrite` 还没有发生,那么就可以立即拷贝 AOF 文件,将最后一条 `flushall` 命令给删了,然后再将该 `AOF` 文件放回去,就可以通过恢复机制,自动恢复所有数据。
43 | - 对于同一份数据来说,AOF 日志文件通常比 RDB 数据快照文件更大。
44 | - AOF 开启后,支持的写 QPS 会比 RDB 支持的写 QPS 低,因为 AOF 一般会配置成每秒 `fsync` 一次日志文件,当然,每秒一次 `fsync`,性能也还是很高的。(如果实时写入,那么 QPS 会大降,redis 性能会大大降低)
45 | - 以前 AOF 发生过 bug,就是通过 AOF 记录的日志,进行数据恢复的时候,没有恢复一模一样的数据出来。所以说,类似 AOF 这种较为复杂的基于命令日志 / merge / 回放的方式,比基于 RDB 每次持久化一份完整的数据快照文件的方式,更加脆弱一些,容易有 bug。不过 AOF 就是为了避免 rewrite 过程导致的 bug,因此每次 rewrite 并不是基于旧的指令日志进行 merge 的,而是**基于当时内存中的数据进行指令的重新构建**,这样健壮性会好很多。
46 |
47 | ### RDB 和 AOF 到底该如何选择
48 | - 不要仅仅使用 RDB,因为那样会导致你丢失很多数据;
49 | - 也不要仅仅使用 AOF,因为那样有两个问题:第一,你通过 AOF 做冷备,没有 RDB 做冷备来的恢复速度更快;第二,RDB 每次简单粗暴生成数据快照,更加健壮,可以避免 AOF 这种复杂的备份和恢复机制的 bug;
50 | - redis 支持同时开启开启两种持久化方式,我们可以综合使用 AOF 和 RDB 两种持久化机制,用 AOF 来保证数据不丢失,作为数据恢复的第一选择; 用 RDB 来做不同程度的冷备,在 AOF 文件都丢失或损坏不可用的时候,还可以使用 RDB 来进行快速的数据恢复。
51 |
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-production-environment.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 生产环境中的 redis 是怎么部署的?
3 |
4 | ## 面试官心理分析
5 | 看看你了解不了解你们公司的 redis 生产集群的部署架构,如果你不了解,那么确实你就很失职了,你的 redis 是主从架构?集群架构?用了哪种集群方案?有没有做高可用保证?有没有开启持久化机制确保可以进行数据恢复?线上 redis 给几个 G 的内存?设置了哪些参数?压测后你们 redis 集群承载多少 QPS?
6 |
7 | 兄弟,这些你必须是门儿清的,否则你确实是没好好思考过。
8 |
9 | ## 面试题剖析
10 | redis cluster,10 台机器,5 台机器部署了 redis 主实例,另外 5 台机器部署了 redis 的从实例,每个主实例挂了一个从实例,5 个节点对外提供读写服务,每个节点的读写高峰qps可能可以达到每秒 5 万,5 台机器最多是 25 万读写请求/s。
11 |
12 | 机器是什么配置?32G 内存+ 8 核 CPU + 1T 磁盘,但是分配给 redis 进程的是10g内存,一般线上生产环境,redis 的内存尽量不要超过 10g,超过 10g 可能会有问题。
13 |
14 | 5 台机器对外提供读写,一共有 50g 内存。
15 |
16 | 因为每个主实例都挂了一个从实例,所以是高可用的,任何一个主实例宕机,都会自动故障迁移,redis 从实例会自动变成主实例继续提供读写服务。
17 |
18 | 你往内存里写的是什么数据?每条数据的大小是多少?商品数据,每条数据是 10kb。100 条数据是 1mb,10 万条数据是 1g。常驻内存的是 200 万条商品数据,占用内存是 20g,仅仅不到总内存的 50%。目前高峰期每秒就是 3500 左右的请求量。
19 |
20 | 其实大型的公司,会有基础架构的 team 负责缓存集群的运维。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-sentinel.md:
--------------------------------------------------------------------------------
1 | # Redis 哨兵集群实现高可用
2 |
3 | ## 哨兵的介绍
4 | sentinel,中文名是哨兵。哨兵是 redis 集群机构中非常重要的一个组件,主要有以下功能:
5 |
6 | - 集群监控:负责监控 redis master 和 slave 进程是否正常工作。
7 | - 消息通知:如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员。
8 | - 故障转移:如果 master node 挂掉了,会自动转移到 slave node 上。
9 | - 配置中心:如果故障转移发生了,通知 client 客户端新的 master 地址。
10 |
11 | 哨兵用于实现 redis 集群的高可用,本身也是分布式的,作为一个哨兵集群去运行,互相协同工作。
12 |
13 | - 故障转移时,判断一个 master node 是否宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题。
14 | - 即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,那就很坑爹了。
15 |
16 | ## 哨兵的核心知识
17 | - 哨兵至少需要 3 个实例,来保证自己的健壮性。
18 | - 哨兵 + redis 主从的部署架构,是**不保证数据零丢失**的,只能保证 redis 集群的高可用性。
19 | - 对于哨兵 + redis 主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练。
20 |
21 | 哨兵集群必须部署 2 个以上节点,如果哨兵集群仅仅部署了 2 个哨兵实例,quorum = 1。
22 |
23 | ```
24 | +----+ +----+
25 | | M1 |---------| R1 |
26 | | S1 | | S2 |
27 | +----+ +----+
28 | ```
29 |
30 | 配置 `quorum=1`,如果 master 宕机, s1 和 s2 中只要有 1 个哨兵认为 master 宕机了,就可以进行切换,同时 s1 和 s2 会选举出一个哨兵来执行故障转移。但是同时这个时候,需要 majority,也就是大多数哨兵都是运行的。
31 |
32 | ```
33 | 2 个哨兵,majority=2
34 | 3 个哨兵,majority=2
35 | 4 个哨兵,majority=2
36 | 5 个哨兵,majority=3
37 | ...
38 | ```
39 |
40 | 如果此时仅仅是 M1 进程宕机了,哨兵 s1 正常运行,那么故障转移是 OK 的。但是如果是整个 M1 和 S1 运行的机器宕机了,那么哨兵只有 1 个,此时就没有 majority 来允许执行故障转移,虽然另外一台机器上还有一个 R1,但是故障转移不会执行。
41 |
42 | 经典的 3 节点哨兵集群是这样的:
43 |
44 | ```
45 | +----+
46 | | M1 |
47 | | S1 |
48 | +----+
49 | |
50 | +----+ | +----+
51 | | R2 |----+----| R3 |
52 | | S2 | | S3 |
53 | +----+ +----+
54 | ```
55 |
56 | 配置 `quorum=2`,如果 M1 所在机器宕机了,那么三个哨兵还剩下 2 个,S2 和 S3 可以一致认为 master 宕机了,然后选举出一个来执行故障转移,同时 3 个哨兵的 majority 是 2,所以还剩下的 2 个哨兵运行着,就可以允许执行故障转移。
57 |
58 | ## redis 哨兵主备切换的数据丢失问题
59 | ### 两种情况和导致数据丢失
60 | 主备切换的过程,可能会导致数据丢失:
61 |
62 | - 异步复制导致的数据丢失
63 |
64 | 因为 master->slave 的复制是异步的,所以可能有部分数据还没复制到 slave,master 就宕机了,此时这部分数据就丢失了。
65 |
66 | 
67 |
68 | - 脑裂导致的数据丢失
69 |
70 | 脑裂,也就是说,某个 master 所在机器突然**脱离了正常的网络**,跟其他 slave 机器不能连接,但是实际上 master 还运行着。此时哨兵可能就会**认为** master 宕机了,然后开启选举,将其他 slave 切换成了 master。这个时候,集群里就会有两个 master ,也就是所谓的**脑裂**。
71 |
72 | 此时虽然某个 slave 被切换成了 master,但是可能 client 还没来得及切换到新的 master,还继续向旧 master 写数据。因此旧 master 再次恢复的时候,会被作为一个 slave 挂到新的 master 上去,自己的数据会清空,重新从新的 master 复制数据。而新的 master 并没有后来 client 写入的数据,因此,这部分数据也就丢失了。
73 |
74 | 
75 |
76 | ### 数据丢失问题的解决方案
77 | 进行如下配置:
78 |
79 | ```bash
80 | min-slaves-to-write 1
81 | min-slaves-max-lag 10
82 | ```
83 |
84 | 表示,要求至少有 1 个 slave,数据复制和同步的延迟不能超过 10 秒。
85 |
86 | 如果说一旦所有的 slave,数据复制和同步的延迟都超过了 10 秒钟,那么这个时候,master 就不会再接收任何请求了。
87 |
88 | - 减少异步复制数据的丢失
89 |
90 | 有了 `min-slaves-max-lag` 这个配置,就可以确保说,一旦 slave 复制数据和 ack 延时太长,就认为可能 master 宕机后损失的数据太多了,那么就拒绝写请求,这样可以把 master 宕机时由于部分数据未同步到 slave 导致的数据丢失降低的可控范围内。
91 |
92 | - 减少脑裂的数据丢失
93 |
94 | 如果一个 master 出现了脑裂,跟其他 slave 丢了连接,那么上面两个配置可以确保说,如果不能继续给指定数量的 slave 发送数据,而且 slave 超过 10 秒没有给自己 ack 消息,那么就直接拒绝客户端的写请求。因此在脑裂场景下,最多就丢失 10 秒的数据。
95 |
96 | ## sdown 和 odown 转换机制
97 | - sdown 是主观宕机,就一个哨兵如果自己觉得一个 master 宕机了,那么就是主观宕机
98 | - odown 是客观宕机,如果 quorum 数量的哨兵都觉得一个 master 宕机了,那么就是客观宕机
99 |
100 | sdown 达成的条件很简单,如果一个哨兵 ping 一个 master,超过了 `is-master-down-after-milliseconds` 指定的毫秒数之后,就主观认为 master 宕机了;如果一个哨兵在指定时间内,收到了 quorum 数量的其它哨兵也认为那个 master 是 sdown 的,那么就认为是 odown 了。
101 |
102 | ## 哨兵集群的自动发现机制
103 | 哨兵互相之间的发现,是通过 redis 的 `pub/sub` 系统实现的,每个哨兵都会往 `__sentinel__:hello` 这个 channel 里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在。
104 |
105 | 每隔两秒钟,每个哨兵都会往自己监控的某个 master+slaves 对应的 `__sentinel__:hello` channel 里**发送一个消息**,内容是自己的 host、ip 和 runid 还有对这个 master 的监控配置。
106 |
107 | 每个哨兵也会去**监听**自己监控的每个 master+slaves 对应的 `__sentinel__:hello` channel,然后去感知到同样在监听这个 master+slaves 的其他哨兵的存在。
108 |
109 | 每个哨兵还会跟其他哨兵交换对 `master` 的监控配置,互相进行监控配置的同步。
110 |
111 | ## slave 配置的自动纠正
112 | 哨兵会负责自动纠正 slave 的一些配置,比如 slave 如果要成为潜在的 master 候选人,哨兵会确保 slave 复制现有 master 的数据;如果 slave 连接到了一个错误的 master 上,比如故障转移之后,那么哨兵会确保它们连接到正确的 master 上。
113 |
114 | ## slave->master 选举算法
115 | 如果一个 master 被认为 odown 了,而且 majority 数量的哨兵都允许主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个 slave 来,会考虑 slave 的一些信息:
116 |
117 | - 跟 master 断开连接的时长
118 | - slave 优先级
119 | - 复制 offset
120 | - run id
121 |
122 | 如果一个 slave 跟 master 断开连接的时间已经超过了 `down-after-milliseconds` 的 10 倍,外加 master 宕机的时长,那么 slave 就被认为不适合选举为 master。
123 |
124 | ```
125 | (down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
126 | ```
127 |
128 | 接下来会对 slave 进行排序:
129 |
130 | - 按照 slave 优先级进行排序,slave priority 越低,优先级就越高。
131 | - 如果 slave priority 相同,那么看 replica offset,哪个 slave 复制了越多的数据,offset 越靠后,优先级就越高。
132 | - 如果上面两个条件都相同,那么选择一个 run id 比较小的那个 slave。
133 |
134 | ## quorum 和 majority
135 | 每次一个哨兵要做主备切换,首先需要 quorum 数量的哨兵认为 odown,然后选举出一个哨兵来做切换,这个哨兵还需要得到 majority 哨兵的授权,才能正式执行切换。
136 |
137 | 如果 quorum < majority,比如 5 个哨兵,majority 就是 3,quorum 设置为 2,那么就 3 个哨兵授权就可以执行切换。
138 |
139 | 但是如果 quorum >= majority,那么必须 quorum 数量的哨兵都授权,比如 5 个哨兵,quorum 是 5,那么必须 5 个哨兵都同意授权,才能执行切换。
140 |
141 | ## configuration epoch
142 | 哨兵会对一套 redis master+slaves 进行监控,有相应的监控的配置。
143 |
144 | 执行切换的那个哨兵,会从要切换到的新 master(salve->master)那里得到一个 configuration epoch,这就是一个 version 号,每次切换的 version 号都必须是唯一的。
145 |
146 | 如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待 failover-timeout 时间,然后接替继续执行切换,此时会重新获取一个新的 configuration epoch,作为新的 version 号。
147 |
148 | ## configuration 传播
149 | 哨兵完成切换之后,会在自己本地更新生成最新的 master 配置,然后同步给其他的哨兵,就是通过之前说的 `pub/sub` 消息机制。
150 |
151 | 这里之前的 version 号就很重要了,因为各种消息都是通过一个 channel 去发布和监听的,所以一个哨兵完成一次新的切换之后,新的 master 配置是跟着新的 version 号的。其他的哨兵都是根据版本号的大小来更新自己的 master 配置的。
--------------------------------------------------------------------------------
/docs/high-concurrency/redis-single-thread-model.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | redis 和 memcached 有什么区别?redis 的线程模型是什么?为什么 redis 单线程却能支撑高并发?
3 |
4 | ## 面试官心理分析
5 | 这个是问 redis 的时候,最基本的问题吧,redis 最基本的一个内部原理和特点,就是 redis 实际上是个**单线程工作模型**,你要是这个都不知道,那后面玩儿 redis 的时候,出了问题岂不是什么都不知道?
6 |
7 | 还有可能面试官会问问你 redis 和 memcached 的区别,但是 memcached 是早些年各大互联网公司常用的缓存方案,但是现在近几年基本都是 redis,没什么公司用 memcached 了。
8 |
9 | ## 面试题剖析
10 |
11 | ### redis 和 memcached 有啥区别?
12 |
13 | #### redis 支持复杂的数据结构
14 | redis 相比 memcached 来说,拥有[更多的数据结构](/docs/high-concurrency/redis-data-types.md),能支持更丰富的数据操作。如果需要缓存能够支持更复杂的结构和操作, redis 会是不错的选择。
15 |
16 | #### redis 原生支持集群模式
17 | 在 redis3.x 版本中,便能支持 cluster 模式,而 memcached 没有原生的集群模式,需要依靠客户端来实现往集群中分片写入数据。
18 |
19 | #### 性能对比
20 | 由于 redis 只使用**单核**,而 memcached 可以使用**多核**,所以平均每一个核上 redis 在存储小数据时比 memcached 性能更高。而在 100k 以上的数据中,memcached 性能要高于 redis。虽然 redis 最近也在存储大数据的性能上进行优化,但是比起 memcached,还是稍有逊色。
21 |
22 | ### redis 的线程模型
23 | redis 内部使用文件事件处理器 `file event handler`,这个文件事件处理器是单线程的,所以 redis 才叫做单线程的模型。它采用 IO 多路复用机制同时监听多个 socket,将产生事件的 socket 压入内存队列中,事件分派器根据 socket 上的事件类型来选择对应的事件处理器进行处理。
24 |
25 | 文件事件处理器的结构包含 4 个部分:
26 |
27 | - 多个 socket
28 | - IO 多路复用程序
29 | - 文件事件分派器
30 | - 事件处理器(连接应答处理器、命令请求处理器、命令回复处理器)
31 |
32 | 多个 socket 可能会并发产生不同的操作,每个操作对应不同的文件事件,但是 IO 多路复用程序会监听多个 socket,会将产生事件的 socket 放入队列中排队,事件分派器每次从队列中取出一个 socket,根据 socket 的事件类型交给对应的事件处理器进行处理。
33 |
34 | 来看客户端与 redis 的一次通信过程:
35 |
36 | 
37 |
38 | 要明白,通信是通过 socket 来完成的,不懂的同学可以先去看一看 socket 网络编程。
39 |
40 | 首先,redis 服务端进程初始化的时候,会将 server socket 的 `AE_READABLE` 事件与连接应答处理器关联。
41 |
42 | 客户端 socket01 向 redis 进程的 server socket 请求建立连接,此时 server socket 会产生一个 `AE_READABLE` 事件,IO 多路复用程序监听到 server socket 产生的事件后,将该 socket 压入队列中。文件事件分派器从队列中获取 socket,交给**连接应答处理器**。连接应答处理器会创建一个能与客户端通信的 socket01,并将该 socket01 的 `AE_READABLE` 事件与命令请求处理器关联。
43 |
44 | 假设此时客户端发送了一个 `set key value` 请求,此时 redis 中的 socket01 会产生 `AE_READABLE` 事件,IO 多路复用程序将 socket01 压入队列,此时事件分派器从队列中获取到 socket01 产生的 `AE_READABLE` 事件,由于前面 socket01 的 `AE_READABLE` 事件已经与命令请求处理器关联,因此事件分派器将事件交给命令请求处理器来处理。命令请求处理器读取 socket01 的 `key value` 并在自己内存中完成 `key value` 的设置。操作完成后,它会将 socket01 的 `AE_WRITABLE` 事件与命令回复处理器关联。
45 |
46 | 如果此时客户端准备好接收返回结果了,那么 redis 中的 socket01 会产生一个 `AE_WRITABLE` 事件,同样压入队列中,事件分派器找到相关联的命令回复处理器,由命令回复处理器对 socket01 输入本次操作的一个结果,比如 `ok`,之后解除 socket01 的 `AE_WRITABLE` 事件与命令回复处理器的关联。
47 |
48 | 这样便完成了一次通信。
49 |
50 | ### 为啥 redis 单线程模型也能效率这么高?
51 | - 纯内存操作。
52 | - 核心是基于非阻塞的 IO 多路复用机制。
53 | - C 语言实现,一般来说,C 语言实现的程序“距离”操作系统更近,执行速度相对会更快。
54 | - 单线程反而避免了多线程的频繁上下文切换问题,预防了多线程可能产生的竞争问题。
--------------------------------------------------------------------------------
/docs/high-concurrency/why-cache.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | 项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果?
3 |
4 | ## 面试官心理分析
5 | 这个问题,互联网公司必问,要是一个人连缓存都不太清楚,那确实比较尴尬。
6 |
7 | 只要问到缓存,上来第一个问题,肯定是先问问你项目哪里用了缓存?为啥要用?不用行不行?如果用了以后可能会有什么不良的后果?
8 |
9 | 这就是看看你对缓存这个东西背后有没有思考,如果你就是傻乎乎的瞎用,没法给面试官一个合理的解答,那面试官对你印象肯定不太好,觉得你平时思考太少,就知道干活儿。
10 |
11 |
12 | ## 面试题剖析
13 | ### 项目中缓存是如何使用的?
14 | 这个,需要结合自己项目的业务来。
15 |
16 | ### 为什么要用缓存?
17 | 用缓存,主要有两个用途:**高性能**、**高并发**。
18 |
19 | #### 高性能
20 | 假设这么个场景,你有个操作,一个请求过来,吭哧吭哧你各种乱七八糟操作 mysql,半天查出来一个结果,耗时 600ms。但是这个结果可能接下来几个小时都不会变了,或者变了也可以不用立即反馈给用户。那么此时咋办?
21 |
22 | 缓存啊,折腾 600ms 查出来的结果,扔缓存里,一个 key 对应一个 value,下次再有人查,别走 mysql 折腾 600ms 了,直接从缓存里,通过一个 key 查出来一个 value,2ms 搞定。性能提升 300 倍。
23 |
24 | 就是说对于一些需要复杂操作耗时查出来的结果,且确定后面不怎么变化,但是有很多读请求,那么直接将查询出来的结果放在缓存中,后面直接读缓存就好。
25 |
26 | #### 高并发
27 | mysql 这么重的数据库,压根儿设计不是让你玩儿高并发的,虽然也可以玩儿,但是天然支持不好。mysql 单机支撑到 `2000QPS` 也开始容易报警了。
28 |
29 | 所以要是你有个系统,高峰期一秒钟过来的请求有 1万,那一个 mysql 单机绝对会死掉。你这个时候就只能上缓存,把很多数据放缓存,别放 mysql。缓存功能简单,说白了就是 `key-value` 式操作,单机支撑的并发量轻松一秒几万十几万,支撑高并发 so easy。单机承载并发量是 mysql 单机的几十倍。
30 |
31 | > 缓存是走内存的,内存天然就支撑高并发。
32 |
33 | ### 用了缓存之后会有什么不良后果?
34 | 常见的缓存问题有以下几个:
35 | - [缓存与数据库双写不一致](/docs/high-concurrency/redis-consistence.md)
36 | - [缓存雪崩、缓存穿透](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
37 | - [缓存并发竞争](/docs/high-concurrency/redis-cas.md)
38 |
39 | 后面再详细说明。
--------------------------------------------------------------------------------
/docs/high-concurrency/why-mq.md:
--------------------------------------------------------------------------------
1 | ## 面试题
2 | - 为什么使用消息队列?
3 | - 消息队列有什么优点和缺点?
4 | - Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别,以及适合哪些场景?
5 |
6 | ## 面试官心理分析
7 | 其实面试官主要是想看看:
8 |
9 | - **第一**,你知不知道你们系统里为什么要用消息队列这个东西?
10 | 不少候选人,说自己项目里用了 Redis、MQ,但是其实他并不知道自己为什么要用这个东西。其实说白了,就是为了用而用,或者是别人设计的架构,他从头到尾都没思考过。
11 | 没有对自己的架构问过为什么的人,一定是平时没有思考的人,面试官对这类候选人印象通常很不好。因为面试官担心你进了团队之后只会木头木脑的干呆活儿,不会自己思考。
12 |
13 | - **第二**,你既然用了消息队列这个东西,你知不知道用了有什么好处&坏处?
14 | 你要是没考虑过这个,那你盲目弄个 MQ 进系统里,后面出了问题你是不是就自己溜了给公司留坑?你要是没考虑过引入一个技术可能存在的弊端和风险,面试官把这类候选人招进来了,基本可能就是挖坑型选手。就怕你干 1 年挖一堆坑,自己跳槽了,给公司留下无穷后患。
15 |
16 | - **第三**,既然你用了 MQ,可能是某一种 MQ,那么你当时做没做过调研?
17 | 你别傻乎乎的自己拍脑袋看个人喜好就瞎用了一个 MQ,比如 Kafka,甚至都从没调研过业界流行的 MQ 到底有哪几种。每一个 MQ 的优点和缺点是什么。每一个 MQ **没有绝对的好坏**,但是就是看用在哪个场景可以**扬长避短,利用其优势,规避其劣势**。
18 | 如果是一个不考虑技术选型的候选人招进了团队,leader 交给他一个任务,去设计个什么系统,他在里面用一些技术,可能都没考虑过选型,最后选的技术可能并不一定合适,一样是留坑。
19 |
20 | ## 面试题剖析
21 | ### 为什么使用消息队列
22 | 其实就是问问你消息队列都有哪些使用场景,然后你项目里具体是什么场景,说说你在这个场景里用消息队列是什么?
23 |
24 | 面试官问你这个问题,**期望的一个回答**是说,你们公司有个什么**业务场景**,这个业务场景有个什么技术挑战,如果不用 MQ 可能会很麻烦,但是你现在用了 MQ 之后带给了你很多的好处。
25 |
26 | 先说一下消息队列常见的使用场景吧,其实场景有很多,但是比较核心的有 3 个:**解耦**、**异步**、**削峰**。
27 |
28 | #### 解耦
29 | 看这么个场景。A 系统发送数据到 BCD 三个系统,通过接口调用发送。如果 E 系统也要这个数据呢?那如果 C 系统现在不需要了呢?A 系统负责人几乎崩溃......
30 |
31 | 
32 |
33 | 在这个场景中,A 系统跟其它各种乱七八糟的系统严重耦合,A 系统产生一条比较关键的数据,很多系统都需要 A 系统将这个数据发送过来。A 系统要时时刻刻考虑 BCDE 四个系统如果挂了该咋办?要不要重发,要不要把消息存起来?头发都白了啊!
34 |
35 | 如果使用 MQ,A 系统产生一条数据,发送到 MQ 里面去,哪个系统需要数据自己去 MQ 里面消费。如果新系统需要数据,直接从 MQ 里消费即可;如果某个系统不需要这条数据了,就取消对 MQ 消息的消费即可。这样下来,A 系统压根儿不需要去考虑要给谁发送数据,不需要维护这个代码,也不需要考虑人家是否调用成功、失败超时等情况。
36 |
37 | 
38 |
39 | **总结**:通过一个 MQ,Pub/Sub 发布订阅消息这么一个模型,A 系统就跟其它系统彻底解耦了。
40 |
41 | **面试技巧**:你需要去考虑一下你负责的系统中是否有类似的场景,就是一个系统或者一个模块,调用了多个系统或者模块,互相之间的调用很复杂,维护起来很麻烦。但是其实这个调用是不需要直接同步调用接口的,如果用 MQ 给它异步化解耦,也是可以的,你就需要去考虑在你的项目里,是不是可以运用这个 MQ 去进行系统的解耦。在简历中体现出来这块东西,用 MQ 作解耦。
42 |
43 | #### 异步
44 | 再来看一个场景,A 系统接收一个请求,需要在自己本地写库,还需要在 BCD 三个系统写库,自己本地写库要 3ms,BCD 三个系统分别写库要 300ms、450ms、200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近 1s,用户感觉搞个什么东西,慢死了慢死了。用户通过浏览器发起请求,等待个 1s,这几乎是不可接受的。
45 |
46 | 
47 |
48 | 一般互联网类的企业,对于用户直接的操作,一般要求是每个请求都必须在 200 ms 以内完成,对用户几乎是无感知的。
49 |
50 | 如果**使用 MQ**,那么 A 系统连续发送 3 条消息到 MQ 队列中,假如耗时 5ms,A 系统从接受一个请求到返回响应给用户,总时长是 3 + 5 = 8ms,对于用户而言,其实感觉上就是点个按钮,8ms 以后就直接返回了,爽!网站做得真好,真快!
51 |
52 | 
53 |
54 | #### 削峰
55 | 每天 0:00 到 12:00,A 系统风平浪静,每秒并发请求数量就 50 个。结果每次一到 12:00 ~ 13:00 ,每秒并发请求数量突然会暴增到 5k+ 条。但是系统是直接基于 MySQL 的,大量的请求涌入 MySQL,每秒钟对 MySQL 执行约 5k 条 SQL。
56 |
57 | 一般的 MySQL,扛到每秒 2k 个请求就差不多了,如果每秒请求到 5k 的话,可能就直接把 MySQL 给打死了,导致系统崩溃,用户也就没法再使用系统了。
58 |
59 | 但是高峰期一过,到了下午的时候,就成了低峰期,可能也就 1w 的用户同时在网站上操作,每秒中的请求数量可能也就 50 个请求,对整个系统几乎没有任何的压力。
60 |
61 | 
62 |
63 | 如果使用 MQ,每秒 5k 个请求写入 MQ,A 系统每秒钟最多处理 2k 个请求,因为 MySQL 每秒钟最多处理 2k 个。A 系统从 MQ 中慢慢拉取请求,每秒钟就拉取 2k 个请求,不要超过自己每秒能处理的最大请求数量就 ok,这样下来,哪怕是高峰期的时候,A 系统也绝对不会挂掉。而 MQ 每秒钟 5k 个请求进来,就 2k 个请求出去,结果就导致在中午高峰期(1 个小时),可能有几十万甚至几百万的请求积压在 MQ 中。
64 |
65 | 
66 |
67 | 这个短暂的高峰期积压是 ok 的,因为高峰期过了之后,每秒钟就 50 个请求进 MQ,但是 A 系统依然会按照每秒 2k 个请求的速度在处理。所以说,只要高峰期一过,A 系统就会快速将积压的消息给解决掉。
68 |
69 | ### 消息队列有什么优缺点
70 | 优点上面已经说了,就是**在特殊场景下有其对应的好处**,**解耦**、**异步**、**削峰**。
71 |
72 | 缺点有以下几个:
73 |
74 | - 系统可用性降低
75 | 系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整,MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用,可以[点击这里查看](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)。
76 |
77 | - 系统复杂度提高
78 | 硬生生加个 MQ 进来,你怎么[保证消息没有重复消费](/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)?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。
79 |
80 | - 一致性问题
81 | A 系统处理完了直接返回成功了,人都以为你这个请求就成功了;但是问题是,要是 BCD 三个系统那里,BD 两个系统写库成功了,结果 C 系统写库失败了,咋整?你这数据就不一致了。
82 |
83 | 所以消息队列实际是一种非常复杂的架构,你引入它有很多好处,但是也得针对它带来的坏处做各种额外的技术方案和架构来规避掉,做好之后,你会发现,妈呀,系统复杂度提升了一个数量级,也许是复杂了 10 倍。但是关键时刻,用,还是得用的。
84 |
85 | ### Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
86 |
87 | | 特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
88 | |---|---|---|---|---|
89 | | 单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
90 | | topic 数量对吞吐量的影响 | | | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 |
91 | | 时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
92 | | 可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
93 | | 消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
94 | | 功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
95 |
96 |
97 | 综上,各种对比之后,有如下建议:
98 |
99 | 一般的业务系统要引入 MQ,最早大家都用 ActiveMQ,但是现在确实大家用的不多了,没经过大规模吞吐量场景的验证,社区也不是很活跃,所以大家还是算了吧,我个人不推荐用这个了;
100 |
101 | 后来大家开始用 RabbitMQ,但是确实 erlang 语言阻止了大量的 Java 工程师去深入研究和掌控它,对公司而言,几乎处于不可控的状态,但是确实人家是开源的,比较稳定的支持,活跃度也高;
102 |
103 | 不过现在确实越来越多的公司会去用 RocketMQ,确实很不错,毕竟是阿里出品,但社区可能有突然黄掉的风险(目前 RocketMQ 已捐给 [Apache](https://github.com/apache/rocketmq),但 GitHub 上的活跃度其实不算高)对自己公司技术实力有绝对自信的,推荐用 RocketMQ,否则回去老老实实用 RabbitMQ 吧,人家有活跃的开源社区,绝对不会黄。
104 |
105 | 所以**中小型公司**,技术实力较为一般,技术挑战不是特别高,用 RabbitMQ 是不错的选择;**大型公司**,基础架构研发实力较强,用 RocketMQ 是很好的选择。
106 |
107 | 如果是**大数据领域**的实时计算、日志采集等场景,用 Kafka 是业内标准的,绝对没问题,社区活跃度很高,绝对不会黄,何况几乎是全世界这个领域的事实性规范。
108 |
--------------------------------------------------------------------------------
/docs/micro-services/README.md:
--------------------------------------------------------------------------------
1 | # 微服务架构
--------------------------------------------------------------------------------
/docs/micro-services/images/PreferFunctionalStaffOrganization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/PreferFunctionalStaffOrganization.png
--------------------------------------------------------------------------------
/docs/micro-services/images/basic-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/basic-pipeline.png
--------------------------------------------------------------------------------
/docs/micro-services/images/conways-law.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/conways-law.png
--------------------------------------------------------------------------------
/docs/micro-services/images/decentralised-data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/decentralised-data.png
--------------------------------------------------------------------------------
/docs/micro-services/images/micro-deployment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/micro-deployment.png
--------------------------------------------------------------------------------
/docs/micro-services/images/sketch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/docs/micro-services/images/sketch.png
--------------------------------------------------------------------------------
/images/220px-Internet_dog.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/220px-Internet_dog.jpg
--------------------------------------------------------------------------------
/images/PreferFunctionalStaffOrganization.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/PreferFunctionalStaffOrganization.png
--------------------------------------------------------------------------------
/images/advanced-java-doocs-shishan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/advanced-java-doocs-shishan.png
--------------------------------------------------------------------------------
/images/async-replication-data-lose-case.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/async-replication-data-lose-case.png
--------------------------------------------------------------------------------
/images/basic-pipeline.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/basic-pipeline.png
--------------------------------------------------------------------------------
/images/bulkhead-partition.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/bulkhead-partition.jpg
--------------------------------------------------------------------------------
/images/consistent-hashing-algorithm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/consistent-hashing-algorithm.png
--------------------------------------------------------------------------------
/images/conways-law.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/conways-law.png
--------------------------------------------------------------------------------
/images/database-id-sequence-step.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/database-id-sequence-step.png
--------------------------------------------------------------------------------
/images/database-shard-method-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/database-shard-method-1.png
--------------------------------------------------------------------------------
/images/database-shard-method-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/database-shard-method-2.png
--------------------------------------------------------------------------------
/images/database-split-horizon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/database-split-horizon.png
--------------------------------------------------------------------------------
/images/database-split-vertically.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/database-split-vertically.png
--------------------------------------------------------------------------------
/images/decentralised-data.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/decentralised-data.png
--------------------------------------------------------------------------------
/images/distributed-system-request-sequence.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/distributed-system-request-sequence.png
--------------------------------------------------------------------------------
/images/distributed-transaction-TCC.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/distributed-transaction-TCC.png
--------------------------------------------------------------------------------
/images/distributed-transaction-XA.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/distributed-transaction-XA.png
--------------------------------------------------------------------------------
/images/distributed-transaction-local-message-table.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/distributed-transaction-local-message-table.png
--------------------------------------------------------------------------------
/images/distributed-transaction-reliable-message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/distributed-transaction-reliable-message.png
--------------------------------------------------------------------------------
/images/dubbo-keep-connection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/dubbo-keep-connection.png
--------------------------------------------------------------------------------
/images/dubbo-not-keep-connection.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/dubbo-not-keep-connection.png
--------------------------------------------------------------------------------
/images/dubbo-operating-principle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/dubbo-operating-principle.png
--------------------------------------------------------------------------------
/images/dubbo-service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/dubbo-service-invoke-road.png
--------------------------------------------------------------------------------
/images/dubbo-spi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/dubbo-spi.png
--------------------------------------------------------------------------------
/images/e-commerce-website-detail-page-architecture-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/e-commerce-website-detail-page-architecture-1.png
--------------------------------------------------------------------------------
/images/e-commerce-website-detail-page-architecture-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/e-commerce-website-detail-page-architecture-2.png
--------------------------------------------------------------------------------
/images/es-cluster-0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-cluster-0.png
--------------------------------------------------------------------------------
/images/es-cluster.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-cluster.png
--------------------------------------------------------------------------------
/images/es-index-type-mapping-document-field.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-index-type-mapping-document-field.png
--------------------------------------------------------------------------------
/images/es-search-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-search-process.png
--------------------------------------------------------------------------------
/images/es-write-detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-write-detail.png
--------------------------------------------------------------------------------
/images/es-write.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/es-write.png
--------------------------------------------------------------------------------
/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/favicon-16x16.png
--------------------------------------------------------------------------------
/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/favicon-32x32.png
--------------------------------------------------------------------------------
/images/get-up-and-study.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/get-up-and-study.png
--------------------------------------------------------------------------------
/images/hash-slot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hash-slot.png
--------------------------------------------------------------------------------
/images/hash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hash.png
--------------------------------------------------------------------------------
/images/high-concurrency-system-design.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/high-concurrency-system-design.png
--------------------------------------------------------------------------------
/images/hystrix-process.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-process.png
--------------------------------------------------------------------------------
/images/hystrix-request-cache.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-request-cache.png
--------------------------------------------------------------------------------
/images/hystrix-semphore-thread-pool.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-semphore-thread-pool.png
--------------------------------------------------------------------------------
/images/hystrix-semphore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-semphore.png
--------------------------------------------------------------------------------
/images/hystrix-thread-pool-isolation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-thread-pool-isolation.png
--------------------------------------------------------------------------------
/images/hystrix-thread-pool-queue.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/hystrix-thread-pool-queue.png
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/icon.png
--------------------------------------------------------------------------------
/images/kafka-after.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-after.png
--------------------------------------------------------------------------------
/images/kafka-before.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-before.png
--------------------------------------------------------------------------------
/images/kafka-order-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-order-01.png
--------------------------------------------------------------------------------
/images/kafka-order-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-order-02.png
--------------------------------------------------------------------------------
/images/kafka-order-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-order-1.png
--------------------------------------------------------------------------------
/images/kafka-order-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/kafka-order-2.png
--------------------------------------------------------------------------------
/images/micro-deployment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/micro-deployment.png
--------------------------------------------------------------------------------
/images/mq-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-1.png
--------------------------------------------------------------------------------
/images/mq-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-10.png
--------------------------------------------------------------------------------
/images/mq-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-11.png
--------------------------------------------------------------------------------
/images/mq-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-2.png
--------------------------------------------------------------------------------
/images/mq-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-3.png
--------------------------------------------------------------------------------
/images/mq-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-4.png
--------------------------------------------------------------------------------
/images/mq-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-5.png
--------------------------------------------------------------------------------
/images/mq-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-6.png
--------------------------------------------------------------------------------
/images/mq-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-7.png
--------------------------------------------------------------------------------
/images/mq-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mq-8.png
--------------------------------------------------------------------------------
/images/mysql-master-slave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/mysql-master-slave.png
--------------------------------------------------------------------------------
/images/rabbitmq-message-lose-solution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-message-lose-solution.png
--------------------------------------------------------------------------------
/images/rabbitmq-message-lose.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-message-lose.png
--------------------------------------------------------------------------------
/images/rabbitmq-order-01.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-order-01.png
--------------------------------------------------------------------------------
/images/rabbitmq-order-02.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-order-02.png
--------------------------------------------------------------------------------
/images/rabbitmq-order-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-order-1.png
--------------------------------------------------------------------------------
/images/rabbitmq-order-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/rabbitmq-order-2.png
--------------------------------------------------------------------------------
/images/redis-caching-avalanche-solution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-caching-avalanche-solution.png
--------------------------------------------------------------------------------
/images/redis-caching-avalanche.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-caching-avalanche.png
--------------------------------------------------------------------------------
/images/redis-caching-penetration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-caching-penetration.png
--------------------------------------------------------------------------------
/images/redis-cluster-split-brain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-cluster-split-brain.png
--------------------------------------------------------------------------------
/images/redis-gossip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-gossip.png
--------------------------------------------------------------------------------
/images/redis-junior-inconsistent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-junior-inconsistent.png
--------------------------------------------------------------------------------
/images/redis-master-slave-replication-detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-master-slave-replication-detail.png
--------------------------------------------------------------------------------
/images/redis-master-slave-replication.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-master-slave-replication.png
--------------------------------------------------------------------------------
/images/redis-master-slave.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-master-slave.png
--------------------------------------------------------------------------------
/images/redis-redlock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-redlock.png
--------------------------------------------------------------------------------
/images/redis-single-thread-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/redis-single-thread-model.png
--------------------------------------------------------------------------------
/images/serialize-deserialize.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/serialize-deserialize.png
--------------------------------------------------------------------------------
/images/service-invoke-road.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/service-invoke-road.png
--------------------------------------------------------------------------------
/images/simple-distributed-system-oa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/simple-distributed-system-oa.png
--------------------------------------------------------------------------------
/images/sketch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/sketch.png
--------------------------------------------------------------------------------
/images/where-is-my-offer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/where-is-my-offer.png
--------------------------------------------------------------------------------
/images/zookeeper-active-standby.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-active-standby.png
--------------------------------------------------------------------------------
/images/zookeeper-centralized-storage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-centralized-storage.png
--------------------------------------------------------------------------------
/images/zookeeper-distributed-coordination.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-distributed-coordination.png
--------------------------------------------------------------------------------
/images/zookeeper-distributed-lock-demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-distributed-lock-demo.png
--------------------------------------------------------------------------------
/images/zookeeper-distributed-lock.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-distributed-lock.png
--------------------------------------------------------------------------------
/images/zookeeper-meta-data-manage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/11JJChina/java-interview/22a4f8ba51c326ea39c863da29355c7bf19661f4/images/zookeeper-meta-data-manage.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 让我们同步进阶
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Welcome to Advanced-Java
15 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/offer.md:
--------------------------------------------------------------------------------
1 | [](https://doocs.github.io/advanced-java/#/offer)
2 |
3 |
4 |
5 | ```
6 | 每天起床第一句 先给自己打个气
7 | 每次刷新查成绩 都要说声保佑me
8 | 魔镜魔镜告诉我 我的offer在哪里
9 | 努力 我要努力 我要变成收割机
10 | offer offer
11 | 我要变成收割机
12 | offer offer
13 |
14 | 为了拿下BAT 天天提着一口气
15 | 大厂小厂投简历 刷光面经笔试题
16 | 天生我才难自弃 可惜面试都悲剧
17 | 努力 我要努力 我要变成收割机
18 | Wow
19 |
20 | 在哪里在哪里在哪
21 | 在哪里在哪里在哪
22 | 在哪里在哪里在哪
23 | 在哪里在哪里在哪
24 | 我的offer在哪里
25 | 燃烧我的卡路里
26 |
27 | 拜拜 二次元 综艺直播动作片 言情玄幻宫斗剧
28 | 拿走拿走别客气
29 |
30 | 拜拜 铂金一 戒掉农药戒吃鸡 通宵开黑玩游戏
31 | 别再熬夜伤身体
32 |
33 | 来来 沉住气 专业课本重拾起 牛客网上刷真题
34 | 保温杯里泡枸杞
35 |
36 | 来来 深呼吸 对照镜子做练习
37 | 单面群面全模拟 不拿offer不放弃
38 |
39 | 为了拿下BAT 天天提着一口气
40 | 大厂小厂投简历 刷光面经笔试题
41 | 天生我才难自弃 可惜面试都悲剧
42 | 努力 我要努力 我要变成收割机
43 | Wow
44 |
45 | 在哪里在哪里在哪
46 | 在哪里在哪里在哪
47 | 在哪里在哪里在哪
48 | 在哪里在哪里在哪
49 | 我的offer在哪里
50 | 燃烧我的卡路里
51 |
52 | 拜拜 二次元 综艺直播动作片 言情玄幻宫斗剧
53 | 拿走拿走别客气
54 |
55 | 拜拜 铂金一 戒掉农药戒吃鸡 通宵开黑玩游戏
56 | 别再熬夜伤身体
57 |
58 | 来来 沉住气 专业课本重拾起 牛客网上刷真题
59 | 保温杯里泡枸杞
60 |
61 | 来来 深呼吸 对照镜子做练习
62 | 单面群面全模拟 不拿offer不放弃
63 |
64 | 奇了怪了 小的时候明明是妈妈说
65 | 考上大学就好惹 没烦恼
66 | 直到熟悉的大学的寝室都住不上了 原来毕了业
67 | 没人要 才烦恼
68 | 希望 offer
69 | 钱是多哒
70 | HR
71 | 超nice哒
72 | 不如跟着节奏没在怕的 努努力
73 | 别让校招季卡住你 卡住你
74 | 不拿offer不放弃
75 | 燃烧我的卡路里
76 |
77 | 拜拜 二次元 综艺直播动作片 言情玄幻宫斗剧
78 | 拿走拿走别客气
79 |
80 | 拜拜 铂金一 戒掉农药戒吃鸡 通宵开黑玩游戏
81 | 别再熬夜伤身体
82 |
83 | 来来 沉住气 专业课本重拾起 牛客网上刷真题
84 | 保温杯里泡枸杞
85 |
86 | 来来 深呼吸 对照镜子做练习
87 | 单面群面全模拟 不拿offer不放弃
88 | 不放弃
89 | 燃烧我的卡路里
90 | 不放弃
91 | 燃烧我的卡路里
92 |
93 | 我要变成收割机
94 |
95 |
96 | _.._ ,------------.
97 | ,' `. ( We want you! )
98 | / __) __` \ `-,----------'
99 | ( (`-`(-') ) _.-'
100 | /) \ = / (
101 | /' |--' . \
102 | ( ,---| `-.)__`
103 | )( `-.,--' _`-.
104 | '/,' ( Uu",
105 | (_ , `/,-' )
106 | `.__, : `-'/ /`--'
107 | | `--' |
108 | ` `-._ /
109 | \ (
110 | /\ . \. offer
111 | / |` \ ,-\
112 | / \| .) / \
113 | ( ,'|\ ,' :
114 | | \,`.`--"/ }
115 | `,' \ |,' /
116 | / "-._ `-/ |
117 | "-. "-.,'| ;
118 | / _/["---'""]
119 | : / |"- '
120 | ' | /
121 | ` |
122 |
123 | ```
124 |
125 | [](https://doocs.github.io/advanced-java)
--------------------------------------------------------------------------------
/sw.js:
--------------------------------------------------------------------------------
1 | /* ===========================================================
2 | * docsify sw.js
3 | * ===========================================================
4 | * Copyright 2016 @huxpro
5 | * Licensed under Apache 2.0
6 | * Register service worker.
7 | * ========================================================== */
8 |
9 | const RUNTIME = 'docsify'
10 | const HOSTNAME_WHITELIST = [
11 | self.location.hostname,
12 | 'fonts.gstatic.com',
13 | 'fonts.googleapis.com',
14 | 'unpkg.com'
15 | ]
16 |
17 | // The Util Function to hack URLs of intercepted requests
18 | const getFixedUrl = (req) => {
19 | var now = Date.now()
20 | var url = new URL(req.url)
21 |
22 | // 1. fixed http URL
23 | // Just keep syncing with location.protocol
24 | // fetch(httpURL) belongs to active mixed content.
25 | // And fetch(httpRequest) is not supported yet.
26 | url.protocol = self.location.protocol
27 |
28 | // 2. add query for caching-busting.
29 | // Github Pages served with Cache-Control: max-age=600
30 | // max-age on mutable content is error-prone, with SW life of bugs can even extend.
31 | // Until cache mode of Fetch API landed, we have to workaround cache-busting with query string.
32 | // Cache-Control-Bug: https://bugs.chromium.org/p/chromium/issues/detail?id=453190
33 | if (url.hostname === self.location.hostname) {
34 | url.search += (url.search ? '&' : '?') + 'cache-bust=' + now
35 | }
36 | return url.href
37 | }
38 |
39 | /**
40 | * @Lifecycle Activate
41 | * New one activated when old isnt being used.
42 | *
43 | * waitUntil(): activating ====> activated
44 | */
45 | self.addEventListener('activate', event => {
46 | event.waitUntil(self.clients.claim())
47 | })
48 |
49 | /**
50 | * @Functional Fetch
51 | * All network requests are being intercepted here.
52 | *
53 | * void respondWith(Promise r)
54 | */
55 | self.addEventListener('fetch', event => {
56 | // Skip some of cross-origin requests, like those for Google Analytics.
57 | if (HOSTNAME_WHITELIST.indexOf(new URL(event.request.url).hostname) > -1) {
58 | // Stale-while-revalidate
59 | // similar to HTTP's stale-while-revalidate: https://www.mnot.net/blog/2007/12/12/stale
60 | // Upgrade from Jake's to Surma's: https://gist.github.com/surma/eb441223daaedf880801ad80006389f1
61 | const cached = caches.match(event.request)
62 | const fixedUrl = getFixedUrl(event.request)
63 | const fetched = fetch(fixedUrl, { cache: 'no-store' })
64 | const fetchedCopy = fetched.then(resp => resp.clone())
65 |
66 | // Call respondWith() with whatever we get first.
67 | // If the fetch fails (e.g disconnected), wait for the cache.
68 | // If there’s nothing in cache, wait for the fetch.
69 | // If neither yields a response, return offline pages.
70 | event.respondWith(
71 | Promise.race([fetched.catch(_ => cached), cached])
72 | .then(resp => resp || fetched)
73 | .catch(_ => { /* eat any errors */ })
74 | )
75 |
76 | // Update the cache with the version we fetched (only for ok status)
77 | event.waitUntil(
78 | Promise.all([fetchedCopy, caches.open(RUNTIME)])
79 | .then(([response, cache]) => response.ok && cache.put(event.request, response))
80 | .catch(_ => { /* eat any errors */ })
81 | )
82 | }
83 | })
--------------------------------------------------------------------------------