Just Do Java

Java 's Blog


  • 首页

  • 分类

  • 作者

  • 归档

  • 关于

公司降薪逼迫员工降薪,看程序员如何怒怼公司

发表于 2021-07-20 | 分类于 Java

事情起末

阿粉从来没有想象过这种事情会发生在阿粉的周边,因为阿粉从来没有见过公司会找人谈话,去要求公司的员工去降低薪资,事情发生在阿粉的一个读者的身上,而他也把事情原原本本的给阿粉描述了,阿粉在这里也做一下自己的评价。

事情是这个样子的,阿粉先暂时的把这个粉丝给说成员工君,员工君的公司是一个小型的互联网公司,而且位置是处在二线城市,大家也可能都知道,一些小型的互联网公司如果是做自己的产品的话,肯定需要拉投资,或者说背后有非常有钱的金主可以支持着做某项产品,而在过程中就会出现各种各样的不确定性,比如说:

  • 金主离开(公司倒闭)

  • 产品不合格(公司倒闭)

  • 拉不到投资(公司倒闭)

  • 产品做的好,有投资(公司能继续变的更好)

实际上在这种小型的互联网公司做开发,总是会承担着这样的风险,因为你不知道什么时候就要准备开始新的面试。

而员工君所在的公司是这样的,公司每个月的开工资的时间是下个月的25号,在阿粉的认知中:

  • 第一阶段/ 1—10号:主要是高科技企业、上市企业,外企和国有企业

  • 第二阶段 / 10—15号:一些A股上市企业以及收益相对较好的企业,完整的管理制度以及比较看重人事管理的公司。

  • 第三阶段/15号以后:大部分是小型单位,特别是社会服务行业和劳动密集类型的企业。

25号开工资是什么概念,也就是说,你三月份入职,然后你在4.25的时候才能收到你三月份的工资,也就是说,整整25天,是在你下个月的时候拿不到工资的,不过这也是合法的,为什么说合法,因为劳动法规定的是,三十天以内发放工资。

也就是说,你三月份的工资,最迟的发放时间是4月30日,如果超过这个期限,也就是我们俗称的“拖欠工资”,如果说因为公司经营困难,劳动法规定也是可以适当拖欠一段时间,但是需要公司备案,而很多公司也不进行备案,直接就明着拖欠工资。

而员工君就遇到了这样的事情,公司3月份的工资,一直拖到了5月20日才进行发放,也就是整整拖欠了接近一个月,而这也是导致接下来事情发生的起因。

狼性文化靠什么来支持

据员工君说,小小的公司竟然还推崇华为的“狼性文化”,不得不说,现在的90后和之前的80后不一样了,“狼性文化”的华为,人家加班,给的是钱,你一个小小的公司,能给什么?梦想?没有钱,拿什么谈梦想?

阿粉之前也听在华为的一个大佬说过一句大实话:在华为期间,平时就很少听人谈及情怀,而对于基层的员工来说,谈的只是:经济,有能力多给员工发钱,给出超过行业平均的薪资,实际上是一家公司最能体现对员工关怀的地方。

而如果一个小公司想要推崇华为的“狼性文化”,那么势必你要把 金钱 放在第一位,如果你觉得能力不行,可以辞退,可以招聘你觉得能力足够强的人来进行,但是面试的时候既然谈好了指定的薪资,就认为你这个人和你要的薪资成正比,这还只是新员工,如果试用期觉得不合适,可以进行辞退,但是对老员工来说,可能就没有那么容易了,于是小公司开始想“损招”来逼迫老员工辞职了。

降薪,逼迫员工自己走

上面是一小部分的截图,涉及隐私,阿粉不把所有的聊天记录放上了,事情就是,因为公司感觉目前开发部门的所有人的工资都偏高,所以想要给部门内的所有人进行薪资的调整,于是开完早会的时候,员工君就被公司的小领导给叫到了会议室,在员工君之前,实际上已经有几个员工被约谈后,不愿意降低薪资的,就准备自己走了。结果出现了员工君这样的狠人。

毕竟谁也不是刚出入社会的 “年轻人”了,面对这种情况,我们需要做好完全的准备,员工君比阿粉想的都细致,因为听到了风声,于是在约谈之前,主动拿出了手机,进行了现场的录音,于是在谈论降低薪资的时候,所有的内容全部都在录音文件中,而这块内容,阿粉不得不称赞员工君的机智,毕竟凡事都得讲证据,他给你降薪,万一他说你同意了怎么办?

最终员工君被辞退了,但是员工君就走上了打官司的道路,在历时一个月的官司中,最终判定,降低薪资不合法,并且付N+1的辞退工资,并且赔偿金赔付了2个月的工资。

那么这个实践给了我们什么样子的启示呢?

知道使用劳动法保护自己的合法权益

阿粉为什么这么说呢?因为现在很多年轻朋友,尤其是初入职场的,很多都会被一些公司欺骗,咱们先把好公司防在一边,只谈那些比较操蛋的公司。

  • 基本底薪3000 剩下的都是绩效,奖金等

阿粉不知道大家听到这个怎么想,毕竟程序员和其他的职业不太一样,不像销售那样需要售卖出一些东西才能获得奖金,绩效提成。如果说基本底薪就3000,阿粉不知道这是属于什么语言的程序员,如果你要是刚毕业参加工作,只是为了学习经验,增长见识,这种情况也有,但是一个工作几年的开发人员,一定不会接受这种,外包除外,因为外包很多都是底薪3000,然后加上项目工资是多少等,一系列的薪资才能到达你面试的时候期望薪资。

  • 我们公司试用期不缴纳五险一金

说实话,这块内容阿粉之前从来没有关注过,直到后来不断的有学弟学妹们从学校走出来之后,询问阿粉,为什么公司说是试用期三个月,试用期内不缴纳五险一金呢?

阿粉于是就咨询了一下老同学,发现这样的情况是非常多的,尤其是针对二线城市的小型公司来说,试用期不缴纳五险一金是非常多的,甚至有些小公司,就算转正之后,也就是给你缴纳个五险,也没有一金,这也就是为什么在劳动仲裁的时候,百分之99 都是劳动者状告公司,而且还都能赢,因为资本有时候真的是对打工者无情的打压。

根据《劳动合同法》第十七条、第十九条规定,用人单位和劳动者可以在劳动合同中约定试用期,试用期包含在劳动合同期限内。同时,根据《劳动法》第七十二条和《社会保险法》第四条的规定,用人单位和劳动者必须依法参加社会保险,缴纳社会保险费。

阿粉也不是那种熟记劳动法的人,只是有时候必须要学会用法律来维护自己的合法权益,不是么?

  • 公司现在正处在困难时期,先给大家降低一下薪资

首先这块内容,你先要表示出明确的单方面的降薪你是不同意的,单方降低工资实际上是单方变更劳动合同的行为,是一种严重违法、违约的行为。

但是如果说公司给你调岗降薪,你没有在一个月之内提出异议,那么就相当于是你同意了公司的调岗降薪了,所以,有问题,一定得处理一下,不要拖,尤其是对自己工作这事。

如果有公司说,我书面发出了通知,并且单独谈话也都告诉你们了,这时候就像阿粉的读者员工君一样,我不同意单方面的降薪,第一,我没迟到,第二,我按时完成了我的工作内容,并且在谈话过程中也有录音,即使两方真的闹得不可开胶的话,对簿公堂,你也有的说。

阿粉之前从事的一个公司,在公司最困难的时候,老板都发话,所有员工的钱不能拖,也不用降,哪怕奖金取消,工资还是照常发放,这才是一个当老板要学会的呀。

所以阿粉就算从之前的公司走了,不管是公司的同事还是领导打电话问一些事情,阿粉同样都比较上心,能解决的,还是帮忙解决,针对这种让员工没有一点感情的公司,说实话,打电话阿粉都会拒接。

如何挑选一个好的公司

  1. 看面试官

首先面试官一定是和你接触时间最长的人,因为他需要面试你,不管你面试的时候到底面试的怎么样,一个好的面试官给人的感觉总是不一样的,大家还记得之前在校招上 B 站的招聘人员的一句话对公司有多么大的影响么?

所以既然能当面试官,那么要么是你将来要去部门的领导,要么也算是技术大佬。面试官如果是那种盛气凌人的那种,居高临下的那种,这样的公司去了也免不了勾心斗角,如果你不是很缺一份工作的话,这样的公司可以再考虑一下。

  1. 看HR

和 HR 的对话,能清晰的反应出这个公司是怎么样子的,因为 HR 很多时候都会问一些比较简单的问题,而且会介绍公司的待遇等情况,还会让你发起一些提问,比如你有什么想问我的,而 HR 一般都会把公司的福利待遇等情况都告知你,比如说,双休,加班补贴,加班是否可以打车等等。

如果你觉得面试还可以,而且有对这个公司有兴趣的话,可以多问一些问题,了解公司总是没坏处的。

  1. 看公司氛围

当你去面试的时候,你就能在其中感觉到一点公司的氛围,公司氛围的好坏,能决定你是不是能在这个公司长久的干下去。

所以阿粉希望读者大大们都能够擦亮自己的双眼,找到一个比较好的工作呢!

阅读全文 »

京东宣布涨薪两个月!别人家的公司你酸了吗?B 站半夜宕机,你慌了吗?

发表于 2021-07-13 | 分类于 Java

Hello,大家好,我是阿粉,最近互联网大厂频繁有动静出来,先是滴滴被下架二十多款 APP,后面有快手,字节宣布取消大小周,再有京东宣布涨薪两个月,这边 7.13 号晚 B站也挂了。

阅读全文 »

对比授权机制,你更想用哪种?

发表于 2021-07-09 | 分类于 Java

授权机制,当我们说到这个问题的时候,大家对它的第一印象是在哪个地方呢?是不是曾经某培训机构教授的 SSO 单点登录的,是的没错,而这种 SSO 的单点登录在当年的培训机构中,使用的就是 Session 共享,也就是用 Redis 做中间模拟 Session ,但是授权机制真的有这么简单么?接下来阿粉就来强势对比一下关于授权机制了。

Cookie-Session 认证授权

Cookie-Session 认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。

但是这时候我们就得考虑一下 Cookie 的存活时间了,当我们关闭浏览器的时候,Cookie就会被删除,就算我们调整了 Cookie 的存活时间,但是他依然有很大的弊端,Cookie 是很容易被拦截到的,阿粉之前就看到过某个知名的 OA 系统,就曾经把用户的ID 放到了 Cookie 中,只不过是把 Cookie 里面的键给设置成了 imageUrl,但是实际上这种,看着有点搞人的意思,图片地址是一堆长的字符串,你在前端拦截到之后, 明眼人一眼就能知道这种肯定不是图片路径,而且当我们使用 Cookie 进行用户识别,用户就会很容易受到跨站请求伪造的攻击,也就是我们经常说的 CSRF .

JWT

既然在这里对比 JWT ,我们就得先知道 JWT 是个什么东西,JSON Web Token (JWT) 实际上用大白话说,它就是一种认证机制,让后台知道请求是来自于受信的客户端。

技术都是随着问题出现的,只要有问题,那么很快就会有解决这个问题的技术出现,同样,JWT 出现的只不过比较早而已,因为现在微服务,分布式横行遍布,不管是大公司,还是小公司,很多都开始做分布式的项目,这做分布式也不仅仅是停留在了只存在大公司了,既然选择使用了分布式,那么各种各样的问题就来了。

  • 跨域身份验证

  • 分布式session共享

  • 分布式站点的单点登录

JWT 是个什么玩意

我们先看一下官方网站给的内容,What is JSON Web Token?

1
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA

JSON Web令牌(JWT)是一种开放标准(RFC7519),它定义了一种紧凑且独立的方式,用于在各方之间安全地作为JSON对象传输信息。由于该信息是数字签名的,因此可以验证和信任此信息。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对对对JWTs进行签名.

阿粉就直接用百度翻译了,结果翻译出来竟然差不多,看来百度翻译有时候也没有那么差劲。

那么什么时候需要去使用 JWT 呢?

在官网中,给出了两种情况下去使用 JWT ,Authorization 和 Information Exchange,一种是授权,授权我们都懂,就是当用户登录后,每个后续请求都将包括JWT,允许用户访问该令牌允许的路由、服务和资源,而资源交换,实际上简单的说,就是在数据传输中用 JWT 令牌在安全地在各方之间传输信息

那么我们既然知道了什么时候来使用 JWT, 我们就来看看 JWT 到底是长成什么样子,

JWT 构成:

  • Header 头部

  • Payload 有效载荷

  • Signature 签名

我们从官网上获取了一段的内容,然后逐个来看,

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

既然它说 JWT 是由三段信息构成的,而这三段信息,是用 .来进行隔开的,也就是上面这长串的字符串,

Header 头部,我们看到图里面也给出了,Header 中存储的实际上就是2部分的内容。typ:类型 alg:加密算法,

