在nodejs中使用RabbitMQ(三)Routing、Topics、Headers

news2025/2/19 16:40:32

示例一、Routing

exchange类型direct,根据消息的routekey将消息直接转发到指定队列。producer.ts 生产者主要发送消息,consumer.ts负责接收消息,同时也都可以创建exchange交换机,创建队列,为队列绑定exchange,为避免重复简化代码,提高可维护性,队列相关操作移动到消费者端。队列,exchange交换机推荐在启动程序前手动创建好。

producer.ts 

import RabbitMQ from 'amqplib/callback_api';


function start() {
  RabbitMQ.connect("amqp://admin:admin1234@localhost:5672?heartbeat=60", function (err0, conn) {
    if (err0) {
      console.error("[AMQP]", err0.message);
      return setTimeout(start, 1000);
    }

    conn.on("error", function (err1) {
      if (err1.message !== "Connection closing") {
        console.error("[AMQP] conn error", err1.message);
      }
    });

    conn.on("close", function () {
      console.error("[AMQP] reconnecting");
      return setTimeout(start, 1000);
    });

    console.log("[AMQP] connected");

    conn.createChannel(async (err2, channel) => {
      if (err2) {
        console.error("[AMQP]", err2.message);
        return setTimeout(start, 1000);
      }

      const exchangeName = 'exchange1';
      channel.assertExchange(
        exchangeName,
        'direct',
        {
          durable: true
        },
        (err, ok) => {
          if (err) {
            console.log('exchange路由转发创建失败', err);
          } else {
            let args = ['info', 'warn', 'error'];
            for (let i = 0; i < 10; ++i) {
              // console.log('message send!', channel.sendToQueue(
              //   queueName,
              //   Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)}`),
              //   { persistent: true, correlationId: 'ooooooooooooooo' },// 消息持久化,重启后存在
              //   // (err: any, ok: Replies.Empty)=>{}
              // ));
              const routeKey = args[Math.floor(Math.random() * 3)];
              console.log('消息发送是否成功', channel.publish(
                exchangeName,
                routeKey,
                Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)},${routeKey}`),
                { persistent: true },
              ));
            }
          }
        }
      );
    });

    setTimeout(() => {
      conn.close();
      process.exit(0);
    }, 1000);
  });
}

start();

consumer.ts

import RabbitMQ, { type Replies } from 'amqplib/callback_api';


RabbitMQ.connect('amqp://admin:admin1234@localhost:5672', (err0, conn) => {
  if (err0) {
    console.error(err0);
    return;
  }

  conn.createChannel(function (err1, channel) {
    const queueName = 'queue1';

    channel.assertQueue(queueName, { durable: true }, (err2) => {
      if (err2) {
        console.log('队列创建失败', err2);
        return;
      }
      console.log('[*] waiting...');

      // 一次只有一个未确认消息,防止消费者过载
      channel.prefetch(1);

      channel.bindQueue(queueName, 'exchange1', 'info', {}, (err3, ok) => {
        console.log(queueName, '队列绑定结果', err3, ok);
      });
      channel.bindQueue(queueName, 'exchange1', 'warn', {}, (err3, ok) => {
        console.log(queueName, '队列绑定结果', err3, ok);
      });
      channel.bindQueue(queueName, 'exchange1', 'error', {}, (err3, ok) => {
        console.log(queueName, '队列绑定结果', err3, ok);
      });

      channel.consume(
        queueName,
        function (msg) {
          console.log('接收到的消息', msg?.content.toString());

          /*
           // 手动确认取消channel.ack(msg); noAck:false,
     
           // 自动确认消息
           // if (msg) {
           //   channel.ack(msg);
           // } 
          */
        },
        {
          noAck: true, // 是否自动确认消息
          // noAck: false
        },
        (err3: any, ok: Replies.Empty) => {
          console.log(err3, ok);
        },
      );
    });

  });

  conn.on("error", function (err1) {
    if (err1.message !== "Connection closing") {
      console.error("[AMQP] conn error", err1.message);
    }
  });

  conn.on("close", function () {
    console.error("[AMQP] reconnecting");
  });
});

consumer2.ts

import RabbitMQ from 'amqplib';


const conn = await RabbitMQ.connect('amqp://admin:admin1234@localhost:5672');

conn.on("error", function (err1) {
  if (err1.message !== "Connection closing") {
    console.error("[AMQP] conn error", err1.message);
  }
});

conn.on("close", function () {
  console.error("[AMQP] reconnecting");
});

const channel = await conn.createChannel();

const queueName = 'queue2';

await channel.assertQueue(queueName, { durable: true });

console.log('[*] waiting...');

// 一次只有一个未确认消息,防止消费者过载
await channel.prefetch(1);

await channel.bindQueue(queueName, 'exchange1', 'error', {});

channel.consume(
  queueName,
  function (msg) {
    console.log('接收到的消息', msg?.content.toString());

    /*
      // 手动确认取消channel.ack(msg); noAck:false,

      // 自动确认消息
      // if (msg) {
      //   channel.ack(msg);
      // } 
    */
  },
  {
    noAck: true, // 是否自动确认消息
    // noAck: false
  },
);

示例二、Topic

exchange的topic类型和direct类似,使用的仍然是routeKey进行匹配转发,topic支持通过*和#进行模糊查询。*代码一个具体单词,#代码0或多个单词。

producer.ts

import RabbitMQ from 'amqplib';


async function start() {
  const conn = await RabbitMQ.connect("amqp://admin:admin1234@localhost:5672?heartbeat=60");

  conn.on("error", function (err1) {
    if (err1.message !== "Connection closing") {
      console.error("[AMQP] conn error", err1.message);
    }
  });

  conn.on("close", function () {
    console.error("[AMQP] reconnecting");
    return setTimeout(start, 1000);
  });

  try {
    const channel = await conn.createChannel();

    console.log("[AMQP] connected");

    const exchangeName = 'exchange4';
    await channel.assertExchange(exchangeName, 'topic', { durable: true });

    let args = ['123.orange.456', '123.456.rabbit', 'lazy', 'lazy.123', 'lazy.123.456'];
    for (let i = 0; i < 20; ++i) {
      // console.log('message send!', channel.sendToQueue(
      //   queueName,
      //   Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)}`),
      //   { persistent: true, correlationId: 'ooooooooooooooo' },// 消息持久化,重启后存在
      //   // (err: any, ok: Replies.Empty)=>{}
      // ));

      const routeKey = args[Math.floor(Math.random() * args.length)];
      console.log('消息发送是否成功', channel.publish(
        exchangeName,
        routeKey,
        Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)},${routeKey}`),
        { persistent: true },
      ));
    }
  } catch (err) {
    console.error("[AMQP]", err);
    return setTimeout(start, 1000);
  }

  setTimeout(() => {
    conn.close();
    process.exit(0);
  }, 1000);
}

start();

consumer.ts

import RabbitMQ from 'amqplib';


const conn = await RabbitMQ.connect('amqp://admin:admin1234@localhost:5672');

conn.on("error", function (err1) {
  if (err1.message !== "Connection closing") {
    console.error("[AMQP] conn error", err1.message);
  }
});

conn.on("close", function () {
  console.error("[AMQP] reconnecting");
});

const channel = await conn.createChannel();

const queueName = 'queue1';

channel.assertQueue(queueName, { durable: true });

console.log('[*] waiting...');

// 一次只有一个未确认消息,防止消费者过载
await channel.prefetch(1);

// *代码一个具体单词,#代码0或多个单词
await channel.bindQueue(queueName, 'exchange4', '*.orange.*', {});

channel.consume(queueName, function (msg) {
  console.log('接收到的消息', msg?.content.toString());

  // 手动确认取消channel.ack(msg);设置noAck:false,
  // 自动确认消息noAck:true,不需要channel.ack(msg);
  try {
    if (msg) {
      channel.ack(msg);
    }
  } catch (err) {
    if (msg) {
      // 第二个参数,false拒绝当前消息
      // 第二个参数,true拒绝小于等于当前消息
      // 第三个参数,3false从队列中清除
      // 第三个参数,4true从新在队列中排队
      channel.nack(msg, false, false);
    }
    console.log(err);
  }
}, {
  // noAck: true, // 是否自动确认消息,为true不需要调用channel.ack(msg);
  noAck: false
});

consumer2.ts

import RabbitMQ, { type Replies } from 'amqplib/callback_api';


RabbitMQ.connect('amqp://admin:admin1234@localhost:5672', (err0, conn) => {
  if (err0) {
    console.error(err0);
    return;
  }

  conn.createChannel(function (err1, channel) {
    const queueName = 'queue2';

    channel.assertQueue(queueName, { durable: true });

    console.log('[*] waiting...');

    // 一次只有一个未确认消息,防止消费者过载
    channel.prefetch(1);

    channel.bindQueue(queueName, 'exchange4', '*.*.rabbit', {}, (err, ok) => {
      console.log(queueName, '队列绑定结果', err, ok);
    });
    channel.bindQueue(queueName, 'exchange4', 'lazy.#', {}, (err, ok) => {
      console.log(queueName, '队列绑定结果', err, ok);
    });

    channel.consume(queueName, function (msg) {
      console.log('接收到的消息', msg?.content.toString());

      // 手动确认取消channel.ack(msg);设置noAck:false,
      // 自动确认消息noAck:true,不需要channel.ack(msg);
      try {
        if (msg) {
          channel.ack(msg);
        }
      } catch (err) {
        if (msg) {
          // 第二个参数,false拒绝当前消息
          // 第二个参数,true拒绝小于等于当前消息
          // 第三个参数,3false从队列中清除
          // 第三个参数,4true从新在队列中排队
          channel.nack(msg, false, false);
        }
        console.log(err);
      }
    }, {
      // noAck: true, // 是否自动确认消息,为true不需要调用channel.ack(msg);
      noAck: false
    });

    // return,error事件不会把消息重新放回队列
    channel.on('return', (msg) => {
      console.error('消息发送失败:', msg);
    });

    channel.on('error', (err) => {
      console.error('通道发生错误:', err);
    });
  });

  conn.on("error", function (err1) {
    if (err1.message !== "Connection closing") {
      console.error("[AMQP] conn error", err1.message);
    }
  });

  conn.on("close", function () {
    console.error("[AMQP] reconnecting");
  });
});

示例三、Headers

 exchange类型headers,根据传递的头部信息进行转发,头部信息类型为object对象。在头部信息中要设置x-match属性,'x-match': 'any', any,下方消息匹配上一个就可以。all,下方消息要全部匹配。

producer.ts

import RabbitMQ from 'amqplib/callback_api';


function start() {
  RabbitMQ.connect("amqp://admin:admin1234@localhost:5672?heartbeat=60", function (err0, conn) {
    if (err0) {
      console.error("[AMQP]", err0.message);
      return setTimeout(start, 1000);
    }

    conn.on("error", function (err1) {
      if (err1.message !== "Connection closing") {
        console.error("[AMQP] conn error", err1.message);
      }
    });

    conn.on("close", function () {
      console.error("[AMQP] reconnecting");
      return setTimeout(start, 1000);
    });

    console.log("[AMQP] connected");

    conn.createChannel(async (err2, channel) => {
      if (err2) {
        console.error("[AMQP]", err2.message);
        return setTimeout(start, 1000);
      }

      const exchangeName = 'exchange5';
      channel.assertExchange(
        exchangeName,
        'headers',
        {
          durable: true
        },
        (err, ok) => {
          if (err) {
            console.log('exchange路由转发创建失败', err);
          } else {
            let args = [
              {
                // 'x-match': 'any', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
                'loglevel': 'info',
                // 'buslevel': 'product',
                // 'syslevel': 'admin'
              },
              {
                // 'x-match': 'any', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
                // 'loglevel': 'info',
                'buslevel': 'product',
                'syslevel': 'admin'
              },
              {
                // 'x-match': 'any', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
                // 'loglevel': 'info',
                'buslevel': 'product',
                // 'syslevel': 'admin'
              },
              {
                // 'x-match': 'all', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
                'loglevel': 'info',
                'buslevel': 'product',
                'syslevel': 'admin'
              },
            ];
            for (let i = 0; i < 20; ++i) {
              // console.log('message send!', channel.sendToQueue(
              //   queueName,
              //   Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)}`),
              //   { persistent: true, correlationId: 'ooooooooooooooo' },// 消息持久化,重启后存在
              //   // (err: any, ok: Replies.Empty)=>{}
              // ));
              
              const routeKey = args[Math.floor(Math.random() * args.length)];
              console.log('消息发送是否成功', routeKey, channel.publish(
                exchangeName,
                '',
                Buffer.from(`发送消息,${i}${Math.ceil(Math.random() * 100000)},${JSON.stringify(routeKey)}`),
                {
                  persistent: true,
                  headers: routeKey
                },
              ));
            }
          }
        }
      );
    });

    setTimeout(() => {
      conn.close();
      process.exit(0);
    }, 1000);
  });
}

