第四天..

练习

递减元素使数组呈锯齿状

给你一个整数数组 nums,每次 操作 会从中选择一个元素并 将该元素的值减少 1。

如果符合下列情况之一,则数组 A 就是 锯齿数组:

每个偶数索引对应的元素都大于相邻的元素,即 A[0] > A[1] < A[2] > A[3] < A[4] > ...
或者,每个奇数索引对应的元素都大于相邻的元素,即 A[0] < A[1] > A[2] < A[3] > A[4] < ...
返回将数组 nums 转换为锯齿数组所需的最小操作次数。

 

示例 1:

输入:nums = [1,2,3]
输出:2
解释:我们可以把 2 递减到 0,或把 3 递减到 1。
示例 2:

输入:nums = [9,6,1,6,2]
输出:4

代码

class Solution {
    public int movesToMakeZigzag(int[] nums) {
        int n=0;int m=0;
        for (int i = 0; i < nums.length; i++) {
            if(i%2==0){//i是偶数
                n = n+jishu(nums,i);//如果是每个奇数下标比两边小,可以判断多少次偶数下标比奇数小,得出奇数索引 
            }else {//i是奇数
                m = m+jishu(nums,i);//如果是每个偶数下标比两边小,可以判断多少次奇数下标比偶数小,得出偶数索引
            }
        }
        return Math.min(n,m);//返回最小的操作次数
    }
    public int jishu(int[] nums,int i){
        int left = 0; int right = 0;
        if(i-1>=0&&nums[i-1]<=nums[i]){//如果左边的值比它小
            left=nums[i]-nums[i-1]+1;//需要这么多次才能让i比左边更小
            //同理,需要这么多次才能让左边比i大
        }
        if(i+1<nums.length&&nums[i+1]<=nums[i]){//如果右边值比它小
            right=nums[i]-nums[i+1]+1;//需要这么多次才能让i比右边更小
            //同理,需要这么多次才能让右边比i大
        }
        return Math.max(left,right);//比较两边那个用的最多(也就是比两边最小值更小)
    }
}
//以偶数索引为例 凡是奇数下标都比两边数小
//如果不符合要求 就要用i的位置-两边最小值+1 就得到符合要求的
//奇数索引同理
//将奇数位置的元素减少到刚好比相邻的偶数位置元素小,得出偶数索引
//将偶数位置的元素减少到刚好比相邻的奇数位置元素小,得出奇数索引

本来想一个上午写三道,这样下午就能多看会课了,但是这一道就给我干到12点了

总结一下,自己写的时候肯定是for循环了 也不粘了 一开始想的是要for两遍,一遍奇数索引,一遍偶数索引,结果花了70多行才写完,然后测试用例还bug不少,眼看就要12点了,遂放弃。

看了大神代码才明白,不需要那么繁琐, 因为题只让减 所以x>=y 要判断x减 不能判断y增

这也就是说,大神代码意思,就是用偶数序列判断奇数索引(因为只能让大的减)

用奇数序列判断偶数索引(同理)

算是搞明白...(可能)已经12点了,下午再开始都快1点多了 真是... 计划赶不上变化

得分最高的单词集合

你将会得到一份单词表 words,一个字母表 letters (可能会有重复字母),以及每个字母对应的得分情况表 score。

请你帮忙计算玩家在单词拼写游戏中所能获得的「最高得分」:能够由 letters 里的字母拼写出的 任意 属于 words 单词子集中,分数最高的单词集合的得分。

单词拼写游戏的规则概述如下:

玩家需要用字母表 letters 里的字母来拼写单词表 words 中的单词。
可以只使用字母表 letters 中的部分字母,但是每个字母最多被使用一次。
单词表 words 中每个单词只能计分(使用)一次。
根据字母得分情况表score,字母 'a', 'b', 'c', ... , 'z' 对应的得分分别为 score[0], score[1], ..., score[25]。
本场游戏的「得分」是指:玩家所拼写出的单词集合里包含的所有字母的得分之和。
 

示例 1:

