Rabbitmq—— 从入门到放弃

2023-05-16

文章目录

  • 背景
  • 总体架构
  • 类与方法
    • BlockingConnection
      • __init__
      • channel
    • BlockingChannel
      • queue_declare
      • queue_delete
      • exchange_declare
      • basic_publish
      • basic_consume
  • 实验验证
    • 安装
      • erlang
      • pika
      • rabbitmq_server
    • 启动
    • RPC示例
      • Server
      • Client

背景

  • Linux平台提供了各种进程间通信的机制,包括:套接字(socket)、管道(pipe)、消息队列等。这些机制基于Linux内核提供的接口实现。可以实现一台节点(服务器)上的两个应用程序的通信以及跨节点的应用程序通信。在不同的操作系统上,比如windows、Linux、FressBSD,这些应用程序间的通信实现与底层操作系统强相关。
  • 如果有一个组件,它定义了进程消息通信的规范,所有的应用程序都按照这个规范实现消息收发,那么这个组件可以就可以实现跨平台、跨架构、跨语言的消息传递,任何应用只要按照这样的规范来实现消息收发,就可以最大程度地减少在消息通信组件上的开发量。
  • AMQP(Advanced Message Queuing Protocol)就是这样一个规范,它规定了要实现一个消息中间件的标准规范。rabbitmq是这个规范的其中一个实现。有了rabbitmq,只要两个应用程序按照AMQP规范和rabbitmq server交互,就可以轻易的完成消息通信并基于此通道定制更高级的通信功能比如RPC。
  • 更进一步,应用程序需要按照AMQP规范来和rabbitmq server交互,这部分的工作可以单独摘出来实现,不同编程语言有各自的软件实现,比如pika模块,就是rabbitmq推荐的python实现,java、go也有也有基于AMQP规范的工具包和库。

总体架构

  • rabbitmq server提供进程间消息传递的通道,无论是消息发送者还是消息的接收者,在rabbitmq server看来都是客户端,使用rabbitmq server提供的队列传递消息前,都需要先连接rabbitmq server。如下:
    请添加图片描述
  • 而客户端和rabbitmq server之间的通信遵守AMQP规范,只要实现该规范就可以用于连接rabbitmq server,我们本文中使用的pika逐渐,就是按照AMQP协议实现的一个与rabbitmq server通信的工具。这里我们以python客户端pika为例。
  • 下图是AMQP(Advanced Message Queuing Protocol)规范中定义的AMQ(Advanced Message Queuing)具体模型:
    在这里插入图片描述- AMQ模型包含了下面3个主要的实体:
  1. message queue: 消息队列,这是AMQP规范的核心实体,它负责存储客户端消息的存储和转发,这些消息可以在内存中,也可以在磁盘上,消息队列根据应用场景定义了各种属性,比如消息私有/共享属性,暂态/持久化属性等。通过对消息队列各种属性的组合,AMQP可以实现传统的消息中间件实体,比如共享队列,可以将消息转发给多个消费者;私有队列,将消息转发给特定的一个队列等。
  2. exchange: 交换机,这是先于消息队列处理消息的实体,它负责路由客户端发来的消息到交换机绑定的消息队列上,交换机根据客户端设置的路由地址(routing key)将消息发送到正确的消息队列上。
  3. routing key:路由关键字,它扮演路由地址的角色,在点对点的路由场景下,routing key通常就是队列的名字。在基于标题的订阅的场景下,生产者的routing key通常是消费者routing key的子集,消费者的routing key通常用字符串匹配模型描述。
  • 下图描述了一个消息通过rabbitmq server传输时的流向:
    在这里插入图片描述

类与方法

BlockingConnection

  • BlockingConnection类声明的全路径为pika.BlockingConnection,它抽象了pika客户端工具和rabbitmq server的连接,封装了连接客户端的方法(__init__函数)以及通过客户端建立通信通道的方法(channel函数)。

init

  • BlockingConnection的初始化函数用来与rabbitmq server建立连接,传入pika.connection.Parameters类的实例作为参数,下面的代码用于连接本机上的rabbitmq server,返回一个BlockingConnection类的实例。
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))           
  • 通过rabbitmqctl list_connections可以看到连接到rabbitmq server的连接数,如下所示,pika客户端工具和rabbitmq server监听的5672端口建立了连接,客户端的端口为57428
    在这里插入图片描述

