欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java手寫Redis服務(wù)端的實(shí)現(xiàn)

 更新時(shí)間:2021年12月27日 11:31:05   作者:SharingOfficer  
本文主要介紹了Java手寫Redis服務(wù)端的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下

零,起因

我為什么要造redis這個(gè)輪子?

1,破除對(duì)redis神秘感。
2,“基礎(chǔ)服務(wù)中臺(tái)”的同事們?cè)陂_會(huì)討論redis云,以及redis代理。
3,開一個(gè)redis資源并不是容易事,為什么不可以不可以寫成java直接推送到未來云上,簡(jiǎn)單方便。
以這個(gè)思路我開始使用業(yè)余時(shí)間研究了redis的tcp通訊原理與redis命令,出發(fā)點(diǎn)是寫一個(gè)redis云代理之類的云管理軟件,但是還是忍不住寫成了java版的redis,本文章主要分享redis的編寫心路歷程

一,redis通訊與Netty

1,tcp

連到Redis服務(wù)器的客戶端建立了一個(gè)到6379端口的TCP連接。

雖然RESP在技術(shù)上不特定于TCP,但是在Redis的上下文中,該協(xié)議僅用于TCP連接(或類似的面向流的連接,如unix套接字)。

使用netty作為通訊框架。

2,協(xié)議

Redis客戶端和服務(wù)器端通信使用名為 RESP (REdis Serialization Protocol) 的協(xié)議。雖然這個(gè)協(xié)議是專門為Redis設(shè)計(jì)的,它也可以用在其它 client-server 通信模式的軟件上。 RESP 協(xié)議在Redis1.2被引入,直到Redis2.0才成為和Redis服務(wù)器通信的標(biāo)準(zhǔn)。這個(gè)協(xié)議需要在你的Redis客戶端實(shí)現(xiàn)。

RESP 是一個(gè)支持多種數(shù)據(jù)類型的序列化協(xié)議:簡(jiǎn)單字符串(Simple Strings),錯(cuò)誤( Errors),整型( Integers), 大容量字符串(Bulk Strings)和數(shù)組(Arrays)。

RESP在Redis中作為一個(gè)請(qǐng)求-響應(yīng)協(xié)議以如下方式使用:

客戶端以大容量字符串RESP數(shù)組的方式發(fā)送命令給服務(wù)器端。 服務(wù)器端根據(jù)命令的具體實(shí)現(xiàn)返回某一種RESP數(shù)據(jù)類型。 在 RESP 中,數(shù)據(jù)的類型依賴于首字節(jié):

單行字符串(Simple Strings): 響應(yīng)的首字節(jié)是 "+" 錯(cuò)誤(Errors): 響應(yīng)的首字節(jié)是 "-" 整型(Integers): 響應(yīng)的首字節(jié)是 ":" 多行字符串(Bulk Strings): 響應(yīng)的首字節(jié)是"$" 數(shù)組(Arrays): 響應(yīng)的首字節(jié)是 "*" 另外,RESP可以使用大容量字符串或者數(shù)組類型的特殊變量表示空值,下面會(huì)具體解釋。RESP協(xié)議的不同部分總是以 "\r\n" (CRLF) 結(jié)束。 字符串 "foobar" 編碼如下:

"$6\r\nfoobar\r\n"

實(shí)際redis命令是什么樣的,比如 SET lhjljh lhjkjhkh

*3\r\n$3\r\nSET\r\n$6\r\nlhjljh\r\n$8\r\nlhjkjhkh

3,編解碼

由于RESP天然是面向處理命令的,所以沒辦法直接把redis消息像grpc或者dubbo那樣直接序列化和反序列化消息。并且每個(gè)內(nèi)容限定了長(zhǎng)度,很適合做成及時(shí)序列化、零拷貝,直接針對(duì)輸入流做反序列化和序列化,這一點(diǎn)與Protostuff序列化協(xié)議的設(shè)計(jì)很類似。 所以序列化直接將服務(wù)端接收的流直接轉(zhuǎn)成值。

image.jpg

編解碼的實(shí)體類直接加入redis server 的處理某一個(gè)長(zhǎng)連接tcp客戶端的管道上。

image.jpg

4,命令處理

將消息解碼成RESP,還需要將RESP轉(zhuǎn)為Command對(duì)象,這里因?yàn)槭莏ava語言,方法與類綁定,編寫上和理解上會(huì)更加容易。但是會(huì)增加一些開銷。

image.jpg

二,redis 的數(shù)據(jù)結(jié)構(gòu)

1,底層主結(jié)構(gòu)