输入:words = ["dog","cat","dad","good"], letters = ["a","a","c","d","d","d","g","o","o"], score = [1,0,9,5,0,0,3,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0]
输出:23
解释:
字母得分为  a=1, c=9, d=5, g=3, o=2
使用给定的字母表 letters,我们可以拼写单词 "dad" (5+1+5)和 "good" (3+2+2+5),得分为 23 。
而单词 "dad" 和 "dog" 只能得到 21 分。
示例 2:

输入:words = ["xxxz","ax","bx","cx"], letters = ["z","a","b","c","x","x","x"], score = [4,4,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,10]
输出:27
解释:
字母得分为  a=4, b=4, c=4, x=5, z=10
使用给定的字母表 letters,我们可以组成单词 "ax" (4+5), "bx" (4+5) 和 "cx" (4+5) ,总得分为 27 。
单词 "xxxz" 的得分仅为 25 。
示例 3:

输入:words = ["leetcode"], letters = ["l","e","t","c","o","d"], score = [0,0,1,1,1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,1,0,0,0,0,0,0]
输出:0
解释:
字母 "e" 在字母表 letters 中只出现了一次,所以无法组成单词表 words 中的单词。
 

提示:

1 <= words.length <= 14
1 <= words[i].length <= 15
1 <= letters.length <= 100
letters[i].length == 1
score.length == 26
0 <= score[i] <= 10
words[i] 和 letters[i] 只包含小写的英文字母。

class Solution {  
  private String[] words;
  private int[] score;
  int[] left = new int[26];
  int ans = 0;
  
  
  // ☆☆☆☆☆ 回溯(子集型)
  public int maxScoreWords(String[] words, char[] letters, int[] score) {
    this.words = words;
    this.score = score;
    for (char c : letters) {
      left[c - 'a']++;
    }
    
    backtrack(words.length - 1, 0);
    
    return ans;
  }
  
  private void backtrack(int i, int curScore) {
    if (i < 0) {
      ans = Math.max(ans, curScore);
      return;
    }
    
    backtrack(i - 1, curScore);
    
    char[] cs = words[i].toCharArray();
    int j;
    for (j = 0; j < cs.length; j++) {
      int idx = cs[j] - 'a';
      if (--left[idx] < 0) {
        // 提前结束遍历单词
        break;
      }
      curScore += score[idx];
    }
    
    if (j >= cs.length) {
      backtrack(i - 1, curScore);
    }
    
    for (int k = Math.min(cs.length - 1, j); k >= 0; k--) {
      ++left[cs[k] - 'a'];
    }
  }
}

直接粘的大神代码,放弃了

想我也曾学过回溯,现在忘光了,甚至看都看不明白

别人眼中的中等题,一次回溯搞定,我眼中的困难题,叹

重学数据结构了要

两数相加

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。


示例 1:


输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:

输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

class ListNode {
     int val;
     ListNode next;
     ListNode() {}
    ListNode(int val) { this.val = val; }
     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 }
class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        //定义一个新联表伪指针,用来指向头指针,返回结果
        ListNode prev = new ListNode(0);
        //定义一个进位数的指针,用来存储当两数之和大于10的时候,
        int carry = 0;
        //定义一个可移动的指针,用来指向存储两个数之和的位置
        ListNode cur = prev;
        //当l1 不等于null或l2 不等于空时,就进入循环
        while(l1!=null || l2!=null){
            //如果l1 不等于null时,就取他的值,等于null时,就赋值0,保持两个链表具有相同的位数
            int x= l1 !=null ? l1.val : 0;
            //如果l1 不等于null时,就取他的值,等于null时,就赋值0,保持两个链表具有相同的位数
            int y = l2 !=null ? l2.val : 0;
            //将两个链表的值,进行相加,并加上进位数
            int sum = x + y + carry;
            //计算进位数
            carry = sum / 10;
            //计算两个数的和,此时排除超过10的请况(大于10,取余数)
            sum = sum % 10;
            //将求和数赋值给新链表的节点,
            //注意这个时候不能直接将sum赋值给cur.next = sum。这时候会报,类型不匹配。
            //所以这个时候要创一个新的节点,将值赋予节点
            cur.next = new ListNode(sum);
            //将新链表的节点后移
            cur = cur.next;
            //当链表l1不等于null的时候,将l1 的节点后移
            if(l1 !=null){
                l1 = l1.next;
            }
            //当链表l2 不等于null的时候,将l2的节点后移
            if(l2 !=null){
                l2 = l2.next;
            }
        }
        //如果最后两个数,相加的时候有进位数的时候,就将进位数,赋予链表的新节点。
        //两数相加最多小于20,所以的的值最大只能时1
        if(carry == 1){
            cur.next = new ListNode(carry);
        }
        //返回链表的头节点
        return prev.next;
    }
}

