RabbitMQ的五种工作模式

RabbitMQ的五种工作模式

RabbitMQ的作用

1、服务解藕:降低服务之间耦合度

2、流量削峰:当高峰情况下,出现大量的请求时,将请求发送到消息队列服务器上,排队等待被处理

3、异步调用:将数据发送到消息队列服务器中,系统可以得到立即响应,整条链路的响应时间只有200毫秒左右

所需的model类

public  interface Constants {
     String Sub_Teacher="校长";
     String SUB_EXCHANGE = "yuque52";
     String SIMPLE_QUEUE = "query52";
     String ROUTER_RIBBIT="路由模式";
     String TOPIC_RIBBIT="主题模式";
}
public class ChannelUtil {
    public static Channel channel(){
        //建立连接
        try {
        ConnectionFactory cf = new ConnectionFactory();
        cf.setHost("localhost");
        cf.setPort(5672);
        cf.setUsername("guest");
        cf.setPassword("guest");
        cf.setVirtualHost("/");
        Connection connection = null;

        connection = cf.newConnection();
        //建立信道
        Channel channel = connection.createChannel();
        return  channel;
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
      return null;
    }
}

一:简单模式(点对点模式)

原理:生产者将消息交给默认的交换机,交换机获取消息后交给绑定这个生产者的队列(投递规则为队列名称和routing key 相同的队列),监听当前队列的消费者获取信息并执行消费逻辑。

使用场景:用户通过接收手机验证码进行注册,页面上点击获取验证码后,将验证码放到消息队列,然后短信服务从队列获取到验证码****,并发送给用户、聊天室

img

一个生产者投递消息给队列 只能允许有一个消费者进行消费 如果集群的话 会进行均摊消费 服务器配置不一样 均摊就不优了,一个生产者投递消息给队列 只能允许有一个消费者进行消费 ,如果集群的话,会进行均摊消费 ,服务器配置不一样,均摊就不优了
生产者:

public class SimplePublisher {

