首页 文章 精选 留言 我的

精选列表

搜索[超融合软件],共10000篇文章
优秀的个人博客,低调大师

mybatis-mp 之简单省力方法,超实用!!!

省力写法-必看 这些都是作者推荐的省心、省力、省事写法 指定数据库类型 虽然 mybatis-mp 支持自动识别,但是具有一定损耗,能加尽量加上 mybatis: configuration: databaseId: MYSQL select 、from、returnType 都可以省略 假如 只select 当前Mapper的实体类:可不写 select 假如 from的是当前Mapper的实体类:可不写 from 假如 返回的是当前Mapper的实体类:可不写 returnType SysUser sysUser = QueryChain.of(sysUserMapper) .eq(SysUser::getId,1) .get(); 一键忽略null,忽略空字符串,自动对字符串进行去空格 SysUser sysUser = QueryChain.of(sysUserMapper) // 忽略 null 条件参数 // 忽略 空字符串 条件参数 // 对字符串进行trim 去空格操作 .forSearch(true) .eq(SysUser::getUserName, null ) .eq(SysUser::getUserName, "" ) .eq(SysUser::getUserName," admin ") .get(); 更多说明:前往 这里 查看 手动优雅忽略条件 Integer id= XXX; String userName= YYY; SysUser sysUser = QueryChain.of(sysUserMapper) .select(SysUser.class,SysRole.class) .from(SysUser.class) .eq(SysUser::getId,id,Objects::nonNull) .like(SysUser::getUserName,userName,StringUtils::isNotBlank) .get(); join 可不用再写on条件 配合 @ForeignKey 注解 可以不用再写on条件 @Data @Table public class SysUser { @TableId private Integer id; private String userName; private String password; @ForeignKey(SysRole.class) private Integer roleId; private LocalDateTime createTime; } public class Demo { @Autowired private SysUserMapper sysUserMapper; public void page() { Pager pager= QueryChain.of(sysUserMapper) .select(SysUser.class) .from(SysUser.class) .join(SysUser.class,SysRole.class) .like(SysUser::getUserName,"abc") .returnType(SysUser.class) .paging(Pager.of(1)); } } 简单自动select列 select(实体类.class) 或 select(VO.class) 都可以自定帮你select所需要的列 public class Demo { @Autowired private SysUserMapper sysUserMapper; public void page() { Pager pager= QueryChain.of(sysUserMapper) .select(SysUser.class) // or select(SysUserVO.class) .from(SysUser.class) .like(SysUser::getUserName,"abc") .returnType(SysUser.class) .paging(Pager.of(1)); } } 结果映射 - VO自动映射 - 如何得到不同的类型的结果? 使用 returnType(XX.class)方法,设置你想要的结果的类型 XX类型 可以是 简单的基本类型包装类,实体类,VO类 实体类:依赖实体类注解 前往 @Table 查看 VO: 依赖VO注解 前往 @ResultEntity 查看 VO映射 - 构建复杂的结构类 mybatis-mp 实现 结果自动映射(多复杂的都可以映射) 重复列名 根本无需担心 可以构建 多重内嵌结构 例如: @ResultEntity(A对应实体类.class) class A { @NestedResultEntity(target=(B对应实体类.class) B b; } class B { @NestedResultEntity(target=(C对应实体类.class) List cs; } class C { Integer id; } 更多更复杂的映射,前往 VO映射注解 查看 链路操作 - connect方法 - 拿到自己的句柄 List list=QueryChain.of(sysUserMapper) .select(SysUser::getId,SysUser::getUserName,SysUser::getRole_id) .from(SysUser.class) .connect(query->{ query.exists(SubQuery.create() .select1() .from(SysUser.class) .eq(SysUser::getId,query.$(SysUser::getId)) .isNotNull(SysUser::getPassword) .limit(1) ); }) .list();

优秀的个人博客,低调大师

.NET 超流行框架 Furion v4.8.7.9 发布

项目信息 Gitee:https://gitee.com/dotnetchina/Furion Github:https://github.com/MonkSoul/Furion 文档:https://dotnetchina.gitee.io/furion 本期亮点 1.Sql高级代理支持IEnumerable<T>、T[]和Tuple<(IEnumerable<T>, T[], T, List<T>)混合返回值 public interface ISql : ISqlDispatchProxy { [SqlExecute("select * from person where id = @id")] Person GetPerson(int id); [SqlExecute("select * from person where id = @id")] Task<Person> GetPersonAsync(int id); // 异步 // ---------------------------------------- [SqlExecute("select * from person")] List<Person> GetPersons(); [SqlExecute("select * from person")] Task<List<Person>> GetPersonsAsync(); // 异步 // ---------------------------------------- [SqlExecute("select * from person")] Person[] GetPersons(); [SqlExecute("select * from person")] Task<Person[]> GetPersonsAsync(); // 异步 // ---------------------------------------- [SqlExecute("select * from person")] IEnumerable<Person> GetPersons2(); [SqlExecute("select * from person")] Task<IEnumerable<Person>> GetPersons2Async(); // 异步 // ---------------------------------------- // 更复杂的组合 [SqlExecute(@" select * from person where id = 1; select * from person; select * from person where id > 0; select * from person where id > 0; ")] (Person, List<Person>, Person[], IEnumerable<Person>) GetPersons(); // 更复杂的组合 [SqlExecute(@" select * from person where id = 1; select * from person; select * from person where id > 0; select * from person where id > 0; ")] Task<(Person, List<Person>, Person[], IEnumerable<Person>)> GetPersonsAsync(); // 异步 } 2. 定时任务支持Http作业 HTTP请求作业通常用于定时请求/访问互联网地址。 services.AddSchedule(options => { options.AddHttpJob(request => { request.RequestUri = "https://www.chinadot.net"; request.HttpMedhod = HttpMethod.Get; // request.Body = "{}"; // 设置请求报文体 }, Triggers.PeriodSeconds(5)); }); 作业执行日志如下: info: 2023-03-11 11:05:36.3616747 +08:00 星期六 L System.Logging.ScheduleService[0] #1 Schedule hosted service is running. info: 2023-03-11 11:05:36.3652411 +08:00 星期六 L System.Logging.ScheduleService[0] #1 Schedule hosted service is preloading... info: 2023-03-11 11:05:36.5172940 +08:00 星期六 L System.Logging.ScheduleService[0] #1 The <job1_trigger1> trigger for scheduler of <job1> successfully appended to the schedule. info: 2023-03-11 11:05:36.5189296 +08:00 星期六 L System.Logging.ScheduleService[0] #1 The scheduler of <job1> successfully appended to the schedule. warn: 2023-03-11 11:05:36.5347816 +08:00 星期六 L System.Logging.ScheduleService[0] #1 Schedule hosted service preload completed, and a total of <1> schedulers are appended. warn: 2023-03-11 11:05:41.5228138 +08:00 星期六 L System.Logging.ScheduleService[0] #15 Schedule hosted service will sleep <4970> milliseconds and be waked up at <2023-03-11 11:05:46.486>. info: 2023-03-11 11:05:41.5542865 +08:00 星期六 L System.Net.Http.HttpClient.HttpJob.LogicalHandler[100] #9 Start processing HTTP request GET https://www.chinadot.net/ info: 2023-03-11 11:05:41.5589056 +08:00 星期六 L System.Net.Http.HttpClient.HttpJob.ClientHandler[100] #9 Sending HTTP request GET https://www.chinadot.net/ info: 2023-03-11 11:05:44.1305461 +08:00 星期六 L System.Net.Http.HttpClient.HttpJob.ClientHandler[101] #8 Received HTTP response headers after 2566.7836ms - 200 info: 2023-03-11 11:05:44.1343977 +08:00 星期六 L System.Net.Http.HttpClient.HttpJob.LogicalHandler[101] #8 End processing HTTP request after 2584.2327ms - 200 info: 2023-03-11 11:05:48.6475959 +08:00 星期六 L System.Logging.ScheduleService[0] #4 Received HTTP response body with a length of <63639> output as follows - 200 <!DOCTYPE html><html><head> <title>dotNET China | 让 .NET 开发更简单,更通用,更流行</title> ...... </body></html> 本期更新 新特性 [新增]定时任务HTTP作业,支持定时请求互联网URL地址4.8.7.7⏱️2023.03.1101d4466 [新增]定时任务作业触发器Trigger执行结果Result和执行耗时ElapsedTime属性4.8.7.7⏱️2023.03.1101d4466 [新增]定时任务作业看板支持查看作业触发器执行结果Result和执行耗时ElapsedTime属性4.8.7.7⏱️2023.03.1101d4466 [新增] 定时任务休眠时长和唤醒时机日志输出4.8.7.6⏱️2023.03.08#I6LANE [新增]Sql高级拦截支持返回IEnumerable<T>和T[]类型值4.8.7.5⏱️2023.03.07f2ca2d3 [新增].m3u8和.ts文件类型MIME支持4.8.7.5⏱️2023.03.07#I6KKEM [新增] 审计日志LoggingMonitor支持对参数贴[SuppressMonitor]特性跳过记录4.8.7.3⏱️2023.03.01#I6IVGW [新增] 审计日志LoggingMonitor监听TraceId、ThreadId、Accept-Language4.8.7.1⏱️2023.02.27df35201 [新增] 规范化结果UnifyContext.GetSerializerSettings(string)静态方法4.8.7.1⏱️2023.02.27#I6HM7T 突破性变化 [升级]适配.NET8 Preview.14.8.7⏱️2023.02.22 [升级]脚手架支持创建.NET8 Preview.1项目4.8.7⏱️2023.02.22 问题修复 [修复] 定时任务服务在停止进程时会卡住30秒问题4.8.7.8⏱️2023.03.13#I6MI9I#I6MHOU [修复] 定时任务看板删除不存在的作业触发器出现空异常4.8.7.7⏱️2023.03.1101d4466 [修复] 日志消息没有处理\n换行符对齐问题4.8.7.6⏱️2023.03.10759bcc5 [修复] 审计日志LoggingMonitor对特定参数贴有[FromServices]特性依旧记录问题4.8.7.3⏱️2023.03.0117b134e [修复]Swagger接口排序同时指定Tag和Order之后无效4.8.7.2⏱️2023.03.01#I6IQDI#I6IP66 其他更改 [调整]定时任务查看作业触发器运行记录由保存10条改为5条4.8.7.7⏱️2023.03.0701d4466 [调整] 脚手架模板,默认启用主流文件类型MIME支持4.8.7.5⏱️2023.03.07e35cdab [调整] 审计日志LoggingMonitor返回值泛型字符串显示格式4.8.7.1⏱️2023.02.27df35201 文档 [新增]ASP.NET 8 集成文档 [新增].NET7 升级 .NET8文档 [更新] 定时任务文档、中间件文档、规范化结果文档、动态WebAPI文档、日志记录文档、事件总线文档、虚拟文件系统文档、Sql高级代理文档、数据库实体文档、任务队列文档 贡献者 kingling (@kinglinglive)!732!729 ksmy (@ksmy)!731 handsome_by (@handsomeboyyl)!727