channel

  • BlockingConnectionchannel函数用于和rabbitmq server建立一个消息通信通道,这个方法返回pika.adapters.blocking_connection.BlockingChannel类的实例。客户端和rabbitmq server的所有通信都通过此通道展开,因此客户端利用rabbitmq通信的编程模型都基于这个类提供的方法展开。下面的代码创建了一个通道:
channel = connection.channel()
  • 通过rabbitmqctl list_channels可以看到连接到serverde channel信息:
    在这里插入图片描述

BlockingChannel

  • BlockingChannel类声明的全路径为pika.adapters.blocking_connection.BlockingChannel,是对客户端与rabbitmq server通道的抽象,无论客户端作为生产者还是消费者,向rabbitmq server提供的队列发送消息基本都通过操作该对象的实例完成。这是rabbitmq 消息通信的核心类。

queue_declare

  • queue_declare是向rabbitmq server声明一个队列,如果这个队列不存在,rabbitmq server会创建,因此queue_declare可以说是创建rabbitmq的方法。下面的代码如果第一次调用,在rabbitmq server中会创建一个名为hello的消息队列:
channel.queue_declare(queue='hello')
  • 通过rabbitmqctl list_queues可以看到rabbitmq server中创建的队列信息:
    在这里插入图片描述

queue_delete

  • 通过queue_delete可以删除队列:
channel.queue_delete(queue='hello')

exchange_declare

  • 声明一个用于路由消息的交换机,客户端通过这个函数来自定义自己的交换机,如果不定义则rabbitmq server会使用默认的交换机。该接口通常用来实现针对某个交换机订阅消息的模型,或者基于交换机的某个标题订阅消息的模型。下面订阅了一个fanout类型的的交换机,这种类型的交换会将它接收到的消息转发给所有绑定到该交换机的消息队列上。
channel.exchange_declare(exchange='logs', exchange_type='fanout')

basic_publish

  • rabbitmq server是专为消息传递开发的开源组件,通常一个客户端发送消息,称生产者 —— producer ,另一个客户端接收消费,称消费者 —— consumer。当生成者准备好消息后,通过basic_publish方法发送。basic_publish函数根据AMQP规范实现了消息发送,其具体用法参考AMQP手册。
  • basic_publish函数有两个重要的参数,分别是exchange和routing_key,exchange指定将本条消息发送到哪个交换机,routing_key设置该交换机在路由匹配时使用的关键字。
  • 下面的代码将message消息发送到rabbitmq server默认使用的交换机,并设置rabbitmq server通过"hello"关键字进行路由,这种情况下关键字通常为队列的名字。
channel.basic_publish(exchange='', routing_key='hello', body=message)               
  • 下面代码定义了一个扇出类型的交换机,命令logs,然后将message消息发送到logs交换机,
channel.exchange_declare(exchange='logs', exchange_type='fanout')
channel.basic_publish(exchange='logs', routing_key='', body=message)

basic_consume

  • basic_consume方法是消费者接收rabbitmq消息的函数,它的核心参数是指定消费者感兴趣的队列名,以及消息到达时的处理函数,下面是客户端为消费者的代码,所有绑定到logs交换机上的队列,都会收到此消息,消息到达后通过callback函数处理,将其打印出来
channel.exchange_declare(exchange='logs', exchange_type='fanout')
result = channel.queue_declare(queue='', exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange='logs', queue=queue_name)
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
    print(" [x] %r" % body)
channel.basic_consume(
    queue=queue_name, on_message_callback=callback, auto_ack=True)

实验验证

安装

erlang

  • erlang语言是rabbitmq的实现语言,下载erlang零依赖源码并编译成rpm包
wget https://github.com/rabbitmq/erlang-rpm/archive/refs/heads/master.zip
unzip master.zip
cd erlang-rpm-master 
make prepare
make erlang
cd RPMS/x86_64/ && rpm -ivh erlang-xxx-x.el7.x86_64.rpm

pika

  • rabbitmq适配了多种消息队列协议,AMQP是其中一个,它是一个通用的开源消息传递协议。pika组件作为AMQP协议的实现,可以作为工具方便地与rabbitmq的队列交互,收发消息,这里需要安装,方便后续编程使用
python3.6 -m pip install pika --upgrade
rm -f /usr/bin/python && ln -s /usr/bin/python3.6 /usr/bin/python	/* 将python软链接到python3.6 */

