分布式微服务面试题汇总

1.分布式微服务概念

1.1、分布式

分布式就是指数据和程序可以不位于一个服务器上,而是分散到多个服务器,以网络上分散分布的地理信息数据及受其影响的数据库操作为研究对象的一种理论计算模型。分布式有利于任务在整个计算机系统上进行分配与优化,克服了传统集中式系统会导致中心主机资源紧张与响应瓶颈的缺陷,解决了网络GIS 中存在的数据异构、数据共享、运算复杂等问题,是地理信息系统技术的一大进步。

简单来说,分布式就是将一个大问题拆分成多个小问题,逐一解决,最终协同合作,而将多个系统协同合作完成一个特定任务,支持分布式处理的软件系统,就是我们所说的分布式系统,具体包括分布式操作系统、分布式程序设计语言及其编译系统分布式文件系统分布式数据库系统等等,这些也是分布式的关键技术。分布式架构系统的出现,其目的则是要利用更多的机器,去处理更多的数据。

1.2、微服务

单一职责: 微服务中每一个服务都对应唯一的业务能力,做到单一职责

微:微服务的服务的拆分粒度很小,每个服务虽小,但“五脏俱全”

面向服务:每个服务都要对外提供统一接口标准(API)

自治:服务间互相独立,互不干扰。

团队独立:每个服务就是一个独立的开发团队,人数可以不多

促进前后端分离:基本采用前后端分离开发,前端一般流行:vue angular react

2.高并发概念及解决方案

高并发是指系统在特定的时间内同时处理大量的并发请求。当系统出现高并发时,可能会导致系统的响应时间变慢、甚至崩溃或停机。通常高并发出现在系统流量激增的情况下,如节假日促销、大型活动或新闻事件等。

1.系统拆分,将一个系统拆分多个子系统,用dubbo,每个系统连成一个数据库,多数据库可以抗高并发

2.缓存:数据库和缓存各存一份数据,读数据从缓存里读,redis缓存走内存,天然抗高并发

3.MQ 消息队列:大量写请求灌入MQ,排队等待,后面系统消费慢慢玩,使用MQ异步写,提升并发性,MQ单机抗几万并发也是ok的。

4.分库分表:一个数据库拆分多个库,多个库抗击高并发,一个表拆分多个表,提高SQL的性能。

5.读写分离:主从架构,主库负责写,从库负责读,读流量大时,还可以增加更多的从库。

6.SolrCloud(solr 云)是Solr提供的分布式搜索方案,可以解决海量数据的 分布式全文检索,因为搭建了集群,因此具备高可用的特性,同时对数据进行主从备份,避免了单点故障问题。可以做到数据的快速恢复。并且可以动态的添加新的节点,再对数据进行平衡,可以做到负载均衡:

java如何处理并发?

1.使用缓存

2.使用生成静态页面 html纯静态页面是效率最高,消耗最小的页面

3.图片与服务器分离

4.代码构造的时候尽量避免不必要的资源浪费

<1>不要频繁的使用new对象,对于在整个应用中只需要存在一个实例的类使用单例模式。

对于string的连接操作,使用stringbuffer或者stringbuilder。对于utility类型的类通过静态方法来访问

<2>避免使用错误的方式,如exception可以控制方法推出,但是Exception要保留stacktrace消耗性能,

除非必要不要使用instanceof做条件判断,尽量使用比的条件判断方式。使用java中效率高的类,比如arraylist比vector快

<3>使用线程安全的集合对象

<4>使用线程池

线程池可以减少创建和销毁线程的次数,每个工作线程都可重复利用,可执行多个任务。

3.RPC和SOA、SOAP、REST的区别

SOA

SOAP

REST

表征状态转移。采用Web 服务使用标准的 HTTP 方法 (GET/PUT/POST/DELETE) 将所有 Web 系统的服务抽象为资源。

目前较为流行的一种组件通信方式。在微服务中有较多使用

REST不是一种协议,它是一种架构, 一种 Web Service 能够如果满足 REST 的几个条件, 通常就称这个系统是 Restful的

RPC

远程方法调用,就是像调用本地方法一样调用远程方法

dubbo就是一种RPC框架。他的通讯协议是RPC协议

4种典型RPC远程调用框架:RMI、Hessian、thrift、dubbo

Spring Cloud也是一种RPC框架,但是区别是它使用的是http协议(要区分应用层协议和传输协议)的传输,整体技术和普通RPC如

①dubbo[使用TCP协议]/thrift有很大区别

②微服务和SOA的区别

③微服务是SOA架构演进的结果。

两者说到底都是对外提供接口的一种架构设计方式,随着互联网的发展,复杂的平台、业务的出现,导致SOA架构向更细粒度、更通过化程度发展,就成了所谓的微服务了。

两者说到底都是对外提供接口的一种架构设计方式,随着互联网的发展,复杂的平台、业务的出现,导致SOA架构向更细粒度、更通过化程度发展,就成了所谓的微服务了。

微服务是SOA发展出来的产物,它是一种比较现代化的细粒度的SOA实现方式。

SOA与微服务的区别在于如下几个方面:

4.单体应用、SOA和微服务架构有什么区别

1、单体应用类似于一个大容器,其中程序的所有组件都被组装在一起并紧密包装;

2、SOA是一组相互通信的服务,通信可以涉及简单的数据传送,也可以涉及两个或多个协调某些活动的服务;

3、微服务架构是一种架构风格,它将应用程序构建为以业务域为模型的小型自治服务集合;

5.Dubbo



1.概述

Dubbo阿里巴巴开源的基于 Java 的高性能RPC(一种远程调用) 分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。

2.为什么使用Dubbo

因为是阿里开源项目,国内很多互联网公司都在用,已经经过很多线上考验。内部使用了NettyZookeeper,保证了高性能高可用性。

3.Dubbo能做什么

1、透明化的远程方法调用,就像调用本地方法一样调用远程方法,只需简单配置,没有任何API侵入。

2、软负载均衡及容错机制,可在内网替代F5等硬件负载均衡器,降低成本,减少单点。

3.、服务自动注册与发现,不再需要写死服务提供方地址,注册中心基于接口名查询服务提供者的IP地址,并且能够平滑添加或删除服务提供者。

Dubbo采用全Spring配置方式,透明化接入应用,对应用没有任何API侵入,只需用Spring加载Dubbo的配置即可,Dubbo基于Spring的Schema扩展进行加载。

4.Dubbo和Spring Cloud区别

1.通信方式不同:Dubbo 使用的是 RPC 通信,而Spring Cloud 使用的是HTTP RESTFul方式

2、组成不一样

①dubbo的服务注册中心为Zookeerper,服务监控中心为dubbo-monitor,无消息总线、服务跟踪、批量任务等组件;

②Spring Cloud的服务注册中心为spring-cloud netflix eureka,服务监控中心为spring-boot admin,有消息总线、数据流、服务跟踪、批量任务等组件;

6.SpringCloud

1.概述

1、SpringCloud是Spring旗下的项目之一,官网地址:http://projects.spring.io/spring-cloud/

2、Spring最擅长的就是集成,把世界上最好的框架拿过来,集成到自己的项目中。

3、SpringCloud也是一样,它将现在非常流行的一些技术整合到一起,实现了诸如: 配置管理,服务发现,智能路由,负载均衡,熔断器,控制总线,集群状态等等功能。

4、Spring Cloud由众多子项目组成,如Spring Cloud Config、Spring Cloud Netflix、Spring Cloud Consul 等,提供了搭建分布式系统及微服务常用的工具,如配置管理、服务发现、断路器、智能路由、微代理、控制总线、一次性token、全局锁、选主、分布式会话和集群状态等,满足了构建微服务所需的所有解决方案。比如使用Spring Cloud Config 可以实现统一配置中心,对配置进行统一管理;使用Spring Cloud Netflix 可以实现Netflix 组件的功能 - 服务发现(Eureka)、智能路由(Zuul)、客户端负载均衡(Ribbon)。

