【永利电子游戏网站】深入理解redis分布式锁和消

2020-01-11 作者:计算机教程   |   浏览(76)

最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令。

四、消息通知

  一般来说,消息队列有两种场景,一种是生产者消费者模式,一种是发布者订阅者模式。利用redis这两种场景的消息队列都能实现。

其实redis在其中做的还是缓存的作用,LPUSH queue task,将task放到queue队列里面,这里稍微偏题一句,其实redis有lpush和rpush,意思就是从左边插入队列和从右边插入队列。这就是生产者的部分,将任务插入到指定队列中。

  3、Watch命令

  watch命令可以监控一个或多个键,一旦其中一个键被修改或删除,之后的事务就不会执行,监控一直持续到exec命令(但是不能保证其他客户不修改这一键值)。

  永利电子游戏网站 1

 

  上例中执行watch命令后,事务执行前修改了key值,所以事务中命令set username xujian没有执行,exec命令返回空结果。

   注意:执行exec命令后立即取消对所有键的监控,如果不想执行事务中的命令也可以使用unwatch命令来保证下一个事务的执行不会受到影响。

由于目前一些编程语言,如PHP等,不能在内存中使用锁,或者如Java这样的,需要一下更为简单的锁校验的时候,redis分布式锁的使用就足够满足了。

  1、生产者消费者模式

  生产者生产消息放到队列中,多个消费者同时监听队列,谁先抢到消息谁就会从队列中取走消息,即对于每个消息最多只能被一个消费者拥有。

  具体的方法就是创建一个任务队列,生产者主动lpush消息,而消费者去rpop数据。但是这样存在一个问题,就是消费者需要主动去请求数据,周期性的请求会造成资源的浪费。如果可以实现一旦有新消息加入队列就通知消费者就好了,这时借助brpop命令就可以实现这样的需求。brpop和rpop命令相似,唯一区别就是当列表中没有元素时,brpop命令会一直阻塞住连接,直到有新元素加入。

BRPOP key timeout

  brpop命令接收两个参数,第一个参数key为键值,第二个参数timeout为超时时间。BRPOP命令取数据时候,如果暂时不存在数据,该命令会一直阻塞直到达到超时时间。如果timeout设置为0,那么就会无限等待下去。

  永利电子游戏网站 2

订阅/发布模型

六、存储优化

  1、精简键名和键值

  2、内部编码优化

