一、cinder-api服务入口

D:\code-program\cinder-codejuno\api\contrib\admin_actions.py

  1. from cinder import volume
  2. class VolumeAdminController(AdminController):
  3. """AdminController for Volumes."""
  4. @wsgi.action('os-migrate_volume')
  5. def _migrate_volume(self, req, id, body):
  6. """Migrate a volume to the specified host."""
  7. context = req.environ['cinder.context']
  8. self.authorize(context, 'migrate_volume')
  9. try:
  10. volume = self._get(context, id)--------根据volume id获取卷对象
  11. except exception.NotFound:
  12. raise exc.HTTPNotFound()
  13. params = body['os-migrate_volume']-----获取request请求体中参数
  14. try:
  15. host = params['host']-------卷要迁移到的主机
  16. except KeyError:
  17. raise exc.HTTPBadRequest(explanation=_("Must specify 'host'"))
  18. force_host_copy = params.get('force_host_copy', False)------从请求体中,获取force_host_copy的值,默认情况下,force_host_copyFalse,
  19. if isinstance(force_host_copy, basestring):
  20. try:
  21. force_host_copy = strutils.bool_from_string(force_host_copy,-----字符串类型转化为bool类型
  22. strict=True)
  23. except ValueError:
  24. raise exc.HTTPBadRequest(
  25. explanation=_("Bad value for 'force_host_copy'"))
  26. elif not isinstance(force_host_copy, bool):
  27. raise exc.HTTPBadRequest(
  28. explanation=_("'force_host_copy' not string or bool"))
  29. self.volume_api.migrate_volume(context, volume, host, force_host_copy)----调用volume.API类里面的方法 步骤一
  30. return webob.Response(status_int=202)

对步骤一进行详解
D:\code-program\cinder-codejuno\volume\api.py

  1. class API(base.Base):
  2. """API for interacting with the volume manager."""
  3.  
  4. @wrap_check_policy
  5. def migrate_volume(self, context, volume, host, force_host_copy):
  6. """Migrate the volume to the specified host."""
  7.  
  8. # We only handle "available" volumes for now
  9. if volume['status'] not in ['available', 'in-use']:------这卷的状态进行判断,只有available', 'in-use这两种状态有效
  10. msg = _('Volume status must be available/in-use.')
  11. LOG.error(msg)
  12. raise exception.InvalidVolume(reason=msg)
  13.  
  14. # Make sure volume is not part of a migration
  15. if volume['migration_status'] is not None:-----确保卷不是出于迁移的状态
  16. msg = _("Volume is already part of an active migration")
  17. raise exception.InvalidVolume(reason=msg)
  18.  
  19. # We only handle volumes without snapshots for now-----------判断卷是否有快照,如果有,那么就抛出异常
  20. snaps = self.db.snapshot_get_all_for_volume(context, volume['id'])
  21. if snaps:
  22. msg = _("volume must not have snapshots")
  23. LOG.error(msg)
  24. raise exception.InvalidVolume(reason=msg)
  25.  
  26. # We only handle non-replicated volumes for now
  27. rep_status = volume['replication_status']
  28. if rep_status is not None and rep_status != 'disabled':
  29. msg = _("Volume must not be replicated.")
  30. LOG.error(msg)
  31. raise exception.InvalidVolume(reason=msg)
  32.  
  33. cg_id = volume.get('consistencygroup_id', None)
  34. if cg_id:
  35. msg = _("Volume must not be part of a consistency group.")
  36. LOG.error(msg)
  37. raise exception.InvalidVolume(reason=msg)
  38.  
  39. #Make juno volume migration api support icehouse.
  40. #Default lvm backend pool is LVM_iSCSI,
  41. # gluster backend poll is GlusterFS.
  42. if "#" not in host:-----------为了兼容ice版本,对host进行改造
  43. backend_driver = host.split("@")[1]
  44. if backend_driver == "lvmdriver":
  45. host = host + "#LVM_iSCSI"
  46. elif backend_driver == "GLUSTERFS_DRIVER1":
  47. host = host + "#GlusterFS"
  48. else:
  49. msg = _("Volume host is bad format.")
  50. raise exception.InvalidVolume(reason=msg)
  51.  
  52. # Make sure the host is in the list of available hosts
  53. elevated = context.elevated()
  54. topic = CONF.volume_topic
  55. services = self.db.service_get_all_by_topic(elevated,
  56. topic,
  57. disabled=False)
  58. found = False
  59. for service in services:
  60. svc_host = volume_utils.extract_host(host, 'backend')-------对卷要迁移到的主机的service状态,进行判断,是否有效,首先该主机的cinder-volume服务状态是up
  61. if utils.service_is_up(service) and service['host'] == svc_host:
  62. found = True
  63. if not found:
  64. msg = (_('No available service named %s') % host)
  65. LOG.error(msg)
  66. raise exception.InvalidHost(reason=msg)
  67.  
  68. # Make sure the destination host is different than the current one
  69. if host == volume['host']:-------------卷要迁移到的主机必须与卷目前所在的主机不一样
  70. msg = _('Destination host must be different than current host')
  71. LOG.error(msg)
  72. raise exception.InvalidHost(reason=msg)
  73.  
  74. self.update(context, volume, {'migration_status': 'starting'})-----此时更新卷的状态的migration_status:starting
  75.  
  76. # Call the scheduler to ensure that the host exists and that it can
  77. # accept the volume
  78. volume_type = {}
  79. volume_type_id = volume['volume_type_id']
  80. if volume_type_id:
  81. volume_type = volume_types.get_volume_type(context, volume_type_id)
  82. request_spec = {'volume_properties': volume,
  83. 'volume_type': volume_type,
  84. 'volume_id': volume['id']}
  85. self.scheduler_rpcapi.migrate_volume_to_host(context,------给exchangecinder-volume的发送rpc请求--对步骤1.1详解
  86. CONF.volume_topic,
  87. volume['id'],
  88. host,
  89. force_host_copy,
  90. request_spec)

