Merge pull request #2 from doocs/master

update
This commit is contained in:
Hank Li 2019-12-07 09:27:00 +08:00 committed by GitHub
commit 1cd44d5664
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
68 changed files with 1801 additions and 184 deletions

7
CODEOWNERS Normal file
View File

@ -0,0 +1,7 @@
*.md @yanglbme
*.html @yanglbme
LICENSE @yanglbme
.gitignore @yanglbme
docs/ @yanglbme
images/ @yanglbme

176
README.md
View File

@ -2,7 +2,7 @@
[![license](https://badgen.net/github/license/doocs/advanced-java?color=green)](https://github.com/doocs/advanced-java/blob/master/LICENSE)
[![original](https://badgen.net/badge/original/%E4%B8%AD%E5%8D%8E%E7%9F%B3%E6%9D%89/orange)](https://github.com/doocs/advanced-java)
[![notice](https://badgen.net/badge/notice/%E7%BB%B4%E6%9D%83%E8%A1%8C%E5%8A%A8/red)](/docs/from-readers/rights-defending-movement.md)
[![wechat-group](https://badgen.net/badge/chat/%E5%BE%AE%E4%BF%A1%E4%BA%A4%E6%B5%81/138c7b)](https://github.com/doocs/advanced-java/issues/70)
[![wechat-group](https://badgen.net/badge/chat/%E5%BE%AE%E4%BF%A1%E4%BA%A4%E6%B5%81/138c7b)](#公众号)
[![reading](https://badgen.net/badge/books/read%20together/cyan)](https://github.com/doocs/technical-books)
[![coding](https://badgen.net/badge/leetcode/coding%20together/cyan)](https://github.com/doocs/leetcode)
[![doocs](https://badgen.net/badge/organization/join%20us/cyan)](https://doocs.github.io/#/?id=how-to-join)
@ -12,115 +12,161 @@
[![issues](https://badgen.net/github/open-issues/doocs/advanced-java)](https://github.com/doocs/advanced-java/issues)
[![PRs Welcome](https://badgen.net/badge/PRs/welcome/green)](http://makeapullrequest.com)
本项目大部分内容来自中华石杉,版权归作者所有,内容涵盖[高并发](#高并发架构)、[分布式](#分布式系统)、[高可用](#高可用架构)、[微服务](#微服务架构)等领域知识。我([@yanglbme](https://github.com/yanglbme))对这部分知识做了一个系统的整理,方便学习查阅。配合《[大型网站技术架构](https://github.com/doocs/technical-books#architecture)——李智慧》、《[Redis 设计与实现](https://github.com/doocs/technical-books#database)——[黄健宏](https://github.com/huangz1990)》、《[Redis 深度历险](https://github.com/doocs/technical-books#database)——钱文品》、《[亿级流量网站架构核心技术](https://github.com/doocs/technical-books#architecture)——张开涛》食用,[效果更佳](https://doocs.gitee.io/advanced-java/#/offer)。
本项目大部分内容来自中华石杉,版权归作者所有,内容涵盖[高并发](#高并发架构)、[分布式](#分布式系统)、[高可用](#高可用架构)、[微服务](#微服务架构)、[海量数据处理](#海量数据处理)等领域知识。我([@yanglbme](https://github.com/yanglbme))对这部分知识做了一个系统的整理,方便学习查阅。配合《[大型网站技术架构](https://github.com/doocs/technical-books#architecture)——李智慧》、《[Redis 设计与实现](https://github.com/doocs/technical-books#database)——[黄健宏](https://github.com/huangz1990)》、《[Redis 深度历险](https://github.com/doocs/technical-books#database)——钱文品》、《[亿级流量网站架构核心技术](https://github.com/doocs/technical-books#architecture)——张开涛》食用,[效果更佳](https://doocs.github.io/advanced-java/#/docs/extra-page/offer)。
学习之前,先来看看 [Issues 讨论区](https://github.com/doocs/advanced-java/issues/9#issue-394275038)的技术面试官是怎么说的吧。本项目也欢迎各位开发者朋友来[分享自己的一些想法和实践经验](/docs/from-readers/README.md)。
学习之前,先来看看 [Issues 讨论区](https://github.com/doocs/advanced-java/issues/9#issue-394275038)的技术面试官是怎么说的吧。本项目也欢迎各位开发者朋友到 [Issues 讨论区](https://github.com/doocs/advanced-java/issues)分享自己的一些想法和实践经验,参与或加入开源组织请看[这里](https://github.com/doocs/advanced-java/issues/61),你也访问 [GitHub Page](https://doocs.github.io) 详细了解一下 Doocs。
[另外,我还将在这里更新内容,感兴趣的朋友可以进来看看。](/docs/extra-page/subscriptions-for-doocs.md)
## 高并发架构
### [消息队列](/docs/high-concurrency/mq-interview.md)
- [为什么使用消息队列消息队列有什么优点和缺点Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](/docs/high-concurrency/why-mq.md)
- [如何保证消息队列的高可用?](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)
- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](/docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)
- [如何保证消息的可靠性传输?(如何处理消息丢失的问题)](/docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md)
- [如何保证消息的顺序性?](/docs/high-concurrency/how-to-ensure-the-order-of-messages.md)
- [如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](/docs/high-concurrency/mq-time-delay-and-expired-failure.md)
- [如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。](/docs/high-concurrency/mq-design.md)
### [消息队列](./docs/high-concurrency/mq-interview.md)
- [为什么使用消息队列消息队列有什么优点和缺点Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?](./docs/high-concurrency/why-mq.md)
- [如何保证消息队列的高可用?](./docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)
- [如何保证消息不被重复消费?(如何保证消息消费的幂等性)](./docs/high-concurrency/how-to-ensure-that-messages-are-not-repeatedly-consumed.md)
- [如何保证消息的可靠性传输?(如何处理消息丢失的问题)](./docs/high-concurrency/how-to-ensure-the-reliable-transmission-of-messages.md)
- [如何保证消息的顺序性?](./docs/high-concurrency/how-to-ensure-the-order-of-messages.md)
- [如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?](./docs/high-concurrency/mq-time-delay-and-expired-failure.md)
- [如果让你写一个消息队列,该如何进行架构设计啊?说一下你的思路。](./docs/high-concurrency/mq-design.md)
### [搜索引擎](/docs/high-concurrency/es-introduction.md)
- [es 的分布式架构原理能说一下么es 是如何实现分布式的啊)?](/docs/high-concurrency/es-architecture.md)
- [es 写入数据的工作原理是什么啊es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?](/docs/high-concurrency/es-write-query-search.md)
- [es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](/docs/high-concurrency/es-optimizing-query-performance.md)
- [es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](/docs/high-concurrency/es-production-cluster.md)
### [搜索引擎](./docs/high-concurrency/es-introduction.md)
- [es 的分布式架构原理能说一下么es 是如何实现分布式的啊)?](./docs/high-concurrency/es-architecture.md)
- [es 写入数据的工作原理是什么啊es 查询数据的工作原理是什么啊?底层的 lucene 介绍一下呗?倒排索引了解吗?](./docs/high-concurrency/es-write-query-search.md)
- [es 在数据量很大的情况下(数十亿级别)如何提高查询效率啊?](./docs/high-concurrency/es-optimizing-query-performance.md)
- [es 生产集群的部署架构是什么?每个索引的数据量大概有多少?每个索引大概有多少个分片?](./docs/high-concurrency/es-production-cluster.md)
### 缓存
- [在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?](/docs/high-concurrency/why-cache.md)
- [Redis 和 Memcached 有什么区别Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?](/docs/high-concurrency/redis-single-thread-model.md)
- [Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?](/docs/high-concurrency/redis-data-types.md)
- [Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](/docs/high-concurrency/redis-expiration-policies-and-lru.md)
- [如何保证 Redis 高并发、高可用Redis 的主从复制原理能介绍一下么Redis 的哨兵原理能介绍一下么?](/docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md)
- [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](/docs/high-concurrency/redis-persistence.md)
- [Redis 集群模式的工作原理能说一下么在集群模式下Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](/docs/high-concurrency/redis-cluster.md)
- [了解什么是 redis 的雪崩、穿透和击穿Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](/docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
- [如何保证缓存与数据库的双写一致性?](/docs/high-concurrency/redis-consistence.md)
- [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](/docs/high-concurrency/redis-cas.md)
- [生产环境中的 Redis 是怎么部署的?](/docs/high-concurrency/redis-production-environment.md)
- [在项目中缓存是如何使用的?缓存如果使用不当会造成什么后果?](./docs/high-concurrency/why-cache.md)
- [Redis 和 Memcached 有什么区别Redis 的线程模型是什么?为什么单线程的 Redis 比多线程的 Memcached 效率要高得多?](./docs/high-concurrency/redis-single-thread-model.md)
- [Redis 都有哪些数据类型?分别在哪些场景下使用比较合适?](./docs/high-concurrency/redis-data-types.md)
- [Redis 的过期策略都有哪些?手写一下 LRU 代码实现?](./docs/high-concurrency/redis-expiration-policies-and-lru.md)
- [如何保证 Redis 高并发、高可用Redis 的主从复制原理能介绍一下么Redis 的哨兵原理能介绍一下么?](./docs/high-concurrency/how-to-ensure-high-concurrency-and-high-availability-of-redis.md)
- [Redis 的持久化有哪几种方式?不同的持久化机制都有什么优缺点?持久化机制具体底层是如何实现的?](./docs/high-concurrency/redis-persistence.md)
- [Redis 集群模式的工作原理能说一下么在集群模式下Redis 的 key 是如何寻址的?分布式寻址都有哪些算法?了解一致性 hash 算法吗?如何动态增加和删除一个节点?](./docs/high-concurrency/redis-cluster.md)
- [了解什么是 redis 的雪崩、穿透和击穿Redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 Redis 的穿透?](./docs/high-concurrency/redis-caching-avalanche-and-caching-penetration.md)
- [如何保证缓存与数据库的双写一致性?](./docs/high-concurrency/redis-consistence.md)
- [Redis 的并发竞争问题是什么?如何解决这个问题?了解 Redis 事务的 CAS 方案吗?](./docs/high-concurrency/redis-cas.md)
- [生产环境中的 Redis 是怎么部署的?](./docs/high-concurrency/redis-production-environment.md)
### 分库分表
- [为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?](/docs/high-concurrency/database-shard.md)
- [现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?](/docs/high-concurrency/database-shard-method.md)
- [如何设计可以动态扩容缩容的分库分表方案?](/docs/high-concurrency/database-shard-dynamic-expand.md)
- [分库分表之后id 主键如何处理?](/docs/high-concurrency/database-shard-global-id-generate.md)
- [为什么要分库分表(设计高并发系统的时候,数据库层面该如何设计)?用过哪些分库分表中间件?不同的分库分表中间件都有什么优点和缺点?你们具体是如何对数据库如何进行垂直拆分或水平拆分的?](./docs/high-concurrency/database-shard.md)
- [现在有一个未分库分表的系统,未来要分库分表,如何设计才可以让系统从未分库分表动态切换到分库分表上?](./docs/high-concurrency/database-shard-method.md)
- [如何设计可以动态扩容缩容的分库分表方案?](./docs/high-concurrency/database-shard-dynamic-expand.md)
- [分库分表之后id 主键如何处理?](./docs/high-concurrency/database-shard-global-id-generate.md)
### 读写分离
- [如何实现 MySQL 的读写分离MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](/docs/high-concurrency/mysql-read-write-separation.md)
- [如何实现 MySQL 的读写分离MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](./docs/high-concurrency/mysql-read-write-separation.md)
### 高并发系统
- [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md)
- [如何设计一个高并发系统?](./docs/high-concurrency/high-concurrency-design.md)
## 分布式系统
### [面试连环炮](/docs/distributed-system/distributed-system-interview.md)
### [面试连环炮](./docs/distributed-system/distributed-system-interview.md)
### 系统拆分
- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](/docs/distributed-system/why-dubbo.md)
- [为什么要进行系统拆分?如何进行系统拆分?拆分后不用 Dubbo 可以吗?](./docs/distributed-system/why-dubbo.md)
### 分布式服务框架
- [说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](/docs/distributed-system/dubbo-operating-principle.md)
- [Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构PB 知道吗?为什么 PB 的效率是最高的?](/docs/distributed-system/dubbo-serialization-protocol.md)
- [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](/docs/distributed-system/dubbo-load-balancing.md)
- [Dubbo 的 spi 思想是什么?](/docs/distributed-system/dubbo-spi.md)
- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](/docs/distributed-system/dubbo-service-management.md)
- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](/docs/distributed-system/distributed-system-idempotency.md)
- [分布式服务接口请求的顺序性如何保证?](/docs/distributed-system/distributed-system-request-sequence.md)
- [如何自己设计一个类似 Dubbo 的 RPC 框架?](/docs/distributed-system/dubbo-rpc-design.md)
- [说一下 Dubbo 的工作原理?注册中心挂了可以继续通信吗?](./docs/distributed-system/dubbo-operating-principle.md)
- [Dubbo 支持哪些序列化协议?说一下 Hessian 的数据结构PB 知道吗?为什么 PB 的效率是最高的?](./docs/distributed-system/dubbo-serialization-protocol.md)
- [Dubbo 负载均衡策略和集群容错策略都有哪些?动态代理策略呢?](./docs/distributed-system/dubbo-load-balancing.md)
- [Dubbo 的 spi 思想是什么?](./docs/distributed-system/dubbo-spi.md)
- [如何基于 Dubbo 进行服务治理、服务降级、失败重试以及超时重试?](./docs/distributed-system/dubbo-service-management.md)
- [分布式服务接口的幂等性如何设计(比如不能重复扣款)?](./docs/distributed-system/distributed-system-idempotency.md)
- [分布式服务接口请求的顺序性如何保证?](./docs/distributed-system/distributed-system-request-sequence.md)
- [如何自己设计一个类似 Dubbo 的 RPC 框架?](./docs/distributed-system/dubbo-rpc-design.md)
### 分布式锁
- [Zookeeper 都有哪些应用场景?](/docs/distributed-system/zookeeper-application-scenarios.md)
- [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](/docs/distributed-system/distributed-lock-redis-vs-zookeeper.md)
- [Zookeeper 都有哪些应用场景?](./docs/distributed-system/zookeeper-application-scenarios.md)
- [使用 Redis 如何设计分布式锁?使用 Zookeeper 来设计分布式锁可以吗?以上两种分布式锁的实现方式哪种效率比较高?](./docs/distributed-system/distributed-lock-redis-vs-zookeeper.md)
### 分布式事务
- [分布式事务了解吗你们如何解决分布式事务问题的TCC 如果出现网络连不通怎么办XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md)
- [分布式事务了解吗你们如何解决分布式事务问题的TCC 如果出现网络连不通怎么办XA 的一致性如何保证?](./docs/distributed-system/distributed-transaction.md)
### 分布式会话
- [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
- [集群部署时的分布式 Session 如何实现?](./docs/distributed-system/distributed-session.md)
## 高可用架构
- [Hystrix 介绍](/docs/high-availability/hystrix-introduction.md)
- [电商网站详情页系统架构](/docs/high-availability/e-commerce-website-detail-page-architecture.md)
- [Hystrix 线程池技术实现资源隔离](/docs/high-availability/hystrix-thread-pool-isolation.md)
- [Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md)
- [Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md)
- [深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md)
- [基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md)
- [基于本地缓存的 fallback 降级机制](/docs/high-availability/hystrix-fallback.md)
- [深入 Hystrix 断路器执行原理](/docs/high-availability/hystrix-circuit-breaker.md)
- [深入 Hystrix 线程池隔离与接口限流](/docs/high-availability/hystrix-thread-pool-current-limiting.md)
- [基于 timeout 机制为服务接口调用超时提供安全保护](/docs/high-availability/hystrix-timeout.md)
- [Hystrix 介绍](./docs/high-availability/hystrix-introduction.md)
- [电商网站详情页系统架构](./docs/high-availability/e-commerce-website-detail-page-architecture.md)
- [Hystrix 线程池技术实现资源隔离](./docs/high-availability/hystrix-thread-pool-isolation.md)
- [Hystrix 信号量机制实现资源隔离](./docs/high-availability/hystrix-semphore-isolation.md)
- [Hystrix 隔离策略细粒度控制](./docs/high-availability/hystrix-execution-isolation.md)
- [深入 Hystrix 执行时内部原理](./docs/high-availability/hystrix-process.md)
- [基于 request cache 请求缓存技术优化批量商品数据查询接口](./docs/high-availability/hystrix-request-cache.md)
- [基于本地缓存的 fallback 降级机制](./docs/high-availability/hystrix-fallback.md)
- [深入 Hystrix 断路器执行原理](./docs/high-availability/hystrix-circuit-breaker.md)
- [深入 Hystrix 线程池隔离与接口限流](./docs/high-availability/hystrix-thread-pool-current-limiting.md)
- [基于 timeout 机制为服务接口调用超时提供安全保护](./docs/high-availability/hystrix-timeout.md)
### 高可用系统
- 如何设计一个高可用系统?
### 限流
- 如何限流?在工作中是怎么做的?说一下具体的实现?
- [如何限流?在工作中是怎么做的?说一下具体的实现?](/docs/high-concurrency/huifer-how-to-limit-current.md)
### 熔断
- 如何进行熔断?
- 熔断框架都有哪些?具体实现原理知道吗?
- [熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix](/docs/high-availability/sentinel-vs-hystrix.md)
- [熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix](./docs/high-availability/sentinel-vs-hystrix.md)
### 降级
- 如何进行降级?
## 微服务架构
- [微服务架构整个章节内容属额外新增,后续抽空更新,也欢迎读者们参与补充完善](https://github.com/doocs/advanced-java)
- [关于微服务架构的描述](/docs/micro-services/microservices-introduction.md)
- [从单体式架构迁移到微服务架构](/docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md)
- [微服务的事件驱动数据管理](/docs/micro-services/event-driven-data-management-for-microservices.md)
- [关于微服务架构的描述](./docs/micro-services/microservices-introduction.md)
- [从单体式架构迁移到微服务架构](./docs/micro-services/migrating-from-a-monolithic-architecture-to-a-microservices-architecture.md)
- [微服务的事件驱动数据管理](./docs/micro-services/event-driven-data-management-for-microservices.md)
### Spring Cloud 微服务架构
- 什么是微服务?微服务之间是如何独立通讯的?
- [什么是微服务?微服务之间是如何独立通讯的?](/docs/micro-services/huifer-what's-microservice-how-to-communicate.md)
- Spring Cloud 和 Dubbo 有哪些区别?
- Spring Boot 和 Spring Cloud谈谈你对它们的理解
- 什么是服务熔断?什么是服务降级?
- 微服务的优缺点分别是什么?说一下你在项目开发中碰到的坑?
- 你所知道的微服务技术栈都有哪些?
- [你所知道的微服务技术栈都有哪些?](/docs/micro-services/huifer-micro-services-technology-stack%20.md)
- [微服务治理策略](/docs/micro-services/huifer-micro-service-governance.md)
- Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别?
- ......
## 海量数据处理
- [如何从大量的 URL 中找出相同的 URL](./docs/big-data/find-common-urls.md)
- [如何从大量数据中找出高频词?](./docs/big-data/find-top-100-words.md)
- [如何找出某一天访问百度网站最多的 IP](./docs/big-data/find-top-1-ip.md)
- [如何在大量的数据中找出不重复的整数?](./docs/big-data/find-no-repeat-number.md)
- [如何在大量的数据中判断一个数是否存在?](./docs/big-data/find-a-number-if-exists.md)
- [如何查询最热门的查询串?](./docs/big-data/find-hotest-query-string.md)
- [如何统计不同电话号码的个数?](./docs/big-data/count-different-phone-numbers.md)
- [如何从 5 亿个数中找出中位数?](./docs/big-data/find-mid-value-in-500-millions.md)
- [如何按照 query 的频度排序?](./docs/big-data/sort-the-query-strings-by-counts.md)
- [如何找出排名前 500 的数?](./docs/big-data/find-rank-top-500-numbers.md)
---
## 贡献者
感谢以下所有朋友对 [GitHub 技术社区 Doocs](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<a href="https://opencollective.com/doocs/contributors.svg?width=890&button=true"><img src="https://opencollective.com/doocs/contributors.svg?width=890&button=false" /></a>
<!-- ALL-CONTRIBUTORS-LIST:END -->
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

33
docs/big-data/README.md Normal file
View File

@ -0,0 +1,33 @@
# 海量数据处理
- [如何从大量的 URL 中找出相同的 URL](/docs/big-data/find-common-urls.md)
- [如何从大量数据中找出高频词?](/docs/big-data/find-top-100-words.md)
- [如何找出某一天访问百度网站最多的 IP](/docs/big-data/find-top-1-ip.md)
- [如何在大量的数据中找出不重复的整数?](/docs/big-data/find-no-repeat-number.md)
- [如何在大量的数据中判断一个数是否存在?](/docs/big-data/find-a-number-if-exists.md)
- [如何查询最热门的查询串?](/docs/big-data/find-hotest-query-string.md)
- [如何统计不同电话号码的个数?](/docs/big-data/count-different-phone-numbers.md)
- [如何从 5 亿个数中找出中位数?](/docs/big-data/find-mid-value-in-500-millions.md)
- [如何按照 query 的频度排序?](/docs/big-data/sort-the-query-strings-by-counts.md)
- [如何找出排名前 500 的数?](/docs/big-data/find-rank-top-500-numbers.md)
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -0,0 +1,16 @@
## 如何统计不同电话号码的个数?
### 题目描述
已知某个文件内包含一些电话号码,每个号码为 8 位数字,统计不同号码的个数。
### 解答思路
这道题本质还是求解**数据重复**的问题,对于这类问题,一般首先考虑位图法。
对于本题8 位电话号码可以表示的号码个数为 10<sup>8</sup> 个,即 1 亿个。我们每个号码用一个 bit 来表示,则总共需要 1 亿个 bit内存占用约 100M。
**思路如下**
申请一个位图数组,长度为 1 亿,初始化为 0。然后遍历所有电话号码把号码对应的位图中的位置置为 1。遍历完成后如果 bit 为 1则表示这个电话号码在文件中存在否则不存在。bit 值为 1 的数量即为 不同电话号码的个数。
### 方法总结
求解数据重复问题,记得考虑位图法。

View File

@ -0,0 +1,17 @@
## 如何在大量的数据中判断一个数是否存在?
### 题目描述
给定 40 亿个不重复的没排过序的 unsigned int 型整数,然后再给定一个数,如何快速判断这个数是否在这 40 亿个整数当中?
### 解答思路
#### 方法一:分治法
依然可以用分治法解决,方法与前面类似,就不再次赘述了。
#### 方法二:位图法
40 亿个不重复整数,我们用 40 亿个 bit 来表示,初始位均为 0那么总共需要内存4,000,000,000b≈512M。
我们读取这 40 亿个整数,将对应的 bit 设置为 1。接着读取要查询的数查看相应位是否为 1如果为 1 表示存在,如果为 0 表示不存在。
### 方法总结
**判断数字是否存在、判断数字是否重复的问题**,位图法是一种非常高效的方法。

View File

@ -0,0 +1,21 @@
## 如何从大量的 URL 中找出相同的 URL
### 题目描述
给定 a、b 两个文件,各存放 50 亿个 URL每个 URL 各占 64B内存限制是 4G。请找出 a、b 两个文件共同的 URL。
### 解答思路
每个 URL 占 64B那么 50 亿个 URL占用的空间大小约为 320GB。
> 5,000,000,000 * 64B ≈ 5GB * 64 = 320GB
由于内存大小只有 4G因此我们不可能一次性把所有 URL 加载到内存中处理。对于这种类型的题目,一般采用**分治策略**,即:把一个文件中的 URL 按照某个特征划分为多个小文件,使得每个小文件大小不超过 4G这样就可以把这个小文件读到内存中进行处理了。
**思路如下**
首先遍历文件 a对遍历到的 URL 求 `hash(URL) % 1000`,根据计算结果把遍历到的 URL 存储到 a<sub>0</sub>, a<sub>1</sub>, a<sub>2</sub>, ..., a<sub>999</sub>,这样每个大小约为 300MB。使用同样的方法遍历文件 b把文件 b 中的 URL 分别存储到文件 b<sub>0</sub>, b<sub>1</sub>, b<sub>2</sub>, ..., b<sub>999</sub> 中。这样处理过后,所有可能相同的 URL 都在对应的小文件中,即 a<sub>0</sub> 对应 b<sub>0</sub>, ..., a<sub>999</sub> 对应 b<sub>999</sub>,不对应的小文件不可能有相同的 URL。那么接下来我们只需要求出这 1000 对小文件中相同的 URL 就好了。
接着遍历 a<sub>i</sub>( `i∈[0,999]`),把 URL 存储到一个 HashSet 集合中。然后遍历 b<sub>i</sub> 中每个 URL看在 HashSet 集合中是否存在,若存在,说明这就是共同的 URL可以把这个 URL 保存到一个单独的文件中。
### 方法总结
1. 分而治之,进行哈希取余;
2. 对每个子文件进行 HashSet 统计。

View File

@ -0,0 +1,39 @@
## 如何查询最热门的查询串?
### 题目描述
搜索引擎会通过日志文件把用户每次检索使用的所有查询串都记录下来,每个查询床的长度不超过 255 字节。
假设目前有 1000w 个记录(这些查询串的重复度比较高,虽然总数是 1000w但如果除去重复后则不超过 300w 个)。请统计最热门的 10 个查询串,要求使用的内存不能超过 1G。一个查询串的重复度越高说明查询它的用户越多也就越热门。
### 解答思路
每个查询串最长为 255B1000w 个串需要占用 约 2.55G 内存,因此,我们无法将所有字符串全部读入到内存中处理。
#### 方法一:分治法
分治法依然是一个非常实用的方法。
划分为多个小文件,保证单个小文件中的字符串能被直接加载到内存中处理,然后求出每个文件中出现次数最多的 10 个字符串;最后通过一个小顶堆统计出所有文件中出现最多的 10 个字符串。
方法可行,但不是最好,下面介绍其他方法。
#### 方法二HashMap 法
虽然字符串总数比较多,但去重后不超过 300w因此可以考虑把所有字符串及出现次数保存在一个 HashMap 中,所占用的空间为 300w*(255+4)≈777M其中4表示整数占用的4个字节。由此可见1G 的内存空间完全够用。
**思路如下**
首先,遍历字符串,若不在 map 中,直接存入 mapvalue 记为 1若在 map 中,则把对应的 value 加 1这一步时间复杂度 `O(N)`
接着遍历 map构建一个 10 个元素的小顶堆,若遍历到的字符串的出现次数大于堆顶字符串的出现次数,则进行替换,并将堆调整为小顶堆。
遍历结束后,堆中 10 个字符串就是出现次数最多的字符串。这一步时间复杂度 `O(Nlog10)`
#### 方法三:前缀树法
方法二使用了 HashMap 来统计次数当这些字符串有大量相同前缀时可以考虑使用前缀树来统计字符串出现的次数树的结点保存字符串出现次数0 表示没有出现。
**思路如下**
在遍历字符串时,在前缀树中查找,如果找到,则把结点中保存的字符串次数加 1否则为这个字符串构建新结点构建完成后把叶子结点中字符串的出现次数置为 1。
最后依然使用小顶堆来对字符串的出现次数进行排序。
### 方法总结
前缀树经常被用来统计字符串的出现次数。它的另外一个大的用途是字符串查找,判断是否有重复的字符串等。

View File

@ -0,0 +1,71 @@
## 如何从 5 亿个数中找出中位数?
### 题目描述
从 5 亿个数中找出中位数。数据排序后,位置在最中间的数就是中位数。当样本数为奇数时,中位数为 第 `(N+1)/2` 个数;当样本数为偶数时,中位数为 第 `N/2` 个数与第 `1+N/2` 个数的均值。
### 解答思路
如果这道题没有内存大小限制,则可以把所有数读到内存中排序后找出中位数。但是最好的排序算法的时间复杂度都为 `O(NlogN)`。这里使用其他方法。
#### 方法一:双堆法
维护两个堆,一个大顶堆,一个小顶堆。大顶堆中最大的数**小于等于**小顶堆中最小的数;保证这两个堆中的元素个数的差不超过 1。
若数据总数为**偶数**,当这两个堆建好之后,**中位数就是这两个堆顶元素的平均值**。当数据总数为**奇数**时,根据两个堆的大小,**中位数一定在数据多的堆的堆顶**。
```java
class MedianFinder {
private PriorityQueue<Integer> maxHeap;
private PriorityQueue<Integer> minHeap;
/** initialize your data structure here. */
public MedianFinder() {
maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
minHeap = new PriorityQueue<>(Integer::compareTo);
}
public void addNum(int num) {
if (maxHeap.isEmpty() || maxHeap.peek() > num) {
maxHeap.offer(num);
} else {
minHeap.offer(num);
}
int size1 = maxHeap.size();
int size2 = minHeap.size();
if (size1 - size2 > 1) {
minHeap.offer(maxHeap.poll());
} else if (size2 - size1 > 1) {
maxHeap.offer(minHeap.poll());
}
}
public double findMedian() {
int size1 = maxHeap.size();
int size2 = minHeap.size();
return size1 == size2
? (maxHeap.peek() + minHeap.peek()) * 1.0 / 2
: (size1 > size2 ? maxHeap.peek() : minHeap.peek());
}
}
```
> 见 LeetCode No.295https://leetcode.com/problems/find-median-from-data-stream/
以上这种方法,需要把所有数据都加载到内存中。当数据量很大时,就不能这样了,因此,这种方法**适用于数据量较小的情况**。5 亿个数,每个数字占用 4B总共需要 2G 内存。如果可用内存不足 2G就不能使用这种方法了下面介绍另一种方法。
#### 方法二:分治法
分治法的思想是把一个大的问题逐渐转换为规模较小的问题来求解。
对于这道题,顺序读取这 5 亿个数字,对于读取到的数字 num如果它对应的二进制中最高位为 1则把这个数字写到 f1 中,否则写入 f0 中。通过这一步,可以把这 5 亿个数划分为两部分,而且 f0 中的数都大于 f1 中的数(最高位是符号位)。
划分之后,可以非常容易地知道中位数是在 f0 还是 f1 中。假设 f1 中有 1 亿个数,那么中位数一定在 f0 中,且是在 f0 中,从小到大排列的第 1.5 亿个数与它后面的一个数的平均值。
> **提示**5 亿数的中位数是第 2.5 亿与右边相邻一个数求平均值。若 f1 有一亿个数,那么中位数就是 f0 中从第 1.5 亿个数开始的两个数求得的平均值。
对于 f0 可以用次高位的二进制继续将文件一分为二,如此划分下去,直到划分后的文件可以被加载到内存中,把数据加载到内存中以后直接排序,找出中位数。
> **注意**,当数据总数为偶数,如果划分后两个文件中的数据有相同个数,那么中位数就是数据较小的文件中的最大值与数据较大的文件中的最小值的平均值。
### 方法总结
分治法,真香!

View File

@ -0,0 +1,56 @@
## 如何在大量的数据中找出不重复的整数?
### 题目描述
在 2.5 亿个整数中找出不重复的整数。注意:内存不足以容纳这 2.5 亿个整数。
### 解答思路
#### 方法一:分治法
与前面的题目方法类似,先将 2.5 亿个数划分到多个小文件,用 HashSet/HashMap 找出每个小文件中不重复的整数,再合并每个子结果,即为最终结果。
#### 方法二:位图法
**位图**,就是用一个或多个 bit 来标记某个元素对应的值,而键就是该元素。采用位作为单位来存储数据,可以大大节省存储空间。
位图通过使用位数组来表示某些元素是否存在。它可以用于快速查找,判重,排序等。不是很清楚?我先举个小例子。
假设我们要对 `[0,7]` 中的 5 个元素 (6, 4, 2, 1, 5) 进行排序可以采用位图法。0~7 范围总共有 8 个数,只需要 8bit即 1 个字节。首先将每个位都置 0
```
0 0 0 0 0 0 0 0
```
然后遍历 5 个元素,首先遇到 6那么将下标为 6 的位的 0 置为 1接着遇到 4把下标为 4 的位 的 0 置为 1
```
0 0 0 0 1 0 1 0
```
依次遍历,结束后,位数组是这样的:
```
0 1 1 0 1 1 1 0
```
每个为 1 的位,它的下标都表示了一个数:
```
for i in range(8):
if bits[i] == 1:
print(i)
```
这样我们其实就已经实现了排序。
对于整数相关的算法的求解,**位图法**是一种非常实用的算法。假设 int 整数占用 4B即 32bit那么我们可以表示的整数的个数为 2<sup>32</sup>
**那么对于这道题**,我们用 2 个 bit 来表示各个数字的状态:
- 00 表示这个数字没出现过;
- 01 表示这个数字出现过一次(即为题目所找的不重复整数);
- 10 表示这个数字出现了多次。
那么这 2<sup>32</sup> 个整数,总共所需内存为 2<sup>32</sup>*2b=1GB。因此当可用内存超过 1GB 时,可以采用位图法。假设内存满足位图法需求,进行下面的操作:
遍历 2.5 亿个整数,查看位图中对应的位,如果是 00则变为 01如果是 01 则变为 10如果是 10 则保持不变。遍历结束后,查看位图,把对应位是 01 的整数输出即可。
### 方法总结
**判断数字是否重复的问题**,位图法是一种非常高效的方法。

View File

@ -0,0 +1,107 @@
## 如何找出排名前 500 的数?
### 题目描述
有 20 个数组,每个数组有 500 个元素,并且有序排列。如何在这 20*500 个数中找出前 500 的数?
### 解答思路
对于 TopK 问题,最常用的方法是使用堆排序。对本题而言,假设数组降序排列,可以采用以下方法:
首先建立大顶堆,堆的大小为数组的个数,即为 20把每个数组最大的值存到堆中。
接着删除堆顶元素,保存到另一个大小为 500 的数组中,然后向大顶堆插入删除的元素所在数组的下一个元素。
重复上面的步骤,直到删除完第 500 个元素,也即找出了最大的前 500 个数。
> 为了在堆中取出一个数据后,能知道它是从哪个数组中取出的,从而可以从这个数组中取下一个值,可以把数组的指针存放到堆中,对这个指针提供比较大小的方法。
```java
import lombok.Data;
import java.util.Arrays;
import java.util.PriorityQueue;
/**
* @author https://github.com/yanglbme
*/
@Data
public class DataWithSource implements Comparable<DataWithSource> {
/**
* 数值
*/
private int value;
/**
* 记录数值来源的数组
*/
private int source;
/**
* 记录数值在数组中的索引
*/
private int index;
public DataWithSource(int value, int source, int index) {
this.value = value;
this.source = source;
this.index = index;
}
/**
*
* 由于 PriorityQueue 使用小顶堆来实现,这里通过修改
* 两个整数的比较逻辑来让 PriorityQueue 变成大顶堆
*/
@Override
public int compareTo(DataWithSource o) {
return Integer.compare(o.getValue(), this.value);
}
}
class Test {
public static int[] getTop(int[][] data) {
int rowSize = data.length;
int columnSize = data[0].length;
// 创建一个columnSize大小的数组存放结果
int[] result = new int[columnSize];
PriorityQueue<DataWithSource> maxHeap = new PriorityQueue<>();
for (int i = 0; i < rowSize; ++i) {
// 将每个数组的最大一个元素放入堆中
DataWithSource d = new DataWithSource(data[i][0], i, 0);
maxHeap.add(d);
}
int num = 0;
while (num < columnSize) {
// 删除堆顶元素
DataWithSource d = maxHeap.poll();
result[num++] = d.getValue();
if (num >= columnSize) {
break;
}
d.setValue(data[d.getSource()][d.getIndex() + 1]);
d.setIndex(d.getIndex() + 1);
maxHeap.add(d);
}
return result;
}
public static void main(String[] args) {
int[][] data = {
{29, 17, 14, 2, 1},
{19, 17, 16, 15, 6},
{30, 25, 20, 14, 5},
};
int[] top = getTop(data);
System.out.println(Arrays.toString(top)); // [30, 29, 25, 20, 19]
}
}
```
### 方法总结
求 TopK不妨考虑一下堆排序

View File

@ -0,0 +1,14 @@
## 如何找出某一天访问百度网站最多的 IP
### 题目描述
现有海量日志数据保存在一个超大文件中,该文件无法直接读入内存,要求从中提取某天访问百度次数最多的那个 IP。
### 解答思路
这道题只关心某一天访问百度最多的 IP因此可以首先对文件进行一次遍历把这一天访问百度 IP 的相关信息记录到一个单独的大文件中。接下来采用的方法与上一题一样,大致就是先对 IP 进行哈希映射,接着使用 HashMap 统计重复 IP 的次数,最后计算出重复次数最多的 IP。
> 注:这里只需要找出出现次数最多的 IP可以不必使用堆直接用一个变量 max 即可。
### 方法总结
1. 分而治之,进行哈希取余;
2. 使用 HashMap 统计频数;
3. 求解**最大**的 TopN 个,用**小顶堆**;求解**最小**的 TopN 个,用**大顶堆**。

View File

@ -0,0 +1,20 @@
## 如何从大量数据中找出高频词?
### 题目描述
有一个 1GB 大小的文件,文件里每一行是一个词,每个词的大小不超过 16B内存大小限制是 1MB要求返回频数最高的 100 个词(Top 100)。
### 解答思路
由于内存限制,我们依然无法直接将大文件的所有词一次读到内存中。因此,同样可以采用**分治策略**,把一个大文件分解成多个小文件,保证每个文件的大小小于 1MB进而直接将单个小文件读取到内存中进行处理。
**思路如下**
首先遍历大文件对遍历到的每个词x执行 `hash(x) % 5000`,将结果为 i 的词存放到文件 a<sub>i</sub> 中。遍历结束后,我们可以得到 5000 个小文件。每个小文件的大小为 200KB 左右。如果有的小文件大小仍然超过 1MB则采用同样的方式继续进行分解。
接着统计每个小文件中出现频数最高的 100 个词。最简单的方式是使用 HashMap 来实现。其中 key 为词value 为该词出现的频率。具体方法是:对于遍历到的词 x如果在 map 中不存在,则执行 `map.put(x, 1)`;若存在,则执行 `map.put(x, map.get(x)+1)`,将该词频数加 1。
上面我们统计了每个小文件单词出现的频数。接下来,我们可以通过维护一个**小顶堆**来找出所有词中出现频数最高的 100 个。具体方法是:依次遍历每个小文件,构建一个**小顶堆**,堆大小为 100。如果遍历到的词的出现次数大于堆顶词的出现次数则用新词替换堆顶的词然后重新调整为**小顶堆**,遍历结束后,小顶堆上的词就是出现频数最高的 100 个词。
### 方法总结
1. 分而治之,进行哈希取余;
2. 使用 HashMap 统计频数;
3. 求解**最大**的 TopN 个,用**小顶堆**;求解**最小**的 TopN 个,用**大顶堆**。

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -0,0 +1,19 @@
## 如何按照 query 的频度排序?
### 题目描述
有 10 个文件,每个文件大小为 1G每个文件的每一行存放的都是用户的 query每个文件的 query 都可能重复。要求按照 query 的频度排序。
### 解答思路
如果 query 的重复度比较大,可以考虑一次性把所有 query 读入内存中处理;如果 query 的重复率不高,那么可用内存不足以容纳所有的 query这时候就需要采用分治法或其他的方法来解决。
#### 方法一HashMap 法
如果 query 重复率高,说明不同 query 总数比较小,可以考虑把所有的 query 都加载到内存中的 HashMap 中。接着就可以按照 query 出现的次数进行排序。
#### 方法二:分治法
分治法需要根据数据量大小以及可用内存的大小来确定问题划分的规模。对于这道题,可以顺序遍历 10 个文件中的 query通过 Hash 函数 `hash(query) % 10` 把这些 query 划分到 10 个小文件中。之后对每个小文件使用 HashMap 统计 query 出现次数,根据次数排序并写入到零外一个单独文件中。
接着对所有文件按照 query 的次数进行排序,这里可以使用归并排序(由于无法把所有 query 都读入内存,因此需要使用外排序)。
### 方法总结
- 内存若够,直接读入进行排序;
- 内存不够,先划分为小文件,小文件排好序后,整理使用外排序进行归并。

View File

@ -21,4 +21,26 @@
- [分布式事务了解吗你们如何解决分布式事务问题的TCC 如果出现网络连不通怎么办XA 的一致性如何保证?](/docs/distributed-system/distributed-transaction.md)
## 分布式会话
- [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
- [集群部署时的分布式 Session 如何实现?](/docs/distributed-system/distributed-session.md)
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -65,10 +65,6 @@ zk 分布式锁,其实可以做的比较简单,就是某个节点尝试创
```java
/**
* ZooKeeperSession
*
* @author bingo
* @since 2018/11/29
*
*/
public class ZooKeeperSession {
@ -141,11 +137,7 @@ public class ZooKeeperSession {
}
/**
* 建立zk session的watcher
*
* @author bingo
* @since 2018/11/29
*
* 建立 zk session 的 watcher
*/
private class ZooKeeperWatcher implements Watcher {
@ -165,10 +157,6 @@ public class ZooKeeperSession {
/**
* 封装单例的静态内部类
*
* @author bingo
* @since 2018/11/29
*
*/
private static class Singleton {
@ -207,6 +195,7 @@ public class ZooKeeperSession {
也可以采用另一种方式,创建临时顺序节点:
如果有一把锁,被多个人给竞争,此时多个人会排队,第一个拿到锁的人会执行,然后释放锁;后面的每个人都会去监听**排在自己前面**的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 zookeeper 给通知,一旦被通知了之后,就 ok 了,自己就获取到了锁,就可以执行代码了。
```java
public class ZooKeeperDistributedLock implements Watcher {

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -6,3 +6,25 @@
## 项目 Page 页
- [GitHub Page](https://doocs.github.io/advanced-java/#/)
- [Gitee Page](https://doocs.gitee.io/advanced-java/#/)
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -2,8 +2,8 @@
# 互联网 Java 工程师进阶知识完全扫盲
> 本系列知识由 Doocs 开源社区总结发布,内容涵盖高并发、分布式、高可用、微服务等
> 本系列知识由 Doocs 技术社区总结发布,内容涵盖高并发、分布式、高可用、微服务、海量数据处理
[Organization](https://github.com/doocs/doocs.github.io)
[Author](https://github.com/yanglbme)
[Get Started](#互联网-java-工程师进阶知识完全扫盲©)
[社区首页](https://github.com/doocs/doocs.github.io)
[维护者](https://github.com/yanglbme)
[开始学习](#互联网-java-工程师进阶知识完全扫盲©)

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

@ -0,0 +1,46 @@
# Doocs 开源社区的公众号来了
GitHub 开源社区 Doocs 旗下唯一公众号“**Doocs开源社区**”,专注于挖掘 IT 技术知识,助力开发者成长。
<div style="text-align:center;">
<img src="./images/qrcode_for_doocs.jpg" width="200px;"/>
</div>
来成为公众号的首批粉丝吗?一定不会辜负你们的期待。
## 为什么选择公众号?
微信公众号的**原创保护功能**做得比较好,内容阅读也比较方便。
后续我的所有原创文章将第一时间通过微信公众号“Doocs开源社区”发布。如果其他公众号号主希望转载内容请在公众号聊天窗口处发消息与我说明或者直接添加我的个人微信(YLB0109)进行交流。
## 公众号的定位是怎样的?
- **内容原创**:所有技术文章均原创发布,不随意从网上复制粘贴一些文章,确保质量。
- **定期更新**:每周输出 1-2 篇文章,保证一定的更新频率。
<div style="text-align:center;">
<img src="./images/article-demo.png" width="300px;"/>
</div>
## 目前都有哪些文章?
因为刚刚推出公众号,目前有以下几篇文章,来先睹为快吧:
- [阿里又一个 20k+ stars 开源项目诞生,恭喜 fastjson](https://mp.weixin.qq.com/s/RNKDCK2KoyeuMeEs6GUrow)
- [刷掉 90% 候选人的互联网大厂海量数据面试题(附题解 + 方法总结)](https://mp.weixin.qq.com/s/rjGqxUvrEqJNlo09GrT1Dw)
- [好用!期待已久的文本块功能究竟如何在 Java 13 中发挥作用?](https://mp.weixin.qq.com/s/kalGv5T8AZGxTnLHr2wDsA)
- [2019 GitHub 开源贡献排行榜新鲜出炉!微软谷歌领头,阿里跻身前 12](https://mp.weixin.qq.com/s/_q812aGD1b9QvZ2WFI0Qgw)
- [Google 搜索的即时自动补全功能究竟是如何“工作”的?](https://mp.weixin.qq.com/s/YlMISSc3Sn890BzTLytcLA)
- [厉害了,原来 Redisson 这么好用!](https://mp.weixin.qq.com/s/lpZ7eRdImy0MyTEVH68HYw)
- [一文带你搞懂 “缓存策略”](https://mp.weixin.qq.com/s/47A_iXY_nArURwUTPHr2IQ)
- [Java Getter/Setter “防坑指南”](https://mp.weixin.qq.com/s/TZqcAw7NTlcvU-p930-eHA)
- [太棒了GitHub Review 代码能力小升级](https://mp.weixin.qq.com/s/Lok0epqn91Q51ygZo_FLkg)
- [巧用 Redis Hyperloglog轻松统计 UV 数据](https://mp.weixin.qq.com/s/w1r-M6YVvQSfUtzO_xe44Q)
后续将推出一系列原创干货文章,敬请期待。
## 是否有交流群?
有的,目前微信群“**Doocs的技术朋友们**”已经接近 200 号人,如果你希望加入,请通过微信与我联系。
**注意**,群内禁止一切垃圾广告信息,包括小程序助力信息、小游戏、社群推广、公众号推广、支付宝推广码等;交流 GitHub、开发相关可以自由分享一些开发相关知识但不提倡整天水群建议还是多花点时间提升自己。
<div style="text-align:center;">
<img src="./images/wechat-group-for-doocs.png" width="300px;"/>
</div>

View File

@ -1,15 +1,37 @@
# GitHub 开发者参与专区
[Doocs/advanced-java](https://github.com/doocs/advanced-java) 欢迎各位开发朋友们分享自己或他人的实践经验与总结。如果你想参与,请参考[提交注意事项](/docs/from-readers/doocs-advanced-java-attention.md)。感谢 [@jerryldh](https://github.com/jerryldh), [@BigBlackSheep](https://github.com/BigBlackSheep), [@sunyuanpinggithub](https://github.com/sunyuanpinggithub) 等多位朋友的反馈,具体请参考 [#46](https://github.com/doocs/advanced-java/issues/46)。
## Articles
## 文章
- [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
- [示例文章](/docs/from-readers/doocs-advanced-java-attention.md)
## Contributors
This project exists thanks to all the people who contribute.
## 贡献者
感谢以下所有朋友对 [GitHub 技术社区 Doocs](https://github.com/doocs) 所做出的贡献,[参与项目维护请戳这儿](https://doocs.github.io/#/?id=how-to-join)。
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<a href="https://github.com/doocs/advanced-java/graphs/contributors"><img src="https://opencollective.com/advanced-java/contributors.svg?width=890&button=false" /></a>
<a href="https://opencollective.com/doocs/contributors.svg?width=890&button=true"><img src="https://opencollective.com/doocs/contributors.svg?width=890&button=false" /></a>
<!-- ALL-CONTRIBUTORS-LIST:END -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -9,16 +9,21 @@
希望各位朋友都注重**维护他人知识产权,尊重他人劳动成果**,我们共同构建一个健康的知识分享生态圈。
## 抄袭名单列表
注:若以下某些链接失效,说明内容已被抄袭者删除,或者已被所在内容平台进行违规清除。
### 博客
| # | 文章 | 抄袭者 |
|---|---|---|
| 1 | [MySQL 面试题](https://jsbintask.cn/2019/02/17/interview/interview-high-concurrency-design/) | jsbintask |
| 2 | [消息队列面试题](https://blog.51cto.com/13904503/2351522) | Java邵先生 |
| 3 | [高并发架构消息队列面试题解析](https://www.cnblogs.com/yuxiang1/p/10542569.html) | 手留余香-博客园 |
| 4 | [消息中间件面试题:消息中间件的高可用](https://www.jianshu.com/p/92862edc7c51) | jsbintask-简书 |
| 5 | [深入 Hystrix 执行时内部原理](https://www.jianshu.com/p/1a14401e219f) | kevin0016-简书 |
| 1 | [如何保证缓存与数据库的双写一致性](https://blog.51cto.com/14230003/2363051) | Java_老男孩-51CTO |
| 2 | [了解什么是 redis 的雪崩和穿透redis 崩溃之后会怎么样?系统该如何应对这种情况?如何处理 redis 的穿透?](https://blog.csdn.net/chang384915878/article/details/86756536) | 你是我的海啸-CSDN |
| 3 | [深入 Hystrix 线程池隔离与接口限流](https://blog.csdn.net/u014513171/article/details/93461724) | 塔寨村村主任-CSDN |
| 4 | [MySQL 面试题](https://jsbintask.cn/2019/02/17/interview/interview-high-concurrency-design/) | jsbintask |
| 5 | [消息队列面试题](https://blog.51cto.com/13904503/2351522) | Java邵先生 |
| 6 | [高并发架构消息队列面试题解析](https://www.cnblogs.com/yuxiang1/p/10542569.html) | 手留余香-博客园 |
| 7 | [消息中间件面试题:消息中间件的高可用](https://www.jianshu.com/p/92862edc7c51) | jsbintask-简书 |
| 8 | [深入 Hystrix 执行时内部原理](https://www.jianshu.com/p/1a14401e219f) | kevin0016-简书 |
### 公众号

View File

@ -1,15 +1,15 @@
# 高可用架构
- [Hystrix 介绍](/docs/high-availability/hystrix-introduction.md)
- [电商网站详情页系统架构](/docs/high-availability/e-commerce-website-detail-page-architecture.md)
- [Hystrix 线程池技术实现资源隔离](/docs/high-availability/hystrix-thread-pool-isolation.md)
- [Hystrix 信号量机制实现资源隔离](/docs/high-availability/hystrix-semphore-isolation.md)
- [Hystrix 隔离策略细粒度控制](/docs/high-availability/hystrix-execution-isolation.md)
- [深入 Hystrix 执行时内部原理](/docs/high-availability/hystrix-process.md)
- [基于 request cache 请求缓存技术优化批量商品数据查询接口](/docs/high-availability/hystrix-request-cache.md)
- [基于本地缓存的 fallback 降级机制](/docs/high-availability/hystrix-fallback.md)
- [深入 Hystrix 断路器执行原理](/docs/high-availability/hystrix-circuit-breaker.md)
- [深入 Hystrix 线程池隔离与接口限流](/docs/high-availability/hystrix-thread-pool-current-limiting.md)
- [基于 timeout 机制为服务接口调用超时提供安全保护](/docs/high-availability/hystrix-timeout.md)
- [Hystrix 介绍](./hystrix-introduction.md)
- [电商网站详情页系统架构](./e-commerce-website-detail-page-architecture.md)
- [Hystrix 线程池技术实现资源隔离](./hystrix-thread-pool-isolation.md)
- [Hystrix 信号量机制实现资源隔离](./hystrix-semphore-isolation.md)
- [Hystrix 隔离策略细粒度控制](./hystrix-execution-isolation.md)
- [深入 Hystrix 执行时内部原理](./hystrix-process.md)
- [基于 request cache 请求缓存技术优化批量商品数据查询接口](./hystrix-request-cache.md)
- [基于本地缓存的 fallback 降级机制](./hystrix-fallback.md)
- [深入 Hystrix 断路器执行原理](./hystrix-circuit-breaker.md)
- [深入 Hystrix 线程池隔离与接口限流](./hystrix-thread-pool-current-limiting.md)
- [基于 timeout 机制为服务接口调用超时提供安全保护](./hystrix-timeout.md)
## 高可用系统
- 如何设计一个高可用系统?
@ -23,4 +23,26 @@
- [熔断框架如何做技术选型?选用 Sentinel 还是 Hystrix](/docs/high-availability/sentinel-vs-hystrix.md)
## 降级
- 如何进行降级?
- 如何进行降级?
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -3,7 +3,7 @@
### 小型电商网站的商品详情页系统架构
小型电商网站的页面展示采用页面全量静态化的思想。数据库中存放了所有的商品信息,页面静态化系统,将数据填充进静态模板中,形成静态化页面,推入 Nginx 服务器。用户浏览网站页面时,取用一个已经静态化好的 html 页面,直接返回回去,不涉及任何的业务逻辑处理。
![e-commerce-website-detail-page-architecture-1](/images/e-commerce-website-detail-page-architecture-1.png)
![e-commerce-website-detail-page-architecture-1](./images/e-commerce-website-detail-page-architecture-1.png)
下面是页面模板的简单 Demo 。
@ -28,7 +28,7 @@
用户浏览网页时,动态将 Nginx 本地数据渲染到本地 html 模板并返回给用户。
![e-commerce-website-detail-page-architecture-2](/images/e-commerce-website-detail-page-architecture-2.png)
![e-commerce-website-detail-page-architecture-2](./images/e-commerce-website-detail-page-architecture-2.png)
虽然没有直接返回 html 页面那么快,但是因为数据在本地缓存,所以也很快,其实耗费的也就是动态渲染一个 html 页面的性能。如果 html 模板发生了变更,不需要将所有的页面重新静态化,也不需要发送请求,没有网络请求的开销,直接将数据渲染进最新的 html 页面模板后响应即可。

View File

@ -1,17 +1,34 @@
## 深入 Hystrix 断路器执行原理
### RequestVolumeThreshold
### 状态机
Hystrix 断路器有三种状态分别是关闭Closed、打开Open与半开Half-Open三种状态转化关系如下
![image-20191104211642271](./images/hystrix-circuit-breaker-state-machine.png)
1. `Closed` 断路器关闭:调用下游的请求正常通过
2. `Open` 断路器打开:阻断对下游服务的调用,直接走 Fallback 逻辑
3. `Half-Open` 断路器处于半开状态:[SleepWindowInMilliseconds](#circuitBreaker.sleepWindowInMilliseconds)
### [Enabled](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerenabled)
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerEnabled(boolean)
```
控制断路器是否工作,包括跟踪依赖服务调用的健康状况,以及对异常情况过多时是否允许触发断路。默认值 `true`
### [circuitBreaker.requestVolumeThreshold](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerrequestvolumethreshold)
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(int)
```
表示在滑动窗口中,至少有多少个请求,才可能触发断路。
表示在一次统计的**时间滑动窗口中(这个参数也很重要,下面有说到)**,至少经过多少个请求,才可能触发断路,默认值 20。**经过 Hystrix 断路器的流量只有在超过了一定阈值后,才有可能触发断路。**比如说,要求在 10s 内经过断路器的流量必须达到 20 个,而实际经过断路器的请求有 19 个,即使这 19 个请求全都失败,也不会去判断要不要断路。
Hystrix 经过断路器的流量超过了一定的阈值,才有可能触发断路。比如说,要求在 10s 内经过断路器的流量必须达到 20 个,而实际经过断路器的流量才 10 个,那么根本不会去判断要不要断路。
### ErrorThresholdPercentage
### [circuitBreaker.errorThresholdPercentage](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitBreaker.errorThresholdPercentage)
```java
HystrixCommandProperties.Setter()
@ -20,29 +37,18 @@ HystrixCommandProperties.Setter()
表示异常比例达到多少,才会触发断路,默认值是 50(%)。
如果断路器统计到的异常调用的占比超过了一定的阈值,比如说在 10s 内,经过断路器的流量达到了 30 个,同时其中异常访问的数量也达到了一定的比例,比如 60% 的请求都是异常(报错 / 超时 / reject就会开启断路。
### SleepWindowInMilliseconds
#### [circuitBreaker.sleepWindowInMilliseconds](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakersleepwindowinmilliseconds)
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerSleepWindowInMilliseconds(int)
```
断路开启,也就是由 close 转换到 open 状态close -> open。那么之后在 `SleepWindowInMilliseconds` 时间内,所有经过该断路器的请求全部都会被断路,不调用后端服务,直接走 fallback 降级机制
断路器状态由 Close 转换到 Open在之后 `SleepWindowInMilliseconds` 时间内,所有经过该断路器的请求会被断路,不调用后端服务,直接走 Fallback 降级机制,默认值 5000(ms)
而在该参数时间过后,断路器会变为 `half-open` 半开闭状态,尝试让一条请求经过断路器,看能不能正常调用。如果调用成功了,那么就自动恢复,断路器转为 close 状态。
而在该参数时间过后,断路器会变为 `Half-Open` 半开闭状态,尝试让一条请求经过断路器,看能不能正常调用。如果调用成功了,那么就自动恢复,断路器转为 Close 状态。
### Enabled
```java
HystrixCommandProperties.Setter()
.withCircuitBreakerEnabled(boolean)
```
控制是否允许断路器工作,包括跟踪依赖服务调用的健康状况,以及对异常情况过多时是否允许触发断路。默认值是 `true`
### ForceOpen
### [ForceOpen](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerforceopen)
```java
HystrixCommandProperties.Setter()
@ -51,7 +57,7 @@ HystrixCommandProperties.Setter()
如果设置为 true 的话,直接强迫打开断路器,相当于是手动断路了,手动降级,默认值是 `false`
### ForceClosed
### [ForceClosed](https://github.com/Netflix/Hystrix/wiki/Configuration#circuitbreakerforceclosed)
```java
HystrixCommandProperties.Setter()
@ -60,6 +66,14 @@ HystrixCommandProperties.Setter()
如果设置为 true直接强迫关闭断路器相当于手动停止断路了手动升级默认值是 `false`
### Metrics 统计器
与 Hystrix 断路器紧密协作的,还有另一个重要组件 —— **统计器Metrics**。统计器中最重要的参数要数滑动窗口([metrics.rollingStats.timeInMilliseconds](https://github.com/Netflix/Hystrix/wiki/Configuration#metricsrollingstatstimeinmilliseconds))以及桶([metrics.rollingStats.numBuckets](https://github.com/Netflix/Hystrix/wiki/Configuration#metricsrollingstatsnumbuckets))了,这里引用[一段博文](https://zhenbianshu.github.io/2018/09/hystrix_configuration_analysis.html)来解释滑动窗口(默认值是 10000 ms
> 一位乘客坐在正在行驶的列车的靠窗座位上,列车行驶的公路两侧种着一排挺拔的白杨树,随着列车的前进,路边的白杨树迅速从窗口滑过。我们用每棵树来代表一个请求,用列车的行驶代表时间的流逝,那么,列车上的这个窗口就是一个典型的滑动窗口,这个乘客能通过窗口看到的白杨树就是 Hystrix 要统计的数据。
Hystrix 并不是只要有一条请求经过就去统计,而是将整个滑动窗口均分为 numBuckets 份,时间每经过一份就去统计一次。**在经过一个时间窗口后,才会判断断路器状态要不要开启,请看下面的例子。**
## 实例 Demo
### HystrixCommand 配置参数
@ -178,4 +192,9 @@ ProductInfo(id=1, name=iphone7手机, price=5599.0, pictureList=a.jpg,b.jpg, spe
而是直接走降级逻辑,调用 getFallback() 执行。
休眠了 3s 后,我们在之后的 70 次请求中,都传入 productId 为 1。由于我们前面设置了 3000ms 过后断路器变为 `half-open` 状态。因此 Hystrix 会尝试执行请求,发现成功了,那么断路器关闭,之后的所有请求也都能正常调用了。
休眠了 3s 后,我们在之后的 70 次请求中,都传入 productId 为 1。由于我们前面设置了 3000ms 过后断路器变为 `half-open` 状态。因此 Hystrix 会尝试执行请求,发现成功了,那么断路器关闭,之后的所有请求也都能正常调用了。
### 参考内容
1. [Hystrix issue 1459](https://github.com/Netflix/Hystrix/issues/1459)
2. [Hystrix Metrics](https://github.com/Netflix/Hystrix/wiki/Configuration#metrics)

View File

@ -17,7 +17,7 @@ HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolat
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE)
```
线程池机制,每个 command 运行在一个线程中限流是通过线程池的大小来控制的信号量机制command 是运行在调用线程中,通过信号量的容量来进行限流。
线程池机制,每个 command 运行在一个线程中限流是通过线程池的大小来控制的信号量机制command 是运行在调用线程中(也就是 Tomcat 的线程池),通过信号量的容量来进行限流。
如何在线程池和信号量之间做选择?
@ -33,7 +33,7 @@ HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolat
每一个 command都可以设置一个自己的名称 command key同时可以设置一个自己的组 command group。
```java
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
public CommandHelloWorld(String name) {
super(cachedSetter);
@ -49,8 +49,8 @@ ThreadPoolKey 代表了一个 HystrixThreadPool用来进行统一监控、统
如果不想直接用 command group也可以手动设置 ThreadPool 的名称。
```java
private static final Setter cachedSetter = Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"));
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"));
public CommandHelloWorld(String name) {
super(cachedSetter);
@ -59,13 +59,13 @@ public CommandHelloWorld(String name) {
```
### command key & command group & command thread pool
**command key** ,代表了一类 command一般来说代表了底层的依赖服务的一个接口。
**command key** ,代表了一类 command一般来说代表了下游依赖服务的某个接口。
**command group** ,代表了某一个底层的依赖服务,这是很合理的,一个依赖服务可能会暴露出来多个接口,每个接口就是一个 command key。command group 在逻辑上去组织起来一堆 command key 的调用、统计信息、成功次数、timeout 超时次数、失败次数等,可以看到某一个服务整体的一些访问情况。一般来说,**推荐**根据一个服务区划分出一个线程池command key 默认都是属于同一个线程池的。
**command group** ,代表了某一个下游依赖服务,这是很合理的,一个依赖服务可能会暴露出来多个接口,每个接口就是一个 command key。command group 在逻辑上对一堆 command key 的调用次数、成功次数、timeout 次数、失败次数等进行统计,可以看到某一个服务整体的一些访问情况。**一般来说推荐根据一个服务区划分出一个线程池command key 默认都是属于同一个线程池的。**
比如说你以一个服务为粒度,估算出来这个服务每秒的所有接口加起来的整体 `QPS` 在 100 左右,你调用这个服务,当前这个服务部署了 10 个服务实例,每个服务实例上,其实用这个 command group 对应这个服务,给一个线程池,量大概在 10 个左右就可以了,你对整个服务的整体的访问 QPS 就大概在每秒 100 左右
比如说有一个服务 A你估算出来服务 A 每秒所有接口加起来的整体 `QPS` 在 100 左右,你有一个服务 B 去调用服务 A。你的服务 B 部署了 10 个实例,每个实例上,用 command group 去对应下游服务 A。给一个线程池量大概是 10 就可以了,这样服务 B 对服务 A 整体的访问 QPS 就大概是每秒 100 了
但是,如果说 command group 对应了一个服务,而这个服务暴露出来的几个接口,访问量很不一样,差异非常之大。你可能就希望在这个服务 command group 内部,包含对应多个接口的 command key做一些细粒度的资源隔离。就是说对同一个服务的不同接口使用不同的线程池。
但是,如果说 command group 对应了一个服务,而这个服务暴露出来的几个接口,访问量很不一样,差异非常之大。你可能就希望在这个服务对应 command group 内部,包含对应多个接口的 command key做一些细粒度的资源隔离。**就是说,希望对同一个服务的不同接口,使用不同的线程池。**
```
command key -> command group
@ -86,7 +86,7 @@ HystrixThreadPoolProperties.Setter().withCoreSize(int value);
### queueSizeRejectionThreshold
如果说线程池中的 10 个线程都在工作中,没有空闲的线程来做其它的事情,此时再有请求过来,会先进入队列积压。如果说队列积压满了,再有请求过来,就直接 reject拒绝请求执行 fallback 降级的逻辑,快速返回。
![hystrix-thread-pool-queue](/images/hystrix-thread-pool-queue.png)
![hystrix-thread-pool-queue](./images/hystrix-thread-pool-queue.png)
控制 queue 满了之后 reject 的 threshold因为 maxQueueSize 不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小。

View File

@ -6,9 +6,9 @@
Hystrix 可以让我们在分布式系统中对服务间的调用进行控制,加入一些**调用延迟**或者**依赖故障**的**容错机制**。
Hystrix 通过将依赖服务进行**资源隔离**进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延同时Hystrix 还提供故障时的 fallback 降级机制。
Hystrix 通过将依赖服务进行**资源隔离**,进而阻止某个依赖服务出现故障时在整个系统所有的依赖服务调用中进行蔓延;同时 Hystrix 还提供故障时的 fallback 降级机制。
总而言之Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。
**总而言之Hystrix 通过这些方法帮助我们提升分布式系统的可用性和稳定性。**
### Hystrix 的历史
Hystrix 是高可用性保障的一个框架。Netflix可以认为是国外的优酷或者爱奇艺之类的视频网站的 API 团队从 2011 年开始做一些提升系统可用性和稳定性的工作Hystrix 就是从那时候开始发展出来的。
@ -31,9 +31,9 @@ Hystrix 是高可用性保障的一个框架。Netflix可以认为是国外
有这样一个分布式系统,服务 A 依赖于服务 B服务 B 依赖于服务 C/D/E。在这样一个成熟的系统内比如说最多可能只有 100 个线程资源。正常情况下40 个线程并发调用服务 C各 30 个线程并发调用 D/E。
调用服务 C只需要 20ms现在因为服务 C 故障了,比如延迟,或者挂了,此时线程会 hang 住 2s 左右。40 个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C同样也会被卡住。这样导致服务 B 的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。服务 A 也挂。
调用服务 C只需要 20ms现在因为服务 C 故障了,比如延迟,或者挂了,此时线程会 hang 住 2s 左右。40 个线程全部被卡住,由于请求不断涌入,其它的线程也用来调用服务 C同样也会被卡住。这样导致服务 B 的线程资源被耗尽,无法接收新的请求,甚至可能因为大量线程不断的运转,导致自己宕机。这种影响势必会蔓延至服务 A导致服务 A 也跟着
![service-invoke-road](/images/service-invoke-road.png)
![service-invoke-road](./images/service-invoke-road.png)
Hystrix 可以对其进行资源隔离,比如限制服务 B 只有 40 个线程调用服务 C。当此 40 个线程被 hang 住时,其它 60 个线程依然能正常调用工作。从而确保整个系统不会被拖垮。

View File

@ -11,7 +11,7 @@
这里是整个 8 大步骤的流程图,我会对每个步骤进行细致的讲解。学习的过程中,对照着这个流程图,相信思路会比较清晰。
![hystrix-process](/images/hystrix-process.png)
![hystrix-process](./images/new-hystrix-process.jpg)
### 步骤一:创建 command
一个 HystrixCommand 或 HystrixObservableCommand 对象,代表了对某个依赖服务发起的一次请求或者调用。创建的时候,可以在构造函数中传入任何需要的参数。
@ -64,9 +64,7 @@ final Future<R> delegate = toObservable().toBlocking().toFuture();
也就是说,先通过 toObservable() 获得 Future 对象,然后调用 Future 的 get() 方法。那么,其实无论是哪种方式执行 command最终都是依赖于 toObservable() 去执行的。
![hystrix-process](/images/hystrix-process.png)
### 步骤三:检查是否开启缓存
### 步骤三:检查是否开启缓存(不太常用)
从这一步开始,就进入到 Hystrix 底层运行原理啦,看一下 Hystrix 一些更高级的功能和特性。
如果这个 command 开启了请求缓存 Request Cache而且这个调用的结果在缓存中存在那么直接从缓存中返回结果。否则继续往后的步骤。
@ -122,8 +120,6 @@ observable.subscribe(new Observer<ProductInfo>() {
如果没有 timeout也正常执行的话那么调用线程就会拿到一些调用依赖服务获取到的结果然后 Hystrix 也会做一些 logging 记录和 metric 度量统计。
![hystrix-process](/images/hystrix-process.png)
### 步骤七:断路健康检查
Hystrix 会把每一个依赖服务的调用成功、失败、Reject、Timeout 等事件发送给 circuit breaker 断路器。断路器就会对这些事件的次数进行统计,根据异常事件发生的比例来决定是否要进行断路(熔断)。如果打开了断路器,那么在接下来一段时间内,会直接断路,返回降级结果。

View File

@ -9,7 +9,7 @@ Hystrix command 执行时 8 大步骤第三步,就是检查 Request cache 是
举个栗子。比如说我们在一次请求上下文中,请求获取 productId 为 1 的数据,第一次缓存中没有,那么会从商品服务中获取数据,返回最新数据结果,同时将数据缓存在内存中。后续同一次请求上下文中,如果还有获取 productId 为 1 的数据的请求,直接从缓存中取就好了。
![hystrix-request-cache](/images/hystrix-request-cache.png)
![hystrix-request-cache](./images/hystrix-request-cache.png)
HystrixCommand 和 HystrixObservableCommand 都可以指定一个缓存 key然后 Hystrix 会自动进行缓存,接着在同一个 request context 内,再次访问的话,就会直接取用缓存。

View File

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

View File

@ -1,7 +1,7 @@
## 深入 Hystrix 线程池隔离与接口限流
前面讲了 Hystrix 的 request cache 请求缓存、fallback 优雅降级、circuit breaker 断路器快速熔断,这一讲,我们来详细说说 Hystrix 的线程池隔离与接口限流。
![hystrix-process](/images/hystrix-process.png)
![hystrix-process](./images/hystrix-process.png)
Hystrix 通过判断线程池或者信号量是否已满,超出容量的请求,直接 Reject 走降级,从而达到限流的作用。
@ -12,7 +12,7 @@ Hystrix 采用了 Bulkhead Partition 舱壁隔离技术,来将外部依赖进
**舱壁隔离**,是说将船体内部空间区隔划分成若干个隔舱,一旦某几个隔舱发生破损进水,水流不会在其间相互流动,如此一来船舶在受损时,依然能具有足够的浮力和稳定性,进而减低立即沉船的危险。
![bulkhead-partition](/images/bulkhead-partition.jpg)
![bulkhead-partition](./images/bulkhead-partition.jpg)
Hystrix 对每个外部依赖用一个单独的线程池,这样的话,如果对那个外部依赖调用延迟很严重,最多就是耗尽那个依赖自己的线程池而已,不会影响其他的依赖调用。

View File

@ -1,9 +1,9 @@
## 基于 Hystrix 线程池技术实现资源隔离
上一讲提到,如果从 Nginx 开始缓存都失效了Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做个讨论),有可能出现调用延时而把缓存服务资源耗尽的情况。这里,我们就来说说,怎么通过 Hystrix 线程池技术实现资源隔离。
[上一讲](./e-commerce-website-detail-page-architecture.md)提到,如果从 Nginx 开始缓存都失效了Nginx 会直接通过缓存服务调用商品服务获取最新商品数据(我们基于电商项目做个讨论),有可能出现调用延时而把缓存服务资源耗尽的情况。这里,我们就来说说,怎么通过 Hystrix 线程池技术实现资源隔离。
资源隔离,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内,不会去用其它资源了,这就叫资源隔离。哪怕对这个依赖服务,比如说商品服务,现在同时发起的调用量已经到了 1000但是线程池内就 10 个线程,最多就只会用这 10 个线程去执行,不会说,对商品服务的请求,因为接口调用延时,将 tomcat 内部所有的线程资源全部耗尽。
资源隔离,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内,不会去用其它资源了,这就叫资源隔离。哪怕对这个依赖服务,比如说商品服务,现在同时发起的调用量已经到了 1000但是分配给商品服务线程池内就 10 个线程,最多就只会用这 10 个线程去执行。不会因为对商品服务调用的延迟,将 Tomcat 内部所有的线程资源全部耗尽。
Hystrix 进行资源隔离,其实是提供了一个抽象,叫做 command。这也是 Hystrix 最最基本的资源隔离技术。
Hystrix 进行资源隔离,其实是提供了一个抽象,叫做 Command。这也是 Hystrix 最最基本的资源隔离技术。
### 利用 HystrixCommand 获取单条数据
我们通过将调用商品服务的操作封装在 HystrixCommand 中,限定一个 key比如下面的 `GetProductInfoCommandGroup`,在这里我们可以简单认为这是一个线程池,每次调用商品服务,就只会用该线程池中的资源,不会再去用其它线程资源了。
@ -28,7 +28,7 @@ public class GetProductInfoCommand extends HystrixCommand<ProductInfo> {
}
```
我们在缓存服务接口中,根据 productId 创建 command 并执行,获取到商品数据。
我们在缓存服务接口中,根据 productId 创建 Command 并执行,获取到商品数据。
```java
@RequestMapping("/getProductInfo")
@ -110,6 +110,6 @@ public String getProductInfos(String productIds) {
我们回过头来,看看 Hystrix 线程池技术是如何实现资源隔离的。
![hystrix-thread-pool-isolation](/images/hystrix-thread-pool-isolation.png)
![hystrix-thread-pool-isolation](./images/hystrix-thread-pool-isolation.png)
从 Nginx 开始,缓存都失效了,那么 Nginx 通过缓存服务去调用商品服务。缓存服务默认的线程大小是 10 个,最多就只有 10 个线程去调用商品服务的接口。即使商品服务接口故障了,最多就只有 10 个线程会 hang 死在调用商品服务接口的路上,缓存服务的 tomcat 内其它的线程还是可以用来调用其它的服务,干其它的事情。
从 Nginx 开始,缓存都失效了,那么 Nginx 通过缓存服务去调用商品服务。缓存服务默认的线程大小是 10 个,最多就只有 10 个线程去调用商品服务的接口。即使商品服务接口故障了,最多就只有 10 个线程会 hang 死在调用商品服务接口的路上,缓存服务的 Tomcat 内其它的线程还是可以用来调用其它的服务,干其它的事情。

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -37,4 +37,25 @@
- [如何实现 MySQL 的读写分离MySQL 主从复制原理是啥?如何解决 MySQL 主从同步的延时问题?](/docs/high-concurrency/mysql-read-write-separation.md)
## 高并发系统
- [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md)
- [如何设计一个高并发系统?](/docs/high-concurrency/high-concurrency-design.md)
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -10,7 +10,7 @@
我先给大家抛出来一个场景。
假如我们现在是一个小创业公司(或者是一个 BAT 公司刚兴起的一个新部门),现在注册用户就 20 万,每天活跃用户就 1 万,每天单表数据量就 1000然后高峰期每秒钟并发请求最多就 10。天就这种系统随便找一个有几年工作经验的然后带几个刚培训出来的随便干干都可以。
假如我们现在是一个小创业公司(或者是一个 BAT 公司刚兴起的一个新部门),现在注册用户就 20 万,每天活跃用户就 1 万,每天单表数据量就 1000然后高峰期每秒钟并发请求最多就 10我的天,就这种系统,随便找一个有几年工作经验的,然后带几个刚培训出来的,随便干干都可以。
结果没想到我们运气居然这么好,碰上个 CEO 带着我们走上了康庄大道,业务发展迅猛,过了几个月,注册用户数达到了 2000 万!每天活跃用户数 100 万!每天单表数据量 10 万条!高峰期每秒最大请求达到 1000同时公司还顺带着融资了两轮进账了几个亿人民币啊公司估值达到了惊人的几亿美金这是小独角兽的节奏

View File

@ -7,7 +7,7 @@
## 面试题剖析
我举个例子,我们以前做过一个 mysql `binlog` 同步的系统,压力还是非常大的,日同步数据要达到上亿,就是说数据从一个 mysql 库原封不动地同步到另一个 mysql 库里面去mysql -> mysql。常见的一点在于说比如大数据 team就需要同步一个 mysql 库过来,对公司的业务系统的数据做各种复杂的操作。
你在 mysql 里增删改一条数据,对应出来了增删改 3 条 `binlog` 日志,接着这三条 `binlog` 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你是换了顺序给执行成删除、修改、增加,不全错了么。
你在 mysql 里增删改一条数据,对应出来了增删改 3 条 `binlog` 日志,接着这三条 `binlog` 发送到 MQ 里面,再消费出来依次执行,起码得保证人家是按照顺序来的吧?不然本来是:增加、修改、删除;你是换了顺序给执行成删除、修改、增加,不全错了么。
本来这个数据同步过来,应该最后这个数据被删除了;结果你搞错了这个顺序,最后这个数据保留下来了,数据同步就出错了。
@ -29,4 +29,4 @@
- 一个 topic一个 partition一个 consumer内部单线程消费单线程吞吐量太低一般不会用这个。
- 写 N 个内存 queue具有相同 key 的数据都到同一个内存 queue然后对于 N 个线程,每个线程分别消费一个内存 queue 即可,这样就能保证顺序性。
![kafka-order-02](/images/kafka-order-02.png)
![kafka-order-02](/images/kafka-order-02.png)

View File

@ -0,0 +1,362 @@
# 如何限流?在工作中是怎么做的?说一下具体的实现?
- Author: [HuiFer](https://github.com/huifer)
- Description: 该文简单介绍限流相关技术以及实现
## 什么是限流
> 限流可以认为服务降级的一种,限流就是限制系统的输入和输出流量已达到保护系统的目的。一般来说系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到的需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。比如:延迟处理,拒绝处理,或者部分拒绝处理等等。
## 限流方法
### 计数器
#### 实现方式
- 控制单位时间内的请求数量
```java
import java.util.concurrent.atomic.AtomicInteger;
public class Counter {
/**
* 最大访问数量
*/
private final int limit = 10;
/**
* 访问时间差
*/
private final long timeout = 1000;
/**
* 请求时间
*/
private long time;
/**
* 当前计数器
*/
private AtomicInteger reqCount = new AtomicInteger(0);
public boolean limit() {
long now = System.currentTimeMillis();
if (now < time + timeout) {
// 单位时间内
reqCount.addAndGet(1);
return reqCount.get() <= limit;
} else {
// 超出单位时间
time = now;
reqCount = new AtomicInteger(0);
return true;
}
}
}
```
- 劣势
- 假设在 00:01 时发生一个请求,在 00:01-00:58 之间不在发送请求,在 00:59 时发送剩下的所有请求 `n-1` (n为限流请求数量),在下一分钟的 00:01 发送n个请求,这样在2秒钟内请求到达了 `2n - 1` 个.
- 设每分钟请求数量为60个,每秒可以处理1个请求,用户在 00:59 发送 60 个请求,在 01:00 发送 60 个请求 此时2秒钟有120个请求(每秒60个请求),远远大于了每秒钟处理数量的阈值
### 滑动窗口
#### 实现方式
- 滑动窗口是对计数器方式的改进,增加一个时间粒度的度量单位
- 把一分钟分成若干等分(6份,每份10秒), 在每一份上设置独立计数器,在 00:00-00:09 之间发生请求计数器累加1.当等分数量越大限流统计就越详细
```java
package com.example.demo1.service;
import java.util.Iterator;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.IntStream;
public class TimeWindow {
private ConcurrentLinkedQueue<Long> queue = new ConcurrentLinkedQueue<Long>();
/**
* 间隔秒数
*/
private int seconds;
/**
* 最大限流
*/
private int max;
public TimeWindow(int max, int seconds) {
this.seconds = seconds;
this.max = max;
/**
* 永续线程执行清理queue 任务
*/
new Thread(() -> {
while (true) {
try {
// 等待 间隔秒数-1 执行清理操作
Thread.sleep((seconds - 1) * 1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
clean();
}
}).start();
}
public static void main(String[] args) throws Exception {
final TimeWindow timeWindow = new TimeWindow(10, 1);
// 测试3个线程
IntStream.range(0, 3).forEach((i) -> {
new Thread(() -> {
while (true) {
try {
Thread.sleep(new Random().nextInt(20) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
timeWindow.take();
}
}).start();
});
}
/**
* 获取令牌,并且添加时间
*/
public void take() {
long start = System.currentTimeMillis();
try {
int size = sizeOfValid();
if (size > max) {
System.err.println("超限");
}
synchronized (queue) {
if (sizeOfValid() > max) {
System.err.println("超限");
System.err.println("queue中有 " + queue.size() + " 最大数量 " + max);
}
this.queue.offer(System.currentTimeMillis());
}
System.out.println("queue中有 " + queue.size() + " 最大数量 " + max);
}
}
public int sizeOfValid() {
Iterator<Long> it = queue.iterator();
Long ms = System.currentTimeMillis() - seconds * 1000;
int count = 0;
while (it.hasNext()) {
long t = it.next();
if (t > ms) {
// 在当前的统计时间范围内
count++;
}
}
return count;
}
/**
* 清理过期的时间
*/
public void clean() {
Long c = System.currentTimeMillis() - seconds * 1000;
Long tl = null;
while ((tl = queue.peek()) != null && tl < c) {
System.out.println("清理数据");
queue.poll();
}
}
}
```
### Leaky Bucket 漏桶
#### 实现方式
- 规定固定容量的桶,有水进入,有水流出. 对于流进的水我们无法估计进来的数量、速度,对于流出的水我们可以控制速度.
```java
public class LeakBucket {
/**
* 时间
*/
private long time;
/**
* 总量
*/
private Double total;
/**
* 水流出去的速度
*/
private Double rate;
/**
* 当前总量
*/
private Double nowSize;
public boolean limit() {
long now = System.currentTimeMillis();
nowSize = Math.max(0, (nowSize - (now - time) * rate));
time = now;
if ((nowSize + 1) < total) {
nowSize++;
return true;
} else {
return false;
}
}
}
```
### Token Bucket 令牌桶
#### 实现方式
- 规定固定容量的桶,token 以固定速度往桶内填充,当桶满时 token 不会被继续放入,每过来一个请求把 token 从桶中移除,如果桶中没有 token 不能请求
```java
public class TokenBucket {
/**
* 时间
*/
private long time;
/**
* 总量
*/
private Double total;
/**
* token 放入速度
*/
private Double rate;
/**
* 当前总量
*/
private Double nowSize;
public boolean limit() {
long now = System.currentTimeMillis();
nowSize = Math.min(total, nowSize + (now - time) * rate);
time = now;
if (nowSize < 1) {
// 桶里没有token
return false;
} else {
// 存在token
nowSize -= 1;
return true;
}
}
}
```
## 工作中的使用
### spring cloud gateway
- spring cloud gateway 默认使用redis进行限流,笔者一般只是修改修改参数属于拿来即用.并没有去从头实现上述那些算法.
```xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
```
```yaml
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: lb://pigx-upms
order: 10000
predicates:
- Path=/admin/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 令牌桶的容积
redis-rate-limiter.burstCapacity: 3 # 流速 每秒
key-resolver: "#{@remoteAddrKeyResolver}" #SPEL表达式去的对应的bean
- StripPrefix=1
```
```java
@Bean
KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
```
### sentinel
- 通过配置来控制每个url的流量
```xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
```
```yaml
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
sentinel:
transport:
dashboard: localhost:8080
port: 8720
datasource:
ds:
nacos:
server-addr: localhost:8848
dataId: spring-cloud-sentinel-nacos
groupId: DEFAULT_GROUP
rule-type: flow
namespace: xxxxxxxx
```
- 配置内容在nacos上进行编辑
```json
[
{
"resource": "/hello",
"limitApp": "default",
"grade": 1,
"count": 1,
"strategy": 0,
"controlBehavior": 0,
"clusterMode": false
}
]
```
- resource资源名即限流规则的作用对象。
- limitApp流控针对的调用来源若为 default 则不区分调用来源。
- grade限流阈值类型QPS 或线程数模式0代表根据并发数量来限流1代表根据QPS来进行流量控制。
- count限流阈值
- strategy判断的根据是资源自身还是根据其它关联资源 (refResource),还是根据链路入口
- controlBehavior流控效果直接拒绝 / 排队等待 / 慢启动模式)
- clusterMode是否为集群模式
### 总结
> sentinel和spring cloud gateway两个框架都是很好的限流框架,但是在我使用中还没有将[spring-cloud-alibaba](https://github.com/alibaba/spring-cloud-alibaba)接入到项目中进行使用,所以我会选择**spring cloud gateway**,当接入完整的或者接入Nacos项目使用setinel会有更加好的体验.

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -14,7 +14,7 @@
大约在 3 年前,国内比较知名的一个互联网公司,曾因为缓存事故,导致雪崩,后台系统全部崩溃,事故从当天下午持续到晚上凌晨 3~4 点,公司损失了几千万。
缓存雪崩的事前事中事后的解决方案如下
缓存雪崩的事前事中事后的解决方案如下
- 事前redis 高可用,主从+哨兵redis cluster避免全盘崩溃。
- 事中:本地 ehcache 缓存 + hystrix 限流&降级,避免 MySQL 被打死。
- 事后redis 持久化,一旦重启,自动从磁盘上加载数据,快速恢复缓存数据。
@ -44,4 +44,7 @@
### 缓存击穿
缓存击穿,就是说某个 key 非常热点,访问非常频繁,处于集中式高并发访问的情况,当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。
解决方式也很简单,可以将热点数据设置为永远不过期;或者基于 redis or zookeeper 实现互斥锁,等待第一个请求构建完缓存之后,再释放锁,进而其它请求才能通过该 key 访问数据。
不同场景下的解决方式可如下:
- 若缓存的数据是基本不会发生更新的,则可尝试将该热点数据设置为永不过期。
- 若缓存的数据更新不频繁,且缓存刷新的整个流程耗时较少的情况下,则可以采用基于 redis、zookeeper 等分布式中间件的分布式互斥锁,或者本地互斥锁以保证仅少量的请求能请求数据库并重新构建缓存,其余线程则在锁释放后能访问到新缓存。
- 若缓存的数据更新频繁或者缓存刷新的流程耗时较长的情况下,可以利用定时线程在缓存过期前主动的重新构建缓存或者延后缓存的过期时间,以保证所有的请求能一直访问到对应的缓存。

View File

@ -42,7 +42,7 @@
**解决方案如下:**
更新数据的时候,根据**数据的唯一标识**,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。
更新数据的时候,根据**数据的唯一标识**,将操作路由之后,发送到一个 jvm 内部队列中。读取数据的时候,如果发现数据不在缓存中,那么将重新执行“读取数据+更新缓存的操作,根据唯一标识路由之后,也发送同一个 jvm 内部队列中。
一个队列对应一个工作线程,每个工作线程**串行**拿到对应的操作,然后一条一条的执行。这样的话,一个数据变更的操作,先删除缓存,然后再去更新数据库,但是还没完成更新。此时如果一个读请求过来,没有读到缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,然后同步等待缓存更新完成。

View File

@ -72,7 +72,7 @@
缺点有以下几个:
- 系统可用性降低<br>
系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了,ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整MQ 一挂,整套系统崩溃的,你不就完了?如何保证消息队列的高可用,可以[点击这里查看](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)。
系统引入的外部依赖越多,越容易挂掉。本来你就是 A 系统调用 BCD 三个系统的接口就好了ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整MQ 一挂,整套系统崩溃,你不就完了?如何保证消息队列的高可用,可以[点击这里查看](/docs/high-concurrency/how-to-ensure-high-availability-of-message-queues.md)。
- 系统复杂度提高<br>
硬生生加个 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)?怎么保证消息传递的顺序性?头大头大,问题一大堆,痛苦不已。

View File

@ -10,4 +10,26 @@
- 微服务的优缺点分别是什么?说一下你在项目开发中碰到的坑?
- 你所知道的微服务技术栈都有哪些?
- Eureka 和 Zookeeper 都可以提供服务注册与发现的功能,它们有什么区别?
- ......
- ......
---
## 公众号
GitHub 技术社区 Doocs 旗下唯一公众号“Doocs 开源社区”欢迎关注专注于分享有价值的文章当然也可以加我个人微信备注GitHub
<table>
<tr>
<td align="center" style="width: 200px;">
<a href="https://github.com/doocs">
<img src="./images/qrcode-for-doocs.jpg" style="width: 400px;"><br>
<sub>公众平台</sub>
</a><br>
</td>
<td align="center" style="width: 200px;">
<a href="https://github.com/yanglbme">
<img src="./images/qrcode-for-yanglbme.jpg" style="width: 400px;"><br>
<sub>个人微信</sub>
</a><br>
</td>
</tr>
</table>

View File

@ -0,0 +1,102 @@
# 微服务治理策略
- Author: [HuiFer](https://github.com/huifer)
- Description: 该文简单介绍微服务的治理策略以及应用技术
## 服务的注册和发
> 解决问题: 集中管理服务
> 解决方法: eureka 、zookeeper
## 负载均衡
> 解决问题: 降低服务器硬件压力
> 解决方法: nginx 、 Ribbon
## 通讯
> 解决问题: 各个服务之间的沟通桥梁
> 解决方法 :
> - 同步消息
> 1. rest
> 1. rpc
> - 异步消息
> 1. MQ
## 配置管理
> 解决问题: 随着服务的增加配置也在增加,如何管理各个服务的配置
> 解决方法: nacos 、 spring cloud config 、 Apollo
## 容错和服务降级
> 解决问题: 在微服务当中,一个请求经常会涉及到调用几个服务,如果其中某个服务不可以,没有做服务容错的话,极有可能会造成一连串的服务不可用,这就是雪崩效应.
> 解决方法: hystrix
## 服务依赖关系
> 解决问题: 多个服务之间来回依赖,启动关系的不明确
> 解决方法:
> 1. 应用分层: 数据层,业务层 数据层不需要依赖业务层,业务层依赖数据,规定上下依赖关系避免循环圈
## 服务文档
> 解决问题: 降低沟通成本
> 解决方法: swagger 、 java doc
## 服务安全问题
> 解决问题: 敏感数据的安全性
> 解决方法: oauth 、 shiro 、 spring security
## 流量控制
> 解决问题: 避免一个服务上的流量过大拖垮整个服务体系
> 解决方法: Hystrix
## 自动化测试
> 解决问题: 提前预知异常,确定服务是否可用
> 解决方法: junit
## 服务上线,下线的流程
> 解决问题: 避免服务随意的上线下线
> 解决方法: 新服务上线需要经过管理人员审核.服务下线需要告知各个调用方进行修改,直到没有调用该服务才可以进行下线.
## 兼容性
> 解决问题: 服务开发持续进行如何做到兼容
> 解决方法: 通过版本号的形式进行管理,修改完成进行回归测试
## 服务编排
> 解决问题: 解决服务依赖问题的一种方式
> 解决方法: docker & k8s
## 资源调度
> 解决问题: 每个服务的资源占用量不同,如何分配
> 解决方法: JVM 隔离、classload 隔离 ; 硬件隔离
## 容量规划
> 解决问题: 随着时间增长,调用逐步增加,什么时候追加机器
> 解决方法: 统计每日调用量和响应时间, 根据机器情况设置阈值,超过阈值就可以追加机器

View File

@ -0,0 +1,204 @@
# 微服务技术栈
- Author: [HuiFer](https://github.com/huifer)
- Description: 该文简单介绍微服务技术栈有哪些分别用来做什么
## 技术栈
### 微服务开发
> 作用: 快速开发服务.
- Spring
- SpringMvc
- SpringBoot
> [官网](https://spring.io/),Spring目前是JavaWeb开发人员必不可少的一个框架,SpringBoot简化了Spring开发的配置目前也是业内主流开发框架.
### 微服务注册发现
> 作用: 发现服务,注册服务.集中管理服务
#### Eureka
- Eureka Server : 提供服务注册服务,各个节点启动后,会在Eureka Server中进行注册.
- Eureka Client : 简化与Eureka Server的交互操作
- Spring Cloud Netflix : [GitHub](https://github.com/spring-cloud/spring-cloud-netflix),[文档](https://cloud.spring.io/spring-cloud-netflix/reference/html/)
#### Zookeeper
- > ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services.
>
> zookeeper是一个集中的服务,用于维护配置信息、命名、提供分布式同步和提供组服务.
- [GitHub](https://github.com/apache/zookeeper)
#### Zookeeper 和 Eureka 区别
1. Zookeeper保证CP , Eureka 保证AP
- C-数据一致性;A-服务可用性;P-服务对网络分区故障的容错性,这三个特性在任何分布式系统中不能同时满足,最多同时满足两个.
### 微服务配置管理
> 作用:统一管理一个或多个服务的配置信息,集中管理.
#### Disconf
- > Distributed Configuration Management Platform(分布式配置管理平台) ,它是专注于各种分布式系统配置管理 的通用组件/通用平台, 提供统一的配置管理服务,是一套完整的基于zookeeper的分布式配置统一解决方案.
- [GitHub](https://github.com/knightliao/disconf)
#### SpringCloudConfig
- [GitHub](https://github.com/spring-cloud/spring-cloud-config)
#### Apollo
- > Apollo阿波罗是携程框架部门研发的分布式配置中心,能够集中化管理应用不同环境、不同集群的配置,配置修改后能够实时推送到应用端,并且具备规范的权限、流程治理等特性,适用于微服务配置管理场景.
- [GitHub](https://github.com/ctripcorp/apollo)
### 权限认证
> 作用:根据系统设置的安全规则或者安全策略,用户可以访问而且只能访问自己被授权的资源,不多不少.
#### Spring Security
- [官网](https://spring.io/projects/spring-security)
#### apache Shiro
> Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiros easy-to-understand API, you can quickly and easily secure any application from the smallest mobile applications to the largest web and enterprise applications.
- [官网](http://shiro.apache.org/)
### 批处理
> 作用: 批量处理同类型数据或事物
#### Spring Batch
- [官网](官网)
### 定时任务
> 作用: 定时做什么.
#### Quartz
- [官网](http://www.quartz-scheduler.org/)
### 微服务调用(协议)
> 通讯协议
#### Rest
- 通过HTTP/HTTPS发送Rest请求进行数据交互
#### RPC
- Remote Procedure Call
- 它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议.RPC不依赖于具体的网络传输协议,tcp、udp等都可以.
#### gRPC
- [官网](https://www.grpc.io/)
- > A high-performance, open-source universal RPC framework
> 所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型.使用的时候客户端调用server端提供的接口就像是调用本地的函数一样.
#### RMI
- Remote Method Invocation
- 纯Java调用
### 服务接口调用
> 作用: 多个服务之间的通讯
#### Feign(HTTP)
- > Spring Cloud Netflix 的微服务都是以 HTTP 接口的形式暴露的,所以可以用 Apache 的 HttpClient 或 Spring 的 RestTemplate 去调用,而 Feign 是一个使用起来更加方便的 HTTP 客戶端,使用起来就像是调用自身工程的方法,而感觉不到是调用远程方法.
- [GitHub](https://github.com/OpenFeign/feign)
### 服务熔断
> 作用: 当请求到达一定阈值时不让请求继续.
#### Hystrix
- > Hystrix is a latency and fault tolerance library designed to isolate points of access to remote systems, services and 3rd party libraries, stop cascading failure and enable resilience in complex distributed systems where failure is inevitable.
>
- [GitHub](https://github.com/Netflix/Hystrix)
#### Sentinel
- > A lightweight powerful flow control component enabling reliability and monitoring for microservices. (轻量级的流量控制、熔断降级 Java 库)
- [GitHub](https://github.com/alibaba/Sentinel)
### 服务的负载均衡
> 作用:降低服务压力,增加吞吐量
#### Ribbon
- >Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现
>
- [GitHub](https://github.com/Netflix/ribbon)
#### Nginx
- > Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务
>
>
- [GitHub](https://github.com/nginx/nginx)
#### Nginx 与 Ribbon区别
1. Nginx 属于服务端负载均衡,Ribbon属于客户端负载均衡.Nginx作用与Tomcat,Ribbon作用与各个服务之间的调用(RPC)
### 消息队列
> 作用: 解耦业务,异步化处理数据
#### Kafka
- [官网](http://kafka.apache.org/)
#### RabbitMQ
- [官网](https://www.rabbitmq.com/)
#### RocketMQ
- [官网](http://rocketmq.apache.org/)
#### activeMQ
- [官网](http://activemq.apache.org/)
### 日志采集(elk)
> 作用:收集各服务日志提供日志分析、用户画像等
#### Elasticsearch
- [GitHub](https://github.com/elastic/elasticsearch)
#### Logstash
- [GitHub](https://github.com/elastic/logstash)
#### Kibana
- [GitHub](https://github.com/elastic/kibana)
### API网关
> 作用:外部请求通过API网关进行拦截处理,再转发到真正的服务
#### Zuul
> Zuul is a gateway service that provides dynamic routing, monitoring, resiliency, security, and more.
>
- [GitHub](https://github.com/Netflix/zuul)
### 服务监控
> 作用:以可视化或非可视化的形式展示出各个服务的运行情况(CPU、内存、访问量等)
#### Zabbix
- [GitHub](https://github.com/jjmartres/Zabbix)
#### Nagios
- [官网](https://www.nagios.org/)
#### Metrics
- [官网](https://metrics.dropwizard.io)
### 服务链路追踪
> 作用:明确服务之间的调用关系
#### Zipkin
- [GitHub](https://github.com/openzipkin/zipkin)
#### Brave
- [GitHub](https://github.com/openzipkin/brave)
### 数据存储
> 作用: 存储数据
#### 关系型数据库
##### MySql
- [官网](https://www.mysql.com/)
##### Oracle
- [官网](https://www.oracle.com/index.html)
##### MsSql
- [官网](https://docs.microsoft.com/zh-cn/sql/?view=sql-server-ver15)
##### PostgreSql
- [官网](https://www.postgresql.org/)
#### 非关系型数据库
##### Mongodb
- [官网](https://www.mongodb.com/)
##### Elasticsearch
- [GitHub](https://github.com/elastic/elasticsearch)
### 缓存
> 作用: 存储数据
#### redis
- [官网](https://redis.io/)
### 分库分表
> 作用: 数据库分库分表方案.
#### shardingsphere
- [官网](http://shardingsphere.apache.org/)
#### Mycat
- [官网](http://www.mycat.io/)
### 服务部署
> 作用: 将项目快速部署、上线、持续集成.
#### Docker
- [官网](http://www.docker.com/)
#### Jenkins
- [官网](https://jenkins.io/zh/)
#### Kubernetes(K8s)
- [官网](https://kubernetes.io/)
#### Mesos
- [官网](http://mesos.apache.org/)

View File

@ -0,0 +1,291 @@
# 什么是微服务?微服务之间是如何独立通讯的?
- Author: [HuiFer](https://github.com/huifer)
- Description: 介绍微服务的定义以及服务间的通信。
## 什么是微服务
- 微服务架构是一个分布式系统, 按照业务进行划分成为不同的服务单元, 解决单体系统性能等不足。
- 微服务是一种架构风格,一个大型软件应用由多个服务单元组成。系统中的服务单元可以单独部署,各个服务单元之间是松耦合的。
> 微服务概念起源: [Microservices](https://martinfowler.com/articles/microservices.html)
## 微服务之间是如何独立通讯的
### 同步
#### REST HTTP 协议
REST 请求在微服务中是最为常用的一种通讯方式, 它依赖于 HTTP\HTTPS 协议。RESTFUL 的特点是:
1. 每一个 URI 代表 1 种资源
2. 客户端使用 GET、POST、PUT、DELETE 4 个表示操作方式的动词对服务端资源进行操作: GET 用来获取资源, POST 用来新建资源(也可以用于更新资源), PUT 用来更新资源, DELETE 用来删除资源
3. 通过操作资源的表现形式来操作资源
4. 资源的表现形式是 XML 或者 HTML
5. 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息
举个例子,有一个服务方提供了如下接口:
```java
@RestController
@RequestMapping("/communication")
public class RestControllerDemo {
@GetMapping("/hello")
public String s() {
return "hello";
}
}
```
另外一个服务需要去调用该接口,调用方只需要根据 API 文档发送请求即可获取返回结果。
```java
@RestController
@RequestMapping("/demo")
public class RestDemo{
@Autowired
RestTemplate restTemplate;
@GetMapping("/hello2")
public String s2() {
String forObject = restTemplate.getForObject("http://localhost:9013/communication/hello", String.class);
return forObject;
}
}
```
通过这样的方式可以实现服务之间的通讯。
#### RPC TCP协议
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务。它的工作流程是这样的:
1. 执行客户端调用语句,传送参数
2. 调用本地系统发送网络消息
3. 消息传送到远程主机
4. 服务器得到消息并取得参数
5. 根据调用请求以及参数执行远程过程(服务)
6. 执行过程完毕,将结果返回服务器句柄
7. 服务器句柄返回结果,调用远程主机的系统网络服务发送结果
8. 消息传回本地主机
9. 客户端句柄由本地主机的网络服务接收消息
10. 客户端接收到调用语句返回的结果数据
举个例子。
首先需要一个服务端:
```java
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* RPC 服务端用来注册远程方法的接口和实现类
* @Date: 2019-11-04
*/
public class RPCServer {
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
private static final ConcurrentHashMap<String, Class> serviceRegister = new ConcurrentHashMap<>();
/**
* 注册方法
* @param service
* @param impl
*/
public void register(Class service, Class impl) {
serviceRegister.put(service.getSimpleName(), impl);
}
/**
* 启动方法
* @param port
*/
public void start(int port) {
ServerSocket socket = null;
try {
socket = new ServerSocket();
socket.bind(new InetSocketAddress(port));
System.out.println("服务启动");
System.out.println(serviceRegister);
while (true) {
executor.execute(new Task(socket.accept()));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static class Task implements Runnable {
Socket client = null;
public Task(Socket client) {
this.client = client;
}
@Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream output = null;
try {
input = new ObjectInputStream(client.getInputStream());
// 按照顺序读取对方写过来的内容
String serviceName = input.readUTF();
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Class serviceClass = serviceRegister.get(serviceName);
if (serviceClass == null) {
throw new ClassNotFoundException(serviceName + " 没有找到!");
}
Method method = serviceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(serviceClass.newInstance(), arguments);
output = new ObjectOutputStream(client.getOutputStream());
output.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
// 这里就不写 output!=null才关闭这个逻辑了
output.close();
input.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
```
其次需要一个客户端:
```java
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* RPC 客户端
* @Date: 2019-11-04
*/
public class RPCclient<T> {
/**
* 通过动态代理将参数发送过去到 RPCServer ,RPCserver 返回结果这个方法处理成为正确的实体
*/
public static <T> T getRemoteProxyObj(final Class<T> service, final InetSocketAddress addr) {
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[]{service}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream out = null;
ObjectInputStream input = null;
try {
socket = new Socket();
socket.connect(addr);
// 将实体类,参数,发送给远程调用方
out = new ObjectOutputStream(socket.getOutputStream());
out.writeUTF(service.getSimpleName());
out.writeUTF(method.getName());
out.writeObject(method.getParameterTypes());
out.writeObject(args);
input = new ObjectInputStream(socket.getInputStream());
return input.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
input.close();
socket.close();
}
return null;
}
});
}
}
```
再来一个测试的远程方法。
```java
public interface Tinterface {
String send(String msg);
}
public class TinterfaceImpl implements Tinterface {
@Override
public String send(String msg) {
return "send message " + msg;
}
}
```
测试代码如下:
```java
import com.huifer.admin.rpc.Tinterface;
import com.huifer.admin.rpc.TinterfaceImpl;
import java.net.InetSocketAddress;
/**
* @Date: 2019-11-04
*/
public class RunTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
RPCServer rpcServer = new RPCServer();
rpcServer.register(Tinterface.class, TinterfaceImpl.class);
rpcServer.start(10000);
}
}).start();
Tinterface tinterface = RPCclient.getRemoteProxyObj(Tinterface.class, new InetSocketAddress("localhost", 10000));
System.out.println(tinterface.send("rpc 测试用例"));
}
}
```
输出 `send message rpc 测试用例`
### 异步
#### 消息中间件
> 常见的消息中间件有 Kafka、ActiveMQ、RabbitMQ、RocketMQ , 常见的协议有AMQP、MQTTP、STOMP、XMPP. 这里不对消息队列进行拓展了, 具体如何使用还是请移步官网.
>

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

View File

@ -7,11 +7,11 @@
简而言之,微服务架构风格[1]是一种将单个应用程序开发为一套小型服务的方法,每个小型服务都在自己的进程中运行,并以轻量级机制(通常是 HTTP 资源 API进行通信。这些服务围绕业务功能构建可通过全自动部署机制来独立部署。这些服务共用一个最小型的集中式管理它们可以使用不同的编程语言编写并使用不同的数据存储技术。
在开始解释微服务风格之前,将它与单monolithic风格进行比较是有用的单片应用程序被构建为单一单元。企业应用程序通常由三个部分构成:客户端用户界面(由用户机器上的浏览器中运行的 HTML 页面和 Javascript 组成)、数据库(由许多表组成,通常是在关系型数据库中管理)系统、服务器端应用程序。服务器端应用程序处理 HTTP 请求,执行一些逻辑处理,从数据库检索和更新数据,选择数据并填充到要发送到浏览器的 HTML 视图中。这个服务器端应用程序是一个整体——一个逻辑可执行文件[2]。对系统的任何更改都涉及构建和部署新版本的服务器端应用程序。
在开始解释微服务风格之前,将它与单monolithic风格进行比较是有用的单体应用程序被构建为单一单元。企业应用程序通常由三个部分构成:客户端用户界面(由用户机器上的浏览器中运行的 HTML 页面和 Javascript 组成)、数据库(由许多表组成,通常是在关系型数据库中管理)系统、服务器端应用程序。服务器端应用程序处理 HTTP 请求,执行一些逻辑处理,从数据库检索和更新数据,选择数据并填充到要发送到浏览器的 HTML 视图中。这个服务器端应用程序是一个整体——一个逻辑可执行文件[2]。对系统的任何更改都涉及构建和部署新版本的服务器端应用程序。
这种单服务器是构建这种系统的自然方式。处理一个请求的所有逻辑都在一个进程中运行,允许你使用语言的基本功能将应用程序划分为类、函数和命名空间。需要注意的是,你可以在开发人员的笔记本电脑上运行和测试应用程序,并使用部署管道确保对程序做出的改动被适当测试并部署到生产环境中。你可以通过在负载均衡器后面运行许多实例来水平扩展整体块。
这种单服务器是构建这种系统的自然方式。处理一个请求的所有逻辑都在一个进程中运行,允许你使用语言的基本功能将应用程序划分为类、函数和命名空间。需要注意的是,你可以在开发人员的笔记本电脑上运行和测试应用程序,并使用部署管道确保对程序做出的改动被适当测试并部署到生产环境中。你可以通过在负载均衡器后面运行许多实例来水平扩展整体块。
应用程序可以取得成功,但越来越多的人对它们感到不满——尤其是在将更多应用程序部署到云的时候。变更周期被捆绑在一起——即使只是对应用程序的一小部分进行了更改,也需要重建和部署整个单应用。随着时间的推移,通常很难保持良好的模块化结构,也更难以保持应该只影响该模块中的一个模块的更改。对系统进行扩展时,不得不扩展整个应用系统,而不能仅扩展该系统中需要更多资源的那些部分。
应用程序可以取得成功,但越来越多的人对它们感到不满——尤其是在将更多应用程序部署到云的时候。变更周期被捆绑在一起——即使只是对应用程序的一小部分进行了更改,也需要重建和部署整个单应用。随着时间的推移,通常很难保持良好的模块化结构,也更难以保持应该只影响该模块中的一个模块的更改。对系统进行扩展时,不得不扩展整个应用系统,而不能仅扩展该系统中需要更多资源的那些部分。
![sketch](/images/sketch.png)
@ -50,7 +50,7 @@
以这种方式组建的一家公司是 [www.comparethemarket.com](http://www.comparethemarket.com/)。跨职能团队负责构建和运营每个产品,每个产品拆分为多个独立的服务,彼此通过消息总线来通信。
大型单应用程序也可以围绕业务功能进行模块化,尽管这不是常见的情况。当然,我们会敦促构建单应用系统的大型团队根据业务线来将自己分解为若干小团队。我们在这里看到的主要问题是,它们往往围绕太多的上下文进行组织。如果单体跨越了模块边界,对团队的个体成员来说,很难将它们装入短期的记忆中。此外,我们看到模块化生产线需要大量的规则来执行。服务组件所要求的更加明确的分离,使得它更容易保持团队边界清晰。
大型单应用程序也可以围绕业务功能进行模块化,尽管这不是常见的情况。当然,我们会敦促构建单应用系统的大型团队根据业务线来将自己分解为若干小团队。我们在这里看到的主要问题是,它们往往围绕太多的上下文进行组织。如果单体跨越了模块边界,对团队的个体成员来说,很难将它们装入短期的记忆中。此外,我们看到模块化生产线需要大量的规则来执行。服务组件所要求的更加明确的分离,使得它更容易保持团队边界清晰。
### 是产品不是项目
我们看到的大多数应用程序开发工作都使用这样一个项目模式:目标是交付一些软件,然后就完工了。一旦完成后,软件将移交给维护组织,然后构建它的项目团队也随之解散了。
@ -115,22 +115,22 @@ Netflix 是遵循这一理念的一个很好的例子。尤其是,以库的形
由于这并不是一篇关于持续交付的文章,我们在这里只关注持续交付的几个关键特性。我们希望有尽可能多的信心确保我们的软件正常运行,因此我们进行了大量的**自动化测试**。想让软件达到“晋级”(Promotion)状态从而“推上”流水线,就意味着要在每一个新的环境中,对软件进行**自动化部署**。
一个单应用程序可以非常愉快地通过这些环境构建、测试和推动。事实证明,一旦你为单体投入了自动化整体生产,那么部署更多的应用程序似乎不再那么可怕了。请记住,持续交付的目标之一就是让“部署”工作变得“枯燥”,所以无论是一个还是三个应用程序,只要部署工作依旧很“枯燥”,那么就没什么可担心的了[12]。
一个单应用程序可以非常愉快地通过这些环境构建、测试和推动。事实证明,一旦你为单体投入了自动化整体生产,那么部署更多的应用程序似乎不再那么可怕了。请记住,持续交付的目标之一就是让“部署”工作变得“枯燥”,所以无论是一个还是三个应用程序,只要部署工作依旧很“枯燥”,那么就没什么可担心的了[12]。
我们看到团队大量的基础设施自动化的另一个领域是在管理生产环境中的微服务。与我们上面的断言(只要部署很无聊)相比,单和微服务之间没有太大的区别,但是每个部署的运行环境可能会截然不同。
我们看到团队大量的基础设施自动化的另一个领域是在管理生产环境中的微服务。与我们上面的断言(只要部署很无聊)相比,单和微服务之间没有太大的区别,但是每个部署的运行环境可能会截然不同。
![micro-deployment](/images/micro-deployment.png)
### 设计时为故障做好准备
使用服务作为组件的结果是,需要设计应用程序以便它们能够容忍服务的失败。如果服务提供者商不可用,任何服务呼叫都可能失败,客户必须尽可能优雅地对此做出响应。与单设计相比这是一个缺点因为它这会引入额外的复杂性来处理它。结果是微服务团队不断反思服务失败是如何影响用户体验的。Netflix 的 [Simian Army](https://github.com/Netflix/SimianArmy) 能够引发服务甚至数据中心的故障在工作日发生故障,从而来测试应用程序的弹性和监控能力。
使用服务作为组件的结果是,需要设计应用程序以便它们能够容忍服务的失败。如果服务提供者商不可用,任何服务呼叫都可能失败,客户必须尽可能优雅地对此做出响应。与单设计相比这是一个缺点因为它这会引入额外的复杂性来处理它。结果是微服务团队不断反思服务失败是如何影响用户体验的。Netflix 的 [Simian Army](https://github.com/Netflix/SimianArmy) 能够引发服务甚至数据中心的故障在工作日发生故障,从而来测试应用程序的弹性和监控能力。
生产中的这种自动化测试足以让大多数运维团队兴奋得浑身颤栗,就像在一周的长假即将到来前一样。这并不是说单块架构风格不能构建先进的监控系统——只是根据我们的经验,这在单块系统中并不常见罢了。
生产中的这种自动化测试足以让大多数运维团队兴奋得浑身颤栗,就像在一周的长假即将到来前一样。这并不是说单体架构风格不能构建先进的监控系统——只是根据我们的经验,这在单体系统中并不常见罢了。
由于服务可能随时发生故障,因此能够快速检测故障并在可能的情况下自动恢复服务就显得至关重要。微服务应用程序非常重视应用程序的实时监控,比如检查架构元素(数据库每秒获得多少请求)和业务相关度量(例如每分钟收到多少订单)。语义监控可以提供出现问题的早期预警系统,从而触发开发团队跟进和调查。
这对于微服务架构来说尤为重要,因为微服务偏好编排和事件写作,这会导致一些紧急状况。虽然许多权威人士对于偶然事件的价值持积极态度,但事实是,“突发行为”有时可能是一件坏事。监控至关重要,它能够快速发现不良紧急行为并进行修复。
系统也可以像微服务一样实现透明的监控——事实上,它们也应该如此。不同之处在于你必须能够知道在不同进程中运行的服务在何时断开了连接。对于同一过程中的库,这种透明性用处并不大。
系统也可以像微服务一样实现透明的监控——事实上,它们也应该如此。不同之处在于你必须能够知道在不同进程中运行的服务在何时断开了连接。对于同一过程中的库,这种透明性用处并不大。
微服务团队希望看到针对每个服务的复杂监控和日志记录,例如显示“运行/宕机”状态的仪表盘以及各种运维和业务相关的指标。有关断路器状态,当前吞吐量和延迟的详细信息也是我们在工作中经常遇到的其他例子。
@ -139,7 +139,7 @@ Netflix 是遵循这一理念的一个很好的例子。尤其是,以库的形
每当要试图将软件系统分解为组件时,你就会面临这样的决策,即如何进行拆分——我们决定拆分应用程序的原则是什么?组件的关键属性具有独立替换和可升级性的特点[13]——这意味着我们寻找这些点,想象如何在不影响其协作者的情况下重写组件。实际上,许多微服务组通过明确地期望许多服务被废弃而不是长期演变来进一步考虑这一点。
Guardian 网站是设计和构建成单应用程序的一个很好的例子,但是它也在微服务方向上不断发展演化。原先的单系统仍然是网站的核心,但他们更喜欢通过构建一些微服务 API 的方式来添加新的功能。这种方法对于本质上是临时的功能尤其方便,例如处理体育赛事的专用页面。网站的这一部分可以使用快速开发语言快速组合在一起,在赛事结束后立即删除。我们在金融机构看到过类似的方法,为市场机会增加新服务,并在几个月甚至几周后丢弃。
Guardian 网站是设计和构建成单应用程序的一个很好的例子,但是它也在微服务方向上不断发展演化。原先的单系统仍然是网站的核心,但他们更喜欢通过构建一些微服务 API 的方式来添加新的功能。这种方法对于本质上是临时的功能尤其方便,例如处理体育赛事的专用页面。网站的这一部分可以使用快速开发语言快速组合在一起,在赛事结束后立即删除。我们在金融机构看到过类似的方法,为市场机会增加新服务,并在几个月甚至几周后丢弃。
这种强调可替换性的特点,是模块化设计一般性原则的一个特例,即通过变化模式来驱动模块化的实现[14]。大家都愿意将那些同时发生变化的东西放在同一个模块,很少变化的系统模块应该与目前正在经历大量变动的系统处于不同的服务中。如果你发现自己反复更改两项服务,那就表明它们应该合并了。
@ -152,13 +152,13 @@ Guardian 网站是设计和构建成单块应用程序的一个很好的例子
然而,尽管有这些积极的经验,但并不是说我们确信微服务是软件架构的未来发展方向。虽然到目前为止我们的经验与整体应用相比是积极的,但我们意识到没有足够的时间让我们做出充分完整的判断。
通常,架构决策所产生的真正效果,只有在该决策做出若干年后才能真正显现。我们已经看到由带着强烈的模块化愿望的优秀团队所做的一些项目,最终却构建出一个单架构,并在几年之内不断腐化。许多人认为,如果使用微服务就不大可能出现这种腐化,因为服务的边界是明确的,而且难以随意搞乱。然而,对于那些开发时间足够长的各种系统,除非我们已经见识得足够多,否则我们无法真正评价微服务架构是如何成熟的。
通常,架构决策所产生的真正效果,只有在该决策做出若干年后才能真正显现。我们已经看到由带着强烈的模块化愿望的优秀团队所做的一些项目,最终却构建出一个单架构,并在几年之内不断腐化。许多人认为,如果使用微服务就不大可能出现这种腐化,因为服务的边界是明确的,而且难以随意搞乱。然而,对于那些开发时间足够长的各种系统,除非我们已经见识得足够多,否则我们无法真正评价微服务架构是如何成熟的。
有人觉得微服务或许很难成熟起来,这当然是有原因的。在组件化上所做的任何工作的成功与否,取决于软件与组件的匹配程度。准确地搞清楚某个组件的边界的位置应该出现在哪里,是一项困难的工作。进化设计承认难以对边界进行正确定位,所以它将工作的重点放到了易于对边界进行重构之上。但是当各个组件成为各个进行远程通信的服务后,比起在单一进程内进行各个软件库之间的调用,此时的重构就变得更加困难。跨越服务边界的代码移动就变得困难起来。接口的任何变化,都需要在其各个参与者之间进行协调。向后兼容的层次也需要被添加进来。测试也会变得更加复杂。
另一个问题是,如果这些组件不能干净利落地组合成一个系统,那么所做的一切工作,仅仅是将组件内的复杂性转移到组件之间的连接之上。这样做的后果,不仅仅是将复杂性搬了家,它还将复杂性转移到那些不再明确且难以控制的边界之上。当在观察一个小型且简单的组件内部时,人们很容易觉得事情已经变得更好了,然而他们却忽视了服务之间杂乱的连接。
最后,还有一个团队技能的因素。新技术往往会被技术更加过硬的团队所采用。对于技术更加过硬的团队而更有效的一项技术,不一定适用于一个技术略逊一筹的团队。我们已经看到大量这样的案例,那些技术略逊一筹的团队构建出了杂乱的单架构。当这种杂乱发生到微服务身上时,会出现什么情况?这需要花时间来观察。一个糟糕的团队,总会构建一个糟糕的系统——在这种情况下,很难讲微服务究竟是减少了杂乱,还是让事情变得更糟。
最后,还有一个团队技能的因素。新技术往往会被技术更加过硬的团队所采用。对于技术更加过硬的团队而更有效的一项技术,不一定适用于一个技术略逊一筹的团队。我们已经看到大量这样的案例,那些技术略逊一筹的团队构建出了杂乱的单架构。当这种杂乱发生到微服务身上时,会出现什么情况?这需要花时间来观察。一个糟糕的团队,总会构建一个糟糕的系统——在这种情况下,很难讲微服务究竟是减少了杂乱,还是让事情变得更糟。
我们听到的一个合理的论点是,你不应该从微服务架构开始,而是从整体开始,保持模块化,并在整体出现问题时将其拆分为微服务。(这个建议并不理想,因为好的进程内接口通常不是一个好的服务接口。)
@ -193,4 +193,4 @@ Guardian 网站是设计和构建成单块应用程序的一个很好的例子
14: Kent Beck highlights this as one his design principles in [Implementation Patterns](https://www.amazon.com/gp/product/0321413091?ie=UTF8&tag=martinfowlerc-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=0321413091).
15: And SOA is hardly the root of this history. I remember people saying "we've been doing this for years" when the SOA term appeared at the beginning of the century. One argument was that this style sees its roots as the way COBOL programs communicated via data files in the earliest days of enterprise computing. In another direction, one could argue that microservices are the same thing as the Erlang programming model, but applied to an enterprise application context.
15: And SOA is hardly the root of this history. I remember people saying "we've been doing this for years" when the SOA term appeared at the beginning of the century. One argument was that this style sees its roots as the way COBOL programs communicated via data files in the earliest days of enterprise computing. In another direction, one could argue that microservices are the same thing as the Erlang programming model, but applied to an enterprise application context.

View File

@ -2,7 +2,9 @@
迁移单体式应用到微服务架构意味着一系列现代化过程,有点像这几代开发者一直在做的事情,实时上,当迁移时,我们可以重用一些想法。
一个策略是不要大规模big bang重写代码只有当你承担重建一套全新基于微服务的应用时候可以采用重写这种方法。重写代码听起来很不错但实际上充满了风险最终可能会失败就如 Martin Fowler 所说“the only thing a Big Bang rewrite guarantees is a Big Bang!”
一个策略是不要大规模big bang重写代码只有当你承担重建一套全新基于微服务的应用时候可以采用重写这种方法。重写代码听起来很不错但实际上充满了风险最终可能会失败就如 Martin Fowler 所说:
> “the only thing a Big Bang rewrite guarantees is a Big Bang!”
相反,应该采取逐步迁移单体式应用的策略,通过逐步生成微服务新应用,与旧的单体式应用集成,随着时间推移,单体式应用在整个架构中比例逐渐下降直到消失或者成为微服务架构一部分。这个策略有点像在高速路上限速到 70 迈对车做维护,尽管有挑战,但是比起重写的风险小很多。
@ -27,7 +29,7 @@ Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用
- 直接访问单体应用数据库
- 自己维护一份从单体应用中同步的数据
胶水代码也被称为容灾层anti-corruption layer这是因为胶水代码保护微服务全新域模型免受传统单体应用域模型污染。胶水代码在这两种模型间提供翻译功能。术语 anti-corruption layer 第一次出现在 Eric Evans 撰写的必读书 Domain Driven Design随后就被提炼为一篇白皮书。开发容灾层可能有点不是很重要但却是避免单体式泥潭的必要部分。
胶水代码也被称为容灾层anti-corruption layer这是因为胶水代码保护微服务全新域模型免受传统单体应用域模型污染。胶水代码在这两种模型间提供翻译功能。术语 anti-corruption layer 第一次出现在 Eric Evans 撰写的必读书 *Domain Driven Design*,随后就被提炼为一篇白皮书。开发容灾层可能有点不是很重要,但却是避免单体式泥潭的必要部分。
将新功能以轻量级微服务方式实现由很多优点,例如可以阻止单体应用变的更加无法管理。微服务本身可以开发、部署和独立扩展。采用微服务架构会给开发者带来不同的切身感受。
@ -68,7 +70,7 @@ Law of Holes 是说当自己进洞就应该停止挖掘。对于单体式应用
抽取模块第一步就是定义好模块和单体应用之间粗粒度接口,由于单体应用需要微服务的数据,反之亦然,因此更像是一个双向 API。因为必须在负责依赖关系和细粒度接口模式之间做好平衡因此开发这种 API 很有挑战性,尤其对使用域模型模式的业务逻辑层来说更具有挑战,因此经常需要改变代码来解决依赖性问题,如图所示:
一旦完成粗粒度接口也就将此模块转换成独立微服务。为了实现必须写代码使得单体应用和微服务之间通过使用进程间通信IPC机制的 API 来交换信息。如图所示迁移前后对比:
一旦完成粗粒度接口也就将此模块转换成独立微服务。为了实现必须写代码使得单体应用和微服务之间通过使用进程间通信IPC机制的 API 来交换信息。如图所示迁移前后对比:
![3](/images/30103116_ZCcM.png)

BIN
images/article-demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

BIN
images/qrcode-for-doocs.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

View File

@ -20,6 +20,7 @@
<li><a href="#/?id=分布式系统">分布式</a></li>
<li><a href="#/?id=高可用架构">高可用</a></li>
<li><a href="#/?id=微服务架构">微服务</a></li>
<li><a href="#/?id=海量数据处理">海量数据</a></li>
<li><a href="#/docs/from-readers/README">读者说</a></li>
</ul>
</li>