cinderclient命令行源码解析
一、简介
openstack的各个模块中,都有相应的客户端模块实现,其作用是为用户访问具体模块提供了接口,并且也作为模块之间相互访问的途径。Cinder也一样,有着自己的cinder-client。
二、argparse简单介绍
argparse是python用于解析命令行参数和选项的标准模块,作为optparse的一个替代被添加到Python2.7。Cinder-client主要就是调用了argparse这个工具包。
使用步骤:
import argparse
parser = argparse.ArgumentParser()
parser.add_argument()
parser.parse_args()
首先导入该模块;然后创建一个解析对象;然后向该对象中添加你要关注的命令行参数和选项,每一个add_argument方法对应一个你要关注的参数或选项;最后调用parse_args()方法进行解析;解析成功之后即可使用。
方法 ArgumentParser(prog=None, usage=None,description=None, epilog=None, parents=[],formatter_class=argparse.HelpFormatter, prefix_chars='-',fromfile_prefix_chars=None, argument_default=None,conflict_handler='error', add_help=True)
这些参数都有默认值,当调用 parser.print_help()或者运行程序时,由于参数不正确(此时python解释器其实也是调用了pring_help()方法)时,会打印这些描述信息,一般只需要传递description参数
方法add_argument(name or flags...[, action][, nargs][, const][, default][, type][, choices][, required][, help][, metavar][, dest])
其中:
name or flags:命令行参数名或者选项,如上面的address或者-p,--port.其中命令行参数如果没给定,且没有设置defualt,则出错。但是如果是选项的话,则设置为None。,parse_args()运行时,会用'-'来认证可选参数,剩下的即为位置参数
nargs:命令行参数的个数,一般使用通配符表示,其中,'?'表示只用一个,'*'表示0到多个,'+'表示至少一个。nargs='*' 表示参数可设置零个或多个;nargs=' '+' 表示参数可设置一个或多个;nargs='?'表示参数可设置零个或一个
default:默认值。
type:参数的类型,默认是字符串string类型,还有float、int等类型。
help:和ArgumentParser方法中的参数作用相似,出现的场合也一致。
dest:如果提供dest,例如dest="a",那么可以通过args.a访问该参数
action:参数出发的动作
store:保存参数,默认
store_const:保存一个被定义为参数规格一部分的值(常量),而不是一个来自参数解析而来的值。
store_ture/store_false:保存相应的布尔值
append:将值保存在一个列表中。
append_const:将一个定义在参数规格中的值(常量)保存在一个列表中。
count:参数出现的次数
parser.add_argument("-v", "--verbosity", action="count", default=0, help="increase output verbosity")
version:打印程序版本信息
choice:允许的参数值
三、cinderclient代码入口查找
- 第一种方式:
- D:\官网代码\python-cinderclient-stable-pike\setup.cfg
- [entry_points]
- console_scripts =
- cinder = cinderclient.shell:main
- 第二种方式:
- [root@test bin]# pwd
- /usr/bin
- [root@test bin]# ls |grep cinder
- cinder
- cinder-all
- cinder-api
- cinder-backup
- cinder-manage
- cinder-rootwrap
- cinder-rtstool
- cinder-scheduler
- cinder-volume
- cinder-volume-usage-audit
- [root@test bin]# cat cinder
- #!/usr/bin/python
- # PBR Generated from u'console_scripts'
- import sys
- from cinderclient.shell import main
- if __name__ == "__main__":
- sys.exit(main())
- [root@test bin]#
四、cinderclient代码分析
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\shell.py
- def main():
- try:
- if sys.version_info >= (3, 0):-----sys.version获取python的版本,默认情况下, 使用系统自带的python版本,python2.6或者python 2.7
- """
- >>> print sys.version_info
- (2, 6, 6, 'final', 0)
- >>>
- """
- OpenStackCinderShell().main(sys.argv[1:])---sys.argv[1:],输入的cinder命令行,sys.argv[0]表示程序本身,sys.argv[1:]表示 输入的参数
- else:
- OpenStackCinderShell().main([encodeutils.safe_decode(item)----走如下分支,步骤一
- for item in sys.argv[1:]])
- except KeyboardInterrupt:
- print("... terminating cinder client", file=sys.stderr)
- sys.exit(130)
- except Exception as e:
- logger.debug(e, exc_info=1)
- print("ERROR: %s" % six.text_type(e), file=sys.stderr)
- sys.exit(1)
对步骤一进行详解
- from cinderclient import api_versions
- from cinderclient import client
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\shell.py
- class OpenStackCinderShell(object):
- def __init__(self):
- self.ks_logger = None
- self.client_logger = None
- def main(self, argv):
- # Parse args once to find version and debug settings
- 解析args参数一次,查找version和debug设置信息
- parser = self.get_base_parser()
- """
- get_base_parser:获取基本的命令行解析器;调用add_argument方法实现添加具体命令行参数;
- 构造参数解析类ArgumentParser的实例parser,然后通过实例调用方法parser.add_argument增加一些固有的参数,比如:--debug,--help,
- --os_auth_type等参数
- """
- (options, args) = parser.parse_known_args(argv)
- """
- parse_known_args()方法的作用就是当仅获取到基本设置时,如果运行命令中传入了之后才会获取到的其他配置,不会报错;
- 而是将多出来的部分保存起来,留到后面使用,解析的参数按属性的方式存储到Namespace对象;
- options的值为命名空间namespace的对象
- """
- self.setup_debugging(options.debug)----打开debug信息
- api_version_input = True
- self.options = options
- do_help = ('help' in argv) or (-----查看是不是需要对命令行进行help查询
- '--help' in argv) or ('-h' in argv) or not argv
- #确定使用API的版本,默认情况下,是版本3
- if not options.os_volume_api_version:
- api_version = api_versions.get_api_version(
- DEFAULT_MAJOR_OS_VOLUME_API_VERSION)
- else:
- api_version = api_versions.get_api_version(
- options.os_volume_api_version)
- # build available subcommands based on version
- #根据api版本号,去查找其对应的版本的扩展版本,其实本质上就是获取
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\contrib\list_extensions.py模块中的类
- major_version_string = "%s" % api_version.ver_major
- self.extensions = client.discover_extensions(major_version_string)
- self._run_extension_hooks('__pre_parse_args__')
- #基于版本api版本,创建对应的子命令解释器,同时根据对应的api_version版本,加载
- D:\官网代码\python-cinderclient-stable-pike\cinderclient不同版本的shell.py文件
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\shell.py模块
- subcommand_parser = self.get_subcommand_parser(api_version,
- do_help, args)
- self.parser = subcommand_parser
- if options.help or not argv:---如果命令行后面跟的是help命令,那么就打印该命令的help信息,直接返回
- subcommand_parser.print_help()
- return 0
- argv = self._delimit_metadata_args(argv)
- # 命令行参数的解析;
- args = subcommand_parser.parse_args(argv)
- self._run_extension_hooks('__post_parse_args__', args)
- # Short-circuit and deal with help right away.
- if args.func == self.do_help:
- self.do_help(args)
- return 0
- elif args.func == self.do_bash_completion:
- self.do_bash_completion(args)
- return 0
- #提取命令行参数中的基本的租户等信息存放到一个元祖里,为后面方法的调用做具体参数的准备
- (os_username, os_password, os_tenant_name, os_auth_url,
- os_region_name, os_tenant_id, endpoint_type,
- service_type, service_name, volume_service_name, os_endpoint,
- cacert, os_auth_type) = (
- args.os_username, args.os_password,
- args.os_tenant_name, args.os_auth_url,
- args.os_region_name, args.os_tenant_id,
- args.os_endpoint_type,
- args.service_type, args.service_name,
- args.volume_service_name,
- args.os_endpoint, args.os_cacert,
- args.os_auth_type)
- auth_session = None
- #对参数的认证权限的一些处理,比如是否提供租户、是否提供密码等
- if os_auth_type and os_auth_type != "keystone":
- auth_plugin = loading.load_auth_from_argparse_arguments(
- self.options)
- auth_session = loading.load_session_from_argparse_arguments(
- self.options, auth=auth_plugin)
- else:
- auth_plugin = None
- if not service_type:
- service_type = client.SERVICE_TYPES[major_version_string]
- # FIXME(usrleon): Here should be restrict for project id same as
- # for os_username or os_password but for compatibility it is not.
- # V3 stuff
- project_info_provided = ((self.options.os_tenant_name or
- self.options.os_tenant_id) or
- (self.options.os_project_name and
- (self.options.os_project_domain_name or
- self.options.os_project_domain_id)) or
- self.options.os_project_id)
- # NOTE(e0ne): if auth_session exists it means auth plugin created
- # session and we don't need to check for password and other
- # authentification-related things.
- if not utils.isunauthenticated(args.func) and not auth_session:
- if not os_password:
- # No password, If we've got a tty, try prompting for it
- if hasattr(sys.stdin, 'isatty') and sys.stdin.isatty():
- # Check for Ctl-D
- try:
- os_password = getpass.getpass('OS Password: ')
- # Initialize options.os_password with password
- # input from tty. It is used in _get_keystone_session.
- options.os_password = os_password
- except EOFError:
- pass
- # No password because we didn't have a tty or the
- # user Ctl-D when prompted.
- if not os_password:
- raise exc.CommandError("You must provide a password "
- "through --os-password, "
- "env[OS_PASSWORD] "
- "or, prompted response.")
- if not project_info_provided:
- raise exc.CommandError(_(
- "You must provide a tenant_name, tenant_id, "
- "project_id or project_name (with "
- "project_domain_name or project_domain_id) via "
- " --os-tenant-name (env[OS_TENANT_NAME]),"
- " --os-tenant-id (env[OS_TENANT_ID]),"
- " --os-project-id (env[OS_PROJECT_ID])"
- " --os-project-name (env[OS_PROJECT_NAME]),"
- " --os-project-domain-id "
- "(env[OS_PROJECT_DOMAIN_ID])"
- " --os-project-domain-name "
- "(env[OS_PROJECT_DOMAIN_NAME])"
- ))
- if not os_auth_url:
- raise exc.CommandError(
- "You must provide an authentication URL "
- "through --os-auth-url or env[OS_AUTH_URL].")
- if not project_info_provided:
- raise exc.CommandError(_(
- "You must provide a tenant_name, tenant_id, "
- "project_id or project_name (with "
- "project_domain_name or project_domain_id) via "
- " --os-tenant-name (env[OS_TENANT_NAME]),"
- " --os-tenant-id (env[OS_TENANT_ID]),"
- " --os-project-id (env[OS_PROJECT_ID])"
- " --os-project-name (env[OS_PROJECT_NAME]),"
- " --os-project-domain-id "
- "(env[OS_PROJECT_DOMAIN_ID])"
- " --os-project-domain-name "
- "(env[OS_PROJECT_DOMAIN_NAME])"
- ))
- if not os_auth_url and not auth_plugin:
- raise exc.CommandError(
- "You must provide an authentication URL "
- "through --os-auth-url or env[OS_AUTH_URL].")
- #没有提供认证会话的,那么与keystone建立认证会话
- if not auth_session:
- auth_session = self._get_keystone_session()
- insecure = self.options.insecure
- self.cs = client.Client(---------------步骤二,本质上是一个http请求
- api_version, os_username,
- os_password, os_tenant_name, os_auth_url,
- region_name=os_region_name,
- tenant_id=os_tenant_id,
- endpoint_type=endpoint_type,
- extensions=self.extensions,
- service_type=service_type,
- service_name=service_name,
- volume_service_name=volume_service_name,
- bypass_url=os_endpoint,
- retries=options.retries,
- http_log_debug=args.debug,
- insecure=insecure,
- cacert=cacert, auth_system=os_auth_type,
- auth_plugin=auth_plugin,
- session=auth_session,
- logger=self.ks_logger if auth_session else self.client_logger)
- try:
- # 如果所要调用的方法没有标志为unauthenticated,则需要进行身份验证操作;
- if not utils.isunauthenticated(args.func):
- self.cs.authenticate()
- except exc.Unauthorized:
- raise exc.CommandError("OpenStack credentials are not valid.")
- except exc.AuthorizationFailure:
- raise exc.CommandError("Unable to authorize user.")
- endpoint_api_version = None
- # Try to get the API version from the endpoint URL. If that fails fall
- # back to trying to use what the user specified via
- # --os-volume-api-version or with the OS_VOLUME_API_VERSION environment
- # variable. Fail safe is to use the default API setting.
- try:
- endpoint_api_version = \
- self.cs.get_volume_api_version_from_endpoint()
- except exc.UnsupportedVersion:
- endpoint_api_version = options.os_volume_api_version
- if api_version_input:
- logger.warning("Cannot determine the API version from "
- "the endpoint URL. Falling back to the "
- "user-specified version: %s",
- endpoint_api_version)
- else:
- logger.warning("Cannot determine the API version from the "
- "endpoint URL or user input. Falling back "
- "to the default API version: %s",
- endpoint_api_version)
- profile = osprofiler_profiler and options.profile
- if profile:
- osprofiler_profiler.init(options.profile)
- try:
- args.func(self.cs, args)----实现根据解析的命令行参数调用具体的方法,假如使用的命令行为cinder list,该处args.func = do_list,
说明这里调用的具体方法是do_list;- finally:
- if profile:
- trace_id = osprofiler_profiler.get().get_base_id()
- print("Trace ID: %s" % trace_id)
- print("To display trace use next command:\n"
- "osprofiler trace show --html %s " % trace_id)
对步骤二详解
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\client.py
- def Client(version, *args, **kwargs):
- """Initialize client object based on given version."""
- api_version, client_class = _get_client_class_and_version(version)----对步骤2.1 详解根据api版本version版本号,获取对应目录下的Client函数
- return client_class(api_version=api_version,*args, **kwargs)-------对步骤2.2的详解
- 对步骤2.1详解
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\client.py
- def _get_client_class_and_version(version):
- if not isinstance(version, api_versions.APIVersion):
- version = api_versions.get_api_version(version)
- else:
- api_versions.check_major_version(version)
- if version.is_latest():
- raise exceptions.UnsupportedVersion(
- _("The version should be explicit, not latest."))
- return version, importutils.import_class(
- "cinderclient.v%s.client.Client" % version.ver_major)
- 对步骤2.2 的详解---假如使用的版本为v2版本
- D:\官网代码\python-cinderclient-stable-pike\cinderclient\v2\client.py
- from cinderclient import client
- from cinderclient import api_versions
- from cinderclient.v2 import availability_zones
- from cinderclient.v2 import cgsnapshots
- from cinderclient.v2 import consistencygroups
- from cinderclient.v2 import capabilities
- from cinderclient.v2 import limits
- from cinderclient.v2 import pools
- from cinderclient.v2 import qos_specs
- from cinderclient.v2 import quota_classes
- from cinderclient.v2 import quotas
- from cinderclient.v2 import services
- from cinderclient.v2 import volumes
- from cinderclient.v2 import volume_snapshots
- from cinderclient.v2 import volume_types
- from cinderclient.v2 import volume_type_access
- from cinderclient.v2 import volume_encryption_types
- from cinderclient.v2 import volume_backups
- from cinderclient.v2 import volume_backups_restore
- from cinderclient.v2 import volume_transfers
- class Client(object):
- def __init__(self, username=None, api_key=None, project_id=None,
- auth_url='', insecure=False, timeout=None, tenant_id=None,
- proxy_tenant_id=None, proxy_token=None, region_name=None,
- endpoint_type='publicURL', extensions=None,
- service_type='volumev2', service_name=None,
- volume_service_name=None, bypass_url=None, retries=0,
- http_log_debug=False, cacert=None, auth_system='keystone',
- auth_plugin=None, session=None, api_version=None,
- logger=None, **kwargs):
- # FIXME(comstud): Rename the api_key argument above when we
- # know it's not being used as keyword argument
- password = api_key
- self.version = '2.0'
- self.limits = limits.LimitsManager(self)
- # extensions------引入统一目录下,不同资源的管理类
- self.volumes = volumes.VolumeManager(self)
- self.volume_snapshots = volume_snapshots.SnapshotManager(self)
- self.volume_types = volume_types.VolumeTypeManager(self)
- self.volume_type_access = \
- volume_type_access.VolumeTypeAccessManager(self)
- self.volume_encryption_types = \
- volume_encryption_types.VolumeEncryptionTypeManager(self)
- self.qos_specs = qos_specs.QoSSpecsManager(self)
- self.quota_classes = quota_classes.QuotaClassSetManager(self)
- self.quotas = quotas.QuotaSetManager(self)
- self.backups = volume_backups.VolumeBackupManager(self)
- self.restores = volume_backups_restore.VolumeBackupRestoreManager(self)
- self.transfers = volume_transfers.VolumeTransferManager(self)
- self.services = services.ServiceManager(self)
- self.consistencygroups = consistencygroups.\
- ConsistencygroupManager(self)
- self.cgsnapshots = cgsnapshots.CgsnapshotManager(self)
- self.availability_zones = \
- availability_zones.AvailabilityZoneManager(self)
- self.pools = pools.PoolManager(self)
- self.capabilities = capabilities.CapabilitiesManager(self)
- self.api_version = api_version or api_versions.APIVersion(self.version)
- # Add in any extensions...
- if extensions:
- for extension in extensions:
- if extension.manager_class:
- setattr(self, extension.name,
- extension.manager_class(self))
- if not logger:
- logger = logging.getLogger(__name__)
- self.client = client._construct_http_client(----本质上就是调用http模块的,建立session连接,同时拼接url的请求头,请求体
- username=username,
- password=password,
- project_id=project_id,
- auth_url=auth_url,
- insecure=insecure,
- timeout=timeout,
- tenant_id=tenant_id,
- proxy_tenant_id=tenant_id,
- proxy_token=proxy_token,
- region_name=region_name,
- endpoint_type=endpoint_type,
- service_type=service_type,
- service_name=service_name,
- volume_service_name=volume_service_name,
- bypass_url=bypass_url,
- retries=retries,
- http_log_debug=http_log_debug,
- cacert=cacert,
- auth_system=auth_system,
- auth_plugin=auth_plugin,
- session=session,
- api_version=self.api_version,
- logger=logger,
- **kwargs)
- def authenticate(self):
- """Authenticate against the server.
- Normally this is called automatically when you first access the API,
- but you can call this method to force authentication right now.
- Returns on success; raises :exc:`exceptions.Unauthorized` if the
- credentials are wrong.
- """
- self.client.authenticate()
- def get_volume_api_version_from_endpoint(self):
- return self.client.get_volume_api_version_from_endpoint()
五、添加一个新的命令行,这个命令行的功能为获取卷的连接信息,
1)命令行设计的样式如下所示:
- cinder create-target volume_id hostname ip initiator
- usage: cinder create [--platform <platform>]
- [--do_local_attach <do_local_attach>]
- [--os_type <os_type>]
- [--multipath <multipath>]
- [<volume_id>]
- [<host_ip >]
- [<host_name>]
- [<initiator>]
- Creates a volume target.
- Positional arguments:
- <volume_id> volume uuid
- <host_ip > host ip ,which attach volume
- <host_name> host name ,which attach volume
- <initiator> host iscsi client,which attach volume
- Optional arguments:
- --platform <platform> host architecture ,which attach volume.Default=x86_64
- --do_local_attach <do_local_attach> if or not attach volume.Default=None
- --os_type <os_type> host operation system,whiic attach volume.Default=linux2
- --multipath <multipath> if or not iscsi multipath.Default=None
2)代码实现,在v2版本的shell.py文件中新增一个do_create_target函数
- D:\官网代码\python-cinderclient-stable-ocata\cinderclient\v2\shell.py
- @utils.arg('volume',
- metavar='<volume_id>',
- help='volume uuid')
- @utils.arg('host_ip',
- metavar='<host_ip>',
- help='host ip ,which attach volume')
- @utils.arg('host_name',
- metavar='<host_name>',
- help='host name ,which attach volume')
- @utils.arg('initiator',
- metavar='<initiator>',
- help='host iscsi client,which attach volume')
- @utils.arg('--platform',
- metavar='<platform>',
- default='x86_64',
- help='host architecture ,which attach volume.Default=x86_64')
- @utils.arg('--do_local_attach',
- metavar='<do_local_attach>',
- default='false',
- help='if or not attach volume.Default=false')
- @utils.arg('--os_type',
- metavar='<os_type>',
- default='linux2',
- help='host operation system,whiic attach volume.Default=linux2')
- @utils.arg('--multipath',
- metavar='<multipath>',
- default='false',
- help='if or not iscsi multipath.Default=false')
- def do_create_target(cs,args):
- """create volume iscsi target."""
- volume_id=args.volume
- connector={}
- connector['ip']=args.host_ip
- connector['host']=args.host_name
- connector['initiator']=args.initiator
- connector['platform']=args.platform
- connector['do_local_attach']=args.do_local_attach
- connector['os_type']=args.os_type
- connector['multipath']=args.multipath
- info=cs.volumes.initialize_connection(volume_id,connector)
- utils.print_dict(info)
输出内容如下:
- [root@test ~]# cinder help create-target
- usage: cinder create-target [--platform <platform>]
- [--do_local_attach <do_local_attach>]
- [--os_type <os_type>] [--multipath <multipath>]
- <volume_id> <host_ip> <host_name> <initiator>
- create volume iscsi target.
- Positional arguments:
- <volume_id> volume uuid
- <host_ip> host ip ,which attach volume
- <host_name> host name ,which attach volume
- <initiator> host iscsi client,which attach volume
- Optional arguments:
- --platform <platform>
- host architecture ,which attach volume.Default=x86_64
- --do_local_attach <do_local_attach>
- if or not attach volume.Default=false
- --os_type <os_type> host operation system,whiic attach
- volume.Default=linux2
- --multipath <multipath>
- if or not iscsi multipath.Default=false
- [root@test ~]#
3)验证命令行
- [root@test ~]# cinder --debug create-target a0ac29de-3a16-4b07-9aac-de63ccdf8fda 10.27.244.149 my03n010027244149.sncloud.com \
iqn.1994-05.com.redhat:7329936b16d9- DEBUG:keystoneauth:REQ: curl -g -i -X POST http://10.27.241.34:8776/v2/d432ed8741cc427da398e4239f44deb4/volumes/a0ac29de-3a16-4b07-9aac-de63ccdf8fda/action
- -H "User-Agent: python-cinderclient"
- -H "Content-Type: application/json"
- -H "Accept: application/json"
- -H "X-Auth-Token: {SHA1}10e60c88cab7620ad3864cb1110d2b9c64a8170f"
- -d
- {
- "os-initialize_connection": {
- "connector": {
- "initiator": "iqn.1994-05.com.redhat:7329936b16d9",
- "ip": "10.27.244.149",
- "platform": "x86_64",
- "host": "my03n010027244149.sncloud.com",
- "do_local_attach": "false",
- "os_type": "linux2",
- "multipath": "false"
- }
- }
- }
- RESP BODY:
- {
- "connection_info": {
- "driver_volume_type": "iscsi",
- "data": {
- "target_luns": [0],
- "target_iqns": ["iqn.2010-10.org.openstack:volume-a0ac29de-3a16-4b07-9aac-de63ccdf8fda"],
- "auth_password": "24do7AqfnLDZ5DyB",
- "target_discovered": false,
- "encrypted": false,
- "qos_specs": null,
- "target_iqn": "iqn.2010-10.org.openstack:volume-a0ac29de-3a16-4b07-9aac-de63ccdf8fda",
- "target_portal": "10.27.244.144:3260",
- "volume_id": "a0ac29de-3a16-4b07-9aac-de63ccdf8fda",
- "target_lun": 0,
- "access_mode": "rw",
- "auth_username": "2y35wC68BsvU8M37tWCn",
- "auth_method": "CHAP",
- "target_portals": ["10.27.244.144:3260"]
- }
- }
- }
cinderclient命令行源码解析的更多相关文章
- 在远程登陆的主机上通过命令行源码编译安装 GNU M4、autoconf、automake 等程序
由于实验需要,最近获得了一个实验室服务器的账号,平常主要通过 ssh 进行远程登陆进行实验.一方面,远程登录的机器只提供终端界面,一般只通过命令行进行任务操作:另一方面,由于是多人共享服务器,故而个人 ...
- Redis系列(九):数据结构Hash源码解析和HSET、HGET命令
2.源码解析 1.相关命令如下: {"hset",hsetCommand,,"wmF",,NULL,,,,,}, {"hsetnx",hse ...
- Redis系列(十):数据结构Set源码解析和SADD、SINTER、SDIFF、SUNION、SPOP命令
1.介绍 Hash是以K->V形式存储,而Set则是K存储,空间节省了很多 Redis中Set是String类型的无序集合:集合成员是唯一的. 这就意味着集合中不能出现重复的数据.可根据应用场景 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- jQuery2.x源码解析(构建篇)
jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...
- 实战录 | Kafka-0.10 Consumer源码解析
<实战录>导语 前方高能!请注意本期攻城狮幽默细胞爆表,坐地铁的拉好把手,喝水的就建议暂时先别喝了:)本期分享人为云端卫士大数据工程师韩宝君,将带来Kafka-0.10 Consumer源 ...
- QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数
QT源码解析(一) QT创建窗口程序.消息循环和WinMain函数 分类: QT2009-10-28 13:33 17695人阅读 评论(13) 收藏 举报 qtapplicationwindowse ...
- Celery 源码解析五: 远程控制管理
今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...
- junit源码解析--初始化阶段
OK,我们接着上篇整理.上篇博客中已经列出的junit的几个核心的类,这里我们开始整理junit完整的生命周期. JUnit 的完整生命周期分为 3 个阶段:初始化阶段.运行阶段和结果捕捉阶段. 这篇 ...
随机推荐
- 对‘sqrt’未定义的引用
首先, 引用数学库 #include<math.h> 引用数学库时,要在编译后加上-lm 是每一个都要加!! 如下: gcc su.c -o su.o -lm gcc -g su.c - ...
- FGPA_Microblaze UART 中断
由于底层所给函数发送与接收都采用中断,所用库函数比较复杂 ,有些更改涉及底层函数,因此结合网上论坛 .百度文库调试了串口中断接收程序.通过串口调试助手发送数据 ,以“发送新行”结束 . 硬件外设波特兰 ...
- 不想得手指关节炎?帮你提炼IDEA常用代码补全操作
一.常用的代码补全操作 1..for和.fori(for 循环遍历) 输入args.for回车(args是一个数组或集合类),则会生成for循环遍历: 输入args.fori回车,则会生成带有索引的f ...
- 11-21 logging 模块
默认情况下Python的logging模块将日志打印到了标准输出中,且只显示了大于等于WARNING级别的日志, 这说明默认的日志级别设置为WARNING(日志级别等级CRITICAL > ER ...
- PHP strip_whitespace() 函数
实例 返回已删除 PHP 注释以及空白字符的 "test.php" 文件的源代码: <?php// PHP comment /** Another PHP comment*/ ...
- PDOStatement::setAttribute
PDOStatement::setAttribute — 设置一个语句属性(PHP 5 >= 5.1.0, PECL pdo >= 0.2.0)高佣联盟 www.cgewang.com 说 ...
- 剑指 Offer 58 - II. 左旋转字符串
本题 题目链接 题目描述 我的题解 方法一:使用库函数 s.substring() 代码如下 public String reverseLeftWords(String s, int n) { ret ...
- python4.3内置函数
常见的内置函数 a=[12,31,31,232,34,32,43,54,36]max1=max(a)#最大函数print(max1)min1=min(a)#最小函数print(min1)sum1=su ...
- JDK11.0.7下载及安装详细教程(win10)
0.背景知识 JRE: Java Runtime Environment JDK:Java Development Kit JRE顾名思义是java运行时环境,包含了java虚拟机,java基础类库. ...
- GitLab Admin Area 500 Error
GitLab Admin Area 500 Error GitLab Admin Area Settings 菜单全部报错 500 解决方法 执行: gitlab-rake cache:clear # ...