2.SpringBoot+SpringCloud架构


.SpringBoot+SpringCloud架构 .SpringBoot+SpringCloud架构图


3.为什么要使用SpringCloud?

单体框架存在的问题

1、所有的功能部署在一个进程内,功能模块间耦合性强

2、当出现服务器宕机,所有的功能都不可用;

3、随着后期功能的扩展和规模的扩大,业务越来越复杂,应用不断增加,服务越来越难维护

SpringCloud优点

缺点

4.SpringCloud怎么处理并发问题

保证服务发现的高可用性(Eureka、Nacos);

配置服务熔断和服务降级(Hystrix);

支持横向扩展服务,动态分配流量到服务器,减少服务器压力;

保证业务逻辑的处理速度

5.SpringBoot和SpringCloud之间有什么联系

①SpringBoot是最基本的微服务框架,SpringCloud是若干个包括但不限于SpringBoot框架的继承;

②SpringBoot的web默认继承了SpringMVC,可实现HTTP+JSON轻量传输

③SpringBoot+SpringCloud实现最核心的RPC技术

④SpringBoot可以单独使用不依赖SpringCloud,但SpringCloud离不开SpringBoot;

6.SpringBoot和SpringMVC的区别有哪些

①SpringMVC是单体框架,SpringBoot是SpringMVC的升级版,可作为单体框架,也可作为前后端分离的框架

②SpringMVC的前端页面是jsp,SpringBoot默认是HTML

③SpringMVC使用重量级的xml配置文件配置,并且需要程序员手动配置

④SpringBoot自动装配了基础的配置,并提供了properties或yml文件让程序员使用个性化配置

⑤SpringMVC必须借助外部容器才能启动,SpringBoot内置tomcat,并以main方法启动

7.springcloud的网关的作用

①统一入口:为全部微服务提供唯一入口点,网关起到外部和内部隔离,保障了后台服务的安全性。

②鉴权校验:识别每一个请求的权限,拒绝不符合要求的请求。

③动态路由:动态的将请求路由到不同的后端集群中。

④减少客户端与服务器的耦合性,服务器可以独立发展,通过网关层来做映射。

8.springcloud有哪些组件?

①服务与发现-Netflix Eureka:该系统下还分为Eureka服务端和Eureka客户端,Eureka服务端用作服务注册中心,支持集群部署。Eureka客户端是一个java客户端,用来处理服务注册与发现。

②负载均衡-Netflix Ribbon:基于Http和Tcp的客户端负载均衡,使得面向REST请求时变换为客户端的负载服务调用,提供客户端的软件负载均衡算法。

③断路器-Netflix Hystrix:它的作用是保护系统,控制故障范围。

④网关-Netflix Zuul:提供api网关,路由,负载均衡等作用。

⑤分布式配置-Springcloud Config:提供服务端和客户端,服务器存储后端的默认实现使用git

9.现用SpringCloud Alibaba 技术栈解决方案

10.服务发现产品对比


服务发现产品对比图


7.Token(令牌)

1.Access Token

1.简述

访问资源接口(API)时所需要的资源凭证

2.简单token的组成

3.特点

4.token的身份验证流程


token的身份验证流程图

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个token并把这个token发送给客户端
  4. 客户端收到token以后,会把它存储起来,比如放在cookie里或者localStorage里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的token
  6. 服务端收到请求,然后去验证客户端请求里面带着的token,如果验证成功,就向客户端返回请求的数据

注意点:

  1. 每一次请求都需要携带token,需要把token放到HTTP的Header里
  2. 基于token的用户认证是一种服务端无状态的认证方式,服务端不用存放token数据。用解析token的计算时间换取session的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
  3. token完全由应用管理,所以它可以避开同源策略

2.Refresh Token

refresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。


Refresh Token工作流程图


3.二者区别

4.Token的缺点

那有人就问了,既然 token 这么好,那为什么各个大公司几乎都采用共享 session 的方式呢,可能很多人是第一次听到 token,token 不香吗? token 有以下两点劣势:

所以 token 更适合一次性的命令认证,设置一个比较短的有效期!!!

拓展:不管是 cookie 还是 token,从存储角度来看其实都不安全(实际上防护 CSRF 攻击的正确方式是用 CSRF token),都有暴露的风险,我们所说的安全更多的是强调传输中的安全,可以用 HTTPS 协议来传输, 这样的话请求头都能被加密,也就保证了传输中的安全。

其实我们把 cookie 和 token 比较本身就不合理,一个是存储方式,一个是验证方式,正确的比较应该是 session vs token

5.Token 和 Session 的区别

Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。

Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。

所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。

其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。

所以简单来说:如果你的用户数据可能需要和第三方共享,或者允许第三方调用 API 接口,用 Token 。如果永远只是自己的网站,自己的 App,用什么就无所谓了

6.为什么要用Token

8.JWT

1. JWT 的原理


JWT 的原理图


2.JWT 认证流程

用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT

Authorization: Bearer 

3.JWT 的使用方式

客户端收到服务器返回的 JWT,可以储存在 Cookie 里面,也可以储存在 localStorage。

1.方式一

GET /calendar/v1/events
Host: api.example.com
Authorization: Bearer 

2.方式二

跨域的时候,可以把 JWT 放在 POST 请求的数据体里。

3.方式三

通过 URL 传输

http://www.example.com/user?token=xxx

项目中使用 JWT

项目地址:github.com/yjdjiayou/jw

4.Token 和 JWT 的区别

相同:

区别:

5.为什么要使用JWT

JWT(Json Web Token)是一种基于JSON的开放标准,用于在网络上以安全协议传输信息。JWT规范定义了一种简单的、独立的、可扩展的方法,可以在不同的应用程序之间安全地传输信息。它遵循一种客户端和服务器之间可信赖的方式,不需要在认证服务器上存储会话信息,因此在分布式系统中具有很高的扩展性。

优势:

9.分库分表

1.背景描述

2.遇到的问题

3.垂直拆分&&水平拆分

微服务架构时,业务切割得足够独立,数据也会按照业务切分,保证业务数据隔离,大大提升了数据库的吞吐能力


垂直分库图


表中字段太多且包含大字段的时候,在查询时对数据库的IO、内存会受到影响,同时更新数据时,产生的binlog文件会很大,MySQL在主从同步时也会有延迟的风险


垂直分表图



水平分表图



水平分库图


4.注意事项

5.分库后面临的问题

10.跨域问题


跨域问题


1.什么是跨域(CORS)

跨域(CORS)是指不同域名之间相互访问。跨域,指的是浏览器不能执行其他网站的脚本,它是由浏览器的同源策略所造成的,是浏览器对于JavaScript所定义的安全限制策略。

2.什么情况会跨域

以上三个条件中有一个条件不同就会产生跨域问题。

3.解决方案

前端解决方案

1、使用JSONP方式实现跨域调用;

2、使用NodeJS服务器做为服务代理,前端发起请求到NodeJS服务器, NodeJS服务器代理转发请求到后端服务器;

后端解决方案

1、nginx反向代理解决跨域

2、服务端设置Response Header(响应头部)的Access-Control-Allow-Origin

3、在需要跨域访问的类和方法中设置允许跨域访问(如Spring中使用@CrossOrigin注解);

4、继承使用Spring Web的CorsFilter(适用于Spring MVC、Spring Boot)

5、实现WebMvcConfigurer接口(适用于Spring Boot)

11.WebSocket

1.什么是WebSocket

WebSocket 是一种在单个TCP连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。

在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接, 并进行双向数据传输。

WebSocket本质上一种计算机网络应用层的协议,用来弥补http协议在持久通信能力上的不足。

WebSocket 协议在2008年诞生,2011年成为国际标准。现在最新版本浏览器都已经支持了。

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

