springboot 多线程_redis官方推荐:SpringBoot用这个,一键多线程

Lettuce是一个可伸缩的线程安全的Redis客户端,提供了同步,异步和响应式使用方式。 如果多线程避免阻塞和事务操作(如BLPOP和MULTI / EXEC),则多个线程可共享一个连接。 Lettuce使用通信使用netty。 支持先进的Redis功能,如Sentinel,群集,管道传输,自动重新连接和Redis数据模型。

下面分享来自网易后端工程师的Lettuce的使用心得~

de7b1c6c542246e8631eca8b3713df67.png

自己整理的Java架构学习视频和大厂项目底层知识点,需要的同学欢迎私信我【资料】发给你~一起学习进步!

Lettuce在Spring boot中的配置

 @Bean(name="clusterRedisURI")    RedisURI clusterRedisURI(){        return RedisURI.builder().withHost("xxx").withPort(6954).build();    }    @Bean    ClusterClientOptions clusterClientOptions(){        return ClusterClientOptions.builder().autoReconnect(true).maxRedirects(1024).build();    }    @Bean    RedisClusterClient redisClusterClient(ClientResources clientResources, ClusterClientOptions clusterClientOptions, RedisURI clusterRedisURI){        RedisClusterClient redisClusterClient= RedisClusterClient.create(clientResources,clusterRedisURI);        redisClusterClient.setOptions(clusterClientOptions);        return redisClusterClient;    } @Bean(destroyMethod = "close")    StatefulRedisClusterConnection statefulRedisClusterConnection(RedisClusterClient redisClusterClient){        return redisClusterClient.connect();    }

基本的使用方式

 @Bean(name="clusterRedisURI")    RedisURI clusterRedisURI(){        return RedisURI.builder().withHost("xxx").withPort(6954).build();    }    @Bean    ClusterClientOptions clusterClientOptions(){        return ClusterClientOptions.builder().autoReconnect(true).maxRedirects(1024).build();    }    @Bean    RedisClusterClient redisClusterClient(ClientResources clientResources, ClusterClientOptions clusterClientOptions, RedisURI clusterRedisURI){        RedisClusterClient redisClusterClient= RedisClusterClient.create(clientResources,clusterRedisURI);        redisClusterClient.setOptions(clusterClientOptions);        return redisClusterClient;    } @Bean(destroyMethod = "close")    StatefulRedisClusterConnection statefulRedisClusterConnection(RedisClusterClient redisClusterClient){        return redisClusterClient.connect();    }

集群模式

 @Bean(name="clusterRedisURI")    RedisURI clusterRedisURI(){        return RedisURI.builder().withHost("xxx").withPort(6954).build();    }    @Bean    ClusterClientOptions clusterClientOptions(){        return ClusterClientOptions.builder().autoReconnect(true).maxRedirects(1024).build();    }    @Bean    RedisClusterClient redisClusterClient(ClientResources clientResources, ClusterClientOptions clusterClientOptions, RedisURI clusterRedisURI){        RedisClusterClient redisClusterClient= RedisClusterClient.create(clientResources,clusterRedisURI);        redisClusterClient.setOptions(clusterClientOptions);        return redisClusterClient;    } @Bean(destroyMethod = "close")    StatefulRedisClusterConnection statefulRedisClusterConnection(RedisClusterClient redisClusterClient){        return redisClusterClient.connect();    }

客户端订阅事件

客户端使用事件总线传输运行期间产生的事件;EventBus可以从客户端资源进行配置和获取,并用于客户端和自定义事件。  

如下事件可以被客户端发送:

  • 连接事件
  • 测量事件 (Lettuce命令延迟测量(CommandLatency))
  • 集群拓扑事件

订阅所有事件,并将事件输出到控制台

client.getResources().eventBus().get().subscribe(e -> {            System.out.println("client 订阅事件: " + e);        });

输出到内容有:

client 订阅事件: ConnectionActivatedEvent [/xx:49910 -> /xx:6008]client 订阅事件: ConnectionActivatedEvent [/xx:49911 -> /xx:6018]client 订阅事件: ConnectedEvent [/xx:49912 -> /xx:6018]

发布事件

用户除了可以通过事件总线订阅事件外还可以通过事件总线发布自定义事件

eventBus.publish(new Event() {           @Override           public String toString() {               return "自定义事件";           }       });

订阅到到内容如下:

client 订阅事件: 自定义事件

读写分离

lettuce master/slave模式支持读写分离,下面看看具体使用方式,只需要指定ReadFrom就可以了

@Bean(destroyMethod = "close")    StatefulRedisMasterSlaveConnection statefulRedisMasterSlaveConnection(RedisClient redisClient, RedisURI redisURI) {        StatefulRedisMasterSlaveConnection connection = MasterSlave.connect(redisClient, new Utf8StringCodec(), redisURI);        connection.setReadFrom(ReadFrom.NEAREST);        return connection;    }}