链表创建新节点用.next 以前可能学过忘了,又死在这了,思路不难懂,就是不会写。

附上大神代码

大体思路

两个链表相加,位数不够补零

就把它们当成普通算数 只不过以前是先算个位 正着写

而链表就相当于把个位放开头了

另外答案按逆序写 拿出来还是那个数

所以用指针去找

  1. 定义一个指针cur = prew去向后移位,用来存储两数字之和

  1. 定义一个指针carry存进位,用来存储大于10的数

  1. 定义一个指针prew定起始位,用来返回最终结果

  1. 循环双节点,直到一方或双方节点为null时,赋值0(用来保持同位,总不能9+NULL吧)

  1. 每次相加都会返回sum,就可以算出carry 所以sum=l1+l2+carry,同时进位sum取余

  1. 每个节点存一个值,需要让cur创建新节点再赋值 移动cur指针到新节点

  1. 做判断,如果l1l2不为null 每次cur移动后 它们也要节点后移

  1. 最后判断 如果carry还有进位数 就创建一个新节点存carry

  1. 最后返回就是prev.next 去找cur添加的子节点 返回的就是答案

字少的

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode left =new ListNode(0);//起始节点
        ListNode right = left; //移动指针
        int n=0;//存进位
        while(l1!=null || l2!=null){//当都不为空时
            int x;int y;
            if(l1!=null){x = l1.val;}//l1不为空取值
                 else{ x = 0;}//l1为空时取0
            if(l2!=null){y = l2.val;}//l2不为空取值
                 else{ y = 0;}//l2为空时取0
            int sum = x+y+n;//取和(无进位就是0 有就是1)
            n = sum/10;//判断是否有进位
            sum = sum%10;//无论有无进位,都要取余
            //因为起始节点有0了
            right.next =new ListNode(sum);//下一个节点赋值
            right = right.next;//指针右移
            if(l1!=null){l1=l1.next;}//l1不为空,右移下一位
            if(l2!=null){l2=l2.next;}//l2不为空,右移下一位
        }
        if(n==1){//如果最后相加进位了
            right.next=new ListNode(n);//再右填一位
        }
        return left.next;//从起始节点0后一位的开始遍历
    }
}

说实话,每天三题要弄明白加上背点八股都已经快6点了,还想着看视频学东西那可真是太屑了

感觉很低效,有的题要思考一下午才能完成,不仅是基础问题还有题的复杂性

以后尽量写写,不会超过上午了,下午听课上午写,不然一天都在死磕算法了,很费时间

羡慕那些算法牛逼的大神

八股

java 容器都有哪些?

List:vector arraylist linkedlist

set:hashset treeset linkhashset

map:hashmap treemap hashtable

queue

Collection 和 Collections 有什么区别?

前者是集合接口 后者是集合工具类

List、Set、Map 之间的区别是什么

list和set是继承于collection

list有安全集合vector map有hashtable

list是有序的 set是无序的

list的常见方法有add()remove()clear()get()contains()size()

set的常见方法有add()remove()clear()contains()size()

map的常见方法有pull()remove()get()values()keyset()getvalue()clear()containskey()containcalue()

size()

list的元素可以重复 后两者不可重复

HashMap 和 Hashtable 有什么区别?

hashmap不安全不同步 但是效率比hashtable快,使用containskey和containsvalue方法

可以存空键值

hashtable线程安全同步,但是效率比hashmap低,使用contains()方法,不可以存空键值