然后他是对头部进行的 Base64 加密,我就是我们在官网摘下来的第一段的内容,就出现了加密字符串 eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 。

Payload 有效载荷

实际上有效载荷实际上就是存储有效信息的地方,那么他都存储了一些什么内容呢?

  • iss (issuer):签发人
  • exp (expiration time):过期时间
  • sub (subject):主题
  • aud (audience):受众
  • nbf (Not Before):生效时间
  • iat (Issued At):签发时间
  • jti (JWT ID):编号( jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击)

为什么会有这么多,因为在 JWT 的规范中,他告诉我们的是,建议但不强制使用,也就是说,你可以根据自身的应用去选择使用,比如官网给出的,他就没有写全面,就使用了三个:

1
2
3
4
5
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}

而实际上这部分的内容却是比较重要的内容。

signature签名信息

实际上这个就是一个组装起来的,将头部和载荷用’.’号连接,再加上一串密钥,经过头部声明的加密算法加密后得到签名。

这个 Header 和 Payload 都是加密过的,而在这个地方它还进行了 “加盐” 的操作,将这三部分用.连接成一个完整的字符串,构成了最终的jwt

jwt其实并不是什么高深莫测的技术,在很多技术人的眼中,可能觉得他会非常的low ,实际上虽然不高端,但是也没有那么 low,认证服务器通过对称或非对称的加密方式利用payload生成signature,并在header中申明签名方式,这就是 JWT 的本质实现方式。

JWT 的有点其实很明显,

  • 通过验证签名的方式可以直接在资源服务器本地完成授权校验

  • 在payload中可以包含用户相关信息,实现了token和用户信息的绑定

使用场景一般是用在一次性的身份验证上,千万不要想着去用 JWT 去代替 Session,虽然 JWT 可以设置失效时间,但是在有效期内,它是无法作废的。

OAuth2认证

OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者。资源所有者同意以后,资源服务器可以向客户端颁发令牌。客户端通过令牌,去请求数据。

其实这个 OAuth 的核心就是向第三方应用颁发令牌,而在 Oauth2 中定义了四种获得令牌的流程,也就是通俗的四种授权方式,但是我们经常使用的也就是那么一种。

  • 授权码

  • 隐藏式(简化)

  • 密码式

  • 客户端凭证

授权码模式

这是在 Oauth 里面的功能算是最完整的,而且流程最严密的授权模式。

授权码模式的步骤:

  • 1.用户访问客户端,后者将前者导向认证服务器

  • 2.用户选择是否给予客户端授权

  • 3.假设用户给予授权,认证服务器将用户导向客户端事先指定的”重定向URI”(redirection URI),同时附上一个授权码

  • 4.客户端收到授权码,附上早先的”重定向URI”,向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见

  • 5.认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)

其实授权码模式就相当于是第三方的应用去先申请一个授权码,然后再用该授权码获取令牌。

总结下来就是四个步骤 : 1:请求授权码 2:返回授权码 3:请求令牌 4:返回令牌

我们给出一个例子,然后分析一下。

1
2
3
4
5
6
7

https://2.com/oauth/authorize? //授权地址
  response_type=code& //参数1:response_type :这里表示授权的类型,此处的值固定为"code"
  client_id=CLIENT_ID& //参数2:client_id :表示客户端的ID
  redirect_uri=CALLBACK_URL& //参数3:redirect_uri :表示重定向URL
  scope=read //参数4:scope: 表示申请的权限范围
  

上面的地址,就相当于第一步,携带所需要的参数请求 网站2,请求获取授权码。

1
https://1.com/callback?code=AUTHORIZATION_CODE //code 授权码

上面的地址就是第二步了,网站2给网站1返回授权码,

1
2
3
4
5
6
7
8

https://2.com/oauth/token?
 client_id=CLIENT_ID&   客户端ID
 client_secret=CLIENT_SECRET& 客户端密钥
 grant_type=authorization_code& 使用的授权模式 authorization_code :授权码模式
 code=AUTHORIZATION_CODE& 授权码
 redirect_uri=CALLBACK_URL  表示重定向URL

上面的地址就到第三步了,用授权码去索要令牌的请求就发送了。

请求发送完成后,2网站收到请求之后,这时候就向 重定向URL 发送以下的 JSON 数据,

1
2
3
4
5
6
7
8
9
{    
  "access_token":"ACCESS_TOKEN", //访问令牌
  "token_type":"bearer",// 令牌类型
  "expires_in":2592000, // 过期时间
  "refresh_token":"REFRESH_TOKEN", // 更新令牌
  "scope":"read", // 权限范围 只读
  "uid":100101, //
  "info":{...} //
}

这时候 访问令牌 我们就要有了,这完成所有的步骤后,我们就拿到了我们访问的令牌了,也就是我们完成了所需要的授权了。

隐藏式

其实隐藏式就是简化版的授权模式,他省略了获取 授权码 的过程,而是直接请求获取 令牌 的过程。

案例如下:

1
2
3
4
5
6
7

https://2.com/oauth/authorize?
  response_type=token& 授权的类型,此处的值固定为"token"
  client_id=CLIENT_ID&  客户端ID
  redirect_uri=CALLBACK_URL& 表示重定向URL
  scope=read  权限范围 只读

上面授权类型直接就是索要令牌,

第二步也很简单,就是直接给你返回你需要的令牌

1
1
1
https://1.com/callback#token=ACCESS_TOKEN

上面的 Token 就是我们需要的令牌了,

密码式

这种为什么称之为 密码式 ,是因为它在请求的时候,是用密码去换令牌,这就需要一个前提,你对这个网站有高度的信用度,如果你不信用他,他给你账号密码作用都不大,给了你也不会授权给它 Token 不是么。

案例步骤如下:

1.请求令牌

1
2
3
4
5
6
7

https://oauth.2.com/token?
  grant_type=password& 授权方式:指定为密码式
  username=USERNAME&  用户名
  password=PASSWORD&  密码
  client_id=CLIENT_ID  客户端ID
  

2.返回令牌

1
1
1
https://1.com/callback#token=ACCESS_TOKEN

这个感觉和隐藏式差距不大,一个是直接要,一个是拿着参数要。

凭证式

这个凭证式的步骤也是比较少的,实际上阿粉感觉这种方式不知道算不算是授权的方式,因为这种模式是客户端以自己的名义向”授权服务提供者”进行认证,但是既然说是,那就暂且的认定他是,

1:请求令牌

1
2
3
4
https://oauth.2.com/token?
  grant_type=client_credentials&  授权方式:凭证式
  client_id=CLIENT_ID&   客户端ID
  client_secret=CLIENT_SECRET 客户端密钥

2.返回令牌

1
1
1
https://1.com/callback#token=ACCESS_TOKEN

这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,也就是说可能出现多个用户共享同一个令牌。

为什么要比较 JWT 和Oauth2 ,因为很多不明所以的人总是会在挑选技术的时候,会把二者拿出来对比,其实上,他们两个没有可比性,因为 JWT 是用于发布接入令牌,并对发布的签名接入令牌进行验证的方法。

OAuth2是一种授权框架,授权第三方应用访问特定资源。

也就是说:

  • OAuth2用在使用第三方账号登录的情况

  • JWT是用在前后端分离, 需要简单的对后台API进行保护

所以你知道怎么选择了么?

文章参考

《阮一峰的网络日志》 《JWT官方文档》

阅读全文 »

2021 入门级的 Java 程序员学习路线图

发表于 2021-07-07 | 分类于 Java

Hello,大家好,我是阿粉,最近看文章发现一个 Java 学习路线图的资料,觉得很不错就拿过来分享给大家,目前这个版本是 1.0 的,之所以说这个是 1.0 的版本主要是因为还有一些高级内容没有加进去,比如 JVM,容器,消息队列,云原生等都还没有提到,不过对于初学者来说这个路线图已经可以上手完成工作了。

阅读全文 »

滴滴出行因违规收集用户个人信息被网信办通知下架!!!

发表于 2021-07-04 | 分类于 Java

Hello 大家好,我是阿粉,最近刚刚过完我党的百年生辰,本是举国同庆的时候,但是往往就有很多不法分子违法违规,这不滴滴就被网信办宣布在所有应用市场下架了么。如下所示

阅读全文 »

手把手叫你搭建一个自己公司的接口文档项目

发表于 2021-07-02 | 分类于 Java

大家还记得之前阿粉给大家推荐的一个写接口文档的神器么?Run-API,前段时间,因为 Show-Doc 进行网站升级,忽然的一天早上 Run-API 失效了,这下整的阿粉就有崩溃了,啥情况,和阿粉对接的前端也有点懵逼,说接口文档忽然访问不了了,阿粉于是赶紧查看,原来是因为阿粉的接口,是完全的依托于 Show-Doc 的服务器进行了发布,为了防止这种情况的出现,阿粉就开始研究关于 Show-Doc 如何在自己的电脑行搭建一个服务,这样如果 Show-Doc 网站再次升级的时候,也就不会出现这么悲剧的事情了。

关于 Show-Doc

ShowDoc 是一个非常适合 IT 团队的在线文档分享工具,它可以加快团队之间沟通的效率,为什么这么说,因为目前现在很多公司都是建立的前后端分离的项目,很多后端都是专职后台业务逻辑的开发,这时候就会出现,每个人写文档的风格不一致,有些人使用 Excel 有些人使用 Word 表格,有些人使用 Swagger 还有人使用 Show-Doc。

而且尤其是在有新老员工离职交接的时候,交接的接口文档都交接不明白,因为很多都是接口写完了,发给前端,功能上线后,文档没用都删除了,这个时候就得去看代码上面的注释,有注释的还好说,没有注释的,那就让你疯狂的崩溃。不知道代码谁写的,这样岂不是很尴尬,这时候我们就可以在公司内部搭建一个文档类型的服务器,好处有很多呀,比如:

  • 一边调试接口、一边自动生成文档

  • 分配项目成员和团队成员,你可以很方便地进行项目文档的权限管理和团队协作

  • 支持多平台客户端,有win客户端、mac客户端、ios、android等,更方便跨平台使用

据说目前一些知名大厂,比如腾讯、华为、百度、京东、字节跳动 这些公司都在使用,阿粉不知道真的假的,有知道的内部员工可以在后台给大家回复一下确认是否使用,还是 Show-Doc 为了宣传效果做的 “虚假宣传”,话不多说,我们开始搞一下安装部署,然后开始使用吧。

安装Show-Doc

上面这个图是需要你安装好本地服务之后,连接本地服务用的,也就是实际写文档的时候用的,搭建软件在下面呢。

下载网址(Windows下)

Show-Doc 版本其实挺多的,因为阿粉之前的服务器已经到期了,我们就先整个 Windows 的版本来整一下,下载 Windows 版本。

还有一个就是 Linux 下有一键脚本安装,比较方便,

windows下安装推荐使用phpStudy集成环境(如果你不用集成环境,请自身确认开启了”php-pdo-sqlite”和”php-sqlite3”扩展)

下载完成之后就是这个鬼样子,阿粉的版本是8.1.1.3,大家安装完成之后,不用担心影响你本地的所有数据信息,放心就行,没啥影响,不用看着有 Apache 和 Mysql 就担心影响本地,只要端口号不冲突,啥问题没有,在他的四个套件中,前三个套件是有用的,第四个 Nginx 做负载均衡 和第一个 Apache 是有冲突的,阿粉安装完成之后,必须让我关闭一个,另外一个才能启动。

但是不影响,启动了 Apache 的时候 Nginx 就算不管他,都能正常的使用,那阿粉就先留下这个坑,如果有问题,到时候再填上他。

点击左侧菜单“网站”,然后点击右边按钮“管理”-“打开根目录”:

这个时候就有比较坑爹的了,如果你去百度,这时候很多人说让我们去下载源码,然后把文件夹直接拷贝进去,这就会出现问题,访问不到,很难受了,有点坑,我们接下来既然打开了根目录,那么就得往里面放东西,需要我们放的就是 show-doc 的源码。

源码地址如下:

show-doc源码github

如果网络不好的,阿粉也给大家准备了,在后台回复showdoc 阿粉会把下载地址给大家,一个是安装包,一个是源码包,一起发给大家。

下载源码包完成之后,进入该文件夹,全选, 把所有文件复制到刚才打开的网站根目录中(例如根目录是D:\phpstudy_pro\WWW)。需要注意的是,不要单纯把showdoc-master这个文件夹复制过去,要进入showdoc-master把里面的文件都复制出来

拷贝进去之后是上图的样子,然后我们点击修改后就可以看到如下,在 WWW 下就是我们要访问的网址。

当我们把这个部署完成之后,我们就能尝试启动一下看看了,

当我们看到这个页面的时候,就是启动从成功了,我们也部署完成了。

提示php-sqlite没安装的问题,你可以打开“管理”-“php扩展”,确保”php-pdo-sqlite”和”php-sqlite3”开启。如果它没开启,你可以点击它开启。

Show-Doc使用

当我们创建的时候,就是上面这个样子

