什么是消息队列?消息队列(Message Queue),字面意思就是存放消息的队列。最简单的消息队列模型包括3个角色:
消息队列:存储和管理消息,也被称为消息代理(Message Broker)
生产者:发送消息到消息队列
消费者:从消息队列获取消息并处理消息
如何实现?之前使用Java的BlockingQueue和线程池实现了简单的消息队列,但这样的实现有两个问题:
内存消耗:若消息很多,则JVM的内存可能被快速耗尽,导致内存不足
非持久性存储:JVM的内存不是持久性的,消息队列中的消息可能发生丢失
所以这里采用Redis来实现,可以很好解决上述问题(为什么不用其他中间件?因为Redis成本更低)
Redis提供了三种不同的方式来实现消息队列:
list结构:基于List结构模拟消息队列
PubSub:基本的点对点消息模型
Stream:比较完善的消息队列模型
基于List结构模拟消息队列实现方式使用LPUSH实现消息进入队列,BRPOP实现消息有阻塞的取出队列
该实现方法的优缺点优点
利用Redis存储,不受限于JVM内存上限
基于Redis的持久化机制,数据...
二分法,很简单啊,很早就知道这个了,不就是一直从中间取值,然后找到target吗
那么, 每次写二分法时,有没有想过,while判断条件里的left和right,究竟是<还是<=呢;更新边界时,right究竟是middle还是middle - 1呢?
至少对我来说,每次写二分这些都是随缘的,能a就行,但是大部分时候a不了
回到正题,写二分法时,最重要的就是保持区间不变
所谓区间不变,就是整个算法当中,都要明确自己的代码是左闭右闭[left, right],还是左闭右开[left, right),以下只会讲左闭右闭的情况,另外的情况就很简单了。
< 还是 <=while里的判断条件,也就是区间是否合法的条件,我们假设left和right相等,区间就是[1,1],这个区间显然合法,所以应该是<=
也不难想到,在左闭右开的情况下,是不能取等号的,因为[1, 1)显然不合法
要不要减一(加一)边界更新的前提是我们已经知道了nums[middle] != target, 此时我们的下一个搜索范围,肯定不应该包含middle了,所以应该是要加一或减一的。
...
在Spring管理的类中注入Bean对象非常容易,只需使用@Autowired或者@Resource注解即可,但是在非Spring管理的对象中,注入Bean对象却没这么简单。
这时,我们就需要自己写一个工具类了,实现很简单,代码如下:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748package com.hmdp.utils;import org.springframework.beans.BeansException;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.lang.NonNull;import org.springframework.stereotype.Component;@Componentpublic c...
虽然Java提供了 synchronized之类的方法来实现线程对临界资源的互斥访问,但是这仅仅局限于单台服务器,对于集群服务器并不适用。
Redis分布式锁
于是我们希望通过Redis来实现服务器集群的线程锁,因为Redis对于所有服务器来说是相同的,且具有较好的性能和较高的可用性。
简单实现具体实现流程如下:
代码实现先写一个锁的抽象类,因为会做多个版本的实现
123456789101112131415public abstract class ILock { /** * 尝试获取锁 * * @param timeoutSec 锁自动释放的市场(秒) * @return 是否成功获取到锁 */ abstract public boolean tryLock(long timeoutSec); /** * 释放锁 */ abstract public void unlock();}
当前版本的最基础实现
12345678910111213141516171819202122...
缓存穿透
缓存穿透:对于一个缓存和数据库中都不存在的数据查询,因为每次查询都会穿过缓存直接打在数据库上,所以很容易增大数据库压力,如下图所示。
对于缓存穿透这个问题,解决方案包括但不限于如下:
缓存空对象 进行一次结果为空的数据库查询后,以该查询为key,null为value,并设置TTL,将其放进缓存,这样在下一次同样的查询时,就不会再进行数据库查询。缺点
缓存空对象会造成缓存的内存开销。
代码实现 123456789101112131415161718192021222324252627282930313233343536373839404142/** * 普通缓存查询,使用存储空对象的方法防止缓存穿透 * * @param keyPrefix key的前缀 * @param id 要查询的id * @param type 查询的实体类型 * @param dbFallBack 数据库查询方法 * @param time 新缓存的过期时间 * @param unit 时间单位 * @param <T>...
基于session完成登录时如何登出@Controller
1234567@PostMapping("/logout")public Result logout(HttpSession session){ log.info("登出"); session.invalidate(); //使当前客户端的session无效化 userService.logout(); return Result.ok();}
@Service
123public void logout(HttpHeaders headers) { UserHolder.removeUser(); //清空TreadLocal的数据}
基于Redis完成登录时登出@Controller
123456@PostMapping("/logout")public Result logout(@RequestHeader HttpHeaders headers){ log....