ReadFrom可选参数以及含义:

参数含义

MASTER

从master节点读取

SLAVE

从slave节点读取

MASTER_PREFERRED

从master节点读取,如果master节点不可以则从slave节点读取

SLAVE_PREFERRED

从slave节点读取,如果slave节点不可用则倒退到master节点读取

NEAREST

从最近到节点读取

下面看看源码是如何实现读写分离的,

//根据意图获取连接   public StatefulRedisConnection getConnection(Intent intent) {        if (debugEnabled) {           logger.debug("getConnection(" + intent + ")");       }       //如果readFrom不为null且是READ       if (readFrom != null && intent == Intent.READ) {           //根据readFrom配置从已知节点中选择可用节点描述           List selection = readFrom.select(new ReadFrom.Nodes() {               @Override               public List getNodes() {                   return knownNodes;               }                @Override               public Iterator iterator() {                   return knownNodes.iterator();               }           });           //如果可选择节点集合为空则抛出异常           if (selection.isEmpty()) {               throw new RedisException(String.format("Cannot determine a node to read (Known nodes: %s) with setting %s",                       knownNodes, readFrom));           }           try {               //遍历所有可用节点               for (RedisNodeDescription redisNodeDescription : selection) {                   //获取节点连接                   StatefulRedisConnection readerCandidate = getConnection(redisNodeDescription);                   //如果节点连接不是打开到连接则继续查找下一个连接                   if (!readerCandidate.isOpen()) {                       continue;                   }                   //返回可用连接                   return readerCandidate;               }               //如果没有找到可用连接,默认返回第一个               return getConnection(selection.get(0));           } catch (RuntimeException e) {               throw new RedisException(e);           }       }       //如果没有配置readFrom或者不是READ 则返回master连接       return getConnection(getMaster());   }

自定义负载均衡

通过上文的读写分离实现代码可以发现,只需要readFrom select方法每次返回的list都是随机无序的就可以实现随机的负载均衡