上面有我们需要的 JSON 转 参数表格,JSON 格式化,还有一些 API 模板 比如:

看到这个,你心动了么?快来安装一波试试吧。

阅读全文 »

一个Redis的雪崩和穿透问题,小学妹画了个图,结果入职了

发表于 2021-06-24 | 分类于 Java

阿粉的一个小学妹最近刚从某个小互联网公司跳槽,然后最近面试的挺多的,一个不善言语的小姑娘,技术还是OK的,本来之前是做UI的,但是时间长了,感觉没太大意思,所以就开始学了后端,然后从原有公司慢慢的转为了后端开发人,也就是我们所说的 “程序猿”,最近面试给阿粉谈了谈她的面试经验。阿粉比较印象深刻的一句话就是,我给你画个图,你看一下,这是对面试官说的,事情是什么样子的呢?

你了解 Redis 穿透和雪崩么?

为什么这么说,因为面试官当你说到 Redis 的时候,面试官问的现在已经不是 “你说一下 Redis 的几种数据结构” ,现在面试问的时候,很多都是对 Redis 的实际使用开始问了,比如说,

  • Redis 都有哪些架构模式? 单机版,主从复制,哨兵机制,集群(proxy 型),集群(直连型)

  • 使用过Redis分布式锁么,它是怎么实现的?

  • 使用过Redis做异步队列么,你是怎么用的?有什么缺点?

  • 什么是缓存穿透?如何避免?什么是缓存雪崩?何如避免?

而阿粉的小学妹遇到的就是关于 Redis 的缓存穿透和雪崩问题了。这个问题学妹配合了一波自己的 UI 功底图加上口头的解释,于是成功的拿到了这个 Offer,也可能是因为小学妹比较美丽并且技术还过的去。所以,就准备入职了。

我们来看看小学妹到底画了什么图,让面试官问了一波之后就入职了。

缓存穿透

如图:图是阿粉找小学妹专门画出来的,大家看一下

既然我们看完图了,相信打击也都看到了什么是缓存穿透了,也就是说,在我们的缓存系统中,也就是 Redis 中,我们都是拿着我们的 Key 去 Redis 中去寻找 Value 中,如果说我们在 Redis 中找不到我们的数据之后,我们就会去数据库中去寻找我们的数据,如果只是单一请求的话,也不能算是个太大的问题,只能称之为击穿而已,但是如果说要是请求并发量很大的话,就会对我们的数据库造成很大的压力,这其实就称之为缓存穿透,而穿透出现的严重后果,就会是缓存的雪崩了,我们先说穿透,一会再说雪崩。

那么都会有什么情况会造成缓存被穿透呢?

  • 自身代码问题

  • 一些恶意攻击、爬虫造成大量空的命中。

如果有个黑客对你们公司的项目和数据库比较感兴趣,他就可能会给你整出巨多的一些不存在的ID,然后就疯狂的去调用你们的某项接口,这些本身不存在的 ID 去查询缓存的数据的时候,那就是压根没有的,这时候就会有大量的请求去访问数据库,虽然可能数据能支撑一段时间,但是早晚会让人家给你整的凉了。

那么应该怎么去解决缓存穿透的问题呢?

  • 利用互斥锁,缓存失效的时候,先去获得锁,得到锁了,再去请求数据库。没得到锁,则休眠一段时间重试。

  • 采用异步更新策略,无论 Key 是否取到值,都直接返回。Value 值中维护一个缓存失效时间,缓存如果过期,异步起一个线程去读数据库,更新缓存。需要做缓存预热(项目启动前,先加载缓存)操作。

  • 提供一个能迅速判断请求是否有效的拦截机制,比如,利用布隆过滤器,内部维护一系列合法有效的 Key。迅速判断出,请求所携带的 Key 是否合法有效。如果不合法,则直接返回。

布隆过滤器实际上是一种比较推荐的方式。

布隆过滤器的实现原理则是这样的:

当一个变量被加入集合时,通过 K 个映射函数将这个变量映射成位图中的 K 个点,把它们置为 1。查询某个变量的时候我们只要看看这些点是不是都是 1 就可以大概率知道集合中有没有它了,如果这些点有任何一个 0,则被查询变量一定不在;如果都是 1,则被查询变量很可能在。注意,这里是可能存在,不一定一定存在!这就是布隆过滤器的基本思想。

而当你说出布隆过滤器的时候,可能这才是面试官想要问你的内容,这时候你就得好好的和面试官开始聊聊什么事布隆过滤器了。

我们还是继续用大众都想看到的图解来解释布隆过滤器。

字符串 “Java” 在经过四个映射函数操作后在位图上有四个点被设置成了 1。当我们需要判断 “ziyou” 字符串是否存在的时候只要在一次对字符串进行映射函数的操作,得到四个 1 就说明 “Java” 是可能存在的。

注意语言,是可能存在,而不是一定存在,

那是因为映射函数本身就是散列函数,散列函数是会有碰撞的,意思也就是说会存在一个字符串可能是 “Java1” 经过相同的四个映射函数运算得到的四个点跟 “Java” 可能是一样的,这种情况下我们就说出现了误算。

另外还有可能这四个点位上的 1 是四个不同的变量经过运算后得到的,这也不能证明字符串 “Java” 是一定存在的。

而我们使用布隆过滤器其实就是提供一个能迅速判断请求是否有效的拦截机制,判断出请求所携带的 Key 是否合法有效。如果不合法,则直接返回。

而阿粉的小学妹给面试官解释了一波这操作之后,看样子,面试官对这个“程序猿”开始有点印象了,接下来就顺着问了,那什么事缓存的雪崩呢?

缓存雪崩

这时候也就是说,当我们有多个请求访问缓存的时候,这时候,缓存中的数据是没有的,也就是说缓存同一时间大面积的失效,这个时候又来了一波请求,结果请求都怼到数据库上,从而导致数据库连接异常

他和穿透实际上相似但是又有所不同,相似的地方是都是搞数据库,不同的是缓存穿透是指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库

而解决缓存雪崩的策略也是比较多的,而且都是比较实用的。比如:

  • 给缓存的失效时间,加上一个随机值,避免集体失效。

  • 双缓存。我们有两个缓存,缓存 A 和缓存 B。缓存 A 的失效时间为 20 分钟,缓存 B 不设失效时间

双缓存策略比较有意思,当请求来临的时候,我们先从 A 缓存中获取,如果 A 缓存有数据,那么直接给他返回,如果 A 中没有数据,那么就直接从 B 中获取数据,直接返回,与此同时,我们启动一个更新的线程,更新 A 缓存和 B 缓存,这就是双缓存的策略。

上述的处理缓存雪崩的情况实际上都是从代码上来进行实现,而我们换个思路考虑呢,也就是从架构的方向去考虑的话,解决方案就是以下的几种了。

  • 限流

  • 降级

  • 熔断

那么怎么实现限流呢?

说到限流降级了,那就不能单纯的去针对 Redis 出现的问题而进行处理了,而实际上是为了保证用户保护服务的稳定性来进行的。

那么为什么要去限流呢?你要单纯的说是为了保证系统的稳定性,那面试官估计得崩溃,这和没说有啥区别,你得举个简单的例子才能正儿八经的忽悠住面试官,比如:

假设,我们当前的程序能够处理10个请求,结果第二天,忽然有200多请求一起过来,整整翻了20倍,这时候,程序就凉了,但是如果第一天晚上的时候,领导给你说,明天你写的那个程序大约会有200多个请求要处理,你这时候是不是得想办法,比如说,能不能再写出另外的一段程序来进行分担请求,这时候其实就相当于需要我们去限流了。

限流算法之漏桶算法

同样的,我们整个图来理解一下这个算法到底是怎么实现的。

如果一桶有一个细眼,我们往里面装水,可以看到水是一滴一滴匀速的下落的,如果桶满了就拒绝水滴继续滴入,没满的话就继续装水,实际上就是这样的水滴实际上就相当于是请求,如果水桶没满的时候,还能继续处理我们进来的请求,当水桶满了的时候,就拒绝处理,让他溢出。

前提是我们的这个桶是个固定的容器,不能随着水的增多桶会变大,要不然那还用什么限流算法。

简单的漏桶算法的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LeakyBucket {
        public long timeStamp = System.currentTimeMillis();  // 当前时间
        public long capacity; // 桶的容量
        public long rate; // 水漏出的速度
        public long water; // 当前水量(当前累积请求数)

        public boolean grant() {
            long now = System.currentTimeMillis();
            // 先执行漏水,计算剩余水量
            water = Math.max(0, water - (now - timeStamp) * rate); 
            
            timeStamp = now;
            if ((water + 1) < capacity) {
                // 尝试加水,并且水还未满
                water += 1;
                return true;
            } else {
                // 水满,拒绝加水
                return false;
        }
    }
}

上面的代码是来自悟空,不得不说,这个简单的例子虽然简单,但是吧这个漏桶算法的简单原理描述的还是差不多的,而在这里最需要注意的,就是桶的容量,还有就是水桶漏洞的出水的速度。

既然我们了解了漏桶算法是如何实现限流的,那么必然也会有他处理不来的情况,因为我们已经定义了水漏出的速度,而这时候如果应对突发的流量忽然涌进来,他处理起来效率就不够高了,因为水桶满了之后,请求都拒绝了,都不处理了。

其实我们所说的漏桶算法还可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。

而我们的漏桶算法主要是能够强行限制数据的传输速率。

那么又有什么算法能够不进行强制限制传输速率,并且实现限流呢?

令牌桶算法

我们感谢百度,我从百度图片中找了个一个比较给力的图来描述令牌桶的算法。

令牌桶算法的基本过程是这个样子的:

  1. 用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中

  2. 假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃

  3. 当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络

  4. 如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外

乍一看,怎么感觉这个令牌桶和漏桶这么像,一个是水滴,一个是令牌,实际上不是。

令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;而如果令牌桶中不存在令牌,则不允许发送流量。

而且他是能够应对突发限制的,虽然传输的速率受到了限制.所以它适合于具有突发特性的流量的一种算法。

而在 Google 开源工具包中的限流工具类RateLimiter ,这个类就是根据令牌桶算法来完成限流。大家有兴趣的可以去看看呀。

漏桶算法和令牌桶算法的区别

漏桶算法与令牌桶算法实际上看起来有点相似,但是不能混淆哈,这就是阿粉在上面说的:

  • 漏桶算法能够强行限制数据的传输速率。

  • 令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输

关于阿粉今天说的这些你学会了么?

文献参考

《百度百科》

阅读全文 »

分布式环境下如何保证 ID 的唯一性

发表于 2021-06-23 | 分类于 Java

前言

首先说下我们为什么需要分布式 ID,以及分布式 ID 是用来解决什么问题的。当我们的项目还处于单体架构的时候,我们使用数据库的自增 ID 就可以解决很多数据标识问题。但是随着我们的业务发展我们的架构就会逐渐演变成分布式架构,那么这个时候再使用数据的自增 ID 就不行了,因为一个业务的数据可能会放在好几个数据库里面,此时我们就需要一个分布式 ID 用来标识一条数据,因此我们需要一个分布式 ID 的生成服务。那么分布式 ID 的服务有什么要求和挑战呢?

阅读全文 »

作为 Java 开发程序员,你知道什么是 Serveless 架构吗?

发表于 2021-06-20 | 分类于 Java

Hello 大家好,我是阿粉,在了解什么是 Serveless 架构之前,我们先看下传统的项目发布和部署的流程是什么样子的。

阅读全文 »

Redis和Mysql如何保证数据一致?面试可以这样说自己的看法

发表于 2021-06-17 | 分类于 Java

阿粉的小学弟最近开始了面试,毕竟也算是工作过一两年的人,现在面试也都开始造飞机了,小学弟开始在面试官面前疯狂造飞机了,也不知道这个飞机好不好用,而开始造飞机的这块内容,就是关于 Redis 的,而面试官问 Redis 的最多的问题,就是如何保证你的 Redis和 MySQL 数据的一致性?接下来我们分别分几种情况来考虑一下这个问题吧。

Redis 和 MySQL 搭配使用在什么地方?

缓存量大但又不常变化的数据

也就是说,当我们在使用 Redis 和 MySQL 的时候,搭配使用的地方就是,数据量比较大,但是这个数据不会经常的变换的位置,比如说,某些商品信息的评论数据,也就是让 Redis 充当 MySQL 的缓存服务器,而要实现的目标也是比较简单的,当客户要查询数据的时候,先访问我们的 Redis ,当 Redis 里面没有数据的时候,从 MySQL 中读取数据,并且存储到 Redis 中。

这个时候 Redis 和 MySQL 的交互就是两部分:

第一部分:同步MySQL数据到Redis

第二部分:同步Redis到MySql

这两部分的内容,实际上就是在一组业务操作中完成的,商品评论信息写入 Redis,如果没有,从 MySQL 中读取,然后写入 Redis。

而接下来的问题就比较严重了,Redis 和 MySQL 数据库数据如何保持一致性?