rabbitmq第三天..

发布信息

发布信息原理

发布确认 就是队列收到生产者发来的信息后 返回信息给生产者

Channel channel = RabbitMqUtils.getChannel()) {
            //开启发布确认
            channel.confirmSelect();
            boolean durable = true;//需要让queue进行持久化

发布确认的策略

单个确认发布

发一个消息后被确认发布 后续消息才会继续发布

缺点:发布速度很慢

 //单个确认
    public static void publishMessageIndividually() throws Exception {
        try (Channel channel = RabbitMqUtils.getChannel()) {
            //队列声明
            String queueName = UUID.randomUUID().toString();
            //队列名字 持久化 共享 自动删除 其他
            channel.queueDeclare(queueName, true, false, false, null);
            //开启发布确认
            channel.confirmSelect();
            //开始时间
            long begin = System.currentTimeMillis();

            //批量发消息1000条
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String message = i + "";
                //交换机 队列名称 其他 消息体
                channel.basicPublish("", queueName, null, message.getBytes());
                //单个消息就马上进行发布
                //服务端返回 false 或超时时间内未返回,生产者可以消息重发
                boolean flag = channel.waitForConfirms();
                if(flag){
                    System.out.println("消息发送成功");
                }
            }

            //结束时间
            long end = System.currentTimeMillis();
            System.out.println("发布" + MESSAGE_COUNT + "个单独确认消息,耗时" + (end - begin) +
                    "ms");
        }
    }
  public static void main(String[] args) throws Exception {
          //1.单个确认
          //发布1000个单独确认消息,耗时899ms
        ConfirmMessage.publishMessageIndividually();
}

批量确认发布

可以发布一批信息后一起确认,极大提高吞吐量

缺点:出现问题时不知道是什么消息出问题了

    //批量确认
    public static void publishMessageBatch() throws Exception {
        try (Channel channel = RabbitMqUtils.getChannel()) {
            //队列声明
            String queueName = UUID.randomUUID().toString();
            //队列名字 持久化 共享 自动删除 其他
            channel.queueDeclare(queueName, true, false, false, null);
            //开启发布确认
            channel.confirmSelect();
            //开始时间
            long begin = System.currentTimeMillis();

            //批量确认消息的大小
            int batchSize=100;

            //批量发布消息 批量确认
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String message=i+"";
                channel.basicPublish("",queueName,null,message.getBytes());

                //判断到100条消息的时候 批量确认一次
                if(i%batchSize == 0){
                    //发布确认
                    channel.waitForConfirms();
                }
            }
            
            //结束时间
            long end = System.currentTimeMillis();
            System.out.println("发布" + MESSAGE_COUNT + "个批量确认消息,耗时" + (end - begin) +
                    "ms");
        }
    }
}
   public static void main(String[] args) throws Exception {
          //1.单个确认
          //发布1000个单独确认消息,耗时899ms
//        ConfirmMessage.publishMessageIndividually();
          //2.批量确认
          //发布1000个批量确认消息,耗时106ms
          ConfirmMessage.publishMessageBatch();

    }

异步确认发布

性价比最高 又可靠又效率 通过函数回调来保证是否投递成功

原理图:

  //异步发布确认
    public static void publishMessageAsync() throws Exception{
        try (Channel channel = RabbitMqUtils.getChannel()) {
            //队列声明
            String queueName = UUID.randomUUID().toString();
            //队列名字 持久化 共享 自动删除 其他
            channel.queueDeclare(queueName, true, false, false, null);
            //开启发布确认
            channel.confirmSelect();
            //开始时间
            long begin = System.currentTimeMillis();


            //消息确认成功 回调函数
            ConfirmCallback ackCallback =(deliveryTag, multiple) ->{
                System.out.println("确认的消息:"+deliveryTag);
            };
            //消息确认失败 回调函数
            /**
             * 1.消息的标识
             * 2.是否批量确认
             * */
            ConfirmCallback nackCallback =(deliveryTag,multiple) ->{
                System.out.println("未确认的消息:"+deliveryTag);
            };
            //消息的监听器监听器放前面 可以监听后面的信息是否发送成功与失败
            /**
             * 1.监听那些消息成功了
             * 2.监听那些消息失败了
             * */
            channel.addConfirmListener(ackCallback,nackCallback);//异步通知

            //批量发布消息 不用它来确认消息 只负责发布
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String message=i+"";
                channel.basicPublish("",queueName,null,message.getBytes());
            }

            //结束时间
            long end = System.currentTimeMillis();
            System.out.println("发布" + MESSAGE_COUNT + "个异步确认消息,耗时" + (end - begin) +
                    "ms");
        }
    }
         //3.异步批量确认
        //发布1000个异步确认消息,耗时68ms
        ConfirmMessage.publishMessageAsync();
    }