public class Sharded< C extends StatefulRedisConnection,V> {     private TreeMap nodes;    private final Hashing algo = Hashing.MURMUR_HASH;    private final Map resources = new LinkedHashMap<>();    private RedisClient redisClient;    private String password;    private Set sentinels;    private RedisCodec codec;     public Sharded(List masters, RedisClient redisClient, String password, Set sentinels, RedisCodec codec) {        this.redisClient = redisClient;        this.password = password;        this.sentinels = sentinels;        this.codec = codec;        initialize(masters);    }     private void initialize(List masters) {        nodes = new TreeMap<>();         for (int i = 0; i != masters.size(); ++i) {            final String master = masters.get(i);            for (int n = 0; n < 160; n++) {                nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), master);            }            RedisURI.Builder builder = RedisURI.builder();            for (HostAndPort hostAndPort : sentinels) {                builder.withSentinel(hostAndPort.getHostText(), hostAndPort.getPort());            }             RedisURI redisURI = builder.withPassword(password).withSentinelMasterId(master).build();            resources.put(master, MasterSlave.connect(redisClient, codec, redisURI));        }     }     public StatefulRedisConnection getConnectionBy(String key) {        return resources.get(getShardInfo(SafeEncoder.encode(key)));    }     public Collection getAllConnection(){        return Collections.unmodifiableCollection(resources.values());    }     public String getShardInfo(byte[] key) {        SortedMap tail = nodes.tailMap(algo.hash(key));        if (tail.isEmpty()) {            return nodes.get(nodes.firstKey());        }        return tail.get(tail.firstKey());    }      public void close(){       for(StatefulRedisConnection connection:  getAllConnection()){            connection.close();        }    }     private static  class SafeEncoder {          static byte[] encode(final String str) {            try {                if (str == null) {                    throw new IllegalArgumentException("value sent to redis cannot be null");                }                return str.getBytes("UTF-8");            } catch (UnsupportedEncodingException e) {                throw new RuntimeException(e);            }        }    }    private interface Hashing {        Hashing MURMUR_HASH = new MurmurHash();         long hash(String key);         long hash(byte[] key);    }      private static  class MurmurHash implements Hashing {          static long hash64A(byte[] data, int seed) {            return hash64A(ByteBuffer.wrap(data), seed);        }           static long hash64A(ByteBuffer buf, int seed) {            ByteOrder byteOrder = buf.order();            buf.order(ByteOrder.LITTLE_ENDIAN);             long m = 0xc6a4a7935bd1e995L;            int r = 47;             long h = seed ^ (buf.remaining() * m);             long k;            while (buf.remaining() >= 8) {                k = buf.getLong();                 k *= m;                k ^= k >>> r;                k *= m;                 h ^= k;                h *= m;            }             if (buf.remaining() > 0) {                ByteBuffer finish = ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN);                // for big-endian version, do this first:                // finish.position(8-buf.remaining());                finish.put(buf).rewind();                h ^= finish.getLong();                h *= m;            }             h ^= h >>> r;            h *= m;            h ^= h >>> r;             buf.order(byteOrder);            return h;        }         public long hash(byte[] key) {            return hash64A(key, 0x1234ABCD);        }         public long hash(String key) {            return hash(SafeEncoder.encode(key));        }    }    }

来源:网易工程师--张伟

有任何问题欢迎留言交流~


整理总结不易,如果觉得这篇文章有意思的话,欢迎转发、收藏,给我一些鼓励~

有想看的内容或者建议,敬请留言!

最近利用空余时间整理了一些精选Java架构学习视频和大厂项目底层知识点,需要的同学欢迎私信我发给你~一起学习进步!有任何问题也欢迎交流~

Java日记本,每日存档超实用的技术干货学习笔记,每天陪你前进一点点~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/340009.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle查询最高一条记录,oracle 查询已有记录,上一条记录,下一条记录

oracle可以使用 lead、lag 函数来查询已有记录的下一条、上一条记录。表结构如下&#xff1a;如要查询Staffno是6-1102的前一条记录select * from staff where staff_no(select c.p from (select staff_no,lag(staff_no,1,0) over (order by staff_no) as p from staff) c wh…

使用Maven进行增量构建

这是2020年&#xff0c;如果您要启动任何新的基于Java的项目&#xff0c;则应优先选择gradle&#xff0c;但由于某些原因&#xff0c;如果您仍然对Maven感兴趣&#xff0c;那么您可能会发现这篇文章有用。 Maven Java / scala编译器插件对增量编译提供了不错的支持&#xff0c…

夏末浅笑_2014年夏末大Java新闻

夏末浅笑正如即将到来的JavaOne那样 &#xff0c;最近在Java社区中已经有很多重大新闻。 这篇文章简要地引用了其中的三个项目&#xff08;Java SE 8更新&#xff0c;Java SE 9和Java EE 8&#xff09;&#xff0c;并对我发现是我在类路径/类加载器问题上见过的更清晰的文章之一…

php删除菜单栏,如何删除WordPress站点健康状态面板和菜单项

下面由WordPress教程栏目给大家介绍删除WordPress站点健康状态面板和菜单项的方法&#xff0c;希望对需要的朋友有所帮助&#xff01;删除 WordPress 站点健康状态面板和菜单项WordPress站点健康功能始于 5.2 版&#xff0c;如不想显示这玩意&#xff0c;可以使用本文的方法删除…

colspan会影响内部单元格宽度失效_电感失效分析

电感失效分析01电感的作用我们通常所说的电感指的是电感器件&#xff0c;它是用绝缘导线(例如漆包线,沙包线等)绕制而成的电磁感应元件。在电路中&#xff0c;当电流流过导体时&#xff0c;会产生电磁场&#xff0c;电磁场的大小除以电流的大小就是电感。电感是衡量线圈产生电磁…

linux清空redis命令,使用Linux管道批量删除Redis的key

------------------------------------------------------Redis并没有提供批量删除记录的方法&#xff0c;这有时候很不方便&#xff0c;特别是重新初始化数据的时候。一般有两种做法&#xff1a;如果业务场景明确&#xff0c;可以通过DBID进行区分&#xff0c;Redis默认的DBID…

ZeptoN正在将程序放入Java

1.简介 Java编程语言或“ Java”于1995年引入。然而&#xff0c;在近25年的时间里&#xff0c;它增加了最初不是核心语言所具有的功能。 此类功能包括枚举&#xff0c;泛型&#xff0c;对基本C样式功能开关语句的许多增强&#xff0c;断言等。 Java是一种编程语言&#xff0c;随…

深入理解python面向对象_转:Python3 面向对象,较为深入的两个理解

一&#xff0c;1. 类的声明和创建对于 Python 函数来说&#xff0c;声明与定义类没什么区别&#xff0c;因为他们是同时进行的&#xff0c;定义(类体)紧跟在声明(含 class 关键字的头行[header line])和可选(但总是推荐使用)的文档字符串后面。同时&#xff0c;所有的方法也必须…

linux循环脚本while循环,Shell脚本while、until循环语句简明教程

一、while循环while循环用于不断执行一系列命令&#xff0c;也用于从输入文件中读取数据&#xff1b;命令通常为测试条件。其格式为&#xff1a;while 命令docommand1command2...commandNdone命令执行完毕&#xff0c;控制返回循环顶部&#xff0c;从头开始直至测试条件为假。以…

分披萨问题_比萨问题–建造者与装饰者

分披萨问题问题陈述 我们需要为一家披萨公司构建软件&#xff0c;该公司想要准备不同类型的披萨&#xff0c;例如鸡肉披萨&#xff0c;扁平面包&#xff0c;意大利辣香肠披萨和特制奶酪&#xff0c;并在上面放些配料。 让我们尝试看看哪种设计模式适合该问题说明以及在哪种情况…

用python随机生成5000个网址_使用Python脚本生成随机IP的简单方法

需求在某应用中&#xff0c;需要根据一定的规则生成随机的IP地址&#xff0c;规则类似于192.168.11.0/24这样的CIDR形式给出。实现经过艰苦卓绝的调试&#xff0c;下面的代码是可以用的&#xff1a;RANDOM_IP_POOL[192.168.10.222/0]def __get_random_ip():str_ip RANDOM_IP_P…

linux应用程序安装PPT免费序,linux下应用程序安装的总结

linux下应用程序安装的总结上一篇 / 下一篇 2009-04-20 14:45:37 / 个人分类&#xff1a;技术我解查看( 203 ) / 评论( 0 ) / 评分( 0 / 0 )一、常用的Linux应用软件的安装包有2种&#xff1a;1. tar包&#xff0c;如software-1.2.3-1.tar.gz。它是使用linux系统的打包工具tar打…

Quarkus入门

Quarkus – 一个为OpenJDK HotSpot和GraalVM量身定制的Kubernetes本机Java堆栈&#xff0c;它是从最佳Java库和标准中精制而成的。 –是一个容器优先的框架&#xff0c;针对快速启动时间和低内存消耗进行了优化。 该框架基于许多流行的Java库构建&#xff0c;并且为构建标准RES…

vmware6.5.2序列号_教你如何查询苹果序列号,查询是否为官换机、激活时间等

如何查询你刚买的苹果设备是否为官换机&#xff1f;或想知道它的激活日期&#xff1f;保修时间&#xff1f;那么赶紧收藏本篇教程吧&#xff01;众所周知&#xff0c;在苹果官网查询苹果设备&#xff0c;是查询不到具体的信息&#xff08;比如激活日期、保修日期、是否为官换机…

linux 几个文件夹作用,linux下每一个文件夹的作用.docx

目录结构及主要内容“/"根冃录部分有以F子冃录&#xff1a;/usr 口录包含所有的命令、程序库、文档和英它文件。这些文件在正常操作中不会被改变的。这个目录也包含你的Linux 发行版本的主要的应用程序&#xff0c;辔如&#xff0c;Netscape0/var目录包含在1E常操作中被改…

参数化测试 junit_JUnit中的参数化测试运行器

参数化测试 junit我们都有书面的单元测试&#xff0c;其中在一个测试中针对不同的可能的输入输出组合进行测试。 让我们以一个简单的斐波那契数列示例为例&#xff0c;看看它是如何完成的。 以下代码针对提到的元素数量计算斐波那契数列&#xff1a; import java.math.BigInt…

python中dataframe合并列名日期到季度_python – 如何在特定日期范围内的pandas列DataFrame中对某些值求和...

我有一个大型的DataFrame,看起来像这样&#xff1a;df UPC Unit_Sales Price Price_Change Date0 22 15 1.99 NaN 2017-10-101 22 7 2.19 True 2017-10-122 22 6 2.19 NaN 2017-10-133 22 7 1.99 True 2017-10-164 22 4 1.99 NaN 2017-10-175 35 15 3.99 NaN 2017-10-096 35 17…

linux环境变量自动配置,Linux进入系统时自动配置 环境变量的要领

用Export命令能够配置环境变量&#xff0c;但是假如每回进入系统之后都要重新配置一遍环境变量就很烦人。Linux给大众提供了自动配置环境变量的要领&#xff0c;那就是修改.bashrc 文件。 通常说来&#xff0c;有两个文件能够提供这种“进入系统时自动配置”的功能&#xff0c;…

金融模型python_盘点金融领域里常用的深度学习模型

作者 | Sonam Srivastava &#xff0c;译者 | sambodhi &#xff0c;编辑 | Vincent &#xff0c;AI前线出品&#xff5c; ID&#xff1a;ai-front在今天我们发布的这篇文章中&#xff0c;作者 Sonam Srivastava 介绍了金融中的三种深度学习用例及这些模型优劣的证据。我们跟随…

Qute模板与Quarkus

Quarkus附带了自己的模板引擎Qute&#xff0c;该引擎已经包含了一组有趣的功能。 在下面的视频中&#xff0c;我将展示基于动作的MVC与JAX-RS的基本用法&#xff0c;该动作将我们的请求转发到HTML模板。 要自己尝试Qute&#xff0c;请查看以下资源&#xff1a; 示例项目 Qut…