优秀的个人博客,低调大师

【超详细】手把手教你搭建MongoDB集群

MongoDB集群搭建 MongoDB集群简介 mongodb 集群搭建的方式有三种: 主从备份(Master - Slave)模式,或者叫主从复制模式。 副本集(Replica Set)模式 分片(Sharding)模式 其中,第一种方式基本没什么意义,官方也不推荐这种方式搭建。另外两种分别就是副本集和分片的方式。 Mongo分片高可用集群搭建 概述 ​ 为解决mongodb在replica set每个从节点上面的数据库均是对数据库的全量拷贝,从节点压力在高并发大数据量的场景下存在很大挑战,同时考虑到后期mongodb集群的在数据压力巨大时的扩展性,应对海量数据引出了分片机制。 什么是分片 ​ 分片是将数据库进行拆分,将其分散在不同的机器上的过程,无需功能强大的服务器就可以存储更多的数据,处理更大的负载,在总数据中,将集合切成小块,将这些块分散到若干片中,每个片只负载总数据的一部分,通过一个知道数据与分片对应关系的组件mongos的路由进程进行操作。 基础组件 其利用到了四个组件:mongos,config server,shard,replica set mongos ​ 数据库集群请求的入口,所有请求需要经过mongos进行协调,无需在应用层面利用程序来进行路由选择,mongos其自身是一个请求分发中心,负责将外部的请求分发到对应的shard服务器上,mongos作为统一的请求入口,为防止mongos单节点故障,一般需要对其做HA(高可用,Highly Available缩写)。 config server ​ 配置服务器,存储所有数据库元数据(分片,路由)的配置。mongos本身没有物理存储分片服务器和数据路由信息,只是缓存在内存中来读取数据,mongos在第一次启动或后期重启时候,就会从config server中加载配置信息,如果配置服务器信息发生更新会通知所有的mongos来更新自己的状态,从而保证准确的请求路由,生产环境中通常也需要多个config server,防止配置文件存在单节点丢失问题。 shard ​ 在传统意义上来讲,如果存在海量数据,单台服务器存储1T压力非常大,考虑到数据库的硬盘,网络IO,还有CPU,内存的瓶颈,如果多台进行分摊1T的数据,到每台上就是可估量的较小数据,在mongodb集群只要设置好分片规则,通过mongos操作数据库,就可以自动把对应的操作请求转发到对应的后端分片服务器上。 replica set ​ 在总体mongodb集群架构中,对应的分片节点,如果单台机器下线,对应整个集群的数据就会出现部分缺失,这是不能发生的,因此对于shard节点需要replica set来保证数据的可靠性,生产环境通常为2个副本+1个仲裁。 整体架构 整体架构涉及到15个节点,我们这里使用Docker容器进行部署 那么我们先来总结一下我们搭建一个高可用集群需要多少个Mongo mongos: 3台 configserver : 3台 shard : 3片; 每个分片由三个节点构成 容器部署情况 角色 端口 暴漏端口 描述 角色 config-server1 27017 -- 配置节点1 -- config-server2 27017 -- 配置节点2 -- config-server3 27017 -- 配置节点3 -- mongos-server1 27017 30001 路由节点1 -- mongos-server2 27017 30002 路由节点2 -- mongos-server3 27017 30003 路由节点3 -- shard1-server1 27017 -- 分片1节点1 Primary shard1-server2 27017 -- 分片1节点2 Secondry shard1-server3 27017 -- 分片1节点3 Arbiter shard2-server1 27017 -- 分片2节点1 Primary shard2-server2 27017 -- 分片2节点2 Secondry shard2-server3 27017 -- 分片2节点3 Arbiter shard3-server1 27017 -- 分片3节点1 Primary shard3-server2 27017 -- 分片3节点2 Secondry shard3-server3 27017 -- 分片3节点3 Arbiter 整体架构预览 基础环境准备 安装Docker 本次使用Docker环境进行搭建,需要提前准备好Docker环境 创建Docker网络 因为需要使用Docker搭建MongoDB集群,所以先创建Docker网络 docker network create mongo-cluster docker network ls 搭建ConfigServer副本集 我们先来搭建ConfigServer的副本集,这里面涉及到三个节点,我们需要创建配置文件以及启动容器 创建挂载目录 我们需要创建对应的挂载目录来存储配置文件以及日志文件 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/config-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/config-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/config-server/logs/{1..3} 创建密钥文件 因为我们知道搭建的话一定要高可用,而且一定要权限,这里mongo之间通信采用秘钥文件,所以我们先进行生成密钥文件 # 创建密钥文件 openssl rand -base64 756 > /tmp/mongo-cluster/config-server/conf/mongo.key # 设置 chmod 600 /tmp/mongo-cluster/config-server/conf/mongo.key 创建配置文件 因为由多个容器,配置文件是一样的,我们只需要创建一个配置文件,其他的容器统一读取该配置文件即可 echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: configsvr #副本集名称 sharding: clusterRole: configsvr # 集群角色,这里配置的角色是配置节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/config-server/conf/mongo.conf 启动容器 启动config-server1 docker run --name config-server1 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/config-server:/data/configdb \ -v /tmp/mongo-cluster/config-server/data/1:/data/db \ -v /tmp/mongo-cluster/config-server/logs/1:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动config-server2 docker run --name config-server2 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/config-server:/data/configdb \ -v /tmp/mongo-cluster/config-server/data/2:/data/db \ -v /tmp/mongo-cluster/config-server/logs/2:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动config-server3 docker run --name config-server3 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/config-server:/data/configdb \ -v /tmp/mongo-cluster/config-server/data/3:/data/db \ -v /tmp/mongo-cluster/config-server/logs/3:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 初始化config-server 登录容器 进入第一台容器 docker exec -it config-server1 bash mongo -port 27017 执行命令 执行以下命令进行MongoDB容器的初始化 rs.initiate( { _id: "configsvr", members: [ { _id : 1, host : "config-server1:27017" }, { _id : 2, host : "config-server2:27017" }, { _id : 3, host : "config-server3:27017" } ] } ) 如果出现OK表示MongoDB配置服务器已经初始化成功 创建用户 因为我们需要对用户进行权限管理,我们需要创建用户,这里为了演示,我们创建超级用户 权限是root use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) 这样就在MongoDB的admin数据库添加了一个用户名为root 密码是root的用户 搭建Shard分片组 由于mongos是客户端,所以我们先搭建好config以及shard之后再搭建mongos。 创建挂载目录 我们先创建挂载目录 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/logs/{1..3} 搭建shard1分片组 在同一台服务器上初始化一组分片 创建密钥文件 因为集群只需要一个密钥文件,我们可以将config-server中的密钥文件复制过来 cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard1-server/conf/ 配置配置文件 因为有多个容器,配置文件是一样的,我们只需要创建一个配置文件,其他的容器统一读取该配置文件即可 echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard1 #复制集名称是 shardsvr sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard1-server/conf/mongo.conf 启动shard1-server1 docker run --name shard1-server1 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard1-server:/data/configdb \ -v /tmp/mongo-cluster/shard1-server/data/1:/data/db \ -v /tmp/mongo-cluster/shard1-server/logs/1:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard1-server2 docker run --name shard1-server2 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard1-server:/data/configdb \ -v /tmp/mongo-cluster/shard1-server/data/2:/data/db \ -v /tmp/mongo-cluster/shard1-server/logs/2:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard1-server3 docker run --name shard1-server3 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard1-server:/data/configdb \ -v /tmp/mongo-cluster/shard1-server/data/3:/data/db \ -v /tmp/mongo-cluster/shard1-server/logs/3:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 初始化shard1分片组 并且制定第三个副本集为仲裁节点 docker exec -it shard1-server1 bin/bash mongo -port 27017 登录后进行初始化节点,这里面arbiterOnly:true是设置为仲裁节点 #进行副本集配置 rs.initiate( { _id : "shard1", members: [ { _id : 0, host : "shard1-server1:27017" }, { _id : 1, host : "shard1-server2:27017" }, { _id : 2, host : "shard1-server3:27017",arbiterOnly:true } ] } ); 显示OK即副本集创建成功 创建用户 因为我们需要对用户进行权限管理,我们需要创建用户,这里为了演示,我们创建超级用户 权限是root use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) 查看节点信息 rs.isMaster() 搭建shard2分片组 创建密钥文件 因为集群只需要一个密钥文件,我们可以将config-server中的密钥文件复制过来 cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard2-server/conf/ 配置配置文件 因为有多个容器,配置文件是一样的,我们只需要创建一个配置文件,其他的容器统一读取该配置文件即可 echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard2 #复制集名称是 shard2 sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard2-server/conf/mongo.conf 启动shard2-server1 docker run --name shard2-server1 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard2-server:/data/configdb \ -v /tmp/mongo-cluster/shard2-server/data/1:/data/db \ -v /tmp/mongo-cluster/shard2-server/logs/1:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard2-server2 docker run --name shard2-server2 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard2-server:/data/configdb \ -v /tmp/mongo-cluster/shard2-server/data/2:/data/db \ -v /tmp/mongo-cluster/shard2-server/logs/2:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard2-server3 docker run --name shard2-server3 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard2-server:/data/configdb \ -v /tmp/mongo-cluster/shard2-server/data/3:/data/db \ -v /tmp/mongo-cluster/shard2-server/logs/3:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 初始化shard2分片组 登录节点后进行初始化分片2 docker exec -it shard2-server1 bin/bash mongo -port 27017 执行下面的命令进行初始化分片2,arbiterOnly:true参数是设置为仲裁节点 #进行副本集配置 rs.initiate( { _id : "shard2", members: [ { _id : 0, host : "shard2-server1:27017" }, { _id : 1, host : "shard2-server2:27017" }, { _id : 2, host : "shard2-server3:27017",arbiterOnly:true } ] } ); 返回ok就表示 创建用户 因为我们需要对用户进行权限管理,我们需要创建用户,这里为了演示,我们创建超级用户 权限是root use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) 搭建shard3分片组 创建密钥文件 因为集群只需要一个密钥文件,我们可以将config-server中的密钥文件复制过来 cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard3-server/conf/ 配置配置文件 因为有多个容器,配置文件是一样的,我们只需要创建一个配置文件,其他的容器统一读取该配置文件即可 echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard3 #复制集名称是 shard3 sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard3-server/conf/mongo.conf 启动shard3-server1 docker run --name shard3-server1 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard3-server:/data/configdb \ -v /tmp/mongo-cluster/shard3-server/data/1:/data/db \ -v /tmp/mongo-cluster/shard3-server/logs/1:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard3-server2 docker run --name shard3-server2 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard3-server:/data/configdb \ -v /tmp/mongo-cluster/shard3-server/data/2:/data/db \ -v /tmp/mongo-cluster/shard3-server/logs/2:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动shard3-server3 docker run --name shard3-server3 -d \ --net=mongo-cluster \ --privileged=true \ -v /tmp/mongo-cluster/shard3-server:/data/configdb \ -v /tmp/mongo-cluster/shard3-server/data/3:/data/db \ -v /tmp/mongo-cluster/shard3-server/logs/3:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 初始化shard3分片组 登录节点后进行初始化分片2 docker exec -it shard3-server1 bin/bash mongo -port 27017 执行下面的命令进行初始化分片3,arbiterOnly:true参数是设置为仲裁节点 #进行副本集配置 rs.initiate( { _id : "shard3", members: [ { _id : 0, host : "shard3-server1:27017" }, { _id : 1, host : "shard3-server2:27017" }, { _id : 2, host : "shard3-server3:27017",arbiterOnly:true } ] } ); 创建用户 因为我们需要对用户进行权限管理,我们需要创建用户,这里为了演示,我们创建超级用户 权限是root use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) 搭建Mongos mongos负责查询与数据写入的路由,是实例访问的统一入口,是一个无状态的节点,每一个节点都可以从config-server节点获取到配置信息 创建挂载目录 我们需要创建对应的挂载目录来存储配置文件以及日志文件 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/logs/{1..3} 创建密钥文件 因为集群只需要一个密钥文件,我们可以将config-server中的密钥文件复制过来 cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/mongos-server/conf/ 创建配置文件 因为有多个容器,配置文件是一样的,我们只需要创建一个配置文件,其他的容器统一读取该配置文件即可,因为Mongos只负责路由,就不需要数据文件了,并且mongos服务是不负责认证的,需要将authorization配置项删除 echo " # 日志文件 systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip # 配置分片,这里面配置的是需要读取的配置节点的信息 sharding: configDB: configsvr/config-server1:27017,config-server2:27017,config-server3:27017 security: keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/mongos-server/conf/mongo.conf 启动mongos集群 启动mongos1 docker run --name mongos-server1 -d \ -p 30001:27017 \ --net=mongo-cluster \ --privileged=true \ --entrypoint "mongos" \ -v /tmp/mongo-cluster/mongos-server:/data/configdb \ -v /tmp/mongo-cluster/mongos-server/logs/1:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动mongos2 docker run --name mongos-server2 -d \ -p 30002:27017 \ --net=mongo-cluster \ --privileged=true \ --entrypoint "mongos" \ -v /tmp/mongo-cluster/mongos-server:/data/configdb \ -v /tmp/mongo-cluster/mongos-server/logs/2:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 启动mongos3 docker run --name mongos-server3 -d \ -p 30003:27017 \ --net=mongo-cluster \ --privileged=true \ --entrypoint "mongos" \ -v /tmp/mongo-cluster/mongos-server:/data/configdb \ -v /tmp/mongo-cluster/mongos-server/logs/3:/data/logs \ mongo --config /data/configdb/conf/mongo.conf 配置mongos-server1 因为mongos是无中心的配置,所有需要每一台都需要进行分片配置 进入容器 docker exec -it mongos-server1 /bin/bash mongo -port 27017 登录Mongos 使用前面设置的root用户密码 use admin; db.auth("root","root"); 配置分片 进行配置分片信息 sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") 配置mongos-server2 因为mongos是无中心的配置,所有需要每一台都需要进行分片配置 进入容器 docker exec -it mongos-server2 /bin/bash mongo -port 27017 登录Mongos 使用前面设置的root用户密码 use admin; db.auth("root","root"); 配置分片 进行配置分片信息 sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") 配置mongos-server3 因为mongos是无中心的配置,所有需要每一台都需要进行分片配置 进入容器 docker exec -it mongos-server3 /bin/bash mongo -port 27017 登录Mongos 使用前面设置的root用户密码 use admin; db.auth("root","root"); 配置分片 进行配置分片信息 sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") Docker-compose方式搭建 环境准备 初始化目录脚本 # 创建config-server 目录 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/config-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/config-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/config-server/logs/{1..3} # 创建shard-server 目录 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/shard{1..3}-server/logs/{1..3} # 创建mongos-server 目录 # 创建配置文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/conf # 创建数据文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/data/{1..3} # 创建日志文件目录 mkdir -p /tmp/mongo-cluster/mongos-server/logs/{1..3} 生成密钥文件 # 创建密钥文件 openssl rand -base64 756 > /tmp/mongo-cluster/config-server/conf/mongo.key # 设置 chmod 600 /tmp/mongo-cluster/config-server/conf/mongo.key cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard1-server/conf/ cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard2-server/conf/ cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/shard3-server/conf/ cp /tmp/mongo-cluster/config-server/conf/mongo.key /tmp/mongo-cluster/mongos-server/conf/ 创建配置文件 echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: configsvr #副本集名称 sharding: clusterRole: configsvr # 集群角色,这里配置的角色是配置节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/config-server/conf/mongo.conf echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard1 #复制集名称是 shardsvr sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard1-server/conf/mongo.conf echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard2 #复制集名称是 shard2 sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard2-server/conf/mongo.conf echo " # 日志文件 storage: # mongod 进程存储数据目录,此配置仅对 mongod 进程有效 dbPath: /data/db systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 # bindIp: 127.0.0.1 #绑定ip replication: replSetName: shard3 #复制集名称是 shard3 sharding: clusterRole: shardsvr # 集群角色,这里配置的角色是分片节点 security: authorization: enabled #是否开启认证 keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/shard3-server/conf/mongo.conf echo " # 日志文件 systemLog: destination: file logAppend: true path: /data/logs/mongo.log # 网络设置 net: port: 27017 #端口号 bindIp: 0.0.0.0 #绑定ip # 配置分片,这里面配置的是需要读取的配置节点的信息 sharding: configDB: configsvr/config-server1:27017,config-server2:27017,config-server3:27017 security: keyFile: /data/configdb/conf/mongo.key #keyFile路径 " > /tmp/mongo-cluster/mongos-server/conf/mongo.conf 启动服务 docker-compos配置文件 使用docker-compos方式启动Docker容器 version: '2' services: config-server1: image: mongo container_name: config-server1 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/config-server:/data/configdb - /tmp/mongo-cluster/config-server/data/1:/data/db - /tmp/mongo-cluster/config-server/logs/1:/data/logs config-server2: image: mongo container_name: config-server2 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/config-server:/data/configdb - /tmp/mongo-cluster/config-server/data/2:/data/db - /tmp/mongo-cluster/config-server/logs/2:/data/logs config-server3: image: mongo container_name: config-server3 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/config-server:/data/configdb - /tmp/mongo-cluster/config-server/data/3:/data/db - /tmp/mongo-cluster/config-server/logs/3:/data/logs shard1-server1: image: mongo container_name: shard1-server1 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard1-server:/data/configdb - /tmp/mongo-cluster/shard1-server/data/1:/data/db - /tmp/mongo-cluster/shard1-server/logs/1:/data/logs shard1-server2: image: mongo container_name: shard1-server2 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard1-server:/data/configdb - /tmp/mongo-cluster/shard1-server/data/2:/data/db - /tmp/mongo-cluster/shard1-server/logs/2:/data/logs shard1-server3: image: mongo container_name: shard1-server3 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard1-server:/data/configdb - /tmp/mongo-cluster/shard1-server/data/3:/data/db - /tmp/mongo-cluster/shard1-server/logs/3:/data/logs shard2-server1: image: mongo container_name: shard2-server1 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard2-server:/data/configdb - /tmp/mongo-cluster/shard2-server/data/1:/data/db - /tmp/mongo-cluster/shard2-server/logs/1:/data/logs shard2-server2: image: mongo container_name: shard2-server2 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard2-server:/data/configdb - /tmp/mongo-cluster/shard2-server/data/2:/data/db - /tmp/mongo-cluster/shard2-server/logs/2:/data/logs shard2-server3: image: mongo container_name: shard2-server3 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard2-server:/data/configdb - /tmp/mongo-cluster/shard2-server/data/3:/data/db - /tmp/mongo-cluster/shard2-server/logs/3:/data/logs shard3-server1: image: mongo container_name: shard3-server1 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard3-server:/data/configdb - /tmp/mongo-cluster/shard3-server/data/1:/data/db - /tmp/mongo-cluster/shard3-server/logs/1:/data/logs shard3-server2: image: mongo container_name: shard3-server2 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard3-server:/data/configdb - /tmp/mongo-cluster/shard3-server/data/2:/data/db - /tmp/mongo-cluster/shard3-server/logs/2:/data/logs shard3-server3: image: mongo container_name: shard3-server3 privileged: true networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/shard3-server:/data/configdb - /tmp/mongo-cluster/shard3-server/data/3:/data/db - /tmp/mongo-cluster/shard3-server/logs/3:/data/logs mongos-server1: image: mongo container_name: mongos-server1 privileged: true entrypoint: "mongos" networks: - mongo-cluster-network command: --config /data/configdb/conf/mongo.conf ports: - "30001:27017" volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/mongos-server:/data/configdb - /tmp/mongo-cluster/mongos-server/logs/1:/data/logs command: --config /data/configdb/conf/mongo.conf mongos-server2: image: mongo container_name: mongos-server2 privileged: true entrypoint: "mongos" networks: - mongo-cluster-network ports: - "30002:27017" volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/mongos-server:/data/configdb - /tmp/mongo-cluster/mongos-server/logs/2:/data/logs command: --config /data/configdb/conf/mongo.conf mongos-server3: image: mongo container_name: mongos-server3 privileged: true entrypoint: "mongos" networks: - mongo-cluster-network ports: - "30003:27017" volumes: - /etc/localtime:/etc/localtime - /tmp/mongo-cluster/mongos-server:/data/configdb - /tmp/mongo-cluster/mongos-server/logs/3:/data/logs command: --config /data/configdb/conf/mongo.conf networks: mongo-cluster-network: driver: bridge 启动服务 docker-compose up -d 初始化文件 执行下面脚本进行容器初始化 docker exec -it config-server1 bash mongo -port 27017 rs.initiate( { _id: "configsvr", members: [ { _id : 1, host : "config-server1:27017" }, { _id : 2, host : "config-server2:27017" }, { _id : 3, host : "config-server3:27017" } ] } ) use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) db.auth("root","root") db.createUser({user:"test",pwd:"test",roles:[{role:'readWrite',db:'test'}]}) docker exec -it shard1-server1 bin/bash mongo -port 27017 #进行副本集配置 rs.initiate( { _id : "shard1", members: [ { _id : 0, host : "shard1-server1:27017" }, { _id : 1, host : "shard1-server2:27017" }, { _id : 2, host : "shard1-server3:27017",arbiterOnly:true } ] } ); use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) db.auth("root","root") db.createUser({user:"test",pwd:"test",roles:[{role:'readWrite',db:'test'}]}) docker exec -it shard2-server1 bin/bash mongo -port 27017 #进行副本集配置 rs.initiate( { _id : "shard2", members: [ { _id : 0, host : "shard2-server1:27017" }, { _id : 1, host : "shard2-server2:27017" }, { _id : 2, host : "shard2-server3:27017",arbiterOnly:true } ] } ); use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) db.auth("root","root") db.createUser({user:"test",pwd:"test",roles:[{role:'readWrite',db:'test'}]}) docker exec -it shard3-server1 bin/bash mongo -port 27017 #进行副本集配置 rs.initiate( { _id : "shard3", members: [ { _id : 0, host : "shard3-server1:27017" }, { _id : 1, host : "shard3-server2:27017" }, { _id : 2, host : "shard3-server3:27017",arbiterOnly:true } ] } ); use admin db.createUser({user:"root",pwd:"root",roles:[{role:'root',db:'admin'}]}) db.auth("root","root") db.createUser({user:"test",pwd:"test",roles:[{role:'readWrite',db:'test'}]}) 初始化分片 docker exec -it mongos-server1 /bin/bash mongo -port 27017 use admin; db.auth("root","root"); sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") docker exec -it mongos-server2 /bin/bash mongo -port 27017 use admin; db.auth("root","root"); sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") docker exec -it mongos-server3 /bin/bash mongo -port 27017 use admin; db.auth("root","root"); sh.addShard("shard1/shard1-server1:27017,shard1-server2:27017,shard1-server3:27017") sh.addShard("shard2/shard2-server1:27017,shard2-server2:27017,shard2-server3:27017") sh.addShard("shard3/shard3-server1:27017,shard3-server2:27017,shard3-server3:27017") 本文由育博学谷狂野架构师发布 如果本文对您有帮助,欢迎关注和点赞;如果您有任何建议也可留言评论或私信,您的支持是我坚持创作的动力 转载请注明出处!