rabbitmq_server

  • rabbitmq_server实现了消息队列,它作为server的形式供应用使用。客户端和服务端通过rabbitmq传递消息时,首先需要先和rabbitmq_server建立连接,我们需要下载该server并部署到本地。
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.10.6/rabbitmq-server-generic-unix-3.10.6.tar.xz
xz -d rabbitmq-server-generic-unix-3.10.6.tar.xz
tar -xf rabbitmq-server-generic-unix-3.10.6.tar
echo "export PATH=${PATH}:/path/to/rabbitmq_server-3.10.6/sbin" >> /etc/profile && source /etc/profile

启动

  • 后台启动rabbitmq_server,其监听的端口默认为5672。
rabbitmq-server -detached

在这里插入图片描述

RPC示例

  • 基于rabbitmq server的RPC的通用框架如下:
    请添加图片描述
  • 客户端将要调用的远程例程索引和函数通过rabbitmq server传递给服务端,服务端解析索引后在函数列表中匹配对应函数,执行例程,将例程的返回值再通过rabbitmq server返回给客户端。
  • 我们将rabbitmq官网的RPC例子摘出,结合上面的rabbitmq基础支持分析其流程,我们的RPC服务端实现一个函数,接受一个整数n,返回整数n作为索引在斐波那契数列的值,函数如下:
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)
  • 客户端通过rabbitmq传输函数的输入n,服务端收到后执行函数并返回结果。

Server

#!/usr/bin/env python3.6
import pika
# 连接rabbitmq server,这里我们的rabbitmq server部署在本地,pika工具通过5672端口连接
connection = pika.BlockingConnection(
    pika.ConnectionParameters(host='localhost'))
# 建立和rabbitmq server的通道
channel = connection.channel()
# 声明一个名为rpc_queue的队列,如果rabbit sever没有会创建
channel.queue_declare(queue='rpc_queue')
# 实现客户端要调用的例程函数
def fib(n):
    if n == 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fib(n - 1) + fib(n - 2)
# 实现rabbitmq server消息到达时的回调函数,处理客户端发送来的消息
def on_request(ch, method, props, body):
	# 取出消息体并转化为整数
    n = int(body)
    print(" [.] fib(%s)" % n)
    # 执行例程函数,获取函数返回值
    response = fib(n)
	# 将函数的返回值封装成消息体,发送到RPC的客户端
	# 
    ch.basic_publish(exchange='',
                     routing_key=props.reply_to,
                     properties=pika.BasicProperties(correlation_id = \
                                                         props.correlation_id),
                     body=str(response))
    ch.basic_ack(delivery_tag=method.delivery_tag)
# 设置通道的qos,规定客户端最多预取1条消息,防止客户端在处理完当前消息之前又接收到新的消息,造成消息的积压
channel.basic_qos(prefetch_count=1)
# 设置当rpc_queue队列有消息到达时,通过on_request函数进行处理
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)
print(" [x] Awaiting RPC requests")
# 开始监听rpc_queue队列
channel.start_consuming()

Client

#!/usr/bin/env python3.6
import pika
import uuid
# 实现一个远程调用的类
class FibonacciRpcClient(object):
	# 实现类的初始化函数
    def __init__(self):
    	# 连接到rabbitmq server
        self.connection = pika.BlockingConnection(
            pika.ConnectionParameters(host='localhost'))
		# 创建到rabbitmq server的消息通道
        self.channel = self.connection.channel()
		# 声明队列,队列名由rabbitmq server自动生成
		# 由于这个队列用于传输参数和返回结果,只需要RPC的调用者知道这个队列的存在
		# 因此exclusive设置为true表示不允许其它客户端消费此队列的消息
        result = self.channel.queue_declare(queue='', exclusive=True)
        # 通过返回结果中的method属性,可以获取rabbitmq server自动生成的队列名
        self.callback_queue = result.method.queue
		# 将上面创建的队列设置为我们感兴趣的队列,将on_response函数作为消息到达时的处理函数
        self.channel.basic_consume(
            queue=self.callback_queue,
            on_message_callback=self.on_response,
            auto_ack=True)

        self.response = None
        self.corr_id = None
	# RPC Server执行完例程后通过rabbitmq发送返回结果,消息到达时的处理函数
    def on_response(self, ch, method, props, body):
        if self.corr_id == props.correlation_id:
            self.response = body
	# 远程过程调用函数,接受n作为斐波那契数列索引
    def call(self, n):
        self.response = None
        # 产生随机的uuid,用来标识RPC的每条消息
        self.corr_id = str(uuid.uuid4())
        # 将消息发送到rabbitmq server的默认交换机
        # 交换机使用RPC服务端监听的队列名rpc_queue作为路由关键字
       #  消息从交换机出来后会分发到rpc_queue队列,所有监听rpc_queue队列的客户端都会收到此消息
       # 设置本次消息的属性:一是设置客户端在收到本次消息后的回调队列,二是标记本条消息的uuid
        self.channel.basic_publish(
            exchange='',
            routing_key='rpc_queue',
            properties=pika.BasicProperties(
                reply_to=self.callback_queue,
                correlation_id=self.corr_id,
            ),
            body=str(n))
       	# 阻塞等待直到发送的消息被处理     
        self.connection.process_data_events(time_limit=None)
        # 返回RPC Server处理后的消息
        return int(self.response)
