- 博客(177)
- 收藏
- 关注
原创 RabbitMQ—HAProxy负载均衡
注意:由于增加了负载均衡器,如果Haproxy所在节点也发生宕机,对于客户端来讲就无法与RabbitMQ交互,因此通常会用Keepalived等高可用解决方案对haproxy做主备,在HAProxy主节点故障时自动将流量转移到备用节点。因此,直接访问某个节点不太好,需要访问一个统一的入口,该入口来帮助负载均衡到不同的RabbitMQ节点。2.如果都只访问集群的某一个节点,容易导致该节点负载过大从而容易崩溃,而其它集群节点很可能比较空闲,因此需要把消息负载均衡到不同的RabbitMQ节点。
2025-07-25 09:38:43
632
原创 RabbitMQ—仲裁队列
S3成为新Leader后向所有节点发送心跳包,此时S1处于Candidate状态接收到心跳包后得知S3的term高于自己,说明已经有新Leader,于是变为Follower,开始正常工作。上述+2表示还有2个副本。当集群中节点数大于五个,比如6个节点,则默认只有5个副本,1主4从副本,从副本随机分配在除主节点外的其它5个节点上(有一个节点会没有从副本)。而仲裁队列就是上述Raft算法运行的集群上的队列,每个仲裁队列有一个主和多个从副本,主副本在主节点(创建仲裁队列的节点是主节点)上,从副本在从节点上。
2025-07-24 11:03:14
785
原创 RabbitMQ集群搭建
在使用命令service rabbitmq-server start启动了一个节点后(主节点),使用rabbitmqctl status来查看当前节点的具体状态和信息(当RabbitMQ只部署到一个节点上,如果节点宕机就会导致整个系统不可用,集群方案就是解决这一类问题的方案(MySQL、Redis等集群思想也都差不多)。注意:集群中的队列、交换机等信息是共享的,即在任意一个节点都可以查询到。但是消息不是共享的,消息在哪个队列所属的节点,该节点关闭后,消息也会消失。由于真正的集群搭建成本较高,因此采用。
2025-07-23 11:03:29
965
原创 Redis原理之分布式锁
如果已经结束则通过lua脚本解锁。新的问题又出现了,执行解锁的过程中,由于get命令和del命令的执行不是原子性的,因此就会出现命令插队的情况。普通的锁比如Java中的synchronized通常解决的是线程安全问题,但是到了多进程多服务器环境下,普通的锁就无法保证多个进程或节点同时访问同一个资源的问题,这就需要分布式锁。上述方案还有问题,由于多个服务器都可以访问Redis来执行各种命令,就可能出现一个服务器设置键值对(加锁),另一个服务器删除键值对(解锁),从而导致问题(当然这个问题不是故意的)。
2025-07-23 11:01:24
1163
原创 RabbitMQ应用问题
对于最少一次,如果消息处理成功但是ACK因为网络等问题没有返回(生产者-MQ的发布确认模式、MQ-消费者的消息可靠性保证机制(手动确认)),消息就会重发,因此就会出现消费者对同一条消息多次处理的情况(同一订单多次扣款)。但是如果在这一过程中执行了修改操作,select查询的结果就不一样,这并不意味着select操作不是幂等的,因为幂等性指对资源的影响,而不是对结果的影响。:队列向消费者分发消息是并行进行的,因此一些顺序性的消息就被分发到不同消费者,不同消费者处理速度不一样,就无法保证顺序性。
2025-07-22 10:50:54
1060
原创 Redis原理之缓存
查询的key在Redis和MySQL数据库中都不存在,这样的key不会被实时缓存到Redis中,后续如果这类key频繁访问,依然会对MySQL造成较大压力。缓存穿透的特殊情况,缓存穿透是针对普遍的key(包括热点和非热点)突然同时失效的问题,而缓存击穿是指热点key突然过期,导致大量查询未命中去查数据库。当Redis刚启动或大批key同时过期,此时Redis中没有数据,查询就无法命中缓存,从而去查MySQL,给数据库带来大量查询压力。1.业务未进行合理的参数校验,导致非法的key被查询。
2025-07-22 10:47:33
965
原创 RabbitMQ—事务与消息分发
当prefetchCount达到设置的上限,队列就不再向消费者推送消息,直到有新的确认到来。RabbitMQ的事务机制要求消息的发送和接收是原子性的,要么同时成功,要么同时失败(失败后已发送的消息或接收的消息会回退)。当某个队列被多个消费者订阅,队列向消费者推送消息(消息分发对应推模式,对拉模式无效),每条消息只会发送给一个消费者。在这种情况下,假设有10条消息,两个消费者,每个消费者会被推送5条消息,消费者1消费速度快(容易空闲),消费者2消费速度慢,那么消费者2就会消息积压,系统实际吞吐量并不高。
2025-07-19 14:09:10
1151
原创 Redis原理之集群
命令redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id [110节点的ID]进行分片内主从关系的构建,第一个ip表示要把哪个节点加入到集群中,第二个ip表示集群中任意节点,--cluster-slave表示当前节点是slave类型,--cluster-master-id表示该slave从属哪个master。注意1:Redis集群是最多能有16384个分片吗?
2025-07-19 14:06:36
936
原创 Redis原理之哨兵机制(Sentinel)
需要提前安装docker和docker-compose,docker简单来讲是一个类似虚拟机的容器,它小巧轻量,提供了容器来统一配置环境信息,尤其是在多服务的环境中,配置很容易出现冲突,使用docker创建容器就可以实现多个服务的环境配置的冲突隔离。如果是slave节点故障,对读写没有太大影响;但是如果是主节点挂了,从节点是不会自动变为主节点的。则是启动节点的方式,ports是端口映射(把物理机的端口映射到容器的端口,容器端口和物理机端口独立,容器端口和容器端口独立,映射了后就可以让物理机访问容器)。
2025-07-18 15:30:53
1034
原创 RabbitMQ—TTL、死信队列、延迟队列
而在消息TTL中,连续发送过期时间长和过期时间短的消息,即使过期时间短的消息已经过期,只要过期时间长的消息还未过期,过期时间短的消息仍然会在队列中。比如消息1TTL为10s,消息2TTL为30s,消息2先发送,消息1后发送。比如订单超时支付自动取消,订单系统下单时设置延迟时间,并将订单消息投递到RabbitMQ中,消息超时则把订单消息发送给消费者(订单系统的订单状态处理模块),订单系统根据是否收到支付系统支付成功的消息或超时订单来修改订单状态(成功支付或超时未支付)。注意:队列TTL和消息TTL的区别。
2025-07-18 15:28:00
710
原创 RabbitMQ—消息可靠性保证
因此消息会先进入OS的缓冲区,待缓冲区满时再统一进行持久化,虽然这个时间很短,但是这期间如果RabbitMQ宕机,消息还为持久化到磁盘上,也会丢失(解决办法:(1)仲裁队列,主从结构。消息确认机制(自动确认和手动确认),自动确认(消息一经发出RabbitMQ就认为消息能到消费者于是进行确认并清除消息,默认auto级别的自动确认),如果需要保证消息的高可靠性需要开启手动确认(消费者显示调用肯定确认或否定确认方法来通知RabbitMQ,RabbitMQ根据手动确认的参数来决定确认成功、重新入队或丢弃)。
2025-07-17 12:51:54
691
原创 Redis原理之主从复制
role表示节点的角色,master_replid是主节点的身份码,offset偏移量表示复制的进度(不是瞬间就把所有的信息复制完),priority表示从节点优先级(用于主节点挂了从节点竞争成为主节点的场景),backlog表示积压缓冲区(和主从复制的策略有关),其余信息字面意思就很容易明白。master节点进行写,slave节点进行读。由于所有的节点的数据都时刻保持一致,因此请求哪个节点得到的数据都一样,这也就提高了系统的性能和并发量,同时如果某个从节点挂掉,只需要请求其他节点即可,提高了可用性。
2025-07-17 12:48:03
886
原创 多注册中心冲突报错Field autoServiceRegistration in org.springframework.cloud.client.serviceregistry
配置了Nacos作为注册中心,项目启动时报错:这是由于启动项目时会自动进行服务注册,需要注入一个唯一的Bean:AutoServiceRegistration,但是容器发现存在两个父类均为AutoServiceRegistration的Bean,一个对应Nacos,一个对应Eureka,简而言之,注册中心太多了,Spring不知道把服务注册到哪个注册中心。
2025-07-16 15:06:19
119
原创 Redis原理之事务
为了解决该问题,Redis引入命令watch,该命令可以监控一个或多个key,一旦在事务期间(multi与exec之间,操作命令放入队列后),其他客户端对监控的key进行修改,就会让执行事务的客户端感知到key被修改了,从而告诉用户该问题的风险。可以发现最终结果是客户端1事务执行后的结果,原因是因为事务队列的命令虽然先到Redis服务器,但是并没有真正执行。假设在客户端1开启事务,把命令放入事务队列,此时客户端2不开启事务,对客户端1那个命令的key进行修改,客户端1再执行事务。
2025-07-16 12:41:54
818
原创 使用Spring Cloud LoadBalancer报错java.lang.IllegalStateException
在使用Spring Cloud LoadBalancer进行负载均衡时,遇到错误:这里使用的restTemplate是使用@LoadBalanced注解的,因此会对该url进行解析,但是解析报错。
2025-07-15 20:03:27
798
原创 Redis原理之持久化
注意:子进程的重写类似于RDB快照文件的生成,因此Redis为了速度,引入混合持久化(配置文件中配置项aof_use_rdb_preamble可以修改是否开启),即开启混合持久化后子进程重写新的AOF文件存储的是和RDB一样的二进制数据,而在后面aof_rewrite_buf缓冲区追加操作新的AOF文件存储的是文本形式的命令操作。就自动触发RDB文件的生成。4.子进程在创建后执行重写,由于子进程已经有父进程fork前的数据了,而AOF文件最终目的是恢复数据,因此只需要把内存中的数据重写到新的AOF文件中。
2025-07-15 17:12:48
970
原创 RabbitMQ工作流程
Producer和RabbitMQ、Consumer和RabbitMQ基于TCP来建立连接,一个连接包含多个通道channel(逻辑上各通道之间相互独立,用于实现连接复用,减少频繁建立/关闭TCP连接的开销),发送消息和接收消息都是基于channel。交换机,生产者的消息进入RabbitMQ后首先进入Exchange,由Exchange进行路由分发(根据消息类型、规则和标签等等),把消息分发给指定的Queue。组件,生产者的消息首先传输到交换机,由交换机进行路由分发到指定的队列中并等待消费者接收。
2025-07-15 17:10:05
1052
原创 Spring原理—加载Bean
最关键的注解是@EnableAutoConfiguration,负责第三方依赖的配置类(@Bean注解的方法获得第三方依赖的对象)和组件(比如MyBatis的@Mapper注解不属于Spring)的扫描,其中包含两个关键注解:@AutoConfigurationPackage和@Import({AutoConfigurationImportSelector.class})。@AutoConfigurationPackage注解把启动类所在的包下面所有的组件都注入到Spring容器中。
2025-07-15 17:06:08
585
原创 SpringBoot系列—统一功能处理(统一异常处理)
initHandlerMappings()方法负责完成请求控制器路由映射处理器的初始化,即该处理器会把请求的URL和控制器方法建立映射关系。如果有多个异常,就通过sort()方法排序,按异常的深度排序(ExceptionDepthComparator类表示异常深度比较器,比如Exception深度为1,那子类RuntimeException的深度就是2,RuntimeException子类NullPointerException的深度就为3),把深度最深的异常放在顺序表matches下标为0的地方。
2025-07-14 13:58:22
982
原创 RabbitMQ的介绍与安装
(2)RabbitMQ采用Erlang语言开发,MQ功能比较全面,几乎支持所有主流语言,开源,提供的界面也很友好,性能较好,吞吐量能达到万级,社区活跃度较高,比较适合中小型公司、数据量没那么大,且并发量没那么太高的场景。点击右侧的Virtual Hosts可以进行虚拟机的管理,这里的虚拟机不同于传统的虚拟机(虚拟电脑),而是表示逻辑上的环境隔离,作用就和MySQL的databases一样,不同的database负责不同的业务。只不过这个队列存储的内容是消息,消息可以简单,比如字符串、JSON等等;
2025-07-14 13:55:51
782
原创 Redis客户端使用(Client、Java、SpringBoot)
由于可能有多个客户端想要跨主机访问Linux服务器的其他服务器的端口号,ssh为了区分这些端口号,就会把Linux的各种服务的端口号映射到本地主机(发出访问请求的主机)的其他端口号(比如Linux的Redis端口6379映射到本地主机8888端口),从而达到客户端访问本地端口即可实现访问Redis端口,这就是ssh的端口转发功能。其余的命令不再演示,Jedis提供的Java客户端访问Redis的命令基本和Redis客户端的命令一致,使用很简单,只是需要注意返回值和变长参数(或者是Map来组织变长参数)。
2025-07-14 13:53:42
795
原创 SpringBoot系列—统一功能处理(统一数据返回格式)
在其StringHttpMessageConverter子类的实现中,内部调用addDefaultHeaders()方法,把已经是Result类型的数据作为参数传入到String类型参数的addDefaultHeaders()方法,参数类型不匹配,因此报错。但是实际中,每个方法返回的数据很灵活,有可能这个方法使用的Result来封装数据,但是其他方法可能没有使用Result封装数据,那我们统一数据返回格式的目的就没有达到。通过该方法可以选择哪些类或哪些方法的response要进行处理,其他的不进行处理。
2025-07-13 13:30:36
922
原创 Redis数据类型之zset
时间复杂度O(logN+M),N是有序集合元素的个数,M是要删除元素的个数,注意这里因为只需要查找start对应的元素,其后的元素连续删除即可(每次删除只需判断分数是否超过max,无需再查找),因此时间复杂度也不是O(logN*M)。时间复杂度O(logN+M),N是有序集合元素的个数,M是要删除元素的个数,注意这里因为只需要查找start对应的元素,其后的元素连续删除即可(无需再查找),因此时间复杂度不是O(logN*M)。时间复杂度O(logN*M),N表示有序集合元素的个数,M表示要删除的元素个数。
2025-07-13 13:28:42
635
原创 启动Haproxy失败,如何排查问题
在启动Haproxy时,发现总是无法访问前端页面。检查配置文件格式,发现并不是配置文件的格式问题,ip和端口号也没有配置出错。启动日志报错无法绑定[0.0.0.0:8100]和[0.0.0.0:5670],也就意味着要么是端口号被占用,要么是端口号因为防火墙或安全性无法开放或者绑定。无任何输出则说明没有被占用,此时就说明可能是因为安全性或者防火墙才导致失败。可是我的虚拟机防火墙并未运行,因此只可能是安全性问题。
2025-07-12 20:41:05
248
原创 Redis数据类型之set
返回值1或0,0表示要移动的元素不存在在source,1表示移动成功或member已经存在destination的集合中(此时该元素仍然会从)source的集合中移除。set类型是集合,集合有两个重要的特性:1.集合中的元素不能重复。2.集合中的元素是无序的(一组数的所有排列的集合是同一个集合)。时间复杂度O(N*M),N是最小集合的大小,M是最大集合的大小。求集合的差集(注意A对B的差集是A有B没有的元素,B对A的差集是B有A没有的元素)。时间复杂度O(N*M),N是最小集合的大小,M是最大集合的大小。
2025-07-12 13:26:55
831
原创 SpringBoot系列—统一功能处理(拦截器)
如果一个接口传输的参数是一种格式,而另一个接口传输的参数是不同的格式,两个接口无法直接调用,因此就需要适配器作为中间件,在适配器内部把两个接口的参数统一,从而实现调用。:父类的方法延迟到子类中去实现。在HttpServletBean类中,初始化Servlet时首先会调用init()方法,该方法首先根据读取Servlet的配置信息,如果配置不为空就加载配置,否则就调用HttpServletBean实例的initServletBean()方法。在initStrategies()方法中,完成了9大组件的初始化,
2025-07-12 13:24:21
838
原创 SpringBoot系列—MyBatis-plus
BaseMapper接口是MyBatis-Plus实现的接口,内部包含了常用的CRUD方法,这里不再演示,可以去Libraries目录下找到myBatis-plus core依赖,进入com.baomidou.mybatisplus.core.mapper.BaseMapper查询这些方法的使用。QueryWrapper和UpdateWrapper使用时有个问题,即把字段写死了,一旦字段名改变,就需要修改实体类的属性名和所有写死字段名的代码,比较麻烦且一旦有没修改到位的地方,就会造成严重后果。
2025-07-11 12:56:27
663
原创 Redis数据类型之list
对消息的传递也可以解耦,即利用多个频道,每个频道传递不同的消息,生产者负责往不同频道写消息,消费者负责从多个不同频道读消息,这里的多个频道就是多个list。时间复杂度O(N),N是list的长度,这里因为list的底层实现可能是链表,链表查询是O(N)的复杂度。时间复杂度O(N),N是list的长度。时间复杂度O(N+M),N是list长度,M是删除的个数。blpop和brpop命令可以同时指定多个key,从左向右遍历key,只要有一个key的list弹出元素,就视为执行成功,命令立即返回。
2025-07-11 12:53:56
663
原创 SpringBoot系列—MyBatis(xml使用)
SQL注入:通过操作输入的数据来修改事先定义好的SQL语句,以达到执行代码对服务器进行攻击的方法。比如:select * from userinfo where id=’’ or 1=’1’,这里把’ or 1=’1作为传递的参数,从而形成即时SQL,该SQL的where语句会执行1=’1’,结果为true,因此该语句相当于select * from userinfo。对于数据量很大的数据库,如果查询所有的数据,将会非常耗时,从而可能影响服务器。因此,SQL注入是常用的攻击服务器的方式。
2025-07-10 13:06:12
733
原创 Redis数据类型之hash
即把user:id作为key,user具体的信息作为hash来存储,uid:1、name:zhangsan、age:18、gender:man等等,这里的uid、name、age和gender是field,对应的值是value。hash主要用于存储结构化的数据(关系型数据库更适合,完全结构化的数据),比如在Java中经常使用user类似的对象存储用户的信息,并且会经常访问(热点数据),就可以把经常访问的用户信息放在redis中。时间复杂度O(1)。获取key的hash类型的值的所有field和value。
2025-07-10 13:02:32
918
原创 Redis数据类型之String
如果是以--raw启动客户端,当截取汉字时,由于汉字的存储是字节,因此可能截取不完整的汉字字节,在通过字符集编码utf-8转化,可能会乱码。注意:redis不会对字符集编码做任何处理(也就是redis只认字节不认字符),由于这里使用FinalShell终端连接redis服务器,,而终端默认的字符集编码是utf-8(此编码下一个汉字字符占3个字节),因此使用终端存入汉字到redis中,“张三”就占6个字节。时间复杂度O(N),N表示新value的长度,通常N比较短,因此实际体验是O(1)。
2025-07-09 12:46:00
788
原创 SpringBoot系列—MyBatis(注解使用)
由于使用aliyun创建的项目是该依赖,因此在MyBatis中就没有办法在多个参数的SQL中使用方法参数名=#{参数名}的方式传入参数了。这里所做的就是把方法参数id和deleteFlag分别赋值给@Param("id")和@Param("deleteFlag")中的参数,此时MyBatis使用@Select注解就可以识别#{}中填写的参数名了,注意:@Param(参数名)必须和#{参数名}对应。使用#{}表示要传递的参数,其中要填的值在单个参数的情况下,不用和方法的参数名称一样,也不会出错。
2025-07-09 12:43:49
855
原创 SpringBoot系列—日志
如果不使用外观模式,对于多种日志实现的框架,如果一个项目已经使用了log4j,而依赖的另一个类库比如Apache Active MQ,它依赖于另外一个日志框架logback,那么就需要把logback也加载进去,就可能出现不同日志框架冲突、代码调用复杂、配置文件多等问题。在上面的Logger对象实际上是由SLF4J包提供的,而SLF4J并不是日志框架,没有实现日志相关的代码,它是日志门面,用到门面模式(又称外观模式)。所有的日志如果都持久化到一个文件中,文件将会随时间越来越大,就需要对日志文件进行分割,
2025-07-08 13:14:32
1048
原创 Redis常用数据类型
当hash中元素个数比较少(少于hash-max-ziplist-entries配置(redis配置文件中,默认512个))或value比较短(小于hash-max-ziplist-value配置(redis配置文件中,默认64字节))使用ziplist。quicklist是linkedlist和ziplist的结合,即本身是linkedlist,链表的每个节点是ziplist,这样就既节省了空间,又可以存较多的数据。是跳表,也是链表,但是每个节点的指针域不只包含下一个节点的地址,还有其他节点的地址。
2025-07-08 13:11:11
681
原创 SpringBoot系列—配置文件
滑动拼图的验证码则是把整个图片的长视为[0,1],选取一个值作为拼图块的中心(比如0.5),拖动拼图块则是判断用户拖拽的当前拼图块的中心的值是否位于选择的值的位置。在com.oopsguy.kaptcha.KaptchaProperties的源码中可以发现,作者实现了自动配置,让我们只用在配置文件中写验证码的配置,无需在自己写验证码生成的代码,即可自动生成验证码(底层还是基于Servlet,使用HttpServletRequest req来进行session的存储)。如果key有多级,就用.分隔。
2025-07-07 12:39:36
1013
原创 Redis常用命令使用
注意:keys命令查询复杂度为O(n),因此生产环境一般都禁止使用keys命令,尤其是keys *(查找所有的key),因为redis是单线程的,当一个客户端使用keys *,导致其他客户端无法使用redis服务器。同时,redis的遍历操作由于服务器无状态(服务器不记录当前遍历的痕迹),因此可以随时取消(如果服务器有状态,即记录遍历的痕迹,那么取消遍历时可能对服务器造成一定的影响)。时间复杂度O(N),N为要判断的key的个数。时间复杂度O(N),N为要删除的key的个数,只删除一个key就是O(1)。
2025-07-07 12:37:04
655
原创 RabbitMQ启动报错:Job for rabbitmq-server.service failed because the control process exited with error
由于是在虚拟机里安装的RabbitMQ,因此一般遇到进程无法启动就考虑:端口号占用问题、ip主机名映射、配置文件问题等等方面。这里已经排除了端口号占用问题,发现是ip主机名映射问题导致的问题。这是由于个人配置的静态ip由于网络环境问题经常发生变化,因此ip和主机名的映射经常是旧的关系。
2025-07-06 14:45:37
422
原创 认识Redis
基于网络,在分布式系统中,由于进程的隔离性,多机间的进程就需要通信,而Redis可以把数据存储在内存中,让多机都可以访问自己内存的数据,因此Redis的应用场景是。比如作为数据库,Redis可以保证数据不会丢失,关机重启后内存数据会丢失,而Redis有持久化的特性,因此可以保证数据不会丢。多线程并不一定可以提高速度,多线程的优势是CPU计算密集型任务,而Redis大多是对内存中的数据存取和修改,不会消耗很多CPU时间,此时频繁加锁解锁和线程间的阻塞唤醒就会成为速度的累赘。
2025-07-05 21:12:21
993
原创 SpringBoot系列—入门
观察启动日志可以发现,在上图第4行,说明SpringBoot内置了Tomcat(传统Tomcat部署Servlet程序webapps目录下有多个项目,因此访问路径需要加上项目名因此SpringBoot项目访问路径不需要加上项目名),因此部署SpringBoot项目时我们不再打成War包部署在Tomcat的webapps目录下。这里是直接使用专业版,内部集成了SpringBoot的插件。同时要选择Spring Web依赖,这是Spring MVC的依赖,SpringBoot程序是基于Spring MVC的。
2025-07-05 21:08:57
805
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人