diff --git a/_sidebar.md b/_sidebar.md index 1b5c702..8a3d546 100644 --- a/_sidebar.md +++ b/_sidebar.md @@ -23,11 +23,22 @@ * [Redis](./docs/d-2-redis.md) * 常用框架 * [Spring](./docs/e-1spring.md) - * [SpringBoot](./docs/e-2springboot(付费).md) + * [SpringBoot(付费)](./docs/e-2springboot(付费).md) * [MyBatis](./docs/e-3mybatis.md) - * [Netty](./docs/e-4netty(付费).md) + * [Netty(付费)](./docs/e-4netty(付费).md) * 系统设计 + * [设计模式](./docs/f-0设计模式.md) * [认证授权](./docs/f-1认证授权.md) - * [系统设计面试指北](./docs/f-2系统设计面试指北.md) -* [微服务](./docs/h-微服务.md) + * [定时任务](./docs/f-2定时任务.md) +* 分布式 + * 理论&算法&协议 + * 网关 + * 分布式 ID + * RPC + * 分布式事务 + * 分布式协调(ZooKeeper) + +* 高性能 + +* 高可用 diff --git "a/docs/e-2springboot(344円273円230円350円264円271円).md" "b/docs/e-2springboot(344円273円230円350円264円271円).md" index 87b6f01..722a55f 100644 --- "a/docs/e-2springboot(344円273円230円350円264円271円).md" +++ "b/docs/e-2springboot(344円273円230円350円264円271円).md" @@ -41,4 +41,3 @@ - diff --git "a/docs/f-0350円256円276円350円256円241円346円250円241円345円274円217円.md" "b/docs/f-0350円256円276円350円256円241円346円250円241円345円274円217円.md" new file mode 100644 index 0000000..8bcec72 --- /dev/null +++ "b/docs/f-0350円256円276円350円256円241円346円250円241円345円274円217円.md" @@ -0,0 +1,7 @@ +**设计模式** 相关的面试题已经整理到了 PDF 手册中,你可以在我的公众号"**JavaGuide**"后台回复"**PDF**" 获取。 + +![JavaGuide 官方公众号](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/gongzhonghaoxuanchuan.png) + +**《设计模式》PDF 电子书内容概览** : + +![《设计模式》PDF文档概览](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/design-pattern-pdf.png) \ No newline at end of file diff --git "a/docs/f-1350円256円244円350円257円201円346円216円210円346円235円203円.md" "b/docs/f-1350円256円244円350円257円201円346円216円210円346円235円203円.md" index 2be0496..6e14f0e 100755 --- "a/docs/f-1350円256円244円350円257円201円346円216円210円346円235円203円.md" +++ "b/docs/f-1350円256円244円350円257円201円346円216円210円346円235円203円.md" @@ -8,3 +8,253 @@ ## 6.1 认证授权 +### 认证 (Authentication) 和授权 (Authorization)的区别是什么? + +这是一个绝大多数人都会混淆的问题。首先先从读音上来认识这两个名词,很多人都会把它俩的读音搞混,所以我建议你先先去查一查这两个单词到底该怎么读,他们的具体含义是什么。 + +说简单点就是: + +- **认证 (Authentication):** 你是谁。 +- **授权 (Authorization):** 你有权限干什么。 + +稍微正式点(啰嗦点)的说法就是 : + +- **Authentication(认证)** 是验证您的身份的凭据(例如用户名/用户 ID 和密码),通过这个凭据,系统得以知道你就是你,也就是说系统存在你这个用户。所以,Authentication 被称为身份/用户验证。 +- **Authorization(授权)** 发生在 **Authentication(认证)** 之后。授权嘛,光看意思大家应该就明白,它主要掌管我们访问系统的权限。比如有些特定资源只能具有特定权限的人才能访问比如 admin,有些对系统资源操作比如删除、添加、更新只能特定人才具有。 + +认证 : + +![](https://img-blog.csdnimg.cn/20210604160908352.png) + +授权: + +![](https://img-blog.csdnimg.cn/20210604161032412.png) + +这两个一般在我们的系统中被结合在一起使用,目的就是为了保护我们系统的安全性。 + +### RBAC 模型了解吗? + +系统权限控制最常采用的访问控制模型就是 **RBAC 模型** 。 + +**什么是 RBAC 呢?** + +RBAC 即基于角色的权限访问控制(Role-Based Access Control)。这是一种通过角色关联权限,角色同时又关联用户的授权的方式。 + +简单地说:一个用户可以拥有若干角色,每一个角色又可以被分配若干权限,这样就构造成"用户-角色-权限" 的授权模型。在这种模型中,用户与角色、角色与权限之间构成了多对多的关系,如下图 + +![RBAC 权限模型示意图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/design-of-authority-system/rbac.png) + +**在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。** + +本系统的权限设计相关的表如下(一共 5 张表,2 张用户建立表之间的联系): + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-11/%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1-%E6%9D%83%E9%99%90.png) + +通过这个权限模型,我们可以创建不同的角色并为不同的角色分配不同的权限范围(菜单)。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/books%E6%9D%83%E9%99%90%E7%AE%A1%E7%90%86%E6%A8%A1%E5%9D%97.png) + +通常来说,如果系统对于权限控制要求比较严格的话,一般都会选择使用 RBAC 模型来做权限控制。 + +### 什么是 Cookie ? Cookie 的作用是什么? + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/cookie-sessionId.png) + +`Cookie` 和 `Session` 都是用来跟踪浏览器用户身份的会话方式,但是两者的应用场景不太一样。 + +维基百科是这样定义 `Cookie` 的: + +> `Cookies` 是某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密)。 + +简单来说: **`Cookie` 存放在客户端,一般用来保存用户信息**。 + +下面是 `Cookie` 的一些应用案例: + +1. 我们在 `Cookie` 中保存已经登录过的用户信息,下次访问网站的时候页面可以自动帮你登录的一些基本信息给填了。除此之外,`Cookie` 还能保存用户首选项,主题和其他设置信息。 +2. 使用 `Cookie` 保存 `SessionId` 或者 `Token` ,向后端发送请求的时候带上 `Cookie`,这样后端就能取到 `Session` 或者 `Token` 了。这样就能记录用户当前的状态了,因为 HTTP 协议是无状态的。 +3. `Cookie` 还可以用来记录和分析用户行为。举个简单的例子你在网上购物的时候,因为 HTTP 协议是没有状态的,如果服务器想要获取你在某个页面的停留状态或者看了哪些商品,一种常用的实现方式就是将这些信息存放在 `Cookie` +4. ...... + +### 如何在项目中使用 Cookie 呢? + +我这里以 Spring Boot 项目为例。 + +**1)设置 `Cookie` 返回给客户端** + +```java +@GetMapping("/change-username") +public String setCookie(HttpServletResponse response) { + // 创建一个 cookie + Cookie cookie = new Cookie("username", "Jovan"); + //设置 cookie过期时间 + cookie.setMaxAge(7 * 24 * 60 * 60); // expires in 7 days + //添加到 response 中 + response.addCookie(cookie); + + return "Username is changed!"; +} +``` + +**2) 使用 Spring 框架提供的 `@CookieValue` 注解获取特定的 cookie 的值** + +```java +@GetMapping("/") +public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) { + return "Hey! My username is " + username; +} +``` + +**3) 读取所有的 `Cookie` 值** + +```java +@GetMapping("/all-cookies") +public String readAllCookies(HttpServletRequest request) { + + Cookie[] cookies = request.getCookies(); + if (cookies != null) { + return Arrays.stream(cookies) + .map(c -> c.getName() + "=" + c.getValue()).collect(Collectors.joining(", ")); + } + + return "No cookies"; +} +``` + +更多关于如何在 Spring Boot 中使用 `Cookie` 的内容可以查看这篇文章:[How to use cookies in Spring Boot](https://attacomsian.com/blog/cookies-spring-boot) 。 + +### Cookie 和 Session 有什么区别? + +**`Session` 的主要作用就是通过服务端记录用户的状态。** 典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 `Session` 之后就可以标识这个用户并且跟踪这个用户了。 + +`Cookie` 数据保存在客户端(浏览器端),`Session` 数据保存在服务器端。相对来说 `Session` 安全性更高。如果使用 `Cookie` 的一些敏感信息不要写入 `Cookie` 中,最好能将 `Cookie` 信息加密然后使用到的时候再去服务器端解密。 + +**那么,如何使用 `Session` 进行身份验证?** + +### 如何使用 Session-Cookie 方案进行身份验证? + +很多时候我们都是通过 `SessionID` 来实现特定的用户,`SessionID` 一般会选择存放在 Redis 中。举个例子: + +1. 用户成功登陆系统,然后返回给客户端具有 `SessionID` 的 `Cookie` 。 +2. 当用户向后端发起请求的时候会把 `SessionID` 带上,这样后端就知道你的身份状态了。 + +关于这种认证方式更详细的过程如下: + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/session-cookie-authentication-process.png) + +1. 用户向服务器发送用户名、密码、验证码用于登陆系统。 +2. 服务器验证通过后,服务器为用户创建一个 `Session`,并将 `Session` 信息存储起来。 +3. 服务器向用户返回一个 `SessionID`,写入用户的 `Cookie`。 +4. 当用户保持登录状态时,`Cookie` 将与每个后续请求一起被发送出去。 +5. 服务器可以将存储在 `Cookie` 上的 `SessionID` 与存储在内存中或者数据库中的 `Session` 信息进行比较,以验证用户的身份,返回给用户客户端响应信息的时候会附带用户当前的状态。 + +使用 `Session` 的时候需要注意下面几个点: + +- 依赖 `Session` 的关键业务一定要确保客户端开启了 `Cookie`。 +- 注意 `Session` 的过期时间。 + +另外,Spring Session 提供了一种跨多个应用程序或实例管理用户会话信息的机制。如果想详细了解可以查看下面几篇很不错的文章: + +- [Getting Started with Spring Session](https://codeboje.de/spring-Session-tutorial/) +- [Guide to Spring Session](https://www.baeldung.com/spring-Session) +- [Sticky Sessions with Spring Session & Redis](https://medium.com/@gvnix/sticky-Sessions-with-spring-Session-redis-bdc6f7438cc3) + +### 多服务器节点下 Session-Cookie 方案如何做? + +Session-Cookie 方案在单体环境是一个非常好的身份认证方案。但是,当服务器水平拓展成多节点时,Session-Cookie 方案就要面临挑战了。 + +举个例子:假如我们部署了两份相同的服务 A,B,用户第一次登陆的时候 ,Nginx 通过负载均衡机制将用户请求转发到 A 服务器,此时用户的 Session 信息保存在 A 服务器。结果,用户第二次访问的时候 Nginx 将请求路由到 B 服务器,由于 B 服务器没有保存 用户的 Session 信息,导致用户需要重新进行登陆。 + +**我们应该如何避免上面这种情况的出现呢?** + +有几个方案可供大家参考: + +1. 某个用户的所有请求都通过特性的哈希策略分配给同一个服务器处理。这样的话,每个服务器都保存了一部分用户的 Session 信息。服务器宕机,其保存的所有 Session 信息就完全丢失了。 +2. 每一个服务器保存的 Session 信息都是互相同步的,也就是说每一个服务器都保存了全量的 Session 信息。每当一个服务器的 Session 信息发生变化,我们就将其同步到其他服务器。这种方案成本太大,并且,节点越多时,同步成本也越高。 +3. 单独使用一个所有服务器都能访问到的数据节点(比如缓存)来存放 Session 信息。为了保证高可用,数据节点尽量要避免是单点。 + +### 如果没有 Cookie 的话 Session 还能用吗? + +这是一道经典的面试题! + +一般是通过 `Cookie` 来保存 `SessionID` ,假如你使用了 `Cookie` 保存 `SessionID` 的方案的话, 如果客户端禁用了 `Cookie`,那么 `Session` 就无法正常工作。 + +但是,并不是没有 `Cookie` 之后就不能用 `Session` 了,比如你可以将 `SessionID` 放在请求的 `url` 里面`https://javaguide.cn/?Session_id=xxx` 。这种方案的话可行,但是安全性和用户体验感降低。当然,为了你也可以对 `SessionID` 进行一次加密之后再传入后端。 + +### 为什么 Cookie 无法防止 CSRF 攻击,而 Token 可以? + +**CSRF(Cross Site Request Forgery)** 一般被翻译为 **跨站请求伪造** 。那么什么是 **跨站请求伪造** 呢?说简单用你的身份去发送一些对你不友好的请求。举个简单的例子: + +小壮登录了某网上银行,他来到了网上银行的帖子区,看到一个帖子下面有一个链接写着"科学理财,年盈利率过万",小壮好奇的点开了这个链接,结果发现自己的账户少了 10000 元。这是这么回事呢?原来黑客在链接中藏了一个请求,这个请求直接利用小壮的身份给银行发送了一个转账请求,也就是通过你的 Cookie 向银行发出请求。 + +```html +科学理财,年盈利率过万 +``` + +上面也提到过,进行 `Session` 认证的时候,我们一般使用 `Cookie` 来存储 `SessionId`,当我们登陆后后端生成一个 `SessionId` 放在 Cookie 中返回给客户端,服务端通过 Redis 或者其他存储工具记录保存着这个 `SessionId`,客户端登录以后每次请求都会带上这个 `SessionId`,服务端通过这个 `SessionId` 来标示你这个人。如果别人通过 `Cookie` 拿到了 `SessionId` 后就可以代替你的身份访问系统了。 + +`Session` 认证中 `Cookie` 中的 `SessionId` 是由浏览器发送到服务端的,借助这个特性,攻击者就可以通过让用户误点攻击链接,达到攻击效果。 + +但是,我们使用 `Token` 的话就不会存在这个问题,在我们登录成功获得 `Token` 之后,一般会选择存放在 `localStorage` (浏览器本地存储)中。然后我们在前端通过某些方式会给每个发到后端的请求加上这个 `Token`,这样就不会出现 CSRF 漏洞的问题。因为,即使有个你点击了非法链接发送了请求到服务端,这个非法请求是不会携带 `Token` 的,所以这个请求将是非法的。 + +![](https://img-blog.csdnimg.cn/20210615161108272.png) + +需要注意的是:不论是 `Cookie` 还是 `Token` 都无法避免 **跨站脚本攻击(Cross Site Scripting)XSS** 。 + +> 跨站脚本攻击(Cross Site Scripting)缩写为 CSS 但这会与层叠样式表(Cascading Style Sheets,CSS)的缩写混淆。因此,有人将跨站脚本攻击缩写为 XSS。 + +XSS 中攻击者会用各种方式将恶意代码注入到其他用户的页面中。就可以通过脚本盗用信息比如 `Cookie` 。 + +推荐阅读:[如何防止 CSRF 攻击?—美团技术团队](https://tech.meituan.com/2018/10/11/fe-security-csrf.html) + +### 什么是 JWT?JWT 由哪些部分组成? + +[JWT 基础概念详解](https://javaguide.cn/system-design/security/jwt-intro.html) + +### 如何基于 JWT 进行身份验证? 如何防止 JWT 被篡改? + +[JWT 基础概念详解](https://javaguide.cn/system-design/security/jwt-intro.html) + +### 什么是 SSO? + +SSO(Single Sign On)即单点登录说的是用户登陆多个子系统的其中一个就有权访问与其相关的其他系统。举个例子我们在登陆了京东金融之后,我们同时也成功登陆京东的京东超市、京东国际、京东生鲜等子系统。 + +![SSO 示意图](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/github/javaguide/system-design/security/sso.png) + + + +### SSO 有什么好处? + +- **用户角度** :用户能够做到一次登录多次使用,无需记录多套用户名和密码,省心。 +- **系统管理员角度** : 管理员只需维护好一个统一的账号中心就可以了,方便。 +- **新系统开发角度:** 新系统开发时只需直接对接统一的账号中心即可,简化开发流程,省时。 + +### 如何设计实现一个 SSO 系统? + +[SSO 单点登录详解](https://javaguide.cn/system-design/security/sso-intro.html) + +### 什么是 OAuth 2.0? + +OAuth 是一个行业的标准授权协议,主要用来授权第三方应用获取有限的权限。而 OAuth 2.0 是对 OAuth 1.0 的完全重新设计,OAuth 2.0 更快,更容易实现,OAuth 1.0 已经被废弃。详情请见:[rfc6749](https://tools.ietf.org/html/rfc6749)。 + +实际上它就是一种授权机制,它的最终目的是为第三方应用颁发一个有时效性的令牌 Token,使得第三方应用能够通过该令牌获取相关的资源。 + +OAuth 2.0 比较常用的场景就是第三方登录,当你的网站接入了第三方登录的时候一般就是使用的 OAuth 2.0 协议。 + +另外,现在 OAuth 2.0 也常见于支付场景(微信支付、支付宝支付)和开发平台(微信开放平台、阿里开放平台等等)。 + +下图是 [Slack OAuth 2.0 第三方登录](https://api.slack.com/legacy/oauth)的示意图: + +![](https://img-blog.csdnimg.cn/20210615151716340.png) + +**推荐阅读:** + +- [OAuth 2.0 的一个简单解释](http://www.ruanyifeng.com/blog/2019/04/oauth_design.html) +- [10 分钟理解什么是 OAuth 2.0 协议](https://deepzz.com/post/what-is-oauth2-protocol.html) +- [OAuth 2.0 的四种方式](http://www.ruanyifeng.com/blog/2019/04/oauth-grant-types.html) +- [GitHub OAuth 第三方登录示例教程](http://www.ruanyifeng.com/blog/2019/04/github-oauth.html) + +### 参考 + +- 不要用 JWT 替代 session 管理(上):全面了解 Token,JWT,OAuth,SAML,SSO:https://zhuanlan.zhihu.com/p/38942172 +- Introduction to JSON Web Tokens:https://jwt.io/introduction +- JSON Web Token Claims:https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims diff --git "a/docs/f-2345円256円232円346円227円266円344円273円273円345円212円241円.md" "b/docs/f-2345円256円232円346円227円266円344円273円273円345円212円241円.md" new file mode 100644 index 0000000..af267a7 --- /dev/null +++ "b/docs/f-2345円256円232円346円227円266円344円273円273円345円212円241円.md" @@ -0,0 +1,317 @@ +## 为什么需要定时任务? + +我们来看一下几个非常常见的业务场景: + +1. 某系统凌晨要进行数据备份。 +2. 某电商平台,用户下单半个小时未支付的情况下需要自动取消订单。 +3. 某媒体聚合平台,每 10 分钟动态抓取某某网站的数据为自己所用。 +4. 某博客平台,支持定时发送文章。 +5. 某基金平台,每晚定时计算用户当日收益情况并推送给用户最新的数据。 +6. ...... + +这些场景往往都要求我们在某个特定的时间去做某个事情。 + +## 单机定时任务技术选型有哪些? + +### Timer + +`java.util.Timer`是 JDK 1.3 开始就已经支持的一种定时任务的实现方式。 + +`Timer` 内部使用一个叫做 `TaskQueue` 的类存放定时任务,它是一个基于最小堆实现的优先级队列。`TaskQueue` 会按照任务距离下一次执行时间的大小将任务排序,保证在堆顶的任务最先执行。这样在需要执行任务时,每次只需要取出堆顶的任务运行即可! + +`Timer` 使用起来比较简单,通过下面的方式我们就能创建一个 1s 之后执行的定时任务。 + +```java +// 示例代码: +TimerTask task = new TimerTask() { + public void run() { + System.out.println("当前时间: " + new Date() + "n" + + "线程名称: " + Thread.currentThread().getName()); + } +}; +System.out.println("当前时间: " + new Date() + "n" + + "线程名称: " + Thread.currentThread().getName()); +Timer timer = new Timer("Timer"); +long delay = 1000L; +timer.schedule(task, delay); + + +//输出: +当前时间: Fri May 28 15:18:47 CST 2021n线程名称: main +当前时间: Fri May 28 15:18:48 CST 2021n线程名称: Timer +``` + +不过其缺陷较多,比如一个 `Timer` 一个线程,这就导致 `Timer` 的任务的执行只能串行执行,一个任务执行时间过长的话会影响其他任务(性能非常差),再比如发生异常时任务直接停止(`Timer` 只捕获了 `InterruptedException` )。 + +`Timer` 类上的有一段注释是这样写的: + +```JAVA + * This class does not offer real-time guarantees: it schedules + * tasks using the Object.wait(long) method. + *Java 5.0 introduced the {@code java.util.concurrent} package and + * one of the concurrency utilities therein is the {@link + * java.util.concurrent.ScheduledThreadPoolExecutor + * ScheduledThreadPoolExecutor} which is a thread pool for repeatedly + * executing tasks at a given rate or delay. It is effectively a more + * versatile replacement for the {@code Timer}/{@code TimerTask} + * combination, as it allows multiple service threads, accepts various + * time units, and doesn't require subclassing {@code TimerTask} (just + * implement {@code Runnable}). Configuring {@code + * ScheduledThreadPoolExecutor} with one thread makes it equivalent to + * {@code Timer}. +``` + +大概的意思就是: `ScheduledThreadPoolExecutor` 支持多线程执行定时任务并且功能更强大,是 `Timer` 的替代品。 + +### ScheduledExecutorService + +`ScheduledExecutorService` 是一个接口,有多个实现类,比较常用的是 `ScheduledThreadPoolExecutor` 。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javaguide/20210607154324712.png) + +`ScheduledThreadPoolExecutor` 本身就是一个线程池,支持任务并发执行。并且,其内部使用 `DelayQueue` 作为任务队列。 + +```java +// 示例代码: +TimerTask repeatedTask = new TimerTask() { + @SneakyThrows + public void run() { + System.out.println("当前时间: " + new Date() + "n" + + "线程名称: " + Thread.currentThread().getName()); + } +}; +System.out.println("当前时间: " + new Date() + "n" + + "线程名称: " + Thread.currentThread().getName()); +ScheduledExecutorService executor = Executors.newScheduledThreadPool(3); +long delay = 1000L; +long period = 1000L; +executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS); +Thread.sleep(delay + period * 5); +executor.shutdown(); +//输出: +当前时间: Fri May 28 15:40:46 CST 2021n线程名称: main +当前时间: Fri May 28 15:40:47 CST 2021n线程名称: pool-1-thread-1 +当前时间: Fri May 28 15:40:48 CST 2021n线程名称: pool-1-thread-1 +当前时间: Fri May 28 15:40:49 CST 2021n线程名称: pool-1-thread-2 +当前时间: Fri May 28 15:40:50 CST 2021n线程名称: pool-1-thread-2 +当前时间: Fri May 28 15:40:51 CST 2021n线程名称: pool-1-thread-2 +当前时间: Fri May 28 15:40:52 CST 2021n线程名称: pool-1-thread-2 +``` + +不论是使用 `Timer` 还是 `ScheduledExecutorService` 都无法使用 Cron 表达式指定任务执行的具体时间。 + +### Spring Task + +![](https://img-blog.csdnimg.cn/20210528145056880.png) + +我们直接通过 Spring 提供的 `@Scheduled` 注解即可定义定时任务,非常方便! + +```java +/** + * cron:使用Cron表达式。 每分钟的1,2秒运行 + */ +@Scheduled(cron = "1-2 * * * * ? ") +public void reportCurrentTimeWithCronExpression() { + log.info("Cron Expression: The time is now {}", dateFormat.format(new Date())); +} + +``` + +我在大学那会做的一个 SSM 的企业级项目,就是用的 Spring Task 来做的定时任务。 + +并且,Spring Task 还是支持 **Cron 表达式** 的。Cron 表达式主要用于定时作业(定时任务)系统定义执行时间或执行频率的表达式,非常厉害,你可以通过 Cron 表达式进行设置定时任务每天或者每个月什么时候执行等等操作。咱们要学习定时任务的话,Cron 表达式是一定是要重点关注的。推荐一个在线 Cron 表达式生成器:[http://cron.qqe2.com/](http://cron.qqe2.com/) 。 + +但是,Spring 自带的定时调度只支持单机,并且提供的功能比较单一。之前写过一篇文章:[《5 分钟搞懂如何在 Spring Boot 中 Schedule Tasks》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485563&idx=1&sn=7419341f04036a10b141b74624a3f8c9&chksm=cea247b0f9d5cea6440759e6d49b4e77d06f4c99470243a10c1463834e873ca90266413fbc92&token=2133161636&lang=zh_CN#rd) ,不了解的小伙伴可以参考一下。 + +Spring Task 底层是基于 JDK 的 `ScheduledThreadPoolExecutor` 线程池来实现的。 + +**优缺点总结:** + +- 优点: 简单,轻量,支持 Cron 表达式 +- 缺点 :功能单一 + +### 时间轮 + +Kafka、Dubbo、ZooKeeper、Netty 、Caffeine 、Akka 中都有对时间轮的实现。 + +时间轮简单来说就是一个环形的队列(底层一般基于数组实现),队列中的每一个元素(时间格)都可以存放一个定时任务列表。 + +时间轮中的每个时间格代表了时间轮的基本时间跨度或者说时间精度,加入时间一秒走一个时间格的话,那么这个时间轮的最高精度就是 1 秒(也就是说 3 s 和 3.9s 会在同一个时间格中)。 + +下图是一个有 12 个时间格的时间轮,转完一圈需要 12 s。当我们需要新建一个 3s 后执行的定时任务,只需要将定时任务放在下标为 3 的时间格中即可。当我们需要新建一个 9s 后执行的定时任务,只需要将定时任务放在下标为 9 的时间格中即可。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javaguide/20210607171334861.png) + +那当我们需要创建一个 13s 后执行的定时任务怎么办呢?这个时候可以引入一叫做 **圈数/轮数** 的概念,也就是说这个任务还是放在下标为 3 的时间格中, 不过它的圈数为 2 。 + +除了增加圈数这种方法之外,还有一种 **多层次时间轮** (类似手表),Kafka 采用的就是这种方案。 + +针对下图的时间轮,我来举一个例子便于大家理解。 + +![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/javaguide/20210607193042151.png) + +上图的时间轮,第 1 层的时间精度为 1 ,第 2 层的时间精度为 20 ,第 3 层的时间精度为 400。假如我们需要添加一个 350s 后执行的任务 A 的话(当前时间是 0s),这个任务会被放在第 2 层(因为第二层的时间跨度为 20\*20=400>350)的第 350/20=17 个时间格子。 + +当第一层转了 17 圈之后,时间过去了 340s ,第 2 层的指针此时来到第 17 个时间格子。此时,第 2 层第 17 个格子的任务会被移动到第 1 层。 + +任务 A 当前是 10s 之后执行,因此它会被移动到第 1 层的第 10 个时间格子。 + +这里在层与层之间的移动也叫做时间轮的升降级。参考手表来理解就好! + +![](https://img-blog.csdnimg.cn/20210607195206797.png) + +**时间轮比较适合任务数量比较多的定时任务场景,它的任务写入和执行的时间复杂度都是 0(1)。** + +## 分布式定时任务技术选型有哪些? + +上面提到的一些定时任务的解决方案都是在单机下执行的,适用于比较简单的定时任务场景比如每天凌晨备份一次数据。 + +如果我们需要一些高级特性比如支持任务在分布式场景下的分片和高可用的话,我们就需要用到分布式任务调度框架了。 + +通常情况下,一个定时任务的执行往往涉及到下面这些角色: + +- **任务** : 首先肯定是要执行的任务,这个任务就是具体的业务逻辑比如定时发送文章。 +- **调度器** :其次是调度中心,调度中心主要负责任务管理,会分配任务给执行器。 +- **执行器** : 最后就是执行器,执行器接收调度器分派的任务并执行。 + +### Quartz + +![](https://img-blog.csdnimg.cn/2021052814502425.png) + +一个很火的开源任务调度框架,完全由`Java`写成。`Quartz` 可以说是 Java 定时任务领域的老大哥或者说参考标准,其他的任务调度框架基本都是基于 `Quartz` 开发的,比如当当网的`elastic-job`就是基于`quartz`二次开发之后的分布式调度解决方案。 + +使用 `Quartz` 可以很方便地与 `Spring` 集成,并且支持动态添加任务和集群。但是,`Quartz` 使用起来也比较麻烦,API 繁琐。 + +并且,`Quzrtz` 并没有内置 UI 管理控制台,不过你可以使用 [quartzui](https://github.com/zhaopeiym/quartzui) 这个开源项目来解决这个问题。 + +另外,`Quartz` 虽然也支持分布式任务。但是,它是在数据库层面,通过数据库的锁机制做的,有非常多的弊端比如系统侵入性严重、节点负载不均衡。有点伪分布式的味道。 + +**优缺点总结:** + +- 优点: 可以与 `Spring` 集成,并且支持动态添加任务和集群。 +- 缺点 :分布式支持不友好,没有内置 UI 管理控制台、使用麻烦(相比于其他同类型框架来说) + +### Elastic-Job + +![](https://img-blog.csdnimg.cn/20210528144508114.png) + +`Elastic-Job` 是当当网开源的一个基于`Quartz`和`ZooKeeper`的分布式调度解决方案,由两个相互独立的子项目 `Elastic-Job-Lite` 和 `Elastic-Job-Cloud` 组成,一般我们只要使用 `Elastic-Job-Lite` 就好。 + +`ElasticJob` 支持任务在分布式场景下的分片和高可用、任务可视化管理等功能。 + +![](https://img-blog.csdnimg.cn/20210608080437356.png) + +ElasticJob-Lite 的架构设计如下图所示: + +![](https://oscimg.oschina.net/oscnet/up-a8f63f828666d43009d5d3497bcbd2cfb61.png) + +从上图可以看出,`Elastic-Job` 没有调度中心这一概念,而是使用 `ZooKeeper` 作为注册中心,注册中心负责协调分配任务到不同的节点上。 + +Elastic-Job 中的定时调度都是由执行器自行触发,这种设计也被称为去中心化设计(调度和处理都是执行器单独完成)。 + +```java +@Component +@ElasticJobConf(name = "dayJob", cron = "0/10 * * * * ?", shardingTotalCount = 2, + shardingItemParameters = "0=AAAA,1=BBBB", description = "简单任务", failover = true) +public class TestJob implements SimpleJob { + @Override + public void execute(ShardingContext shardingContext) { + log.info("TestJob任务名:【{}】, 片数:【{}】, param=【{}】", shardingContext.getJobName(), shardingContext.getShardingTotalCount(), + shardingContext.getShardingParameter()); + } +} +``` + +**相关地址:** + +- Github 地址:https://github.com/apache/shardingsphere-elasticjob。 +- 官方网站:https://shardingsphere.apache.org/elasticjob/index_zh.html 。 + +**优缺点总结:** + +- 优点 :可以与 `Spring` 集成、支持分布式、支持集群、性能不错 +- 缺点 :依赖了额外的中间件比如 Zookeeper(复杂度增加,可靠性降低、维护成本变高) + +### XXL-JOB + +![](https://img-blog.csdnimg.cn/20210528144611728.png) + +`XXL-JOB` 于 2015 年开源,是一款优秀的轻量级分布式任务调度框架,支持任务可视化管理、弹性扩容缩容、任务失败重试和告警、任务分片等功能, + +![](https://img-blog.csdnimg.cn/20210608080550433.png) + +根据 `XXL-JOB` 官网介绍,其解决了很多 `Quartz` 的不足。 + +![](https://img-blog.csdnimg.cn/20210607202503193.png) + +`XXL-JOB` 的架构设计如下图所示: + +![](https://oscimg.oschina.net/oscnet/up-b8ecc6acf651f112c4dfae98243d72adea3.png) + +从上图可以看出,`XXL-JOB` 由 **调度中心** 和 **执行器** 两大部分组成。调度中心主要负责任务管理、执行器管理以及日志管理。执行器主要是接收调度信号并处理。另外,调度中心进行任务调度时,是通过自研 RPC 来实现的。 + +不同于 `Elastic-Job` 的去中心化设计, `XXL-JOB` 的这种设计也被称为中心化设计(调度中心调度多个执行器执行任务)。 + +和 `Quzrtz` 类似 `XXL-JOB` 也是基于数据库锁调度任务,存在性能瓶颈。不过,一般在任务量不是特别大的情况下,没有什么影响的,可以满足绝大部分公司的要求。 + +不要被 `XXL-JOB` 的架构图给吓着了,实际上,我们要用 `XXL-JOB` 的话,只需要重写 `IJobHandler` 自定义任务执行逻辑就可以了,非常易用! + +```java +@JobHandler(value="myApiJobHandler") +@Component +public class MyApiJobHandler extends IJobHandler { + + @Override + public ReturnT execute(String param) throws Exception { + //...... + return ReturnT.SUCCESS; + } +} +``` + +还可以直接基于注解定义任务。 + +```java +@XxlJob("myAnnotationJobHandler") +public ReturnT myAnnotationJobHandler(String param) throws Exception { + //...... + return ReturnT.SUCCESS; +} +``` + +![](https://img-blog.csdnimg.cn/20210607200728212.png) + +**相关地址:** + +- Github 地址:https://github.com/xuxueli/xxl-job/。 +- 官方介绍:https://www.xuxueli.com/xxl-job/ 。 + +**优缺点总结:** + +- 优点:开箱即用(学习成本比较低)、与 Spring 集成、支持分布式、支持集群、内置了 UI 管理控制台。 +- 缺点:不支持动态添加任务(如果一定想要动态创建任务也是支持的,参见:[xxl-job issue277](https://github.com/xuxueli/xxl-job/issues/277))。 + +### PowerJob + +![](https://img-blog.csdnimg.cn/20210528145009701.png) + +非常值得关注的一个分布式任务调度框架,分布式任务调度领域的新星。目前,已经有很多公司接入比如 OPPO、京东、中通、思科。 + +这个框架的诞生也挺有意思的,PowerJob 的作者当时在阿里巴巴实习过,阿里巴巴那会使用的是内部自研的 SchedulerX(阿里云付费产品)。实习期满之后,PowerJob 的作者离开了阿里巴巴。想着说自研一个 SchedulerX,防止哪天 SchedulerX 满足不了需求,于是 PowerJob 就诞生了。 + +更多关于 PowerJob 的故事,小伙伴们可以去看看 PowerJob 作者的视频 [《我和我的任务调度中间件》](https://www.bilibili.com/video/BV1SK411A7F3/)。简单点概括就是:"游戏没啥意思了,我要扛起了新一代分布式任务调度与计算框架的大旗!"。 + +由于 SchedulerX 属于人民币产品,我这里就不过多介绍。PowerJob 官方也对比过其和 QuartZ、XXL-JOB 以及 SchedulerX。 + +![](https://oscimg.oschina.net/oscnet/up-795f5e9b0d875063717b1ee6a08f2ff1c01.png) + +## 总结 + +这篇文章中,我主要介绍了: + +- **定时任务的相关概念** :为什么需要定时任务、定时任务中的核心角色、分布式定时任务。 +- **定时任务的技术选型** : XXL-JOB 2015 年推出,已经经过了很多年的考验。XXL-JOB 轻量级,并且使用起来非常简单。虽然存在性能瓶颈,但是,在绝大多数情况下,对于企业的基本需求来说是没有影响的。PowerJob 属于分布式任务调度领域里的新星,其稳定性还有待继续考察。ElasticJob 由于在架构设计上是基于 Zookeeper ,而 XXL-JOB 是基于数据库,性能方面的话,ElasticJob 略胜一筹。 + +这篇文章并没有介绍到实际使用,但是,并不代表实际使用不重要。我在写这篇文章之前,已经动手写过相应的 Demo。像 Quartz,我在大学那会就用过。不过,当时用的是 Spring 。为了能够更好地体验,我自己又在 Spring Boot 上实际体验了一下。如果你并没有实际使用某个框架,就直接说它并不好用的话,是站不住脚的。 + +最后,这篇文章要感谢艿艿的帮助,写这篇文章的时候向艿艿询问过一些问题。推荐一篇艿艿写的偏实战类型的硬核文章:[《Spring Job?Quartz?XXL-Job?年轻人才做选择,艿艿全莽~》](https://mp.weixin.qq.com/s?__biz=MzUzMTA2NTU2Ng==&mid=2247490679&idx=1&sn=25374dbdcca95311d41be5d7b7db454d&chksm=fa4963c6cd3eead055bb9cd10cca13224bb35d0f7373a27aa22a55495f71e24b8273a7603314&scene=27#wechat_redirect) 。 \ No newline at end of file diff --git "a/docs/f-2347円263円273円347円273円237円350円256円276円350円256円241円351円235円242円350円257円225円346円214円207円345円214円227円.md" "b/docs/f-2347円263円273円347円273円237円350円256円276円350円256円241円351円235円242円350円257円225円346円214円207円345円214円227円.md" deleted file mode 100644 index 8989576..0000000 --- "a/docs/f-2347円263円273円347円273円237円350円256円276円350円256円241円351円235円242円350円257円225円346円214円207円345円214円227円.md" +++ /dev/null @@ -1,232 +0,0 @@ - - -## 6.2 系统设计面试指北 - -**系统设计在面试中一定是最让面试者头疼的事情之一。** 因为系统设计相关的问题通常是开放式的,所以没有标准答案。你在和面试官思想的交流碰撞中会慢慢优化自己的系统设计方案。理论上来说,系统设计面试也是和面试官一起一步一步改进原有系统设计方案的过程。 - -系统设计题往往也非常能考察出面试者的综合能力,回答好的话,很容易就能在面试中脱颖而出。不论是对于参加社招还是校招的小伙伴,都很有必要重视起来。 - -接下来,我会带着小伙伴们从我的角度出发来谈谈:**如何准备面试中的系统设计部分**。 - -由于文章篇幅有限,就不列举实际例子了,可能会在后面的文章中单独提一些具体的例子。 - -*个人能力有限。如果文章有任何需要改善和完善的地方,欢迎在评论区指出,共同进步!* - -### 6.2.1 系统设计面试一般怎么问? - -我简单总结了一下系统设计面试相关问题的问法: - -1. 设计一个某某系统比如秒杀系统、微博系统、抢红包系统、短网址系统。 -2. 设计某某系统中的一个功能比如哔哩哔哩的点赞功能。 -3. 设计一个框架比如 RPC 框架、消息队列、缓存框架、分布式文件系统等等。 -4. 某某系统的技术选型比如缓存用`Redis` 还是 `Memcached`、网关用 `Spring Cloud Gateway` 还是` Netflix Zuul2` 。 - -### 6.2.2 系统设计怎么做? - -我们将步骤总结成了以下 4 步。 - -#### Step1:问清楚系统具体要求 - -**当面试官给出了系统设计题目之后,一定不要立即开始设计解决方案。** 你需要先理解系统设计的需求:功能性需求和非功能性需求。 - -为了避免自己曲解题目所想要解决的问题,你可以先简要地给面试官说说自己的理解, - -**为啥要询问清楚系统的功能性需求也就是说系统包含哪些功能呢?** - -毕竟,如果面试官冷不丁地直接让你设计一个微博系统,你不可能把微博系统涵盖的功能比如推荐信息流、会员机制等一个一个都列举出来,然后再去设计吧!你需要筛选出系统所提供的核心功能(**缩小边界范围**)! - -**为啥要询问清楚系统的非功能性需求或者说约束条件比如系统需要达到多少QPS呢?** - -让你设计一个1w人用的微博系统和100w人用的微博系统能一样么?不同的约束系统对应的系统设计方案肯定是不一样的。 - -#### Step2:对系统进行抽象设计 - -我们需要在一个 High Level 的层面对系统进行设计。 - -你可以画出系统的抽象架构图,这个抽象架构图中包含了系统的一些组件以及这些组件之间的连接。 - -![](https://cdn.jsdelivr.net/gh/javaguide-tech/blog-images/IDEA%E6%8F%92%E4%BB%B6/1*lcomA7YV5En0NricVxy0rQ.png) - - - -#### Step3:考虑系统目前需要优化的点 - -对系统进行抽象设计之后,你需要思考当前抽象的系统设计有哪些需要优化的点,比如说: - -1. 当前系统部署在一台机器够吗?是否需要部署在多台机器然后进行负载均衡呢? -2. 数据库处理速度能否支撑业务需求?是否需要给指定字段加索引?是否需要读写分离?是否需要缓存? -3. 数据量是否大到需要分库分表? -4. 是否存在安全隐患? -5. 系统是否需要分布式文件系统? -6. ...... - -#### Step4:优化你的系统抽象设计 - -根据 Step 3 中的"系统需要优化的点" 对系统的抽象设计做进一步完善。 - -### 6.2.3 系统设计该如何准备? - -#### 知识储备 - -系统设计面试非常考察你的知识储备,系统设计能力的提高需要大量的理论知识储备。比如说你要知道大型网站架构设计必备的三板斧: - -1. **高性能架构设计**: 熟悉系统常见性能优化手段比如引入 **读写分离**、**缓存**、负载均衡、**异步** 等等。 -2. **高可用架构设计** :CAP理论和BASE理论、通过集群来提高系统整体稳定性、超时和重试机制、应对接口级故障:**降级**、**熔断**、**限流**、排队。 -3. **高扩展架构设计** :说白了就是懂得如何拆分系统。你按照不同的思路来拆分软件系统,就会得到不同的架构。 - -#### 实战 - -虽然懂得了理论,但是自己没有进行实践的话,很多东西是无法体会到的! - -因此,你还要 **不断通过实战项目锻炼自己的系统设计能力。** - -#### 保持好奇心 - -多思考自己经常浏览的网站是怎么做的。比如: - -1. 你刷微博的时候可以思考一下微博是如何记录点赞数量的? -2. 你看哔哩哔哩的时候可以思考一下消息提醒系统是如何做的? -3. 你使用短链系统的时候可以考虑一下短链系统是如何做的? -4. ...... - -#### 技术选型 - -实现同样的功能,一般会有多种技术选择方案,比如缓存用`Redis` 还是 `Memcached`、网关用 `Spring Cloud Gateway` 还是` Netflix Zuul2` 。 很多时候,面试官在系统设计面过程中会具体到技术的选型,因而,你需要区分不同技术的优缺点。 - -### 6.2.4 系统设计面试必知 - -> 系统设计的时候必然离不开描述性能相关的指标比如 QPS。 - -#### 性能相关的指标 - -- **响应时间** : 响应时间RT(Response-time)就是用户发出请求到用户收到系统处理结果所需要的时间。 RT是一个非常重要且直观的指标,RT数值大小直接反应了系统处理用户请求速度的快慢。 -- **并发数** : 并发数可以简单理解为系统能够同时供多少人访问使用也就是说系统同时能处理的请求数量。并发数反应了系统的负载能力。 -- **吞吐量** : 吞吐量指的是系统单位时间内系统处理的请求数量。一个系统的吞吐量与请求对系统的资源消耗等紧密关联。请求对系统资源消耗越多,系统吞吐能力越低,反之则越高。 -- **QPS 和 TPS** : - - **QPS(Query Per Second)** :服务器每秒可以执行的查询次数; - - **TPS(Transaction Per Second)** :服务器每秒处理的事务数(这里的一个事务可以理解为客户发出请求到收到服务器的过程); - -书中是这样描述 QPS 和 TPS 的区别的。 - -> QPS vs TPS:QPS 基本类似于 TPS,但是不同的是,对于一个页面的一次访问,形成一个TPS;但一次页面请求,可能产生多次对服务器的请求,服务器对这些请求,就可计入"QPS"之中。如,访问一个页面会请求服务器2次,一次访问,产生一个"T",产生2个"Q"。 - -TPS、QPS都是吞吐量的常用量化指标。 - -- **QPS(TPS)** = 并发数/平均响应时间(RT) -- **并发数** = QPS * 平均响应时间(RT) - -#### 系统活跃度 - -介绍几个描述系统活跃度的常见名词,建议牢牢记住。你不光会在回答系统设计面试题的时候碰到,日常工作中你也会经常碰到这些名词。 - -- **PV(Page View)** : 访问量, 即页面浏览量或点击量,衡量网站用户访问的网页数量;在一定统计周期内用户每打开或刷新一个页面就记录1次,多次打开或刷新同一页面则浏览量累计。UV 从网页打开的数量/刷新的次数的角度来统计的。 -- **UV(Unique Visitor)** :独立访客,统计1天内访问某站点的用户数。1天内相同访客多次访问网站,只计算为1个独立访客。UV 是从用户个体的角度来统计的。 -- **DAU(Daily Active User)** : 日活跃用户数量。 -- **MAU(monthly active users)** : 月活跃用户人数。 - -举例:某网站 DAU为 1200w, 用户日均使用时长 1 小时,RT为0.5s,求并发量和QPS。 - -平均并发量 = DAU(1200w)* 日均使用时长(1 小时,3600秒) /一天的秒数(86400)=1200w/24 = 50w - -真实并发量(考虑到某些时间段使用人数比较少) = DAU(1200w)* 日均使用时长(1 小时,3600秒) /一天的秒数-访问量比较小的时间段假设为8小时(57600)=1200w/16 = 75w - -峰值并发量 = 平均并发量 * 6 = 300w - -QPS = 真实并发量/RT = 75W/0.5=100w/s - -#### 常用性能测试工具 - -既然系统设计涉及到系统性能方面的问题,那在面试的时候,面试官就很可能会问:**你是如何进行性能测试的?** - -推荐 4 个后端比较常用的性能测试工具: - -1. **Jmeter** :Apache JMeter 是 JAVA 开发的性能测试工具。 -2. **LoadRunner**:一款商业的性能测试工具。 -3. **Galtling** :一款基于Scala 开发的高性能服务器性能测试工具。 -4. **ab** :全称为 Apache Bench 。Apache 旗下的一款测试工具,非常实用。 - -没记错的话,除了 **LoadRunner** 其他几款性能测试工具都是开源免费的。 - -再推荐 2 个前端比较常用的性能测试工具: - -1. **Fiddler**:抓包工具,它可以修改请求的数据,甚至可以修改服务器返回的数据,功能非常强大,是Web 调试的利器。 -2. **HttpWatch**: 可用于录制HTTP请求信息的工具。 - -#### 常见软件的QPS - -这里给出的 QPS 仅供参考,实际项目需要进行压测来计算。 - -- **Nginx** :一般情况下,系统的性能瓶颈基本不会是 Nginx。单机 Nginx 可以达到 30w +。 -- **Redis**: Redis 官方的性能测试报告:[https://redis.io/topics/benchmarks](https://redis.io/topics/benchmarks) 。从报告中,我们可以得出 Redis 的单机 QPS 可以达到 8w+(CPU性能有关系,也和执行的命令也有关系比如执行 SET 命令甚至可以达到10w+QPS)。 -- **MySQL**: MySQL 单机的 QPS 为 大概在 4k 左右。 -- **Tomcat** :单机 Tomcat 的QPS 在 2w左右。这个和你的 Tomcat 配置有很大关系,举个例子Tomcat 支持的连接器有 **NIO**、**NIO.2** 和 **APR**。 `AprEndpoint` 是通过 JNI 调用 APR 本地库而实现非阻塞 I/O 的,性能更好,Tomcat 配置 APR 为 连接器的话,QPS 可以达到 3w左右。更多相关内容可以自行搜索 Tomcat 性能优化。 - -#### 系统设计原则 - -**合适优于先进> 演化优于一步到位> 简单优于复杂** - -#### 常见的性能优化策略 - -性能优化之前我们需要对请求经历的各个环节进行分析,排查出可能出现性能瓶颈的地方,定位问题。 - -下面是一些性能优化时,我经常拿来自问的一些问题: - -1. 当前系统的SQL语句是否存在问题? -2. 当前系统是否需要升级硬件? -3. 系统是否需要缓存? -4. 系统架构本身是不是就有问题? -5. 系统是否存在死锁的地方? -6. 数据库索引使用是否合理? -7. 系统是否存在内存泄漏?(Java 的自动回收内存虽然很方便,但是,有时候代码写的不好真的会造成内存泄漏) -8. 系统的耗时操作进行了异步处理? -9. ...... - -#### 性能优化必知法则 - -**SQL优化,JVM、DB,Tomcat参数调优> 硬件性能优化(内存升级、CPU核心数增加、机械硬盘—>固态硬盘等等)> 业务逻辑优化/缓存> 读写分离、集群等> 分库分表** - -### 6.2.5 系统设计面试的注意事项 - -#### 想好再说 - -没必要面试官刚问了问题之后,你没准备好就开始回答。这样不会给面试官带来好印象的!系统设计本就需要面试者结合自己的以往的经验进行思考,这个过程是需要花费一些时间的。 - -#### 没有绝对的答案 - -系统设计没有标准答案。重要的是你和面试官一起交流的过程。 - -一般情况下,你会在和面试官的交流过程中,一步一步完成系统设计。这个过程中,你会在面试官的引导下不断完善自己的系统设计方案。 - -因此,你不必要在系统设计面试之前找很多题目,然后只是单纯记住他们的答案。 - -#### 勿要绝对 - -系统设计没有最好的设计方案,只有最合适的设计方案。这就类比架构设计了:**软件开发没有银弹,架构设计的目的就是选择合适的解决方案。** **何为银弹?** 狼人传说中,只有银弹(银质子弹)才能制服这些猛兽。对应到软件开发活动中,银弹特指开发者们寻求的一种克服软件开发这个难缠的猛兽的"万能钥匙🔑"。 - -#### 权衡利弊 - -知道使用某个技术可能会为系统带来的利弊。比如使用消息队列的好处是解耦和削峰,但是,同样也让系统可用性降低、复杂性提高,同时还会存在一致性问题(消息丢失或者消息未被消费咋办)。 - -#### 慢慢优化 - -刚开始设计的系统不需要太完美,可以慢慢优化。 - -#### 不追新技术 - -使用稳定的、适合业务的技术,不必要过于追求新技术。 - -#### 追简避杂 - -系统设计应当追求简单避免复杂。KISS( Keep It Simple, Stupid)原则——保持简单,易于理解。 - -### 6.2.6 总结 - -这篇文章简单带着小伙伴们分析了一下系统设计面试。如果你还想要深入学习的话,可以参考: [https://github.com/donnemartin/system-design-primer]( https://github.com/donnemartin/system-design-primer ) 。 - -![](https://guide-blog-images.oss-cn-shenzhen.aliyuncs.com/2020-11/image-20201104084854121.png) - -### 6.2.7 参考 - -1. https://github.com/donnemartin/system-design-primer -2. https://www.acecodeinterview.com/intro/ -3. https://gist.github.com/vasanthk/485d1c25737e8e72759f \ No newline at end of file diff --git "a/docs/h-345円276円256円346円234円215円345円212円241円.md" "b/docs/h-345円276円256円346円234円215円345円212円241円.md" deleted file mode 100644 index 0160fb2..0000000 --- "a/docs/h-345円276円256円346円234円215円345円212円241円.md" +++ /dev/null @@ -1,22 +0,0 @@ - - - - -# 八 微服务/分布式 - -概览(看看自己能回答几题): - -1. 为什么要网关? -2. 你知道有哪些常见的网关系统? -3. 限流的算法有哪些? -4. 为什么要分布式 id ? -5. 分布式 id 生成策略有哪些? -6. 了解RPC吗? -7. 有哪些常见的 RPC 框架? -8. 如果让你自己设计 RPC 框架你会如何设计? -9. Dubbo 了解吗? -10. Dubbo 提供了哪些负载均衡策略? -11. 谈谈你对微服务领域的了解和认识! - -答案地址:[https://t.zsxq.com/F6yrJiI](https://t.zsxq.com/F6yrJiI ) 。这部分内容的答案更新在[知识星球](http://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=100013795&idx=1&sn=aa2db4799c432bb944b6786ae0ec4c56&chksm=4ea1b92879d6303e9077546e2bc42a78f0cd3e18d9adb06e9f15e49e3d8337ec4bd384a25367#rd)。 -

AltStyle によって変換されたページ (->オリジナル) /