SpringBoot高并发

目录

Spring cache

简介:

操作步骤

添加缓存@Cacheable

keyGenerator

condition

unless

sync

更新缓存 @CachePut

缓存清除@CacheEvict

组合缓存规则@Caching

抽取缓存的公共配置@CacheConfig

SpringBoot整合enCache

需要引入的jar:

ehcache和spring cache的区别

分布式-redis缓存

redis介绍

使用场景

序列化问题

redis的五种数据类型

java操作redis

消息中间件

RabbitMQ

安装

docker安装RabbitMQ

本地计算机安装RabbitMQ

核心概念

消息Message

消息的生产者(Producer)

交换器(Exchange)

1.Fanout(订阅模式/广播模式)

基本概念:

代码示例:

2.Direct(点对点模式/路由模式/单播模式)

基本概念:

代码示例:

3.Topic(通配符模式/主题交换器)

基础概念

代码示例


Spring cache

简介:

Spring Cache是一个框架,实现了基于注解的缓存功能。只需要简单地加一个注解,就能实现缓存功能。

Spring Cache提供了一层抽象,底层可以切换不同的cache实现。具体就是通过CacheManager接口来统一不同的缓存技术CacheManager是Spring提供的各种缓存技术抽象接口。

针对不同的缓存技术需要实现不同的CacheManager:

CacheManager

描述

EhCacheCacheManager

使用EhCache作为缓存技术

GuavaCacheManager

使用Google的GuavaCache作为缓存技术

RedisCacheManager

使用Redis作为缓存技术

操作步骤

  1. 引入依赖

对于Spring boot项目,只需要引入web启动器就会把Spring-cache引入,所以不需要单独导springboot的包(cache是Springboot提供的)

  1. 开启缓存开关
@SpringBootApplication
@EnableCaching  //缓存开关
public class 启动器类名(){}
  1. 基本用法
@RestController
public class UserController(){
    //默认的缓存管理器用的是ConcurrentMapCacheManager
    //弊端是,每次项目重启,上次缓存好的数据就会清空
    @Autowired
    CacheManager cacheManager;
    @Autowired
    UserService userService;

    @GetMapping
    @Cacheable(value="user",key="#root.targetClass+'.users'")
    //value也可以使用另一种写法:cacheName={"user"},等同于:value="user"
    public List<User> getList(){
        return userService.lsit();
    }

    @GetMapping("{id}")
    @Cacheable(value="user",key="#id")  //注:此处的#id引用的是方法参数id
    public User getById(@PathVariable Integer id){
        return userService.getById(id);
    }

    @PostMapping("/add")
	@CachePut(value="user",key="#user.id")  //注:此处的user.id,引用的为插入数据后回填的id
    public User add(@RequestBody User user){
        userService.saveOrUpdate(user);
        return user;
    }
	
    @PutMapping("/update")
    @CachePut(value="user",key="#user.id")
    public User update(@RequestBody User user){
        userService.saveOrUpdate(user);
        return user;
    }

    @DeleteMapping("{id}")
    @CacheEvict(value="user",key="#id")
    public void del(@PathVariable Integer id){
        userService.removeById(id);
    }
}

添加缓存@Cacheable

  • cacheName:也可以直接写value,指定缓存组件的名字;将方法的返回结果存放在哪个缓存区中,是数组的方式,可以指定多个缓存
  • key:缓存数据使用的key可以用它来指定,默认是使用方法参数的值,
    • #id:参数id的值
    • #a0 #p0 #root. args[0]
keyGenerator

key的生成器;可以自己指定key的生成器的组件id

参考网址:no12-springboot高并发第一次课-spring缓存和redis-[Java][springboot][springboot学习]博客-jf3q杰凡IT问答

condition

指定符合条件的情况下才缓存

例如:condition="#a0>1"就是说当第一个参数大于0的时候才进缓存

