java后端redis点击上Java选择设为星标_第1页
java后端redis点击上Java选择设为星标_第2页
java后端redis点击上Java选择设为星标_第3页
java后端redis点击上Java选择设为星标_第4页
java后端redis点击上Java选择设为星标_第5页
已阅读5页,还剩299页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Redis+Tomcat+Nginx集群实现Session蕃薯耀Java后端2019-11-点击上⽅Java后端,选择设为星标作者蕃薯耀shuyao上篇35Java或者直接在附件中(版本为2.0.2,2017-11-27⽇前版本 :#-redishostsex::6379,:6379,:6380,#-redispassword(mons-logging-1.2.jar3.jedis-4.tomcat-cluster-redis-session-manager-压缩⽂件中有使⽤⽅法,⻅readMe.txtMovethedownloadedjarstotomcat/lib 22.Addtomcatsystemproperty就是配置⼀个环境变量,和Jdk配置的环境变量⼀样,需要配置⼀个catalina.base的环境变量,值为TOMCAT33.Extractdownloadedpackage(tomcat-cluster-redis-session-manager.zip)toconfigureRediscredentialsinredis-data-*tomcat/conf/redis-data-把⽬录下的配置⽂件h.oris到occ⽬录下第四步:4.Addthebelowtwolinesin<ManagerclassName="tomcat.request.session.redis.SessionManager"LicensedtotheApacheSoftwareFoundation(ASF)underoneormorethisworkforadditionalinformationregardingcopyrightownership.TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0(the"License");youmaynotusethisfileexceptincompliancewiththeLicense.YoumayobtainacopyoftheLicenseatUnlessrequiredbyapplicablelaworagreedtoinwriting,softwaredistributedundertheLicenseisdistributedonan"ASIS"BASIS,SeetheLicenseforthespecificlanguage erningpermissionsandlimitationsundertheLicense.--><!--Thecontentsofthisfilewillbeloadedforeachwebapplication--<!--Defaultsetofmonitoredresources.Ifoneofthesechanges,the--<!--webapplicationwillbereloaded.-- mentthistodisablesessionpersistenceacrossTomcatrestarts--<Managerpathname=""-- mentthistoenableCometconnectiontacking(provideseventsonsessionexpirationaswellaswebapplifecycle)--><Valve etConnectionManagerValve"-- 55.Verifythesessionexpirationtime(minutes)in 2 2 在http{……}⾥加上upstreamupstreamupstreammyTomcatCluster{tomcatCluster和 server:9400weight=1failtimeout=5smaxweight表⽰权重,权重越⼤, location/location/locationlocation/#indexindex.htmllocation/myTomcatCluster对应upstream 名进⼊D:\soft\nginx-1.12.2启动服务:(启动⼀闪⽽过,但打开进程管理器能看到是已经启动的关闭服 令:nginx-s重新加载令:nginx-sreload,修改配置⽂件后,可以使⽤该命令直接加载,不需要重启将已经配置好的⼀个oct ⼀份,修改端⼝,然后再修改⼀下o的配置⽂件(v.xl)我的⼀个oc在:<Engine<EnginedefaultHost="localhost"其中tomcat9300端⼝的<Engine<EnginedefaultHost="localhost"jvmRoute="jvm9300"tomcat9400端⼝的<Engine<EnginedefaultHost="localhost"jvmRoute="jvm9400"<%@pagelanguage="java"contentType="text/html;charset=UTF-<!DOCTYPEhtmlPUBLIC"-//W3C//DTDHTML4.01<metahttp-equiv="Content-Type"content="text/html;charset=UTF-//HttpSessionsession=out.println("<br>SESSIONID:"+out.out.println("<br>SESSIONID:"+然后把这个项⽬分别部署 端⼝的个tomcat中,分别启动,记得也启动Nginx和redis然后打开浏览器通过地址项⽬ ⽆论怎么刷新 (打开新的⻚也是(⾮新窗⼝))的都是900,也就是端⼝号为900的tct后缀.9400就是前⾯配置的:<Engine<EnginedefaultHost="localhost"jvmRoute="jvm9400" 使⽤i实现o共享的好处就是,把管理放在中,如果服务器重启或,i保存在中,下次重启后⼀样⽣效,避免i失效,同样最好也做集群,避免i重启或。--END 可进⼊⽆⼴ 理解InliJIDEA的项⽬配置和Web部Java4Git学Java, 万汨Java后端2019-11-点击上⽅Java⽽i另辟蹊径,结合其有序队列以及h编码,实现了空间搜索功能,且拥有极⾼的运⾏效率。本⽂将从源码⻆度对其算法原理进⾏解析,并推算查询时间复杂度。⾃Redis3.2开始,Redis基于geohash和有序集合提供了地理位置相关功能。RedisGeo模块包含了以下6GEOPOS:从key⾥⾯返回所有给定位置对象的位置(经度和纬度);GEODIST:返回两个给定位置之间的距离;GEOHASH:Geohash Redisgeo操作中只包含了“增”和“查”的操作,并没有专⻔的“删除”命令。主要是因为Redis内部使⽤有序集合(zset)保在i源码.的⽂件注释中,只说明了该⽂件为EA、E和EEM的实现⽂件(其实在也实现了另三个命令)。从侧⾯看出其他三个命令为辅助命令。 /*GEOADDkeylonglatname[long2lat2name2...longNlatNnameN] *c)/*Checkargumentsnumberforsanity.if((c->argc-2)%3!0)/*Needanoddnumberofargumentsifwegotthisfar...addReplyError(c,"syntaxerror.TryGEOADDkeyx1]y1]name1]""x2]y2]name2]...");}intelements(c->argc-2)/intargc2+elements*2;/*ZADDkeyscoreele...robj**argvzcalloc(argc*sizeof(robj*));argv0]crea argv1]c->argv1];/*key*/incrRefCount(argv1]);/*CreatetheargumentvectortocallZADDinordertoaddthescore,valuepairstotherequestedzset,wherescoreisanencodedversionoflat,long.for(i0;i<elements;i++)doublexy CERR)for(i0;i<argc; }GeoHashBitsrobj*valc->argv2+i*3+ Redisgeo转换过的位置理论上会有约0.3*1.414=0.424⽶的误差。范围单位:m|km|ft|mi|||和和分别新增了GEORADIUSRO和GEORADIUSBYMEMBERRO两个只读命令。不过,在实际开发中笔者发现在javapackageRedis.s.jedis.params.geo的GeoRadiusParam参数类中并不包"member1""member1",distance1,longitude1,latitude1]]"member2",distance2,longitude2,O[COUNTcount][STOREkey][STORedisTkey]GEORADIUSBYMEMBERstoRedist0;/*0forSTORE,1forSTORedisT.doublexy2]{0if(flags&RADIUSCOORDS)}doubleradiusmeters0,conversionif((radiusmetersextractDistanceOrReply(c,c->argv+baseargs-&conversion))<0)}//获取可选参数(withdist、withhash、withcoords、sort、intwithdist0,withhash0,withcoordsintsortSORTlonglongcountif(c->argc>baseargs)...}if(storekey&&(withdistwithhash{"STOREoptioninGEORADIUSisnotcompatiblewith""WITHDIST,WITHHASHandWITHCOORDSoptions");}if(count!0&& SORTNONE)sortSORTGeoHashRadiusgeoArray*gamembersOfAllNeighbors(zobj,georadius,xy0],xy1],radiusmeters,/*Ifnomatchingresults,theusergetsanemptyreply.if(ga->used0&&storekeygeoArray(ga);}的是geohashGetAreasByRadiusWGS84和membersOfAllNeighbors//geohashGeoHashRadiusgeohashGetAreasByRadiusWGS84(doublelongitude,doubledoubleradiusmeters)returngeohashGetAreasByRadius(longitude,latitude,radius}GeoHashRadiusgeohashGetAreasByRadius(doublelongitude,doublelatitude,double meters)//些参数设置GeoHashRangelongrange,latrange;GeoHashRadiusradius;GeoHashBitshash;GeoHashAreaarea;doubleminlon,maxlon,minlat,maxdoublebounds//计算⽬标区域外接矩形的范围(⽬标区域为:以⽬标为中⼼,半径为指定距离的圆geohashBoundingBox(longitude,latitude,radiusmeters,bounds);minlonbounds0];minlatbounds1];maxlonbounds2];maxlatbounds3];stepsgeohashEstimateStepsByRadius(radius//设置最⼤最⼩值:-180<longitude<18085<latitude<//将待查按指定精度(steps)编码成geohashgeohashEncode(&longrange,&latgeohashDecode(longrange,lat{GeoHashBitsneighborsunsignedinti,count0,lastprocessedintdebugmsgneighbors0]neighbors8]n.neighbors.southif(HASHISZERO(neighborsi]))}if(lastprocessedneighborsi].bits neighbors neighborslast{}count+membersOfGeoHashBox(zobj,neighborsi],ga,lon,lat,radius);lastprocessedi;}}intmembersOfGeoHashBox(robj*zobj,GeoHashBitshash,geoArray*ga,doublelon,doublelat,doubleradius)GeoHashFix52Bitsmin,max;returngeoGetPointsInRange(zobj,min,max,lon,lat,radius,}intgeoGetPointsInRange(robj*zobj,doublemin,doublemax,doublelon,doublelat,doubleradius,geoArray*ga)个的边界范围zrangespecrange{.minmin,.maxmax.minex0.maxex1sizetorigincountga->used;sds//搜索集合zobj可能有ZIPLIST和SKIPLIST两种编码⽅式,这⾥以SKIPLIST为例,逻辑是样的 OBJENCODINGZIPLIST)}elseif(zobj->encodingOBJENCODING{zset*zszobj->ptr;zskiplist*zslzs->zsl;zskiplistNode*ln;if((lnzslFirstInRange(zsl, NULL)/*Nothingexistsstartingatourmin.Noresults.return}sdseleln-/*Abortwhenthenodeisnolongerinrange.if(!zslValueLteMax(ln->score,eleCERR)sds lnln->level}}returnga->used-}doubledistance,xy if(!decodeGeohash(score,xy))returnCERR;/*Can'tdecode.if(!geohashGetDistanceIfInRadiusWGS84(lon,lat,xy0],xyradius,{returnC}gp->longitudexy0];gp->latitudexy1];gp->distgp->membermember;gp->scorescore;returnCOK;}换句话说,h⽹格等级越⾼,所覆盖的地理位置范围就越⼩。当我们根据输⼊半径和中⼼点位置计算出的能够覆盖⽬标区域的最⾼等级的九宫格(⽹格)时,就已经对九宫格外的元素进⾏了筛除。这⾥之所以使⽤九宫格,⽽不⽤单个⽹格,主要原因还是为了避免边界情况,尽可能缩⼩查询区域范围。试想以0为中⼼,就算查1⽶范围,单个⽹格覆盖的话也得查整个地球⾸先在每个o⽹格中的h下是有序集合的跳表数据结构:中EUS查找附近的⼈功能,时间复杂度为:+lM))。其中N为九宫格范围内的位置元素数量(要算距离);M是指定层级格⼦的数量,lM)是跳表结构中找到每个格⼦⾸元素的时间复杂度(这个过程⼀般会进⾏9次)。结合i本⾝基于内[1] [3] Redis持久化的⼏种⽅式-RDBJava后端2019-11-以下⽂章来源于Java 上⼀篇⼀千⾏MySQL内存中的数据到磁盘,以便Redis重启时能够从磁盘中恢复原有的数据,⽽整个过程就叫做Redis持久化。RedisRedisMemcachedMemcached快照⽅式(RDB,RedisDataBase)⽂件追加⽅式(AOFAppendOnlyFile),RDB(RedisDataBase)是将某⼀个时刻的内存快照(Snapshot),⼿动触发持久化的操作有两个:save和bgsave,它们主要区别体现在:是否阻塞Redissave在客⼾端中执⾏saveRedisRedisRDB持久化完成,才会响应其他客⼾端发来令,所以在⽣产环境⼀定要慎⽤。savesave命令之后,持久化⽂件化。save命令执⾏流程,如下图所⽰:

dump.rdb的修改时间就变了,这就表 成功的触发了RDB持bgsavebgsave(backgroundsave)既保存的意思,它

save命令最⼤的区别就是bgsave会forkfork(,Redis整个流程都阻塞的save命令来说,显然bgsave命令更适合我们使⽤。bgsave命令使⽤,如下图所⽰:①savemsave设置的触发条件,⾃动执⾏⼀次bgsave命令。注意:当设置多个savemn命令时,满⾜任意⼀个条件都会触发持久化。例如,我们设置了以下两个savemn命令:save60save60060s10Redis60sRedis10次,那么Redis600s内,Redis②flushall命令⽤于清空Redis数据库,在⽣产环境下⼀定慎⽤,当Redis

flushall命令之后,则会触发⾃动持久化,把在Redis主从 发Redis持久化。

RDB配置参数可以在RedisRDB#stop-wites-on-bgsave-eopessiondbchecksum.disavesave9001:表⽰900秒内如果⾄少有1个keysave3001030010keysave60100006010000个key pression③rdbchecksumRedis中可以使⽤命令查询当前配置参数。查询命令的格式为:configgetxxx,例如,想要获取RDB⽂件的名称设置,可以使⽤configgetdbfilename,执⾏效果如下图所⽰:RDB的⽂件⽬录,可使⽤命令configgetdir,执⾏效果如下图所⽰:Redis使⽤命令⾏设置,例如,使⽤configsetdir"/usr/data"就是⽤于修改RDB的⽬录注意:⼿动修改Redis配置⽂件的⽅式是全局⽣效的,即重启Redis服务器设置参数也不会丢失,⽽使⽤命令修改的⽅式,在RedisRedisRedisRedis⼩贴⼠:Redis的配置⽂件位于Redis安装⽬录的根路径下,默认名称为redis.conf⼩贴⼠:Redis的配置⽂件位于Redis安装⽬录的根路径下,默认名称为redis.confRedisRedisRDBdump.rdb,RedisRDB⽂件恢复持久化数据。如果根⽬录没有dump.rdb⽂件,请先将dump.rdb⽂件移动到Redis的根⽬录。src/redis-Redis在启动时有⽇志信息,会显⽰是否加载了RDB⽂件,我们执⾏Redis启动命令: ,如下图所RDB对恢复⾮常有⽤,它是⼀个紧凑的⽂件,可以更快的传输到服务器进⾏Redis服务恢复盘,Redis主进程并不会执⾏磁盘I/O等操作;因为RDB只能保存某个时间间隔的数据,如果中途Redis服务被意外终⽌了,则会丢失⼀段时间内的Redis数据;需要经常CPU性能不佳,则可能导致Redis停⽌为客⼾端服务⼏毫秒甚⾄⼀秒钟。禁⽤持久化可以提⾼Redis的执⾏效率,如果对数据丢失不敏感的情况下,可以在连接客⼾端的情况下,执⾏configsetsave""命Redis的持久化,如下图所⽰:通过本⽂我们可以得知,RDB持久化分为⼿动触发和⾃动触发两种⽅式,它的优点是⽂件⼩,Redis启动时恢复数据⽐较快,缺点是有丢失数据的⻛险。RDB⽂件的恢复也很简单,只需要把RDB⽂件放到Redis的根⽬录,在Redis启动时就会⾃动加载并【END 即可进⼊⽆⼴告 项⽬中常⽤到的19条MySQL零基础认识SpringGit SpringBoot+Redis+注解+器来实现接⼝幂等性校Java后端1⽉6点击上⽅Java作者189275403ed分布式锁--redis(jedis、redisson)或zookeeper实现本⽂采⽤第2redistoken为需要保证幂等性的每⼀次请求创建⼀个唯⼀标识tokentokentoken存⼊redistokenredis中删除此tokentoken@ApiIdempotent注解+器对请求进⾏压测⼯具:jmeter本⽂重点介绍幂等性核⼼实现,no如何集成ri、rn、no等细枝末节不在本⽂讨论范围之内,的 项⽬:@paramkey}}*publicStringset(Stringkey,Stringvalue,int{Jedisjedistryjedisreturnjedis.setex(key,expireTime, {log.error("setkey:{}value:{}expireTime:{}error",key,value,expireTime,e);returnnull;}{}}{Jedisjedistryjedis}catch(Exception{log.error("getkey:{}error",key,e);returnnull;}{}}publicLongdel(String{Jedisjedistryjedis}catch(Exception{log.error("delkey:{}error",key,e);returnnull;}{}}publicBooleanexists(String{Jedisjedistryjedis}catch(Exception{log.error("existskey:{}error",key,e);returnnull;}{}}publicLongexpire(Stringkey,int{Jedisjedistryjedisreturnjedis.expire(key.getBytes(),}catch(Exception{log.error("expirekey:{}error",key,e);returnnull;}{}}{Jedisjedistryjedis}catch(Exception{log.error("ttlkey:{}error",key,e);returnnull;}{}}privatevoidclose(Jedis{if(null!jedis){}}}⾃定义注解 publicclassApiIdempotentInterceptorimplementsHandlerInterceptorpublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objectreturn}HandlerMethodhandlerMethod(HandlerMethod)handler;MethodmethodhandlerMethod.getMethod();ApiIdempotentmethodAnnotationif(methodAnnotation!null)}return}{}publicvoidpostHandle(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,Objecto,}public pletion(HttpServletRequesthttpServletRequest,HttpServletResponsehttpServletResponse,Objecto,}}publicclassTokenServiceImplimplementsTokenServiceprivatestaticfinalStringTOKENNAMEpublicServerResponse{StringstrRandomUtil.UUID32();StrBuildertokennewtoken.append(Constant.Redis.TOKENPREFIX).append(str);}publicvoidcheckToken(HttpServletRequest{Stringtokenrequest.getHeader(TOKENif(StringUtils.isBlank(token)){//header中不存在tokenrequest.getParameter(TOKENif(StringUtils.isBlank(token)){//parameter中也不存在}}if(!jedisUtil.exists(token))}Longdelif(del<0)}}}publicstaticvoidmain(String]{SpringApplication.run(TestApplication.class,}finalCorsConfigurationcorsConfigurationnewCorsConfiguration();urlBasedCorsConfigurationSource.registerCorsConfiguration("/**",returnnew}//接⼝幂等性 }publicApiIdempotentInterceptor{returnnew}}查看4jmeter测试⼯具模拟50token5、header或参数均不传token,tokentokentoken值为上图中,不能单纯的直接删除token⽽不校验是否删除成功,会出现并发安全性问题,因为,有可能多个线程同时⾛到第46⾏,此时tokenjedisUtil.del(token实际上只有⼀次真正的删除操作,下⾯重现⼀下 springaop-ENDHashGit 神⽜003Java后端2⽉17 创建锁的策略:e的普通e⼀般都允许覆盖,A⽤⼾t某个e后,B在t相同的e时同样能成功,如果是锁场景,那就⽆法知道到底是哪个⽤⼾et成功的;这⾥ei的et⽅式为我们解决了这个问题,简单原理是:当A⽤⼾先et成功了,那⽤⼾e的时候就返回失败,满⾜了某个时间点只允许⼀个⽤⼾拿到锁。publicpublicbooleansetnx(Stringkey,String{}catch(Exceptionex)如下测试url,正常来说第⼀次返回了true,第⼆次返回了false,由于第⼆次请求的时候redis的key已存在,所以⽆法set功上⾯是创建锁,同样的具有有效时间,但是我们不能完全依赖这个有效时间,场景如:有效时间设置分钟,本⾝⽤⼾后,没遇到什么特殊情况正常⽣成了抢购订单后,此时其他⽤⼾应该能正常下单了才对,但是由于有个分钟后锁才能⾃动释放,那其他⽤⼾在这分钟⽆法正常下单(因为锁还是⽤⼾的),因此我们需要⽤⼾操作完后,主动去 :这⾥也使⽤了jedis⽅式,直接执⾏lua:根据val判断其是否存在,如果存在就当;只不过直接通过eval执⾏,这样避免多⼀次操作了redis⽽已,缩短了原⼦操作的间隔。(不同⻅解请留⾔探讨);同*privateStringqiang(Stringb)longstartTimewhile((startTime+timeout)>System.currentTimeMillis())if(nKuCuen<0)}if Key,b))"⽤⼾{}拿到锁...",tryif(nKuCuen<0)}try}catch(InterruptedException{}nKuCuen-("⽤⼾{}抢单成功跳出...所剩库存:{}",b,"⽤⼾{}释放锁...", Key,}}else//if(b.equals("神⽜-50")||b.equals("神⽜-69"))//}}return}(startTime+timeout)>=System.currentTimeMillis():判断未抢成功的⽤⼾,timeout秒内继续获取锁 获取锁后并下单成功,最后释放锁 Key,-END 「focusoncode 更新⼀篇⾼质量技术博⽂(⽆⼴告 SpringBoot整合Java后端2⽉22作者艾神不⼩⼼SpringBootRedis 点击redis-server.exe开启RedisRedisRedis⼆、整合到Spring1、在项⽬中加⼊Redis依赖,pom<!整合<!整合Reds缓存⽀持><groupId>org.spr redjedmaxactve:100maxdle:10maxwat:100000tmeout:50003、新建RedisConfiguration配置类,继承CachingConfigurerSupport,@EnableCaching开启注解。@Con@Configuraton@EnableCachngpublcclassRedsConfiguratonextendsCachngConfigurerSupport@OverrpublcKeyGenerator{returnnewKeyGenerator(){@OverrdepublcObjectgenerate(Objecto,Methodmethod,Object...objects)StrngBuldersb=newStrngBuppend(obj.toStrng());}System.out.prntln"调⽤Reds缓存Key:"+sb.toStrng());returnsb.toStrng();}}*@paramconnectpublcCacheManagercacheManager(RedsConnectonFactoryconnectonFactory)RedsCacheManagerredsCacheManage=RedsCacheManager.create(connectonFactory);returnredsCacheManager;}publcRed te<Strng,Strng>red te(RedsConnectonFactoryfactory)StrngRed te=newStrngRedsTemJackson2JsonRedsSeralzerjackson2JsonRedsSeralze=newJackson2JsonRedsSeralzer(Object.class);ObjectMapperom=newObjectMapper();jackson2JsonRedsSeralzer.setObjectMapper(om);temte.setValueSeralzer(jackson2JsonRedsSeralzer);temte.afterPropertesSet();returntem}}publcnterfaceRed*Hash结构添加元素*@paramkeykey*@paramhashKeyhashKey*@paramdoman*Hash结构获取指定key所有键值对*@paramkey*Map<HK,T>hashFndAll(Str*Hash结构获取单个元素*@paramkey*@paramhashKey*ThashGet(StrvodhashRemove(Str*Lst结构向尾部(Rght)添加元素*@paramkey*@paramdoman**Lst结构向头部(Left)添加元素*@paramkey*@paramdoman*LonglstUnshft(Strngkey,Tdoma*Lst结构获取所有元素*@paramkey*Lst<T>lstFndAll(Str*Lst结构移除并获取数组第⼀个元素*@paramkey*vodvaluePut(Strngkey,Tdoma*设置过期时间*@paramkey键*@paramtmeout时间*@paramtmeUntbooleanexprse(Strngkey,longtmeout,TmeUnttmeUnt)}5、下⾯是创建RedisHelperImpl//在构造器中获取redsTemte实例,key(nothashKey)默认使⽤Strng类型prvateRedsTem te<Strng,T>redsTem prvateHashOperatons<Strng,HK,T>hashOperatons;prva stOperatons<Strng,T>lstOperatons;prvateSetOperatons<Strng,T>setOperatons;prvateValueOperatons<Strng,T>valueOperat//IDEA虽然报错,但是依然可以注⼊成功,实例化操作对象后就可以直接调⽤⽅法操作Reds数据库@AutowredpublcRedsHelperImpl(Red te<Strng,T>red {ths.red te=red ths.hashOperatons=redsTem ths.lstOperatons=redsTem te.opsForLst();ths.zSetOperatons=redsTem ths.setOperatons=redsTem }@OverrpublcvodhashPut(Strngkey,HKhashKey,Tdoma}@OverrpublcMap<HK,T>hashFndAll(Str{returnhashOperatons.entr}@Overr{returnhashOperat}@OverrpublcvodhashRemove(Str{hashOperat}@OverrpublcLonglstPush(Strngkey,Tdoma}@OverrpublcLonglstUnshft(Strngkey,Tdoma{returnlstOperatons.leftPush(key,doma}@OverrpublcLst<T>lstFndAll(Str{ te.hasKey(key)){return}returnlstOperatons.range(key,0,lstOperatons.s}@OverrpublcTlstLPop(Strngkey){}@OverrpublcvodvaluePut(Strngkey,Tdoma}@OverrpublcTgetValue(Str}@Overrpublcvodremove(Str{red }@Overrpublcbooleanexprse(Strngkey,longtmeout,TmeUnttmeUn{returnred te.expre(key,tmeout,tmeUn}}编写TestRedis@RunW@RunWth(SprngRunner.class)@SprngBootTestpublcclassTestRedsprvateStrngRed testrngRed prvateRed tered prvateRedsHelperImplredpublcvodtest() cepton strngRedsTem Assert.assertEquals("111",strngRed System.out.prntln(strngRedsTem Authoruser=newAuthor();user.setName(user.setIntrol("不会打篮球的程序不是好男⼈");redsHelper.valuePut("aaa",user);}publcvodtestObj() cept{Authoruser=newAuthor();user.setName("Jerry");user.setIntrolValueOperatons<Strng,Author>operatons=redsTem operatons.set("502",user);Thread.sleep(booleanexsts=redsTem f(exsts){System.out.prntln(red te.opsForValue().get(}System.out.prntln("exsts}//Assert.assertEquals("aa",operat}}@EnableCach@EnableCachng//开启缓存@SprngBootApplcatonpublcclassPoetryApplcatonpublcstatcvodman(Strng[]args)SprngApplcaton.run(PoetyApplcat}}上⾯的Redis相关写法是我们⾃定义设置并获取的,那么我们经常要在接⼝的地⽅去使⽤Redis进⾏缓存相关实体publcclassAuthorController{prvatefinalstatcLoggerlogge=LoggerFactory.getLogger(AuthorController.class);@AutowredprvateAuthorRepostoryauthorRepos@Cacheable(value="poemInfo//⾃动根据⽅法⽣成缓存@PostMappng(value="/poemInfo")publcResult<Author>author(@RequestParam("authord")ntauthord,@RequestParam("authorname")Strngauthorname){f(StrngUtls.sEmpty(authord)StrngUtls.sEmpty(authorname)){returnResultUtls.error(ResultCode.INVALIDPARAM}Optonal<Author>optonal=authorRepostory.getAuthorByIdAndName(authord,authorname);f(optonal.sPresent()){author=optonal.l())){Strngs=author.getIntrol();Strngntro=s.splt("\\s+")[0];author.setIntrol(ntro);}}}returnResultUt}}这⾥-END 「focusoncode⽇更新⼀篇⾼质量技术博⽂(⽆⼴告 Redis Java后端2019-11-点击上⽅Java后端,选择设为星标作者来源 但这并不能减少业务逻辑对数据库的增删改操作的IO Memcache的代码层类似HashRedis特点如下:RedisRedis的效率很⾼,官⽅给出的数据是100000+QPS另外单线程也能处理⾼并发请求,还可以避免频繁上下⽂切换和锁的竞争,如果想要多核运⾏也可以启动多个实例。数据结构简单,对数据操作也简单,RedisMp,Mp最⼤的优点就是存取的时间复杂度为O(1)。Redis使⽤多路I/O复⽤模型,为⾮阻塞IO注:Redis采⽤的I/O多路复⽤函数:epoll/kqueue/evport/select阅读关于SpringBootJava、Spring全家桶等技术⽂章,欢 :Java后因地制宜,优先选择时间复杂度为O(1)的I/O多路复⽤函数作为底层实现。由于Select要遍历每⼀个IO,所以其时间复杂度为O(n),通常被作为保底⽅案。基于React设计模式I/O。最基本的数据类型,其值最⼤可512M,⼆进制安全(Redis的String可以包含任何⼆进制数据,包含jpg对象等注:如果重复写⼊keyString元素组成的字典,适⽤于对象String元素组成的⽆序集合,通过哈希表实现(增删改查时间复杂度为O(1))另外,当我们使⽤Smembers遍历Set中的元素时,其顺序也是不确定的,是通过Hash运算过后的结果。Sorted通过分数来为集合中的成员进⾏从⼩到⼤的排序。Redis⽤于计数的HyperLogLog、⽤于⽀持地理位置信息的Geo假设Redis中有⼗亿条Key,如何从这么多Key中找到固定前缀的⽅法1:使⽤Keys[pattern]:查找所有符合给定模式Pattern的使⽤Keys[pattern]指令可以找到所有符合Pattern条件的Key,但是Keys会⼀次性返回所有符合条件的Key,所以会造成Redis的卡顿。假设RedisKey,对内存的消耗在某些条件下也是keyskeystest*//返回所有以test为前缀的⽅法2:使⽤SCANcursor[MATCHpattern][COUNTMATCHpattern:查询KeyCountSCANSCAN以00此命令并不保证每次执⾏都返回某个给定数量的元素,甚⾄会返回0个元素,但只要游标不是0,程序都不会认为SCAN命令结束,但是返回的元素数量⼤概率符合Count参数。另外,SCAN⽀持模糊查询。SCANSCAN0MATCHtest*COUNT10//每次返回10条以test为前缀的分布式锁是控制分布式系统之间共同 共享资源的⼀种锁的实现。如果⼀个系统,或者不同系统的不同主机之间共享某分任意时刻只有⼀个客⼾端获取到锁,不能有两个客⼾端同时获取到锁。锁只能被持有该锁的客⼾端删除,不能由其他客⼾端删除。死锁:获取锁的客⼾端因为某些原因⽽宕机继⽽⽆法释放锁,其他客⼾端再也⽆法获取锁⽽导致死锁,此时需要有特殊机容错:当各个节点,如某个RedisSETNX实现,SETNXkeyvalueKey该命令时间复杂度为O(1),如果设置成功,则返回1,否则返回0。区之前先使⽤SETNX指令,查看是否设置成功。如果设置成功则说明前⽅没有客⼾端正在该资源,如果设置失败则说明有客⼾端正在该资源,那么当前客⼾端就需由于SETNX并不⽀持传⼊EXPIRE参数,所以我们可以直接使⽤EXPIRE指令来对特定的Key来设置过期时间。RedisServiceRedisServiceredisServiceSpringUtils.getBean(RedisService.class);longstatusredisService.setnx(key,"1");}解决:从Redis2.6.12版本开始,我们就可以使⽤Set操作,将SETNX和EXPIREEXsecond:设置键的过期时间为Second秒。PXmillisecond:设置键的过期时间为MilliSecond毫秒。SETSETKEYvalueEXseconds]PXmilliseconds]NX注:SET操作成功完成时才会返回OK,否则返回nil有了SET我们就可以在程序中使⽤类似下⾯的代码实现分布式锁了:RedisServiceRedisServiceredisServiceStringresultredisService.set(lockKey,requestId,SETIFNOTEXIST,SETWITHEXPIRETIME,expireTime);}①使⽤Redis中的List使⽤上⽂所说的Redis的数据结构中的List作为队列Rpush⽣产消息,LPOP消费消息。此时我们可以看到,该队列是使⽤Rpush⽣产队列,使⽤LPOP消费队列。缺点:LPOP弥补:可以通过在应⽤层引⼊Sleep机制去调⽤LPOP重试。②使⽤BLPOPkeykey…]BLPOPkeykey…]timeout③Pub/Sub:订阅者模KafkaRedis由于Redis将数据 在内存⽽不是磁盘中,所以内存⼀旦断电,Redis中 所以Redis有持久化机制来保证数据的安全性。Redis⽬前有两种持久化⽅式,即RDB和AOF,RDB是通过保存某个时间点的全量数据快照实现数据的持久化,当恢复数据时,直接通过RDB⽂件中的快照,将数据恢复。RDB持久化会在某个特定的间隔保存那个时间点的全量数据的快照。RDB配置⽂件,redis.conf:savesave9001#在900s内如果有1save30010#在300s内如果有10save6010000#在60s内如果有10000stop-writes-on-bgsave-erroryes#stop-writes-on-bgsave-error:①RDB的创建与载⼊SAVE:阻塞Redis的服务器进程,直到RDB⽂件被创建完毕。SAVE⼊,由于RedisBGSAVE:该指令会Fork出⼀个⼦进程来创建RDBRDB续接收客⼾端的请求。我们也可以通过使⽤lastsave指令来查看BGSAVE是否执⾏成功,lastsave可以返回最后⼀次执⾏成功BGSAVE的时②⾃动化触发RDB持久化的⽅式⾃动化触发RDB根据redis.conf配置⾥的SAVEmn定时触发(实际上使⽤的是BGSAVE)。 执⾏DebugReload执⾏Shutdown且没有开启AOF持久化。③BGSAVE的原理检查是否存在⼦进程正在执⾏AOF或者RDB的持久化任务。如果有则返回false调⽤Redis源码中的rdbSaveBackground⽅法,⽅法中执⾏fork()产⽣⼦进程执⾏RDB操作。关于fork()中的Copy-On-Write。fork()在Linux中创建⼦进程采⽤Copy-On-Write(写时拷⻉技术),即如果有多个调⽤者同时要求相同资源(如内存或磁 ④RDB持久化⽅式的缺点RDB持久化⽅式的缺点如下:I/O⽽严重影响性能。可能会因为Redis宕机⽽丢失从当前⾄最近⼀次快照期间的数据。AOF持久化是通过保存Redis的写状态来记录数据库的。相对RDB来说,RDB持久化是通过备份数据库的状态来记录数据库,⽽AOF持久化是备份数据库接收到的指令:AOF记录除了查询以外的所有变更数据库状态的指令。以增量的形式追加保存到AOF⽂件中。①打开redis.conf配置⽂件,将appendonly属性改为yes②修改appendfsync属性,该属性可以接收三种参数,分别是always,everysec,noalways表⽰总是即时将缓冲区内容写⼊AOF⽂件当中,everysec表⽰每隔⼀秒将缓冲区内容写⼊AOF⽂件,no表⽰将写⼊⽂件操作交由操作系统决定。appendonlyappendonly#appendsyncalways#appendfsyncno⽇志重写解决AOF随着写操作的不断增加,AOF⽂件会越来越⼤。假设递增⼀个计数器100RDB持久化⽅式,我们只要保存最终结果100即可。命令实际可以精简为⼀条。RedisAOFCOW(写时拷⻉。重调⽤fork⼦进程把新的AOF写到⼀个临时⽂件⾥,不依赖原来的AOF⽂件。主进程持续将新的变动同时写到内存和原来的AOF⾥。主进程获取⼦进程重写AOF的完成信号,往新AOF同步增量变动。使⽤新的AOF⽂件替换掉旧的AOF⽂件。AOF和RDB的优缺点如下:RDB全量数据快照,⽂件⼩,恢复快。RDB⽆法保存最近⼀次快照之后的数据。优点:Redis4.0之后推出了此种持久化⽅式,RDB作为全量备份,AOF的数据,AOF则将Redis指令存⼊⽂件中,这样⼜会造成⽂件体积⼤,恢复时间⻓等弱点。在RDB-AOF⽅式下,持久化策略⾸先将缓存中数据以RDB⽅式全量写⼊⽂件,再将写⼊后新增的数据以AOF的⽅式追加在RDB数据的后⾯,在下⼀次做RDB持久化的时候将AOF的数据重新以RDB的形式写⼊⽂件。在此种策略的持久化过程中,⼦进程会通过管道从⽗进程增量数据,在以RDB格式保存全量数据时,也会通过管道数RDBAOF荐的⼀种持久化⽅式。RDB和AOF从图可知,Redis启动时会先检查AOF是否存在,如果AOF存在则直接加载AOF,如果不存在AOF,则直接加载RDB⽂Pipeline和Linux的管道类似,它可以让Redis批量执⾏指令。毕后才能继续执⾏,这中间不仅仅多了RTT,还多次使⽤了系统IO。Pipeline由于可以批量执⾏指令,所以可以节省多次IORedis⼀般是使⽤⼀个Master节点来进⾏写操作,⽽若⼲个Slave节点进⾏读操作,Master和Slave分别代表了⼀个个不同的RedisServer实例。另外定期的数据备份操作也是单独选择⼀个Slave去完成,这样可以最⼤程度发挥Redis的性能,为的是保证数据的弱⼀致另外,Master和Slave的数据不是⼀定要即时同步的,但是在⼀段时间后Master和Slave的数据是趋于同步的,这就是最Slave发送Sync命令到MasterMaster启动⼀ 进程,将Redis中的数据快照保存到⽂件中Master将保存数据快照期间接收到的写命令缓存起来。Master完成写⽂件操作后,将该⽂件发送给Slave。使⽤新的AOF⽂件替换掉旧的AOF⽂件。Master将这期间收集的增量写命令发送给Slave端。MasterMaster接收到⽤⼾的操作指令,判断是否需 到Slave将操作记录追加到AOF⽂件。将操作到其他Slave:对⻬主从库;往响应缓存写⼊指令。将缓存中的数据发送给Slave。RedisSentinel(哨兵主从模式弊端:当Master宕机后,Redis集群将不能对外提供写⼊操作。RedisSentinel可解决这⼀问题。解决主从同步Master宕机后的主从切换问题::提醒:通过API⾃动故障迁移:主从切换(在Master宕机后,将其中⼀个Slave转为Master,其他的Slave从该节点同步数据Redis按照某种规则去划分数据,分散在多个节点上。通过将数据分到多个Redis服务器上,来减轻单个Redis服务器的②⼀致性Hash算法既然要将数据进⾏分⽚,那么通常的做法就是获取节点的Hash但这样的⽅法有明显的弊端,当Redis节点数需要动态增加或减少的时候,会造成⼤量的Key⽆法被命中。所以Redis中引⼊了⼀致性Hash算法。该算法对2^32取模,将Hash值空间组成虚拟的圆环,整个圆环按顺时针⽅向组织,每个节点依次为012…2^32-1之后将每个服务器进⾏HashHashHash算法,将数据定位到特定的Redis服务器上。如果定位到的地⽅没有Redis③Hash环的数据倾斜问题⼤部分集中在Redis集群的其中⼀台或⼏台服务器上。Hash算法运算后的数据⼤部分被存放在AB节点只存放了少量的数据,久⽽久之A节点将被撑服务器节点,称为虚拟节点,可以在服务器IP或者主机名后放置⼀个编号实现。例如上图:将NodeA和NodeB两个节点分为NodeA#1-A#3,NodeB#1-B#3【END 可进⼊⽆⼴告 我们再来聊⼀聊Java hub⼯程Git ⼀⽂梳理Redis张君鸿Java后端2019-09-点击上⽅蓝⾊字体,选择“标星”优质⽂章,第时间送达作者张君鸿RedisRedis⽀持多种不同的数据结构,包括5ListSortSet:有序集合Bitmaps:位图,在string的基础上进⾏位操作,可以实现节省空间的数据结构。Hyperloglog:⽤于估计⼀个set中元素数量的概率性的数据结构。BloomFilter不同数据结构的相同之处从上⾯的介绍,我们看到⽀持的数据结构的不同,但其实,Redis的每⼀种数据结构都由⼀个key和value⽽所有数据结构的key的值都是任意合法的字符串,不同的数据结构的区别就在于value的值的不同⽽不同⽐如,最简简单的String数据结构,其value为String,所以String⽽Hash数据结构,其value为⼀个哈希列表,所以Hash这⾥就列出String和Hash来讲解说明,关 RedisRedis官⽹将Redis令按照功能划分为15个 分组,其中,Kyes令对所有的数据结构都通⽤,因此,有必要在了解其他数据结构命令前好好学习⼀下。keys命令的作⽤是列出Redis所有的ke,该命令的时间复杂度为O(N)N随着Redis中key的数量增加⽽增加,因此Redis有⼤量的keykeys命令会执⾏很⻓时间,⽽由于Rediskeys命令。"""otest1oneootest2oaobkeysheelo? expire设置expire设置key在多少秒之后过期,pexpire设置key在多少毫秒之后过期,成功返回1,失败返回0expirekeypexpirekeyexists命令⽤于判断⼀个或多个key是否存在,判断多个key时,key之间⽤空格分隔,exists的返回值为整数,表⽰当前判断有多少个key是存在的。existskey[keysetsettest1t1existstest1test2existstest3test3del命令⽤于删除⼀个或多个key,多个key之间⽤空格分隔,其返回值为整数,表⽰成功删除了多少个存在的key,因此,如果只删除⼀个key,则可以从返回值中判断是否成功,如果删除多个key,则只能得到删除成功的数量。delkey[key...]tdeltestmsettest12test21deltest1test2test3deltest1test2msetmsettesttestvaluetest1test1expiretest10#设置10期expiretttttl和pttl命令⽤于获取key的过期时间,其返回值为整型,代表的意义分为⼏种情况:当key不存在或过期时间,返回-2。当key存在且永久有效时,返回-1当key有设置过期时间时,返回为剩下的秒数(pttlttlpttl⽰例(ttl的演⽰,pttlsettesttestsettesttest0ttlsettest1#永久有效ttltest1ttl设置key在某个时间戳过期,expreat参数时间戳⽤秒表⽰,⽽pexpireat则⽤毫秒表⽰,与expire和pexpire回10表⽰失败。setsettesttestsettest1移除key的过期时间,将key设置为永久有效,当key设置了过期时间,使⽤persist命令移除后返回1,如果key⾝就是永久有效的,则返回0persistkeysetsettesttestttltest效0(integer)1判断key是什么类型的数据结构,返回值为string,list,set,hash,zset,分别表⽰我们前⾯介绍的Redis的5种基础数据结geo,hyperloglog,bitmaps等复杂的数据结构,都是在这五种基础数据结构上实现,⽐如geo是zset类型,hyperloglog和bitmaps都为string。typetypesetsettesttesttypetesthsethtesttesttesttypehtest 样除了负载均衡,Nginx快来薅当当的⽺⽑!聊聊Java数据库不使⽤外键的9个理由 为什么RedisJava后端2019-11-点击上⽅Java后端,选择设为星标作者draveness.me/redis-io-multiplexing最近在看UNIXRedisRedisI/O多路复⽤BlockingI/Oreadwrite(FileDescriptorFDRedis的就是I/O多路复⽤模型了: 关于select的具体使⽤⽅法,在⽹络上资料很多,这⾥就不过多展开介绍了;与此同时也有其它的I/O多路复⽤函数epoll/kqueue/evport,它们相⽐select性能更优秀,同时也能⽀ ReactorRedis服务采⽤Reactor的⽅式来实现⽂ 处理器(每⼀个⽹络连接其实都对应⼀个⽂件描述符 处理器使⽤I/O多路复⽤模块同时 多个FD,当accept、read、write和close⽂件 会回调FD绑定的 处理器是在单线程上运⾏的,但是通过I/O多路复⽤模块的引⼊,实现了同时对多个FD读写的 络通信模型的性能,同时也可以保证整个Redis服务实现的简单。I/Oselect、epoll、avportkqueueI/OstaticintaeApiCreate(aeEventLoopstaticintaeApiResize(aeEventLoop*eventLoop,intsetsize)staticvoidaeApi (aeEventLoop*eventLoop)staticintaeApiAddEvent(aeEventLoop*eventLoop,intfd,intmask)staticvoidaeApiDelEvent(aeEventLoop*eventLoop,intfd,intmask)staticintaeApiPoll(aeEventLoop*eventLoop,structtimeval*tvp)同时,因为各个函数所需要的参数不同,我们在每⼀个⼦模块内部通过⼀个aeApiState来需要的上下⽂信息{typedefstructaeApiState这些上下⽂信息 在eventLoop的void*state中,不 封装selectselect可以FD的可读、可写以及出现错误的情况I/Oselectselect/*/*fileesreadable初始化⼀个可读的fdset集合,保存需要可读性的FD;使⽤FDSET将fd加⼊rfds;调⽤select⽅法rfds中的FD是否可读selectFDRedisaeselectaeApiCreaterfdsstaticstaticintaeApiCreate(aeEventLoop{aeApiState*state=aeApiAddEventaeApiDelEventFDSETFDCLRfdsetFD整个aeselect⼦模块中最重要的函数就是aeApiPoll,它是实际调⽤select函数的部分,其作⽤就是在I/O多路复⽤函数返回时,将对应的FD加⼊aeEventLoop的fired数组中,并返回 staticstaticintaeApiPoll(aeEventLoop*eventLoop,structtimeval{aeApiState intretval,j,numevents=0;memcpy(&state->rfds,&state->rfds,sizeof(fdfor(j=0;j<=eventLoop->maxfd;{int mask=AEREADABLE;mask=AEWRITABLE;eventLoop->fired[numevents].fd=j;epollRedisepollepollcreateepollif(!state)return-{state->epfd=epollcreate(1024);/*1024isjustahintforthekernel{ ); return-returnstaticstaticintaeApiAddEvent(aeEventLoop*eventLoop,intfd,int{aeApiState*state=eventLoop-structepolleventee={0};/*avoidvalgrindwarning/*Ifthefdwasalreadymonitoredforsomeevent,weneeda*operation.OtherwiseweneedanADDoperation.if(mask&AEWRITABLE)ee.events=EPOLLOUT;ee.data.fd=fd;returnepollselectepollwaitFDepollwait回时会提供⼀个epollevent数组:typedeftypedefunionepolldata fd/**/uint64tu64;其

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论