start();

consumer.ts

import RabbitMQ, { type Replies } from 'amqplib/callback_api';


RabbitMQ.connect('amqp://admin:admin1234@localhost:5672', (err0, conn) => {
  if (err0) {
    console.error(err0);
    return;
  }

  conn.createChannel(function (err1, channel) {
    const queueName = 'queue1';

    channel.assertQueue(queueName, { durable: true });

    console.log('[*] waiting...');

    // 一次只有一个未确认消息,防止消费者过载
    channel.prefetch(1);

    // *代码一个具体单词,#代码0或多个单词
    channel.bindQueue(
      queueName,
      'exchange5',
      '',
      {
        'x-match': 'any', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
        'loglevel': 'info',
        'buslevel': 'product',
        'syslevel': 'admin'
      },
      (err, ok) => {
        console.log(queueName, '队列绑定结果', err, ok);
      },
    );

    channel.consume(queueName, function (msg) {
      console.log('接收到的消息', msg?.content.toString());

      // 手动确认取消channel.ack(msg);设置noAck:false,
      // 自动确认消息noAck:true,不需要channel.ack(msg);
      try {
        if (msg) {
          channel.ack(msg);
        }
      } catch (err) {
        if (msg) {
          // 第二个参数,false拒绝当前消息
          // 第二个参数,true拒绝小于等于当前消息
          // 第三个参数,3false从队列中清除
          // 第三个参数,4true从新在队列中排队
          channel.nack(msg, false, false);
        }
        console.log(err);
      }
    }, {
      // noAck: true, // 是否自动确认消息,为true不需要调用channel.ack(msg);
      noAck: false
    }, (err: any, ok: Replies.Empty) => {
      console.log(err, ok);
    });

    // return,error事件不会把消息重新放回队列
    channel.on('return', (msg) => {
      console.error('消息发送失败:', msg);
    });

    channel.on('error', (err) => {
      console.error('通道发生错误:', err);
    });
  });

  conn.on("error", function (err1) {
    if (err1.message !== "Connection closing") {
      console.error("[AMQP] conn error", err1.message);
    }
  });

  conn.on("close", function () {
    console.error("[AMQP] reconnecting");
  });
});