如果处理异步未确认消息

使用ConcurrentLinkedQueue 并发链路队列

            //开启发布确认
            channel.confirmSelect();

            /**
             * 线程安全有序的一个哈希表 适用于高并发情况
             * 1.轻松的将序号与消息进行关联
             * 2.轻松的批量删除条目 只要给到序号 序号key消息value
             * 3.支持高并发(多线程)
             *
             * */
            ConcurrentSkipListMap outstandingConfirms=new ConcurrentSkipListMap<>();

//监听器
            //消息确认成功 回调函数
            ConfirmCallback ackCallback =(deliveryTag, multiple) ->{
                if(multiple) {//是批量吗
                    //2.删除掉已经确认的消息,剩下就是未确认的消息
                    //通过标识拿到被确认的消息并清理
                    ConcurrentNavigableMap confimed = outstandingConfirms.headMap(deliveryTag);
                    confimed.clear();
                }else {
                    outstandingConfirms.remove(deliveryTag);
                }
                    System.out.println("确认的消息:" + deliveryTag);

            };
            //消息确认失败 回调函数
            /**
             * 1.消息的标识
             * 2.是否批量确认
             * */
            ConfirmCallback nackCallback =(deliveryTag,multiple) ->{
               //3.打印未确认的消息有哪些
                String message = (String) outstandingConfirms.get(deliveryTag);
                System.out.println(
                        "未确认的消息是:"+ message+
                        "::::未确认的消息tag:"+deliveryTag);

            };
            //消息的监听器监听器放前面 可以监听后面的信息是否发送成功与失败
            /**
             * 1.监听那些消息成功了
             * 2.监听那些消息失败了
             * */
            channel.addConfirmListener(ackCallback,nackCallback);//异步通知

//发消息
            //开始时间
            long begin = System.currentTimeMillis();
            //批量发布消息 不用它来确认消息 只负责发布
            for (int i = 0; i < MESSAGE_COUNT; i++) {
                String message=i+"";
                channel.basicPublish("",queueName,null,message.getBytes());
                //1.此处记录所有要发送的消息-消息和
                //括号内(信道下一次发布的值序号,信息值)
                outstandingConfirms.put(channel.getNextPublishSeqNo(),message);
            }

三种发布确定速度对比

单独发布消息

同步等待确认,简单,但吞吐量非常有限。

批量发布消息

批量同步等待确认,简单,合理的吞吐量,一旦出现问题但很难推断出是那条

消息出现了问题。

异步处理:(最常用)

最佳性能和资源使用,在出现错误的情况下可以很好地控制,但是实现起来稍微难些

交换机

之前的没写交换机的原因是 rabbitmq本身有一个默认交换机

我们假设的是工作队列背后,每个任务都恰好交付给一个消费者(工作进程)。

在这一部分中,我们将做一些完全不同的事情-我们将消息传达给多个消费者。这种模式

称为 ”发布/订阅

原理图

交换机的介绍

RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列

相反,生产者只能将消息发送到交换机(exchange),交换机工作的内容非常简单,一方面它接收来

自生产者的消息,另一方面将它们推入队列

交换机类型

直接(direct), 主题(topic) ,标题(headers) , 扇出(fanout)

channel.basicPublish("","hello",null,message.getBytes());