2.WebSocket 的其他特点包括:

(1)建立在 TCP 协议之上,服务器端的实现比较容易。

(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。

(3)数据格式比较轻量,性能开销小,通信高效。

(4)可以发送文本,也可以发送二进制数据。

(5)没有同源限制,客户端可以与任意服务器通信。

(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

ws://example.com:80/some/path


WebSocket示例图


3.为什么需要 WebSocket?

我们已经有了 HTTP 协议,为什么还需要另一个协议?它能带来什么好处?

因为 HTTP 协议有一个缺陷:通信只能由客户端发起,不具备服务器推送能力。

举例来说,我们想了解查询今天的实时数据,只能是客户端向服务器发出请求,服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。



这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。我们只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。

在 WebSocket 协议出现以前,创建一个和服务端进双通道通信的 web 应用,需要依赖HTTP协议,进行不停的轮询,这会导致一些问题:

http协议本身是没有持久通信能力的,但是我们在实际的应用中,是很需要这种能力的,所以,为了解决这些问题,WebSocket协议由此而生,于2011年被IETF定为标准RFC6455,并被RFC7936所补充规范。

并且在HTML5标准中增加了有关WebSocket协议的相关api,所以只要实现了HTML5标准的客户端,就可以与支持WebSocket协议的服务器进行全双工的持久通信了。

4.WebSocket 与 HTTP 的区别

WebSocket 与 HTTP的关系图:


WebSocket 与 HTTP的关系图


相同点: 都是一样基于TCP的,都是可靠性传输协议。都是应用层协议。

联系: WebSocket在建立握手时,数据是通过HTTP传输的。但是建立之后,在真正传输时候是不需要HTTP协议的

下面一张图说明了 HTTP 与 WebSocket 的主要区别:


HTTP 与 WebSocket 的主要区别图


1、WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息,而HTTP是单向的

2、WebSocket是需要浏览器和服务器握手进行建立连接的,而http是浏览器发起向服务器的连接。

注意:虽然HTTP/2也具备服务器推送功能,但HTTP/2 只能推送静态资源,无法推送指定的信息。

5. WebSocket协议的原理

与http协议一样,WebSocket协议也需要通过已建立的TCP连接来传输数据。具体实现上是通过http协议建立通道,然后在此基础上用真正的WebSocket协议进行通信,所以WebSocket协议和http协议是有一定的交叉关系的。

首先,WebSocket 是一个持久化的协议,相对于 HTTP 这种非持久的协议来说。简单的举个例子吧,用目前应用比较广泛的 PHP 生命周期来解释。

HTTP 的生命周期通过 Request 来界定,也就是一个 Request 一个 Response ,那么在 HTTP1.0 中,这次 HTTP 请求就结束了。

在 HTTP1.1 中进行了改进,使得有一个 keep-alive,也就是说,在一个 HTTP 连接中,可以发送多个 Request,接收多个 Response。但是请记住 Request = Response, 在 HTTP 中永远是这样,也就是说一个 Request 只能有一个 Response。而且这个 Response 也是被动的,不能主动发起。

首先 WebSocket 是基于 HTTP 协议的,或者说借用了 HTTP 协议来完成一部分握手。

首先我们来看个典型的 WebSocket 握手

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw**
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

熟悉 HTTP 的童鞋可能发现了,这段类似 HTTP 协议的握手请求中,多了这么几个东西。

Upgrade: websocket
Connection: Upgrade

这个就是 WebSocket 的核心了,告诉 Apache 、 Nginx 等服务器:注意啦,我发起的请求要用 WebSocket 协议,快点帮我找到对应的助理处理~而不是那个老土的 HTTP。

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw**
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

首先, Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的,告诉服务器:泥煤,不要忽悠我,我要验证你是不是真的是 WebSocket 助理。

然后, Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议。简单理解:今晚我要服务A,别搞错啦~

最后, Sec-WebSocket-Version 是告诉服务器所使用的 WebSocket Draft (协议版本),在最初的时候,WebSocket 协议还在 Draft 阶段,各种奇奇怪怪的协议都有,而且还有很多期奇奇怪怪不同的东西,什么 Firefox 和 Chrome 用的不是一个版本之类的,当初 WebSocket 协议太多可是一个大难题。。不过现在还好,已经定下来啦~大家都使用同一个版本: 服务员,我要的是13岁的噢 _

然后服务器会返回下列东西,表示已经接受到请求, 成功建立 WebSocket 啦!

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

这里开始就是 HTTP 最后负责的区域了,告诉客户,我已经成功切换协议啦~

Upgrade: websocket
Connection: Upgrade

依然是固定的,告诉客户端即将升级的是 WebSocket 协议,而不是 mozillasocket,lurnarsocket 或者 shitsocket。

然后, Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key 。 服务器:好啦好啦,知道啦,给你看我的 ID CARD 来证明行了吧。

后面的, Sec-WebSocket-Protocol 则是表示最终使用的协议。

至此,HTTP 已经完成它所有工作了,接下来就是完全按照 WebSocket 协议进行了。

总结,WebSocket连接的过程是:

首先,客户端发起http请求,经过3次握手后,建立起TCP连接;http请求里存放WebSocket支持的版本号等信息,如:Upgrade、Connection、WebSocket-Version等;

然后,服务器收到客户端的握手请求后,同样采用HTTP协议回馈数据;

最后,客户端收到连接成功的消息后,开始借助于TCP传输信道进行全双工通信。

6.Websocket的优缺点

优点:

缺点:

7.WebSocket应用场景

不能使用WebSocket的场景

如果我们需要通过网络传输的任何实时更新或连续数据流,则可以使用WebSocket。如果我们要获取旧数据,或者只想获取一次数据供应用程序使用,则应该使用HTTP协议,不需要很频繁或仅获取一次的数据可以通过简单的HTTP请求查询,因此在这种情况下最好不要使用WebSocket

注意:如果仅加载一次数据,则RESTful Web服务足以从服务器获取数据。

8.websocket 断线重连

心跳就是客户端定时的给服务端发送消息,证明客户端是在线的, 如果超过一定的时间没有发送则就是离线了。

如何判断在线离线?

当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中,

第二次客户端定时再次发送请求依旧携带唯一标识、以及时间戳,服务端到db或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,

得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线;

如何解决断线问题?

通过查阅资料了解到 nginx 代理的 websocket 转发,无消息连接会出现超时断开问题。网上资料提到解决方案两种,一种是修改nginx配置信息,第二种是websocket发送心跳包。

下面就来总结一下本次项目实践中解决的websocket的断线 和 重连 这两个问题的解决方案。

主动触发包括主动断开连接,客户端主动发送消息给后端

1、主动断开连接

ws.close();

主动断开连接,根据需要使用,基本很少用到。

2、主动发送消息

ws.send("hello world");

针对websocket断线我们来分析一下,

心跳检测步骤:

1、客户端每隔一个时间间隔发生一个探测包给服务器

2、客户端发包时启动一个超时定时器

3、服务器端接收到检测包,应该回应一个包

4、如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器

5、如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了


// 前端解决方案:心跳检测
var heartCheck = {
    timeout: 30000, //30秒发一次心跳
    timeoutObj: null,
    serverTimeoutObj: null,
    reset: function(){
        clearTimeout(this.timeoutObj);
        clearTimeout(this.serverTimeoutObj);
        return this;
    },
    start: function(){
        var self = this;
        this.timeoutObj = setTimeout(function(){
            //这里发送一个心跳,后端收到后,返回一个心跳消息,
            //onmessage拿到返回的心跳就说明连接正常
            ws.send("ping");
            console.log("ping!")

            self.serverTimeoutObj = setTimeout(function(){//如果超过一定时间还没重置,说明后端主动断开了
                ws.close(); //如果onclose会执行reconnect,我们执行ws.close()就行了.如果直接执行reconnect 会触发onclose导致重连两次
            }, self.timeout);
        }, this.timeout);
    }
}

var ws = new ReconnectingWebSocket(url);
// 断线重连:
reconnectSocket(){
    if ('ws' in window) {
        ws = new ReconnectingWebSocket(url);
    } else if ('MozWebSocket' in window) {
       ws = new MozWebSocket(url);
    } else {
      ws = new SockJS(url);
    }
}

断网监测支持使用js库:offline.min.js


onLineCheck(){
    Offline.check();
    console.log(Offline.state,'---Offline.state');
    console.log(this.socketStatus,'---this.socketStatus');

    if(!this.socketStatus){
        console.log('网络连接已断开!');
        if(Offline.state **= 'up' && websocket.reconnectAttempts > websocket.maxReconnectInterval){
            window.location.reload();
        }
        reconnectSocket();
    }else{
        console.log('网络连接成功!');
        websocket.send("heartBeat");
    }
}

// 使用:在websocket断开链接时调用网络中断监测
websocket.onclose => () {
    onLineCheck();
};

9.WebSocket 怎么与前端建立长连接,怎么收发数据

1.在前端代码中创建一个 WebSocket 对象:

const socket = new WebSocket("ws://example.com/socket"); // 替换为实际的 WebSocket 服务器地址

2.监听 WebSocket 事件,包括连接建立、接收消息和错误处理:

socket.onopen = function() {
   console.log("WebSocket 连接已建立");
};

socket.onmessage = function(event) {
   const message = event.data;
   console.log("接收到消息:", message);
   // 在这里处理收到的消息
};

socket.onerror = function(error) {
   console.error("WebSocket 错误:", error);
};

3.发送数据给服务器:

socket.send("Hello, server!"); // 发送字符串数据

4.关闭 WebSocket 连接:

socket.close();

在服务器端,你需要实现一个 WebSocket 服务器来处理与前端的长连接。具体的实现方式会根据使用的编程语言和框架而有所不同。大多数编程语言都包含了 WebSocket 库,你可以使用该库来创建 WebSocket 服务器,并处理来自客户端的连接、消息和关闭事件。

在实际应用中,你可以使用 WebSocket 来实现实时聊天、即时通讯等功能,通过 WebSocket 的双向通信能力,实现前端与服务器之间的实时数据交互。

10.总结

11.Redis

1.Redis概述

一般情况下,redis是用来实现应用和数据库之间的一个读操作的缓冲层,主要目的是去减少数据库的IO,还可以提升数据的IO性能

整体架构如下:


Redis整体架构图


当应用程序需要去读取某个数据的时候,首先会先尝试Rdids里面去加载,如果命中了就直接返回,如果没有命中就直接从数据库里面查询,查询到数据之后再把数据缓存到Redis里面

2.如何保证Redis与数据库的数据保持一致

1.问题出现原因

一份数据同时保存在数据库和Redis里面,当数据发生变化的时候,需要同时去更新Redis和Mysql,由于更新是有先后顺序的,并且redis并不不想Mysql中的多表事务操作,可以满足ACID的特性,所以就会出现一个叫数据一致性的问题。

2.解决方案

1.先更新数据库,再更新缓存

如果先更新数据库再更新缓存,那么如果更新缓存失败就会导致数据和Redis中的数据是不一致的

2.先删缓存,再更新数据库

如果先删除缓存再更新数据库,理想情况下,是应用下次访问Redis的时候,发现Redis里面的数据是空的,那么就会从数据库加载保存到Redis里面,也就说数据理论上是一致的。

注意:如果极端情况下,由于删除Redis和更新数据库这两个操作并不是原子操作,所以在这个过程中,如果出现其他线程来访问,还是会出现数据不一致的问题。所以,如果需要在极端情况下,仍然去保证Reids和Mysql的数据一致性就只能采用最终一致性的方案。

基于RocketMQ的可靠性消息通信来实现数据的最终一致性或者可以直接通过Canal组件监控MySQL中binlog的日志,把更新后的数据同步到Redis里面,因为这里是基于最终一致性来实现的,如果业务场景不能去接受数据的短期不一致性,那么就不能使用这样的一个方案来实现。

3.Redis的优缺点

优点:

1 读写性能优异

2 支持数据持久化,支持AOF和RDB两种持久化方式

3 支持主从复制,主机会自动将数据同步到从机,可以进行读写分离

4 数据结构丰富:除了支持string类型的value外还支持string、hash、set、sortedset、list等数据结构。

缺点:

1 Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。

2 主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。

3 redis的主从复制采用全量复制,复制过程中主机会fork出一个子进程对内存做一份快照,并将子进程的内存快照保存为文件发送给从机,这一过程需要确保主机有足够多的空余内存。若快照文件较大,对集群的服务能力会产生较大的影响,而且复制过程是在从机新加入集群或者从机和主机网络断开重连时都会进行,也就是网络波动都会造成主机和从机间的一次全量的数据复制,这对实际的系统运营造成了不小的麻烦。

4 Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。为避免这一问题,运维人员在系统上线时必须确保有足够的空间,这对资源造成了很大的浪费。

4.数据类型

1、字符串(String):字符串是Redis中最基本的数据类型,它可以存储任何类型的数据,如文本、数字等。

2、哈希(Hash):哈希是一个键值对集合,类似于关联数组或者Java中的Map。哈希存储了多个字段和对应的值,可以用于存储对象数据。

3、列表(List):列表是一个有序的字符串列表。可以在列表的两端进行插入和删除操作,还支持获取指定范围的元素。列表可以被用作队列、栈等数据结构。

4、集合(Set):集合是一个无序的字符串集合,每个元素都是唯一的。集合支持添加、删除和查询元素,还支持求交集、并集等操作,适合处理一些不重复的数据。

5、有序集合(Sorted Set):有序集合是集合的扩展,每个元素都关联一个"分数",并按照分数进行排序。有序集合在增加、删除元素的同时会自动维护元素的顺序。

6、位图(Bitmap):位图是一种特殊的字符串数据类型,它可以被看作是一系列二进制位的集合。位图支持对位进行操作,例如设置某位的值、获取某位的值等,适用于处理位运算相关的场景。

5.应用场景

6.持久化方式

1.RDB持久化

RDB,就是把内存数据以快照的形式保存到磁盘中。和AOF相比,它记录的是某一个时刻的数据,而不是操作。RDB持久化,是指在指定时间间隔内,执行指定次数的写操作,将内存中的数据集快照写入磁盘中,它是Redis默认的持久化方式,执行完操作后,在指定的目录下会生成一个drump.rdb文件,Redis重启的时候,通过加载drump.rdb文件恢复数据。

2.RDB优点

3.AOF优缺点

优点:

缺点:

4.如何选择RDB和AOF

1、如果数据不能丢失,RDB和AOF混用

2、如果只作为缓存使用,可以承受几分钟的数据丢失的话,可以只使用RDB

3、如果只是用AOF,优先使用everysec的写回策略

7.缓存雪崩、击穿、穿透

1.缓存雪崩

如果缓存系统出现故障,所有的并发流量就会直接到达数据库

1.什么是缓存雪崩?

缓存雪崩指的是在同一时间,大量的Key同时失效,导致大量的请求直接绕开了我们的Redis直接打到数据库,数据库一下顶不住直接挂彩了,这就是缓存雪崩的场景。

如下图所示。


缓存雪崩图


造成缓存雪崩的主要原因就是缓存集中失效,或者缓存服务发生故障,瞬间的大并发流量压垮了数据库

2.解决方案

2.缓存击穿

1.什么是缓存击穿

接下来最后聊聊缓存击穿,有的同学可能容易把它和缓存穿透搞混,听起来意思很像,这里分析一下我的记法——巴雷特 (当然你也可以记成 AWM )。不知道大家玩过枪战游戏没,巴雷特是一把杀伤力巨大的狙击枪,单点的杀伤力爆炸,哪怕你站在门后都能把你一枪 击穿。  回到正题,缓存击穿有点类型我们刚刚举的小例子,它指的是对于一个非常热门的key在不停地扛着大并发需求,如果这个key瞬间失效了,此时瞬间大并发就直接到达了数据库,数据库瞬间挂彩,这感觉就像大木桶打穿了一个洞的感觉(没错就是巴雷特的感觉 )  缓存击穿一般是两个场景产生,注意我们强调的是并发的场景下:

1、该数据没有人查询过,所以缓存中没有,第一次就遭受大并发访问(某些冷门数据

2、缓存中存在,但恰好失效了,大并发访问瞬间落到数据库(热点数据


缓存击穿图


造成缓存击穿的主要原因就是:我们为缓存中的数据设置了过期时间。如果在某个时刻从数据库获取了大量的数据,并设置了相同的过期时间,这些缓存的数据会在同一时刻失效,造成缓存击穿问题。

2.解决方案

3.缓存穿透

首先,我们来说说缓存穿透。什么是缓存穿透呢?缓存穿透问题在一定程度上与缓存命中率有关。如果我们的缓存设计的不合理,缓存的命中率非常低,那么,数据访问的绝大部分压力都会集中在后端数据库层面。

1.什么是缓存穿透?

如果在请求数据时,在缓存层和数据库层都没有找到符合条件的数据,也就是说,在缓存层和数据库层都没有命中数据,那么,这种情况就叫做缓存穿透。如下图。


缓存穿透图


造成缓存穿透的主要原因就是:对于一些缓存和数据库中都不存在的数据,而用户却不断对该数据进行请求,如果你的数据库甚至没有建立索引,那么数据库还会进行全表扫描,压力更大。每次数据库都需要去进行查,然后查不到,然后又继续查,然后又查不到,然后又…最终数据库卒,这就是缓存穿透。

2.解决方案

8.和MySQL数据库的区别

MySQL数据库是一种关系型数据库管理系统(RDBMS),它使用结构化查询语言(SQL)来管理和处理数据。MySQL是一个广泛使用的数据库系统,它支持大型数据集和并发访问,并提供了强大的功能和稳定性。MySQL适用于各种应用场景,从简单的个人网站到大型企业级应用程序都可以使用它来存储和管理数据。

以下是MySQL的一些关键特点和优势:

Redis数据库是一种开源的高性能键值对存储系统,它提供了快速的读写速度和灵活的数据结构支持。Redis支持多种数据类型,包括字符串、哈希表、列表、集合和有序集合等,使它非常适合用于缓存、会话管理、实时统计等应用场景

以下是Redis的一些关键特点和优势:

9.Redis哨兵机制

1.概念

Reids的哨兵机制是一种用于监控和管理Redis主从复制和高可用性的机制

2.原理简述

在Redis中,哨兵(sentinel)是一种特殊的Redis实例,它 的主要任务是监控Redis主服务器(主节点/Master)和从服务器(从节点slave)的状态,并在主服务器出现故障时自动将一个从服务器升级为新的主服务器

哨兵通过周期性(每10秒会向主节点和从节点发送info命令获取最拓扑结构图)地向Redis服务器发送PING命令来检测服务器是否正常运行。如果一个主服务器在一定时间内没有响应哨兵的PING命令,哨兵会将该主服务器标记为主管下线。当多个哨兵都将同一个主服务器标志为主管下线时,该主服务器将被标志为客观下线


原理图


一旦主服务器被标志为客观下线,哨兵会执行故障转移操作,选择一个可用的从服务器升级为新的主服务器,并将其他从服务器重新配置为复制新的主服务器。这样可以确保系统的高可用性和数据的持久性。

哨兵还可以监控Redis的主从复制状态,如果从服务器断开连接或者复制过程中出现异常,哨兵会尝试重新连接或者重新同步数据。

通过哨兵机制,Redis可以实现自动的主从切换和故障恢复,提高系统的高可用和稳定性

12.nginx

1.什么是nginx和可以做什么事情

2.Nginx 作为 web 服务器

Nginx 可以作为静态页面的 web 服务器,同时还支持 CGI 协议的动态语言,比如 perl、php 等。但是不支持 java。Java 程序只能通过与 tomcat 配合完成。Nginx 专为性能优化而开发, 性能是其最重要的考量,实现上非常注重效率 ,能经受高负载的考验,有报告表明能支持高 达 50,000 个并发连接数。

3.正向代理

Nginx 不仅可以做反向代理,实现负载均衡。还能用作正向代理来进行上网等功能。 正向代理:如果把局域网外的 Internet 想象成一个巨大的资源库,则局域网中的客户端要访 问 Internet,则需要通过代理服务器来访问,这种代理服务就称为正向代理。

简单一点:通过代理服务器来访问服务器的过程就叫 正向代理。需要在客户端配置代理服务器进行指定网站访问

4.反向代理

5.负载均衡


负载均衡图

6.动静结合

为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。


动静结合图


13.如何解决超卖问题

1、悲观锁:在访问共享资源之前加锁,防止其他线程同时访问该资源。例如,在数据库中使用 SELECT … FOR UPDATE 语句锁定行记录。但是悲观锁的缺点是效率低,在高并发环境下容易导致性能问题。

2、乐观锁:在访问共享资源之前不加锁,通过版本号机制或者 CAS 算法等方式来控制并发操作。例如,在数据库中采用版本号机制实现乐观锁。当系统并发量较高时,乐观锁比悲观锁具有更好的性能表现。

3、预扣库存:在订单提交前,根据库存量计算每个订单的需要占用的库存数量,扣除对应的库存量,然后再提交订单。如果库存量不足,则提示用户库存不足,无法提交订单。但是预扣库存可能会带来死锁、性能降低等问题。

4、消息队列:将订单请求转化为消息发送到消息队列中,通过队列负载均衡的方式来决定哪个消费者应该处理该消息。消息队列通过缓存订单请求可以消除争用,提高并发性能。

14.分布式锁

1.概念

在单机系统下,如果多个线程同时访问一个变量或者代码片段就会产生多线程问题。(被访问的变量或者代码片段被称之为临界区域)这时我们就需要让所有线程按顺序一个一个执行对这个数据的操作就可以避免这个问题。但是在分布式的架构下,会有多台服务器同时运行,也就同时会有多个JVM运行,某一个JVM中的变量在其他JVM中是不可见的,所以简单的单机系统下的锁是无法解决分布式架构中的多线程的问题。我们就需要找到在多个JVM中都可见的锁来解决这个问题。这时就需要分布式锁。

2.分类

1.基于数据库实现的乐观锁

提到乐观锁就不得不提CAS(compare and swap)思想,顾名思义比较和交换,在准备做数据持久化时先比较数据库中的值与操作时的值是否相等,如果相等则进行修改,否则放弃操作或者进入循环。

CAS存在几点问题:

1.如果长时间没有操作成功线程一直在循环中,对CPU的开销过大。

2.只能保证一个变量的原子性操作。

3.存在ABA问题。

ABA问题如图



当线程1读入A数据后,线程1进入阻塞状态或者处理业务过长。这时线程2读入数据,将A改成B,随后线程3读入数据,将B改成A,这时线程1开始存入数据,将线程1读到的数据与数据库中的数据进行对比发现相同,这时可以将A改成C 。

表面上看好像并没有什么问题,但是遇到金融方面业务就会遇到问题。

例如:我的银行卡上有100元,这时我要取50元,由于某种原因我点击两次按钮,有线程1和线程2同时操作,线程1取走我50元后,线程2进入阻塞状态,这时有人往我卡上打入50元,线程2开始执行操作,在最后发现我账户上为100元与当时查到的相同,第二次成功取走50元导致发生多线程问题。

解决方法:加入版本号

在数据库中加入新的一列version,在每一次update成功后都进行+1,这样就可以完美避免ABA问题。SQL为update 表明 set data = data, version = version + 1 where version = version。

2.基于redis实现的分布式锁

redis分布式锁主要体现为对redis服务器中的key和value的抢占。当线程需要获取锁时就会向redis中插入一条key,value,如果插入成功就可以成功获取锁,如果redis中已经存在了该锁导致插入失败则获取锁失败。


上面模式的redis分布式锁存在以下问题



当线程1拿到锁后在执行业务时因为某些原因进入阻塞状态导致锁超时自动释放,这时线程2拿到锁,再线程2的业务没有执行完时,线程1从阻塞状态转换为运行状态,业务执行完成执行释放锁的代码,这是线程2拿到的锁被莫名奇妙的释放了,这就产生了问题。

解决方案有两种:

1.设置锁超时时间远大于业务执行时间

2.为每个线程拿到锁时添加唯一标识(建议在key上存储业务名,在value上存储uuid),删除锁时要判断锁与当前线程是否对应

针对解决方案2仍然存在问题:

在判断key是否相等然后删除锁这并不满足原子性



当线程1代码执行结束准备释放锁前判断锁和线程1是否对应,判断结束后线程1进入阻塞状态,还是会发生上面提到的问题。

解决方案:将判断线程和锁是否对应和释放锁这两步操作放到Lua脚本中执行。

3.基于zookeeper实现的分布式锁

zookeeper的分布式锁主要是利用了临时顺序节点的特性,使每个进来的线程按顺序执行。

首先建立一个持久节点作为父节点,当有线程需要执行被锁控制的代码时会先查询父节点下面是否有其他子节点,如果没有则在父节点下面创建一个临时顺序节点作为子节点,获得锁。



当线程2需要执行代码时,同样的还是会在父节点下创建一个临时顺序节点,然后判断自己是不是顺序最靠前的节点,如果不是则注册一个watcher用于监听前面的节点。之后进来的每一个节点都是监听其前面的一个节点。

如果线程执行结束,则会删除自己的节点,后面监听他的节点发现其被删除后即可开始执行。

15.前后端问题

1.前端获取后端数据的方式

1、AJAX 请求:AJAX 是一种异步的 HTTP 请求方式,它可以让页面无需重新加载就能从后端获取数据。前端通过 JavaScript 发送 AJAX 请求,后端将返回数据以 JSON 或 XML 格式回应,前端再进行相应的数据处理。

2、WebSocket:WebSocket 是一种双向通信协议,它可以实现服务器主动发送数据给客户端,并可以方便地处理大量数据传输。一般在需要实时通信的场景下使用,如聊天室或实时游戏。

3、Fetch API:Fetch API 是一个新的 Web 平台 API,可以用 JavaScript 异步获取网络资源,包括文本、JSON 和二进制数据。Fetch API 具有更好的可读性和可扩展性,是一种优秀的替代 XMLHttpRequest(XHR)方法的方式。

4、WebHooks:WebHooks 是通过在服务端注册一个回调 URL,当数据发生变动时,后端会向这个 URL 发送 HTTP 请求,通知前端数据更新。WebHooks 需要服务端的支持,需要前后端协同设计。

2. 常见的前后端鉴权方式

1、Session-Cookie

2、Token 验证(包括 JWT,SSO)

3、OAuth2.0(开放授权)

3.为什么要前后端分离?有什么优缺点

前后端分离指的是将前端和后端的开发分别进行,前端专注于UI设计和交互逻辑后端专注于业务逻辑和数据处理。该架构模式的兴起主要是应对云计算、大数据和移动互联网等新兴应用场景,其优缺点如下:

优点:

1、分离关注点:前端和后端各自关注自己的领域,降低了系统的耦合性,提高了可维护性和可扩展性。

2、可重用性:后端提供数据接口,前端根据接口规范开发,使得前后端可以独立完成,各自可以选择不同的开发语言和框架,提高了代码的可重用性。

3、独立开发:前后端可以在同一时间进行独立开发,不会相互影响,从而可以加快产品开发上线的速度。

4、并行协同:由于前后端是独立进行,因此可以采用并行协同的方式完成开发,从而更快地响应市场需求。

缺点:

1、技术门槛较高:由于前后端都需要开发技能和经验,因此需要更多人才参与,技术门槛较高。

2、API设计困难:分离后需要定义API接口规范,需要把握好接口设计粒度,定义好参数名称和类型等,需要更多技术和管理的沟通协调。

3、安全问题: 前端分离后,由于前端代码在浏览器中运行,因此可能会被攻击者利用安全漏洞而被盗取敏感信息。

16.缓存和淘汰策略

1、为什么需要缓存?

2.常用的缓存类型:

3.常见的淘汰策略:

最近最少使用(LRU):淘汰最近最少被访问过的数据,保留最常用的数据。

先进先出(FIFO):按照进入存储的顺序淘汰数据,最早进入的数据最先被淘汰。

最不经常使用(LFU):淘汰访问频率最低的数据,保留访问频率较高的数据。

随机替换:随机选择要被淘汰的数据。

定期清理:定期清理过期或无效的缓存数据。

17.消息队列

一、消息队列概述

消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以简单地概述为:

当不需要立即获得结果,但是并发量又需要进行控制的时候,差不多就是需要使用消息队列的时候。

消息队列主要解决了应用耦合异步处理、流量削峰等问题。

当前使用较多的消息队列有RabbitMQ、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMQ等,而部分数据库如Redis、Mysql以及phxsql也可以实现消息队列的功能。

二、消息队列的使用场景

消息队列在实际应用中包括如下四个场景:

下面详细介绍上述四个场景以及消息队列如何在上述四个场景中使用:

2.1 异步处理

具体场景:用户为了使用某个应用,进行注册,系统需要发送注册邮件并验证短信。对这两个操作的处理方式有两种:串行和并行。

(1)串行方式:新注册信息生成后,先发送注册邮件,再发送验证短信;


串行方式图


在这种方式下,需要最终发送验证短信后再返回给客户端。

(2)并行处理:新注册信息写入后,由发短信和发邮件并行处理;


并行处理图


在这种方式下,发短信和发邮件需要处理完成后再返回给客户端。

假设以上三个子系统处理的时间均为50ms,且不考虑网络延迟,则总的处理时间:

串行:50+50+50=150ms
并行:50+50=100ms

若使用消息队列:


使用消息队列图


并在写入消息队列后立即返回成功给客户端,则总的响应时间依赖于写入消息队列的时间,而写入消息队列的时间本身是可以很快的,基本可以忽略不计,因此总的处理时间相比串行提高了两倍,相比并行提高了一倍。

2.2 应用耦合

具体场景:用户使用QQ相册上传一张图片,人脸识别系统会对该图片进行人脸识别,一般的做法是,服务器接收到图片后,图片上传系统立即调用人脸识别系统,调用完成后再返回成功,如下图所示:



该方法有如下缺点:

若使用消息队列:



客户端上传图片后,图片上传系统将图片信息如uin、批次写入消息队列,直接返回成功;而人脸识别系统则定时从消息队列中取数据,完成对新增图片的识别。此时图片上传系统并不需要关心人脸识别系统是否对这些图片信息的处理、以及何时对这些图片信息进行处理。事实上,由于用户并不需要立即知道人脸识别结果,人脸识别系统可以选择不同的调度策略,按照闲时、忙时、正常时间,对队列中的图片信息进行处理。

2.3 限流削峰

具体场景:购物网站开展秒杀活动,一般由于瞬时访问量过大,服务器接收过大,会导致流量暴增,相关系统无法处理请求甚至崩溃。而加入消息队列后,系统可以从消息队列中取数据,相当于消息队列做了一次缓冲。



该方法有如下优点:

1、请求先入消息队列,而不是由业务处理系统直接处理,做了一次缓冲,极大地减少了业务处理系统的压力;

2、队列长度可以做限制,事实上,秒杀时,后入队列的用户无法秒杀到商品,这些请求可以直接被抛弃,返回活动已结束或商品已售完信息;

2.4 消息驱动的系统

具体场景:用户新上传了一批照片, 人脸识别系统需要对这个用户的所有照片进行聚类,聚类完成后由对账系统重新生成用户的人脸索引(加快查询)。这三个子系统间由消息队列连接起来,前一个阶段的处理结果放入队列中,后一个阶段从队列中获取消息继续处理。



该方法有如下优点:

三、消息队列的两种模式

消息队列包括两种模式,点对点模式(point to point, queue)和发布/订阅模式(publish/subscribe,topic)。

3.1 点对点模式

点对点模式下包括三个角色:


消息发送者生产消息发送到queue中,然后消息接收者从queue中取出并且消费消息。消息被消费以后,queue中不再有存储,所以消息接收者不可能消费到已经被消费的消息。

点对点模式特点:

3.2 发布/订阅模式

发布/订阅模式下包括三个角色:


发布者将消息发送到Topic,系统将这些消息传递给多个订阅者。

发布/订阅模式特点:

四、常用消息队列介绍

本部分主要介绍四种常用的消息队列(RabbitMQ/ActiveMQ/RocketMQ/Kafka)的主要特性、优点、缺点。

4.1 RabbitMQ

1.概念

RabbitMQ是一个开源的消息中间件,它基于AMQP协议,采用生产者-消费者模式,用于实现不同应用程序之间的异步消息传递

2.主要特性

1、可靠性: 提供了多种技术可以让你在性能和可靠性之间进行权衡。这些技术包括持久性机制、投递确认、发布者证实和高可用性机制;

2、灵活的路由: 消息在到达队列前是通过交换机进行路由的。RabbitMQ为典型的路由逻辑提供了多种内置交换机类型。如果你有更复杂的路由需求,可以将这些交换机组合起来使用,你甚至可以实现自己的交换机类型,并且当做RabbitMQ的插件来使用;

3、消息集群:在相同局域网中的多个RabbitMQ服务器可以聚合在一起,作为一个独立的逻辑代理来使用;

4、队列高可用:队列可以在集群中的机器上进行镜像,以确保在硬件问题下还保证消息安全;

5、多种协议的支持:支持多种消息队列协议;

6、服务器端用Erlang语言编写,支持只要是你能想到的所有编程语言;

7、管理界面: RabbitMQ有一个易用的用户界面,使得用户可以监控和管理消息Broker的许多方面;

8、跟踪机制:如果消息异常,RabbitMQ提供消息跟踪机制,使用者可以找出发生了什么;

9、插件机制:提供了许多插件,来从多方面进行扩展,也可以编写自己的插件;

3.使用需要及支持平台

使用RabbitMQ需要:

RabbitMQ可以运行在Erlang语言所支持的平台之上:

4.优缺点

优点

1、由于erlang语言的特性,mq 性能较好,高并发;

2、健壮、稳定、易用、跨平台、支持多种语言、文档齐全;

3、有消息确认机制和持久化机制,可靠性高;

4、高度可定制的路由;

5、管理界面较丰富,在互联网公司也有较大规模的应用;

6、社区活跃度高;

缺点

1、尽管结合erlang语言本身的并发优势,性能较好,但是不利于做二次开发和维护;

2、实现了代理架构,意味着消息在发送到客户端之前可以在中央节点上排队。此特性使得RabbitMQ易于使用和部署,但是使得其运行速度较慢,因为中央节点增加了延迟,消息封装后也比较大;

3、需要学习比较复杂的接口和协议,学习和维护成本较高;

5.rabbitmq怎么保证消息不重复消费

1、消费者应答模式:当消费者从队列中获取消息后,可以通过手动应答的方式告诉RabbitMQ已经成功处理了该消息。只有在消费者明确应答后,RabbitMQ才会将该消息从队列中删除。这样可以确保消息只会被消费一次。

2、消息持久化:通过将消息标记为持久化,可以确保即使在RabbitMQ服务器重启后,消息也不会丢失。

3、消息去重:在消费者端可以通过记录已经消费过的消息ID或者唯一标识,来判断是否重复消费。

6.项目中使用及设置topic值

在我的项目中,我们通常使用消费者应答模式和消息持久化来保证消息不重复消费消费者从队列中获取消息后,处理完毕后手动应答,确保消息被消费一次。同时,我们将消息标记为持久化,以防止消息丢失

关于设置topic,在RabbitMQ中,topic是一种用于根据消息的路由键进行匹配的交换机类型。通过设置topic,可以实现灵活的消息路由和订阅。在设置topic时,需要考虑以下几个方面:

1、定义topic的命名规范:根据业务需求和场景,定义合适的topic名称,以便于标识和管理。

2、设置topic的路由键:根据消息的特征和路由需求,设置合适的路由键。可以使用通配符(*和#)来实现模糊匹配。

3、绑定消费者队列:将消费者队列绑定到相应的topic上,以便消费者可以接收到匹配的消息。

在集群环境中,可以通过以下方式设置topic:

1、创建多个消费者队列:在集群环境中,可以创建多个消费者队列,并将它们绑定到相同的topic上。这样,当消费方发送一个消息时,RabbitMQ会将消息发送到所有绑定的队列中,从而实现消息的多播。

2、使用消费者组:可以为每个消费者组设置一个独立的队列,并将它们绑定到相同的topic上。这样,同一组的消费者会共享消息,但不同组的消费者不会重复消费。

4.2 ActiveMQ

ActiveMQ是由Apache出品,ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现。它非常快速,支持多种语言的客户端和协议,而且可以非常容易的嵌入到企业的应用环境中,并有许多高级功能。

主要特性:

1、服从 JMS 规范:JMS 规范提供了良好的标准和保证,包括:同步或异步的消息分发,一次和仅一次的消息分发,消息接收和订阅等等。遵从 JMS 规范的好处在于,不论使用什么 JMS 实现提供者,这些基础特性都是可用的;

2、连接性:ActiveMQ 提供了广泛的连接选项,支持的协议有:HTTP/S,IP 多播,SSL,STOMP,TCP,UDP,XMPP等等。对众多协议的支持让 ActiveMQ 拥有了很好的灵活性。

3、支持的协议种类多:OpenWire、STOMP、REST、XMPP、AMQP ;

4、持久化插件和安全插件:ActiveMQ 提供了多种持久化选择。而且,ActiveMQ 的安全性也可以完全依据用户需求进行自定义鉴权和授权;

5、支持的客户端语言种类多:除了 Java 之外,还有:C/C++,.NET,Perl,PHP,Python,Ruby;

6、代理集群:多个 ActiveMQ 代理可以组成一个集群来提供服务;

7、异常简单的管理:ActiveMQ 是以开发者思维被设计的。所以,它并不需要专门的管理员,因为它提供了简单又使用的管理特性。有很多中方法可以监控 ActiveMQ 不同层面的数据,包括使用在 JConsole 或者 ActiveMQ 的Web Console 中使用 JMX,通过处理 JMX 的告警消息,通过使用命令行脚本,甚至可以通过监控各种类型的日志

使用ActiveMQ需要:

ActiveMQ可以运行在Java语言所支持的平台之上。

优点:

1、跨平台(JAVA编写与平台无关有,ActiveMQ几乎可以运行在任何的JVM上)

2、可以用JDBC:可以将数据持久化到数据库。虽然使用JDBC会降低ActiveMQ的性能,但是数据库一直都是开发人员最熟悉的存储介质。将消息存到数据库,看得见摸得着。而且公司有专门的DBA去对数据库进行调优,主从分离;

3、支持JMS :支持JMS的统一接口;

4、支持自动重连;

5、有安全机制:支持基于shiro,jaas等多种安全配置机制,可以对Queue/Topic进行认证和授权。

6、监控完善:拥有完善的监控,包括Web Console,JMX,Shell命令行,Jolokia的REST API;

7、界面友善:提供的Web Console可以满足大部分情况,还有很多第三方的组件可以使用,如hawtio;缺点:

4.3 RocketMQ

RocketMQ出自 阿里公司的开源产品,用 Java 语言实现,在设计时参考了 Kafka,并做出了自己的一些改进,消息可靠性上比 Kafka 更好。RocketMQ在阿里集团被广泛应用在订单,交易,充值,流计算,消息推送,日志流式处理,binglog分发等场景。

主要特性:

1、是一个队列模型的消息中间件,具有高性能、高可靠、高实时、分布式特点;

2、Producer、Consumer、队列都可以分布式;

3、Producer向一些队列轮流发送消息,队列集合称为Topic,Consumer如果做广播消费,则一个consumer实例消费这个Topic对应的所有队列,如果做集群消费,则多个Consumer实例平均消费这个topic对应的队列集合;

4、能够保证严格的消息顺序;

5、提供丰富的消息拉取模式;

6、高效的订阅者水平扩展能力;

7、实时的消息订阅机制;

8、亿级消息堆积能力;

9、较少的依赖;

使用RocketMQ需要:

RocketMQ可以运行在Java语言所支持的平台之上。

优点:

1、单机支持 1 万以上持久化队列

2、RocketMQ 的所有消息都是持久化的,先写入系统 PAGECACHE,然后刷盘,可以保证内存与磁盘都有一份数据,访问时,直接从内存读取。

3、模型简单,接口易用(JMS 的接口很多场合并不太实用);

4、性能非常好,可以大量堆积消息在broker中;

5、支持多种消费,包括集群消费、广播消费等。

6、各个环节分布式扩展设计,主从HA;

7、开发度较活跃,版本更新很快。



缺点:

1、支持的客户端语言不多,目前是java及c++,其中c++不成熟;

2、RocketMQ社区关注度及成熟度也不及前两者;

3、没有web管理界面,提供了一个CLI(命令行界面)管理工具带来查询、管理和诊断各种问题;

4、没有在 mq 核心中去实现JMS等接口;

4.4 Kafka

Apache Kafka是一个分布式消息发布订阅系统。它最初由LinkedIn公司基于独特的设计实现为一个分布式的提交日志系统( a distributed commit log),之后成为Apache项目的一部分。Kafka系统快速、可扩展并且可持久化。它的分区特性,可复制和可容错都是其不错的特性。

主要特性:

1、快速持久化,可以在O(1)的系统开销下进行消息持久化;

2、高吞吐,在一台普通的服务器上既可以达到10W/s的吞吐速率;

3、完全的分布式系统,Broker、Producer、Consumer都原生自动支持分布式,自动实现负载均衡;

4、支持同步和异步复制两种HA;

5、支持数据批量发送和拉取;

6、zero-copy:减少IO操作步骤;

7、数据迁移、扩容对用户透明;

8、无需停机即可扩展机器;

9、其他特性:严格的消息顺序、丰富的消息拉取模型、高效订阅者水平扩展、实时的消息订阅、亿级的消息堆积能力、定期删除机制;

使用Kafka需要:

优点:

1、客户端语言丰富,支持java、.net、php、ruby、python、go等多种语言;

2、性能卓越,单机写入TPS约在百万条/秒,消息大小10个字节;

3、提供完全分布式架构, 并有replica机制, 拥有较高的可用性和可靠性, 理论上支持消息无限堆积;

4、支持批量操作;

5、消费者采用Pull方式获取消息, 消息有序, 通过控制能够保证所有消息被消费且仅被消费一次;

6、有优秀的第三方Kafka Web管理界面Kafka-Manager;

7、在日志领域比较成熟,被多家公司和多个开源项目使用;

缺点:

1、Kafka单机超过64个队列/分区,Load会发生明显的飙高现象,队列越多,load越高,发送消息响应时间变长

2、使用短轮询方式,实时性取决于轮询间隔时间;

3、消费失败不支持重试;

4、支持消息顺序,但是一台代理宕机后,就会产生消息乱序;

5、社区更新较慢;

4.5 四者对比

这里列举了上述四种消息队列的差异对比:


四种消息队列的差异对比图


18.搜索框架

1.Solr和Elasticsearch框架

Solr 和 Elasticsearch(ES)都是流行的开源搜索框架,用于实现高性能、可扩展和全文搜索。它们都基于 Apache Lucene 引擎,但在一些方面有所不同。

Solr 是一个基于 Java 的搜索平台,提供了一个简单而强大的 RESTful API,使用户可以通过 HTTP 请求进行搜索和索引操作。Solr 是一个独立的应用程序,使用 XML 或 JSON 配置文件进行配置。它支持丰富的功能,如分布式搜索、复杂查询语法、全文搜索、分面搜索、高亮显示、自定义插件等。Solr 还有一个用户友好的管理界面,可以方便地监控和管理搜索实例。

Elasticsearch 是一个分布式、实时的搜索和分析引擎,也是用 Java 编写的。ES 提供了一个 RESTful API,使用户可以通过 HTTP 请求进行索引和检索操作。ES 具有强大的集群和分片路由功能,以实现高可用性和可伸缩性。ES 的查询语法简洁易懂,支持全文搜索、文档聚合、地理位置搜索、自动建议等高级搜索特性。它还集成了 Kibana,一个用于数据可视化的工具。

相比而言,Solr 更适合构建复杂的搜索应用程序,提供了更多的高级功能和自定义选项。它的配置相对简单,适合处理较大的数据集。Solr 还有广泛的社区支持和文档资源。

ES 则更适合实时搜索和日志分析场景,具有更好的水平扩展性和集群管理功能。ES 的查询速度也较快,并且与 Kibana 的集成使得数据可视化更便捷。

简而言之,Solr 更注重功能的全面性和灵活性,而 ES 则更专注于实时性和扩展性。选择哪个搜索框架取决于具体的需求和应用场景。

19.幂等

1.概念

在计算机科学中,幂等是指对同一操作的多次执行所产生的结果是一致的。在分布式系统或者网络通信中,由于各种原因(例如网络超时、重试等),可能会导致同一操作被执行多次。为了确保系统的正确性和一致性,需要对幂等性进行处理。

2.处理方式

1、唯一标识符:为每个操作生成一个唯一的标识符,并将该标识符与操作关联。在执行操作之前,先检查该标识符是否已经存在,如果存在则说明操作已经执行过,可以直接返回之前的结果,避免重复执行。

2、幂等性检查:在执行操作之前,先检查操作的状态或者结果,判断是否已经执行过。例如,可以查询数据库或者缓存中的状态,如果已经存在相应的记录或者结果,可以直接返回之前的结果,避免重复执行。

3、乐观锁:在执行操作之前,先获取一个锁或者版本号,将其与操作关联。在执行操作时,先检查锁或者版本号是否匹配,如果匹配则说明操作已经执行过,可以直接返回之前的结果,避免重复执行。

4、幂等性标记:在执行操作之前,先为操作添加一个幂等性标记。在执行操作时,先检查该标记是否存在,如果存在则说明操作已经执行过,可以直接返回之前的结果,避免重复执行。

5、事务处理:在执行操作时,使用事务来确保操作的原子性和一致性。如果同一操作被多次执行,事务处理可以保证只有第一次执行会生效,后续的执行会被忽略。

展开阅读全文

页面更新:2024-05-12

标签:队列   分布式   缓存   客户端   协议   消息   服务器   数据库   数据   系统

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2008-2024 All Rights Reserved. Powered By bs178.com 闽ICP备11008920号-3
闽公网安备35020302034844号

Top