优秀的个人博客,低调大师

黑客论坛公开泄露Facebook超5亿数据

近期,Facebook的用户数据再一次遭到严重泄露。据悉,此次事件波及用户数超过5亿,其中包括超过3200万条美国用户记录,1100万条英国用户记录和600万条印度用户记录。泄露内容于某黑客论坛公布,这是迄今为止Facebook最严重的数据泄露事件。 据悉,这些数据包括用户的全名,以及Facebook ID、地点、出生日期、个人简历、电话号码和电子邮件地址。安全人员将缓存中的部分数据与Facebook的密码重置功能进行对比验证,发现数据是真实的。 此次的数据泄露事件还波及到了一些欧盟官员,包括欧盟司法专员Didier Reynders、卢森堡首相Xavier Bettel和数十名欧盟官员。专家建议,为了防止被黑客攻击,使用额外的安全措施是关键,特别是如果你是潜在的高调目标,比如媒体人或政治家,使用移动认证器应用程序进行双因素认证,并使用独特的长密码。欧盟自己的网络安全专家也呼吁同事检查自己是否是攻击的受害者。

优秀的个人博客,低调大师

必装 6 款超神的 GitHub 插件

有太多增强 GitHub 功能的插件了,就 Chrome 商店就有上千款。本文章不会列举所有的插件,只会列举六个大家肯定用的到且接地气的插件,其他花里胡哨的或者我没用过的插件就不推荐了。 图1:Chrome 商店内的相关插件 由于网络的问题,国内下载 Chrome 插件可能不太方便。老逛都帮你们下载好了,在文末获取下载方式就行了。 1.Octotree 在开发一个项目的时候,会创建很多包使代码结构层次分明。 拿 Java 项目来说,一般会创建 src 目录用来存放项目源码,test 目录存放一些用来测试的脚本。在 src 目录下,还会有 resource 资源和一些业务逻辑代码(比如遵循 MVC 开发模式的Controller、Service、Dao ) 其他项目亦然,我们发现一个完整项目,代码是层次分明的,但是如果使用 GitHub 看一个项目源码时体验会非常差,查看不同的目录操作是这样的: 图2:GitHub 目录操作 毫无疑问,这样的体验是非常糟糕的。而 Chrome 插件 Octotree 就能解决这个问题,当你安装了这个插件,打开 GitHub 仓库时会在左边有一个按钮,点击按钮就会有这个项目的完整目录树了,体验方式和在 IDE 中差不多。 图3:目录树展示 Chrome 插件 Octotree 有 30W + 用户,评分 5.0 。 图4:插件下载示意图 2. Sourcegraph 如果老逛推荐了一个还不错的 GitHub 项目,你想学习一下它的代码逻辑,你会怎么做 ?我猜应该是先 Download ,导入 IDE 把报错都解决了,然后 Run 起来,紧接着读它的源码。 我们习惯使用 IDE 读源码,原因可能是大部分人都习惯使用Ctrl +鼠标左键快速定位代码的功能。 但是有的人就懒得下载源码,如果直接在 GitHub 上看源码是这样的: 图5:GitHub 页面浏览源码示意图 这样看源码既没有代码层级结构,也没办法跳转定位。有没有一款插件能够解决这一痛点? 答案是有的。这个插件的名字叫Sourcegraph。该插件在 Chrome 商店评分 4.8,100000+ 的用户,超级受欢迎,功能丰富,宛若一个功能强大的 IDE。 图6:插件下载示意图 如果自己没办法登录谷歌商店下载,老逛也提供了其他的下载方式,老规矩文末获取。 安装插件完毕,打开 GitHub ,下图所示这个炫酷的图标就会出现,点击它你就会发现新世界。 图7:红圈为插件功能启动按钮 点击这个图标,界面会变成下图这样的。每个区域的功能都标注了出来,我觉得在浏览代码层面上可以媲美编辑器,接下来介绍三个比较常用的功能。 图8:插件功能界面 1. 文件树浏览 这款插件也支持文件树,而且还配备了更多更强大的功能,如果安装了这款插件完全可以抛弃刚刚提到的 Octotree 插件。 图9 :目录树 2.代码定位 如果当前文件中有一个实例化的对象,我们想查看该对象对应的类源码,在 IDEA 中,我们可以 Ctrl + 鼠标左键进行定位,装了这个插件可以点击「Go to definition」即可跳转。 其实一个叫 OctoLinker的插也能实现这个功能,但是装了 Sourcegraph 插件就足够了。 视频1:代码定位功能演示 3.搜索增强 当你刚刚学习了一个技术,想找一个 Demo 学习。这时候你不需要在打开 GitHub 搜索了, 配备了这款插件的 Chrome 浏览器即可完成这一操作 ,没错,直接在浏览器搜索栏搜索代码而不是在 GitHub 平台上的搜索,同时支持持跨仓库搜索。 你只需要在浏览器的地址栏中键入 src然后空格,即可在 Sourcegraph 上搜索查询 Demo 了。 视频2 :搜索增强功能演示 Sourcegraph基本可以满足我们在浏览器端浏览源码的需求,但是还有很多需求它无法满足,比如:只想下载一个项目中的某个文件。使用 git clone 命令克隆的是整个仓库,如何轻松地获取一个仓库中的部分代码? 3. GitZip 该插件就能帮助你轻松下载一个仓库中的部分代码,使用起来也很简单。安装插件后,找到你想下载的目录,在空白处点击鼠标右键,点击 Download目录名 就可以下载了。 图10 :下载部分代码演示 老逛认为装这三个插件就足够了,下面我会简单介绍几个以前用过但是最近不怎么常用的插件,不想往下看的朋友可以点个赞然后退出了。 Notifier for Github 该插件可以帮助显示 GitHub 未读消息数,同样也支持桌面通知,让你第一时间可以知道 GitHub 上有没有新消息。 Octohint GitHub 只提供基本的语法高亮,你想找到某个变量需要 Ctrl + F 去找。如果是一个大的文件,这种传统查找方式比较繁琐,效率低下。 安装了 Octohint 这款插件,在使用 GitHub 浏览代码时,默认会加入参数属性提示、变量名高亮等功能,让浏览代码更加方便。 图11 :插件功能演示 Github Hovercard 这是一款简洁的 GitHub 悬浮卡片 Chrome 插件, 可方便的查看 GitHub 上的 user/repo/issue 等信息,增强了 GitHub 的交互体验。 4. 下载方式 我把这六款插件下载了下来,本公众号「逛逛GitHub」后台输入命令「GitHub插件」就能获取下 载链接了。 安装也很简单:点击Chrome菜单 → 更多工具 → 扩展程序,进入扩展程序页面。 图12:扩展页面入口 拖拽下载好的 .crx 扩展进去就完成了安装 图13:拖拽安装插件 5.瞎扯 好了,如果你用过其他不错的插件可以后台给我留言,我都会看。还有那个抢茅台的事情也回复一下大家,后台很多人问我有没有抢到,我的积分比较低,所以抢了两次就放弃了,如果你的小白分低于 100 就不要陪跑了。 最后,喜欢本文章的可以转发、在看。 以下平台都有老逛的身影,欢迎大家关注我在其他平台的同名账号。 推荐阅读 1. 一个人竟然撸了一个抖音 App 2. IntelliJ IDEA 从入门到上瘾 3. GitHub 暗黑模式终于来了! 每天推荐一个有趣、好玩且可能你会用到的 GitHub 项目。 △扫码关注我△ 本文分享自微信公众号 - 逛逛GitHub(ggGithub)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