//这里的#a0也就是#id   
@Cacheable(value = {"emp"},keyGenerator = "mykeyGenerator",condition = "#a0>1")
    public Employee getById(Integer id){
        return employeeDao.getEmpId(id);
    }
unless

否定缓存,与condition相反,可以理解为如果不满足条件就进缓存,如果满足条件就不进缓存

例如:condition="#a0>1"就是说当第一个参数小于0的时候才进缓存

@Cacheable(value = {"emp"},keyGenerator = "mykeyGenerator",unless = "#id ==2")
public Employee getById(Integer id){
    return employeeDao.getEmpId(id);
}
sync

是否使用异步模式。默认是false。当使用sync注解的时候,unless就不能用了。

更新缓存 @CachePut

修改了数据库的某个数据,同时更新缓存;

新增数据,并添加到缓存中;

@PutMapping("/update")
@CachePut(value="user",key="#user.id")
public User update(@RequestBody User user){
    userService.saveOrUpdate(user);
    return user;
}

@PostMapping("/add")
@CachePut(value="user",key="#user.id")  //注:此处的user.id,引用的为插入数据后回填的id
public User add(@RequestBody User user){
    userService.saveOrUpdate(user);
    return user;
}

缓存清除@CacheEvict

  1. key:指定要清除的数据
  2. allEntries=true:是否清除这个缓存区中所有的数据
  3. beforeInvocation
    1. beforeInvocation=false:缓存的清除是否在方法前执行,默认代表缓存清除操作是在方法执行之后执行,如果出现异常缓存就不会清除;
    2. beforeInvocation=true:代表缓存的清除是在方法前执行,无论是否发生异常都删除缓存
    @DeleteMapping("{id}")
    @CacheEvict(value="user",key="#id")
    public void del(@PathVariable Integer id){
        userService.removeById(id);
    }
//同时删除多个缓存
    @Caching(evict = {
         @CacheEvict(value = {"category"}, key = "'test'"),
         @CacheEvict(value = {"category"}, key = "'test1'")
    })
    //指定删除某个分区一下的所有数据
    @CacheEvict(value = {"category"}, allEntries = true)

组合缓存规则@Caching

例子:根据名字条件查询完员工信息后,让根据id查询的时候也使用缓存

 @Caching(
     //使用name查询完后,使用参数name作为key添加对象到缓存中
    cacheable = {
            @Cacheable(value = "emp",key = "#name")
    },
     //随后使用更新缓存,从返回的对象中getID属性为key,也添加到缓存中
    put = {
            @CachePut(value = "emp",key = "#result.id"),
    }
)
public Employee getByName(String name){
    return employeeDao.getByName(name);
}

例子:添加员工信息后,同步更新缓存中员工信息的集合

 @Caching(
     //将插入员工信息添加到缓存
    put = {
        @CachePut(value = "emp",key = "#result.id"),
    }
    //删除缓存中的集合,这样下次查询的时候就是从数据库中读取到的新员工信息集合,从而实现同步更新集合
    evict={
        @CacheEvict(value = "emp",key = "empList")
    }
)
public Employee add(Employee employee){
	service.save(employee);
    return employee;
}

同理,每次删除或者修改数据的时候,都可以使用组合缓存规则同步更新缓存中集合的数据

抽取缓存的公共配置@CacheConfig

同一个Service或Controller的缓存注解中value很多的值都是重复的,每次都写一样的会造成代码冗余,@CacheConfig则将公共的属性给抽取了出来

@CacheConfig(cacheNames = "emp")//cacheNames>cacheName属性,等同于Value
public class service类或者Controller类{}

SpringBoot整合enCache

需要引入的jar:

 <dependency>
      <groupId>net.sf.ehcache</groupId>
      <artifactId>ehcache</artifactId>
      <version>2.10.6</version>
  </dependency>

参考文章:

SpringBoot之——配置EHCache_冰河的技术博客_51CTO博客

ehcache和spring cache的区别

