- dubbo
参考大神的dubbo面试的18问:https://blog.csdn.net/weixin_44337261/article/details/88149072
- dubbo是什么
Dubbo是阿里巴巴SOA服务化治理方案的核心框架,Dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案。 - dubbo架构
1.当服务的提供者启动时,会将服务的名称:IP:端口会写入注册中心.
2.注册中心内部会维护服务列表,某个服务提供者关机了,服务还能正常进行
3.当消费者需要访问服务时,需要先访问注册中心获取服务列表信息.之后将服务列表保存到本地缓存中.方便后续的访问.在客户端内部有负载均衡的算法,筛选出一台服务器,之后进行访问.
4.如果后台服务器出现宕机现象.这时注册中心通过心跳检测的方式判断服务器是否宕机.如果服务器宕机则会将该服务器信息从服务列表中删除.之后将新的服务列表发送消费者(客户端)进行更新. - dubbo底层原理
总结:RPC调用的规则可以传输java对象.底层实现时将数据转化流,并且该流经过加密处理.并且rpc内部使用UTF-8编码格式
要求:传输的java对象必须序列化
设计:
实现一个RPC远程调用框架
提供者:
public class ProviderMain {
public static void main(String[] args) {
try {
System.out.println("start server 20881");
ServerSocket serverSocket = new ServerSocket(20881);
while (true) {
ObjectInputStream objectInputStream = null;
ObjectOutputStream objectOutputStream = null;
try {
Socket socket = serverSocket.accept();
// 1,接收数据
InputStream inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
String interfaceName = objectInputStream.readUTF();
String methodName = objectInputStream.readUTF();
Class[] parameterTypes = (Class[]) objectInputStream.readObject();
Object[] argments = (Object[]) objectInputStream.readObject();
// 2,执行方法
// 根据interfaceName找到实现类
// map(interfaceName,impl);
Class implClass = CartServiceImpl.class;
Object implObject = implClass.newInstance();
Method method = implClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(implObject, argments);
// 3,返回数据
OutputStream outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeObject(result);
} catch (Exception e) {
e.printStackTrace();
} finally {
objectInputStream.close();
objectOutputStream.close();
}
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
消费者:
public class CartController {
public static void main(String[] args) {
try {
Object object = getObject(CartService.class);
CartService cartService = (CartService) object;
cartService.findCartByUserId(91L);
} catch (Exception e) {
e.printStackTrace();
}
}
static class MyInvocationHandler implements InvocationHandler {
String interfaceName;
public MyInvocationHandler(String interfaceName) {
super();
this.interfaceName = interfaceName;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
// 1,得方法名,参数数据
String methodName = method.getName();
Class[] parameterTypes = method.getParameterTypes();
// 2,发送数据
Socket socket = new Socket("127.0.0.1", 20881);
OutputStream outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
objectOutputStream.writeUTF(interfaceName);
objectOutputStream.writeUTF(methodName);
objectOutputStream.writeObject(parameterTypes);
objectOutputStream.writeObject(args);
// 3,接收数据
InputStream inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
Object result = objectInputStream.readObject();
System.out.println("收到远程方法执行结果" + result);
return result;
} catch (Exception e) {
e.printStackTrace();
} finally {
objectOutputStream.close();
objectInputStream.close();
}
return null;
}
}
public static Object getObject(final Class interfaceInfo) {
// 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用
String interfaceName = interfaceInfo.getName();
ClassLoader classLoader = interfaceInfo.getClassLoader();
Class[] interfaces = { interfaceInfo };
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(interfaceName);
Object object = Proxy.newProxyInstance(classLoader, interfaces, myInvocationHandler);
return object;
}
}
- 运用
提供者:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
<!-- 1,设置应用名称 -->
<dubbo:application name="provider-of-cart"/>
<!-- 2,设置服务注册中心zookeeper地址 -->
<dubbo:registry address="zookeeper://192.168.216.202:2181">
</dubbo:registry>
<!-- 3,设置dubbo端口号 -->
<dubbo:protocol name="dubbo" port="20883"></dubbo:protocol>
<!-- 4,注册服务实现类对象-->
<bean class="com.jt.service.CartServiceImpl" id="cartService">
</bean>
<!-- 5,设置客户端能访问接口,像servlet注册-->
<dubbo:service interface="com.jt.service.CartService" ref="cartService">
</dubbo:service>
</beans>
注册中心、dubbo端口、注册服务实现类、客户端服务访问接口
服务消费者:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
<!--1,设置应用名-->
<dubbo:application name="consumer-of-cart" />
<!--2,设置注册中心地址 -->
<dubbo:registry address="zookeeper://192.168.216.202:2181" />
<!--3,得到远程服务代理对象,可以像使用本地bean一样使用cartService -->
<dubbo:reference timeout="50000" id="cartService" interface="com.jt.cart.service.CartService" />
注册中心地址,访问接口名
- 面试问题
1、一般在项目中需要先启动服务提供者,后启动服务消费者,否则会报错,解决办法:
如:a,b 消费b , b也需要消费a中间的service.
启动时检查提供者是否存在,true报错,false忽略 不检查
@Configuration
public class DubboConfig {
/**
* 消费者配置不主动监督zookeeper服务
*
* @return
*/
@Bean
public ConsumerConfig consumerConfig() {
ConsumerConfig consumerConfig = new ConsumerConfig();
consumerConfig.setCheck(false);
consumerConfig.setTimeout(40000);
return consumerConfig;
}
}
2、说明:当zk如果宕机后,消费者能否正确消息?????关zk,provider1,provider2
答案:可以
因为zk会动态的向客户端更新服务列表信息.当zk宕机后,由于之前已经同步了zk的服务列表信息,所以客户端可以 按照自己已经缓存的清单进行访问.如果在这个期间服务端程序发现宕机现象,那么则访问故障机时由于不能通信,则等待超时时间,则访问下一台服务器.
如果这时,所有的服务端程序都宕机,则整个服务陷入瘫痪.
- Zookeeper
-
zookeeper是什么
ZooKeeper是一个分布式应用程序协调服务。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。
当客户端发起请求时,zookeeper返回正确的服务器地址. -
集群原理
总结:Zookeeper --原子广播—Server之间的同步—。Zab协议:两种模式–》1.恢复模式(选主)2.广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复(选主)模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
补充:心跳检测能够监控服务的健康状况,同springcloud的Eureka服务器差不多功能 -
作为dubbo的注册中心Zookeeper对dubbo存储数据的示例:
节点类型
有4种类型的znode
1、PERSISTENT–持久化目录节点
客户端与zookeeper断开连接后,该节点依旧存在
2、PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
3、EPHEMERAL-临时目录节点
客户端与zookeeper断开连接后,该节点被删除
4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
- RabbitMq、ActiveMq、RocketMq、kafaka
-
应用解耦
订单系统写入消息队列,然后返回用户下单成功,
库存系统去订阅消息,进行入库 -
流量销峰
应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。用户的请求,服务器接收后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到 错误页面
秒杀业务根据消息队列中的请求信息,再做后续处理
通过消息队列缓解服务器压力. -
异步处理
RabbitMQ:
1.rabbitMQ监控系统的端口:15672
2.rabbitMQ服务调用端口:5672
1.0 如何保证消息队列的顺序执行
场景:
由于拿到每个数据后,消费每个数据的速度不一样,导致了入数据库的数据顺序有问题。
解决方案:(RabbitmQ)
1、多个队列将消息往一个队列发
2、消费者内部采用内存队列来消费
解决方案(ActiveMq的):
有消息组
https://blog.csdn.net/YAOQINGGG/article/details/82563304
2.0如何保证消息不丢失?
三处消息丢失:
生产者:confirm机制。
rabbitmq:开启持久化 2处 元数据 和 队列数据。
消费者:手动ack告诉rabbitmq 手动确认机制。
一:rabbitmq的事务,阻塞的,回滚,然后重试;confirm机制,消息队列回传你ack确认(针对提供者断线了导致的数据丢失),如果没能收到会回调你nack接口告诉你失败了
二:持久化数据开启(针对ActiveMq挂了)
三:消费者采取手动确认机制 消费结束的时候手动ack 到消息队列。
参考:https://www.cnblogs.com/756623607-zhang/p/10507267.html
3.0如何处理消费者消费失败?
加入一个死信队列,后台启动线程重新消费
消息队列模式:
1.简单模式
工作原理:
当客户端(生产者)将消息写入消息队列中时,消息队列中信息的数量加1.
消费者实时监听消息队列,当消息队列中有消息时,则获取消息,之后执行业务逻辑.同时消息队列的数量减一
实现:
<!-- 消息队列 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>3.5.1</version>
</dependency>
消息生产者
public class Test_1_simple_provider {
@Test
public void provider() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,发送消息,routingKey必须与queue一致
String msg="msg1";
channel.basicPublish("", "orderQueue", null, msg.getBytes());
//5,关闭
channel.close();
connection.close();
System.out.println("发送数据成功");
}
}
消息消费者:
public class Test_1_simple_consumer {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("orderQueue", true, consumer);
//5,取消息
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者取到:"+mString);
}
}
}
2.工作模式
说明:
由一个生产者负责消息写入队列,但是如果有一个消费者负责消费,可能会造成消息的积压.所以准备多个消费者共同消费一个队列中的消息.
实现:
生产者:
public class Test_2_work_provider {
@Test
public void provider() throws Exception {
// 1,建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 2,建立通道
Channel channel = connection.createChannel();
// 3,定义队列
// durable true 持久化,重启服务器后,数据还有
// exclusive true,只能通过当前连接消费 false
// autoDelete true 队列中消息处理完后,自动删除队列
// arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
// 4,发送消息
for (int i = 3; i < 10; i++) {
String msg = "msg" + i;
channel.basicPublish("", "orderQueue", null, msg.getBytes());
}
// 5,关闭
channel.close();
connection.close();
System.out.println("发送数据成功");
}
}
消费者1
public class Test_2_work_consumer1 {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("orderQueue", true, consumer);
//5,取消息
System.out.println("消费者1启动");
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者1取到:"+mString);
}
}
}
消费者2
public class Test_2_work_consumer2 {
@Test
public void consumer() throws Exception
{
//1,建立连接
ConnectionFactory factory=new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection=factory.newConnection();
//2,建立通道
Channel channel=connection.createChannel();
//3,定义队列
//durable true 持久化,重启服务器后,数据还有
//exclusive true,只能通过当前连接消费 false
//autoDelete true 队列中消息处理完后,自动删除队列
//arguments 参数
channel.queueDeclare("orderQueue", true, false, false, null);
//4,创建消费者
QueueingConsumer consumer=new QueueingConsumer(channel);
//autoAck:自动回复消息
channel.basicConsume("orderQueue", true, consumer);
//5,取消息
System.out.println("消费者2启动");
while(true)
{
Delivery delivery=consumer.nextDelivery();
byte[] data=delivery.getBody();
String mString=new String(data);
System.out.println("消费者2取到:"+mString);
}
}
}
说明:
消息被两个消费者消费
3.发布订阅模式
特点:如果生产者发送消息,那么订阅的全部消费者都会执行消息.
后期添加的消费者得不到以前的消息。需要先启动消费者
生产者:
public class Test_3_publish_p {
// 定义生产者
@Test
public void provider() throws IOException {
System.out.println("开始发布消息");
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
// 定义通道
Channel channel = connection.createChannel();
// 定义交换机名称
String exchange_name = "E1";
// fanout是定义发布订阅模式 direct是 路由模式 topic是主题模式
channel.exchangeDeclare(exchange_name, "fanout");
String msg = "order1" ;
channel.basicPublish(exchange_name, "", null, msg.getBytes());
channel.close();
connection.close();
}
}
消费者1:
public class Test_3_publish_c1 {
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E1";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "fanout");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者1");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"入库");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
消费者2:
@Test
public void consumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.216.202");
factory.setPort(5672);
factory.setUsername("jtadmin");
factory.setPassword("jtadmin");
factory.setVirtualHost("/jt");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String exchange_name = "E1";
//定义交换机模式
channel.exchangeDeclare(exchange_name, "fanout");
String queue_name = UUID.randomUUID().toString();
System.out.println("队列名称"+queue_name);
//定义队列
channel.queueDeclare(queue_name, true, false, false, null);
//将队列和交换机绑定 key:表示接收数据标识
channel.queueBind(queue_name, exchange_name, "");
//定义消费数量
channel.basicQos(1);
//定义消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
//将消费者和队列绑定,并且需要手动返回
channel.basicConsume(queue_name, false, consumer);
System.out.println("消费者2");
while(true){
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String msg = new String(delivery.getBody());
System.out.println(msg+"给用户发短信");
//false表示一个一个返回
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
说明:
两个消费者都能消费消息
4.路由模式
说明:路由模式是发布订阅模式的升级,通过定义不同的路由key使得程序将消息发送到不同的队列中.
5.主题模式
说明:
可以通过路由key将消息发送到一类相同的key中 使用通配符实现
符号说明:
#号:表示任意字符(任意个.)
*号:任意单个字符或者词组(单个.)
ActiveMq:
1、点对点模式:
只有一个消费者可以接收到消息
不能重复消费
生产者:
$queneName = "/queue/userReg";
消费者:
$stomp->subscribe('/queue/userReg');
2、发布/订阅模型特点:
多个消费者都可以收到消息
能重复消费
生产者:
$queneName = "/topic/userReg";
消费者:
$stomp->subscribe('/topic/userReg');
几种消息队列的比较:
例如:
面试:公司是怎么处理消息被消费者消费失败的问题的
引入了死信队列,将失败的消息放到死信队列中,重新消费。
- Redis
- 缓存机制
说明:缓存中的数据其实是数据库数据的备份.缓存机制其实就是有效的降低了用户访问真实物理设备的访问频次.
前提:缓存一般保存到内存中
问题:
1.缓存的内存空间如何维护?
2.如何保证数据一致性?
3.缓存中的数据如何保证不丢失?
4.如何实现缓存高可用
- Redis介绍
网址: https://redis.io/
Redis 是一个开源(BSD许可)的,key-value存储的.内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。 它支持多种类型的数据结构,如 字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets) 与范围查询, bitmaps, hyperloglogs 和 地理空间(geospatial) 索引半径查询。 Redis 内置了 复制(replication),LUA脚本(Lua scripting), LRU驱动事件(LRU eviction),事务(transactions) 和不同级别的 磁盘持久化(persistence), 并通过 Redis哨兵(Sentinel)和自动 分区(Cluster)提供高可用性(high availability)。
结果:读的速度是110000次/s,写的速度是81000次/s
- Redis分片介绍
说明:如果单台redis出现宕机的现象.会影响整个服务.单台的redis内存受限.
解决方法:实现redis的分片.
优点:
1.可以实现redis的内存动态扩容
2.Redis的保存的数据,分别保存到多台redis中,如果其中有一台redis出现问题,那么其中的数据只损失1/n的数据.
哈希一致性算法
说明:redis分片时采用哈希一致性算法,实现数据动态的绑定.
术语:
1.节点(node) 代表真实的redis服务器
2.key 用户保存/读取关键字
均衡性
问题:
由于node节点是经过hash一致性算法计算的结果.那么可能会出现计算的节点所分配的内存不均.如果内存不均,会导致某些节点内存负载过高.
特点说明:
要求全部的数据,尽可能的均匀分配到不同的节点中,每个节点中保存的数据尽可能接近1/n
实现步骤:
如果遇到节点负载不均时,会自动的启动虚拟节点,进行数据的平衡
单调性
说明:如果节点的个数增加,原有的节点的挂载会自动的发生变化.将满足条件的数据自动的挂载到新的节点中.
原则:尽可能保证原有的数据不变
总结:节点增加,数据动态发生迁移
- Redis哨兵实现
比较
分片特点:端口不一样
优点:实现内存动态扩容
缺点:
1.如果分片的节点宕机,则数据丢失
2.如果分片的节点宕机,那么整个服务器都不能正常使用.
哨兵特点:两台redis
实现主从挂载
- Redis中持久化策略
说明:因为redis中保存的数据都在内存中,当断电/宕机.缓存中的数据都会被清空.如果redis中没有配置持久化策略,安全性不够完善.
策略说明:
1.RDB方式(定期)
该方式是redis默认选择的持久化策略
特点:持久化的效率更高,定期持久化可能会丢失数据
2.AOF方式(/s)
该方式需要通过配置文件手动开启
特点:持久化效率低,每秒持久化/每次操作持久化,保证数据尽可能不丢失
持久化步骤:
1.当用户set操作时,redis中的数据会新增/更新
2.这时根据用户选择的持久化的策略.自动的进行数据持久化操作.以下以RDB模式为例.
3.定期会将redis中全部的数据通过xxx.RDB文件的方式保存.
4.如果redis服务器宕机重启时,首先会加载持久化文件xxx.RDB.恢复内存中的数据.
5.当用户使用redis时,这时redis内存中已经恢复了数据,为用户继续提供服务.
- Redis中内存策略
因为redis中的数据全部都写入内存中.当redis中的数据不能再次写入时这时redis服务会有问题.所以应该保证redis服务永远可写.如何实现redis数据内存优化呢??
1.定义redis中最大内存
2.LRU算法:
内存管理的一种页面置换算法,对于在内存中但又不用的数据块(内存块)叫做LRU,操作系统会根据哪些数据属于LRU而将其移出内存而腾出空间来加载另外的数据。
3.内存优化手段:
volatile-lru -> 将设定超时时间的数据并且其中使用较少的数据进行删除
allkeys-lru -> 将redis中全部key进行LRU筛选,之后进行删除
volatile-random -> 设定了超时间的数据,随机删除
allkeys-random -> 全部的key随机删除
volatile-ttl -> 将已经设定了超时时间的数据,按照存活时间排序,将马上要过期的数据进行删除.
noeviction -> 不做任何操作,将报错信息返回给用户.
4.修改优化策略
- Redis集群搭建
比较:
分片和哨兵说明
优点:
1.分片可以实现内存的动态的扩容
2.使用分片能够将数据分开保存,如果宕机/数据损坏只丢失1/n的数据
3.哨兵可以实现redis的高可用.
缺点:
1.如果分片中的一个节点宕机,那么整个分片将不能正常运行.
2.分片中的数据因为hash一致性的原因.可能会导致数据分布不是特别的平均.
3.如果哨兵宕机,则整个服务陷入瘫痪.
说明:
redis集群实质上将redis分片和redis哨兵的机制进行整合.redis集群中每个节点多可以与其他节点进行通讯.同时集群内部有心跳检测.如果节点发生宕机的现象.由所在集群的全部服务器负责推选.保证服务的正常运行.如果全部的从节点宕机,并且这时主节点宕机那么整个集群才会奔溃.
- 面试问题总结
redis中能存储哪些数据类型?
字符串(strings), 散列(hashes), 列表(lists), 集合(sets), 有序集合(sorted sets)
redis中的数据持久化策略?
rdb,将用户操作记录在二进制文件中,如果瘫痪或者停电了,将从文件中读取,这是redis的默认持久化策略。
Redis计数器:是能够解决并发请求的,让其串行去执行,