目录

Nova API

Nova API 是访问、使用 Nova 各组件服务的唯一途径,作为 novaclient 和 Nova services 之间的中间层。Nova API 需要保证高度的稳定性,所以这些 API 的名称和返回的数据结构都不能轻易的改变。

Nova API 的目录结构

.
|-- ec2
|-- metadata
|-- openstack
| |-- __init__.py
| |-- api_version_request.py
| |-- auth.py
| |-- common.py
| |-- compute
| |-- extensions.py
| |-- rest_api_version_history.rst
| |-- urlmap.py
| |-- urlmap.pyc
| |-- versioned_method.py
| |-- wsgi.py
|-- opts.py
|-- sizelimit.py
|-- validation
| |-- __init__.py
| |-- parameter_types.py
| |-- validators.py
|-- validator.py

ec2 | metadata | openstack 这三个目录,分别对应了 Nova 所提供的三种类型 API 服务(EC2 API/Openstack API/Metadata API)。其中 Openstack API 含有 v2/v3/v2.1 三个版本,它们的源代码存放于 nova/api/openstack/compute/ 路径下。对于 Openstac API 而言,每一个 API 都对应一种资源。 v2 版本的核心资源就存放在nova/api/openstack/compute/ 路径下,包含了一些 images.py/servers.py 等资源,所有的资源在 API 服务启动的时候就会被加载。

Nova API 的执行过程

1. novaclient 将 Commands 转换为标准的HTTP请求

一般的 Openstack 用户和管理员能够通过执行简易的 Openstack Commands 来管理和使用 Openstack 。但需要注意的是,Openstack Services 的 API 并不会识别这类指令,所以在 API 的外层还需要一重转化机制 —— novaclient

如果是使用 Devstack 进行部署的话,novaclient 会被默认安装在本地,安装目录为 /usr/local/lib/python2.7/dist-packages/novaclient ,我们可以通过设置该目录下的 shell.py 文件中的DEFAULT_OS_COMPUTE_API_VERSION选项来修改 novacliient 的版本。AS BELOW:

DEFAULT_OS_COMPUTE_API_VERSION = '2.latest'

执行 Openstack Commands 都发生了什么? (以 nova list 为例)

1. 执行 nova list指令

2. 该指令被 shell.py 捕获

3. shell.py 将所需要的 Header/Context 等信息和 nova list 的操作请求内容进行组合封装,形成一个标准的 HTTP 请求

4. HTTP 请求被 nova-api 监听捕获

5. nova-api 按照 WSGI 规范的流程来完成HTTP请求的分发、路由、执行、响应的操作。

EXAMPLE:(为了方便查看对下面的Output做了处理)

fanguiju@jmilkfan:~/devstack$ nova --debug list    # --debug 选项:打印出一些中间的请求信息