第一个参数是交换机的名称。空字符串表示默认或无名称交换机:消息能路由发送到队列中其实

是由 routingKey(bindingkey)绑定 key 指定的,如果它存在的话

临时队列 (不持久化的)

一旦我们断开了消费者的连接,队列将被自动删除

创建临时队列的方式如下: 没有队列名称的

String queueName = channel.queueDeclare().getQueue()

fanout扇出交换机

扇出交换机 一发多接

消费者
/**
 *消息接收
 */
public class ReceiveLogs01 {
    // 交换机的名称
    private static final String EXCHANGE_NAME = "logs";
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        //声明一个交换机
        channel.exchangeDeclare(EXCHANGE_NAME, "fanout");

        //声明一个临时队列
        /**
         * 生成一个临时的队列 队列的名称是随机的
         * 当消费者断开和该队列的连接时 队列自动删除
         */
        String queueName = channel.queueDeclare().getQueue();
        //把该临时队列绑定我们的 exchange 其中 routingkey(也称之为 binding key)为空字符串
        /**
         * 绑定交换机与队列
         *
         */

        channel.queueBind(queueName, EXCHANGE_NAME, "");
        System.out.println("等待接收消息,把接收到的消息打印在屏幕.....");
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("ReceiveLogs01控制台打印接收到的消息"+message);
        };
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> { });
    }
}

生产者
/**
 *生产者
 */
public class EmitLog {
    //交换机的名称
    private static final String EXCHANGE_NAME = "logs";
    public static void main(String[] argv) throws Exception {
        try (Channel channel = RabbitMqUtils.getChannel()) {
            /**
             * 声明一个 exchange
             * 1.exchange 的名称
             * 2.exchange 的类型
             */
            channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入信息");
            while (sc.hasNext()) {
                String message = sc.nextLine();
                channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
                System.out.println("生产者发出消息" + message);
            }
        }
    }
}

direct直接交换机

直接交换机 根据routingkey发消息

生产者

public class DirctLogs {
    //交换机的名称
    private static final String EXCHANGE_NAME = "direct_logs";
    public static void main(String[] argv) throws Exception {
        try (Channel channel = RabbitMqUtils.getChannel()) {
            
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入信息");
            while (sc.hasNext()) {
                String message = sc.nextLine();
                //就改变error和下面的交换机相同即可
                channel.basicPublish(EXCHANGE_NAME, "error", null, message.getBytes("UTF-8"));
                System.out.println("生产者发出消息" + message);
            }
        }
    }
}

消费者1

public class ReceiveLogsDirect01 {
    private static final String EXCHANGE_NAME = "direct_logs";
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        //声明交换机 direct直接交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);


        String queueName = "console";
        //交换机 声明一个队列
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定 后面是info 就是routingkey
        channel.queueBind(queueName, EXCHANGE_NAME, "info");
        channel.queueBind(queueName, EXCHANGE_NAME, "warning");

        System.out.println("等待接收消息.....");
        //接收信息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("ReceiveLogsDirect02接收绑定键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
        };
        //消费者取消时的消息回调
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

消费者2

public class ReceiveLogsDirect02 {
    private static final String EXCHANGE_NAME = "direct_logs";
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        //声明交换机 direct直接交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);


        String queueName = "disk";
        //交换机 声明一个队列
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定 后面是info 就是routingkey
        channel.queueBind(queueName, EXCHANGE_NAME, "error");

        System.out.println("等待接收消息.....");
        //接收信息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("ReceiveLogsDirect02接收绑定键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message);
        };
        //消费者取消时的消息回调
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

Topic主题交换机

必须是单词列表 以点号分割eg: quick.orange.rabbit

*(星号)可以代替一个单词 eg: *.orange.*

#(井号)可以替代零个或多个单词 eg:(lazy.#)

更多的例子

quick.orange.rabbit 被队列 Q1Q2 接收到

lazy.orange.elephant 被队列 Q1Q2 接收到

quick.orange.fox 被队列 Q1 接收到

lazy.brown.fox 被队列 Q2 接收到

lazy.pink.rabbit 虽然满足两个绑定但只被队列 Q2 接收一次

