redis场景分析+spring cloud概述

转摘 https://blog.csdn.net/g6U8W7p06dCO99fQ3/rss/list

 

Redis开发设计规范及案例分析

    1、合理使用集合类

    案例:某活动需求,每天10点对昨天参加活动的用户进行推送提醒。开发人员使用redis存储每
    天参加活动的用户,通过zrangebyscore命令获取目标用户进行提醒,提醒完后使用zremrangeby
    score命令从redis中清除这批用户。某一天,发现这二个命令均出现了慢日志报警,排查发现这
    一天参加活动的用户约有5万。

    分析: 案例中使用了redis的sortedset存储用户信息,其中value是用户的账号、score是用户
    参加活动的时间,由于zrangebyscore和zremrangebyscore命令的时间复杂度是O(long(N)+M),
    其中M是操作的元素个数,N是集合元素总数,本例中当用户数量为5万时,出现慢日志。可以通
    国缩小每次查询的集合数量,可以将一天分成多段,分批查询,比如查24小时范围内的用户改成
    查4小时范围的用户,分别查6次处理即可。
    如果用户参加活动的时间很几种,在某一个时间段(比如晚上18点到22点)查出来的数量还是很多
    可以把粒度分的更细一些比如1小时或者30分钟,如果确定用户参加活动集中在某个时间点,
    可以考虑使用zscan遍历操作并删除。另外,对于目标时间范围有确定的首位元素时,还可以通
    过zrank命令查出元素的位置,通过zrange以及zremrangebyrank来进行查询和删除操作。
    
    小结:使用sortedset、set、list、hash等集合类的O(N)操作时要评估当前元素个数的规模以及
    将来的增长规模,对于短期可能变为大集合的key,要预估O(N)操作的元素数量,避免全量操作
    可以使用hscan、sscan、zscan进行渐进操作。集合元素数量过大在使用过程中会影响redis的实
    际性能,元素个数建议不要超过5000,元素数量过大可考虑拆分成多个key进行处理

    2、合理设置过期时间

    案例: 投票功能,用于统计今日环比昨日的增长数量,开发人员使用redis存储每天的投票
    数量,key设计为vote_count_{date},其中{date}为当天的日期,由于没有设置过期时间,一年
    以后产生了360多个key,实际在用的key始终只有2个。

    分析:该案例中,每个生产的key在2天以后都不会再使用了,可将key加上过期时间

    案例2: 统计功能,用户会不定期导入一批数据进redis,每一批数据需要在30分钟后、一天后
    3天后、7天后进行统计,并将结果发送给用户。开发人员使用同一个sortedset存储这些导入的
    数据,每天定时任务执行计算任务。由于没有清理、导致大量结束计算任务的废弃数据残留。
    
    分析: 该案例中,每一批数据都有相应的生命周期,在导入的第7天执行完最后一次计算任务
    生命周期结束,由于集合里的元素不能单独设置过期时间,可在代码逻辑中对最后一次使用
    这批数据后进行清理操作

    小结:如果key没有设置超时时间,会一直占用内存。对于可以预估使用生命周期的key应当设置
    合理的过期时间或在最后一次操作时进行清理,避免垃圾数据残留

    3、合理使用批操作命令

    案例:某运营需求,给用户生成短链,短链由断链前缀+短码组成,根据短码找到用户对应的
    手机号,开发人员使用redis hash结构存储短码手机号的映射。接口每次会导入5万个手机号

    分析: 下面是开发人员的三种操作redis方案的伪代码

    方案1: 直接使用redis的hset逐个设置
    for(50000;) hset(key, 短码, 手机号) 
    结果:失败,redis ops飙升,同时接口响应超时

    方案2:改用redis的hmset一次将所有元素设置到hash中
    map<短码, 手机号> 50000个元素 hmset(key, map)
    结果: 失败, 出现redis慢日志

    方案3: 使用hmset,每次设置500个,循环100次
    map<短码,手机号> 500个元素  for(100;)  hmset(key, map)
    结果: 成功

    对于大量频繁的hset操作可以使用hmset替代减少redis操作次数同时提升处理速度。但要考虑
    单次请求操作的数量,避免慢日志

    小结: 在使用redis使用过程中,要正视网络往返时间,合理利用批量操作命令,减少通信
    时延和redis访问频次。 mset/hmset等都支持一次输入多个key,lpush、rpush、sadd等命令
    都支持一次输入多个value,也要注意每次操作数量不要过多,建议控制在500个以内
    pipelining模式可以一次输入多个指令,redis提供了一个pipeline的管道操作模式,将多个
    指令汇总到队列中批量执行,减少tcp交互产生的时间,一般情况下有10%-30%不等的性能提升
    更快的是lua script模式,还可以包含逻辑,redis内嵌了lua解析器,可以执行lua脚本,脚本
    可以通过eval等命令直接执行。

    4、减少不比亚的请求

    案例: 某业务系统,当用户进入某个页面时会同时请求多个接口,每个接口都会校验用户状态
    是否有效,用户状态存在redis里并设置有过期时间,对于key未过期但过期时间大于指定阈值
    需要重新设置有效时间,否则需要使用del命令删除掉。但是部分key由于过期其实已经不存在
    所以出现部分无效del命令。用户越多,就会有越多的无效命令

    分析: ttl命令对于key不存在的情况会返回-2,若key不存在则不勇调用del命令,减少无用请
    求

    小结: redis的所有请求对于不存在的key都会有输出返回,合理利用返回值处理,避免不必要
    的请求,提升业务吞吐量

    5、避免value设置过大

    案例:某开发人员将一个商品集合信息序列化后用redis的字符串类型存储,使用的时候在反
    系列化成对象列表使用,大小超过1M,在网络传输的时候由于数据比较大会触发拆包,会降低
    redis的吞吐量

    分析: 数量比较多的时候可以考虑改用hash结构存储,每一个field是商品id,value是该商品
    对象,如果数量较大可使用hscan获取

    小结: string类型尽量控制在1kb以内。虽然redis对单个key可以缓存的对象长度能够支持的
    很大,但实际使用场合一定要合理拆分过大的缓存项,1K基本是redis性能的一个拐点。当缓存项超过10K、100K、1M性能下降会特别明显

    6、设计规范的key明

    A: 可读性 以业务明为前缀,用冒号分割,可使用业务名:子业务名:id的结构命令,子业务
    下多单词可再用下划线分割。例如:活动系统-人拉人红包活动-idactivity:invite_redpacket
    :001

    B: 简洁性 保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视

    C: 不包含转义字符  不包含空格、换行、单双引号以及其他转义字符 

    7、留心禁用命令

    keys monitor  flushhall  flushdb应当通过redis的rename机制禁掉命令,若没有禁用,开发
    人员谨慎使用。其中flushall  flushdb会清空redis数据; keys命令可能会引起慢日志;
    monitor命令在开启的情况下会降低redis吞吐量