Redis 和 MySQL 数据库数据如何保持一致性?

为什么会存在这样的一个问题呢?阿粉用网上拿过来的图给大家分析一波。

首先,当我们请求发送到服务器的时候,这个时候,我们先去缓存里面拿我们需要的数据,如果没有的话,我们就去数据库加载数据,加载完成之后,然后再把数据写入到缓存里面。

接下来问题来了,如果你的读和写存在并发的时候,会出现什么样子的问题呢?这个时候,我们就比较尴尬了,压根就没办法保证读和写的顺序,这时候就出现了 Redis 和 MySQL 数据不一致的问题了。

我们准备多种不同的方案来进行不同的分析。

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

为什么不考虑这种使用方案呢?

假设我们现在有两个请求 一个是 A 一个是 B ,假设 A 这时候进行请求,A 先更新数据库,接着 B 请求来了, B 更新数据库,结果 B 请求快,B 直接先更新了缓存,这时候缓存更新的内容为 B 更新的,也就是 b ,然后 A 这时候更新完数据库之后,又要更新缓存,这时候 A 更新了缓存,结果最后,缓存里面保存的数据是 a 。

也就是这样的

A —> 更新数据库 —-> 更新缓存为a 我更新数据库慢,我存了个a

B —> 更新数据库 —-> 更新缓存为b 我更新数据库快,我存了个b

本来应该最后更新完成之后的缓存中应该是 b ,结果最后出现了 a ,如果出现这种问题的时候,领导来看的时候,通常挨整的还是程序员自己呀。

这时候,在缓存里面存在的就应该算是脏数据了,所以,这种方案不推荐。

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

同样的 A B 两个请求,比如说这时候,A请求要进行一个写的操作,而 B 请求要进行一个读取的操作,这时候,A 肯定要删除缓存,就这这个时候,B 来了,我要读取,结果,缓存里面数据不存在,就直接去读数据库,然后把数据库的内容写入到缓存里面了,而这个时候的数据是 A 还没有进行过修改的数据,也就是一个老数据,等读完了之后,A进行了修改,这时候,你的缓存和你数据库中的数据就会出现不一样的情况了。

因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题,这种方案,同样不可行。

那么我们应该怎么样去保证 Redis 和 MySQL 数据一致性呢?

如何保证 Redis 和 MySQL 数据一致性。

这时候就会有两个在面试的时候,说分布式很容易给自己挖了个大坑的地方,那就是最终一致性和强一致性,而数据库和缓存双写,就必然会存在不一致的问题。

这个命题就会有很感人的地方,你要做出一个选择,如果你选择强一致性,那就不能放缓存,所以,我们也就是仅仅能够保证最终的一致性,而如果选择强一致性,那算了,你别用缓存了。

而且我们说的这个,只是说降低概率发生,而不能完全的避免,但是我们还是要说。

延时双删策略

在写库前后都进行 Redis 的删除操作,并且第二次删除通过延迟的方式进行

那么应该是什么样子的实现逻辑呢?

  • 第一步:先删除缓存

  • 第二步:再写入数据库

  • 第三步:休眠xxx毫秒(根据具体的业务时间来定)

  • 第四步:再次删除缓存。

中间的休眠时间,根据自己的业务时间来进行定夺,这个双删策略实际上就是为了解决你在读数据的时候,生成的过期的数据被第二次写的操作给删除掉。

总有面试官喜欢问为什么要双删,因为第一次删除的是还没更新前的数据,第二次删除则是因为读取的并发性导致的缓存重新写入数据出现的垃圾数据。

这时候总有杠精面试官会问:如果你们的删缓存失败了,怎么办?那不是还是会出现缓存和数据库不一致的情况么?

比如一个写数据请求,然后写入数据库了,删缓存失败了,这会就出现不一致的情况了。

这时候我们就需要一个中间件的无私配合了,那就是使用消息来进行重试机制。

步骤:

  1. 业务代码去更新数据库

  2. 数据库的操作进行记录日志。

  3. 订阅程序提取出所需要的数据以及key

  4. 获得该信息尝试删除缓存,发现删除失败的时候,发送消息到消息队列

  5. 继续重试删除缓存的操作,直到删除缓存成功。

其实这个方法和另外一个地方很像,分布式事务的处理方式,就是保证数据的最终一致性,而在分布式事务中,则称之为这种为最大努力通知。

而为什么说是很像,实际上最大努力通知采用的实际上也是 MQ ,但是采用的是 MQ 的 ack 确认机制来进行完成的。

那么最大努力通知又是什么样的流程呢?

  1. 业务方把通知发送给 MQ

  2. 接收通知方监听 MQ

  3. 接收通知方接收消息,业务处理完成回应ack

  4. 接收通知方若没有回应ack则MQ会重复通知,MQ 按照间隔时间从短到长的方式逐步拉大通知间隔,直到达到通知要求的时间上限,比如24小时之后不再进行通知。

  5. 接收通知方可通过消息校对接口来校对消息的一致性

而为什么叫最大努力通知呢,实际上也很容易理解,他并没有从本质上解决问题,只是把问题数目从100 变成了 10 ,毕竟有些内容第一次没处理,第二次就可能会被处理掉。也就是说降低了这种有问题情况的发生,毕竟保证的都是最终一致性。

你面试的时候知道怎么和面试官 Battle 了么?

阅读全文 »

原来传统BIO的局限性在这里!

发表于 2021-06-16 | 分类于 Java

大家都知道传统的 BIO 网络模型有各种各样的缺点,于是就有了关于 NIO 网络模型的出现,更多的人也都开始喜欢使用 Netty 这种框架来进行开发,而摒弃了传统的 BIO 的模型,今天阿粉就给大家说一下为什么这么多人对 BIO 网络模型这么的头疼。

BIO

BIO 网络模型实际上就是使用传统的 Java IO 编程,相关联的类和接口都在 java.io 下。

BIO 模型到底是个什么玩意?

BIO (blocking I/O) 同步阻塞,我们看这个翻译,blocking I/O,实际上就能看出来,就是阻塞,当我们的客户端发起请求的时候,服务端就会开启一个线程,专门为这个客户端提供对应的读写操作,只要客户端发起了,这个服务端的线程就一直保持存在,就算客户端啥也不干,那也在那里开着,就是玩。

不过说一句,现在用 BIO 的不是很多了,因为强制使用的时代过去了,现在还有用 JDK1.4 以下的项目么?估计应该是没有了,阿粉之前曾经维护过一个使用 JRUN 的项目,使用的JDK的版本,就是非常古老的。

我们来整一个服务端和客户端来看看是什么样子的模型。