×××××××××××××××××××××××××××××××××××××××××××××××# 1.GET http://200.21.18.2:5000/v2.0 验证 keystone v2.0 version 是否为稳定版本
DEBUG (session:198) REQ: curl -g -i -X GET http://200.21.18.2:5000/v2.0 -H "Accept: application/json" -H "User-Agent: python-keystoneclient"
INFO (connectionpool:207) Starting new HTTP connection (1): 200.21.18.2
DEBUG (connectionpool:387) "GET /v2.0 HTTP/1.1" 200 337 DEBUG (session:216) RESP: [200] Content-Length: 337 Vary: X-Auth-Token Keep-Alive: timeout=5, max=100 Server: Apache/2.4.7 (Ubuntu) Connection: Keep-Alive Date: Thu, 04 Aug 2016 03:31:34 GMT Content-Type: application/json x-openstack-request-id: req-39b02ac4-7cce-441b-bc25-140e75ab4e2e
RESP BODY: {"version": {"status": "stable", "updated": "2014-04-17T00:00:00Z", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.identity-v2.0+json"}], "id": "v2.0", "links": [{"href": "http://200.21.18.2:5000/v2.0/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}]}}
××××××××××××××××××××××××××××××××××××××××××××××××# 1. End ××××××××××××××××××××××××××××××××××××××××××××××××# 2. POST /v2.0/tokens HTTP/1.1 添加一个 tokens
#注意:其实这个token是请求Keystone发放的,但是这里的Output并没有展现出来这一步,所以从经验上建议Output只作为参考
DEBUG (v2:86) Making authentication request to http://200.21.18.2:5000/v2.0/tokens
DEBUG (connectionpool:387) "POST /v2.0/tokens HTTP/1.1" 200 2844
DEBUG (session:198) REQ: curl -g -i -X GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813 -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}ac4acef5a9f5a568af553beb59ff5d99f1928849"
INFO (connectionpool:207) Starting new HTTP connection (1): 200.21.18.2
DEBUG (connectionpool:387) "GET /v2.1/6c4e4d58cb9d4451b36e774b348e8813 HTTP/1.1" 404 52 DEBUG (session:216) RESP: [404] Date: Thu, 04 Aug 2016 03:31:34 GMT Connection: keep-alive Content-Type: text/plain; charset=UTF-8 Content-Length: 52 X-Compute-Request-Id: req-c1edb651-a398-4f02-a580-920bec9f1252
RESP BODY: 404 Not Found The resource could not be found.
#没有路由到该资源(没有Mapper)
××××××××××××××××××××××××××××××××××××××××××××××××# 2. End ××××××××××××××××××××××××××××# 3. GET http://200.21.18.2:8774/v2.1/ 使用 tokens ;验证 Nova v2.1 version 是否为稳定版本
DEBUG (session:198) REQ: curl -g -i -X GET http://200.21.18.2:8774/v2.1/ -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-Auth-Token: {SHA1}ac4acef5a9f5a568af553beb59ff5d99f1928849"
DEBUG (connectionpool:387) "GET /v2.1/ HTTP/1.1" 200 385 DEBUG (session:216) RESP: [200] Content-Length: 385 X-Compute-Request-Id: req-f4056355-11f5-4064-9ac9-5dfe1e722655 Vary: X-OpenStack-Nova-API-Version Connection: keep-alive X-Openstack-Nova-Api-Version: 2.1 Date: Thu, 04 Aug 2016 03:31:34 GMT Content-Type: application/json
RESP BODY: {"version": {"status": "CURRENT", "updated": "2013-07-23T11:33:21Z", "links": [{"href": "http://200.21.18.2:8774/v2.1/", "rel": "self"}, {"href": "http://docs.openstack.org/", "type": "text/html", "rel": "describedby"}], "min_version": "2.1", "version": "2.12", "media-types": [{"base": "application/json", "type": "application/vnd.openstack.compute+json;version=2.1"}], "id": "v2.1"}}
××××××××××××××××××××××××××××××××××××××××××××××××# 3. End ×××××××××××××××××# 4. GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/detail 获取虚拟机的列表详细信息
DEBUG (session:198) REQ: curl -g -i -X GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/detail -H "User-Agent: python-novaclient" -H "Accept: application/json" -H "X-OpenStack-Nova-API-Version: 2.6" -H "X-Auth-Token: {SHA1}ac4acef5a9f5a568af553beb59ff5d99f1928849"
DEBUG (connectionpool:387) "GET /v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/detail HTTP/1.1" 200 1975 DEBUG (session:216) RESP: [200] Content-Length: 1975 X-Compute-Request-Id: req-1089b520-09b5-4c37-b57a-04123d30504f Vary: X-OpenStack-Nova-API-Version Connection: keep-alive X-Openstack-Nova-Api-Version: 2.6 Date: Thu, 04 Aug 2016 03:31:35 GMT Content-Type: application/json
RESP BODY: {"servers": [{"status": "ACTIVE", "OS-EXT-SRV-ATTR:ramdisk_id": "", "updated": "2016-08-02T07:45:56Z", "hostId": "7e4e4bc79676933ebc8359ea7e954050084589d5805a1e50e009de48", "OS-EXT-SRV-ATTR:host": "fanguiju", "addresses": {"private": [{"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:5f:bf:b2", "version": 4, "addr": "10.0.0.2", "OS-EXT-IPS:type": "fixed"}]}, "links": [{"href": "http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/42da5d12-a470-4193-8410-0209c04f333a", "rel": "self"}, {"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/servers/42da5d12-a470-4193-8410-0209c04f333a", "rel": "bookmark"}], "key_name": null, "image": {"id": "f76a9eb1-e976-4d50-9185-e7b256d14c40", "links": [{"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/images/f76a9eb1-e976-4d50-9185-e7b256d14c40", "rel": "bookmark"}]}, "OS-EXT-SRV-ATTR:user_data": null, "OS-EXT-STS:task_state": null, "OS-EXT-STS:vm_state": "active", "OS-EXT-SRV-ATTR:instance_name": "instance-00000001", "OS-EXT-SRV-ATTR:root_device_name": "/dev/vda", "OS-SRV-USG:launched_at": "2016-08-02T07:26:16.000000", "OS-EXT-SRV-ATTR:hypervisor_hostname": "fanguiju", "flavor": {"id": "84", "links": [{"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/flavors/84", "rel": "bookmark"}]}, "id": "42da5d12-a470-4193-8410-0209c04f333a", "security_groups": [{"name": "default"}], "OS-SRV-USG:terminated_at": null, "OS-EXT-SRV-ATTR:kernel_id": "", "OS-EXT-AZ:availability_zone": "nova", "user_id": "135b2cb86962401c82044fd4ca9daae4", "name": "TestVMwareInterface", "OS-EXT-SRV-ATTR:launch_index": 0, "created": "2016-08-02T07:25:49Z", "tenant_id": "6c4e4d58cb9d4451b36e774b348e8813", "OS-DCF:diskConfig": "AUTO", "os-extended-volumes:volumes_attached": [], "accessIPv4": "", "accessIPv6": "", "OS-EXT-SRV-ATTR:reservation_id": "r-kud9zm9w", "OS-EXT-SRV-ATTR:hostname": "testvmwareinterface", "progress": 0, "OS-EXT-STS:power_state": 0, "config_drive": "True", "metadata": {}}]} +--------------------------------------+---------------------+--------+------------+-------------+------------------+
| ID | Name | Status | Task State | Power State | Networks |
+--------------------------------------+---------------------+--------+------------+-------------+------------------+
| 42da5d12-a470-4193-8410-0209c04f333a | TestVMwareInterface | ACTIVE | - | NOSTATE | private=10.0.0.2 |
+--------------------------------------+---------------------+--------+------------+-------------+------------------+
*******************************************# 4. END

NOTE 1: 在使用 Openstack Cammands 的时候一般会发送两次请求:

NOTE 2: 如果这个请求是直接使用 Restful API 发出的,那么我们只需要处理最后一次就可以了。例如上述的第4次请求:GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/detail

而且从上面的 Output 可以看出,我们甚至可以直接使用curl指令来发送HTTP请求。

下面是最后一次 Response 的 JSON 数据( Openstack service 的响应数据以 JSON 数据结构返回),我们可以从中获取一些有用的信息作为 DEBUG 的依据。

{
"servers": [
{
"status": "ACTIVE",
"OS-EXT-SRV-ATTR:ramdisk_id": "",
"updated": "2016-08-02T07:45:56Z",
"hostId": "7e4e4bc79676933ebc8359ea7e954050084589d5805a1e50e009de48",
"OS-EXT-SRV-ATTR:host": "fanguiju",
"addresses": {
"private": [
{
"OS-EXT-IPS-MAC:mac_addr": "fa:16:3e:5f:bf:b2",
"version": 4,
"addr": "10.0.0.2",
"OS-EXT-IPS:type": "fixed"
}
]
},
"links": [
{
"href": "http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/42da5d12-a470-4193-8410-0209c04f333a",
"rel": "self"
},
{
"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/servers/42da5d12-a470-4193-8410-0209c04f333a",
"rel": "bookmark"
}
],
"key_name": null,
"image": {
"id": "f76a9eb1-e976-4d50-9185-e7b256d14c40", #Image ID
"links": [
{
"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/images/f76a9eb1-e976-4d50-9185-e7b256d14c40",
"rel": "bookmark"
}
]
},
"OS-EXT-SRV-ATTR:user_data": null,
"OS-EXT-STS:task_state": null,
"OS-EXT-STS:vm_state": "active",
"OS-EXT-SRV-ATTR:instance_name": "instance-00000001",
"OS-EXT-SRV-ATTR:root_device_name": "/dev/vda",
"OS-SRV-USG:launched_at": "2016-08-02T07:26:16.000000",
"OS-EXT-SRV-ATTR:hypervisor_hostname": "fanguiju",
"flavor": {
"id": "84",
"links": [
{
"href": "http://200.21.18.2:8774/6c4e4d58cb9d4451b36e774b348e8813/flavors/84",
"rel": "bookmark"
}
]
},
"id": "42da5d12-a470-4193-8410-0209c04f333a", # GuestOS ID
"security_groups": [
{
"name": "default"
}
],
"OS-SRV-USG:terminated_at": null,
"OS-EXT-SRV-ATTR:kernel_id": "",
"OS-EXT-AZ:availability_zone": "nova",
"user_id": "135b2cb86962401c82044fd4ca9daae4",
"name": "TestVMwareInterface",
"OS-EXT-SRV-ATTR:launch_index": 0,
"created": "2016-08-02T07:25:49Z",
"tenant_id": "6c4e4d58cb9d4451b36e774b348e8813", #tenant_id 会在 novaclient 的 shell.sh 组装 HTTP URL 的时候被添加进去
"OS-DCF:diskConfig": "AUTO",
"os-extended-volumes:volumes_attached": [],
"accessIPv4": "",
"accessIPv6": "",
"OS-EXT-SRV-ATTR:reservation_id": "r-kud9zm9w",
"OS-EXT-SRV-ATTR:hostname": "testvmwareinterface",
"progress": 0,
"OS-EXT-STS:power_state": 0,
"config_drive": "True",
"metadata": {}
}
]
}

2. PasteDeploy 将 HTTP 请求路由到具体的 WSGI Application

首先,Openstack 在启动 nova-api service 时,会根据 Nova 配置文件 nova.conf 中的配置项 enabled_apis = ec2,osapi_compute,metadata 来创建一个或多个 WSGI Server 。每一个 WSGI Server 负责处理一种类型的 Nova API 请求,上述的选项值表示了三种类型的 Nova API 。而且在创建 WSGI Server 的同时会根据 Paste 的配置文件 nova/etc/nova/api-paste.ini 加载 WSGI Application 。在加载 WSGI Application 之后, WSGI Application 就处在等待和监听请求的状态。加载的动作由nova/nova/service.py实现。

# nova/nova/service.py
class WSGIService(service.Service):
"""Provides ability to launch API from a 'paste' configuration.""" def __init__(self, name, loader=None, use_ssl=False, max_url_len=None):
self.name = name
self.manager = self._get_manager()
self.loader = loader or wsgi.Loader() #从配置文件加载 Nova API 对应的 WSGI Application,由 HTTP 请求的 URL 来决定
self.app = self.loader.load_app(name)
# inherit all compute_api worker counts from osapi_compute
if name.startswith('openstack_compute_api'):
wname = 'osapi_compute'
else:
wname = name
self.host = getattr(CONF, '%s_listen' % name, "0.0.0.0")
self.port = getattr(CONF, '%s_listen_port' % name, 0)
self.workers = (getattr(CONF, '%s_workers' % wname, None) or
processutils.get_worker_count())
if self.workers and self.workers < 1:
worker_name = '%s_workers' % name
msg = (_("%(worker_name)s value of %(workers)s is invalid, "
"must be greater than 0") %
{'worker_name': worker_name,
'workers': str(self.workers)})
raise exception.InvalidInput(msg)
self.use_ssl = use_ssl #使用指定的 HostIP 和 Port 创建用于监听HTTP请求的 Socket,这个Socket只会捕获与 host 和 port 对应的 HTTP 请求。
self.server = wsgi.Server(name,
self.app,
host=self.host,
port=self.port,
use_ssl=self.use_ssl,
max_url_len=max_url_len)
# Pull back actual port used
self.port = self.server.port
self.backdoor_port = None

api-paste.ini 配置文件定义了不同类型的 composite section。 EG. [composite:metadata] / [composite:ec2] / [composite:ec2cloud] / [composite:osapi_compute] / [composite:openstack_compute_api_v21] 等。当 Socket 监听到 HTTP 请求: GET http://200.21.18.2:8774/v2.1/6c4e4d58cb9d4451b36e774b348e8813/servers/detail时,composite section 是该请求进入到 nova-api 后第一个通过的 Section。下面是该请求通过 api-paste.ini 配置映射到一个 WSGI Application 的步骤:

  • Step1. 因为这个请求是 OpenstackAPI 类型,所以会被 [composite:osapi_compute] 监听到,并使用 nova.api.openstack.urlmap 模块下的 urlmap_factory 函数将请求分发到 [composite:openstack_compute_api_v21]
[composite:osapi_compute]
use = call:nova.api.openstack.urlmap:urlmap_factory
/: oscomputeversions
/v1.1: openstack_compute_api_v21_legacy_v2_compatible
/v2: openstack_compute_api_v21_legacy_v2_compatible
/v2.1: openstack_compute_api_v21
  • Step2. [composite:openstack_compute_api_v21] 再使用 nova.api.auth模块下的 pipeline_factory_v21 函数对请求进一步分发。

    而且这里需要根据 nova.conf 配置文件中的认证策略选项 auth_strategy 来确定使用那一种认证方式 (keystone/noauth2),在 nova/api/auth.py 文件定义了默认为认证方式为 keystone 。
[composite:openstack_compute_api_v21]
use = call:nova.api.auth:pipeline_factory_v21
noauth2 = compute_req_id faultwrap sizelimit noauth2 osapi_compute_app_v21
keystone = compute_req_id faultwrap sizelimit authtoken keystonecontext osapi_compute_app_v21
  • Step3. keystone/noauth2 这两个 Pipeline 的参数列表中除了最后一个参数之外的都是 filter section,最后一个参数对应了一个 app section( Application) 。

    等这个 pipeline 里所有的 filter 都执行完了之后,最终再调用 application osapi_compute_app_v21
[app:osapi_compute_app_v21]
paste.app_factory = nova.api.openstack.compute:APIRouterV21.factory

所以说,将 HTTP 请求路由到哪一个 WSGI Application 是由配置文件 api-paste.ini 来决定的。

而且需要注意的是:Openstack Project 拥有着各自不同的 Paste 配置文件,即拥有各自不同的 WSGI Serever 和 WSGI Application 。

3. Routes 将 HTTP 请求路由到具体的操作函数并执行

在 Openstack 中,每个资源都被封装成为一个 nova.api.openstack.wsgi.Resource 对象,且对应着一个 WSGI Application ,这样就保证了资源的独立性。上面,我们已经确定了处理 HTTP 请求的 Application ,除此之外,我们还需要继续的去确定这个 HTTP 请求希望执行 Application 中具体的哪一个操作函数。这需要 Routes Module 的 Mapper 对象来支撑。

Routes Module:主要用于把接收到的 HTTP URL 请求的后缀 Path(EG. /servers/detail)映射到具体的操作函数。其实现原理是创建一个 mapper 对象,然后调用该对象的 connect() 方法把后缀 Path 和 HTTP 内建方法映射到一个 Controller 的某个 Action 上。Controller 是一个自定义的类实例对象,每一个OPenstack 资源都会对应一个 Controller,同时 Controller 也是资源操作函数的集合。Action 表示 Controller 对象提供的操作函数(EG. index/show/delect/update/create )。一般调用这些 Action 之前会把这个 Action 映射到 HTTP 的内置方法。EG. GET/POST/DELETE/PUT 。 HTTP 请求的 URL Path 和 HTTP 内置方法能够确定对 Openstack 中唯一的一个资源执行何种操作。

class Router(object):
"""WSGI middleware that maps incoming requests to WSGI apps.""" def __init__(self, mapper):
"""Create a router for the given routes.Mapper. Each route in `mapper` must specify a 'controller', which is a
WSGI app to call. You'll probably want to specify an 'action' as
well and have your controller be an object that can route
the request to the action-specific method. Examples:
mapper = routes.Mapper()
sc = ServerController() # Explicit mapping of one route to a controller+action
mapper.connect(None, '/svrlist', controller=sc, action='list') # Actions are all implicitly defined
mapper.resource('server', 'servers', controller=sc) # Pointing to an arbitrary WSGI app. You can specify the
# {path_info:.*} parameter so the target app can be handed just that
# section of the URL.
mapper.connect(None, '/v1.0/{path_info:.*}', controller=BlogApp()) """
self.map = mapper
# 使用 routers 模块将 mapper 和 _dispatch 关联起来
# routes.middleware.RoutesMiddleware
self._router = routes.middleware.RoutesMiddleware(self._dispatch,
self.map) @webob.dec.wsgify(RequestClass=Request)
def __call__(self, req):
"""Route the incoming request to a controller based on self.map. If no match, return a 404.
"""
# 根据 mapper 将HTTP请求路由到适当的 WSGI Application,即资源上。再根据 HTTP 请求的 URL 将其路由到对应的 Controller 中的 Action。
return self._router @staticmethod
@webob.dec.wsgify(RequestClass=Request)
def _dispatch(req):
"""Dispatch the request to the appropriate controller. Called by self._router after matching the incoming request to a route
and putting the information into req.environ. Either returns 404
or the routed WSGI app's response. """
# 根据 HTTP 请求的 environ 信息并根据上述设置的 environ 来找到 URL 对应的 Controller 。
match = req.environ['wsgiorg.routing_args'][1]
if not match:
return webob.exc.HTTPNotFound()
app = match['controller']
return app

通过 Route 模块,一个 HTTP 请求 的 URL 能够映射到对应的资源的 Action(对资源操作的方法) 。

openstack nova 源码解析 — Nova API 执行过程从(novaclient到Action)的更多相关文章

  1. 【OpenStack】OpenStack系列13之Nova源码解析与API扩展

    学习思路 议程:代码结构-主干流程-分层架构-业务模型-数据库模型-消息模型 分布式架构:Api:横向扩展    rpc:纵向扩展 分层架构:Controller接口层.View/Manager逻辑层 ...

  2. [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...

  3. Mybatis 系列10-结合源码解析mybatis 的执行流程

    [Mybatis 系列10-结合源码解析mybatis 执行流程] [Mybatis 系列9-强大的动态sql 语句] [Mybatis 系列8-结合源码解析select.resultMap的用法] ...

  4. Fabric1.4源码解析:链码实例化过程

    之前说完了链码的安装过程,接下来说一下链码的实例化过程好了,再然后是链码的调用过程.其实这几个过程内容已经很相似了,都是涉及到Proposal,不过整体流程还是要说一下的. 同样,切入点仍然是fabr ...

  5. [Java并发] AQS抽象队列同步器源码解析--独占锁释放过程

    [Java并发] AQS抽象队列同步器源码解析--独占锁获取过程 上一篇已经讲解了AQS独占锁的获取过程,接下来就是对AQS独占锁的释放过程进行详细的分析说明,废话不多说,直接进入正文... 锁释放入 ...

  6. 解析jQuery中extend方法--源码解析以及递归的过程《二》

    源码解析 在解析代码之前,首先要了解extend函数要解决什么问题,以及传入不同的参数,会达到怎样的效果.extend函数内部处理传入的不同参数,返回处理后的对象. extend函数用来扩展对象,增加 ...

  7. Openstack Nova 源码分析 — RPC 远程调用过程

    目录 目录 Nova Project Services Project 的程序入口 setuppy Nova中RPC远程过程调用 nova-compute RPC API的实现 novacompute ...

  8. Flask源码解析:Flask应用执行流程及原理

    WSGI WSGI:全称是Web Server Gateway Interface,WSGI不是服务器,python模块,框架,API或者任何软件,只是一种规范,描述服务器端如何与web应用程序通信的 ...

  9. 【Spring源码解析】—— 结合SpringMVC过程理解IOC容器初始化

    关于IOC容器的初始化,结合之前SpringMVC的demo,对其过程进行一个相对详细的梳理,主要分为几个部分: 一.IOC的初始化过程,结合代码和debug过程重点说明 1. 为什么要debug? ...

随机推荐

  1. 视觉里程计:2D-2D 对极几何、3D-2D PnP、3D-3D ICP

    参考链接:https://mp.weixin.qq.com/s/89IHjqnw-JJ1Ak_YjWdHvA #include <iostream> #include <opencv ...

  2. DELPHI中枚举类型数据的介绍和使用方法

    在看delphi程序的时候看到aa=(a,b,c,d);这样的东西,还以为是数组,同事说是函数,呵呵,当然这两个都不屑一击,原来这样式子是在声明并付值一个枚举类型的数据.下边写下来DELPHI中枚举类 ...

  3. Likecloud-吃、吃、吃

    题目背景 问世间,青春期为何物? 答曰:"甲亢,甲亢,再甲亢:挨饿,挨饿,再挨饿!" 题目描述 正处在某一特定时期之中的李大水牛由于消化系统比较发达,最近一直处在饥饿的状态中.某日 ...

  4. NX二次开发CreateDialog函数在UI.hxx文件和WinUser.h中的冲突【转载】

    文章出自https://blog.csdn.net/qq_41843732/article/details/91422764 在UG二次开发中,若使用MFC库,一旦加上#include<Afx. ...

  5. [HNOI2015]菜肴制作 题解(贪心+拓扑)

    Description 知名美食家小 A被邀请至ATM 大酒店,为其品评菜肴. ATM 酒店为小 A 准备了 N 道菜肴,酒店按照为菜肴预估的质量从高到低给予 1到N的顺序编号,预估质量最高的菜肴编号 ...

  6. nteract 使用教程

    安装 直接去官网下载 一路回车 官网 建立python虚拟环境 和我们平时一样 不同的是在建立完之后 要安装一个kernel Using Python3 with pip and a virtual ...

  7. f-li.cn

    package org.rx.service.command.impl; import lombok.Getter; import lombok.Setter; import org.rx.core. ...

  8. node.js是用来做什么的

    Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高效.(事件驱动:事件触发过程中,进行决策的一种策略,简单说就是跟随当前时间点上出现的事物,调用可用的资源进行解决该事物,使得不 ...

  9. VIM查找空格

    匹配1到多个空格 /\s\+ 或者开启very magic模式 /\v\s+

  10. C#编程入门--MYSQLHELPER

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...