public boolean tryLock(String lock,long expireTime){ String expire = String.valueOf(System.currentTimeMillis()   expireTime   1); Long result = jedis.setNx(lock,expire); if(result == 1L){ jedis.expire(lock, expireTime); return true; } //判断超时key可能未删掉 String currentValue = jedis.get(lock); if(Long.parseLong(currentValue)  System.currentTimeMillis()){ jedis.set(lock, expire); jedis.expire(lock, expireTime); return true; } return false;}//expire是key的值,这里是为了防止运行超时锁被其他线程拿走之后误删锁public unlock(String lock,String expire){ String value = jedis.get(lock); if(value != null && value != expire && Long.parseLong(value)  System.currentTimeMillis()) jedis.del(lock);} 

五、管道

  客户端和Redis使用TCP协议连接,不管是客户端向Redis发送命令还是Redis向客户端返回命令的执行结果,都需要经过网络传输。在执行过个命令时,每条命令都需要等待上一条命令执行完才能执行,即使命令不需要上一条命令的执行结果。

  Redis的底层通信协议对管道提供了支持,通过管道可以一次性发送多条命令并在执行完后一次性将结果返回,当一组命令中每条命令都不依赖之前命令的执行结果时就可以将这组命令一起通过管道发出。管道通过减少客户端与Redis的通信次数来实现降低往返时延累计值的目的。

分布式锁

  2、发布者订阅者模式

  发布者生产消息放到队列里,多个监听队列的订阅者都会受到同一份消息。

  生产者使用下面命令来发布消息:

PUBLISH CHANNEL MESSAGE

  订阅者通过下面的命令来订阅消息,执行subscribe命令后,客户端进入订阅状态,处于此状态的客户端不能使用4个属于“发布/订阅”模型的命令之外的命令。另外,可以使用subscribe channel1.1 channel1.2 ... 同时订阅多个频道。

SUBSCRIBE CHANNEL

  永利电子游戏网站 3

  3、Java实现的redis的消息队列

  在jedis中,有对应的方法进行订阅和发布,为了传输对象,需要将对象进行序列化,并封装成字符串进行处理。

  下面我们要实现三个类,一个对应publish,一个对应subscribe,一个对应要传递的对象实体类:

  实体类:

import java.io.Serializable;
/**
 * 实体类
 * 封装消息
 * @author Administrator
 *
 */
public class Message implements Serializable
{
    private static final long serialVersionUID = 1L;
    private String title;
    private String content;
    public String getTitle()
    {
        return title;
    }
    public void setTitle(String title)
    {
        this.title = title;
    }
    public String getContent()
    {
        return content;
    }
    public void setContent(String content)
    {
        this.content = content;
    }
}

  Publish类:

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import redis.clients.jedis.Jedis;
/**
 * 发布者,用于发布消息
 * @author Administrator
 *
 */
public class TestPub
{
    public static void main(String[] args)
    {
        Jedis jedis = new Jedis("127.0.0.1");
        try
        {
            Message message = new Message();
            message.setTitle("体育新闻");
            message.setContent("著名NBA球星科比退役了!");
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(message);
            String msg1 = baos.toString("ISO-8859-1");

            jedis.publish("foo", msg1);
        } catch (Exception e)
        {
            e.printStackTrace();
        }
        jedis.close();
    }
}

  Subscribe类:

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPubSub;
/**
 * 订阅者,用于接收消息
 * @author Administrator
 *
 */
public class TestSub
{
    public static void main(String[] args)
    {
        Jedis jedis = new Jedis("127.0.0.1");
        JedisPubSub jedisPubSub = new JedisPubSub() 
        {
            @Override
            public void onUnsubscribe(String channel, int subscribedChannels)
            {
            }

            @Override
            public void onSubscribe(String channel, int subscribedChannels)
            {
            }

            @Override
            public void onPUnsubscribe(String pattern, int subscribedChannels)
            {
            }

            @Override
            public void onPSubscribe(String pattern, int subscribedChannels)
            {
            }

            @Override
            public void onPMessage(String pattern, String channel, String message)
            {
            }

            @Override
            public void onMessage(String channel, String message)
            {
                try
                {
                    ByteArrayInputStream bis = new ByteArrayInputStream(message.getBytes("ISO-8859-1"));
            // 此处指定字符集将字符串编码成字节数组,此处的字符集需要与发布时的字符集保持一致
                    ObjectInputStream ois = new ObjectInputStream(bis);
                    Message  message2= (Message) ois.readObject();
                    System.out.println(message2.getTitle() "n" message2.getContent());
                } catch (Exception e)
                {
                    e.printStackTrace();
                } finally
                {

                }
            }
        };
        jedis.subscribe(jedisPubSub, "foo");
        jedis.close();
    }
}

  先执行订阅操作,然后发布者发布消息,执行结果为:

  永利电子游戏网站 4

生产者/消费者模型需要存在生产者和消费者两方,而在redis中队列的存储和获取可以作为消息队列被生产者和消费者使用,这里就不用Java代码写了,使用redis命令来说明。

七、参考资料

  1、Redis入门指南

消息队列主要应用在网络服务中异步任务的实现,redis可以充当消息队列实现生产者/消费者模型和订阅/发布模型。

4、store参数

  默认情况下,sort会直接返回排序结果,如果希望保存排序结果,可以使用store参数。

  注意:sort命令的时间复杂度是O(n mlogm),其中n表示要排序的列表(集合或有序集合)中的元素个数,m表示要返回的元素的个数。当n较大的时候sort命令的性能相对较低,并且redis在排序前会建立一个长度为n的容器来存储待排序的元素。在开发中使用sort要注意以下几点:

  1、尽可能减少待排序键中元素的数量(使n尽可能小)

  2、使用limit参数只获取需要的数据(使m尽可能小)

  3、如果要排序的数据量较大,尽可能使用store参数将结果缓存

订阅/发布模型简单来说就是由发布者向所有订阅者发送任务,任何订阅者都可以获取任务,这里redis的实现就是使用订阅命令。

  1、有序集合

  有序集合常见的使用场景是大数据排序,如游戏的玩家排行榜。

本文由永利电子游戏网站发布于计算机教程,转载请注明出处:【永利电子游戏网站】深入理解redis分布式锁和消

关键词: