diff --git a/docs/cs-basics/data-structure/tree.md b/docs/cs-basics/data-structure/tree.md index 6938b184..d8fe1c10 100644 --- a/docs/cs-basics/data-structure/tree.md +++ b/docs/cs-basics/data-structure/tree.md @@ -176,8 +176,8 @@ public void postOrder(TreeNode root){ if(root == null){ return; } + postOrder(root.left); postOrder(root.right); - postOrder(root.left); system.out.println(root.data); } ``` diff --git a/docs/distributed-system/api-gateway.md b/docs/distributed-system/api-gateway.md index 322294ef..46019c0b 100644 --- a/docs/distributed-system/api-gateway.md +++ b/docs/distributed-system/api-gateway.md @@ -21,7 +21,7 @@ category: 分布式 ## 网关能提供哪些功能? -绝大部分网关可以提供下面这些功能: +绝大部分网关可以提供下面这些功能(有一些功能需要借助其他框架或者中间件): - **请求转发**:将请求转发到目标微服务。 - **负载均衡**:根据各个微服务实例的负载情况或者具体的负载均衡策略配置对请求实现动态的负载均衡。 @@ -37,6 +37,7 @@ category: 分布式 - **异常处理**:对于业务服务返回的异常响应,可以在网关层在返回给用户之前做转换处理。这样可以把一些业务侧返回的异常细节隐藏,转换成用户友好的错误提示返回。 - **API 文档:** 如果计划将 API 暴露给组织以外的开发人员,那么必须考虑使用 API 文档,例如 Swagger 或 OpenAPI。 - **协议转换**:通过协议转换整合后台基于 REST、AMQP、Dubbo 等不同风格和实现技术的微服务,面向 Web Mobile、开放平台等特定客户端提供统一服务。 +- **证书管理**:将 SSL 证书部署到 API 网关,由一个统一的入口管理接口,降低了证书更换时的复杂度。 下图来源于[百亿规模 API 网关服务 Shepherd 的设计与实现 - 美团技术团队 - 2021](https://mp.weixin.qq.com/s/iITqdIiHi3XGKq6u6FRVdg)这篇文章。 diff --git a/docs/high-availability/timeout-and-retry.md b/docs/high-availability/timeout-and-retry.md index 79d32f32..5a0a9a9e 100644 --- a/docs/high-availability/timeout-and-retry.md +++ b/docs/high-availability/timeout-and-retry.md @@ -50,11 +50,20 @@ category: 高可用 重试的核心思想是通过消耗服务器的资源来尽可能获得请求更大概率被成功处理。由于瞬态故障和偶然性故障是很少发生的,因此,重试对于服务器的资源消耗几乎是可以被忽略的。 +### 常见的重试策略有哪些? + +常见的重试策略有两种: + +1. **固定间隔时间重试**:每次重试之间都使用相同的时间间隔,比如每隔1.5秒进行一次重试。这种重试策略的优点是实现起来比较简单,不需要考虑重试次数和时间的关系,也不需要维护额外的状态信息。但是这种重试策略的缺点是可能会导致重试过于频繁或过于稀疏,从而影响系统的性能和效率。如果重试间隔太短,可能会对目标系统造成过大的压力,导致雪崩效应;如果重试间隔太长,可能会导致用户等待时间过长,影响用户体验。 +2. **梯度间隔重试**:根据重试次数的增加去延长下次重试时间,比如第一次重试间隔为1秒,第二次为2秒,第三次为4秒,以此类推。这种重试策略的优点是能够有效提高重试成功的几率(随着重试次数增加,但是重试依然不成功,说明目标系统恢复时间比较长,因此可以根据重试次数延长下次重试时间),也能通过柔性化的重试避免对下游系统造成更大压力。但是这种重试策略的缺点是实现起来比较复杂,需要考虑重试次数和时间的关系,以及设置合理的上限和下限值。另外,这种重试策略也可能会导致用户等待时间过长,影响用户体验。 + +这两种适合的场景各不相同。固定间隔时间重试适用于目标系统恢复时间比较稳定和可预测的场景,比如网络波动或服务重启。梯度间隔重试适用于目标系统恢复时间比较长或不可预测的场景,比如网络故障和服务故障。 + ### 重试的次数如何设置? 重试的次数不宜过多,否则依然会对系统负载造成比较大的压力。 -重试的次数通常建议设为 3 次。并且,我们通常还会设置重试的间隔,比如说我们要重试 3 次的话,第 1 次请求失败后,等待 1 秒再进行重试,第 2 次请求失败后,等待 2 秒再进行重试,第 3 次请求失败后,等待 3 秒再进行重试。 +重试的次数通常建议设为 3 次。大部分情况下,我们还是更建议使用梯度间隔重试策略,比如说我们要重试 3 次的话,第 1 次请求失败后,等待 1 秒再进行重试,第 2 次请求失败后,等待 2 秒再进行重试,第 3 次请求失败后,等待 3 秒再进行重试。 ### 重试幂等 diff --git a/docs/high-performance/load-balancing.md b/docs/high-performance/load-balancing.md index 1c827466..b8396465 100644 --- a/docs/high-performance/load-balancing.md +++ b/docs/high-performance/load-balancing.md @@ -123,6 +123,12 @@ Java 领域主流的微服务框架 Dubbo、Spring Cloud 等都内置了开箱 最小连接法可以尽可能最大地使请求分配更加合理化,提高服务器的利用率。不过,这种方法实现起来也最复杂,需要监控每一台服务器处理的请求连接数。 +### 两次随机法 + +两次随机法在随机法的基础上多增加了一次随机,多选出一个服务器。随后再根据两台服务器的负载等情况,从其中选择出一个最合适的服务器。 + +两次随机法的好处是可以动态地调节后端节点的负载,使其更加均衡。如果只使用一次随机法,可能会导致某些服务器过载,而某些服务器空闲。 + ## 七层负载均衡可以怎么做? 简单介绍两种项目中常用的七层负载均衡解决方案:DNS 解析和反向代理。 diff --git a/docs/java/jvm/classloader.md b/docs/java/jvm/classloader.md index 021a0ba1..b3526a75 100644 --- a/docs/java/jvm/classloader.md +++ b/docs/java/jvm/classloader.md @@ -314,7 +314,7 @@ Tomcat 这四个自定义的类加载器对应的目录如下: - `CommonClassLoader`作为 `CatalinaClassLoader` 和 `SharedClassLoader` 的父加载器。`CommonClassLoader` 能加载的类都可以被 `CatalinaClassLoader` 和 `SharedClassLoader` 使用。因此,`CommonClassLoader` 是为了实现公共类库(可以被所有 Web 应用和 Tomcat 内部组件使用的类库)的共享和隔离。 - `CatalinaClassLoader` 和 `SharedClassLoader` 能加载的类则与对方相互隔离。`CatalinaClassLoader` 用于加载 Tomcat 自身的类,为了隔离 Tomcat 本身的类和 Web 应用的类。`SharedClassLoader` 作为 `WebAppClassLoader` 的父加载器,专门来加载 Web 应用之间共享的类比如 Spring、Mybatis。 -- 每个 Web 应用都会创建一个单独的 `WebAppClassLoader`,并在启动 Web 应用的线程里设置线程上下文加载器为 `WebAppClassLoader`。各个 `WebAppClassLoader` 实例之间相互隔离,进而实现 Web 应用之间的类隔。 +- 每个 Web 应用都会创建一个单独的 `WebAppClassLoader`,并在启动 Web 应用的线程里设置线程线程上下文类加载器为 `WebAppClassLoader`。各个 `WebAppClassLoader` 实例之间相互隔离,进而实现 Web 应用之间的类隔。 单纯依靠自定义类加载器没办法满足某些场景的要求,例如,有些情况下,高层的类加载器需要加载低层的加载器才能加载的类。 @@ -322,15 +322,15 @@ Tomcat 这四个自定义的类加载器对应的目录如下: 再比如,假设我们的项目中有 Spring 的 jar 包,由于其是 Web 应用之间共享的,因此会由 `SharedClassLoader` 加载(Web 服务器是 Tomcat)。我们项目中有一些用到了 Spring 的业务类,比如实现了 Spring 提供的接口、用到了 Spring 提供的注解。所以,加载 Spring 的类加载器(也就是 `SharedClassLoader`)也会用来加载这些业务类。但是业务类在 Web 应用目录下,不在 `SharedClassLoader` 的加载路径下,所以 `SharedClassLoader` 无法找到业务类,也就无法加载它们。 -如何解决这个问题呢? 这个时候就需要用到 **线程上下文加载器(`ThreadContextClassLoader`)** 了。 +如何解决这个问题呢? 这个时候就需要用到 **线程上下文类加载器(`ThreadContextClassLoader`)** 了。 -拿 Spring 这个例子来说,当 Spring 需要加载业务类的时候,它不是用自己的类加载器,而是用当前线程的上下文类加载器。还记得我上面说的吗?每个 Web 应用都会创建一个单独的 `WebAppClassLoader`,并在启动 Web 应用的线程里设置线程上下文加载器为 `WebAppClassLoader`。这样就可以让高层的类加载器(`SharedClassLoader`)借助子类加载器( `WebAppClassLoader`)来加载业务类,破坏了 Java 的类加载委托机制,让应用逆向使用类加载器。 +拿 Spring 这个例子来说,当 Spring 需要加载业务类的时候,它不是用自己的类加载器,而是用当前线程的上下文类加载器。还记得我上面说的吗?每个 Web 应用都会创建一个单独的 `WebAppClassLoader`,并在启动 Web 应用的线程里设置线程线程上下文类加载器为 `WebAppClassLoader`。这样就可以让高层的类加载器(`SharedClassLoader`)借助子类加载器( `WebAppClassLoader`)来加载业务类,破坏了 Java 的类加载委托机制,让应用逆向使用类加载器。 -线程上下文加载器的原理是将一个类加载器保存在线程私有数据里,跟线程绑定,然后在需要的时候取出来使用。这个类加载器通常是由应用程序或者容器(如 Tomcat)设置的。 +线程线程上下文类加载器的原理是将一个类加载器保存在线程私有数据里,跟线程绑定,然后在需要的时候取出来使用。这个类加载器通常是由应用程序或者容器(如 Tomcat)设置的。 `Java.lang.Thread` 中的`getContextClassLoader()`和 `setContextClassLoader(ClassLoader cl)`分别用来获取和设置线程的上下文类加载器。如果没有通过`setContextClassLoader(ClassLoader cl)`进行设置的话,线程将继承其父线程的上下文类加载器。 -Spring 获取线程上下文加载器的代码如下: +Spring 获取线程线程上下文类加载器的代码如下: ```java cl = Thread.currentThread().getContextClassLoader(); diff --git a/docs/system-design/framework/mybatis/mybatis-interview.md b/docs/system-design/framework/mybatis/mybatis-interview.md index 6f7f4aba..9035da1a 100644 --- a/docs/system-design/framework/mybatis/mybatis-interview.md +++ b/docs/system-design/framework/mybatis/mybatis-interview.md @@ -16,6 +16,8 @@ head: > 本篇文章由 JavaGuide 收集自网络,原出处不明。 +> +> 比起这些枯燥的面试题,我更建议你看看文末推荐的 MyBatis 优质好文。 ### #{} 和 \${} 的区别是什么? @@ -298,4 +300,12 @@ MyBatis 提供了 9 种动态 sql 标签: 面试题看似都很简单,但是想要能正确回答上来,必定是研究过源码且深入的人,而不是仅会使用的人或者用的很熟的人,以上所有面试题及其答案所涉及的内容,在我的 MyBatis 系列博客中都有详细讲解和原理分析。 - \ No newline at end of file + + +### 文章推荐 + +- [2W字全面剖析Mybatis中的9种设计模式](https://juejin.cn/post/7273516671574687759) +- [从零开始实现一个MyBatis加解密插件](https://mp.weixin.qq.com/s/WUEAdFDwZsZ4EKO8ix0ijg) +- [MyBatis最全使用指南](https://juejin.cn/post/7051910683264286750) +- [脑洞打开!第一次看到这样使用MyBatis的,看得我一愣一愣的。](https://juejin.cn/post/7269390456530190376) +- [MyBatis居然也有并发问题](https://juejin.cn/post/7264921613551730722) \ No newline at end of file diff --git a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md index afc7b63f..173c766a 100644 --- a/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md +++ b/docs/system-design/framework/spring/spring-knowledge-and-questions-summary.md @@ -132,7 +132,7 @@ Spring 时代我们一般通过 XML 文件来配置 Bean,后来开发人员觉 相关阅读: - [IoC 源码阅读](https://javadoop.com/post/spring-ioc) -- [面试被问了几百遍的 IoC 和 AOP ,还在傻傻搞不清楚?](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486938&idx=1&sn=c99ef0233f39a5ffc1b98c81e02dfcd4&chksm=cea24211f9d5cb07fa901183ba4d96187820713a72387788408040822ffb2ed575d28e953ce7&token=1736772241&lang=zh_CN#rd) +- [IoC & AOP详解(快速搞懂)](./ioc-and-aop.md) ### 什么是 Spring Bean? @@ -359,7 +359,7 @@ Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某 当然你也可以使用 **AspectJ** !Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。 -AOP 切面编程设计到的一些专业术语: +AOP 切面编程涉及到的一些专业术语: | 术语 | 含义 | | :---------------- | :-------------------------------------------------------------------: | @@ -444,7 +444,7 @@ MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心 - Model:系统涉及的数据,也就是 dao 和 bean。 - View:展示模型中的数据,只是用来展示。 -- Controller:处理用户请求都发送给 ,返回数据给 JSP 并展示给用户。 +- Controller:接受用户请求,并将请求发送至 Model,最后返回数据给 JSP 并展示给用户 ![](https://oss.javaguide.cn/java-guide-blog/mvc-model2.png)