quick.brown.fox 不匹配任何绑定不会被任何队列接收到会被丢弃

quick.orange.male.rabbit 是四个单词不匹配任何绑定会被丢弃

lazy.orange.male.rabbit 是四个单词但匹配 Q2

当队列绑定关系是下列这种情况时需要引起注意

当一个队列绑定键是#,那么这个队列将接收所有数据,就有点像 fanout 了

如果队列绑定键当中没有#和*出现,那么该队列绑定类型就是 direct 了

消费者C1

public class ReceiveLogsTopic01 {
    //交换机名字
    private static final String EXCHANGE_NAME = "topic_logs";
    //接收消息
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        //声明交换机 topic主题交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);


        String queueName = "Q1";
        //声明一个队列
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定 第三个就是routingkey
        channel.queueBind(queueName, EXCHANGE_NAME, "*.orange.*");

        System.out.println("等待接收消息.....");
        //接收信息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("ReceiveLogsTopic01接收绑定键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message+"接收队列:"+queueName);
        };
        //消费者取消时的消息回调
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

消费者02

public class ReceiveLogsTopic02 {
    //交换机名字
    private static final String EXCHANGE_NAME = "topic_logs";
    //接收消息
    public static void main(String[] argv) throws Exception {
        Channel channel = RabbitMqUtils.getChannel();
        //声明交换机 topic主题交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);


        String queueName = "Q2";
        //声明一个队列
        channel.queueDeclare(queueName, false, false, false, null);
        //绑定 第三个就是routingkey
        channel.queueBind(queueName, EXCHANGE_NAME, "*.*.rabbit");
        channel.queueBind(queueName, EXCHANGE_NAME, "lazy.#");

        System.out.println("等待接收消息.....");
        //接收信息
        DeliverCallback deliverCallback = (consumerTag, delivery) -> {
            String message = new String(delivery.getBody(), "UTF-8");
            System.out.println("ReceiveLogsTopic02接收绑定键:"+delivery.getEnvelope().getRoutingKey()+",消息:"+message+"接收队列:"+queueName);
        };
        //消费者取消时的消息回调
        channel.basicConsume(queueName, true, deliverCallback, consumerTag -> {
        });
    }
}

生产者

public class EmitLogTopic {
        //交换机的名称
        private static final String EXCHANGE_NAME = "topic_logs";
    public static void main(String[] argv) throws Exception {
        try (Channel channel = RabbitMqUtils.getChannel()) {
            channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
            /**
             * Q1-->绑定的是
             * 中间带 orange 带 3 个单词的字符串(*.orange.*)
             * Q2-->绑定的是
             * 最后一个单词是 rabbit 的 3 个单词(*.*.rabbit)
             * 第一个单词是 lazy 的多个单词(lazy.#)
             *
             */
            Map<String, String> bindingKeyMap = new HashMap<>();
            bindingKeyMap.put("quick.orange.rabbit","被队列 Q1Q2 接收到");
            bindingKeyMap.put("lazy.orange.elephant","被队列 Q1Q2 接收到");
            bindingKeyMap.put("quick.orange.fox","被队列 Q1 接收到");
            bindingKeyMap.put("lazy.brown.fox","被队列 Q2 接收到");
            bindingKeyMap.put("lazy.pink.rabbit","虽然满足两个绑定但只被队列 Q2 接收一次");
            bindingKeyMap.put("quick.brown.fox","不匹配任何绑定不会被任何队列接收到会被丢弃");
            bindingKeyMap.put("quick.orange.male.rabbit","是四个单词不匹配任何绑定会被丢弃");
            bindingKeyMap.put("lazy.orange.male.rabbit","是四个单词但匹配 Q2");

            for (Map.Entry<String, String> bindingKeyEntry: bindingKeyMap.entrySet()){
                String routingKey = bindingKeyEntry.getKey();
                String message = bindingKeyEntry.getValue();
                channel.basicPublish(EXCHANGE_NAME,routingKey, null, message.getBytes("UTF-8"));
                System.out.println("生产者发出消息" + message);
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值