超详细的RabbitMQ入门,看这篇就够了!

思维导图 一、什么是消息队列 消息指的是两个应用间传递的数据。数据的类型有很多种形式,可能只包含文本字符串,也可能包含嵌入对象。 “消息队列(Message Queue)”是在消息的传输过程中保存消息的容器。在消息队列中,通常有生产者和消费者两个角色。生产者只负责发送数据到消息队列,谁从消息队列中取出数据处理,他不管。消费者只负责从消息队列中取出数据处理,他不管这是谁发送的数据。 二、为什么使用消息队列 主要有三个作用: 解耦。如图所示。假设有系统B、C、D都需要系统A的数据,于是系统A调用三个方法发送数据到B、C、D。这时,系统D不需要了,那就需要在系统A把相关的代码删掉。假设这时有个新的系统E需要数据,这时系统A又要增加调用系统E的代码。为了降低这种强耦合,就可以使用MQ,系统A只需要把数据发送到MQ,其他系统如果需要数据,则从MQ中获取即可。 异步。如图所示。一个客户端请求发送进来,系统A会调用系统B、C、D三个系统,同步请求的话,响应时间就是系统A、B、C、D的总和,也就是800ms。如果使用MQ,系统A发送数据到MQ,然后就可以返回响应给客户端,不需要再等待系统B、C、D的响应,可以大大地提高性能。对于一些非必要的业务,比如发送短信,发送邮件等等,就可以采用MQ。 削峰。如图所示。这其实是MQ一个很重要的应用。假设系统A在某一段时间请求数暴增,有5000个请求发送过来,系统A这时就会发送5000条SQL进入MySQL进行执行,MySQL对于如此庞大的请求当然处理不过来,MySQL就会崩溃,导致系统瘫痪。如果使用MQ,系统A不再是直接发送SQL到数据库,而是把数据发送到MQ,MQ短时间积压数据是可以接受的,然后由消费者每次拉取2000条进行处理,防止在请求峰值时期大量的请求直接发送到MySQL导致系统崩溃。 三、RabbitMQ的特点 RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件。首先要知道一些RabbitMQ的特点,官网可查: 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。 多种协议。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。 支持多种语言客户端。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。 插件机制。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。 四、RabbitMQ初の体验 4.1 安装RabbitMQ (Win10系统) 由于只是学习需要,所以安装在win10系统,就懒得开虚拟机。如果用Linux系统安装的话,我建议用Docker拉一个RabbitMQ的镜像下来,这样会方便一点。 4.1.1 安装erLang语言,配置环境变量 首先到erlang官网下载win10版安装包。 下载完之后,就得到这个东西: 接着双击安装,一直点next(下一步)就行了,安装完之后,配置环境变量。 使用cmd命令,输入 erl -version 验证: 4.1.2 安装RabbitMQ服务端 在RabbitMQ的gitHub项目中,下载window版本的服务端安装包。 下载后,就得到这个东西: 接着到双击安装,一直点下一步安装即可,安装完成后,找到安装目录: 在此目录下打开cmd命令,输入rabbitmq-plugins enable rabbitmq_management命令安装管理页面的插件: 然后双击rabbitmq-server.bat启动脚本,然后打开服务管理可以看到RabbitMQ正在运行: 这时,打开浏览器输入http://localhost:15672,账号密码默认是:guest/guest 到这一步,安装就大功告成了! 4.2 永远的Hello Word 服务端搭建好了之后肯定要用客户端去操作,接下来就用Java做一个简单的HelloWord演示。 因为我用的是SpringBoot,所以在生产者这边加入对应的starter依赖即可: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> 一般需要创建一个公共项目common,共享一些配置,比如队列主题,交换机名称,路由匹配键名称等等。 首先在application.yml文件加上RabbitMQ的配置信息: spring: rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guest 然后再生产者这边,加上common包的maven依赖,然后创建一个Direct交换机以及队列的配置类: @Configuration public class DirectRabbitConfig { @Bean public Queue rabbitmqDemoDirectQueue() { /** * 1、name: 队列名称 * 2、durable: 是否持久化 * 3、exclusive: 是否独享、排外的。如果设置为true,定义为排他队列。则只有创建者可以使用此队列。也就是private私有的。 * 4、autoDelete: 是否自动删除。也就是临时队列。当最后一个消费者断开连接后,会自动删除。 * */ return new Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC, true, false, false); } @Bean public DirectExchange rabbitmqDemoDirectExchange() { //Direct交换机 return new DirectExchange(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, true, false); } @Bean public Binding bindDirect() { //链式写法,绑定交换机和队列,并设置匹配键 return BindingBuilder //绑定队列 .bind(rabbitmqDemoDirectQueue()) //到交换机 .to(rabbitmqDemoDirectExchange()) //并设置匹配键 .with(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING); } } 然后再创建一个发送消息的Service类: @Service public class RabbitMQServiceImpl implements RabbitMQService { //日期格式化 private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Resource private RabbitTemplate rabbitTemplate; @Override public String sendMsg(String msg) throws Exception { try { String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32); String sendTime = sdf.format(new Date()); Map<String, Object> map = new HashMap<>(); map.put("msgId", msgId); map.put("sendTime", sendTime); map.put("msg", msg); rabbitTemplate.convertAndSend(RabbitMQConfig.RABBITMQ_DEMO_DIRECT_EXCHANGE, RabbitMQConfig.RABBITMQ_DEMO_DIRECT_ROUTING, map); return "ok"; } catch (Exception e) { e.printStackTrace(); return "error"; } } } 然后根据业务放在需要用的地方,比如定时任务,或者接口。我这里就简单一点使用Controller层进行发送: @RestController @RequestMapping("/mall/rabbitmq") public class RabbitMQController { @Resource private RabbitMQService rabbitMQService; /** * 发送消息 * @author java技术爱好者 */ @PostMapping("/sendMsg") public String sendMsg(@RequestParam(name = "msg") String msg) throws Exception { return rabbitMQService.sendMsg(msg); } } 生产者写完之后,就写消费者端的代码,消费者很简单。maven依赖,yml文件配置和生产者一样。只需要创建一个类,@RabbitListener注解写上监听队列的名称,如图所示: 这里有个小坑,一开始RabbitMQ服务器里还没有创建队列: 这时如果启动消费者,会报错: 要先启动生产者,发送一条消息: 最后再启动消费者,进行消费: 这时候就会持续监听队列的消息,只要生产者发送一条消息到MQ,消费者就消费一条。我这里尝试发送4条: 由于队列不存在,启动消费者报错的这个问题。最好的方法是生产者和消费者都尝试创建队列,怎么写呢,有很多方式,我这里用一个相对简单一点的: 生产者的配置类加点东西: //实现BeanPostProcessor类,使用Bean的生命周期函数 @Component public class DirectRabbitConfig implements BeanPostProcessor { //这是创建交换机和队列用的rabbitAdmin对象 @Resource private RabbitAdmin rabbitAdmin; //初始化rabbitAdmin对象 @Bean public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) { RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory); // 只有设置为 true,spring 才会加载 RabbitAdmin 这个类 rabbitAdmin.setAutoStartup(true); return rabbitAdmin; } //实例化bean后,也就是Bean的后置处理器 @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //创建交换机 rabbitAdmin.declareExchange(rabbitmqDemoDirectExchange()); //创建队列 rabbitAdmin.declareQueue(rabbitmqDemoDirectQueue()); return null; } } 这样启动生产者就会自动创建交换机和队列,不用等到发送消息才创建。 消费者需要加一点代码: @Component //使用queuesToDeclare属性,如果不存在则会创建队列 @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.RABBITMQ_DEMO_TOPIC)) public class RabbitDemoConsumer { //...省略 } 这样,无论生产者还是消费者先启动都不会出现问题了~ 代码地址:https://github.com/yehongzhi/mall 五、RabbitMQ中的组成部分 从上面的HelloWord例子中,我们大概也能体验到一些,就是RabbitMQ的组成,它是有这几部分: Broker:消息队列服务进程。此进程包括两个部分:Exchange和Queue。 Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列。 Queue:消息队列,存储消息的队列。 Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。 Consumer:消息消费者。消费队列中存储的消息。 这些组成部分是如何协同工作的呢,大概的流程如下,请看下图: 消息生产者连接到RabbitMQ Broker,创建connection,开启channel。 生产者声明交换机类型、名称、是否持久化等。 生产者发送消息,并指定消息是否持久化等属性和routing key。 exchange收到消息之后,根据routing key路由到跟当前交换机绑定的相匹配的队列里面。 消费者监听接收到消息之后开始业务处理。 六、Exchange的四种类型以及用法 从上面的工作流程可以看出,实际上有个关键的组件Exchange,因为消息发送到RabbitMQ后首先要经过Exchange路由才能找到对应的Queue。 实际上Exchange类型有四种,根据不同的类型工作的方式也有所不同。在HelloWord例子中,我们就使用了比较简单的Direct Exchange,翻译就是直连交换机。其余三种分别是:Fanout exchange、Topic exchange、Headers exchange。 6.1 Direct Exchange 见文知意,直连交换机意思是此交换机需要绑定一个队列,要求该消息与一个特定的路由键完全匹配。简单点说就是一对一的,点对点的发送。 完整的代码就是上面的HelloWord的例子,不再重复代码。 6.2 Fanout exchange 这种类型的交换机需要将队列绑定到交换机上。一个发送到交换机的消息都会被转发到与该交换机绑定的所有队列上。很像子网广播,每台子网内的主机都获得了一份复制的消息。简单点说就是发布订阅。 代码怎么写呢,演示一下: 首先要先配置交换机和队列的名称: public class RabbitMQConfig { /** * RabbitMQ的FANOUT_EXCHANG交换机类型的队列 A 的名称 */ public static final String FANOUT_EXCHANGE_QUEUE_TOPIC_A = "fanout.A"; /** * RabbitMQ的FANOUT_EXCHANG交换机类型的队列 B 的名称 */ public static final String FANOUT_EXCHANGE_QUEUE_TOPIC_B = "fanout.B"; /** * RabbitMQ的FANOUT_EXCHANG交换机类型的名称 */ public static final String FANOUT_EXCHANGE_DEMO_NAME = "fanout.exchange.demo.name"; } 再配置FanoutExchange类型的交换机和A、B两个队列,并且绑定。这种类型不需要配置routing key: @Component public class DirectRabbitConfig implements BeanPostProcessor { @Resource private RabbitAdmin rabbitAdmin; @Bean public Queue fanoutExchangeQueueA() { //队列A return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A, true, false, false); } @Bean public Queue fanoutExchangeQueueB() { //队列B return new Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_B, true, false, false); } @Bean public FanoutExchange rabbitmqDemoFanoutExchange() { //创建FanoutExchange类型交换机 return new FanoutExchange(RabbitMQConfig.FANOUT_EXCHANGE_DEMO_NAME, true, false); } @Bean public Binding bindFanoutA() { //队列A绑定到FanoutExchange交换机 return BindingBuilder.bind(fanoutExchangeQueueA()).to(rabbitmqDemoFanoutExchange()); } @Bean public Binding bindFanoutB() { //队列B绑定到FanoutExchange交换机 return BindingBuilder.bind(fanoutExchangeQueueB()).to(rabbitmqDemoFanoutExchange()); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { //启动项目即创建交换机和队列 rabbitAdmin.declareExchange(rabbitmqDemoFanoutExchange()); rabbitAdmin.declareQueue(fanoutExchangeQueueB()); rabbitAdmin.declareQueue(fanoutExchangeQueueA()); return null; } } 创建service发布消息的方法: @Service public class RabbitMQServiceImpl implements RabbitMQService { private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); @Resource private RabbitTemplate rabbitTemplate; //发布消息 @Override public String sendMsgByFanoutExchange(String msg) throws Exception { Map<String, Object> message = getMessage(msg); try { rabbitTemplate.convertAndSend(RabbitMQConfig.FANOUT_EXCHANGE_DEMO_NAME, "", message); return "ok"; } catch (Exception e) { e.printStackTrace(); return "error"; } } //组装消息体 private Map<String, Object> getMessage(String msg) { String msgId = UUID.randomUUID().toString().replace("-", "").substring(0, 32); String sendTime = sdf.format(new Date()); Map<String, Object> map = new HashMap<>(); map.put("msgId", msgId); map.put("sendTime", sendTime); map.put("msg", msg); return map; } } Controller接口: @RestController @RequestMapping("/mall/rabbitmq") public class RabbitMQController { /** * 发布消息 * * @author java技术爱好者 */ @PostMapping("/publish") public String publish(@RequestParam(name = "msg") String msg) throws Exception { return rabbitMQService.sendMsgByFanoutExchange(msg); } } 接着在消费者项目这边,创建两个队列的监听类,监听队列进行消费: @Component @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_A)) public class FanoutExchangeConsumerA { @RabbitHandler public void process(Map<String, Object> map) { System.out.println("队列A收到消息:" + map.toString()); } } @Component @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.FANOUT_EXCHANGE_QUEUE_TOPIC_B)) public class FanoutExchangeConsumerB { @RabbitHandler public void process(Map<String, Object> map) { System.out.println("队列B收到消息:" + map.toString()); } } 然后启动生产者和消费者两个项目,可以看到管理界面创建了一个FanoutExchange交换机和两个队列,并且绑定了: 使用POSTMAN进行发送消息,测试: 然后可以看到控制台,两个队列同时都收到了相同的消息,形成了发布订阅的效果: 6.3 Topic Exchange 直接翻译的话叫做主题交换机,如果从用法上面翻译可能叫通配符交换机会更加贴切。这种交换机是使用通配符去匹配,路由到对应的队列。通配符有两种:"*" 、 "#"。需要注意的是通配符前面必须要加上"."符号。 * 符号:有且只匹配一个词。比如 a.*可以匹配到"a.b"、"a.c",但是匹配不了"a.b.c"。 # 符号:匹配一个或多个词。比如"rabbit.#"既可以匹配到"rabbit.a.b"、"rabbit.a",也可以匹配到"rabbit.a.b.c"。 废话不多说,代码演示一下: 依然是配置TopicExchange名称和三个队列的名称: /** * RabbitMQ的TOPIC_EXCHANGE交换机名称 */ public static final String TOPIC_EXCHANGE_DEMO_NAME = "topic.exchange.demo.name"; /** * RabbitMQ的TOPIC_EXCHANGE交换机的队列A的名称 */ public static final String TOPIC_EXCHANGE_QUEUE_A = "topic.queue.a"; /** * RabbitMQ的TOPIC_EXCHANGE交换机的队列B的名称 */ public static final String TOPIC_EXCHANGE_QUEUE_B = "topic.queue.b"; /** * RabbitMQ的TOPIC_EXCHANGE交换机的队列C的名称 */ public static final String TOPIC_EXCHANGE_QUEUE_C = "topic.queue.c"; 然后还是老配方,配置交换机和队列,然后绑定,创建: @Component public class DirectRabbitConfig implements BeanPostProcessor { //省略... @Bean public TopicExchange rabbitmqDemoTopicExchange() { //配置TopicExchange交换机 return new TopicExchange(RabbitMQConfig.TOPIC_EXCHANGE_DEMO_NAME, true, false); } @Bean public Queue topicExchangeQueueA() { //创建队列1 return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A, true, false, false); } @Bean public Queue topicExchangeQueueB() { //创建队列2 return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B, true, false, false); } @Bean public Queue topicExchangeQueueC() { //创建队列3 return new Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C, true, false, false); } @Bean public Binding bindTopicA() { //队列A绑定到FanoutExchange交换机 return BindingBuilder.bind(topicExchangeQueueB()) .to(rabbitmqDemoTopicExchange()) .with("a.*"); } @Bean public Binding bindTopicB() { //队列A绑定到FanoutExchange交换机 return BindingBuilder.bind(topicExchangeQueueC()) .to(rabbitmqDemoTopicExchange()) .with("a.*"); } @Bean public Binding bindTopicC() { //队列A绑定到FanoutExchange交换机 return BindingBuilder.bind(topicExchangeQueueA()) .to(rabbitmqDemoTopicExchange()) .with("rabbit.#"); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { rabbitAdmin.declareExchange(rabbitmqDemoTopicExchange()); rabbitAdmin.declareQueue(topicExchangeQueueA()); rabbitAdmin.declareQueue(topicExchangeQueueB()); rabbitAdmin.declareQueue(topicExchangeQueueC()); return null; } } 然后写一个发送消息的service方法: @Service public class RabbitMQServiceImpl implements RabbitMQService { @Override public String sendMsgByTopicExchange(String msg, String routingKey) throws Exception { Map<String, Object> message = getMessage(msg); try { //发送消息 rabbitTemplate.convertAndSend(RabbitMQConfig.TOPIC_EXCHANGE_DEMO_NAME, routingKey, message); return "ok"; } catch (Exception e) { e.printStackTrace(); return "error"; } } } 写一个Controller接口: @RestController @RequestMapping("/mall/rabbitmq") public class RabbitMQController { @Resource private RabbitMQService rabbitMQService; /** * 通配符交换机发送消息 * * @author java技术爱好者 */ @PostMapping("/topicSend") public String topicSend(@RequestParam(name = "msg") String msg, @RequestParam(name = "routingKey") String routingKey) throws Exception { return rabbitMQService.sendMsgByTopicExchange(msg, routingKey); } } 生产者这边写完,就写消费端,消费端比较简单,写三个监听类: @Component @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A)) public class TopicExchangeConsumerA { @RabbitHandler public void process(Map<String, Object> map) { System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_A + "]收到消息:" + map.toString()); } } @Component @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B)) public class TopicExchangeConsumerB { @RabbitHandler public void process(Map<String, Object> map) { System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_B+ "]收到消息:" + map.toString()); } } @Component @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C)) public class TopicExchangeConsumerC { @RabbitHandler public void process(Map<String, Object> map) { System.out.println("队列[" + RabbitMQConfig.TOPIC_EXCHANGE_QUEUE_C + "]收到消息:" + map.toString()); } } 大功告成,然后启动项目开始调试。启动成功后可以看到队列和路由键绑定的关系: 通过POSTMAN进行测试,测试一下 rabbit.# 的路由键是否能够匹配成功: 测试成功,队列A消费到消息: 接着测试 a.* 路由键,发送 routingKey = a.b : 比较常用的就是以上三种:直连(DirectExchange),发布订阅(FanoutExchange),通配符(TopicExchange)。熟练运用这三种交换机类型,基本上可以解决大部分的业务场景。 实际上稍微思考一下,可以发现通配符(TopicExchange)这种模式其实是可以达到直连(DirectExchange)和发布订阅(FanoutExchange)这两种的效果的。 FanoutExchange不需要绑定routingKey,所以性能相对TopicExchange会好一点。 6.4 Headers Exchange 这种交换机用的相对没这么多。它跟上面三种有点区别,它的路由不是用routingKey进行路由匹配,而是在匹配请求头中所带的键值进行路由。如图所示: 创建队列需要设置绑定的头部信息,有两种模式:全部匹配和部分匹配。如上图所示,交换机会根据生产者发送过来的头部信息携带的键值去匹配队列绑定的键值,路由到对应的队列。代码怎么实现呢,往下看演示代码: 首先还是需要定义交换机名称,队列名称: /** * HEADERS_EXCHANGE交换机名称 */ public static final String HEADERS_EXCHANGE_DEMO_NAME = "headers.exchange.demo.name"; /** * RabbitMQ的HEADERS_EXCHANGE交换机的队列A的名称 */ public static final String HEADERS_EXCHANGE_QUEUE_A = "headers.queue.a"; /** * RabbitMQ的HEADERS_EXCHANGE交换机的队列B的名称 */ public static final String HEADERS_EXCHANGE_QUEUE_B = "headers.queue.b"; 然后设置交换机,队列,进行绑定: @Component public class DirectRabbitConfig implements BeanPostProcessor { @Bean public Queue headersQueueA() { return new Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A, true, false, false); } @Bean public Queue headersQueueB() { return new Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B, true, false, false); } @Bean public HeadersExchange rabbitmqDemoHeadersExchange() { return new HeadersExchange(RabbitMQConfig.HEADERS_EXCHANGE_DEMO_NAME, true, false); } @Bean public Binding bindHeadersA() { Map<String, Object> map = new HashMap<>(); map.put("key_one", "java"); map.put("key_two", "rabbit"); //全匹配 return BindingBuilder.bind(headersQueueA()) .to(rabbitmqDemoHeadersExchange()) .whereAll(map).match(); } @Bean public Binding bindHeadersB() { Map<String, Object> map = new HashMap<>(); map.put("headers_A", "coke"); map.put("headers_B", "sky"); //部分匹配 return BindingBuilder.bind(headersQueueB()) .to(rabbitmqDemoHeadersExchange()) .whereAny(map).match(); } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { rabbitAdmin.declareExchange(rabbitmqDemoHeadersExchange()); rabbitAdmin.declareQueue(headersQueueA()); rabbitAdmin.declareQueue(headersQueueB()); return null; } } 再写一个Service方法发送消息: @Service public class RabbitMQServiceImpl implements RabbitMQService { @Resource private RabbitTemplate rabbitTemplate; @Override public String sendMsgByHeadersExchange(String msg, Map<String, Object> map) throws Exception { try { MessageProperties messageProperties = new MessageProperties(); //消息持久化 messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT); messageProperties.setContentType("UTF-8"); //添加消息 messageProperties.getHeaders().putAll(map); Message message = new Message(msg.getBytes(), messageProperties); rabbitTemplate.convertAndSend(RabbitMQConfig.HEADERS_EXCHANGE_DEMO_NAME, null, message); return "ok"; } catch (Exception e) { e.printStackTrace(); return "error"; } } } 再写一个Controller接口: @RestController @RequestMapping("/mall/rabbitmq") public class RabbitMQController { @Resource private RabbitMQService rabbitMQService; @PostMapping("/headersSend") @SuppressWarnings("unchecked") public String headersSend(@RequestParam(name = "msg") String msg, @RequestParam(name = "json") String json) throws Exception { ObjectMapper mapper = new ObjectMapper(); Map<String, Object> map = mapper.readValue(json, Map.class); return rabbitMQService.sendMsgByHeadersExchange(msg, map); } } 生产者这边写完了,再写两个队列的监听类进行消费: @Component public class HeadersExchangeConsumerA { @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A)) public void process(Message message) throws Exception { MessageProperties messageProperties = message.getMessageProperties(); String contentType = messageProperties.getContentType(); System.out.println("队列[" + RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_A + "]收到消息:" + new String(message.getBody(), contentType)); } } @Component public class HeadersExchangeConsumerB { @RabbitListener(queuesToDeclare = @Queue(RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B)) public void process(Message message) throws Exception { MessageProperties messageProperties = message.getMessageProperties(); String contentType = messageProperties.getContentType(); System.out.println("队列[" + RabbitMQConfig.HEADERS_EXCHANGE_QUEUE_B + "]收到消息:" + new String(message.getBody(), contentType)); } } 大功告成~启动项目,打开管理界面,我们可以看到交换机绑定队列的信息: 跟上面示意图一样~证明没有问题,一切尽在掌握之中。使用POSTMAN发送,测试全匹配的队列A: 再测试部分匹配的队列B: 总结 这篇文章就先写到这里了。回顾一下学了哪些: 什么是消息队列?为什么使用消息队列? RabbitMQ的特点、组成部分、工作流程 安装RabbitMQ,以及完成一个HelloWord小案例 RabbitMQ交换机的四种类型的特点,以及使用方法 实际上RabbitMQ还有事务机制和负载均衡这些还没讲,因为篇幅实在有点长了,差不多5千字了。所以放在下期讲吧,尽请期待一下。 上面所有例子的代码都上传github了: https://github.com/yehongzhi/mall 如果你觉得这篇文章对你有用,点个赞吧~ 你的点赞是我创作的最大动力~ 想第一时间看到我更新的文章,可以微信搜索公众号「java技术爱好者」,拒绝做一条咸鱼,我是一个努力让大家记住的程序员。我们下期再见!!! 能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

