oslo_messaging学习系列之二《OpenStack中使用消息队列》

2023-05-16

目录

消息驱动

oslo.messaging模块详解

Transport

Target(oslo_messaging.target.Target)

Server

RPC Client

RPC Client参数传递


消息驱动

服务端使用的驱动函数:

函数1:
class kombu.Queue(name=‘’,exchange=None,routing_key=‘’,channel=None,bingdings=None, on_declared=none,**kwargs)
parameters:
name--队列名字
exchange -- 绑定的exchange
routing_key--路由,exchange根据routing_key来路由消息
channel -- 绑定的channel
durable -- Sever重启,queue是否消失
exclusive -- 队列专有,属于当前连接(connection)
auto_delete -- 所有的consumer使用完后,自动删除。如果exclusive被设定,则auto_delete默认被设定为true。

函数2:
queue_declare(self,nowait=False,passive=False),在server上创建队列
nowait	True不等待server的相应信息
passive	True server不创建队列,可以用来检测当前server是否存在该队列

queue_bind(self, nowait=False)	queue与Exchange绑定

declare(self, nowait=False)	创建队列并与Exchange绑定,内部调用queue_declare,queue_bind

purge(self,nowait=False)	删除队列中所有信息

delete(self,if_unused=False, if_empty=Flase,nowait=False)删除队列
	if_unused		队列没被使用时删除
	if_empty		队列空时删除

作为服务端的代码示例:

from kombu.entity import Exchange, Queue
from kombu.messaging mport Consumer
from kombu.connection import Connection