对步骤1.1详解

D:\code-program\cinder-codejuno\scheduler\rpcapi.py,给rabbitmq中,exchange为cinder-volume的发送rpc.cast请求

  1. class SchedulerAPI(object):
  2. '''Client side of the scheduler rpc API.
  3. def migrate_volume_to_host(self, ctxt, topic, volume_id, host,
  4. force_host_copy=False, request_spec=None,
  5. filter_properties=None):
  6.  
  7. cctxt = self.client.prepare(version='1.3')
  8. request_spec_p = jsonutils.to_primitive(request_spec)
  9. return cctxt.cast(ctxt, 'migrate_volume_to_host',
  10. topic=topic,
  11. volume_id=volume_id,
  12. host=host,
  13. force_host_copy=force_host_copy,
  14. request_spec=request_spec_p,
  15. filter_properties=filter_properties)

 二、cinder-scheduler 接受rpc请求,对迁移的主机进行判断 

  1. D:\code-program\cinder-codejuno\scheduler\manager.py
  2. from cinder.volume import rpcapi as volume_rpcapi
  3. # Default scheduler driver to use (string value)
  4. #scheduler_driver=cinder.scheduler.filter_scheduler.FilterScheduler
  5. class SchedulerManager(manager.Manager):
  6. """Chooses a host to create volumes."""
  7.  
  8. def migrate_volume_to_host(self, context, topic, volume_id, host,
  9. force_host_copy, request_spec,
  10. filter_properties=None):
  11. """Ensure that the host exists and can accept the volume."""
  12.  
  13. def _migrate_volume_set_error(self, context, ex, request_spec):
  14. volume_state = {'volume_state': {'migration_status': None}}
  15. self._set_volume_state_and_notify('migrate_volume_to_host',
  16. volume_state,
  17. context, ex, request_spec)
  18.  
  19. try:
  20. tgt_host = self.driver.host_passes_filters(context, host,-----------driver的取值是配置文件中scheduler_driver的取值--对步骤二进行详解
  21. request_spec,
  22. filter_properties)
  23. except exception.NoValidHost as ex:
  24. _migrate_volume_set_error(self, context, ex, request_spec)
  25. except Exception as ex:
  26. with excutils.save_and_reraise_exception():
  27. _migrate_volume_set_error(self, context, ex, request_spec)
  28. else:
  29. volume_ref = db.volume_get(context, volume_id)-----从数据库中,获取卷的信息
  30. volume_rpcapi.VolumeAPI().migrate_volume(context, volume_ref,-----对步骤三进行详解
  31. tgt_host,----目标主机
  32. force_host_copy)
  33.  
  34. 对步骤二进行详解
  35. D:\code-program\cinder-codejuno\scheduler\filter_scheduler.py
  36. class FilterScheduler(driver.Scheduler):
  37. """Scheduler that can be used for filtering and weighing."""
  38. def host_passes_filters(self, context, host, request_spec,
  39. filter_properties):
  40. """Check if the specified host passes the filters."""
  41. weighed_hosts = self._get_weighted_candidates(context, request_spec,------根据请求卷的特性,过滤出可用的存储节点
  42. filter_properties)
  43. for weighed_host in weighed_hosts:------对过滤出来的可用存储节点中,寻找是否有request请求参数中,携带的host的主机信息,如果有,则返回该主机的状态,否则抛出异常
  44. host_state = weighed_host.obj
  45. if host_state.host == host:
  46. return host_state
  47.  
  48. msg = (_('Cannot place volume %(id)s on %(host)s')
  49. % {'id': request_spec['volume_id'], 'host': host})
  50. raise exception.NoValidHost(reason=msg)
  51.  
  52. 对步骤三进行详解
  53. cinder-volume发送rpc.cast请求,进行卷的迁移
  54. D:\code-program\cinder-codejuno\volume\rpcapi.py
  55. class VolumeAPI(object):
  56. '''Client side of the volume rpc API.
  57. def migrate_volume(self, ctxt, volume, dest_host, force_host_copy):
  58. new_host = utils.extract_host(volume['host'])----获取卷所在的host主机
  59. cctxt = self.client.prepare(server=new_host, version='1.8')----更改rcp.client的环境信息,向卷所在的特定主机host发送rpc 请求
  60. host_p = {'host': dest_host.host,-----卷迁移的目标主机
  61. 'capabilities': dest_host.capabilities}
  62. cctxt.cast(ctxt, 'migrate_volume', volume_id=volume['id'],
  63. host=host_p, force_host_copy=force_host_copy)

 三、卷所在的主机的cinder-volume服务接受rpc请求 

  1. # Driver to use for volume creation (string value)
  2. #volume_driver=cinder.volume.drivers.lvm.LVMISCSIDriver
  3. D:\code-program\cinder-codejuno\volume\manager.py
  4. class VolumeManager(manager.SchedulerDependentManager):
  5. """Manages attachable block storage devices."""
  6. RPC_API_VERSION = '1.19'
  7. target = messaging.Target(version=RPC_API_VERSION)
  8.  
  9. def migrate_volume(self, ctxt, volume_id, host, force_host_copy=False,
  10. new_type_id=None):
  11. """Migrate the volume to the specified host (called on source host)."""
  12. try:
  13. # NOTE(flaper87): Verify the driver is enabled
  14. # before going forward. The exception will be caught
  15. # and the migration status updated.
  16. utils.require_driver_initialized(self.driver)-----后端存储驱动的初始化
  17. except exception.DriverNotInitialized:
  18. with excutils.save_and_reraise_exception():
  19. self.db.volume_update(ctxt, volume_id,
  20. {'migration_status': 'error'})
  21.  
  22. volume_ref = self.db.volume_get(ctxt, volume_id)----根据volumeid 获取卷的信息
  23. model_update = None
  24. moved = False
  25.  
  26. status_update = None
  27. if volume_ref['status'] == 'retyping':
  28. status_update = {'status': self._get_original_status(volume_ref)}
  29.  
  30. self.db.volume_update(ctxt, volume_ref['id'],
  31. {'migration_status': 'migrating'})------更新卷的状态的migration_statusmigrating
  32. if not force_host_copy and new_type_id is None:------默认force_host_copy为假,走这个分支,由后端存储驱动的迁移卷函数完成卷迁移
  33. try:
  34. LOG.debug("volume %s: calling driver migrate_volume",
  35. volume_ref['id'])
  36. moved, model_update = self.driver.migrate_volume(ctxt,-----------步骤四详解
  37. volume_ref,
  38. host)
  39. if moved:
  40. updates = {'host': host['host'],
  41. 'migration_status': None}
  42. if status_update:
  43. updates.update(status_update)
  44. if model_update:
  45. updates.update(model_update)
  46. volume_ref = self.db.volume_update(ctxt,
  47. volume_ref['id'],
  48. updates)
  49. except Exception:
  50. with excutils.save_and_reraise_exception():
  51. updates = {'migration_status': None}
  52. if status_update:
  53. updates.update(status_update)
  54. model_update = self.driver.create_export(ctxt, volume_ref)
  55. if model_update:
  56. updates.update(model_update)
  57. self.db.volume_update(ctxt, volume_ref['id'], updates)
  58. if not moved:----如果force_host_copy为真,走这个分支
  59. try:
  60. self._migrate_volume_generic(ctxt, volume_ref, host,----步骤五详解
  61. new_type_id)
  62. except Exception:
  63. with excutils.save_and_reraise_exception():
  64. updates = {'migration_status': None}
  65. if status_update:
  66. updates.update(status_update)
  67. model_update = self.driver.create_export(ctxt, volume_ref)
  68. if model_update:
  69. updates.update(model_update)
  70. self.db.volume_update(ctxt, volume_ref['id'], updates)

 四、 force_host_copy取值不同,所走分支不同的详解