优秀的个人博客,低调大师

超多新功能!Apache APISIX 发布 1.2 版本

Apache APISIX 是云原生 API 网关,不仅可以帮你处理传统的南北向流量,也可以处理服务间的东西向流量。它是基于 Nginx 和 etcd 来实现,和传统 API 网关相比,Apache APISIX 具备动态路由、动态上游和插件热加载的特性,特别适合微服务体系下的 API 管理。 目前有众多国内外知名企业和科研机构正在使用 Apache APISIX,下图列出了主动登记的部分公司,未声明的公司数量众多,无法准确的统计。 本次发布的 Apache APISIX 1.2 版本,是新特性最多的一次发布,新增了 Kafka、CORS、TCP 和 UDP logger、代理缓存、代理镜像等多个插件,主要变更如下: 内核 🌅支持 etcd 集群. #1283 默认使用本地 DNS resolver. 支持在header_filter、body_filter和log阶段运行全局插件. 将目录lua/apisix修改为apisix(不向下兼容). 增加 dashboard 子模块. 允许自定义共享字典. 插件 🌅新增 Apache Kafka 插件. 🌅新增 CORS 插件. 🌅新增 TCP logger 插件. 🌅新增 UDP logger 插件. 🌅新增 proxy mirror 插件. 🌅新增 proxy cache 插件. 在 proxy-rewrite 插件中废弃 websocket 开关(不向下兼容). OAuth 插件中增加基于公钥的自省支持. response-rewrite 插件通过 base64 来支持传输二进制数据. gRPC 转码插件支持deadline. limit count 插件支持 redis 权限认证. Zipkin 插件支持名字和本地服务器 ip 的记录. Wolf-Rbac 插件增加change_pwd和user_info参数. Apache APISIX 的社区也在飞速发展,从加入 Apache 孵化器之初的 20 多位贡献者,增加到现在的 70 多位贡献者,其中包含 19 位 PPMC 和 22 位 committer。 加入 Apache APISIX 如果你希望使用修改任意配置都无需重启的 web 服务器,如果你希望使用代码简洁、云原生友好的微服务网关,那么 Apache APISIX 就是你不二的选择。 可以在 GitHub 上提交 issue 和 PR:https://github.com/apache/incubator-apisix。让我们一起努力,打造世界级的开源项目!

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册