底層主樹使用跳表ConcurrentSkipListMap實(shí)現(xiàn),沒用hash類map的原因是服務(wù)端是集群后,客戶端可能使用hash路由,會(huì)導(dǎo)致服務(wù)端嚴(yán)重的hash沖突,性能大打折扣

image.jpg

key為封裝的“String”,重寫了equals方法避免相同的key但是在jvm中指針不同

image.jpg

value是一個(gè)接口,實(shí)現(xiàn)類是redis的五大基本類型,所有數(shù)據(jù)類型都包含超時(shí)時(shí)間

image.jpg

2,key

用封裝的值做value的原因是方便統(tǒng)一管理

image.jpg

3,list

底層使用LinkedList的原因是LinkedList實(shí)現(xiàn)了多種接口,實(shí)現(xiàn)各種命令直接調(diào)用其現(xiàn)成實(shí)現(xiàn)的方法即可

image.jpg

image.jpg

4,set

底層使用HashSet,redis里的set沒有多特殊

image.jpg

5,hash

底層使用HashMap,這里和開頭說的HashMap不沖突。為什么不用跳表?壓縮列表很巧妙,大抵的意思就是將通信收到的數(shù)組直接填充到list中,將list直接按照次序直接當(dāng)map使用,主要是0拷貝的思想,無需創(chuàng)建新資源,性能極高,但注意壓縮列表與壓縮無關(guān)。

image.jpg

6,zset

首先需要封裝一個(gè)帶有值和分值的對(duì)象

image.jpg

再用TreeMap重寫compare方法即可,使用TreeMap原因是他天然有良好的排序功能,很多hash一致路由的算法都用的TreeMap二開。

image.jpg

三,redis AOF 持久化

1,aof線程與tcp線程解耦,即寫緩沖

再解析redis命令時(shí),將redis寫命令添加到寫aof日志的隊(duì)列中

image.jpg

這里自己封裝了一個(gè)堵塞隊(duì)列,單線程吞吐量可以達(dá)到3000W /s是LinkedBlockingQueue的6到10倍,完全可以勝任此場(chǎng)景

image.jpg

image.jpg

RingBlockingQueue吞吐量非常高的原因是使用了內(nèi)存連續(xù)頁的機(jī)制。

image.jpg

2,aof持久化協(xié)議

aof協(xié)議一句話概括就是將寫命令,追加到日志中,開始時(shí)將命令讀取,當(dāng)作收到網(wǎng)絡(luò)的命令執(zhí)行即可。由于協(xié)議過于簡(jiǎn)單,這里就不貼鏈接了。 aof之日格式如下圖:

aof_img.jpg

3,aof的加載與存儲(chǔ)實(shí)現(xiàn)

這里讀寫內(nèi)存都是用的內(nèi)存文件映射,好處是讀寫性能好,壞處是可能會(huì)出現(xiàn)內(nèi)存泄漏,調(diào)試期間比較麻煩。

image.jpg

4,內(nèi)存文件映射與面向?qū)ο?/h3>

這里存儲(chǔ)和加載aof文件的代碼都是面向過程的,看起來非常復(fù)雜。實(shí)際上之前是按照面向?qū)ο髮懙?,封裝成了行對(duì)象,調(diào)用落盤符和拾起方法就可以寫入和讀取aof中的命令,但是TPS僅為10w/s,后來權(quán)衡后改為面向過程,吞吐量提升到了100W的TPS以上。

四,redis 的集群特性

1,主從

這里很容易聯(lián)想到mysql的只從,很多場(chǎng)景下會(huì)使用基于mysql主從的讀寫分離,或者zk的主從。 但實(shí)際上redis的主從是不保證一致性的,個(gè)人認(rèn)為redist的主從主要考慮的是cap的分布式容錯(cuò)性。 因?yàn)閞edis主從不保證一致性,所以使用redis讀寫分離,可能造成一些不一致的問題,寫寫是一致的,但是讀是不一致的,可以根據(jù)項(xiàng)目需要做取舍。

2,主從復(fù)制

redis的主從復(fù)制這里作者沒看懂(可能也是一致性上有坑沒動(dòng)力去看),所以沒寫出來。

3,分片集群

redis集群主要分為幾個(gè)唯獨(dú): 主從、分區(qū)集群、代理。 一般在redis客戶端的視角下,主要是分區(qū)集群,根據(jù)發(fā)送給redis的key做hash、md5等操作,取一個(gè)所有客戶端的共識(shí)值,將key和value發(fā)送,也就是客戶端路由分布式軟件的集群實(shí)現(xiàn)方式京東的redis集群設(shè)計(jì)到redis具體一個(gè)分片。