默认情况下,force_host_copy为假,由后端存储驱动来完成卷的迁移工作,如果force_host_copy为真,那么有cinder-volume服务所在的主机完成,卷的迁移工作

force_host_copy为假的情况

  1. 对步骤四进行详解
  2. ceph不支持卷迁移,以lvm卷为前提进行分析
  3. D:\code-program\cinder-codejuno\volume\drivers\lvm.py
  4. class LVMISCSIDriver(LVMVolumeDriver, driver.ISCSIDriver):
  5. def migrate_volume(self, ctxt, volume, host, thin=False, mirror_count=0):
  6. """Optimize the migration if the destination is on the same server.
  7.  
  8. If the specified host is another back-end on the same server, and
  9. the volume is not attached, we can do the migration locally without
  10. going through iSCSI.
  11. """
  12.  
  13. false_ret = (False, None)
  14. if volume['status'] != 'available':
  15. return false_ret
  16. if 'location_info' not in host['capabilities']:
  17. return false_ret
  18. info = host['capabilities']['location_info']
  19. try:
  20. (dest_type, dest_hostname, dest_vg, lvm_type, lvm_mirrors) =\
  21. info.split(':')
  22. lvm_mirrors = int(lvm_mirrors)
  23. except ValueError:
  24. return false_ret
  25. if (dest_type != 'LVMVolumeDriver' or dest_hostname != self.hostname):
  26. return false_ret
  27.  
  28. if dest_vg != self.vg.vg_name:
  29. vg_list = volutils.get_all_volume_groups()
  30. try:
  31. (vg for vg in vg_list if vg['name'] == dest_vg).next()
  32. except StopIteration:
  33. message = (_("Destination Volume Group %s does not exist") %
  34. dest_vg)
  35. LOG.error(message)
  36. return false_ret
  37.  
  38. helper = utils.get_root_helper()
  39. dest_vg_ref = lvm.LVM(dest_vg, helper,
  40. lvm_type=lvm_type,
  41. executor=self._execute)
  42. self.remove_export(ctxt, volume)
  43. self._create_volume(volume['name'],
  44. self._sizestr(volume['size']),
  45. lvm_type,
  46. lvm_mirrors,
  47. dest_vg_ref)
  48.  
  49. volutils.copy_volume(self.local_path(volume),
  50. self.local_path(volume, vg=dest_vg),
  51. volume['size'],
  52. self.configuration.volume_dd_blocksize,
  53. execute=self._execute)
  54. self._delete_volume(volume)
  55. model_update = self._create_export(ctxt, volume, vg=dest_vg)
  56.  
  57. return (True, model_update)
  58. 如果迁移卷的dest_vg,与该节点配置的vg相同,那么就在该vg上对源卷进行一个拷贝,删除源卷的动作
  59. 如果迁移卷的dest_vg,与该节点配置的vg不相同,那么判断dest_vg有效的情况下,移除源卷的export,在dest_vg上创建一个新卷,拷贝数据到目标卷,删除源卷