10 | 技术选型:Ehcache、Guava Cache、Spring Cache - 简书

注:ehcache和spring cache不是一回事

分布式-redis缓存

redis介绍

Redis是目前使用最广泛的内存数据存储系统之一。它支持更丰富的数据结构,支持数据持久化、事务、HA高可用、双机集群系统,主从库。

Redis是Key-value存储系统。它支持的value类型包括String、List、Set、 Zset和hash。 这些数据都支持push/pop/add/remove,以及取交集、并集、差集、或更丰富的操作,而这些操作都是原子性的。

使用场景

  • 1.缓存

缓存现在几乎是所有中大型网站都在用的必杀技,合理的利用缓存不仅能够提升网站访问速度,还能大大降低数据库的压力。Redis提供了键过期功能.也提供了灵活的键淘汰策略,所以,现在Redis用在缓存的场合非常多

  • 2.排行榜

很多网站都有排行榜应用的,如京东的月度销量榜单、商品按时间的上新排行榜等。Redis提供的有序集合数据类构能实现各种复杂的排行榜应用。

  • 3.计数器

什么是计数器,如电商网站商品的浏览量、视频网站视频的播放数等。为了保证数据实时效,每次浏览都得给+1 ,并发量高时如果每次都请求数据库操作无疑是种挑战和压力。Redis提供的incr命令来实现计数器功能,内存操作,性能非常好,非常适用于这些计数场景。

  • 4.分布式会话

集群模式下,在应用不多的情况下一般使用容器 自带的session复制功能就能满足,当应用增多相对复杂的系统中,一般都会搭建以Redis等内存数据库为中心的session服务, session不再由容器管理,而是由session服务及内存数据库管理。

  • 5.分布式锁

很多互联网公司中都使用了分布式技术,分布式技术带来的技术挑战是对同一个资源的并发访问,如全局ID、减库存、秒杀等场景,并发量不大的场景可以使用数据库的悲观锁、乐观锁来实现,但在并发量高的场合中,利用数据库锁来控制资源的并发访问是不太理想的,大大影响了数据库的性能。可以利用Redis的setnx功能来编写分布式的锁,如果设置返回1说明获取锁成功。否则获取锁失败,实际应用中要考虑的细节要更多。

  • 6.社交网络

点赞、踩、关注/被关注、共同好友等是社交网站的基本功能,社交网站的访问量通常来说比较大,而且传统的关系数据库类型不适合存储这种类型的数据, Redis提供的哈希、集合等数据结构能很方便的的实现这些功能。

  • 7.最新列表

Redis列表结构, LPUSH可以在列表头部插入一个内容ID作为关键字, LTRIM可用来限制列表的数量,这样列表永远为N个ID ,无需查询最新的列表,直接根据ID去到对应的内容页即可。

  • 8.消息系统

消息队列是大型网站必用中间件,如ActiveMQ、RabbitMQ、 Kafka等流行的消息队列中间件,主要用于业务解耦、流星削峰及异步处理实时性低的业务。Redis提供了发布/订阅及阻塞队列功能,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。

序列化问题

我们将数据存入redis缓存时,会发现redis中的数据与主键都是二进制的数据,这种现象并不影响程序运行,但是非常影响程序员与缓存的交互操作

解决方案:添加一个redis配置类,在配置类中设置编码格式


@Configuration
public class MyRedisConfig  {

    @Bean
    //参数RedisConnectionFactory为redis连接工厂,由springboot自动填充,不需要手动添加参数
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
        RedisTemplate redisTemplate = new RedisTemplate();
        //添加redis连接工厂
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //设置redis的key序列化程序
        redisTemplate.setKeySerializer(RedisSerializer.string());
        //设置redis值的序列化程序
        redisTemplate.setValueSerializer(RedisSerializer.json());
        return redisTemplate;
    }
}

redis的五种数据类型