consumer2.ts

import RabbitMQ, { type Replies } from 'amqplib/callback_api';


RabbitMQ.connect('amqp://admin:admin1234@localhost:5672', (err0, conn) => {
  if (err0) {
    console.error(err0);
    return;
  }

  conn.createChannel(function (err1, channel) {
    const queueName = 'queue2';

    channel.assertQueue(queueName, { durable: true });

    console.log('[*] waiting...');

    // 一次只有一个未确认消息,防止消费者过载
    channel.prefetch(1);

    channel.bindQueue(
      queueName,
      'exchange5',
      '',
      {
        'x-match': 'all', // any,下方消息匹配上一个就可以; all,下方消息要全部匹配
        'loglevel': 'info',
        'buslevel': 'product',
        'syslevel': 'admin'
      },
      (err, ok) => {
        console.log(queueName, '队列绑定结果', err, ok);
      },
    );

    channel.consume(
      queueName,
      function (msg) {
        console.log('接收到的消息', msg?.content.toString());

        /*
         // 手动确认取消channel.ack(msg); noAck:false,
   
         // 自动确认消息
         // if (msg) {
         //   channel.ack(msg);
         // } 
        */
      },
      {
        noAck: true, // 是否自动确认消息
        // noAck: false
      },
      (err: any, ok: Replies.Empty) => {
        console.log(err, ok);
      },
    );

    // return,error事件不会把消息重新放回队列
    channel.on('return', (msg) => {
      console.error('消息发送失败:', msg);
    });

    channel.on('error', (err) => {
      console.error('通道发生错误:', err);
    });
  });

  conn.on("error", function (err1) {
    if (err1.message !== "Connection closing") {
      console.error("[AMQP] conn error", err1.message);
    }
  });

  conn.on("close", function () {
    console.error("[AMQP] reconnecting");
  });
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2298524.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《open3d qt 网格泊松采样成点云》

open3d qt 网格泊松采样成点云 效果展示二、流程三、代码效果展示 效果好一点,速度慢一点。 二、流程 创建动作,链接到槽函数,并把动作放置菜单栏 参照前文 三、代码 1、槽函数实现 void on_actionMeshPossionSample_triggered()//泊松采样 void MainWindow::

从算法到落地:DeepSeek如何突破AI工具的同质化竞争困局

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;Linux网络编程 &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 ​ Linux网络编程笔记&#xff1a; https://blog.cs…

阿里云一键部署DeepSeek-V3、DeepSeek-R1模型

目录 支持的模型列表 模型部署 模型调用 WebUI使用 在线调试 API调用 关于成本 FAQ 点击部署后服务长时间等待 服务部署成功后&#xff0c;调用API返回404 请求太长导致EAS网关超时 部署完成后&#xff0c;如何在EAS的在线调试页面调试 模型部署之后没有“联网搜索…

python学opencv|读取图像(六十六)使用cv2.minEnclosingCircle函数实现图像轮廓圆形标注

【1】引言 前序学习过程中&#xff0c;已经掌握了使用cv2.boundingRect()函数实现图像轮廓矩形标注&#xff0c;相关文章链接为&#xff1a;python学opencv|读取图像&#xff08;六十五&#xff09;使用cv2.boundingRect()函数实现图像轮廓矩形标注-CSDN博客 这篇文章成功在图…

嵌入式经常用到串口,如何判断串口数据接收完成?

说起通信&#xff0c;首先想到的肯定是串口&#xff0c;日常中232和485的使用比比皆是&#xff0c;数据的发送、接收是串口通信最基础的内容。这篇文章主要讨论串口接收数据的断帧操作。 空闲中断断帧 一些mcu&#xff08;如&#xff1a;stm32f103&#xff09;在出厂时就已经在…

从图像中提取的每行数字作为一张完整的图片,而不是每个数字单独成为一张图片

具体实现思路&#xff1a; 提取行区域&#xff1a;先通过轮廓或空白区域分割出每行数字。确保每行是一个整体&#xff1a;在提取每行时&#xff0c;确保提取区域的宽度包含该行所有的数字&#xff08;即避免单独分割每个数字&#xff09;。保存每一行作为一张图片&#xff1a;…

文心一言4月起全面免费,6月底开源新模型:AI竞争进入新阶段?

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼 Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 一、文心一言免费化的背后&#xff1a;AI成本与应用的双重驱动1️⃣成本下降&#xff0c;推动文心一言普及2…

基于斜坡单元的机器学习模型预测滑坡易发性,考虑条件因素的异质性

&#xff11;、引用 Chang Z, Catani F, Huang F, et al. Landslide susceptibility prediction using slope unit-based machine learning models considering the heterogeneity of conditioning factors[J]. Journal of Rock Mechanics and Geotechnical Engineering, 2023…

面向对象程序设计-实验七

6-1 计算捐款总量 这里需要设计一个捐款人类Donator及一个相关函数getMaxName( )&#xff0c;Donator类中包含捐款人的姓名及其捐款额 代码清单&#xff1a; #include <iostream> using namespace std; class Donator { private: string name; float money; //单位&…

Java面试宝典:说下Spring Bean的生命周期?

Java面试宝典专栏范围&#xff1a;JAVA基础&#xff0c;面向对象编程&#xff08;OOP&#xff09;&#xff0c;异常处理&#xff0c;集合框架&#xff0c;Java I/O&#xff0c;多线程编程&#xff0c;设计模式&#xff0c;网络编程&#xff0c;框架和工具等全方位面试题详解 每…

early bird inject

基本原理 本质是利用windows系统的apc机制&#xff0c;以及涉及到windows进程启动的流程. 因为线程初始化阶段LdrInitializeThunk函数会调用NtTestAlert函数,这个函数执行后,所有apc队列中的例程都会执行.因此我们在主线程初始化之前向主线程的apc队列中加入恶意代码即可实现…

uvm错误记录4

如下所示&#xff0c;奇怪的是penable莫名其妙的出X。可问题&#xff0c;我发送激励了。 仔细定位发现&#xff0c;39行用的是vif中的penable, 问题是都是赋值&#xff0c;却出现同时赋值多次&#xff0c;这是因为nonblocking和blocking同时触发导致的&#xff0c;因此&#xf…

3dtiles——Cesium ion for Autodesk Revit Add-In插件

一、说明&#xff1a; Cesium已经支持3dtiles的模型格式转换&#xff1b; 可以从Cesium官方Aesset中上传gltf等格式文件转换为3dtiles&#xff1b; 也可以下载插件&#xff08;例如revit-cesium插件&#xff09;转换并自动上传到Cesium官方Aseet中。 Revit转3dtiles插件使用…

QT 异步编程之多线程

一、概述 1、在进行桌面应用程序开发的时候&#xff0c;假设应用程序在某些情况下需要处理比较复制的逻辑&#xff0c;如果只有一个线程去处理&#xff0c;就会导致窗口卡顿&#xff0c;无法处理用户的相关操作。这种情况下就需要使用多线程&#xff0c;其中一个线程处理窗口事…

Proxmox 更新软件包数据库(TASK ERROR: command ‘apt-get update‘ failed: exit code 100)

1、连接自己报错的物理机Shell&#xff0c;编辑文件 vi /etc/apt/sources.list.d/pve-enterprise.list 2、注释文件的第一行在开头加上# 按I进入编辑模式后 开头添加# 然后shift&#xff1a; 输入wq或者wq&#xff01;进行保存 3、注释后执行两个命令apt-get update 和 apt…

JVM——垃圾回收算法

目录 垃圾回收算法 评价标准&#xff1a; 标记-清除算法&#xff1a; 复制算法&#xff1a; 标记-整理算法&#xff1a; 分代GC&#xff1a; arthas查看分代之后的内存情况&#xff1a; 垃圾回收算法 java是如何实现垃圾回收的呢&#xff1f;简单来说&#xff0c;垃圾回…

服务器安全——日志分析和扫描

如何通过访问日志查询被攻击 扫描攻击 攻击日志 GET /index?sindex/%5Cthink%5CModule/Action/Param/$%7Bphpinfo()%7D HTTP/1.1", host: "主机", referrer: "主机sindex/\think\Module/Action/Param/${phpinfo()}" 攻击日志文件 .error.log sql注…

ubuntu 22.04 安装vsftpd服务

先决条件&#xff0c;确保你已经配置好了存储库。 安装vsftpd 为了方便实验&#xff0c;我已经切换到了root用户。 rootlocal:~# apt-get install vsftpd修改配置 配置文件在 /etc/vsftpd.conf rootlocal:~# grep -vE ^#|^$ /etc/vsftpd.conf listenNO listen_ipv6YES anonymou…

STM32F407通过FSMC扩展外部SRAM和NAND FLASH

1 扩展外部SRAM 1.1 地址情况 FSMC控制器的存储区分为4个区(Bank)&#xff0c;每个区256MB。其中&#xff0c;Bank1可以用于连接SRAM、NOR FLASH、PSRAM&#xff0c;还可以连接TFT LCD。Bank1的地址范围是0x60000000&#xff5e;0x6FFFFFFF。Bank1又分为4个子区&#xff0c;每…

AndroidStudio查看Sqlite和SharedPreference

1.查看Sqlite 使用App Inspection&#xff0c;这是个好东西 打开方式&#xff1a;View → Tool Windows → App Inspection 界面如图&#xff1a; App inspection不但可以看Sqlite还可以抓包network和background task连抓包工具都省了。 非常好使 2.查看sharedPreference 使…