force_host_copy为真的情况,在这种情况下,是把目的卷,挂载到源卷所在的存储节点上,然后执行linux dd的命令,进行数据的拷贝,因此,拷贝数据的时间长短,完全取决于卷的大小,及存储节点物理主机的cpu及io性能,所以这种情况下,会导致一种情况是,因为数据量太大,数据dd拷贝的时间太长,导致迁移失败的问题

  1. 对步骤五进行详解
  2. # Timeout for creating the volume to migrate to when
  3. # performing volume migration (seconds) (integer value)
  4. #migration_create_volume_timeout_secs=300
  5. D:\code-program\cinder-codejuno\volume\manager.py
  6. class VolumeManager(manager.SchedulerDependentManager):
  7. def _migrate_volume_generic(self, ctxt, volume, host, new_type_id):
  8. rpcapi = volume_rpcapi.VolumeAPI()
  9.  
  10. # Create new volume on remote host
  11. new_vol_values = {}
  12. for k, v in volume.iteritems():
  13. new_vol_values[k] = v
  14. del new_vol_values['id']
  15. del new_vol_values['_name_id']
  16. # We don't copy volume_type because the db sets that according to
  17. # volume_type_id, which we do copy
  18. del new_vol_values['volume_type']
  19. if new_type_id:
  20. new_vol_values['volume_type_id'] = new_type_id
  21. new_vol_values['host'] = host['host']
  22. new_vol_values['status'] = 'creating'
  23. new_vol_values['migration_status'] = 'target:%s' % volume['id']
  24. new_vol_values['attach_status'] = 'detached'
  25. new_volume = self.db.volume_create(ctxt, new_vol_values)
  26. rpcapi.create_volume(ctxt, new_volume, host['host'],
  27. None, None, allow_reschedule=False)
  28. #以上部分,在数据库中新增一条卷的记录,同时在目的存储节点上,创建一个指定存储类型的卷
  29. # Wait for new_volume to become ready
  30. 在指定的时间段内,检查目标存储节点上的卷,状态是否正常
  31. starttime = time.time()
  32. deadline = starttime + CONF.migration_create_volume_timeout_secs
  33. new_volume = self.db.volume_get(ctxt, new_volume['id'])
  34. tries = 0
  35. while new_volume['status'] != 'available':
  36. tries = tries + 1
  37. now = time.time()
  38. if new_volume['status'] == 'error':
  39. msg = _("failed to create new_volume on destination host")
  40. raise exception.VolumeMigrationFailed(reason=msg)
  41. elif now > deadline:
  42. msg = _("timeout creating new_volume on destination host")
  43. raise exception.VolumeMigrationFailed(reason=msg)
  44. else:
  45. time.sleep(tries ** 2)
  46. new_volume = self.db.volume_get(ctxt, new_volume['id'])
  47.  
  48. # Copy the source volume to the destination volume
  49. try:
  50. if (volume['instance_uuid'] is None and
  51. volume['attached_host'] is None):
  52. self.driver.copy_volume_data(ctxt, volume, new_volume,---这里的driver volume_driver 的值,对步骤六详解
  53. remote='dest')
  54. # The above call is synchronous so we complete the migration
  55. self.migrate_volume_completion(ctxt, volume['id'],-----完成迁移的后续工作,删除源卷,更新数据库状态
  56. new_volume['id'], error=False)
  57. else:
  58. nova_api = compute.API()
  59. # This is an async call to Nova, which will call the completion
  60. # when it's done
  61. nova_api.update_server_volume(ctxt, volume['instance_uuid'],
  62. volume['id'], new_volume['id'])
  63. except Exception:
  64. with excutils.save_and_reraise_exception():
  65. msg = _("Failed to copy volume %(vol1)s to %(vol2)s")
  66. LOG.error(msg % {'vol1': volume['id'],
  67. 'vol2': new_volume['id']})
  68. volume = self.db.volume_get(ctxt, volume['id'])
  69. # If we're in the completing phase don't delete the target
  70. # because we may have already deleted the source!
  71. if volume['migration_status'] == 'migrating':
  72. rpcapi.delete_volume(ctxt, new_volume)
  73. new_volume['migration_status'] = None
  74. 对步骤六进行详解
  75. D:\code-program\cinder-codejuno\volume\driver.py
  76. self.driver的取值为volume_driver的值,该方法调用的是下面父类里面的方法
  77. 核心功能是把目的节点上的卷,挂载到源卷的节点上,进行Linux dd方式的数据拷贝,在数据考完完成以后,卸载目的卷
  78. class VolumeDriver(object):
  79.  
  80. def copy_volume_data(self, context, src_vol, dest_vol, remote=None):
  81. """Copy data from src_vol to dest_vol."""
  82. LOG.debug(('copy_data_between_volumes %(src)s -> %(dest)s.')
  83. % {'src': src_vol['name'], 'dest': dest_vol['name']})
  84.  
  85. properties = utils.brick_get_connector_properties()
  86. dest_remote = True if remote in ['dest', 'both'] else False
  87. dest_orig_status = dest_vol['status']
  88. try:
  89. dest_attach_info = self._attach_volume(context,
  90. dest_vol,
  91. properties,
  92. remote=dest_remote)
  93. except Exception:
  94. with excutils.save_and_reraise_exception():
  95. msg = _("Failed to attach volume %(vol)s")
  96. LOG.error(msg % {'vol': dest_vol['id']})
  97. self.db.volume_update(context, dest_vol['id'],
  98. {'status': dest_orig_status})
  99.  
  100. src_remote = True if remote in ['src', 'both'] else False
  101. src_orig_status = src_vol['status']
  102. try:
  103. src_attach_info = self._attach_volume(context,
  104. src_vol,
  105. properties,
  106. remote=src_remote)
  107. except Exception:
  108. with excutils.save_and_reraise_exception():
  109. msg = _("Failed to attach volume %(vol)s")
  110. LOG.error(msg % {'vol': src_vol['id']})
  111. self.db.volume_update(context, src_vol['id'],
  112. {'status': src_orig_status})
  113. self._detach_volume(context, dest_attach_info, dest_vol,
  114. properties, force=True, remote=dest_remote)
  115.  
  116. copy_error = True
  117. mode = self.HOST_BASED
  118. key_values = {}
  119. try:
  120. size_in_mb = int(src_vol['size']) * 1024 # vol size is in GB
  121. src_device_path = src_attach_info['device']['path']
  122. dest_device_path = dest_attach_info['device']['path']
  123. if (not isinstance(src_device_path, six.string_types) or
  124. not isinstance(dest_device_path, six.string_types)):
  125. mode = self.FILE_BASED
  126. key_values = {src_vol['id']: mode,
  127. src_vol['id'] + 'previous_progress': '0'}
  128. if mode == self.HOST_BASED:
  129. key_values[src_vol['id'] + 'source'] = src_device_path
  130. key_values[src_vol['id'] + 'dest'] = dest_device_path
  131. key_values[src_vol['id'] + 'pid'] = None
  132. else:
  133. key_values[src_vol['id'] + 'dest_handle'] = None
  134. self._add_migration_info_key(key_values)
  135.  
  136. volume_utils.copy_volume(
  137. src_device_path,
  138. dest_device_path,
  139. size_in_mb,
  140. self.configuration.volume_dd_blocksize)
  141. copy_error = False
  142. except Exception:
  143. with excutils.save_and_reraise_exception():
  144. msg = _("Failed to copy volume %(src)s to %(dest)s.")
  145. LOG.error(msg % {'src': src_vol['id'], 'dest': dest_vol['id']})
  146. finally:
  147. self._detach_volume(context, dest_attach_info, dest_vol,
  148. properties, force=copy_error,
  149. remote=dest_remote)
  150. self._detach_volume(context, src_attach_info, src_vol,
  151. properties, force=copy_error,
  152. remote=src_remote)

  

 