Redis支持五种数据类型: string (字符串) , hash(哈希) , list(列表) , set(集合)及zset(sorted

set:有序集合)。

  • 1.String (字符串)

string是redis 最基本的类型,你可以理解成与Memcached 一模一样的类型,一个 key对应一个 value.string类型是二进制安全的。意思是redis的string可以包含任何数据。比如jpg图片或者序列化的对象(实体类上必须实现序列化)。

string类型是Redis最基本的数据类型, string 类型的值最大能存储512MB.

常用命令:set, get, decr, incr, mget等。

注意:一个键最大能存储512MB.

  • 2.Hash(哈希)

Redis hash是一个键值(key=>value)对集合 ;是一个string类型的field和value的映射表, hash特别适合用于存储对象。

每个hash可以存储232-1键值对(40多亿)。

常用命令: hget. hset. hgetall等。

应用场景:存储一些结构化的数据.比如用户的昵称、年龄、性别、积分等,存储一个用户信息对象数据。

  • 3.List (列表)

Redis列表是简单的字符串列表,按照插入顺序排序。你可以添加一个元素到列表的头部(左边)或者尾部(右边).

list类型经常会被用于消息队列的服务,以完成多程序之间的消息交换。

常用命令: Ipush. rpush. lpop. rpop. Irange等。

列表最多可存储232- 1元素(4294967295,每个列表可存储40多亿)。

  • 4.Set(集合)

Redis的Set是string类型的无序集合。和列表一样,在执行插入和删除和判断是否存在某元素时,效率是很高的。集合最大的优势在于可以进行交集并集差集操作。Set可包含的最大元素数量是4294967295.集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。

应用场景:

1.利用交集求共同好友。

2.利用唯-性,可以统计访问网站的所有独立IP。

3.好友推荐的时候根据tag求交集,大于某个threshold (临界值的)就可以推荐。

常用命令: sadd、spop、 smembers. sunion等。

集合中最大的成员数为232 - 1(4294967295,每个集合可存储40多亿个成员)。

  • 5.zset(sorted set :有序集合)

Redis zset和set一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。 redis正是通过分数来为集合中的成员进行从小到大的排序。

zset的成员是唯一的,但分数(score)却可以重复。sorted set是插入有序的,即自动排序。

常用命令: zadd、zrange、 zrem、 zcard等。

查询所有:zrange xxx 0 -1

查询某个成员的分数:zscore xxx yyy

当你需要-一个有序的并且不重复的集合列表时,那么可以选择sorted set数据结构。

应用举例:

(1 )例如存储全班同学的成绩,其集合value可以是同学的学号,而score就可以是成绩。

(2)排行榜应用,根据得分列出topN的用户等。

java操作redis

  • RedisTemplate

Spring封装了RedisTemplate来操作Redis,它支持所有的Redis原生的API。在RedisTemplate中定义了5中数据接口的操作方法。

1.opsForValue():操作字符串。

2.opsForHash():操作散列(是一个String类型的filed和value的映射表,hash特别适合用于存储对象,value中放的是结构化对象)。

3.opsForList( ):操作列表(是简单的字符串列表,按照插入顺序排序,可以添加一个元素到列表的头部或尾部)。