JVM内存结构、内存模型、对象模型

    1、JVM内存结构

    java代码运行在虚拟机上的,而虚拟机在执行java程序的过程中会把所管理的内存划分为若干
    个不同的数据区域,这些区域都有各自的用途。 其中有些区域随着虚拟机进程的启动而存在
    而有些区域则依赖用户线程启动和结束而建立和销毁
    https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_png/6fuT3emWI5IUn7IK1IHXbPncn0qUV
    qFDPOXDhD9dqSdaMa09ibl7QBFXBZgQ0C7vvb0UUAO3zqszZlNVcPBrMgg/640?

    2、内存模型

    JMM是和多线程相关的,描述了一组规则或规范,这个规范定义了一个线程对共享变量的写入
    时对另一个线程是可见的。 java多线程之间是通过共享内存进行通信的,而由于采用共享内存
    进行通信,在通信过程中会存在一系列如可见性、原子性、顺序性等问题,而JMM围绕多线程
    通信以及相关的一系列特性而建立的模型。JMM定义了一些语法集,这些语法映射到Java语言中
    就是volatile、synchronized等关键字。 在JMM中,把多个线程间通信的共享内存称为主内存
    而在并发编程中多个线程都维护一个自己的本地内存,其中保存的数据是主内存中的数据拷贝
    而JMM主要是空值本地内存和主内存之间的数据交互的。

    原子性: 在java中,为了保证原子性,提供了二个高级的字节码指令monitorenter和
    monitorexit。 这二个字节码,在java中对应的关键字就是synchronized

    可见性: java内存模型是通过在变量修改后将新值同步主内存,在变量读取前从主内存刷新变
    量值得这种依赖主内存作为传递媒介的方式实现的。 java中的volatile提供了一个功能,被
    修饰的变量在被修改后可以立即同步到主内存,被其修饰的变量在每次使用之前都从主内存刷
    新。 synchronized final二个关键字也可以实现可见性

    有序性: volatile关键字会禁止指令重排。synchronized关键字保证同一时刻只运行一条线程
    操作

    3、对象模型

    https://ss.csdn.net/p?https://mmbiz.qpic.cn/mmbiz_jpg/6fuT3emWI5IUn7IK1IHXbPncn0qUVqFDqcEAdRUkia8yax6Ijr8Q8f0omsf7Eq0HcibyUQXViaDydhXiaIicvbTfJ4g/640?

    JVM内存结构和java虚拟机运行时区域有关,JMM和并发编程有关,对象模型和java对象在虚
    拟机中表现形式有关