cinder migrate基础内容-源码分析的更多相关文章

  1. Java基础 ArrayList源码分析 JDK1.8

    一.概述 本篇文章记录通过阅读JDK1.8 ArrayList源码,结合自身理解分析其实现原理. ArrayList容器类的使用频率十分频繁,它具有以下特性: 其本质是一个数组,因此它是有序集合 通过 ...

  2. Java基础——HashTable源码分析

    HashTable是基于哈希表的Map接口的同步实现 HashTable中元素的key是唯一的,value值可重复 HashTable中元素的key和value不允许为null,如果遇到null,则返 ...

  3. cinder migrate基础内容-1

    一.卷迁移rest api接口 POST /v2/{project_id}/volumes/{volume_id}/action 迁移一个卷到特定的主机,在请求体中指定 os-migrate_volu ...

  4. List-LinkedList、set集合基础增强底层源码分析

    List-LinkedList 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 继上一章继续讲解,上章内容: List-ArreyLlist集合基础增强底层源码分析:https:// ...

  5. Spring基础系列-AOP源码分析

    原创作品,可以转载,但是请标注出处地址:https://www.cnblogs.com/V1haoge/p/9560803.html 一.概述 Spring的两大特性:IOC和AOP. AOP是面向切 ...

  6. python基础-11 socket,IO多路复用,select伪造多线程,select读写分离。socketserver源码分析

    Socket socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. sock ...

  7. 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 百篇博客分析OpenHarmony源码 | v14.14

    百篇博客系列篇.本篇为: v14.xx 鸿蒙内核源码分析(内存汇编篇) | 谁是虚拟内存实现的基础 | 51.c.h .o 内存管理相关篇为: v11.xx 鸿蒙内核源码分析(内存分配篇) | 内存有 ...

  8. v77.01 鸿蒙内核源码分析(消息封装篇) | 剖析LiteIpc(上)进程通讯内容 | 新的一年祝大家生龙活虎 虎虎生威

    百篇博客分析|本篇为:(消息封装篇) | 剖析LiteIpc进程通讯内容 进程通讯相关篇为: v26.08 鸿蒙内核源码分析(自旋锁) | 当立贞节牌坊的好同志 v27.05 鸿蒙内核源码分析(互斥锁 ...

  9. Spring Ioc源码分析系列--Ioc的基础知识准备

    Spring Ioc源码分析系列--Ioc的基础知识准备 本系列文章代码基于Spring Framework 5.2.x Ioc的概念 在Spring里,Ioc的定义为The IoC Containe ...

随机推荐

  1. 解决node 运行接口 出现 Cannot destructure property `us` of 'undefined' or 'null'.

    出现 参数是 undefined or null 一.检查是否安装 body-parser server.js中是否引入 app.use(bodyParser.urlencoded({ extende ...

  2. sqlzoo - SELECT from WORLD Tutorial 答案

    01.SELECT from WORLD Tutorial 01.显示所有国家的名称,大洲和人口. SELECT name, continent, population FROM world; 02. ...

  3. PHP strtotime() 函数

    ------------恢复内容开始------------ 实例 将任何字符串的日期时间描述解析为 Unix 时间戳: <?php // 设置时区 date_default_timezone_ ...

  4. PHP imagecolorclosesthwb - 取得与指定的颜色最接近的色度的黑白色的索引

    imagecolorclosesthwb — 取得与指定的颜色最接近的色度的黑白色的索引.高佣联盟 www.cgewang.com 语法 int imagecolorclosesthwb (s res ...

  5. C/C++编程笔记:C++入门知识丨认识C++的函数和对象

    一. 本篇要学习的内容和知识结构概览 二. 知识点逐条分析 1. 混合型语言 C++源文件的文件扩展名为.cpp, 也就是c plus plus的简写, 在该文件里有且只能有一个名为main的主函数, ...

  6. Linux的VMWare中Centos7磁盘分区管理 fdisk分区和制作文件系统格式化和开机自动挂载

    一.硬盘的组成零件扇区 磁道 磁盘容量 磁盘分区 简介 硬盘由容量.柱面数.磁头数.扇区数 C/H/S, Cylinder, Head, Sector(柱面/磁头数/扇区数) 1.磁头数表示硬盘总共有 ...

  7. kafka的学习1

    1.Kafka是什么? Apache Kafka 是一款开源的分布式消息引擎系统.倘若“消息引擎系统”这个词对你来说有点陌生的话,那么“消息队列”“消息中间件”的提法想必你一定是有所耳闻的.不过说实话 ...

  8. Chrome太占内存?试试这个

    " The Great Suspender" 是一个免费的开源 Google Chrome 扩展程序,适用于那些发现chrome占用过多系统资源或经常遭受chrome崩溃的人. 一 ...

  9. 嵌入式linux简介

    嵌入式linux系统应用非常广泛,涵盖各行各业,基于ARM.mips等微处理器架构的硬件平台.基于嵌入式linux系统的设备已经深入生活中各个角落,随处可见.   我们常说的嵌入式linux系统,其实 ...

  10. 035_go语言中的速率限制

    代码演示 package main import "fmt" import "time" func main() { requests := make(chan ...