4.opsForSet( ):操作集合(set是存放不重复值的集合,利用set可以做全局去重的功能,还可以进行交集并集差集等操作。

5.opsForZset( ):操作有序集合。

package com.QianMo.service.impl;

import com.QianMo.entity.SysUser;
import com.QianMo.mapper.SysUserMapper;
import com.QianMo.service.IScoreService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.Set;

@Service
public class IScoreServiceImpl implements IScoreService {
    private String key = "QianMo:user:score";
    @Resource
    SysUserMapper userMapper;
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void incrementScore(Serializable id) {
        SysUser user = userMapper.selectById(id);
        /*
        opsForZSet:操作有序集合
        incrementScore:插入数据到有序集合中,
        第一个参数为key(主键),第二参数为数据,第三个参数为自增的大小(1.0就是score每次都自增1.0)
        */
        redisTemplate.opsForZSet().incrementScore(key, user, 1.0);
    }

    @Override
    public Set list(int range) {
        /*
        reverseRangeWithScores:从排序集中获取元素
        第一个参数为key(主键),第二个参数为开始索引,第二个参数为结束索引
        ZSet的索引从0开始,此处表示从提取从0到range参数代表值范围内的元素集合
        ZSetOperations.TypedTuple<SysUser>:表示提取出来的集合包含score和元素
        */
        Set<ZSetOperations.TypedTuple<SysUser>> set 
        = redisTemplate.opsForZSet().reverseRangeWithScores(key, 0, range);
        return set;
    }
}

此处:提取集合中元素时,存在两种情况,需要score和不需要score

  • 需要score
Set<ZSetOperations.TypedTuple<SysUser>> set = redisTemplate.opsForZSet().reverseRangeWithScores("user:list", 0, 4);

ZSetOperations.TypedTuple<SysUser>的底层实现如下:

  • 不需要score
Set<SysUser>> set = redisTemplate.opsForZSet().reverseRangeWithScores("user:list", 0, 4);

思考:redis可能会存在的问题:缓存击穿,缓存穿透,缓存雪崩?

总结:当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,

但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis 里面取出一一个对象,那么使用RedisTemplate是更好的选择。

消息中间件

RabbitMQ

RabbitMQ是个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。翻译过来就是高级消息队列协议

应用场景

  1. 异步处理
  2. 应用解耦
  3. 流量削峰
  4. 消息通信

安装

docker安装RabbitMQ

#使用finalShell连接完虚拟机后,使用命令打开docker
sudo systemctl start docker
#安装RabbitMQ镜像,此处为安装最新的RabbitMQ
docker pull rabbitmq
#创建容器:docker run为创建容器 -d:后台启动服务器
#--name为容器名称   -p指定服务器运行的端口,5672为应用访问端口,15672为控制台Web端口号
#rabbitmq为镜像名
docker run --name mr -p 15672:15672 -p 5672:5672 -d rabbitmq 
#启动容器
docker start 容器id

常见问题:

创建rabbitMQ容器并运行后,无法访问web管理界面


#运行交互式的Bash shell
docker exec -it 容器id /bin/bash
#打开sbin文件夹
cd sbin
#执行命令打开web界面管理插件
rabbitmq-plugins enable rabbitmq_management
#查看插件是否启动
rabbitmq-plugins list

如果确定已经将插件打开,则检查Linux防火墙

  1. 打开虚拟机,进入linux自带的浏览器

  2. 输入访问链接:ip:15672,如果能在Linux自带的浏览器打开,则说明是Linux防火墙的问题
#停止防火墙服务
systemctl stop firewalld 
#确保下次系统重启时不会自动启动防火墙
systemctl disable firewalld
  1. 使用windows浏览器访问RabbitMQ的web管理界面

注:docker下载的RabbitMQweb界面管理路径为:端口ip:15672

本地计算机安装RabbitMQ

按顺序安装Erlang>RabbitMQ

注:所有安装包都是无脑下一步就行,只需要注意安装路径

常见问题:

1.如果无法打开可视化管理工具,则在RabbitMQ安装路径的sbin目录下,打开cmd,安装插件

rabbitmq-plugins enable rabbitmq_management

2.本机下载的RabbitMQ是不允许用户名和安装路径存在中文的,因此,如果下载时电脑用户已经是中文名,不需要修改,哪怕是修改也没有用处,解决方案如下:

  • 步骤一: 需要使用管理员权限的cmd, 进入到sbin目录下
  • 步骤二:删除本来的服务
 rabbitmq-service.bat remove
  • 步骤三: (此处的路径为RabbitMQ安装路径下的data文件夹,自己按需求更改),不要有中文和空格
 set RABBITMQ_BASE=D:\rabbitmq\data
  • 步骤四: (重新安装服务)
rabbitmq-service.bat install
  • 步骤五: (安装管理插件,就是可视化)
rabbitmq-plugins enable rabbitmq_management
注:如果后期还要安装插件,需要先执行步骤三,不然会提示你已经修改了路径
  • 步骤六:重新启动RabbitMQ
rabbitmq-server.bat

打开界面管理后如图(本机下载的RabbitMQ路径为Localhost:15672),localhost=本机

账户密码默认为guest

核心概念

消息Message

由消息头和消息体组成。

消息体是不透明的,而消息头是由一系列的可选属性组成的。

这些属性routing-key (路由键)、priority (相对于其他消息的优先权)、delivery-mode (指出

该消息可能需要持久性存储)等。

消息的生产者(Producer)

一个向交换器发布消息的客户端应用程序

交换器(Exchange)

用来接收生产者发送的消息并将这些消息按照路由规则给到消息队列

Exchange有4种类型: direct(默认), fanout, topic,和headers,常用的为前三种

1.Fanout(订阅模式/广播模式)
基本概念:

交换器会把所有发送到该交换器的消息路由到所有与该交换器绑定的消息队列中。订阅模式
与Binding Key和Routing Key无关,交换器将接受到的消息分发给有绑定关系的所有消息队列队列(不论Binding Key和Routing Key是什么)。类似于子网广播,子网内的每台主机都获得了一份复制的消息。Fanout交换机转发消息是最快的

代码示例:

创建一个配置类,配置一个广播交换器,按需求配置多个消息队列

/*广播交换器*/
@Configuration
public class FanoutConfig {
    @Bean
    FanoutExchange fanoutExchange(){
        return new FanoutExchange("fanout.news");
    }
    //it部消息队列
    @Bean
    Queue queueIt(){
        return new Queue("fanout.news.queueIt");
    }
    //市场部消息队列
    @Bean
    Queue queueMark(){
        return new Queue("fanout.news.queueMark");
    }
    //市场部的绑定关系
    @Bean
    Binding bindingMark(){
        return BindingBuilder.bind(queueMark()).to(fanoutExchange());
    }
    //It部的绑定关系
    @Bean
    Binding bindingIt(){
        return BindingBuilder.bind(queueIt()).to(fanoutExchange());
    }
}

模拟一个消息生产者

@Autowired
RabbitTemplate rabbitTemplate;
//广播生产者
@GetMapping("/fanout")
public void fanout(){
    rabbitTemplate.convertAndSend("fanout.news",null,"新闻信息");
}

模拟一个消费者

@ @RabbitListener(queues = "fanout.news.queueIt"):配置当前方法监听的消息队列

/*广播消费者*/
@RestController
public class FanoutController  {
    @RabbitListener(queues = "fanout.news.queueIt")
    public void show(String message){
        System.out.println("It部门接收到的新闻信息: "+message);
    }
    @RabbitListener(queues = "fanout.news.queueMark")
    public void show2(String message){
        System.out.println("市场部门接收到的新闻信息: "+message);
    }
}
2.Direct(点对点模式/路由模式/单播模式)
基本概念:

精确匹配:当消息的Routing Key(消息秘钥)与 Exchange和Queue 之间的Binding Key(绑定秘钥)完全匹配,如果匹配成功,将消息分发到该Queue。只有当Routing Key和Binding Key完全匹配的时候,消息队列才可以获取消息。Direct是Exchange的默认模式。
RabbitMQ默认提供了一个Exchange,名字是空字符串,类型是Direct,绑定到所有的Queue(每一个Queue和这个无名Exchange之间的Binding Key是Queue的名字)。所以,有时候我们感觉不需要交换器也可以发送和接收消息,但是实际上是使用了RabbitMQ默认提供的Exchange。

代码示例:

同上,先添加一个配置类,添加一个点对点交换器,消息队列,路由

/**
 * RabbitMQ配置类
 * 交换机
 * 消息队列
 * 绑定关系
 */
@Configuration
public class DirectConfig {
    //创建了一个交换器 -direct
    @Bean
    DirectExchange directExchange() {
        return new DirectExchange("direct.email", true, false);
    }

    //装配消息队列
    @Bean
    Queue queue() {
        return new Queue("email.queue");
    }
	//绑定信息,将交换器和消息队列绑定到一起
    @Bean
    Binding binding() {
        return BindingBuilder.bind(queue()).to(directExchange()).with("email.queue.direct");
    }
}

模拟生产者

    //单播生产者
    @GetMapping("/direct")
    public void direct
	//三个参数分别为:交换器,路由,发送的信息
        rabbitTemplate.convertAndSend("direct.email","email.queue.direct","3131934665@qq.com");
    }

模拟消费者

//监听的消息队列
@RabbitListener(queues = "email.queue")
public void show(String message){
System.out.println("接收到的邮件信息: "+message);
}

3.Topic(通配符模式/主题交换器)
基础概念

按照正则表达式模糊匹配:用消息的Routing Key与 Exchange和Queue 之间的Binding Key进行模糊匹配,如果匹配成功,将消息分发到该Queue。
Routing Key是一个句点号“. ”分隔的字符串(我们将被句点号“. ”分隔开的每一段独立的字符串称为一个单词)。Binding Key与Routing Key一样也是句点号“. ”分隔的字符串。Binding Key中可以存在两种特殊字符“ * ”与“#”,用于做模糊匹配,其中“*”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)

代码示例

在配置类中添加交换器,消息队列和路由

/*主题交换器*/
@Configuration
public class TopicConfig {
    @Bean
    public TopicExchange topicExchange(){
        return new TopicExchange("topic.address");
    }
    /*中国的消息队列*/
    @Bean
    Queue queueChina(){
        return new Queue("China.queue");
    }
    /*俄罗斯的消息队列*/
    @Bean
    Queue queueRussia(){
        return new Queue("Russia.queue");
    }
    /*天气的消息队列*/
    @Bean
    Queue queueWeather(){
        return new Queue("Weather.queue");
    }
    /*新闻的消息队列*/
    @Bean
    Queue queueNews(){
        return new Queue("News.queue");
    }
    @Bean
    Binding ChinaBinding(){
        return  BindingBuilder.bind(queueChina()).to(topicExchange()).with("China.#");
    }
    @Bean
    Binding RussiaBinding(){
        return  BindingBuilder.bind(queueRussia()).to(topicExchange()).with("Russia.#");
    }
    @Bean
    Binding WeatherBinding(){
        return  BindingBuilder.bind(queueWeather()).to(topicExchange()).with("#.Weather");
    }
    @Bean
    Binding NewsBinding(){
        return  BindingBuilder.bind(queueNews()).to(topicExchange()).with("#.News");
    }
}

模拟生产者

 //主题生产者
    @GetMapping("/topic")
    public void topic(){
        rabbitTemplate.convertAndSend("topic.address","China.News","中国的新闻");
    }

模拟消费者


//主题消费者
@RestController
public class TopicController {
    @RabbitListener(queues = "China.queue")
    public void  showChina(String message){
        System.out.println(message);
    }
}

使用前,需注意application.properties文件或者application.yml文件的配置

计算机本地下载:

#服务器主机地址
spring.rabbitmq.host=localhost
#端口号
spring.rabbitmq.port=5672
#将虚拟主机设定为本机
spring.rabbitmq.virtual-host=/
#密码
spring.rabbitmq.password=guest
#账号
spring.rabbitmq.username=guest

docker下载:

#默认为localhost(本机),但docker下载都是下载到虚拟机上的,因此需要将默认主机修改成虚拟机ip
spring.rabbitmq.host=虚拟机ip

导入依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值