conn = Connection(‘amqp://openstack:***@ip:5672’)//建立一个connection连接
channel = conn.channel()
new_exchange = Exchange(‘openstack’,type=’topic’,durable=False)//创建exchange
new_queue=Queue(“queue_name”,channel=channel,exchange= new_exchange,routing_key=’compute’,durable=False)//设置队列与exchange和channel绑定
new_queue.declare()
queue.consume(callback=self._callback, consumer_tag=str(tag), nowait=self.nowait)

生产端如何发送消息呢?

producer = conn.Producer(serializer='json')
producer.publish({'name': '/tmp/lolcat1.avi', 'size': 1301013},
                      exchange=media_exchange, routing_key='video',
                      declare=[video_queue])

示例:

from kombu.entity import Exchange, Queue
from kombu.messaging mport Consumer,Producer
from kombu.connection import Connection
from kombu.transport.base import Message

conn = Connection(‘amqp://openstack:***@ip:5672’)//建立一个connection连接
channel = conn.channel()#从这个连接中获取一个channel
new_exchange = Exchange(‘openstack’,type=’topic’,durable=False)//用于描述exchange
new_queue=Queue(“queue_name”,channel=channel,exchange= new_exchange,routing_key=’compute’,durable=False)//设置队列与exchange和channel绑定
#构造message
message=Message(channel=channel,body=”This is test”)
# 构造Producer
producer=Producer(channel,serializer=’json’,exchange=new_exchange)
producer.publish(message.body,routing_key=’compute’)

oslo.messaging模块详解

OpenStack中使用组件oslo.message作为消息传递的中间件,通过MQ完成很多协程任务,实现服务之间的分布式部署。

Oslo.message中的几个重要概念:

server(服务端):为RPC服务器提供给多个端点(Endpoint),每个包含一组远程调用客户端的方法,创建一个RPC服务器,提供一个传输队列,目标和端点列表。

client(客户端):客户端负责调用服务端提供的RPC接口。

exchange(交换):一个包含各个项目主体(topic)的容器。

namespace(命名空间):服务器端可以在一个主体上暴露多组方法,每组方法属于一个命名空间。

method(方法):一个方法由一个名字和一组命名的参数组成。

transport(传输工具):一个传送RPC请求到服务器端并将响应返回给客户端的底层消息系统。目前常用的transport有rabbitmq和qpid。

API version:每个命名空间都有一个版本号,当命名空间的接口变化时,这个版本号也会响应增加。向前兼容的修改只需要更改小版本号,向前不兼容的更改需要更改大版本号。

Transport

Tansport(传输层)主要实现RPC底层的通信(比如socket)以及事件循环、多线程等其他功能。可以通过URL来获得指向不同Transport实现的句柄。URL格式:

Transport://user:password@hotst1:port[,hostN:portN]/virtual_host

    从cfg对象中读取transport_url、rpc_backend、control_exchange信息构造Transport对象。用户可以使用oslo.messaging.get_transport函数来获取transport对象实例的句柄。

    import oslo_messaging

    transport = solo_messaging.get_transport(cfg, url=None,**kwargs)

    对于notify,这里调用的应该是:

    url = "rabbit://test:test@192.168.117.156:5672/"

oslo_messaging.set_transport_defaults("jorh_notify")

transport = oslo_messaging.get_notification_transport(cfg.CONF, url=url)

    jorh_notify是对应的exchange的值。

    get_transport和 get_notification_transport得到的是两个不同的对象,但是继承的是同一个对象,因此用相同的函数也不会有什么影响!

注释:

  1. oslo_messaging.set_transport_defaults函数是设置默认的exchanges值,在nova中设置的是nova,如果不设置默认是openstack;
  2. oslo_messaging.get_notification_transport是根据url中设置的值去找到对应的driver,例如rabbit对应的是oslo_messaging._drivers.impl_rabbit:RabbitDriver,这个是在oslo_messaging. setup.cfg文件中设置的。然后找到对应的driver,再用这个driver初始化transport对象,这个transport就是对各种driver进行的二次封装。
  3. 初始化RabbitDriver,这个driver首先根据conf中设置参数,初始化connection_pool,然后再调用父类进行初始化oslo_messaging._drivers.amqpdriver.AMQPDriverBase。所有的发送和接收方法都是通过调用这个类来实现的。

因此这里的transport,其实就是一个类,其中有一个connection_pool与rabbitmq server保持连接状态,然后发送或者接收就会调用这个类中的方法。

Target(oslo_messaging.target.Target)

Target封装指定某一个消息最终目的地的所有信息。Target封装了所有将要用到的信息,以确定应该将消息发送到何处或服务器正在侦听什么信息。不管作为服务端还是作为客户端,Target对象是必须构造的!

RPC Server target:

    topic和server是必须的,exchange是可选的。

RPC endpoint target:

    namespace和version是可选的

RPC client sending a message:

    topic是必须的,其他的属性是可选的

Notification Server target:

    topic是必须的,exchange是可选的;其他属性忽略。

Notificaton target:

    topic是必须的,excahnge是可选的;其他属性忽略。

注意:在不同场景下构造Target对象需要不同的参数。

  1. 创建RPC服务器时,需要topic和server参数,exchange参数可选(默认为openstack);指定一个endpoint的target时,namespace和version是可选的。
  2. 客户端发送消息时,需要topic参数,其他可选。
  1. endpoints:是可供别人远程调用的方法,也是RPC中的远程函数
  2. executor:服务运行的方式,单线程或者多线程

Server

一个RPC服务器可以暴露多个endpoint,每个endpoint包含一组方法,这组方法可以被客户端通过transport对象远程调用。创建Server对象时,需要指定Transport、Target和一组endpoint。

from oslo_config import cfg
import oslo_messaging
from oslo_log import log as logging
import time

CONF = cfg.CONF
LOG = logging.getLogger(__name__)
logging.register_options(CONF)
logging.setup(CONF,"myservice")
CONF(default_config_files=["app.conf"])
server=None
class ServerControlEndpoint(object):
    def __init__(self,server):
        print("init the ServerControlEndpoint function!")
        self.server = server
    def stop(self,ctx):
        print("call the stop function")
        if self.server:
            print("start to stop the sever")
            self.server.stop()

class TestEndpoint(object):
    def test(self,ctxt,arg):
        print("test")
        print(arg)
        time.sleep(100)
        return arg
url = "rabbit://test:test@192.168.117.156:5672/"
oslo_messaging.set_transport_defaults("jorhson_control") #设置control_exchange
transport = oslo_messaging.get_transport(cfg.CONF, url=url) #得到transport方法
target = oslo_messaging.Target(topic="jorh_topic",server="jorh_server") #target其实就是一个对象
endpoints = [ServerControlEndpoint(server),TestEndpoint()]
server = oslo_messaging.get_rpc_server(transport,target,endpoints,executor=None)
try:
    server.start()
    while True:
        time.sleep(10)
except KeyboardInterrupt:
    print("Stopping server")
    server.stop()

注意oslo_messaging.get_rpc_server函数返回的server对象,是对transporttarget对象的封装,其中的start方法,也是调用transport对象的driver(这里是oslo_messaging._drivers.amqpdriver.AMQPDriverBase)中的listen方法,向rabbitmq服务端注册。这里传入的topicserver是在rabbitmq 服务端注册成队列,一共有三种队列:

        conn.declare_topic_consumer(exchange_name=self._get_exchange(target),
                                    topic=target.topic,
                                    callback=listener)
        conn.declare_topic_consumer(exchange_name=self._get_exchange(target),
                                    topic='%s.%s' % (target.topic, target.server),
                                    callback=listener)
        conn.declare_fanout_consumer(target.topic, listener)

nova-compute为例,第一个创建的是compute direct,第二个创建的是compute.node1 direct,第三个创建的是compute -***  fault***代表随机字符;由于该随机字符是随机的,每次启动rpc consumer的时候,都会创建一个新的fault类型的队列绑定在compute-***这个exchange上;nova-compute每重启一次,都会创建一个新的compute_fanout_***队列,绑定在compute_fanout这个exchange

topic: computecompute.node1(即所谓的queue)是绑定在jorhson_control上(exchanges

fault类型的队列,绑定在compute.node1_fanout这个 exchanges

openstack中的nova-compute服务创建RPC consumer的过程和上面的代码是类似的。

这里注册的回调函数是:oslo_messaging._drivers.amqpdriver.RpcAMQPListener

也即是针对server端,如果有消息被服务端接收之后,都会调用这个类对象进行处理!

注:python中一切皆对象,函数也是对象,同时也是可调用对象(callable)。自定义的函数、内置函数和类都属于可调用对象,但凡是可以把一对括号()应用到某个对象身上都可以称之为可调用对象,判断对象是否可调用可以用函数callable。一个类实例要变成一个可调用对象,只需要实现一个特殊方法__call__().

RpcAMQPListener类对象会将oslo_messaging._drivers.amqpdriver.AMQPIncomingMessage作为自有变量,在RpcAMQPListener对象中会首先调用message.acknowledge(),对客户端的消息进行确认;之后调用oslo_messaging._drivers.amqpdriver.AMQPListener对象(RpcAMQPListener的父类),在该函数中会进一步调用AMQPIncomingMessage对象,并将该对象放入到incoming队列中。

Nova-compute是如何创建RPC server以及如何收发消息的:

  1. nova.service.Service.start函数中会调用messaging.get_rpc_server函数构造rpc_server对象,如上述例子所示,传入的参数有TRANSPORTtarget,这两个的构造过程和上述例子差不多。Endpoints是通过nova.compute.manager._ComputeV5Proxy来构造的,就是获取manager对象中的所有类方法。
  2. oslo_messaging.rpc.server.get_rpc_server方法:首先将endpoints中的方法封装在oslo_messaging.rpc.dispatcher.RPCDispatcher对象中,后续可以通过调用oslo_messaging.rpc.dispatcher.RPCDispatcher.dispatch函数,对方法进行进一步的处理。将RPCDispatcher对象作为参数传入,并返回oslo_messaging.rpc.server.RPCServer对象。
  3. nova.service.Service.start函数中调用RPCServer.start()方法实现服务的创建。
  4. Nova层面调用的RPCServer.start函数是oslo_messaging.server.MessageHandlingServer.start,在这个函数中首先创建一个listeneroslo_messaging.rpc.server.RPCServer._create_listener--> oslo_messaging._drivers.amqpdriver.AMQPDriverBase.listen,这个函数创建三个队列,并把对象oslo_messaging._drivers.amqpdriver.RpcAMQPListener作为这三个队列的回调函数,完成相应队列的启动;最后返回oslo_messaging._drivers.base.PollStyleListenerAdapter对象;然后再调用新创建listener(PollStyleListenerAdapter)start函数,也就是oslo_messaging._drivers.base.PollStyleListenerAdapter.start;前面创建三个队列的时相当于nova_compute接收消息的服务就已经启动了,这里只是将self.listener.start(self._on_incoming)[oslo_messaging._drivers.base.PollStyleListenerAdapter.start]中传入的oslo_messaging.server.MessageHandlingServer._on_incoming 作为callback函数放到一个线程中进行调用,因为这个函数会进一步调用oslo_messaging.rpc.server.RPCServer._process_incoming_process_incoming函数也会进一步调用RPCDispatcher.dispatch完成最终compute服务中的函数分发。
  5. 步骤4中创建队列的函数是oslo_messaging._drivers.impl_rabbit.Connection.declare_direct_consumer,即先构造oslo_messaging._drivers.impl_rabbit.Consumer对象,在这个对象中,会将回调对象RpcAMQPListener进行进一步的封装;这个对象是比较接近底层驱动的函数,直接调用rabbitmqAPI,向其中注册一个回调函数,这个回调函数会将接收到的消息转换成python能够识别的字符,并封装成oslo_messaging._drivers.impl_rabbit.RabbitMessage对象,然后再调用RpcAMQPListener对象对该消息进行处理。因此在RpcAMQPListener对象中调用的acknowledge函数就是oslo_messaging._drivers.impl_rabbit.RabbitMessage.acknowledge函数。
  6. RpcAMQPListener对象中又会调用oslo_messaging._drivers.amqpdriver.AMQPListener来处理得到的消息,同时又会将对象oslo_messaging._drivers.amqpdriver.AMQPIncomingMessage作为其成员变量,后面会用到该变量来区分cast请求还是call请求,分别进行处理。AMQPListener则会调用AMQPIncomingMessage对象进行消息的处理,并同时把AMQPIncomingMessage对象放入到self.incoming列表中。
  7. 在步骤4中,传入PollStyleListenerAdapter对象中的参数有一个是RpcAMQPListener,在oslo_messaging._drivers.base.PollStyleListenerAdapter.start函数中会创建一个线程,不断的调用oslo_messaging._drivers.amqpdriver.AMQPListener.poll获取得到的消息,这个消息就是AMQPListener对象中添加到self.incoming列表中的AMQPIncomingMessage对象,并交给刚才传入的回调函数_on_incoming来处理,层层调用直到oslo_messaging.rpc.server.RPCServer._process_incoming,完成具体函数的分发。
  8. 消息处理:在_process_incoming函数中会调用self.dispatcher.dispatch(message)进行具体的消息分发,dispatch对象是步骤2中传入oslo_messaging.rpc.server.RPCServer对象的oslo_messaging.rpc.dispatcher.RPCDispatcher对象。从原来的endpoint中找到对应的函数原型,将参数经过deserialize_entity反序列化之后,得到对象实例进行处理,并将返回的消息经过序列化之后再返回给对方(如果有的话)。
self.endpoints=[<nova.compute.manager.ComputeManager>, <nova.baserpc.BaseRPCAPI >]

endpoint=nova.compute.manager.ComputeManager

self.serializer.deserialize_context=<bound method ProfilerRequestContextSerializer.deserialize_context of <nova.rpc.ProfilerRequestContextSerializer >>

最终在oslo_messaging.rpc.dispatcher.RPCDispatcher.dispatch函数中完成类方法的调用和参数的处理

9. 在_process_incoming函数中会调用message.reply(res),实际上调用的函数是oslo_messaging._drivers.amqpdriver.AMQPIncomingMessage.reply函数,会判断是否存在msg_id,这个参数是客户端传过来的,如果不存在这个参数,则直接返回了,存在还要向这个队列中发送消息。

RPC Client

远程调用RPC Server上的方法。远程调用的时候,需要提供一个字典对象指明调用的上下文,调用方法的名字和传递给调用方法的参数。

调用方式:

  1. 通过cast方式,异步远程调用,请求发送之后客户端直接返回。
  2. call方式 同步远程调用,等待服务器响应返回结果。

不管是cast还是call方式,其在业务层的构造方式基本一致:

cctxt = self.router.client(ctxt).prepare(
                server=_compute_host(None, instance), version=version)
cctxt.cast(ctxt, 'rescue_instance', **msg_args)
或者
return cctxt.call(ctxt, 'remove_volume_connection',
                          instance=instance, volume_id=volume_id)

只有调用的方法不一样,cast和call。

需要比较下cast和call的方式在底层实现上有什么不一样!直接比较两个函数中的传参即可看出:

oslo_messaging.rpc.client._BaseCallContext.cast:
self.transport._send(self.target, msg_ctxt, msg, retry=self.retry, transport_options=self.transport_options)

oslo_messaging.rpc.client._BaseCallContext.call:
result = self.transport._send(self.target, msg_ctxt, msg, wait_for_reply=True,
                    timeout=timeout, call_monitor_timeout=cm_timeout,
                    retry=self.retry, transport_options=self.transport_options)

可以通过上述代码看到两者最主要的区别在于在调用transport中的_send函数时,cast没有传time_out参数,而call却传了该参数,并且设置wait_for_reply为true。

跟着函数调用继续:oslo_messaging._drivers.amqpdriver.AMQPDriverBase._send

在这个函数中,会判断wait_for_reply标志位,如果该值为true,则代表是同步消息,会构造msg_id,并通过self._waiter.listen(msg_id)创建一个队列;然后将消息发送给服务端,这一步是cast和call都是一样的流程;后面客户端会继续监听这个msg_id队列,并设置一个超时时间,如果在设置的时间内未监听到服务端发来的消息,则会抛出超时的异常错误;最后会通过self._waiter.unlisten(msg_id)取消队列。

作为服务端,必须注册一组endpoint,这个endpoint就是队列接受消息之后的回调函数。见server基本原理。

在oslo.messaging组件中,oslo_messaging._drivers.impl_rabbit.Connection对象是用来建立连接的,和oslo_messaging._drivers.pool.Pool对象一起构成oslo_messaging._drivers.pool.ConnectionPool对象。

oslo_messaging._drivers.amqpdriver.AMQPDriverBase对象对oslo_messaging._drivers.impl_rabbit.Consumer对象和oslo_messaging._drivers.impl_rabbit.Connection对象进行进一步的封装,其中Connection对象中针对self._producer = kombu.messaging.Producer直接调用驱动的参数,然后再调用self._producer.publish将消息发送到目标route中。AMQPDriverBase对象中包含send,send_notification,listen,listen_for_notifications,虽然底层调用并不会区分是call还是cast请求,作为consumer,也不会区分是否是notification,这些都是在AMQPDriverBase对象中完成的。对于call请求,就是利用ReplyWaiter对象(oslo_messaging._drivers.amqpdriver.ReplyWaiter.wait),重新生成一个consumer进行队列的监听。而在listener端,会注册回调函数(对象) RpcAMQPListener(oslo_messaging._drivers.amqpdriver.RpcAMQPListener),在该对象中,得到消息中的_reply_q,并通过向其发送消息,然后ReplyWaiter对象中的线程会不断的轮询该队列,从而得到返回的消息,从而构成了同步消息流程。

而RPC cast请求不会在消息体中构造reply_q,因此RpcAMQPListener不会publish消息。

RPC Client参数传递

以call方法为例,oslo_messaging.rpc.client._BaseCallContext.call,在该函数中会从nova层传入method(在nova层将被远程调用的函数)和kwargs(在调用函数时需要传入的参数)。在oslo_messaging.rpc.client._BaseCallContext._make_message函数中会将method和对应的参数封装成字典的形式;然后将该参数传入oslo_messaging._drivers.amqpdriver.AMQPDriverBase._send方法中,在该方法中首先会判断是否是call请求,如果是call请求还要在这个消息体重增加_reply_q的字段,最后调用oslo_messaging._drivers.common.serialize_msg函数,将字典形式的msg变为JSON格式的消息再进行传输。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

oslo_messaging学习系列之二《OpenStack中使用消息队列》 的相关文章

  • 全景避障、VIO

    VINS Mono代码分析与总结 完整版 单目与IMU的融合可以有效解决单目尺度不可观测的问题 鱼眼摄像头SLAM xff1a PAN SLAM 全景相机SLAM 原论文 xff1a Panoramic SLAM from a multip
  • CAN总线-ACK应答机制分析

    1 xff1a 应答场定义 应答场长度为 2 个位 xff0c 包含应答间隙 xff08 ACK SLOT xff09 和应答界定符 xff08 ACK DELIMITER xff09 在应答场里 xff0c 发送站发送两个 隐性 位 当接
  • 树莓派4b 引脚图

    树莓派 4B 详细资料
  • 控制~线性系统~的能控性和能观性

    现控笔记 xff08 四 xff09 xff1a 能控性和能观性 能控性 xff1a 是控制作用u t 支配系统的状态向量x t 的能力 xff1b 回答u t 能否使x t 作任意转移的问题 能观性 xff1a 是系统的输出y t 反映系
  • 创建功能包

    创建功能包 xff1a catkin create pkg 在Amos WS src路径下 xff0c 打开控制台输入catkin create pkg my package std msgs rospy roscpp 创建一个名为my p
  • SLAM算法

    一 概述 Simultaneous Localization and Mapping SLAM 原本是Robotics领域用来做机器人定位的 xff0c 最早的SLAM算法其实是没有用视觉camera的 xff08 Robotics领域一般
  • 激光雷达入门

    转载自 xff1a https zhuanlan zhihu com p 33792450 前言 上一次的分享里 xff0c 我介绍了一个重要的感知传感器 摄像机 摄像机作为视觉传感器 xff0c 能为无人车提供丰富的感知信息 但是由于本身
  • 【超详细】韦东山:史上最全嵌入式Linux学习路线图

    我是1999年上的大学 xff0c 物理专业 在大一时 xff0c 我们班里普遍弥漫着对未来的不安 xff0c 不知道学习了物理后出去能做什么 你当下的经历 当下的学习 xff0c 在未来的一天肯定会影响到你 毕业后我们也各自找到了自己的职
  • ArUco码辅助定位——计算机视觉

    使用USB网络摄像头和ROS跟踪ArUco Markers
  • 基于D435i的点云重建

    Task 采用D435i采集深度图和RGB图像 xff0c 进行点云重建和聚类 1 xff09 解析Bag数据 xff1a import os import cv2 import numpy as np import rosbag from
  • vncviewer黑屏问题解决

    最近在重启服务器后 xff0c 用vnc进行远程桌面连接时 xff0c vnc能够连上 xff0c 或有提示错误 xff0c 或无提示错误 xff0c 但显示黑屏 在网上搜索了甚久 xff0c 各种google xff0c 各种baidu
  • Unbuntu 系统及VNC Viewer显示中文

    一行命令搞定 xff1a apt get install ttf wqy zenhei
  • 在嵌入式Linux系统上安装打印机

    简介 xff1a 在Linux环境中安装打印机 xff0c 通常是cups ghostscript等 xff0c 但体积通常很大 xff0c 几十兆 在我应用的环境 xff0c 要求打印模块不大于5M xff0c 在网上搜索的方案是将cup
  • 深度学习环境搭建:win10+GTX1060 + tensorflow1.5+keras+cuda9.0+cudnn7

    2018年 2月8日下午 xff0c 开始搭建环境 我新买了联想Y720笔记本电脑一台 xff0c 希望用它来开展深度学习的探索 根据之前的一点点经验 xff0c 搭建深度学习的环境 本篇博客主要记录的是流程 xff0c 不提供相关数据的下
  • Linux C/C++面试题汇总

    Linux C C 43 43 面试题汇总 前言计算机基础程序的内存空间进程和线程相关 关键字conststaticvolatile C C 43 43 指针 前言 最近面试的比较多 xff0c 看了很多关于面试的内容 xff0c 有些平时
  • NVIDIA TX2--3--NVIDIA Jetson TX2 查看系统版本参数状态及重要指令

    Yolov 1 TX2上用YOLOv3训练自己数据集的流程 VOC2007 TX2 GPU Yolov 2 一文全面了解深度学习性能优化加速引擎 TensorRT Yolov 3 TensorRT中yolov3性能优化加速 xff08 基于
  • freertos之timer浅析

    背景 freertos的定时器与我所见得到其他RTOS不一样 xff0c 我知道的ucosii是在每次tick 43 43 的时候会检查定时器链表 xff0c smc rtos也是这样做的 xff0c rtt没看过源码不清楚 xff0c 而
  • vins-fusion gps融合相关总结

    1 简介 xff1a VINS Fusion在VINS Mono的基础上 xff0c 添加了GPS等可以获取全局观测信息的传感器 xff0c 使得VINS可以利用全局信息消除累计误差 xff0c 进而减小闭环依赖 相比于局部传感器 xff0
  • vins-mono里的坐标系

    vins mono里主要涉及三个坐标系 xff1a word坐标系 xff0c body坐标系即IMU帧坐标系 xff0c cam坐标系即相机帧坐标系 对于单目系统而言 xff0c 初始化时就会确定世界坐标系 首先进行纯视觉初始化 SFM
  • 华三交换机配置telnet远程登录和http、https登录

    1 配置管理IP地址 lt H3C gt system view 进入系统视图 H3C int vlan 1 进入管理VLAN1 H3C Vlan interface1 ip address 1 1 1 1 24 配置默认管理IP地址 H3

随机推荐

  • C——char(字符串)转int

    有时候需要对输入的数字进行计算之类的操作 xff0c 这时候需要将char转int类型 char是一个单独字节 xff0c 可以保存一个本地字符集的内容的类型 一般使用char 的格式来使用 int就是一个范围较小的无符号整数类型 注意 x
  • Linux设备驱动——第三章字符驱动

    当对幸福的憧憬过于急切 xff0c 那痛苦就在人的心灵深处升起 加缪 本章的目的是编写一个完整的字符设备驱动 我们开发一个字符驱动是因为这一类适合大部分简单的硬件设备 字符驱动也比块驱动易于理解 本章的最终目的是编写一个模块化的字符驱动 x
  • FreeRTOS(一)系统时钟和中断

    RTOS系统运行必需要有时钟 xff0c FreeRTOS可以选择SysTick或TIM作为时钟源 本文以再stm32f1上的移植介绍 选择SysTick需要在FreeRTOSConfig h中取消SysTick Handler 函数的映射
  • 对于USB Bulk通信发送0包的理解

    写Device USB驱动的时候 xff0c 当Bulk送信发送的数据长度恰好是wMaxPacketSize的整数倍时 xff0c 是否应该发送0包的问题搞得我焦头烂额 查找了好多资料 xff0c 有的说要加 xff0c 这是USB协议的一
  • upload漏洞专题

    一 upload上传绕过专题 后缀检验绕过 1 黑名单检测绕过 1 上传文件重命名 span class token comment 由于只有后缀是可控的 span 所以常见的后缀为php中 php2 php3 php4 php5 phtm
  • Pony语言学习(七)——表达式(Expressions)语法(单篇向)

    一 字面量 xff08 Literals xff09 xff08 一 xff09 Bool值 xff1a 没啥要说的 xff0c 就是true和false x1f44a xff08 二 xff09 数值 xff08 Numeric Lite
  • Pony语言学习(八):引用能力(Reference Capabilities)

    xff08 如果你有更好的翻译 xff0c 请务必联系我 我们需要和Rust术语做到翻译看齐 xff09 一 总览 xff08 特译 xff1a https tutorial ponylang io reference capabiliti
  • Pony语言学习(二):基础类型 之 Class

    写在前面的 xff1a 这次咱们来唠唠Pony的基础类型 xff0c 这里说的基础类型指的不是int string boolean float什么内置数据类型 xff0c 而是Pony中用来定义类型的几种方法 xff0c 分别是 Class
  • 匿名管道和命名管道

    进程间通信 xff08 IPC xff09 每个进程有各自不同的用户地址空间 xff0c 任何一个进程的全局变量在另一个进程中都看不到 所以进程之间要交换数据必须通过内核 xff0c 在内核中开辟一块缓冲区 xff0c 进程1把数据从用户空
  • ROS修改rosbag中话题的frame_id,以及发布坐标在Rviz中与点云融合显示

    ROS修改rosbag中话题的frame id xff0c 以及发布坐标在Rviz中与点云融合显示 读取rosbag xff0c 修改话题frame id与话题名并循环发布 span class token keyword import s
  • 定义类模板,未定义的引用

    在使用类模板的时候 xff0c 将类的申明和定义放在两个文件中 xff0c 在编译的时候出现 XXX未定义的引用 xff0c 即找不到对应的函数实现 解决办法是 xff1a 将类的定以和申明都放在一个头文件中 xff0c 这样就可以避免上述
  • It is a distutils installed project

    方法1 xff1a 方法2 xff1a 直接全局搜索对应文件 xff0c 具体包括 34 package name 34 文件夹 和 34 package name 34 egg info xff0c 找到后直接删除即可 eg sudo f
  • git将当前修改文件生成patch

    应用场景 xff1a 只想将当前文件夹中改动的文件打包成一个patch xff0c 并不想提交相应的代码 执行命令 xff1a git diff nova api openstack compute servers py nova cmd
  • OpenStack--虚拟机操作(挂起、暂停和搁置)

    1 OpenStack虚拟机三种操作 1 1 挂起和恢复 xff08 suspend和resume xff09 挂起 xff1a openstack中的挂起 xff08 suspend xff09 操作是将虚拟机的内存保存到磁盘中 xff0
  • eMMC 里 DDR52 HS200 HS400 等的含义

    eMMC 里 DDR52 HS200 HS400 这些名词指的是不同的速度 DDR52就是最高 52M clock xff0c 数据速率就是 52 x 2 61 104 HS200 就是最高 200M clock xff0c 单通道 xff
  • Libvirt的job机制3--基本原理

    在Job机制简介中介绍了在libvirt中调用接口时如何使用Job机制 xff1b 在条件变量基本原理中介绍了条件变量的一些基本原理 xff0c 这个是Job机制的基础 本文主要介绍Job机制中用到的一些函数 结构体等变量 xff0c 分析
  • libvirt初始化流程分析

    libvirtd daemon初始化流程比较复杂 xff0c 本文对代码流程进行初步分析 xff0c 可能有不对的地方 xff0c 随时会进行更新和补充 总体代码流程 main remote remote deamon c daemonCo
  • OpenStack调试方法总结

    1 利用remote pdb进行调试 下载链接remote pdb 2 0 0 py2 py3 none any whl 虚拟化文档类资源 CSDN下载 from remote pdb import RemotePdb RemotePdb
  • Compute通过Conductor访问数据库方法

    目录 方法注册 实例序列化与反序列化 conductor与数据库交互 Instance类对象与ComputeManager类对象 compute服务的worker数量 这里以instance save函数为例进行说明 方法注册 nova组件
  • oslo_messaging学习系列之二《OpenStack中使用消息队列》

    目录 消息驱动 oslo messaging模块详解 Transport Target xff08 oslo messaging target Target xff09 Server RPC Client RPC Client参数传递 消息