# 初始化一个远程调用实例
fibonacci_rpc = FibonacciRpcClient()
# 调用RPC
print(" [x] Requesting fib(6)")
response = fibonacci_rpc.call(6)
print(" [.] Got %r" % response)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rabbitmq—— 从入门到放弃 的相关文章

  • RPC 模型中的correlationId 和临时队列 - AMQP

    我正在读书RPC模型 http www rabbitmq com tutorials tutorial six java html在 AMQP 中使用 RabbitMQ 本教程创建了一个临时队列 并且还correlationId 临时队列是
  • 在使用 FromEventPattern 订阅之前捕获事件

    我正在使用 Rx 框架编写消息监听器 我面临的问题是 我正在使用的库使用一个消费者 每当消息到达时就会发布事件 我已经设法通过以下方式消费传入的消息Observable FromEventPattern但我对服务器中已有的消息有疑问 目前我
  • 没有连接的 AMQP/RabbitMQ 通道什么时候会死亡?

    我有一个简单的 RabbitMQ 测试程序 随机将消息排队 另一个读取它们 所有这些都使用 Spring AMQP 如果消费者死亡 例如 在没有机会关闭其连接或通道的情况下终止进程 则它尚未确认的任何消息似乎将永远保持未确认状态 我看过很多
  • 与 RabbitMQ 相比,Amazon SQS 的性能较慢

    我想在我的 Web 应用程序中集成消息队列中间层 我测试了 Rabbitmq 和 Amazon SQS 但发现 Amazon SQS 速度很慢 我在 Amazon SQS 中每秒收到 80 个请求 而在 Rabbitmq 中每秒收到 200
  • 在点网核心应用程序中使用 RabbitMQ 跳过 MassTransit 中的队列

    我有三个项目 一个是Dot net core MVC 两个是API项目 MVC 正在调用一个 API 来获取用户详细信息 当询问用户详细信息时 我通过 MassTransit 向队列发送消息 我看到跳过队列 第三个项目中有消费者 即API项
  • 如何禁用 RabbitMQ 默认 tcp 监听端口 - 5672

    我已经配置了RabbitMQrabbitmq config具有新端口号的文件 即带有 SSL 的 5671 现在我想禁用默认端口 即 5672 配置文件如下 rabbit ssl listeners 5671 ssl options cac
  • 如何根据条件限制并发消息消耗

    场景 我已经简化了事情 许多最终用户可以从前端 Web 应用程序 生产者 开始工作 繁重的工作 例如渲染大型 PDF 这些作业被发送到单个持久的 RabbitMQ 队列 许多工作应用程序 消费者 处理这些作业并将结果写回到数据存储中 这个相
  • Spring AMQP + RabbitMQ 3.3.5 ACCESS_REFUSED - 使用身份验证机制 PLAIN 拒绝登录

    我遇到以下异常 org springframework amqp AmqpAuthenticationException com rabbitmq client AuthenticationFailureException ACCESS R
  • Spring AMQP Java 客户端中的队列大小

    我使用 Spring amqp 1 1 版本作为我的 java 客户端 我有一个大约有 2000 条消息的队列 我想要一个服务来检查这个队列大小 如果它是空的 它会发出一条消息说 所有项目已处理 我不知道如何获取当前队列大小 请帮忙 我用谷
  • 列出与rabbitmq java客户端API交换的绑定

    我似乎在文档中找不到任何信息 所以我想知道是否可以通过某种方式使用 java RabbitMQ API 获取与交换相关的所有绑定 我在查询 api bindings 时正在寻找类似 http api 结果的内容 api definition
  • RabbitMQ C# API:如何检查绑定是否存在?

    使用 RabbitMQ C API 我如何检查给定队列到给定交换是否存在绑定 很多 RabbitMQ 调用都是幂等的 所以有些人可能会说在这些情况下检查是不必要的 但我认为它们在测试中很有用 您可以使用他们的 REST API 来调用并查看
  • rabbitmq 通道因 PRECONDITION_FAILED 关闭 - 快速回复消费者不存在

    当我们从 Spring Boot 服务向rabbitmq 发布消息时 出现以下错误 而且这是间歇性的 我们无法重现这一点 AMQP 连接 123 11 xxx xx 5672 错误 org springframework amqp rabb
  • MongoDB 架构设计 - 实时聊天

    我正在启动一个项目 我认为该项目特别适合 MongoDB 因为它提供的速度和可扩展性 我目前感兴趣的模块是与实时聊天有关的 如果我要在传统的 RDBMS 中执行此操作 我会将其分为 频道 一个频道有很多用户 用户 一个用户有一个频道但有多条
  • 死信交换 RabbitMQ 丢弃消息

    我正在尝试在 RabbitMQ 中实现 dlx 队列 场景很简单 我有 2 个队列 1 活着 2 死亡 x dead letter exchange 立即 x message ttl 5000 以及 立即 交换 这必然是 1 活着 我尝试运
  • 在 Celery 工作线程中捕获 Heroku SIGTERM 以优雅地关闭工作线程

    我对此进行了大量研究 令我惊讶的是我还没有在任何地方找到一个好的答案 我正在 Heroku 上运行一个大型应用程序 并且我有某些运行很长时间处理的 celery 任务 并在任务结束时保存结果 每次我在 Heroku 上重新部署时 它都会发送
  • 更改 RabbitMQ 队列中的参数

    我有一个 RabbitMQ 队列 最初声明如下 var result channel QueueDeclare NewQueue true false false null 我正在尝试添加死信交换 因此我将代码更改为 channel Exc
  • RabbitMQ 管理插件窗口呈现为空白页面

    I have installed Erlang RabbitMQ and configured the management plugin as per the instructions on the website https www r
  • RabbitMQ - 如何死信/处理过期队列中的消息?

    我有一个队列x expires放 我遇到的问题是我需要对队列中的消息进行进一步处理IF队列过期 我最初的想法是设置x dead letter exchange在队列中 但是 当队列过期时 消息就会消失而不会进入死信交换 如何处理死信或以其他
  • 面向服务的架构 - AMQP 或 HTTP

    一点背景 非常大的整体 Django 应用程序 所有组件都使用相同的数据库 我们需要分离服务 以便我们可以独立升级系统的某些部分而不影响其余部分 我们使用 RabbitMQ 作为 Celery 的代理 现在我们有两个选择 使用 REST 接
  • ECONNREFUSED:无法连接到集群内默认端口上的 RabbitMQ pod

    我的本地集群中有一个运行 RabbitMQ 的 pod 我已经将其配置为 apiVersion v1 kind Service metadata name service rabbitmq spec selector app service