Spring Cloud Netflix主要组件介绍


1、服务注册与发现Eureka


Eureka由多个instance(服务实例)组成,这些服务分为两种: Eureka Server Eureka Client。
我们将Eureka client分为service provider和service consumer。 Eureka Server Server服务
的注册中心,负责维护注册的服务列表。 Service Provider: 服务提供方, 作为Eureka
Client,向Eureka Server 做服务注册、续约和下线等操作,注册的主要数据包括服务名、机器
ip、端口号、域名等。 Service Consumer: 服务消费方,作为一个Eureka Client,向Eureka
Server获取Service Provider的注册信息,并通过远程调用与Service Provider进行通信。
Service Consumer也可以随时向Eureka Server注册,来让自己变成一个Service Provider.
spring cloud针对服务注册与发现,进行了一层抽象,并提供了三种实现: Eureka Consul
Zookeeper。目前支持最好的就是Eureka,其次是Consul,最后Zookeeper


2、Eureka Server


作为一个独立的部署单元,以rest api的形式为服务实例提供了注册、管理和查询等操作。同时
Eureka Server也提供了可视化的监控页面。


2.1、Eureka Server的高可用集群


Eureka Server可用运行多个实例来构建集群,解决单点问题,但不同zookeeper的选举leader的
过程,Eureka Server采用的是Peer to Peer对等通信。这是一种去中心化的架构,无master/
slave区分,每一个peer都是对等的。在这种架构中,节点通过彼此互相注册来提高可用性,每
个节点需要添加一个或多个有效的ServiceURL指向其他节点。 每个节点都可被视为其他及节点
的副本。 如果某个Eureka Server宕机,Eureka Client的请求会自动切换到新的Eureka
Server节点,当宕机的服务器重新恢复后,Eureka会在此将其纳入到服务器集群管理之中。当
节点开始接受客户端请求时,所有的操作都会进行replicateToPeer(节点间复制)操作,将请求
复制到其他Eureka Server当前所知的所有节点中。 一个新的Eureka Server节点启动后,会
首先尝试从邻近节点获取所有实例注册表信息,完成初始化。 Eureka Server通过getEurekaSer
viceUrls()方法获取所有的节点,并且会通过心跳续约的方式定期更新。默认配置下,如果
Eureka Server在一定时间内没有接收到某个服务实例的心跳, Eureka Server将会注销该实例
默认是90秒。 当Eureka Server节点在短时间内丢失过多的心跳时,那么这个节点就会进入自我
保护模式。


3、Service Provider


3.1 服务注册


Service Provider本质上是一个Eureka Client。 它启动时,会调用服务注册方法,向Eureka
Server注册自己的信息。 Eureka Server会维护一个已注册服务的列表,这个列表为一个嵌套
的hashmap。 第一层, application name和对应的服务实例 第二层,服务实例及其对应的
注册信息,包括ip、端口等。 当实例状态发生变化时,也会想Eureka Server更新自己的服务
状态,同时用replicateToPeers()向其他Eureka Server节点做状态同步


3.2 续约与剔除


服务实例启动后,会周期性向Eureka Server发送心跳以续约自己的信息,避免自己的注册信息
被剔除。续约的方式与服务注册基本一致: 首先更新自身状态,再同步到其他Peer。 如果
Eureka Server在一段时间内没有收到某个微服务节点的心跳,Eureka server将会注销该微服务
节点


4、Service Consumer


Service Consumer本质上也是一个Eureka Client(也会想Eureka Server注册,只是这个注册
信息无关紧要而已)。它启动后,会从Eureka Server上获取所有实例的注册信息,包括ip地址
端口等,并缓存到本地。这些信息默认每30秒更新一次。 如果与Eureka Server通信中断,
Server Consumer可以通过本地缓存与Service Provider通信。 基于Service Consumer获取到
的服务实例信息,就可以进行服务调用了。spring cloud为service consumer提供了丰富的服务
调用工具: ribbon 实现客户端的负载均衡 hystrix 断路器 feign restful service客户端
整合ribbon和hystrix


 

posted @ 2018-07-11 15:27  秋水秋色  阅读(130)  评论(0)    收藏  举报