目录

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. APICloud框架--sublime使用自定义loader

    官方的apploader调试器,只是有官方的一些模块,如果我们使用非官方的模块就要使用自定义loader进行调试.接下来就走一边sublime设置自定义loader的步骤 修改config.xml 打 ...

  2. lnmp mysql高负载优化

    mysql负载会造成cpu占用高的问题如果没启用innodb的话 用这个配置/usr/local/mysql/share/mysql/my-large.cnf 替换/etc/my.cnf 也可参考如下 ...

  3. Intel Pin基础

    参考:http://software.intel.com/sites/landingpage/pintool/docs/62732/Pin/html/ http://blog.nruns.com/bl ...

  4. 你真的懂return吗?

    递归算法中什么时候用return啥时候不用呢? 使用2个例子来说明:快速排序和二分查找 # 二分查找def binarySearch (arr, l, r, x): # 基本判断 if r >= ...

  5. Python匿名函数(lambda函数)

    匿名函数 -- 一行函数 lambda -- 关键字 x是普通函数的形参(位置,关键字...)可以不接收参数(x可以不写) :x是普通函数的函数值(只能返回一个数据类型)(:x返回值必须写) 1)此函 ...

  6. D-Ubuntu中修改MySQL的默认数据集(client和server)

    Ubuntu16.04,MySQL5.7 1, sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf 使用vim编辑MySQL的配置文件,不同版本的MySQL配置文件 ...

  7. nuxt 利用lru-cache 做服务器数据请求缓存

    // 运行与服务端的js // node.js lru-cache import LRU from 'lru-cache' const lruCache = LRU({ // 缓存队列长度 max: ...

  8. linux每日命令(3):which命令

    这个命令我也神佑体会它的用处,在linux要查找某个文件,但不知道放在哪里了,可以使用下面的一些命令来搜索: which     查看可执行文件的位置. whereis 查看文件的位置. locate ...

  9. what codes does sudo command do in Linux?

    sometime, to make your change of configuration file be effective to web application, we have to rest ...

  10. xpdf -Portable Document Format(PDF)文件阅读器

    总览 xpdf [选项] [PDF文件 [page]] 描述 Xpdf是一个 Portable Document Format(PDF) 文件阅读软件.(PDF文件也经常被称为Acrobat 文件,这 ...