1.如何使用 Redis 实现一个排行榜?
Redis实现排行榜是Redis中一个很常见的场景,主要使用的是ZSet进行实现,下面是为什么选用ZSet:
有序性:排行榜肯定需要实现一个排序的功能,在Redis中有序的数据结构有List和ZSet;
支持分数操作:ZSet可以对集合中的元素进行增删改查操作,十分贴合排行榜中用户分数动态变化的场景,而List并不能针对分数进行操作,只有其中的value进行操作;
支持范围查询:ZSet可以按照分数进行范围查询,如排行榜中的Top10需求就可通过该特性进行实现;
支持去重:由于ZSet属于Set的特殊数据结构,因此同样拥有Set不可重复的特性,对于排行榜中不可出现重复项的需求也十分贴合,而List只能手动去重。
因此选择ZSet实现排行榜相对于List实现会更合适和高效。
以学生成绩排行为例,下面是使用Redis命令实现
# 添加示例数据 ZADD scores 90 "张三" ZADD scores 85 "李四" ZADD scores 95 "王五" ZADD scores 92 "赵六" # 查询排名前3的学生信息 ZRANGE scores 0 2 WITHSCORES # 查询排名前3的打印 1) "王五" 2) "95" 3) "赵六" 4) "92" 5) "张三" 6) "90" # 删除学生“李四”的成绩信息 ZREM scores "李四"下面是SpringBoot整合Redis进行实现
// 添加学生成绩 public void addScore(String name, int score) { redisTemplate.opsForZSet().add("scores", name, score); } // 查询排名前N的学生成绩 public List<Map.Entry<String, Double>> getTopScores(int n) { return redisTemplate.opsForZSet().reverseRangeWithScores("scores", 0, n - 1) .stream() .map(tuple -> new AbstractMap.SimpleEntry<>(tuple.getValue(), tuple.getScore())) .collect(Collectors.toList()); } // 删除某个学生的成绩 public void removeScore(String name) { redisTemplate.opsForZSet().remove("scores", name); }
2.什么是网关,网关有哪些作用?
网关(Gateway)是在计算机网络中用于连接两个独立的网络的设备,它能够在两个不同协议的网络之间传递数据。在互联网中,网关是一个可以连接不同协议的网络的设备,比如说可以连接局域网和互联网,它可以把局域网的内部网络地址转换成互联网上的合法地址,从而使得局域网内的主机可以与外界通信。
在计算机系统中,网关可以用于实现负载均衡、安全过滤、协议转换等功能。具体来说,网关可以分为以下几种:
应用网关:用于应用层协议的处理,如 HTTP、SMTP 等。(Nginx、HA Proxy)
数据库网关:用于数据库访问的控制和管理。
通信网关:用于不同通信协议之间的数据交换,如 TCP/IP、UDP/IP 等。
API 网关:用于管理和转发 API 请求,实现 API 的授权、限流、监控等功能。( Spring Cloud Gateway )
网关的作用如下:
实现协议的转换:不同网络之间通常使用不同的协议,通过网关可以实现协议的转换,使得不同网络之间能够相互通信。
提供数据转发功能:网关可以对传输的数据进行过滤、路由、转发等处理,确保数据的可靠传输。
实现安全策略:网关可以对传输的数据进行加密、认证、授权等操作,保证数据的安全性和可靠性。
提供缓存功能:网关可以将一部分数据缓存起来,提高数据的访问速度和响应性能。
支持负载均衡:网关可以将请求分配到不同的服务器上,实现负载均衡,提高系统的可用性和性能。
实现访问控制:网关可以对访问进行控制,防止未授权的访问和攻击,提高系统的安全性。
总的来说,网关可以为不同网络提供连接和通信的功能,同时也可以提供安全、性能、可靠性等方面的增强功能,是现代计算机系统中不可或缺的一部分。
针对Spring Cloud Gateway:
官网:Spring Cloud Gateway
网关出现的原因是微服务架构的出现,不同的微服务一般会有不同的网络地址,而外部客户端可能需要调用多个服务的接口才能完成一个业务需求。如果让客户端直接与各个微服务通信,会有以下的问题:
客户端会多次请求不同的微服务,增加了客户端的复杂性;
存在跨域请求,在一定场景下处理相对复杂;
认证复杂,每个服务都需要独立认证;
难以重构,随着项目的迭代,可能需要重新划分微服务。例如,可能将多个服务合并成一个或者将一个服务拆分成多个。而划分出多个微服务就代表可能出现多个新的访问地址,如果客户端直接与微服务通信,那么重构将会很难实施;
某些微服务可能使用了防火墙/浏览器不友好的协议,直接访问会有一定的困难;
那么使用网关的好处就如下:
路由
负载均衡
统一鉴权
跨域
统一业务处理
访问控制
发布控制
流量染色
接口保护
同一日志
统一文档
常见的网关产品有Tyk,Kong,Zuul以及Spring Cloud Gateway
3.线程的生命周期是什么,线程有几种状态,什么是上下文切换?
来自:Starry、黑马程序员
1、六种状态
Java
中有六种状态:新建状态(New)、就绪状态(Runnable)、阻塞状态(Blocked)、等待状态(Waiting)、超时等待(Timed_Waiting)、终止状态(Terminated)
NEW:初始状态,线程被创建出来但没有被调用 start() 。
RUNNABLE:运行状态,线程被调⽤了 start() 等待运行的状态。
BLOCKED:阻塞状态,需要等待锁释放。
WAITING:等待状态,表示该线程需要等待其他线程做出⼀些特定动作(通知或中断)。
TIME_WAITING:超时等待状态,可以在指定的时间自行返回而不是像 WAITING 那样⼀直等待。
TERMINATED:终⽌状态,表示该线程已经运行完毕。
2、五种状态
从
操作系统
层面的划分,线程有五种状态:新建、就绪、运行、阻塞和死亡状态。
新建状态(New) : 新创建了一个线程对象
就绪状态(runnable) : 线程对象创建后,其他线程调用了该对象的 start 方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权
运行状态(Running) : 就绪状态的线程获取了 CPU,执行程序代码
阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态
死亡状态(Dead) :线程执行完了或者因异常退出了 run 方法,该线程结束生命周期。
阻塞情况又分为三种:
等待阻塞:运行的线程执行 wait 方法,该线程会释放占用的所有资源,JVM会把该线程放入 "等待池"中,进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用 notify 或者 notifyAll 方法才能被唤醒,wait 是object 类的方法。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则 JVM 会把该线程放入 "锁池"中。
其他阻塞:运行的线程执行 sleep 或者 join 方法,或者发出了 I/O请求时,JVM 会把该线程设置为阻塞状态。当 sleep 状态超时、join 等待线程终止或者超时、或者 I/O处理完毕时,线程重新转入就绪状态。 sleep 是 Thread 类的方法。
3、上下文切换
线程的状态变化通常是由操作系统进行管理和控制的,当线程状态发生变化时,需要进行上下文切换。
上下文切换是指将当前线程的状态保存下来,并将CPU资源切换到另一个线程上运行的过程。上下文切换需要花费一定的时间和系统资源,因此,线程的上下文切换次数要尽量减少,以提高系统的性能。