随机推荐

  • C#中的readonly与const区别

    xfeff xfeff const 的概念就是一个包含不能修改的值的变量 常数表达式是在编译时可被完全计算的表达式 因此不能从一个变量中提取的值来初始化常量 如果 const int a 61 b 43 1 b是一个变量 xff0c 显然不
  • 改变无线连接、有线连接的优先级

    有线和无线连的是同一个网络 xff0c 当笔记本打开时 xff0c 总是优先使用无线连接 xff0c 如何转变优先级为有线连接呢 xff1f 1 打开网络和共享中心 2 更改适配器设置 xff0c 打开网络连接窗口 3 单击此窗口的高级菜单
  • 杂感一

    从2014年7月工作至今已有快2年了 xff0c csdn的博客从毕业后就很少上了 工作中有很多收获 技术上 也在不断积累和成长中 不管做什么事情 xff0c 要坚持下去 xff0c 方得初心 xff0c 把坚持养成习惯 xff0c 学习如
  • Kotlin实战---Retrofit网络模型

    没有Kotlin基础的小伙伴先进这里 Koltin基础文章 1 Java和Kotlin互相调用之间的注意事项 1 解决关键字冲突 span class token keyword public span span class token k
  • MFC隐藏主窗口的方法

    隐藏基于对话框的MFC应用程序窗口的方法 推荐这个方法 xff0c 非常好用 很多人可能会将窗口创建出来 然后用一个 ShowWindow SW HIDE 的方法去隐藏窗口 当然这是可以做到隐藏的功能 但是有一点不足的地方就是窗口在隐藏之前
  • JSP 通过Servlet将excel数据导入SQL

    1 gt 在网上下载jxl jar 这个JAR包用于Java操作excel 下载后 xff0c 将这个包复制到工程Webroot下的WEB INF下的lib中 xff0c 或是在工程中导入jxl jar包 2 gt 准备excel文件 如图
  • 1=5,2=15,3=215,4=2145,那么5=?

    如题 xff0c 1 61 5 xff0c 2 61 15 xff0c 3 61 215 xff0c 4 61 2145 xff0c 那么5 61 xff1f 答案 xff1a 5 61 1 哎 xff0c 这个题出的 xff0c 没反应过
  • 村子里有50个人,每人有一条狗,在这50条狗中有病狗(这种病不传染),于是人们要找出病狗。

    xff29 xff22 xff2d 公司向来以高素质人才作为企业持续竞争力的保证 进入 xff29 xff22 xff2d 公司是差不多每个 xff29 xff34 人的梦想 下面这条 xff29 xff22 xff2d 公司的面试题 xf
  • 删除单向链表中的某一个节点

    已知一个单向链表的表头head xff0c 写出一个删除某一个节点的算法 xff0c 要求先找到此节点 xff0c 然后删除 include lt iostream gt using namespace std typedef struct
  • 多段图的最短路径问题-----动态规划法

    对多段图 xff0c 求最短路径 xff0c 如图 xff1a 对其使用动态规划法 xff1a 阶段 xff1a 将图中的顶点划分5个阶段 xff0c k 状态 xff1a 每个阶段有几种供选择的点s 决策 xff1a 当前状态应在前一个状
  • Android 文件存储 和 权限管理

    转载请标明出处 xff1a xff1a http blog csdn net huaiyiheyuan article details 52473984 android SD卡主要有两种存储方式 Internal External Stor
  • Python 连接Linux服务器完成上传下载和执行命令及查询目录下的文件

    Python 连接Linux服务器完成上传下载和执行命令及查询目录下的文件 记录一些用于连接linux获取远端文件或者上传文件的小工具 xff0c 另外还有执行shell命令和查找linux目录下文件是否存在 span class toke
  • 【学习整理】Windows server 2019AD域之创建用户的三种形式

    内置工具程序csvde exe xff0c ldifde exe dsadd exe csvde exe xff1a 能利用它来新建用户账户 xff0c 但不能修改 需要将用户数据输入纯文本文件中ldifde exe xff1a 可以利用它
  • 邮件退信提示

    一般情况下 xff0c 当您发送的邮件无法正常到达收件人时 xff0c winmail 邮件系统将会自动给您发一封系统退信 xff0c 这封退信通知里面包含了无法正常发送到对方邮件地址的原因 xff0c 所以绝大多数情况下可以通过退信通知来
  • 学以致用--注解加反射实现Butterknife的View注入功能

    不知不觉更文挑战来到了第三天 xff0c 今天来写一篇反射和注解的应用篇 对反射不熟悉的同学 xff0c 请阅读 搞懂Java反射和JDK里的动态代理 对注解不熟悉的同学 xff0c 请阅读 搞懂Java高级特性 注解 首先这篇文章 xff
  • 百度试题---开发测试工程师

    一 问答题 说出常用的几种希哈函数 xff0c 其作用是什么 xff1f 描述OSI 的七层网络结构 xff0c HTTP 工作在哪一层 xff1f 描述一段C 语言代码程序能运行起来的代码要求和执行过程 二 算法设计 有一车苹果 xff0
  • 错误: Entry在LinkedHashMap中不是公共的; 无法从外部程序包中对其进行访问

    遇到了一个很奇怪的问题 xff0c 使用LinkedHashMap来做LRU缓存时 xff0c 重写protected boolean removeEldestEntry Entry lt String String gt eldest 方
  • 【安卓真机调试】较全面的Android真机调试详解

    目录 1 启动调试功能1 1 配置设备上的开发者选项1 2 运行可调试的 build 变体 2 开始调试2 1 设置断点2 2 选择设备2 3 在工具栏中点击Debug图标2 4 打开Debug窗口2 5 将调试程序连接到正在运行的应用上
  • 如何搭建ftp服务器实现文件共享

    这里以windows系统和linux系统为例 xff0c 简单介绍一下如何在这2种系统下搭建ftp服务器 xff0c 整个过程非常简单 xff0c 感兴趣的朋友可以自己尝试一下 xff1a windows windows系统自带有ftp服务
  • Rabbitmq—— 从入门到放弃

    文章目录 背景总体架构类与方法BlockingConnection init channel BlockingChannelqueue declarequeue deleteexchange declarebasic publishbasi