五,redis 的壓測(cè)與調(diào)優(yōu)

1,aof內(nèi)存泄漏

開啟aof壓測(cè)發(fā)現(xiàn)出現(xiàn)了內(nèi)存泄漏,后來發(fā)現(xiàn)是頻繁新建內(nèi)存池而造成的,所以將內(nèi)存池池化,即aof對(duì)象中僅存在一個(gè)bytebuff內(nèi)存池。

2,內(nèi)存復(fù)用提升性能

這里編解碼沒有單獨(dú)開辟byte數(shù)據(jù)接收bytebuff的數(shù)據(jù)進(jìn)行編解碼,編解碼直接讀取bytebuff進(jìn)行編解碼,沒有出現(xiàn)內(nèi)存拷貝,唯獨(dú)新建了BytesWrapper對(duì)象,但存儲(chǔ)的數(shù)據(jù)都是使用BytesWrapper對(duì)象,對(duì)內(nèi)存新建/銷毀的開銷很少。

3,0.05%消息延遲超200ms排查

下圖為c語言版的redis壓測(cè)數(shù)據(jù):

cppredis.jpg

下圖為java語言版的redis壓測(cè)數(shù)據(jù):

javaredis.jpg

4,性能表現(xiàn)

redis原版的性能大概是E5系列CPU 4-5w左右,上圖中是使用amd芯片測(cè)試的數(shù)據(jù)。 使用redis自帶的壓測(cè)工具,維持100個(gè)客戶端連接,java版性能是c語言原版性能的75-90%左右,性能依然強(qiáng)悍。

到此這篇關(guān)于Java手寫Redis服務(wù)端的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Java手寫Redis服務(wù)端內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 阿里四面之Spring Exception的原理解析

    阿里四面之Spring Exception的原理解析

    本文給大家介紹阿里四面之Spring Exception的原理解析,本文通過錯(cuò)誤場(chǎng)景分析給大家詳細(xì)介紹Spring異常處理流程,感興趣的朋友一起看看吧
    2021-10-10
  • java之如何定義USB接口

    java之如何定義USB接口

    這篇文章主要介紹了java之如何定義USB接口問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • 詳解log4j.properties的簡(jiǎn)單配置和使用

    詳解log4j.properties的簡(jiǎn)單配置和使用

    本篇文章主要介紹了詳解log4j.properties的簡(jiǎn)單配置和使用,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • 如何在SpringBoot項(xiàng)目中使用Oracle11g數(shù)據(jù)庫(kù)

    如何在SpringBoot項(xiàng)目中使用Oracle11g數(shù)據(jù)庫(kù)

    這篇文章主要介紹了在SpringBoot項(xiàng)目中使用Oracle11g數(shù)據(jù)庫(kù)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-06-06
  • java實(shí)現(xiàn)簡(jiǎn)單單鏈表

    java實(shí)現(xiàn)簡(jiǎn)單單鏈表

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)簡(jiǎn)單單鏈表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-02-02
  • Java 多線程實(shí)例詳解(三)

    Java 多線程實(shí)例詳解(三)

    本文主要介紹 java 線程安全的知識(shí),這里整理了相關(guān)資料及實(shí)現(xiàn)示例代碼,有興趣的小伙伴可以參考下
    2016-09-09
  • Tomcat安裝配置及Eclipse配置詳解

    Tomcat安裝配置及Eclipse配置詳解

    給大家介紹一下Tomcat安裝配置及Eclipse配置的全部圖文過程,如果你對(duì)這個(gè)還有不明白,一起跟著小編學(xué)習(xí)下。
    2017-11-11
  • 關(guān)于Jsoup將相對(duì)路徑轉(zhuǎn)為絕對(duì)路徑的方法

    關(guān)于Jsoup將相對(duì)路徑轉(zhuǎn)為絕對(duì)路徑的方法

    這篇文章主要介紹了關(guān)于Jsoup將相對(duì)路徑轉(zhuǎn)為絕對(duì)路徑的方法,jsoup 是一款Java 的HTML解析器,可直接解析某個(gè)URL地址、HTML文本內(nèi)容,需要的朋友可以參考下
    2023-04-04
  • Springboot內(nèi)置Tomcat配置參數(shù)調(diào)優(yōu)方式

    Springboot內(nèi)置Tomcat配置參數(shù)調(diào)優(yōu)方式

    這篇文章主要介紹了Springboot內(nèi)置Tomcat配置參數(shù)調(diào)優(yōu)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • Java實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng)

    Java實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09

最新評(píng)論