    public static void main(String[] args) throws IOException, TimeoutException {
        //创建交换机
        //创建消息队列
         /*
                queue – 队列名
                durable – 是否持久化,如果为true则会持久化 (服务器重启后仍存在)
                exclusive – 是否独占,true则为独占队列,其他链接不能使用
                autoDelete – 是否自动删除消息,true则在使用完后删除
                arguments – map类型,其他参数
            * */
        Channel channel = ChannelUtil.channel();
        channel.queueDeclare("queue52",false,false,true,null);

        //发送消息
        /*
         exchange 交换机
         routing-key 路由键
         properties 属性
         body 消息体
         */
        Scanner scanner = new Scanner(System.in);
        print("请输入你要发送的消息");
        String msg =scanner.next();
        channel.basicPublish("","queue52",null,msg.getBytes("UTF-8"));
        System.out.println("[x] 消息发送成功! "+msg);
    }
    private static void print(String str) {

        System.out.println("发布者:"+str);

    }
}

消费者:

public class SimpleConsumer {
    public static void main(String[] args) throws IOException {
        //建立连接
        //建立信道
        Channel channel = ChannelUtil.channel();

        //创建交换机

        //创建消息队列
        //绑定交换机
        //接受消息
        channel.basicConsume("queue52",
                (consumerTag,delivery)->{
                    //处理消息
                    byte[] body = delivery.getBody();
                    String message = new String(body,"utf-8");
                    System.out.println("[x] 收到消息"+message);
                },(consumerTag)->{});
    }
}

二:工作队列 (单发送多接收

默认是Round-robin

原理:生产者将消息交给交换机,交换机交给绑定的队列,队列有多个消费者监听,一条消息只能由一个消费者消费,这样就形成了资源竞争,谁的资源空闲大,争抢到的可能性就大。

使用场景:即工作竞争模式或者又称消费者竞争模式。即谁先拿到消息,谁来负责 消费。常见的业务场景有抢红包,秒杀大型项目中的资源调度过程(直接由最空闲的系统争抢到资源处理任务) ,一个电商平台,有两个订单服务,用户下单的时候,任意一个订单服务消费用户的下单请求生成订单即可。不用两个订单服务同时消费用户的下单请求,分布式的任务派发,为了保证消息发送的可靠性,不丢失,使消息持久化,同时为了防止接收端在处理消息时down掉,只有在消息处理完成后才发送ack消息。

img

循环调度,轮流所有的消费者 发送消息。当消费者稳定运行时,他们接受的消息数量是一样的。体现在工作任务的派发时,他是"公平派遣"。
生产者:

public class WorkPublisher {
    public static void main(String[] args)throws IOException, TimeoutException  {
        //建立连接和通道
        Channel channel = ChannelUtil.channel();
        //创建交换机,类型是FANPUT
        channel.queueDeclare(Constants.Sub_Teacher, false,false,false,null);
        //创建队列到消费者端执行
        Scanner scanner = new Scanner(System.in);
        while (true){
            print("请输入你要发送的消息");
            String msg=scanner.next();
            if ("q".equals(msg)){
                break;
            }
            //发送给交换机    不提供routing-key
            channel.basicPublish("",Constants.Sub_Teacher,null,msg.getBytes("utf-8"));
            print("消息发送成功:  "+msg);

        }
    }
    private static void print(String str) {
        System.out.println("发布者:"+str);
    }
}

消费者


public class WorkConsumer {
    private String name;

    public WorkConsumer(String name) {
        this.name = name;
    }
    public void consume() throws IOException {
        //建立信道和连接
        Channel channel = ChannelUtil.channel();
        //创建消息队列
        //绑定在交换机上
        //监听队列中的消息接收消息
        channel.basicConsume(
                Constants.Sub_Teacher,
                true,
                (consumerTag,delivery)->{
                    //处理消息
                    byte[] body = delivery.getBody();
                    String message = new String(body,"utf-8");
                    System.out.println(name + " 收到消息是:"+message);
                },(consumerTag)->{});

    }

三:发布订阅模式

原理:生产者将消息扔给交换机,交换机类型是fanout,不同的队列注册到交换机上,不同的消费注册在不同的队列上。所有消费者都会收到消息。

场景:有一个商城,我们新添加一个商品后,可能同时需要去更新缓存和数据库。邮件群发,群聊天,广告

交换机没有保存功能,如果没有消费者,则消息会丢失。

img
生产者:


public class SubPublisher {
    public static void main(String[] args) throws IOException, TimeoutException {
        //建立信道和连接
        Channel channel = ChannelUtil.channel();
        //创建交换机,类型是FANOUT
        channel.exchangeDeclare(Constants.Sub_Teacher, BuiltinExchangeType.FANOUT);

        //创建队列到消费者执行
        //发送消息
        Scanner scanner = new Scanner(System.in);
        while (true){
            print("请输入你要发送的消息");
            String msg=scanner.next();
            if ("q".equals(msg)){
                break;
            }
            //发送给交换机,不提供routing-key
            channel.basicPublish(Constants.Sub_Teacher,"",null,msg.getBytes("utf-8"));
            print("消息发送成功:"+msg);
        }
    }
    private static void print(String str) {
        System.out.println("发布者:"+str);
    }
}

消费者


public class SubConsumer {
    private String name;

    public SubConsumer(String name) {
        this.name = name;
    }
    public void consume() throws IOException {
        //建立信道和连接
        Channel channel = ChannelUtil.channel();
        //创建交换机
        //创建消息队列
        String ribbitName=channel.queueDeclare().getQueue();
        //绑定交换机
        channel.queueBind(ribbitName, Constants.Sub_Teacher,"");
        //接收消息
        channel.basicConsume(ribbitName,true,
                (consumerTag,delivery)->{
                    //处理消息
                    byte[] body = delivery.getBody();
                    String message = new String(body,"utf-8");
                    System.out.println(name + "[x] 收到消息"+message);
                },(consumerTag)->{});
    }
}

四:路由模式

原理:生产者将消息发送给交换机,消息携带具体的routingkey。交换机类型是direct,接收到消息中的routingkey,比对与之绑定的队列的routingkey,分发到不同的队列上。

场景:一个商城,新添加了一个商品,实时性不是很高,只需要添加到数据库即可,不用刷新缓存。项目中的error报错

img生产者:


public class RoutePublisher {
    public static void main(String[] args)throws IOException,RuntimeException {
        //创建信道和连接
        Channel channel = ChannelUtil.channel();
        //创建交换机,类型是DIRECT
        channel.exchangeDeclare(Constants.ROUTER_RIBBIT, BuiltinExchangeType.DIRECT);
        //发送消息
        Scanner scanner = new Scanner(System.in);
        while(true) {
            print("请输入您要发送的消息");
            String message = scanner.next();

            print("请输入您的routing-key");
            String routingKey = scanner.next();
            if("exit".equals(message)){
                break;
            }
            //(2) 发给交换机,提供routing-key
            channel.basicPublish(Constants.ROUTER_RIBBIT, routingKey, null, message.getBytes("UTF-8"));
            print("[x] 消息发送成功! " + message);
        }
    }
    private static void print(String str) {
        System.out.println("发布者:"+str);
    }
}

消费者


public class RouteConsumer {
    private String name;
    //加入bindingkey,用于队列与交换机的绑定
    private String bindingKey;

    public RouteConsumer(String name, String bindingKey) {
        this.name = name;
        this.bindingKey = bindingKey;
    }

    public void consume() throws IOException{
        //建立信道
        Channel channel = ChannelUtil.channel();
        //创建消息队列
        String queueName = channel.queueDeclare().getQueue();
        //通过bindingKey来绑定交换机
        channel.queueBind(queueName, Constants.ROUTER_RIBBIT,bindingKey);
        //接收消息
        channel.basicConsume(queueName,true,
                (consumerTag,delivery)->{
                    //处理消息
                    byte[] body = delivery.getBody();
                    String message = new String(body,"utf-8");
                    System.out.println(name + "[x] 收到消息"+message);
                },(consumerTag)->{});


    }
}

五:主题订阅模式

原理:将路由键和某模式进行匹配,此时队列需要绑定在一个模式上,“#”匹配一个词或多个词,“*”只匹配一个词

场景:一个商城,新添加了一个商品,实时性不是很高,只需要添加到数据库即可,数据库包含了主数据库mysql1和从数据库mysql2的内容,不用刷新缓存

img
生产者


public class TopicPublisher {
    public static void main(String[] args)throws IOException,RuntimeException {
        //建立连接和信道
        Channel channel = ChannelUtil.channel();
        //创建交换机,类型是TOPIC
        channel.exchangeDeclare(Constants.TOPIC_RIBBIT, BuiltinExchangeType.TOPIC);
        //发送消息
        Scanner scanner = new Scanner(System.in);
        while(true) {
            print("请输入您要发送的消息");
            String message = scanner.next();
            print("请输入您的routing-key");
            String routingKey = scanner.next();
            if("exit".equals(message)){
                break;
            }
            //(2) 发给交换机,提供routing-key
            channel.basicPublish(Constants.TOPIC_RIBBIT, routingKey, null, message.getBytes("UTF-8"));
            print("[x] 消息发送成功! " + message);
        }
    }
    private static void print(String str) {
        System.out.println("发布者:"+str);
    }
}

消费者


public class TopicConsumer {
    private String name;
    //加入bindingKey,用于队列与交换机的绑定
    private String bindingKey;

    public TopicConsumer(String name, String bindingKey) {
        this.name = name;
        this.bindingKey = bindingKey;
    }
    public void consume() throws IOException{
        //建立信道和连接
        Channel channel = ChannelUtil.channel();

        //创建消息d队列
        String queueName=channel.queueDeclare().getQueue();
        //通过bingingKey来绑定交换机
        channel.queueBind(queueName,Constants.TOPIC_RIBBIT,bindingKey);
        //接收消息
        channel.basicConsume(queueName,true,
                (consumerTag,delivery)->{
                    //处理消息
                    byte[] body = delivery.getBody();
                    String message = new String(body,"utf-8");
                    System.out.println(name + " 收到消息"+message);
                },(consumerTag)->{});
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值