Openstack Cinder中建立volume過程的原始碼解析(8)
感謝朋友支援本部落格,歡迎共同探討交流,由於能力和時間有限,錯誤之處在所難免,歡迎指正!
如果轉載,請保留作者資訊。
部落格地址:http://blog.csdn.net/gaoxingnengjisuan
郵箱地址:[email protected]
在這篇部落格中,我將詳細的分析task類VolumeCastTask具體是如何來實現根據請求資訊進行卷的建立的。
我們來回顧類VolumeCastTask的原始碼實現:
進一步來看方法_cast_create_volume的原始碼實現:class VolumeCastTask(base.CinderTask): """ 遠端呼叫實現卷的建立操作; """ def __init__(self, scheduler_rpcapi, volume_rpcapi, db): super(VolumeCastTask, self).__init__(addons=[ACTION]) self.volume_rpcapi = volume_rpcapi self.scheduler_rpcapi = scheduler_rpcapi self.db = db self.requires.update(['image_id', 'scheduler_hints', 'snapshot_id', 'source_volid', 'volume_id', 'volume_type', 'volume_properties']) def __call__(self, context, **kwargs): """ 遠端呼叫實現卷的建立操作; context = <cinder.context.RequestContext object at 0x4337e10> kwargs = {'volume_properties': {'status': 'creating', 'volume_type_id': None, 'user_id': u'ef073287176048bd861dcd9d9c4d9808', 'availability_zone': 'nova', 'reservations': ['4d53f59d-f1ca-4a62-a6ff-7b4609bd6512', '513f29df-26b2-49d3-bd96-951623e4d20f'], 'volume_admin_metadata': [], 'attach_status': 'detached', 'display_description': None, 'volume_metadata': [], 'metadata': {}, 'encryption_key_id': None, 'source_volid': None, 'snapshot_id': None, 'display_name': u'shinian01', 'project_id': u'6c3c74779a614d3b81dd75518824e25c', 'id': '3e68f81a-3e9e-45f6-8332-55c31a3195dc', 'size': 1}, 'source_volid': None, 'image_id': None, 'snapshot_id': None, 'volume_type': {}, 'volume_id': '3e68f81a-3e9e-45f6-8332-55c31a3195dc', 'scheduler_hints': None} """ scheduler_hints = kwargs.pop('scheduler_hints', None) request_spec = kwargs.copy() filter_properties = {} if scheduler_hints: filter_properties['scheduler_hints'] = scheduler_hints # filter_properties = {} # _cast_create_volume:遠端呼叫建立卷的方法,實現卷的建立操作; self._cast_create_volume(context, request_spec, filter_properties)
這個方法主要實現了根據具體情況通過遠端呼叫建立新卷的操作。在這個方法中,大致可以分為四個步驟:def _cast_create_volume(self, context, request_spec, filter_properties): """ 遠端呼叫建立卷的方法,實現卷的建立操作; request_spec = {'image_id': None, 'volume_type': {}, 'volume_id': '3e68f81a-3e9e-45f6-8332-55c31a3195dc', 'snapshot_id': None, 'volume_properties': {'status': 'creating', 'volume_type_id': None, 'user_id': u'ef073287176048bd861dcd9d9c4d9808', 'availability_zone': 'nova', 'reservations': ['4d53f59d-f1ca-4a62-a6ff-7b4609bd6512', '513f29df-26b2-49d3-bd96-951623e4d20f'], 'volume_admin_metadata': [], 'attach_status': 'detached', 'display_description': None, 'volume_metadata': [], 'metadata': {}, 'encryption_key_id': None, 'source_volid': None, 'snapshot_id': None, 'display_name': u'shinian01', 'project_id': u'6c3c74779a614d3b81dd75518824e25c', 'id': '3e68f81a-3e9e-45f6-8332-55c31a3195dc', 'size': 1}, 'source_volid': None} filter_properties = {} """ source_volid = request_spec['source_volid'] # source_volid = None; volume_id = request_spec['volume_id'] # volume_id = 3e68f81a-3e9e-45f6-8332-55c31a3195dc snapshot_id = request_spec['snapshot_id'] # snapshot_id = None; image_id = request_spec['image_id'] # image_id = None; host = None # snapshot_same_host:這個引數定義了是否根據快照在快照所處的主機上建立卷; # 引數的預設值為True; # snapshot_id = None,說明請求中不是要求根據快照來建立卷; if snapshot_id and CONF.snapshot_same_host: # 根據snapshot_id獲取指定卷的快照; snapshot_ref = self.db.snapshot_get(context, snapshot_id) # 根據snapshot_ref['volume_id']獲取volume; source_volume_ref = self.db.volume_get(context, snapshot_ref['volume_id']) # 根據卷的屬性獲取它所在的主機資訊; host = source_volume_ref['host'] # source_volid = None,說明請求中不是要求根據現有的指定的捲來建立卷; elif source_volid: # 根據source_volid獲取volume; source_volume_ref = self.db.volume_get(context, source_volid) # 根據卷的屬性獲取它所在的主機資訊; host = source_volume_ref['host'] # 如果沒有獲取到主機資訊,說明沒有確定建立卷的目標主機,需要通過排程器擇優獲取目標主機,並實現遠端呼叫方法create_volume,來進行建立卷的操作; if not host: # create_volume:遠端呼叫實現卷的建立操作; # self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI(); self.scheduler_rpcapi.create_volume( context, CONF.volume_topic, volume_id, snapshot_id=snapshot_id, image_id=image_id, request_spec=request_spec, filter_properties=filter_properties) # 如果獲取到主機的資訊,則直接通過volume_rpcapi實現遠端呼叫方法create_volume,來進行卷的建立操作。 else: now = timeutils.utcnow() values = {'host': host, 'scheduled_at': now} # 在捲上設定給定的屬性,並進行更新; volume_ref = self.db.volume_update(context, volume_id, values) # 遠端呼叫實現建立並匯出卷; # self.volume_rpcapi = volume_rpcapi.VolumeAPI(); self.volume_rpcapi.create_volume( context, volume_ref, volume_ref['host'], request_spec, filter_properties, allow_reschedule=False, snapshot_id=snapshot_id, image_id=image_id, source_volid=source_volid)
1.從輸入引數request_spec中獲取相應的資料資訊,主要用於鑑別採用何種建立卷的方法;
2.判斷是否根據現有快照在快照所在的主機上進行新卷的建立,根據判斷結果從具體的引數中獲取建立新卷的目標主機;
3.如果沒有獲取到主機資訊,說明沒有確定建立卷的目標主機,需要通過排程器擇優獲取目標主機,並實現遠端呼叫方法create_volume,來進行建立卷的操作;
4.如果獲取到主機的資訊,則直接通過volume_rpcapi實現遠端呼叫方法create_volume,來進行卷的建立操作。
我們先來看上述第3步驟的具體實現過程,在這裡我們知道self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI(),所以這裡呼叫的建立卷的方法為/cinder/scheduler/rpcapi.py----class SchedulerAPI----def create_volume,我們來看方法的原始碼實現:
def create_volume(self, ctxt, topic, volume_id, snapshot_id=None,
image_id=None, request_spec=None,
filter_properties=None):
"""
遠端呼叫實現建立卷的主機選擇,以及卷的建立操作;
"""
request_spec_p = jsonutils.to_primitive(request_spec)
return self.cast(ctxt, self.make_msg(
'create_volume',
topic=topic,
volume_id=volume_id,
snapshot_id=snapshot_id,
image_id=image_id,
request_spec=request_spec_p,
filter_properties=filter_properties),
version='1.2')
我們可以看到,這個方法中通過廣播的方式,傳送請求遠端呼叫建立卷的方法create_volume,這裡具體執行的方法是/cinder/scheduler/manager.py----class SchedulerManager----def create_volume,我們來看方法的原始碼實現:
def create_volume(self, context, topic, volume_id, snapshot_id=None,
image_id=None, request_spec=None,
filter_properties=None):
"""
volume_rpcapi.create_volume是呼叫manager.py中的create_volume方法,
該方法先從資料庫中取出volume的資訊,然後呼叫volume_utils.notify_about_volume_usage方法(是不是通知RabbitMQ?)。
然後繼續從volume資訊中取vol_name,vol_size,
並且如果入參中有snapshot_id,說明從快照建立卷,則從DB中取該snapshot的資訊,
如果入參中有source_volID,說明是從已有卷建立卷,則從DB中取該源卷資訊,
如果入參中有image_id,說明是從映象建立卷,則從glance中獲取映象伺服器資訊,映象ID,映象位置,映象的metadata.
然後呼叫該類的私有方法_create_volume,該方法首先判斷如果snapshot_ref,imag_id,srcvol_ref都是空,
則說明是建立一個空卷,就呼叫driver.create_volume去創卷,
如果有snapshot_ref引數,則呼叫driver.create_volume_from_snapshot方法去創卷,
如果請求中有源卷的引數,則呼叫driver.create_cloned_volume去創卷(實際上就是克隆一個卷)。
如果請求中有映象引數,則呼叫driver.clone_image方法去創卷,
如果clone_image失敗,則呼叫普通創卷方法先建立個空卷,
然後將卷狀態置為downloading,
然後呼叫_copy_image_to_volume方法把映象內容拷貝入卷中。
"""
# 構建並返回用於通過遠端排程建立卷的flow;
flow = create_volume.get_scheduler_flow(db, self.driver,
request_spec,
filter_properties,
volume_id, snapshot_id,
image_id)
assert flow, _('Schedule volume flow not retrieved')
flow.run(context)
if flow.state != states.SUCCESS:
LOG.warn(_("Failed to successfully complete"
" schedule volume using flow: %s"), flow)
我們這裡可以看到,這裡再一次應用taskflow模式來實現通過排程器選取目標主機並實現建立卷的操作,taskflow模式的具體應用方法在前面的部落格中我們已經進行過詳細的解析,這裡不再贅述,這裡我們只是簡單的看看原始碼實現:
def get_scheduler_flow(db, driver, request_spec=None, filter_properties=None,
volume_id=None, snapshot_id=None, image_id=None):
"""
Constructs and returns the scheduler entrypoint flow.
構建並返回用於通過遠端排程建立卷的flow;
flow將會做以下的事情:
1. 為相關的task注入keys和values;
2. 實現了從輸入的引數中提取排程器的規範資訊的操作;
3. 對於出錯的task進行處理,傳送錯誤通知,記錄錯誤資訊等;
4. 遠端呼叫實現在主機上建立卷;
"""
# flow_name:volume_create_scheduler;
flow_name = ACTION.replace(":", "_") + "_scheduler"
# 獲取類Flow的例項化物件;
scheduler_flow = linear_flow.Flow(flow_name)
# This injects the initial starting flow values into the workflow so that
# the dependency order of the tasks provides/requires can be correctly
# determined.
# 新增一個給定的task到flow;
# 這個類實現了注入字典資訊到flow中;
scheduler_flow.add(base.InjectTask({
'request_spec': request_spec,
'filter_properties': filter_properties,
'volume_id': volume_id,
'snapshot_id': snapshot_id,
'image_id': image_id,
}, addons=[ACTION]))
# This will extract and clean the spec from the starting values.
# 新增一個給定的task到flow;
# ExtractSchedulerSpecTask:實現了從輸入的引數中提取排程器的規範資訊的操作;
scheduler_flow.add(ExtractSchedulerSpecTask(db))
# The decorator application here ensures that the method gets the right
# requires attributes automatically by examining the underlying functions
# arguments.
@decorators.task
def schedule_create_volume(context, request_spec, filter_properties):
"""
實現通過合適的排程器演算法選擇建立卷的主機,並實現卷的建立;
"""
def _log_failure(cause):
"""
記錄錯誤資訊;
"""
LOG.error(_("Failed to schedule_create_volume: %(cause)s") %
{'cause': cause})
def _notify_failure(cause):
"""
When scheduling fails send out a event that it failed.
當排程出現錯誤時,則傳送一個標誌為錯誤的事件通知;
"""
topic = "scheduler.create_volume"
payload = {
'request_spec': request_spec,
'volume_properties': request_spec.get('volume_properties', {}),
'volume_id': volume_id,
'state': 'error',
'method': 'create_volume',
'reason': cause,
}
try:
publisher_id = notifier.publisher_id("scheduler")
notifier.notify(context, publisher_id, topic, notifier.ERROR,
payload)
except exception.CinderException:
LOG.exception(_("Failed notifying on %(topic)s "
"payload %(payload)s") % {'topic': topic,
'payload': payload})
try:
# 目前cinder中提供了三種排程器方法實現建立卷的主機的選擇,並實現卷的建立;
driver.schedule_create_volume(context, request_spec, filter_properties)
except exception.NoValidHost as e:
# Not host found happened, notify on the scheduler queue and log
# that this happened and set the volume to errored out and
# *do not* reraise the error (since whats the point).
# 當排程出現錯誤時,則傳送一個標誌為錯誤的事件通知;
_notify_failure(e)
# 記錄錯誤資訊;
_log_failure(e)
_error_out_volume(context, db, volume_id, reason=e)
except Exception as e:
# Some other error happened, notify on the scheduler queue and log
# that this happened and set the volume to errored out and
# *do* reraise the error.
with excutils.save_and_reraise_exception():
_notify_failure(e)
_log_failure(e)
_error_out_volume(context, db, volume_id, reason=e)
# 新增一個給定的task到flow;
# schedule_create_volume:通過排程器獲取目標主機並實現遠端呼叫建立新卷的操作;
scheduler_flow.add(schedule_create_volume)
# 獲取flow的除錯資訊;
return flow_utils.attach_debug_listeners(scheduler_flow)
在這個方法中,構建了flow任務流物件,並注入了三個task任務物件,其中前兩個任務主要是完成若干資訊資料的注入和獲取,其中最重要的任務就是第三個任務,即通過排程器獲取目標主機並實現遠端呼叫建立新卷的操作。我們來看其具體實現方法的原始碼:
@decorators.task
def schedule_create_volume(context, request_spec, filter_properties):
"""
實現通過合適的排程器演算法選擇建立卷的主機,並實現卷的建立;
"""
def _log_failure(cause):
"""
記錄錯誤資訊;
"""
LOG.error(_("Failed to schedule_create_volume: %(cause)s") %
{'cause': cause})
def _notify_failure(cause):
"""
When scheduling fails send out a event that it failed.
當排程出現錯誤時,則傳送一個標誌為錯誤的事件通知;
"""
topic = "scheduler.create_volume"
payload = {
'request_spec': request_spec,
'volume_properties': request_spec.get('volume_properties', {}),
'volume_id': volume_id,
'state': 'error',
'method': 'create_volume',
'reason': cause,
}
try:
publisher_id = notifier.publisher_id("scheduler")
notifier.notify(context, publisher_id, topic, notifier.ERROR,
payload)
except exception.CinderException:
LOG.exception(_("Failed notifying on %(topic)s "
"payload %(payload)s") % {'topic': topic,
'payload': payload})
try:
# 目前cinder中提供了三種排程器方法實現建立卷的主機的選擇,並實現卷的建立;
driver.schedule_create_volume(context, request_spec, filter_properties)
except exception.NoValidHost as e:
# Not host found happened, notify on the scheduler queue and log
# that this happened and set the volume to errored out and
# *do not* reraise the error (since whats the point).
# 當排程出現錯誤時,則傳送一個標誌為錯誤的事件通知;
_notify_failure(e)
# 記錄錯誤資訊;
_log_failure(e)
_error_out_volume(context, db, volume_id, reason=e)
except Exception as e:
# Some other error happened, notify on the scheduler queue and log
# that this happened and set the volume to errored out and
# *do* reraise the error.
with excutils.save_and_reraise_exception():
_notify_failure(e)
_log_failure(e)
_error_out_volume(context, db, volume_id, reason=e)
這個方法中最重要的語句就是:
driver.schedule_create_volume(context, request_spec, filter_properties)
這條語句所實現的作用就是呼叫指定的排程器演算法實現目標主機的確定,並實現新卷的建立的操作。在H版的cinder模組中,暫時實現了三個排程器演算法,即過濾-承重演算法、隨機獲取主機演算法和選取建立卷數量最少作為目標主機的演算法。這裡所呼叫的方法是由driver所確定的;
我們在類SchedulerManager的初始化方法中可以看到:
if not scheduler_driver:
scheduler_driver = CONF.scheduler_driver
self.driver = importutils.import_object(scheduler_driver)
所以具體排程器演算法的確定就是由變數scheduler_driver所確定的,我們又可以看到:
scheduler_driver_opt = cfg.StrOpt('scheduler_driver',
default='cinder.scheduler.filter_scheduler.'
'FilterScheduler',
help='Default scheduler driver to use')
所以系統預設的排程器演算法為:
cinder.scheduler.filter_scheduler.FilterScheduler.schedule_create_volume,所以我們先來看這個方法的原始碼實現:
def schedule_create_volume(self, context, request_spec, filter_properties):
"""
對主機進行過濾和稱重操作,獲取最優主機,並實現遠端呼叫建立並匯出卷;
"""
# 對主機進行過濾稱重操作,獲取最優的主機;
weighed_host = self._schedule(context, request_spec, filter_properties)
if not weighed_host:
raise exception.NoValidHost(reason="")
host = weighed_host.obj.host
volume_id = request_spec['volume_id']
snapshot_id = request_spec['snapshot_id']
image_id = request_spec['image_id']
# @@@@在捲上設定給定的屬性,並進行更新;
updated_volume = driver.volume_update_db(context, volume_id, host)
# @@@@在最優主機選定之後,新增附加資訊到過濾器屬性;
self._post_select_populate_filter_properties(filter_properties,
weighed_host.obj)
# context is not serializable
filter_properties.pop('context', None)
# create_volume:遠端呼叫實現建立並匯出卷;
self.volume_rpcapi.create_volume(context, updated_volume, host,
request_spec, filter_properties,
allow_reschedule=True,
snapshot_id=snapshot_id,
image_id=image_id)
這裡採用了對所有可用主機進行過濾和稱重操作來實現確定目標主機,並呼叫create_volume方法實現遠端呼叫在目標主機上建立新卷的操作。具體方法實現比較簡單,脈絡比較清晰,所以這裡不進行詳細的解析。
我們再來看隨機選取目標主機的排程器演算法的原始碼實現:
def schedule_create_volume(self, context, request_spec, filter_properties):
"""
Picks a host that is up at random.
實現隨機選取主機;
遠端呼叫實現在選取的主機上建立並匯出卷;
"""
topic = CONF.volume_topic
# _schedule:實現隨機選取主機;
host = self._schedule(context, topic, request_spec,
filter_properties=filter_properties)
volume_id = request_spec['volume_id']
snapshot_id = request_spec['snapshot_id']
image_id = request_spec['image_id']
# volume_update_db:在給定的卷的資料庫中設定屬性資訊並進行更新;
updated_volume = driver.volume_update_db(context, volume_id, host)
# create_volume:遠端呼叫實現建立並匯出卷;
self.volume_rpcapi.create_volume(context, updated_volume, host,
request_spec, filter_properties,
snapshot_id=snapshot_id,
image_id=image_id)
我們也可以看到在這個方法中,也呼叫create_volume方法實現遠端呼叫在目標主機上建立新卷的操作;
我們再來看選取最少卷作為目標主機的排程器演算法的原始碼實現:
def schedule_create_volume(self, context, request_spec, filter_properties):
"""
Picks a host that is up and has the fewest volumes.
選取擁有最少卷的主機作為目標主機;
"""
elevated = context.elevated()
volume_id = request_spec.get('volume_id')
snapshot_id = request_spec.get('snapshot_id')
image_id = request_spec.get('image_id')
volume_properties = request_spec.get('volume_properties')
volume_size = volume_properties.get('size')
availability_zone = volume_properties.get('availability_zone')
zone, host = None, None
if availability_zone:
zone, _x, host = availability_zone.partition(':')
if host and context.is_admin:
topic = CONF.volume_topic
service = db.service_get_by_args(elevated, host, topic)
if not utils.service_is_up(service):
raise exception.WillNotSchedule(host=host)
updated_volume = driver.volume_update_db(context, volume_id, host)
self.volume_rpcapi.create_volume(context, updated_volume, host,
request_spec, filter_properties,
snapshot_id=snapshot_id,
image_id=image_id)
return None
results = db.service_get_all_volume_sorted(elevated)
if zone:
results = [(s, gigs) for (s, gigs) in results
if s['availability_zone'] == zone]
for result in results:
(service, volume_gigabytes) = result
if volume_gigabytes + volume_size > CONF.max_gigabytes:
msg = _("Not enough allocatable volume gigabytes remaining")
raise exception.NoValidHost(reason=msg)
if utils.service_is_up(service) and not service['disabled']:
updated_volume = driver.volume_update_db(context, volume_id,
service['host'])
self.volume_rpcapi.create_volume(context, updated_volume,
service['host'], request_spec,
filter_properties,
snapshot_id=snapshot_id,
image_id=image_id)
return None
msg = _("Is the appropriate service running?")
raise exception.NoValidHost(reason=msg)
我們也可以看到在這個方法中,也呼叫了create_volume方法實現遠端呼叫在目標主機上建立新卷的操作;
我們可以看到,在這三個排程器演算法中,最後都呼叫了方法create_volume實現遠端呼叫在目標主機上建立新卷的操作。我們再回到方法_cast_create_volume中,可以看到也是呼叫了create_volume方法實現遠端呼叫在目標主機上建立新卷的目標。
對於方法create_volume的實現過程,我將會在下一篇部落格中進行詳細解析。
相關推薦
Openstack Cinder中建立volume過程的原始碼解析(8)
感謝朋友支援本部落格,歡迎共同探討交流,由於能力和時間有限,錯誤之處在所難免,歡迎指正!如果轉載,請保留作者資訊。部落格地址:http://blog.csdn.net/gaoxingnengjisuan郵箱地址:[email protected] 在這篇部落格中,
Openstack Cinder中建立volume過程的原始碼解析(9)
感謝朋友支援本部落格,歡迎共同探討交流,由於能力和時間有限,錯誤之處在所難免,歡迎指正!如果轉載,請保留作者資訊。部落格地址:http://blog.csdn.net/gaoxingnengjisuan郵箱地址:[email protected] 我們在上一篇部落
Mybaits 原始碼解析 (二)----- 根據配置檔案建立SqlSessionFactory(Configuration的建立過程)
我們使用mybatis操作資料庫都是通過SqlSession的API呼叫,而建立SqlSession是通過SqlSessionFactory。下面我們就看看SqlSessionFactory的建立過程。 配置檔案解析入口 我們看看第一篇文章中的測試方法 1 public static void m
Mybaits 原始碼解析 (四)----- SqlSession的建立過程(看懂框架原始碼再也不用死記硬背面試題)
SqlSession是mybatis的核心介面之一,是myabtis介面層的主要組成部分,對外提供了mybatis常用的api。myabtis提供了兩個SqlSesion介面的實現,常用的實現類是DefaultSqlSession。它相當於一個數據庫連線物件,在一個SqlSession中可以執行多條SQL語句
Mybaits 原始碼解析 (六)----- 全網最詳細:Select 語句的執行過程分析(上篇)(Mapper方法是如何呼叫到XML中的SQL的?)
上一篇我們分析了Mapper介面代理類的生成,本篇接著分析是如何呼叫到XML中的SQL 我們回顧一下MapperMethod 的execute方法 public Object execute(SqlSession sqlSession, Object[] args) { Object res
EventBus原始碼解析(一)—訂閱過程
1.EventBus原始碼解析(一)—訂閱過程 2.EventBus原始碼解析(二)—釋出事件和登出流程 前言 最近發現EventBus用起來是真的方便,本來對於EventBus我對於這個框架的原始碼的閱讀的優先順序是比較低的,因為這個框架不像OkHttp,Gli
【MapReduce詳解及原始碼解析(一)】——分片輸入、Mapper及Map端Shuffle過程
title: 【MapReduce詳解及原始碼解析(一)】——分片輸入、Mapper及Map端Shuffle過程 date: 2018-12-03 21:12:42 tags: Hadoop categories: 大資料 toc: true 點選檢視我的部落格:Josonlee’
ElasticSearch原始碼解析(三):索引建立
我們先來看看索引建立的事例程式碼: Directory directory = FSDirectory.getDirectory("/tmp/testindex"); // Use standard analyzer Analyzer analyzer = new
Spring原始碼解析(4):IOC過程下
上文說到populateBean方法中,對被@Autowired註解的屬性方法進行注入。在這之後,BeanFactory執行applyPropertyValues方法,這個方法中,一個是把之前解析出來的屬性值設定到bean中去;一個是繼續解析出BeanDefinition中定
roaringbitmap 原始碼解析(3)底層容器相互add過程
今天主要講述roaringbitmap的add計算。 先判斷高位是否存在相同的,如果相同,再低位的容器相add。否則,直接跳過 public static RoaringBitmap and(final RoaringBitmap x1, final
Spring原始碼解析(七):Spring AOP中對攔截器呼叫的實現
前面我們分析了Spring AOP實現中得到Proxy物件的過程,下面我們看看在Spring AOP中攔截器鏈是怎樣被呼叫的,也就是Proxy模式是怎樣起作用的,或者說Spring是怎樣為我們提供AOP功能的; 在JdkDynamicAopProxy中生成Proxy物件的時
Mybaits 原始碼解析 (七)----- Select 語句的執行過程分析(下篇)全網最詳細,沒有之一
我們上篇文章講到了查詢方法裡面的doQuery方法,這裡面就是呼叫JDBC的API了,其中的邏輯比較複雜,我們這邊文章來講,先看看我們上篇文章分析的地方 SimpleExecutor 1 public <E> List<E> doQuery(MappedStatement m
HashMap原始碼解析(JDK8)
前言 這段時間有空,專門填補了下基礎,把常用的ArrayList、LinkedList、HashMap、LinkedHashMap、LruCache原始碼看了一遍,List相對比較簡單就不單獨介紹了,Map準備用兩篇的篇幅,分別介紹HashMap和(LruCache+LinkedHa
qt 中建立一個工作執行緒(例子)
當一個事件需要很長的處理時間,就建立一個工作執行緒,防止主介面卡死。 1.新建一個QT的gui專案,裡面包含main.cpp,mainwindow.h,mainwindow.cpp,mainwindow.ui檔案 2.新建一個頭檔案thread.h,派生一個執行緒類,重新寫一個執行緒的入口函式。
Spring原始碼解析(十三)——AOP原理——AnnotationAwareAspectJAutoProxyCreator註冊
* 2、 AnnotationAwareAspectJAutoProxyCreator: * AnnotationAwareAspectJAutoProxyCreator &nbs
Spring原始碼解析(四)——元件註冊4
/** * 給容器中註冊元件; * 1)、包掃描+元件標註註解(@Controller/@Service/@Repository/@Component)[自己寫的類] * 2)、@Bean[匯入的第三方包裡面的元件] * 3)、@Import[快速給容器中匯入一個
Spring原始碼解析(八)——生命週期——BeanPostProcessor在spring底層的使用
一、ApplicationContextAwareProcessor import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import or
Spring原始碼解析(七)——生命週期——BeanPostProcessor
https://blog.csdn.net/u011734144/article/details/72600932 http://www.cnblogs.com/lucas2/p/9430169.html BeanPostProcessor:bean的後置處理器。在bean
Spring原始碼解析(三)——元件註冊3
@Scope設定元件作用域 import com.ken.domain.Person; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Config
Spring原始碼解析(二)——元件註冊2
import com.ken.service.BookService; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.