Server端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public class TimeServer {
    public static void main(String[] args){
        ServerSocket server=null;
        try {
            server=new ServerSocket(18080);
            System.out.println("服务启动 端口:18080...");
            while (true){
                Socket client = server.accept();
                //每次接收到一个新的客户端连接,启动一个新的线程来处理
                new Thread(new TimeServerHandler(client)).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public class TimeServerHandler implements Runnable{
    private Socket clientProxxy;

    public TimeServerHandler(Socket clientProxxy) {
        this.clientProxxy = clientProxxy;
    }

    @Override
    public void run() {
        BufferedReader reader = null;
        PrintWriter writer = null;
        try {
            reader = new BufferedReader(new InputStreamReader(clientProxxy.getInputStream()));
            writer =new PrintWriter(clientProxxy.getOutputStream()) ;
            while (true) {//因为一个client可以发送多次请求,这里的每一次循环,相当于接收处理一次请求
                String request = reader.readLine();
                if (!"GET CURRENT TIME".equals(request)) {
                    writer.println("BAD_REQUEST");
                } else {
                    writer.println(Calendar.getInstance().getTime().toLocaleString());
                }
                writer.flush();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                writer.close();
                reader.close();
                clientProxxy.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

Client端:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class TimeClient {
    public static void main(String[] args)  {
        BufferedReader reader = null;
        PrintWriter writer = null;
        Socket client=null;
        try {
            client=new Socket("127.0.0.1",18080);
            writer = new PrintWriter(client.getOutputStream());
            reader = new BufferedReader(new InputStreamReader(client.getInputStream()));

            while (true){//每隔5秒发送一次请求
                writer.println("GET CURRENT TIME");
                writer.flush();
                String response = reader.readLine();
                System.out.println("Current Time:"+response);
                Thread.sleep(5000);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                writer.close();
                reader.close();
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

执行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12

服务端:
服务启动 端口:18080...

客户端:
Current Time:2021-6-16 11:37:34
Current Time:2021-6-16 11:37:39
Current Time:2021-6-16 11:37:44
Current Time:2021-6-16 11:37:49
Current Time:2021-6-16 11:37:54
Current Time:2021-6-16 11:37:59
Current Time:2021-6-16 11:38:04

我们使用的是 Client 发送请求指令”GET CURRENT TIME”给server端,每隔5秒钟发送一次,每次 Server 端都返回当前时间。

这就相当于是多个 Client 同时请求 Server ,每个 Client 创建一个线程来进行处理.

Accpetor thread 只负责与 Client 建立连接,worker thread用于处理每个thread真正要执行的操作。

在到了这里的时候,我们就发现了一些事情,感觉不对了有没有,

BIO 的局限性一

  • 每一个 Client 建立连接后都需要创建独立的线程与 Server 进行数据的读写,业务处理。

这时候就会出现什么样子的问题,我们如果说有上千个客户端的时候,我们服务端就会创建上千个线程,有多少客户端,就创建多少个线程,这对于 Java 来说, 代价实在是太大。

如果说我们线程在到达一定数量的时候,我们在做线程的切换的时候,大家可以想象一下对资源的浪费是什么样子的,一个线程和一百个线程甚至超过一千个线程的时候,在线程进行上下文切换的时候,会出现什么样子的问题。

针对某个线程来说,这种 BIO 的模型,在这个线程读取数据的时候,如果没有数据了,那线程就开始了阻塞,为了能够做到响应数据,这个线程在一直阻塞,这时候有新的请求的时候,线程阻塞,好吧,那我只能等,一直处于一个等待不能执行的状态。

BIO 的局限性二

  • 并发数大的时候,会创建非常多的线程来处理连接,系统资源会出现非常大的开销。

BIO 的局限性三

  • 在线程阻塞的时候,会造成资源的浪费。

实际上说是三个局限性,总得来说,他就是一个局限,浪费资源,开销大,这是非常致命的,一个小小的功能的话,你占用的服务器大量的资源,只是硬件上这块的内容,都得增加多少的成本,现在万恶的资本家们,抱着能省就省的原则,又扯远了,我们还是回归到 BIO 上。

而且这种 BIO 的模型,在本质上说,实际上就相当于是一个 1:1 的关系,而这种 1:1 的关系如果在客户端非常多的时候,创建的线程数所浪费的资源是非常巨大的,所以就出现了另外一种模型,NIO 模型。而 NIO 模型实际上目前使用的那可真的是太普遍了,比如说 Netty ,都是选择使用这种模型,不再继续使用 BIO 的模型了。

而关于 NIO 阿粉已经说了很多次,文章都写了好多,阿粉把文章都给大家放在下面,按照顺序阅读,不然阿粉怕大家会有点混乱

什么是BIO,NIO?他们和多路复用器有啥关系?

Java:前程似锦的 NIO 2.0

用Socket编程?我还是选择了Netty

而据说 JDK1.8 之后的 原生NIO API 中,会有空轮询的bug,会让服务器的 CPU 瞬间飙升到100%,具体真假,阿粉反正是没有碰到过,也没有测试出来,真的假的就留给大家去试试了。

文章参考

《田守枝的Java技术博客》 《Netty权威指南》

阅读全文 »

8 条伊隆·马斯克的特斯拉员工必须严格遵守的职场规则

发表于 2021-06-11 | 分类于 Java

Hello 大家好,我是阿粉,如果对币圈有了解的朋友会知道币圈经常会过山车式的跳水和疯涨,而这种情况发生往往很有可能是伊隆·马斯克发了一条动态导致的。马斯克可以说是很传奇的一个人,这位企业家兼 CEO 正在通过 SpaceX 彻底改变航天行业,在特斯拉改变电动汽车的世界,能以一己之力影响币圈,也可以创办 SpaceX 上太空,还可以创办特斯拉造福人类,不得不说对人类的贡献还是很显著的。

这么传奇的一个老板,是怎么看待公司员工的呢?

阅读全文 »

Activity 工作流中的表,原来表示的是这些

发表于 2021-06-10 | 分类于 Java

前几天,阿粉给大家说了关于 Activiti 的使用,后台就有好友私信阿粉说,这些表都不知道是什么意思,不行呀,看不明白呀,于是阿粉就打算再这次给大家讲一下关于 Activiti 的这些表中的字段都是表示的什么意思。

<–more–>

Activiti 的表

表   说明
act_ge_bytearray   通用数据
act_ge_property   流程引擎数据
act_hi_actinst   历史节点表
act_hi_attachment   历史附件表
act_hi_comment   历史意见表
act_hi_detail   历史详情
act_hi_identitylink   历史流程人员
act_hi_procinst   历史流程实例
act_hi_taskinst   历史任务
act_hi_varinst   历史变量
act_id_group   用户信息组
act_id_info   用户信息详情
act_id_membership   组和对应信息关联表
act_id_user   用户信息表
act_procdef_info   流程定义信息
act_re_deployment   部署信息
act_re_model   流程设计模型
act_re_procdef   流程定义数据
act_ru_event_subscr   信息监听
act_ru_execution   运行时流程执行数据
act_ru_identitylink   运行时节点人员数据信息
act_ru_job   定时任务数据
act_ru_task   运行时任务节点
act_ru_variable   流程变量数据

阿粉把之前的表给大家都拿出来了,然后我们一一来看这些字段都是些什么意思。在说这个Activiti的表结构的意思的时候,我们肯定首先要知道 Activiti 的生命周期,生命周期要经过的步骤如下:

1.流程部署 —> 2.启动流程实例 — > 3.执行流程对象(一个流程实例包含多执行对象) —> 4.完成整个流程

而我们要想了解这个流程,就得先从表结构开始了,我们来看看面所有的表结构吧。

大家如果对表结构没有任何的兴趣,那就往后滑,看后面的关于 Activiti 的流程的内容。

1.act_ge_bytearray 通用数据,二进制数据表

保存流程定义图片和xml、Serializable(序列化)的变量,即保存所有二进制数据,特别注意类路径部署时候,不要把svn等隐藏文件或者其他与流程无关的文件也一起部署到该表中,会造成一些错误(可能导致流程定义无法删除)。

字段说明

  • ID_ : 主键ID,也是主键唯一索引

  • REV_: Version(版本) 乐观锁

  • NAME_: 部署的文件名称,如:mail.bpmn、mail.png 、mail.bpmn20.xml

  • DEPLOYMENT_ID_: 部署表ID

  • BYTES_: 部署文件

  • GENERATED_: 是否是引擎生成,0为用户生成 1为Activity生成

在这里我们就要注意到 REV_ 这个乐观锁了,乐观锁每次在执行数据的修改操作时,都会带上一个版本号,一旦版本号和数据的版本号一致就可以执行修改操作并对版本号执行+1操作,否则就执行失败。因为每次操作的版本号都会随之增加,所以不会出现ABA问题,因为版本号只会增加不会减少。除了version以外,还可以使用时间戳,因为时间戳天然具有顺序递增性。好像又开始偏题了,我们回归正题,继续看表。

2.act_ge_property 流程引擎数据表
  • NAME_: 属性名称,也是主键

  • VALUE_: 资源

  • REV_: 乐观锁

  • next.dbid 当 Activiti 使用DbIdGenerator来生成主键时,用来表示Id块的起始值;Id块就是Activiti产生主键时,Id的取值范围,从next.dbid ~ next.dbid+idBlockSize-1 ,默认idBlockSize = 2500

  • schema.version 表示数据结构版本

  • schema.history 表示数据表结构的更新历史

这里面的数据一般情况下是这几个内容,标识的实际上相当于是 Activiti 的版本的一些相关的信息。

3.act_hi_actinst 历史节点表

这个表实际上就是表示的都是历史活动信息,流程流转过的所有节点的记录都在这个表中,但是他是记录的所有节点信息,而在 taskinst 只记录 usertask 内容

  • ID_: 主键ID

  • PROC_DEF_ID_: 流程定义ID

  • PROC_INST_ID_: 流程实例ID

  • EXECUTION_ID_: 执行实例ID

  • ACT_ID_: 节点定义ID

  • TASK_ID_: 任务实例ID 其他节点类型实例ID在这里为空

  • CALL_PROC_INST_ID_: 调用外部流程的流程实例ID

  • ACT_NAME_: 节点定义名称

  • ACT_TYPE_: 节点类型,如startEvent、userTask

  • ASSIGNEE_: 节点签收人

  • START_TIME_: 开始时间

  • END_TIME_: 结束时间

  • DURATION_: 耗时

4.act_hi_attachment 历史附件表
  • ID_: 主键ID

  • REV_: 乐观锁

  • USER_ID_: 用户ID

  • NAME_: 附件名称

  • DESCRIPTION_: 描述信息

  • TYPE_: 附件类型

  • TASK_ID_: 节点实例ID

  • PROC_INST_ID_: 流程实例ID

  • URL_: 附件地址

  • CONTENT_ID_: ACT_GE_BYTEARRAY的ID 二进制数据表的ID(对应关系)

5.act_hi_comment 历史意见表
  • ID_: 主键ID

  • TYPE_: 类型:event(事件)comment(意见)

  • TIME_: 填写时间

  • USER_ID_: 填写人用户ID

  • TASK_ID_: 节点实例ID

  • PROC_INST_ID_: 流程实例ID

  • ACTION_: 行为类型 AddUserLink、DeleteUserLink、AddGroupLink、DeleteGroupLink、AddComment、AddAttachment、DeleteAttachment.

  • MESSAGE_: 用于存放流程产生的信息,比如审批意见

  • FULL_MSG_: 附件地址

6.act_hi_detail 历史详情表
  • ID_: 主键ID

  • TYPE_: 类型 FormProperty–表单 VariableUpdate–参数

  • PROC_INST_ID_: 流程实例ID

  • EXECUTION_ID_: 执行实例ID

  • TASK_ID_: 任务实例ID

  • ACT_INST_ID_: 节点实例ID,ACT_HI_ACTINST表的ID

  • NAME_: 名称

  • VAR_TYPE_: 参数类型

  • REV_: 乐观锁

  • TIME_: 创建时间

  • BYTEARRAY_ID_: ACT_GE_BYTEARRAY表的ID

  • DOUBLE_: 存储变量类型为Double

  • LONG_: 存储变量类型为long

  • TEXT_: 存储变量值类型为String

  • TEXT2_: 此处存储的是JPA持久化对象时,才会有值。此值为对象ID

业务表单中填写的流程需要用到的变量,以及控制流程流转的变量所有的详细信息都会保存在这个历史详情表中。

7.act_hi_identitylink 历史流程人员

这个表其实就比较好了,因为当我们发起各种各样流程的时候,我们需要的永远都是保证数据从哪里来,已经谁发起的,追根溯源好找人呀。

  • ID_: 主键ID

  • GROUP_ID_: 组ID

  • TYPE_: 类型 assignee、candidate、owner、starter 、participant

  • USER_ID_: 用户ID

  • TASK_ID_: 节点实例ID

  • PROC_INST_ID_: 流程实例ID

主要存储历史节点参与者的信息,就是把发起流程的,还有参与过这个流程的人员信息,全部都加到表中。

8.act_hi_procinst 历史流程实例 (画重点的表!)
  • ID_: 主键ID

  • PROC_INST_ID_: 流程实例ID

  • BUSINESS_KEY_: 业务主键,业务表单的ID

  • PROC_DEF_ID_: 流程定义ID

  • START_TIME_: 开始时间

  • END_TIME_: 结束时间

  • DURATION_: 耗时

  • START_USER_ID_: 起草人的ID

  • START_ACT_ID_: 开始节点ID

  • END_ACT_ID_: 结束节点ID

  • SUPER_PROCESS_INSTANCE_ID_: 父流程实例ID

  • DELETE_REASON_: 删除原因

9.act_hi_taskinst 历史任务信息表 (画重点的表!)
  • ID_: 主键ID

  • PROC_DEF_ID_: 流程定义ID

  • TASK_DEF_KEY_: 节点定义ID

  • PROC_INST_ID_: 流程实例ID

  • EXECUTION_ID_: 执行实例ID

  • NAME_: 名称

  • PARENT_TASK_ID_: 父节点实例ID

  • DESCRIPTION_: 描述

  • OWNER_: 签收人(默认为空,只有在委托时才有值)任务的拥有者

  • ASSIGNEE_: 签收人或被委托

  • START_TIME_: 开始时间

  • CLAIM_TIME_: 提醒时间

  • END_TIME_: 结束时间

  • DURATION_: 耗时

  • DELETE_REASON_: 删除原因(completed,deleted)

  • PRIORITY_: 优先级别

  • DUE_DATE_: 过期时间,表明任务应在多长时间内完成

  • FORM_KEY_: 节点定义的formkey,desinger节点定义的form_key属性

10.act_hi_varinst 历史变量表
  • ID_: 主键ID

  • PROC_INST_ID_: 流程实例ID

  • EXECUTION_ID_: 执行实例ID

  • TASK_ID_: 任务实例ID

  • NAME_: 名称

  • VAR_TYPE_: 参数类型

  • REV_: 乐观锁

  • BYTEARRAY_ID_: ACT_GE_BYTEARRAY表的主键

  • DOUBLE_: 存储DoubleType类型的数据

  • LONG_: 存储LongType类型的数据

  • TEXT_: 存储变量值类型为String,如此处存储持久化对象时,值jpa对象的class

  • TEXT2_: 此处存储的是JPA持久化对象时,才会有值。此值为对象ID

11.act_id_group 用户信息组
  • ID_: 主键ID

  • REV_: 乐观锁

  • NAME_: 用户组名称

  • TYPE_: 用户组类型

12.act_id_info 用户信息详情表
  • ID_: 主键ID

  • REV_: 乐观锁

  • USER_ID_: 用户ID

  • TYPE_: 类型

  • KEY_: formINPut名称

  • VALUE_: 值

  • PASSWORD_: 密码

  • PARENT_ID_: 父节点

用户信息详情表,这个表好像有点鸡肋,目前说是还没有用到,

13.act_id_membership 用户与分组对应信息表

总得来说,这个表是真的简单,因为只是表示用户和组之间的对应关系,和很多硬件方面的内容好像很类似,就几个字段。

  • USER_ID: 用户ID

  • GROUP_ID: 用户组ID

14.act_id_user 用户信息表
  • ID_: 主键ID

  • REV_: 乐观锁

  • FIRST_: 用户姓

  • LAST_: 用户名

  • EMAIL_: 邮箱

  • PWD_: 密码

  • PICTURE_ID_: 头像Id

15.act_procdef_info 流程定义信息表
16.act_re_deployment 部署信息表
  • ID_: 主键ID

  • NAME_: 部署文件名

  • CATEGORY_: 分类类别

  • DEPLOY_TIME_: 部署时间

这个表主要就是在部署流程定义时需要被持久化保存下来的信息。

17.act_re_model 流程设计模型表
  • ID_: 主键ID

  • REV_: 乐观锁

  • NAME_: 名称

  • KEY_:模型的关键字 流程引擎用到。比如:FTOA_SWGL

  • CATEGORY_: 类型,用户自己对流程模型的分类。

  • CREATE_TIME_: 创建时间

  • LAST_UPDATE_TIME_: 最后的修改时间

  • VERSION_: 版本

  • META_INFO_: 以json格式保存流程定义的信息

  • DEPLOYMENT_ID_: 部署ID

  • EDITOR_SOURCE_VALUE_ID_: 编辑源值ID

  • EDITOR_SOURCE_EXTRA_VALUE_ID_: 编辑源额外值ID(外键ACT_GE_BYTEARRAY )

18.act_re_procdef 流程定义数据表

这个表示业务流程定义数据表,对应关系和 act_re_deployment 是多对一的关系。

  • ID_: 主键ID

  • REV_: 乐观锁

  • CATEGORY_: 流程定义的Namespace就是类别,该编号就是流程文件targetNamespace的属性值

  • NAME_: 流程名称,该编号就是流程文件process元素的name属性值

  • KEY_: 流程编号,该编号就是流程文件process元素的id属性值

  • VERSION_: 流程版本号,由程序控制,新增即为1,修改后依次加1来完成的

  • DEPLOYMENT_ID_: 部署表ID

  • RESOURCE_NAME_: 流程bpmn文件名称

  • DGRM_RESOURCE_NAME_: png流程图片名称

  • DESCRIPTION_: 描述信息

  • HAS_START_FORM_KEY_: 是否从key启动,start节点是否存在formKey 0否 1是

  • SUSPENSION_STATE_: 是否挂起,1激活 2挂起

19.act_ru_event_subscr 信息监听表
  • ID_: 主键ID

  • REV_: 乐观锁

  • EVENT_TYPE_: 事件类型

  • EVENT_NAME_: 事件名称

  • EXECUTION_ID_: 执行实例ID

  • PROC_INST_ID_: 流程实例ID

  • ACTIVITY_ID_: 活动实例ID

  • CONFIGURATION_: 配置信息

  • CREATED_: 创建时间

20.act_ru_execution 运行时流程执行数据表

这个表实际上就是很多 OA 中会出现的比如说,待办信息的展示。

  • ID_: 主键ID

  • REV_: 乐观锁

  • PROC_INST_ID_: 流程实例ID

  • BUSINESS_KEY_: 业务主键ID

  • PARENT_ID_: 父节点实例ID

  • PROC_DEF_ID_: 流程定义ID

  • SUPER_EXEC_:

  • ACT_ID_: 节点实例ID即 ACT_HI_ACTINST 中ID

  • IS_ACTIVE_: 激活状态,是否存活

  • IS_CONCURRENT_: 是否为并行(true/false)

  • IS_SCOPE_: 范围定义

  • IS_EVENT_SCOPE_: 事件范围

  • SUSPENSION_STATE_: 挂起状态 1激活 2挂起

  • CACHED_ENT_STATE_: 缓存结束状态

21.act_ru_identitylink 运行时节点人员数据信息表

任务参与者数据表。主要存储当前节点参与者的信息。

  • ID_: 主键ID

  • REV_: 乐观锁

  • GROUP_ID_: 分组ID

  • TYPE_: 用户组类型 主要分为以下几种:assignee、candidate、owner、starter、participant。即:受让人,候选人,所有者、起动器、参与者

  • USER_ID_: 用户ID

  • TASK_ID_: 任务Id

  • PROC_INST_ID_: 流程实例ID

  • PROC_DEF_ID_: 流程定义Id

22.act_ru_job 定时任务数据表
  • ID_: 主键ID

  • REV_: 乐观锁

  • TYPE_: 类型

  • LOCK_EXP_TIME_: 锁定释放时间

  • LOCK_OWNER_: 挂起者

  • EXCLUSIVE_:

  • EXECUTION_ID_: 执行实例ID

  • PROCESS_INSTANCE_ID_: 流程实例ID

  • PROC_DEF_ID_: 流程定义ID

  • RETRIES_:

  • EXCEPTION_STACK_ID_: 异常信息ID

  • EXCEPTION_MSG_: 异常信息

  • DUEDATE_: 到期时间

  • REPEAT_: 重复

  • HANDLER_TYPE_: 处理类型

  • HANDLER_CFG_: 标识

23.act_ru_task 运行时任务节点表
  • ID_: 主键ID

  • REV_: 乐观锁

  • EXECUTION_ID_: 执行实例ID

  • PROC_INST_ID_: 流程实例ID

  • PROC_DEF_ID_: 流程定义ID

  • NAME_: 节点定义名称

  • PARENT_TASK_ID_: 父节点实例ID

  • DESCRIPTION_: 节点定义描述

  • TASK_DEF_KEY_: 任务定义的ID

  • OWNER_: 拥有者(一般情况下为空,只有在委托时才有值) 实际签收人

  • ASSIGNEE_: 签收人或委托人

  • DELEGATION_: 委托类型

  • PRIORITY_: 优先级别,默认为:50

  • CREATE_TIME_: 创建时间

  • DUE_DATE_: 耗时

  • SUSPENSION_STATE_: 是否挂起,1代表激活 2代表挂起

24.act_ru_variable 流程变量数据表
  • ID_: 主键ID

  • REV_: 乐观锁

  • TYPE_: 编码类型

  • NAME_: 变量名称

  • EXECUTION_ID_: 执行实例ID

  • PROC_INST_ID_: 流程实例ID

  • TASK_ID_: 节点实例ID(Local)

  • BYTEARRAY_ID_:ACT_GE_BYTEARRAY的ID

  • DOUBLE_: 存储变量类型为Double

  • LONG_: 存储变量类型为long

  • TEXT_: 存储变量值类型为String 如此处存储持久化对象时,值jpa对象的class

  • TEXT2_: 此处存储的是JPA持久化对象时,才会有值。此值为对象ID

Activiti生命周期

1.流程部署 —> 2.启动流程实例 — > 3.执行流程对象(一个流程实例包含多执行对象) —> 4.完成整个流程

上图是个请假流程图,我们按照这个来整点代码来安排一下这个工作请假审批流。

  1. 画个图,发布流程,进行流程部署
1
2
3
4
5
6
rocessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.createDeployment()
  .addClasspathResource("activity.cfg.xml")
  .deploy();
Log.info("Number of process definitions: " + repositoryService.createProcessDefinitionQuery().count());
  1. 启动一个流程实例
1
2
3
4
5
6
7
8
9
    Map<String, Object> variables = new HashMap<String, Object>();
    variables.put("employeeName", "Kermit");
    variables.put("numberOfDays", new Integer(4));
    variables.put("vacationMotivation", "I'm really tired!");
    
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("vacationRequest", variables);
    
    Log.info("Number of process instances: " + runtimeService.createProcessInstanceQuery().count());
  1. 执行流程对象
1
2
3
4
5
TaskService taskService = processEngine.getTaskService();
List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("management").list();
for (Task task : tasks) {
  Log.info("Task available: " + task.getName());
}

上面代码中的 tasks 实际上可以说是一个任务列表,展示了所有必须由整个用户处理的任务

  1. 完成任务
1
2
3
4
5
Task task = tasks.get(0);
Map<String, Object> taskVariables = new HashMap<String, Object>();
taskVariables.put("vacationApproved", "false");
taskVariables.put("managerMotivation", "We have a tight deadline!");
taskService.complete(task.getId(), taskVariables);

对Activiti来说,就是需要complete任务,这个时候,实际上一个简单的请假流程就已经完成了,这时候,流程实例就会进入到下一个环节中。

而在这中间,我们可以设置一下把请假流程挂起,挂起的时候,就不能创建新流程了,不然就会出现异常,

1
2
3
4
5
6
repositoryService.suspendProcessDefinitionByKey("vacationRequest");
try {
  runtimeService.startProcessInstanceByKey("vacationRequest");
} catch (ActivitiException e) {
  e.printStackTrace();
}

我们通过 RepositoryService 挂起了流程,这个时候,流程不能继续执行,也不会执行异步操作,当我们需要激活这个流程的时候,我们就需要调用:

runtimeService.activateProcessInstanceXXX 方法来对流程进行激活了。

Activiti 的生命周期,就是这么简单,你学会了么?

文章参考

《Activiti使用手册》 《Spring-Activiti代码》

阅读全文 »

手把手教你搭建一个简单的Activity工作流项目

发表于 2021-06-10 | 分类于 Java

前段时间,公司说要做技术分享,于是没周都会安排同事进行技术方面的分享,虽然有时候大部分的人在玩手机,有些同事也在专心致志的在学习,毕竟程序员永远都是保持在学习写代码的路上,JDK都出到16了,尽管你可能现在还是在使用 JDK8 但是还是要继续学习呀。于是阿粉就准备研究一些公司目前没有用到的关系,就学习了一下 Activity 工作流的相关知识,在这里阿粉也分享给大家。

<–more–>

什么是 Activity 工作流引擎 ?

什么是工作流,比如说,我们在公司请假,可能要走蹭蹭审批的流程,从你自己到 Leader,然后从 Leader 到部门经理,然后部门经理再到人事部门,这一系列的流程实际上就相当于是一个工作流程,而这个就是一个工作流的最容易理解的模型。

这肯定官方解读肯定不是这样,不然也太接地气了点,那么什么是 Activity 工作流呢?

工作流(Workflow),指“业务过程的部分或整体在计算机应用环境下的自动化”。是对工作流程及其各操作步骤之间业务规则的抽象、概括描述。在计算机中,工作流属于计算机支持的协同工作(CSCW)的一部分。后者是普遍地研究一个群体如何在计算机的帮助下实现协同工作的。

工作流主要解决的主要问题是:为了实现某个业务目标,利用计算机在多个参与者之间按某种预定规则自动传递文档、信息或者任务。

其实看到百度百科给我们的提示,我们就知道了,实际上工作流就是为了让多个业务目标之间,按照某种规则传递信息。

上面的图是一个请假的流程图,网上的图,不是手动画的,但是表示的意思还是很清晰的。

接下来我们就整一个关于 Activity 的项目来搞一搞吧。

准备工作

  1. 我们先再 IDEA 中装个插件 actiBPM ,直接装就好。

  1. 从 GitHub 上下载源代码 https://github.com/Activiti/Activiti 或者我们直接从官网上下载,https://www.activiti.org/get-started,下载版本的话,新版本也可以,老版本也凑活,我们下载了个比较古老的版本,5.22.

因为网速原因,数据包阿粉已经给大家准备好了,大家回复 Activity 就可以获取下载连接,

数据库在文件中,大家要注意,文件中的数据库是有对应的,mysql,oracle,这些都是不一样的,别直接打开就导入,结果导入半天,报了一大堆错误,还不知道为啥出错。

使用这几个 SQL 的脚本建立完数据库之后,就是上图的这些了,我们来看看都是有哪些表。

  • ACT_HI_*: 这些表包含历史数据,比如历史流程实例, 变量,任务等等

  • ACT_ID_*: 这些表包含身份信息,比如用户,组等等。

  • ACT_RE_*: 表包含了流程定义和流程静态资源 (图片,规则,等等)

  • ACT_RU_*: 包含流程实例,任务,变量,异步任务等运行中的数据

下面的这些表示通过下载的源码包然后进行导入进来的,我们下面在使用 Activity 的时候,我们会直接使用 Activity 设计好流程图,然后我们直接让它帮我们去生成表。

表   说明
act_ge_bytearray   通用数据
act_ge_property   流程引擎数据
act_hi_actinst   历史节点表
act_hi_attachment   历史附件表
act_hi_comment   历史意见表
act_hi_detail   历史详情
act_hi_identitylink   历史流程人员
act_hi_procinst   历史流程实例
act_hi_taskinst   历史任务
act_hi_varinst   历史变量
act_id_group   用户信息组
act_id_info   用户信息详情
act_id_membership   组和对应信息关联表
act_id_user   用户信息表
act_procdef_info   流程定义信息
act_re_deployment   部署信息
act_re_model   流程设计模型
act_re_procdef   流程定义数据
act_ru_event_subscr   信息监听
act_ru_execution   运行时流程执行数据
act_ru_identitylink   运行时节点人员数据信息
act_ru_job   定时任务数据
act_ru_task   运行时任务节点
act_ru_variable   流程变量数据

我们了解了这些表数据都是干啥用的之后,接下来就直接从使用开始吧,毕竟要先看看这用起来是啥样子,才能知道他到底为什么这么香。

这些表如果你自己不拿出来用的话,使用 IDEA 创建关于 Activity 的项目的时候,会给你自动创建表,就类似 Hibernate 一样。

使用 IDEA 创建 Activity 项目

  1. 使用 IDEA 创建一个项目,然后再 POM 文件中加入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
  <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!--- Activiti依赖导入 -->
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>5.18.0</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-engine</artifactId>
        <version>5.18.0</version>
        <exclusions>
            <exclusion>
                <artifactId>slf4j-api</artifactId>
                <groupId>org.slf4j</groupId>
            </exclusion>
            <exclusion>
                <artifactId>spring-beans</artifactId>
                <groupId>org.springframework</groupId>
            </exclusion>
            <exclusion>
                <artifactId>jackson-core-asl</artifactId>
                <groupId>org.codehaus.jackson</groupId>
            </exclusion>
            <exclusion>
                <artifactId>commons-lang3</artifactId>
                <groupId>org.apache.commons</groupId>
            </exclusion>
            <exclusion>
                <artifactId>commons-lang3</artifactId>
                <groupId>org.apache.commons</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.35</version>
    </dependency>

工作流就是工作流,那是不是得有流程图,就像某些 OA 系统中,要先进行定义流程图,然后自动给你开始搞事情,我们画个流程图来试试。

  1. 在src/main/resources下面新建一个BPMN文件

流程图建立完成之后,就出现了在 IDEA 中从来没有见过的画面,

大家看上图的右半部分,是不是有很多的类似按钮的标志,我们来解释一下他们都是些什么。

  • StartEvent:启动事件元素,启动事件元素就是启动流程实例的,也就是发起一个流程的,是流程的起点。它可以配置的很简单,也可以很复杂。

  • EndEvent:结束事件元素,Activity工作流始于开始任务,止于结束任务

  • UserTask:用户操作的任务

  • ScriptTask: 脚本任务

  • ServiceTask:服务任务

  • MailTask: 邮件任务

  • ManualTask: 手工任务

  • ReceiveTask: 接收任务

  • BusinessRuleTask:规则任务

  • CallActivityTask:调用其他流程任务

  • SubProcess: 子流程

  • Pool: Pool池

  • Lane: Lane小巷 (注意:Lane小巷是放在Pool池里面的)

  • ParallelGateWay: 并行网关

  • ExclusiveGateWay: 排他网关

  • InclusiveGateWay: 包容网关

  • EventGateWay: 事件网关

  • BoundaryEvent: 边界事件

  • IntermediateCatchingEvent: 中间事件

  • IntermediateThrowingEvent:边界补偿事件

  • Annotation: 注释

我们先画一个简单的流程图,然后生成我们需要的表。

图我们换完了,接下来我们就来整个 Activity 的配置文件,然后使用配置文件去生成表。

activity.cfg.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/managementactivity?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="jdbcUsername" value="root"></property>
        <property name="jdbcPassword" value="123456"></property>
        <property name="databaseSchemaUpdate" value="true"></property>
    </bean>
</beans>

接下来我们就是用这个配置,去生成我们的数据表,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.activity.zhiyikeji.management;

import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.junit.Test;

/**
 * @ClassName LeaveFlow
 * @Author
 * @Date 2021/6/11 14:06
 * @Description LeaveFlow
 */
public class LeaveFlow {
    @Test
    public void creatTable(){
        ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activity.cfg.xml").buildProcessEngine();
    }

}

执行的时候,我们看一下控制台打印了什么内容,

然后去看看你的数据库,是不是生成成功了,看一看表的数量,一般是24,

从数据包中取出来的 SQL 脚本是多了一个流程定义信息表,这么看来,这个表对使用 Activity 来说意义不大,没他还是可以运行的。

我们已经创建好表之后,接下来我们就来直接进行部署我们画的流程图,然后看看数据库的表中是一些什么样子的数据。

1
2
3
4
5
6
7
8
9
10
11
    /**
     * 部署请假流程
     */
    @Test
    public void deployLeaveFlow(){
        ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activity.cfg.xml").buildProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentBuilder builder = repositoryService.createDeployment();
        builder.addClasspathResource("zhiyikeji.BPMN");//bpmn文件的名称
        builder.deploy();
    }

嘿,提示完成了,一次过,没出错,

1
2
14:32:50.747 [main] DEBUG org.activiti.engine.impl.interceptor.LogInterceptor - --- DeployCmd finished --------------------------------------------------------
14:32:50.747 [main] DEBUG org.activiti.engine.impl.interceptor.LogInterceptor - 

当我看到这个的时候,我就放心了,我知道,看来,进去了,没啥问题了,那我们就来试试启动一下这个流程。

1
2
3
4
5
6
7
8
9
    /**
     * 启动请假流程
     */
    @Test
    public void startProcess() {
        ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activity.cfg.xml").buildProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        runtimeService.startProcessInstanceByKey("leaveProcess");//流程的名称,也可以使用ByID来启动流程
    }

在我们执行完启动请假流程的时候,在 act_ru_task 运行时任务节点表中,就有了我们的一条任务,这样我们就能看到这个任务是什么了。

是不是这么看有点太基础了,这东西看起来也没有我们想象的这么高大上,那我们就找个开源项目,然后把工作流所有的东西都跑起来,然后再去一个个的分析工作流的内容。

阿粉找到了一个开源的项目,项目还是不错的,尤其是得感谢开源出来的大佬shenzhanwang ,先给大家放上图,大家有兴趣的可以下载。

毕竟开源不易,大家对这个有兴趣的可以下载一下看看,阿粉之后再继续给大家了解一下关于 Activity 的里面的一些画 bpmn 图的那些流程上的所有内容。

回复 Activity 获取 Activity的包和源码地址呦。

阅读全文 »

用责任链模式实现 OA 系统中的流程审批

发表于 2021-06-08 | 分类于 Java

Hello 大家好,我是阿粉,工作中我们经常会遇到很多需要上级或者上级的上级一层层审批的流程,作为程序员如果要让你实现这个流程,你会采用什么方式呢?

好了思考一分钟结束,很显然大家一致的回答就是责任链模式。那么什么是责任链模式呢?如何使用责任链模式去完成这个流程呢?下面我们来看一下。

阅读全文 »

Mockito 一个优秀的 Mock 测试框架

发表于 2021-06-05 | 分类于 Java

Hello 大家好,我是阿粉,日常工作中很多时候我们都需要同事间的相互配合协作完成某些功能,所以我们经常会遇到服务或者应用内不同模块之间要互相依赖的场景。比如下面的场景,serviceA 中的 methodA() 方式依赖 serviceB 中的 methodB() 方法返回操作的结果。那如果我们要对自己的methodA() 方法进行编写单元测试,还需要等其他同事的methodB() 方法开发完成才行。那有没有什么办法我们可以跳过或者说模拟方法 B 的输出呢?这就引出了我们今天的主角 Mockito,一个优秀的 Mock 测试框架。

阅读全文 »

分布式锁原来实现起来这么简单

发表于 2021-06-04 | 分类于 Java

阿粉最近迷上了 Redis,为什么呢?感觉 Redis 确实功能很强大呀,一个基于内存的 Key-Value 存储的数据库,竟然有这么多的功能,而阿粉也要实实在在的把 Redis 来弄一下,毕竟面试的时候,Redis 可以说是一个非常不错的加分项。

<–more–>

分布式锁

为什么需要分布式锁?

目前很多的大型项目全部都是基于分布式的,而分布式场景中的数据一致性问题一直是一个不可忽视的问题,大家知道关于分布式的 CAP 理论么?

CAP 理论就是说任何一个分布式系统都无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance),最多只能同时满足两项。

而我们的系统最终满足的永远都是最终一致性,而这种最终一致性,有些时候有人会喜欢问关于分布式事务,而有些人则偏重在分布式锁上。

分布式锁的种类

  1. 数据库实现分布式锁

  2. 缓存实现分布式锁

  3. Zookeeper实现分布式锁

但是阿粉选择的就是使用缓存来实现分布式锁,也就是我们在项目中最经常使用的 Redis ,谈到 Redis,那真是可以用在太多地方了,比如说:

  • 会话缓存

  • 消息队列

  • 分布式锁

  • 发布,订阅消息

  • 商品列表,评论列表

我们今天就来实现用 Redis 来实现分布式锁,并且要学会怎么使用。

准备工作

1.准备使用 Jedis 的 jar 包,在项目中导入 jar 包。

1
2
3
4
5
6
<!--jedis-->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>
  1. 直接来写个工具类吧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class RedisPoolUtil {

    private static final String LOCK_SUCCESS = "OK";
    private static final String SET_IF_NOT_EXIST = "NX";
    private static final String SET_WITH_EXPIRE_TIME = "PX";

    private RedisPoolUtil(){}
    /**
     * 
     * @param jedis 
     * @param lockKey 加锁
     * @param requestId 请求的标志位
     * @param expireTime 超时时间
     * @return
     */
    public static boolean tryGetDistributedLock(Jedis jedis,String lockKey, String requestId, int expireTime) {

        String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

        if (LOCK_SUCCESS.equals(result)) {
            return true;
        }else{
            try{
                Thread.sleep(10);//休眠100毫秒
            }catch(Exception e){
                e.printStackTrace();
            }
        }
        return false;
    }
}

jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime); 这个加锁的姿势才是我们最需要了解的,不然你用的时候都不知道怎么使用。

key:加锁的键,实际上就是相当于一个唯一的标志位,不同的业务,你可以使用不同的标志位进行加锁。

requestId:这个东西实际上就是用来标识他是哪一个请求进行的加锁,因为在分布式锁中,我们要知道一件事,就是加锁的和解锁的,必须是同一个客户端才可以。

而且还有一种比较经典的就是 B 把 A 的锁给释放了,导致释放混乱,如果你不加相同的请求,A 线程处理业务,执行了加锁,锁的过期时间是5s, B线程尝试获取锁,如果 A 处理业务时间超过5s,这时候 A 就要开始释放锁,而B在这时候没有检测到这个锁,从而进行了加锁,这时候加锁的时候,A还没处理完对应业务,当他处理完了之后,再释放锁的话,要是就是直接把 B 刚加的锁释放了,要么就是压根都没办法释放锁。

SET_IF_NOT_EXIST:看字面意思,如果 key 不存在,我们进行Set操作,如果存在,啥都不干,也就不在进行加锁。

SET_WITH_EXPIRE_TIME:是否过期

expireTime:这是给 key 设置一个过期的时间,万一你这业务一直被锁着了,然后之后的业务想加锁,你直接给一直持有这个这个锁,不进行过期之后的释放,那岂不是要凉了。

上面的方法中 tryGetDistributedLock 这个方法也就是我们通常使用的加锁的方法。

解锁

1
2
3
4
5
6
7
8
9
10
11
 public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {

        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

        if ("OK".equals(result)) {
            return true;
        }
        return false;

    }

大家看到这个 script的时候,会感觉有点奇怪,实际上他就是一个 Lua 的脚本,而 Lua 脚本的意思也比较简单。

  1. 先获取锁对应的value值,检查是否与requestId相等

  2. 如果相等则删除锁(解锁)

  3. 执行eval()方法

其实这时候就有些人说,直接 del 删除不行么?你试试你如果这么写的话,你们的领导会不会把你的腿给你打断。

这种不先判断锁的拥有者而直接解锁的方式,会导致任何客户端都可以随时进行解锁,也就是说,这锁就算不是我加的,我都能开,这怎么能行呢?

在这里给大家放一段使用的代码,比较简单,但是可以直接用到你们的项目当中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try{
Boolean result = RedisPoolUtil.tryGetDistributedLock(jedis, "xxxxx", uuid, 5000);

if(result) {
        xxxx代码片段
}else{

}

}catch(){

}finally{
RedisPoolUtil.releaseDistributedLock(jedis,"xxxxx", uuid);
}

分布式锁的要求

  1. 满足互斥性。也就是说不管在什么时候,只有一个客户端能够持有锁,不能是多个客户端。

  2. 不能出现死锁。就是说,如果要实现分布式锁,不能说当一个锁没有释放的时候,其他的客户端不能进行加锁,要保证不影响其他的客户端加锁。

  3. 加锁和解锁必须是同一个客户端

分布式的CAP理论

我们先把这个实现方式实现了,然后我们再来说说大家最不愿意看的理论知识,毕竟这理论知识是你面试的时候经常会被问到的。

分布式CAP理论:

加州大学伯克利分校的 Eric Brewer 教授在 ACM PODC 会议上提出 CAP 猜想。2年后,麻省理工学院的 Seth Gilbert 和 Nancy Lynch 从理论上证明了 CAP。之后,CAP 理论正式成为分布式计算领域的公认定理。

也就是说,在二十年前的时候,CAP 理论只是个猜想。结果两年之后被证实了,于是,大家在考虑分布式的时候,就有根据来想了,不再是空想了。

什么是分布式的 CAP 理论 ?

一个分布式系统最多只能同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)这三项中的两项

这个和(Atomicity)不太一样,因为之前看有些人说,在 CAP 理论中的 A 和数据库事务中的 A 是一样的,单词都不一样,那能一样么?

Availability :分布式中的 A 表示的是可用性,也就是说服务一直可用,而且是正常响应时间。

而你在搭建分布式系统的时候,要保证每个节点都是稳定的,不然你的可用性就没有得到相对应的保证,也谈不上是什么分布式了。只能称之为一个伪分布式。

Consistency: 一致性

也就是说你的更新操作成功并返回客户端完成后,所有节点在同一时间的数据完全一致,这个如果你在使用 Redis 做数据展示的时候,很多面试官都会问你,那你们是怎么保证数据库和缓存的一致性的呢?

毕竟你只是读取的话,没什么问题,但是设计到更新的时候,不管是先写数据库,再删除缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。

所以如果你对这个很感兴趣,可以研究一下,比如说:

  1. 延时双删策略

  2. 懒加载 懒加载可采取双删+TTL失效来实现

  3. 主动加载

如果你能在面试的时候把这些都给面试官说清楚,至少感觉你应该能达到你自己的工资要求。

Partition tolerance:分区容错性

分布式系统在遇到某节点或网络分区故障的时候,仍然能够对外提供满足一致性和可用性的服务。

其实在 CAP 理论当中,我们是没有办法同时满足一致性、可用性和分区容错性这三个特性,所以有所取舍就可以了。

关于使用 Redis 分布式锁,大家学会了么?

阅读全文 »

分布式锁原来实现起来这么简单

发表于 2021-06-04 | 分类于 Java

阿粉最近迷上了 Redis,为什么呢?感觉 Redis 确实功能很强大呀,一个基于内存的 Key-Value 存储的数据库,竟然有这么多的功能,而阿粉也要实实在在的把 Redis 来弄一下,毕竟面试的时候,Redis 可以说是一个非常不错的加分项。

阅读全文 »

分布式全局唯一ID方案这么多?

发表于 2021-06-02 | 分类于 Java

前段时间阿粉想着如何去优化我们公司中已经存在的分布式中的唯一ID,而提起唯一的ID,相信如果不是从事传统行业的人,肯定都有所了解,分布式架构下,唯一ID生成方案,是我们在设计一个系统,尤其是数据库使用分库分表的时候常常会遇见的问题,尤其是当我们进行了分库分表之后,对这个唯一ID的要求也就越来越高。那么唯一ID方案都有哪些呢?

<–more–>

分布式全局唯一ID

往往一谈分布式,总是会 色变,因为在很多面试的时候,都会问你,会不会分布式?你们项目的架构是怎么做的,做的如何?你们既然使用了分布式,那么你们的分布式事务是怎么处理的,你们分布式全局唯一 ID 使用的是什么算法来实现的?

往往谈到这个的时候,很多面试的朋友就会很尴尬,我都是直接用的,我好像完全没有注意过。当你意识到这一点的时候,往往接下来的问题,你回答的就会开始磕磕绊绊,于是面试凉了。

并发越大的系统,数据就越大,数据越大就越需要分布式,而大量的分布式数据就越需要唯一标识来识别它们,而这些唯一标识,我们就称之为分布式全局唯一的ID。

Redis实现全局唯一ID

阿粉的项目说实话,还不是特别的差劲。于是阿粉就开始想着,这分布式的全局唯一ID,为啥生成的时候都是使用 UUID ,要么就是自增主键呢?

于是阿粉准备使用 Redis 来生成分布式全局唯一ID。

Redis实现全局唯一ID原理

因为 Redis 的所有命令是单线程的,所以可以利用 Redis 的原子操作 INCR 和 INCRBY,来生成全局唯一的ID。 方式一:StringRedisTemplate

1
2
3
4
5
public class Activity {
    private Long id;
    private String name;
    private BigDecimal price;
}

上面是我们的活动的实体类,马上就要 618 了,各位做电商的是不是开始准备搞事情了?可以学习一下用一下试试,我们活动中有 id ,活动的名称 name ,还有对应活动设置好的价格 price 等等,字段可能还会有很多,我们需要的暂时就列出这么多。

1
2
3
4
5
6
7
8
9
public class IdGeneratorService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private static final String ID_KEY = "id:generator:activity";

    public Long incrementId() {
        return stringRedisTemplate.opsForValue().increment(ID_KEY);
    }
1
long id = idGeneratorService.incrementId(); 调用生成

但是看起来是不是总是感觉好像有点 low ,我们是不是就要准备来整的高大上一点,毕竟代码就像一个程序员的内裤,虽然自己看着有洞感觉没啥,但是别人看到是不是就很不爽了,那就整个他们看起来比较高大上一点的。

方式二:

为什么会有方案二,那是因为我们的 Redis 很多时候都不是只有一个 Redis,都是搭建的集群,既然是集群,我们就要开始合理的利用上集群。

那么我们就要开始考虑到集群方面的知识了,那么我们的思路就有了。于是出现了:集群中每个节点预生成生成ID;然后与redis的已经存在的ID做比较。如果大于,则取节点生成的ID;小于的话,取Redis中最大ID自增。

这个时候我们还需要一段 lua 脚本来保证我们实现的ID是唯一的,这才是真正的本质,不然我们实现的ID在高端,不唯一,有个锤子用

核心脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
local function get_max_seq()
    local key = tostring(KEYS[1])
    local incr_amoutt = tonumber(KEYS[2])
    local seq = tostring(KEYS[3])
    local month_in_seconds = 24 * 60 * 60 * 30
    if (1 == redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

setnx

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key, seq)) then redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

expire

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key, month_in_seconds) return seq else local prev_seq = redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

get

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key) if (prev_seq < seq) then redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

set

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key, seq) return seq else --[[ 不能直接返回redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

incr

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key),因为返回的是number浮点数类型,会出现不精确情况。 注意: 类似"16081817202494579"数字大小已经快超时lua和reids最大数值,请谨慎的增加seq的位数 --]] redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

incrby

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key, incr_amoutt) return redis.call(

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

get

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

, key) end end end return get_max_seq()

以上的 lua 的脚本来自于Ydoing,一个博客的大佬,我们现在既然会使用他生成全局唯一的ID,那么是不是就得搞清楚为什么会选择 Redis 来实现分布式全局唯一的ID。

Redis 的所有命令是单线程的

上一段开头,阿粉就说 Redis 的命令都是单线程的,相信如果你在面试官面前这么说,面试官肯定会问你一句,为什么 Redis 是单线程而不是多线程的呢?

Redis 基于 Reactor 模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:多个套接字、IO多路复用程序、文件事件分派器、事件处理器。因为文件事件分派器队列的消费是单线程的,所以 Redis 才叫单线程模型。

当你说到这个 Reactor 模式的时候,如果大家深入研究过 Netty 的模型,就会发现,这个模式在 Netty 中也是有使用的,我们这时候是不是就得需要去官网上去瞅瞅看,为什么这么说。

什么是Reactor模型

Reactor模型实际上都知道,就是一个多路复用I/O模型,主要用于在高并发、高吞吐量的环境中进行I/O处理。

而这种多路复用的模型所依赖的永远都是那么几个内容,事件分发器,事件处理器,还有调用的客户端,

Reactor模型是一个同步的I/O多路复用模型,我们还是先把这个同步的I/O多路复用模型给弄清楚了再看其他的。

这个相信大家肯定不是很熟悉,而阿粉在之前也给大家说了关于Netty中的Channel,文章地址发给大家,用Socket编程?我还是选择了Netty,在文章中,我们已经给大家说了关于Channel,而这种单线程的模型是什么样子的呢?

图已经给大家画出来了,丑是丑了点,但是意思还是表达出来了。

这种模型也就是说:Redis 单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。

而面试官还会有一种问法,为什么使用 Redis 就会快。

这个相信大家肯定能回答出来,因为 Redis 是一种基于内存的存储数据,为什么内存快?

因为这种快速是针对存储在磁盘上的数据来说的,因为内存中的数据,断电之后,消失了,你下次来的时候,不还是需要从磁盘读取出来,然后保存,所以说在Redis速度快。扯远了,回来继续说 Redis的单线程。

我们来看看官网给我们的解释:

1
2
3
4
5
6
7
8
Redis is single threaded. How can I exploit multiple CPU / cores?
It's not very frequent that CPU becomes your bottleneck with Redis, as usually Redis is either memory or network bound. For instance, using pipelining Redis running on an average Linux system can deliver even 1 million requests per second, so if your application mainly uses O(N) or O(log(N)) commands, it is hardly going to use too much CPU.

However, to maximize CPU usage you can start multiple instances of Redis in the same box and treat them as different servers. At some point a single box may not be enough anyway, so if you want to use multiple CPUs you can start thinking of some way to shard earlier.

You can find more information about using multiple Redis instances in the Partitioning page.

However with Redis 4.0 we started to make Redis more threaded. For now this is limited to deleting objects in the background, and to blocking commands implemented via Redis modules. For future releases, the plan is to make Redis more and more threaded.

其实翻译过来大致就是说,当我们使用 Redis 的时候,CPU 成为瓶颈的情况并不常见,因为 Redis 通常是内存或网络受限的。

其实说白了,官网就是说我们 Redis 就是这么的快,并且正是由于在单线程模式的情况下已经很快了,就没有必要在使用多线程了。这整的是不是就有点恶心了。阿粉也说说自己的见解,毕竟这官网的话有点糊弄人的意思。

其实 Redis 使用单个 CPU 绑定一个内存,针对内存的处理就是单线程的,而我们使用多个 CPU 模拟出多个线程来,光在多个 CPU 之间的切换,然后再操作 Redis ,实际上就不如直接从内存中拿出来,毕竟耗时在这里摆着。

你认为的 Redis 为什么是单线程的?

阅读全文 »

你知道 Redis 服务器接收到一条命令是如何执行的吗?

发表于 2021-05-30 | 分类于 Java

Hello 大家好,我是阿粉,Redis 作为工作中不可缺少的缓存组件,相信很多小伙伴都会使用到,我们日常使用的时候都是通过代码或者客户端去链接 Redis 服务器来操作数据的。那么一条简单的set name ziyou 命令是如何执行的,中间都经历了哪些过程想必很少会有人去了解。今天阿粉就带大家看一下一条简单的set name ziyou 命令是如何执行的。

我们可以看到在执行set name ziyou 这个命令过后,先显示一个OK 在终端里面。下面我们看下这整个过程都经历了哪些步骤。

命令的整个执行分为下面几个步骤,我们先看流程,在仔细分析:

  1. 客户端发送命令请求;
  2. 服务端读取命令请求;
  3. 命令执行器进行操作
    1. 命令执行器查找命令实现函数;
    2. 命令执行器执行预备操作;
    3. 命令执行器调用命令的实现函数;
    4. 命令执行器执行后续工作;
  4. 服务端将命令回复发送给客户端;
  5. 客户端接收并打印命令回复内容;

客户端发送命令请求

首先当客户端和服务端建立好了链接过后,当我们输入命令 set name ziyou 命令请求的时候,客户端会将这个命令进行协议转换,然后通过连接将转换后的协议发送到服务端。

比如当我们输入命令set name ziyou 的时候,客户端会将这个原始命令转换成*3\r\n$3\r\nset\r\n$4\r\nname\r\n$5\r\nziyou,这个协议大家应该比较眼熟,就是 Redis 管道的文件格式。简单解释下这个协议的意思,前面的*3 表示这个命令总共有三个参数,其中的$3,$4,$5 表示相应参数的长度。

服务端读取命令请求

当服务端收到该客户端的数据时,就会调用命令请求处理器来处理对应的消息。这块主要涉及到三个操作,第一个是保存命令,也就是会将命名的请求信息读取出来保存到对应客户端的输入缓冲区里面;保存完了过后会对输入缓冲区里面的内容进行解析,也就是对上面转换后的协议进行解析,解析出要执行的命令和对应的参数,将参数内容和参数个数保存到客户端的对应参数里面;第三步是调用命令执行器来执行命令。执行的命令和参数保存在RedisClient 结构的 argv 参数中,如下图所示,命令分析完成后,第三步才能更好的进行执行操作:

命令执行器

命令执行器查找实现函数

思考一个问题,我们这里 argv[0] 参数中的命令的是进行set 操作,在这里是个 set 字符串,那么 Redis 服务器是如何进行执行的呢?我们可以想到的是需要根据这个字符串找到对应的函数来进行操作,Redis 在内部有个的命令表,是一个字典结果,key 就是对应的命令名字,字典的值就是一个个 RedisCommand 结构,记录了命令的实现信息。

结构如下,简单来说就是通过 argv[0] 中的命令名称找到命令表中对应的redisCommand 结构,然后根据 proc 指针找到对应的执行命令。这里说明一下,命令名称的大小写没有任何影响,我们在输入的时候不用关心命令名称的大小写问题。

命令执行器执行预备操作

在 Redis 服务器执行相关命令之前,为了保证命令能够正确的执行,还需要进行相关的预备处理,部分预操作如下:

  1. 检查命令的参数和输入的参数个数是否一致,不一致则直接返回错误;
  2. 检查客户端是否通过身份验证,未通过身份验证则只能执行 AUTH 命令进行身份验证;
  3. 检查服务器的内容使用情况,为了保证命令执行成功,可能会需要进行内容回收;

除了上面的功能之外还有很多需要预备执行的动作,而且根据服务器部署的情况不一样,单机还是集群需要执行的操作还有不同。只有当所有的 预备操作都执行成功过后,才会真正的执行用户的命令。

由此可见 Redis 的性能是真正的高效,在有这么做操作流程的情况下还能保住命令执行的如此快速,不得不说真的很优秀。

命令执行器调用命令的实现函数

当前面的预备操作都完成过后,命令执行器就会调用对应的实现函数,在我们这里的例子就是调用 setCommand(redisClient *c) 函数进行数据写入操作,具体的 key 值和 value 值在 redisClient 结构中已经保存了,所以只要传递一个指针进去就可以了。setCommand() 命令执行后会返回一个OK\r\n ,这个返回会被保存到客户端的输出缓冲区当中,输出缓冲区的内容后续会被返回到客户端,给用户展示出来,如前面的图片显示的内容。

命令执行器执行后续工作

当命令执行器调用具体的实现函数过后,服务器还会有相应的一些操作要做,比如如果开启了慢日志功能,会检查是否要写入慢日志;如果开启了 AOF 则需要将刚刚执行的命令写入 AOF 的缓冲区中;以及如果有服务器备份或者监听的时候,会把刚刚执行的命令广播过去。

服务端将命令回复发送给客户端

实现函数执行完过后会将执行结果保存到客户端的输出缓冲区中,此时服务器的命令回复处理器会将缓冲区中的命令回复发送给客户端。命令回复处理器发送完数据过后会将客户端的输出缓冲区清理,方便后续的命令存入数据,同样回复的数据也是经过协议转换的。

客户端接收并打印命令回复内容

客户端收到回复数据过后就数据转换成可读的形式,输出到控制台。这样就得到了我们第一张图片的结果。

总结

通过上面所有的过程,我们可以看到,就是一个简单的set name ziyou 这样的语句,整个执行的过程也还是很复杂的,Redis 服务器在设计的时候要考虑很多东西,安全,性能等等方面。

引用

《Redis 设计与实现第二版》

阅读全文 »
1 2 3 4 … 31
Java Geek Tech

Java Geek Tech

一群热爱 Java 的技术人

610 日志
116 分类
24 作者
RSS
GitHub 知乎
Links
  • 纯洁的微笑
  • 沉默王二
  • 子悠
  • 江南一点雨
  • 炸鸡可乐
  • 郑璐璐
  • 程序通事
  • 懿
© 2019 - 2022 Java Geek Tech
由 Jekyll 强力驱动
主题 - NexT.Mist