第三方技术整合
# 缓存

缓存模拟——与数据库交互
@Autowired private BookMapper bookMapper; //创建一个HashMap,用来模拟缓存,一个id对应一个book private HashMap<Integer,Book> cache=new HashMap<>(); @Override //使用HashMap 模拟缓存 实现 public Book getById(int id) { Book book = cache.get(id); //如果缓存中有数据,则从缓存中读取,如果缓存中没有数据,则从数据库读取到缓存 if(book == null){ Book queryBook = bookMapper.selectById(id); cache.put(id,queryBook); return queryBook; } return cache.get(id); }缓存模拟——与外部数据交互(简单版)
@Service public class MsgServiceImpl implements IMsgService { private HashMap<String,String> cache=new HashMap<>(); @Override //生成验证码 public String get(String tel) { String code = tel.substring(tel.length() - 6);//指定开始,默认到结尾——取 从第6为开始取-- 不包括开头 cache.put(tel,code);//将生成的验证码放入缓存 return code; } @Override //将输入的验证码与查询的验证码相匹配 public boolean check(String tel, String code) {//从问号得到数据 String QueryCode = cache.get(tel); return QueryCode.equals(code); } }
# Simple
SpringBoot整合缓存——默认缓存技术(Simple)
引入坐标
<!-- 引入缓存坐标--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>开启缓存
@EnableCaching@SpringBootApplication @EnableCaching //开启缓存功能 public class CacheApplication { }设置缓存
@Cacheable//使用springBoot整合的缓存技术 //缓存空间中没有对应值时,执行return 将数据存入缓存 //缓存空间有值时,直接从缓存空间取值,不执行return 后的查询操作 @Cacheable(value = "cacheSpace",key = "#id") //value 随便起名,定义一片空间,存储缓存 //id作为一个键值,对应一个数据 public Book getById(int id) { return bookMapper.selectById(id); }与外部数据交互(专业版—— 验证码案例)
工具类
生成验证码——用到了数据结构的思想来简化程序
private final String[] patch={"000000","00000","0000","000","00","0",""}; //生成验证码 6位 public String generator(String tel){ int hash =tel.hashCode(); int encryption=20206666; long result=hash^encryption;//做异或 long nowTime=System.currentTimeMillis(); result = nowTime ^ result; long code = result % 1000000;//取余数,小于等于6位 code=code < 0 ? -code:code;//全部取正数 String codeStr=code+"";//转换成字符串 int length = codeStr.length();//根据长度补0,1位补5个0,2为补4个0 ,等等 return patch[length]+codeStr;//返回6为验证码 }从缓存中获得验证码
如果直接将方法定义在service内容,从内部调用方法,不走spring容器,那它头上注解不会生效,即不会开启缓存
@Cacheable使用时,若想只取数据,不存数据,则返回null即可//get方法 不能直接放在service中,因为它需要经过容器管理,才能启用缓存 //如果直接内部调用方法,不走spring容器,那它头上注解不会生效,即不会开启缓存。 @Cacheable(value = "SMSCode",key="#tel") //从缓存中拿验证码,只拿不放,返回null即可 public String getCode(String tel){ return null; }
service
@CachePut将数据存入缓存,只存不取,每次调用方法都会产生新数据@Cacheable不光将数据存入缓存,而且当再次执行方法时,会先查询缓存中的数据,如果有直接拿,不产生新数据.equals()将可能为null的值放在后面@Autowired private CodeUtils codeUtils; @Override // @Cacheable(value = "SMSCode",key="#tel") //存入缓存,若key相同,则从存缓存中取数据 @CachePut(value = "SMSCode",key="#tel") //存入缓存,只存 不取,新数据会覆盖旧数据 public String SendCode(String tel) { String code = codeUtils.generator(tel);//调用方法,产生验证码 return code; } @Override public boolean CheckCode(SMSCode smsCode) { //比对验证码 String queryCode = codeUtils.getCode(smsCode.getTel()); return smsCode.getCode().equals(queryCode);//将可能为null的值放在后面 }
# Ehcache
SpringBoot整合缓存——Ehcache——不需要修改代码
导入坐标
<!-- 引入缓存 Ehcache--> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> </dependency>配置yml——告诉springBoot 用那种缓存方式
#配置cache 不配是默认的 simple cache: type: ehcache导入配置文件
# Redis
SpringBoot整合缓存——Redis——不需要修改代码
导入坐标
<!-- 引入 redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>配置yml
#配置redis cache: type: redis redis: # cache-null-values: false #是否缓存空值 # enable-statistics: false time-to-live: 20s #生存时间 一定要带单位 # use-key-prefix: false #是否使用键前缀 使用——"SMSCode::17854201283" 不使用——"17854201283" 默认是true,最好是true # key-prefix: sms_ #设置键前缀是啥 redis: host: localhost port: 6379
# memcached
SpringBoot整合缓存——memcached——需要修改代码
安装服务 ——第一个是安装,第二个是启动服务

导入坐标
<!-- 引入memcached--> <dependency> <groupId>com.googlecode.xmemcached</groupId> <artifactId>xmemcached</artifactId> <version>2.4.7</version> </dependency>配置yml
- 这里配置的仅仅是 memcached的链接属性,是自定义的名称,和官方没关系,springBoot没有整合,所有没有正规配置。
#配置memcache memcached: servers: localhost:11211 poolSize: 10 opTimeout: 3000 #3秒钟没有读到就认为超时代码
设置属性
//配置memcached的连接属性 @Component @Data @ConfigurationProperties(prefix = "memcached") public class XmemcacheProperties { private String servers; private int poolSize; private long opTimeout; }配置客户端
@Bean public MemcachedClient getMemcacaheClient() throws IOException { //创建Builder 传入服务器地址 MemcachedClientBuilder memcachedClientBuilder= new XMemcachedClientBuilder(xmemcacheProperties.getServers()); //设置参数 memcachedClientBuilder.setConnectionPoolSize(xmemcacheProperties.getPoolSize());//连接池数量 memcachedClientBuilder.setOpTimeout(xmemcacheProperties.getOpTimeout());//超时时间 //创建客户端 MemcachedClient memcachedClient=memcachedClientBuilder.build(); return memcachedClient; }Service层代码修改- 这里不能使用之前的
@Cacheable,因为没有整合,所有只能自己set,自己put - 当一个接口被两个类实现的时候,使用@Autowired不能注入属性,需要使用@Resource(name="")的方式注入
@Autowired private CodeUtils codeUtils; @Override public String SendCode(String tel) { System.out.println(111); String code = codeUtils.generator(tel);//调用方法,产生验证码 try { memcachedClient.set(tel,10,code);//第一个参数是key,第二个是0表示永不过期,是10表示保留10s,第三个是值 } catch (Exception e) { e.printStackTrace(); } return code; } @Override public boolean CheckCode(SMSCode smsCode) { //比对验证码 String queryCode= null; try { //如果验证码过期了,这里会报空指针异常 queryCode = memcachedClient.get(smsCode.getTel()).toString(); } catch (Exception e) { e.printStackTrace(); } return smsCode.getCode().equals(queryCode);//将可能为null的值放在后面 }- 这里不能使用之前的
# jetcache

引入坐标
<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.6.4</version> </dependency>配置yml
#配置jetcache jetcache: statIntervalMinutes: 1 #每过1分钟 控制台显示一次统计数据 areaInCacheName: false #不使用前缀 #不使用前缀"book_2" ---使用前缀"default_book_2" #本地方案 local: default: type: linkedhashmap keyConvertor: fastjson #key转换成字符串的方式 #远程方案 remote: default: type: redis host: localhost port: 6379 keyConvertor: fastjson #key转换成字符串的方式 valueEncode: java #将java对象转换成编码,存入redis valueDecode: java #从redis中取出编码,将编码转换成java对象 poolConfig: #必须要配一个,不然会报错 maxTotal: 50 sms: type: redis host: localhost port: 6379 keyConvertor: fastjson #key转换成字符串的方式 valueEncode: java #将java对象转换成编码,存入redis valueDecode: java #从redis中取出编码,将编码转换成java对象 poolConfig: #必须要配一个,不然会报错 maxTotal: 50

代码
- 开启缓存
@SpringBootApplication //jetcache启用缓存的主开关 @EnableCreateCacheAnnotation //开启注解方法缓存———— 与上面那个注解配合使用 @EnableMethodCache(basePackages = "com.diana") public class JetcacheApplication { }- 硬编码方式使用缓存,put,get
//自定义缓存空间————remote //第0个参数是使用哪个配置,默认可以不写;第一个参数是命名空间;第二个是过期时间,默认是秒 @CreateCache(area = "sms",name="jetCache_",expire = 10) private Cache<String,String> jetCache_; // //定义第二套缓存空间————remote 可以分开管理 // @CreateCache(area = "sms",name="jetCache2_",expire = 10) // private Cache<String,String> jetCache2_; //自定义缓存空间————local //使用本地缓存-- 只存在本地,不存入reids 默认是both,本地和redis都使用 @CreateCache(name="jetCache_local_",expire = 100,cacheType = CacheType.LOCAL) private Cache<String,String> jetCache_local_; @Override public String SendCode(String tel) { String code = codeUtils.generator(tel);//调用方法,产生验证码 // jetCache_.put(tel,code);//放入远程缓存 jetCache_local_.put(tel,code);//放入本地缓存 return code; } @Override public boolean CheckCode(SMSCode smsCode) { //比对验证码 // String queryCode = jetCache_.get(smsCode.getTel());//从远程缓存中取数据 String queryCode = jetCache_local_.get(smsCode.getTel());//从本地缓存中取数据 return smsCode.getCode().equals(queryCode);//将可能为null的值放在后面 }缓存对象必须保证可以序列化
public class Book implements Serializable {} //实现接口,以便对象可以进行序列化操作注解方式使用缓存
@Cached用来存,取数据@CacheRefresh,用来刷新缓存,保证缓存和数据库数据保持一致,一般不用@CacheInvalidate,用在delete方法上,保证删除数据后,清除对应缓存@CacheUpdate用在update方法上,保证修改数据后,修改对应缓存
@Override //使用jetcache 技术,默认使用redis存储 @Cached(name="book_",key = "#id",expire = 3600) //@CacheRefresh(refresh = 10)//10s刷新一次缓存 一般不用,就是为了保证缓存和数据库数据保持一致 public Book getById(int id) { return bookMapper.selectById(id); } @Override public boolean save(Book book) { return bookMapper.insert(book)>0; } @Override @CacheInvalidate(name="book_",key = "#id")//删除缓存,当调用删除操作删除数据时,缓存会同步删除 public boolean delete(int id ) { return bookMapper.deleteById(id)>0; } @Override @CacheUpdate(name="book_",key="#book.id",value = "#book") //更新缓存,当调用更新操作更新数据时,缓存会同步更新 public boolean update(Book book) { return bookMapper.updateById(book)>0; }
# j2cache
j2cache是一个缓存整合框架,可以提供缓存的整合方案,使用各种缓存搭配使用,自身不提供缓存功能
案例: 使用reids和ehcache实现2级缓存
引入坐标
<!-- 引入j2cache--> <dependency> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-core</artifactId> <version>2.8.5-release</version> </dependency> <dependency><!-- 默认导入了一个redis,建议使用你使用--> <groupId>net.oschina.j2cache</groupId> <artifactId>j2cache-spring-boot2-starter</artifactId> <version>2.8.0-release</version> </dependency>配置yml
j2cache: config-location: j2cache.properties #声明配置文件配置properties
#配置1级缓存 j2cache.L1.provider_class=ehcache ehcache.configXml=ehcache.xml #设置是否启用二级缓存 默认是true j2cache.l2-cache-open=true #配置2级缓存 #这是是官方提供的配置 类 j2cache.L2.provider_class=net.oschina.j2cache.cache.support.redis.SpringRedisProvider j2cache.L2.config_section=redis redis.hosts=localhost:6379 #模式 redis.mode=single #前缀名———— 想写入更多配置,查询bkg文件下的j2cache.properties redis.namespace=SMSCode #1) "SMSCode:sms:17854201285" ——带前缀 #2) "sms:17854201285" —— 不带前缀 #1级缓存中的数据怎样到达二级缓存 广播模式 #这个类提供的是 redis 去别的地方拿数据 j2cache.broadcast= net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy代码
@Autowired//注入一个缓存对象 private CacheChannel cacheChannel; @Override public String SendCode(String tel) { String code = codeUtils.generator(tel);//调用方法,产生验证码 cacheChannel.set("sms",tel,code); return code; } @Override public boolean CheckCode(SMSCode smsCode) { String queryCode = cacheChannel.get("sms",smsCode.getTel()).asString(); return smsCode.getCode().equals(queryCode);//将可能为null的值放在后面 }
# 任务
主要是做定时任务
java 实现简单定时器
public class TimerTaskApp { public static void main(String[] args){ Timer timer=new Timer(); //定时器 TimerTask task=new TimerTask() { @Override public void run() { System.out.println("timer is running "); } };//定时任务 timer.schedule(task,0,2000); //从当前开始,每2s执行一次任务 } }
# SpringBoot整合Quartz
——复杂,不推荐
基本概念
- 工作(Job):用于定义具体执行的工作
- 工作明细(JobDetail): 用于描述定时工作相关的信息
- 触发器(Trigger):用于描述触发工作的规则,通常使用cron表达式定义调度规则
- 调度器(Scheduler):描述了工作明细与触发器的对应关系
引入坐标
<!-- 引入quartz坐标--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>定义要实现的任务,继承
QuartzJobBean//定义要实现的任务 public class Myquartz extends QuartzJobBean { @Override//context是 工作执行的上下文对象 protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println("task is running"); } }定时器实现
//quartz 定时器配置 @Configuration public class QuartzConfig { @Bean public JobDetail printJobDetail(){ //绑定具体的工作 return JobBuilder.newJob(Myquartz.class).storeDurably().build(); } @Bean public Trigger printJobTrigger(){ //绑定对应的工作明细 //("0/3 10 6 11 4 ?"); 4月11日6点10分0秒开始,每3秒执行一次 //————问号表示星期几,一般星期几何几月几日不同时规定,问号表示跟随前面 ScheduleBuilder scheduleBuilder=CronScheduleBuilder.cronSchedule("0/3 * * * * ?");//任意时间,0s开始,每3秒执行一次 return TriggerBuilder.newTrigger().forJob(printJobDetail()).withSchedule(scheduleBuilder).build(); } }
# SpringBoot 定时器
——简单,推荐
开启定时任务开关
@EnableScheduling public class TaskApplication { }配置yml
spring: task: scheduling: #任务调度线程池大小 默认1 pool: size: 1 #调度线程名称前缀 thread-name-prefix: spring_task_ shutdown: #线程池关闭时是否等待所有任务完成 await-termination: false #调度线程关闭前最大等待时间,确保最后一定关闭 await-termination-period: 10s编写定时任务——加注解——使用cron表达式定义调度规则
@Scheduled(cron = "0/6 * * * * ?") public void print(){ System.out.println("spring task run..."); //前面是调用线程的名字 System.out.println(Thread.currentThread().getName()+":spring task run..."); }
# 邮件
# SpirngBoot整合JavaMail
传输协议
- SMTP:简单邮件传输协议,用于发送电子邮件的传输协议
- POP3:用于接收电子邮件的标准协议
- IMAP:互联网消息协议,是POP3的替代协议(比POP3要好那么一丢丢)
引入坐标
<!-- 引入mail 坐标--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!-- 解决配置实体类关联配置文件 警告问题--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>配置文件
#配置email spring: mail: host: smtp.qq.com #使用qq邮箱服务 smtp.126.com#使用126邮箱服务 username: 1693849288@qq.com password: pmpartwhndcrfcjg #配置email 信息 mail: mail_from: 1693849288@qq.com mail_to: diana_liangbing@126.com mail_title: "SpringBoot 整合 JAVAMail" mail_msg: "hello javaMail!!" ahref: "<a href='https://www.baidu.com'>点击前往百度</a>" img: "<img src='https://img1.baidu.com/it/u=4065318963,1219732686&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=675'>"发送简单邮件
mail.getMail_from()+"(diana)"——//加个括号作为发件人,否则默认是邮箱前缀
@Autowired private JavaMailSender javaMailSender; @Autowired private Mail mail; public void sendMail(){ SimpleMailMessage message=new SimpleMailMessage(); System.out.println(mail); message.setFrom(mail.getMail_from()+"(diana)");//加个括号,发件人,否则默认是邮箱前缀 message.setTo(mail.getMail_to()); message.setSubject(mail.getMail_title()); message.setText(mail.getMail_msg()); javaMailSender.send(message); }发送复杂邮件
- 可以发送html的标签来加载网站和图片——
- 1.
setText(mail.getAhref(),true);——true 表示发送html 标签信息,可以解析出来,默认是false,这样的话只能发送文本消息 - 2.具体的html标签可参照上面的参数配置
- 3.使用多个
setText()命令,最后一个会覆盖前面的。如果想发送多个内容,需要将所有的内容写入到一个string中
- 1.
- 可以发送附件
- 1.
MimeMessageHelper helper=new MimeMessageHelper(message,true);——true 表示可以发送附件` - 2.使用多个
addAttachment()命令,可以直接发送多个文件。
- 1.
public void sendMail() { //附件信息 File f1=new File("D:\\生活\\学习\\西电杭研院\\云共享\\OneDrive\\文档\\云共享\\java\\SpringBoot\\实用篇\\mail\\pom.xml"); File f2=new File("D:\\生活\\学习\\西电杭研院\\云共享\\OneDrive\\文档\\云共享\\java\\SpringBoot\\实用篇\\mail\\src\\main\\resources\\凉冰.jpg"); try { MimeMessage message=javaMailSender.createMimeMessage(); MimeMessageHelper helper=new MimeMessageHelper(message,true); //true 表示可以发送附件 helper.setFrom(mail.getMail_from()+"(diana)");//加个括号,发件人,否则默认是邮箱前缀 helper.setTo(mail.getMail_to()); helper.setSubject(mail.getMail_title()); //发送正文 ——多个setText 后面的会覆盖前面的——要想发送多个内容,要把内容放到一个字符串中 helper.setText(mail.getAhref());//默认是false,显示文本 "<a href='https://www.baidu.com'>点击前往百度</a>" helper.setText(mail.getAhref(),true);//用true 表示发送的html文件 --点击前往百度-- helper.setText(mail.getImg(),true);//多个setText 后面的会覆盖前面的 //发送附件 ——多个addAttachment 会一起发过去,添加多个附件 helper.addAttachment(f1.getName(),f1); helper.addAttachment(f2.getName(),f2); javaMailSender.send(message); } catch (Exception e) { e.printStackTrace(); } }- 可以发送html的标签来加载网站和图片——
# 消息
# 基础概念
消息发送方——生产者
消息接收方——消费者
同步消息——需要等待回复,然后继续进行
异步消息——不需要等待回复,你回不回复,不影响我的运行
消息队列——主服务器接收大量请求后,将服务提交给消息队列,然后让子服务器去消息队列中去取。

企业中的三种异步消息传递技术(规范)
JMS(规范)

AMQP(协议)

MQTT(小型)
消息队列遥测传输,专为小设备设计,是物联网生态系统中主要成分之一。
技术实现
- ActiveMQ
- RabbitMQ
- RocketMQ
- Kafka
# 手工模拟消息队列
- 使用ArrayList集合,模拟简单的消息队列
//手动模拟消息队列实现
@Service
public class MessageServiceImpl implements IMessageService {
private ArrayList<String> msgList=new ArrayList<String>();
@Override //往消息队列中传入订单
public void sendMessage(String id) {
System.out.println("待发送短信的订单已纳入处理队列,id:"+id);
msgList.add(id);
}
@Override//从消息队列中取出订单
public String doMessage() {
String id = msgList.remove(0);//接收从队列中移除的订单id
System.out.println("已完成短信发送业务,id:"+id);
return id;
}
}
# ActiveMQ
启动服务
D:\Program Files\mq\activemq\apache-activemq-5.16.3\bin\win64\activemq.bat双击即可- 网页管理
http://127.0.0.1:8161/
引入坐标
<!-- ActiveMQ--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-activemq</artifactId> </dependency>配置yml
#配置ActiveMQ spring: activemq: broker-url: tcp://localhost:61616 jms: template: default-destination: diana #指定一个destination,相当于是给消息队列起名字 pub-sub-domain: true #是否开启发布订阅模型,默认为false,即默认为点对点模型代码
- 使用接口规范定义,不需要使用具体的实现类,就像使用JDBC模板那样
- 推荐使用监听功能,实现自动消费。
@Autowired private JmsMessagingTemplate messagingTemplate; //接口规范 不需要使用实现类定义 @Override //往消息队列中传入订单 public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列,id:"+id); //messagingTemplate.convertAndSend(id); //放到默认位置 ——default-destination: diana messagingTemplate.convertAndSend("order.queue.id",id);//指定 destination } @Override//从消息队列中取出订单 --------手动从消息队列中取出信息--手动消费 public String doMessage() { // String id = messagingTemplate.receiveAndConvert(String.class);//从默认destination——diana 中取 String id = messagingTemplate.receiveAndConvert("order.queue.id",String.class); System.out.println("已手动完成短信发送业务,id:"+id); return id; }//作为监听器存在,一旦发现消息队列中有消息进来,立即消费掉 @Component public class MessageListener { @JmsListener(destination = "order.queue.id")//--------自动从消息队列中取出信息--自动消费 @SendTo("order.other.queue.id")//将方法的返回值自动发送到另外一个队列——可有可无 public String receive(String id) { System.out.println("已自动完成短信发送业务,id:"+id); return "new:"+id; } }
# RabbitMQ
启动服务
D:\Program Files\mq\rabbitmq\rabbitmq\rabbitmq\rabbitmq_server-3.9.13\sbin下cmdrabbitmq-service.bat strat——启动服务rabbitmq-service.bat stop——关闭服务rabbitmq-service.bat staus——查看启动状态rabbitmq-plugins.bat list——查看所有插件rabbitmq-plugins.bat enable rabbitmq_management—启动客户端插件
启动
rabbitmq_management——默认端口 15672访问网站 (opens new window) guest,guest

导入坐标
<!-- rabbitmq--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency>配置yml
#配置 rabbitmq rabbitmq: host: localhost port: 5672 #服务端口5672,对外管理端口 15672两种模式
直连模式配置——Direct
// rabbitmq 直连 配置 @Configuration public class RabbitmqConfigDirect { @Bean//新建消息队列对象 public Queue directQueue(){ return new Queue("direct_queue"); } @Bean//新建消息队列对象 public Queue directQueue2(){ //durable: 是否持久化,默认false //exclusive: 是否当前连接专用,默认false;如果设为true,则连接关闭后队列即被删除 //autoDelete: 是否自动删除,当生产者或消费者不在使用此队列,自动删除 return new Queue("direct_queue2",true,false,false); } @Bean//新建交换机对象 public DirectExchange directExchange(){ return new DirectExchange("directExchange"); } @Bean//绑定交换机对象和消息队列对象 public Binding bindingDirect(){ return BindingBuilder.bind(directQueue()).to(directExchange()).with("direct"); } @Bean//一个交换机对象可以绑定多个消息队列对象 public Binding bindingDirect2(){ return BindingBuilder.bind(directQueue2()).to(directExchange()).with("direct2"); } }分发模式——Topic
- 与Direct 不同的地方在于 分发功能,可以根据绑定时的名字,根据匹配规则,分发到不同服务

- 当一个服务符合两个绑定的匹配时,会同时将消息发送到两个不同的队列
@Bean//绑定交换机对象和消息队列对象 public Binding bindingTopic(){ //*表示匹配到任意,这样就算有个分发功能,一个绑定名称可以对应多个 到 return BindingBuilder.bind(topicQueue()).to(topicExchange()).with("topic.*.id"); } @Bean//一个交换机对象可以绑定多个消息队列对象 public Binding bindingTopic2(){ //当一个业务匹配到多个绑定对象时,会将消息分发到多个队列 return BindingBuilder.bind(topicQueue2()).to(topicExchange()).with("topic.orders.*"); }主服务代码——两种方式代码是一样的——绑定名称不一样
@Autowired private AmqpTemplate amqpTemplate; @Override—————————————————————————————————————————————————————直连模式———————————一一对应 public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列,id:"+id); //directExchange交换机对象,direct名字--对应绑定名称,一一对应 amqpTemplate.convertAndSend("directExchange","direct",id); } @Override——————————————————————————————————————————————————topic模式——————————————匹配规则对应 public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列,id:"+id); //topicExchange交换机对象名称,topic.orders.id --对应绑定名称,有分发模式,匹配到即可 amqpTemplate.convertAndSend("topicExchange","topic.orders.id",id); }监听器——两种方式代码是一样的——队列名称不一样
@RabbitListener(queues = "direct_queue")
@RabbitListener(queues = "direct_queue")//-----直连模式---自动从消息队列中取出信息--自动消费 public void receive(String id) { System.out.println("a1已自动完成短信发送业务,id:"+id); } @RabbitListener(queues = "topic_queue")//----topic模式----自动从消息队列中取出信息--自动消费 public void receive(String id) { System.out.println("queue1已自动完成短信发送业务,id:"+id); }- 当多个监听器同时监听一个队列时,多个监听器会轮询操作
# RocketMQ
启动服务
解决java安装路径带空格问题 (opens new window)
修改
runserver.cmd和runbroker.cmd- 1.将
JAVA_HOME定义成字符串 ——set JAVAHOME="%JAVA_HOME%" - 2.在倒数第三行中使用
%JAVAHOME%替换掉原来的%JAVA_HOME%——set "JAVA_OPT=%JAVA_OPT% -Djava.ext.dirs=%BASE_DIR%lib;%JAVAHOME%\jre\lib\ext" - 3.在倒数第二行中,将
%CLASSPATH%修改为"%CLASSPATH%",加了一对英文双引号
- 1.将
启动流程
- 先启动
mqnamesrv.cmd,双击即可启动命名服务器 - 再启动
mqbroker.cmd,双击即可启动业务服务器 - 服务器功能测试:生产者
tools org.apache.rocketmq.exemple.quickstart.Producer - 服务器功能测试:消费者
tools org.apache.rocketmq.exemple.quickstart.Consumer
- 先启动
导入坐标
<!-- rocketmq--> <dependency> <groupId>org.apache.rocketmq</groupId> <artifactId>rocketmq-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency>配置yml
#配置rocketmq rocketmq: name-server: localhost:9876 producer: group: group_rocketmq #初始化生产者组名(随便起)代码
- service
@Autowired private RocketMQTemplate rocketMQTemplate; @Override public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列,id:"+id); // rocketMQTemplate.convertAndSend("order_id",id);//同步消息 //定义回调方法 SendCallback callback=new SendCallback() { @Override public void onSuccess(SendResult sendResult) { System.out.println("发送成功!!"); } @Override public void onException(Throwable throwable) { System.out.println("发送失败!!"); } };//发送异步消息 rocketMQTemplate.asyncSend("order_id",id,callback); }- listener ——实现接口,注解写在上方
@Component//监听器 //destination 的名称(同存入消息时的名称), 消费者组的名称(随便起),这里让消费者和生产者同组 @RocketMQMessageListener(topic = "order_id",consumerGroup = "group_rocketmq") public class MessageListener implements RocketMQListener<String> { @Override public void onMessage(String id) { System.out.println("已自动完成短信发送业务,id:"+id); } }
# Kafka
启动服务
- 启动zookeeper
zookeeper-server-start.bat ../../config/zookeeper.properties- 默认端口 2181
- 启动kafka
kafka-server-start ..\..\config\server.properties- 默认端口9092
- 创建topic
kafka-topics --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic diana - 查看topic
kafka-topics --zookeeper localhost:2181 --list - 删除topic
kafka-topics --delete --zookeeper localhost:2181 --topic diana - 生产者功能测试
kafka-console-producer --broker-list localhost:9092 --topic diana - 消费者功能测试
kafka-console-consumer --bootstrap-server localhost:9092 --topic diana --from-beginning
- 启动zookeeper
导入坐标
<!-- kafka--> <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency>配置yml
#配置kafka kafka: bootstrap-servers: localhost:9092 consumer: group-id: order #配置消费者组id代码
- service
@Autowired private KafkaTemplate<String,String> kafkaTemplate; @Override public void sendMessage(String id) { System.out.println("待发送短信的订单已纳入处理队列(kafka),id:"+id); kafkaTemplate.send("diana",id);//前面是topic 名称,后面是要送入队列的消息 }- listener
@KafkaListener(topics = "diana")//设置topic名称 public void onMessage(ConsumerRecord<String,String> record) { System.out.println("已自动完成短信发送业务(kafaka),id:"+record.value()); }
