1. 程式人生 > >ceph原始碼解析--osd篇

ceph原始碼解析--osd篇

Ceph分散式檔案系統的程式碼分析的文章網上是比較少的,本團隊成員對ceph做過詳細的程式碼閱讀,包括mds、osd、client等模組,但是缺少條理清晰的文件總結。暫且先放上OSD的程式碼分析,等後續整理陸續放上其它模組的。

1 OSD的基本結構
主要的類,涉及的執行緒,工作的方式

1.1 類OSD
該類主要用以處理網路訊息,與mds客戶端等之間的網路連線的維護。當收到客戶端或者mds對物件的資料請求後,交給相關的類進行處理。

1.1.1 主要物件

ObjectStore store; /對object訪問介面的封裝**/

OSDSuperblock superblock; 主要是版本號等資訊

OSDMapRef osdmap;

1.1.2 OSD中的執行緒池

[1] op_tp:

op_wq(this, g_conf->osd_op_thread_timeout, &op_tp)

scrub_finalize_wq(this, g_conf->osd_scrub_finalize_thread_timeout, &op_tp)

這裡的op_wq是當OSD中當有請求操作時,會將該操作分配給所屬的PG處理:

涉及的操作型別包括:CEPH_MSG_OSD_OP(client op) , MSG_OSD_SUBOP(for replication etc.) ,MSG_OSD_SUBOPREPLY。這些操作都要交給PG處理。

通過方法enqueue_op(pg, op);加入佇列

   // add to pg's op_queue

   pg->op_queue.push_back(op);                  //該pg中加入該操作

   op_wq.queue(pg);            //由於該pg有了操作,將pg入隊,op_tp中的執行緒會處理

其中op_wq的定義如下:

struct OpWQ : public ThreadPool::WorkQueue {

OSD *osd;

OpWQ(OSD *o, time_t ti, ThreadPool *tp)

  : ThreadPool::WorkQueue<PG>("OSD::OpWQ", ti, ti*10, tp), osd(o) {}



bool _enqueue(PG *pg);

void _dequeue(PG *pg) {

  assert(0);

}

bool _empty() {

  return osd->op_queue.empty();

}

PG *_dequeue();

void _process(PG *pg) {

  osd->dequeue_op(pg);

}

void _clear() {

  assert(osd->op_queue.empty());

}

} op_wq;

OpWQ主要操作osd->op_queue,即deque op_queue;

[2] recovery_tp

recovery_wq(this, g_conf->osd_recovery_thread_timeout, &recovery_tp)

struct RecoveryWQ : public ThreadPool::WorkQueue {

OSD *osd;

RecoveryWQ(OSD *o, time_t ti, ThreadPool *tp)

  : ThreadPool::WorkQueue<PG>("OSD::RecoveryWQ", ti, ti*10, tp), osd(o) {}

RecoveryWQ 主要操作osd->recovery_queue,實際上封裝與recovery相關的操作,這裡recovery操作具體由每個PG執行。

void _process(PG *pg) {

  osd->do_recovery(pg);

}

[3] disk_tp

remove_wq(this, g_conf->osd_remove_thread_timeout, &disk_tp)

     osd->backlog_queue

// backlogs

   xlist<PG*> backlog_queue;

rep_scrub_wq(this, g_conf->osd_scrub_thread_timeout, &disk_tp)

       struct RepScrubWQ : public ThreadPool::WorkQueue<MOSDRepScrub> {

private:

OSD *osd;

list rep_scrub_queue;

snap_trim_wq(this, g_conf->osd_snap_trim_thread_timeout, &disk_tp)

     osd->snap_trim_queue

     // -- snap trimming --

xlist snap_trim_queue;

backlog_wq(this, g_conf->osd_backlog_thread_timeout, &disk_tp)

     osd->backlog_queue

     // backlogs

xlist backlog_queue;

[4] command_tp

command_wq(this, g_conf->osd_command_thread_timeout, &command_tp)

list command_queue;

osd->command_queue

void _process(Command *c) {

  osd->osd_lock.Lock();

  osd->do_command(c->con, c->tid, c->cmd, c->indata);

  osd->osd_lock.Unlock();

  delete c;

}

1.2 PG
PG,物件訪問的上層控制,確定讀取的物件的位置等資訊,對物件的實際的讀寫資料控制由FileStore完成。

Ceph系統中為了管理物件,將物件進行了分組。PG即place_group就是ceph中的分組。

1.2.1 主要物件

class PG {

       struct Info {                    描述一個PG的基本資訊

                   pg_t pgid;

                   pg_stat_t stats;

                        struct History {}                 建立的版本號,修改時間等

       }

       struct Query {       Query - used to ask a peer for information about a pg.向其他OSD查詢一個pg的資訊

               __s32 type;

         eversion_t since;

         Info::History history;

     }

struct Log { incremental log of recent pg changes. pg修改的日誌

         struct Entry {

                        __s32      op;

                   hobject_t  soid;

                        osd_reqid_t reqid;

                        uint64_t offset;   // [soft state] my offset on disk

               }

list log; // the actual log.

}

IndexLog - adds in-memory index of the log, by oid. 日誌在記憶體中的索引

struct IndexedLog : public Log {

     hash_map<hobject_t,Entry*> objects;  // ptrs into log.  be careful!          每個物件對應的日誌

hash_map caller_ops;

               list<Entry>::iterator complete_to;           // recovery pointers

     }



      class OndiskLog {

               uint64_t tail;             // first byte of log.

               uint64_t head;

     }

struct Missing { //summary of missing objects.

//kept in memory, as a supplement to Log.

map missing; // oid -> (need v, have v)

map<version_t, hobject_t> rmissing;  // v -> oid

}

list op_queue; // op queue PG操作的佇列

// pg state

Info info;

const coll_t coll;

IndexedLog log;

hobject_t log_oid;

hobject_t biginfo_oid;

OndiskLog ondisklog;

Missing missing;

int role; // 0 = primary, 1 = replica, -1=none. 該pg的角色,主,備

/* Encapsulates PG recovery process */ PG recover處理的過程

class RecoveryState {

RecoveryMachine machine;

RecoveryCtx *rctx;

}

}

父類PG主要是用以對PG本身的維護,對PG的修改,日誌的管理等。

Srcub的過程:

PG收集其管理的所有的objects,並向PG的副本請求物件的資訊,進行物件狀態的異常檢查。

ReplicatedPG主要用以操作物件,物件操作介面的封裝。

1.3 FileStore
負責向osd裝置中資料的讀寫,作為類OSD的成員物件store出現。

1.4 FileJournal
負責日誌的管理,通過日誌恢復資料等,作為類OSD的成員物件journal出現。

2 OSD讀寫資料的過程
2.1 客戶端發起請求的過程
int Client::ll_read(Fh *fh, loff_t off, loff_t len, bufferlist *bl)

int Client::read(Fh *f, int64t offset, uint64_t size, bufferlist *bl)

int Client::readsync(Fh *f, uint64_t off, uint64_t len, bufferlist *bl)

     //前幾個引數均在結構體Inode中

Inode *in = f->inode;

filer->read_trunc(in->ino, &in->layout, in->snapid,

                     pos, left, &tbl, 0,

                     in->truncate_size, in->truncate_seq,

                     onfinish);

int read_trunc(inodeno_t ino,

        ceph_file_layout *layout,

        snapid_t snap,

       uint64_t offset,

       uint64_t len,

       bufferlist *bl,   // ptr to data

             int flags,

        uint64_t truncate_size,

        __u32 truncate_seq,

       Context *onfinish)

向osd讀取資料的過程:

1 將要讀取資料的長度和偏移轉化為要訪問的物件

file_to_extents(ino, layout, offset, len, extents);

2 向osd發起請求

objecter->sg_read(extents, snap, bl, flags, onfinish);

Filer.h

     //計算需要讀取的資料所在的extent,extent沿用了brtfs檔案系統的概念

     // ino ==> extents, extent實際上是object,offset

根據檔案偏移訪問物件的過程:

void Filer::file_to_extents(inodeno_t ino, ceph_file_layout *layout,

                        uint64_t offset, uint64_t len,

                        vector<ObjectExtent>& extents)

_u32 objectsize = layout->fl_object_size;

_u32 su = layout->flstripe_unit;

_u32 stripecount = layout->fl_stripe_count;

uint64_t stripes_per_object = object_size / su;

     每個物件有兩部分ino和objectno

// layout into objects

uint64_t blockno = cur / su;          // which block

uint64_t stripeno = blockno / stripe_count;    // which horizontal stripe        (Y)

uint64_t stripepos = blockno % stripe_count;   // which object in the object set (X)

uint64_t objectsetno = stripeno / stripes_per_object;       // which object set

uint64_t objectno = objectsetno * stripe_count + stripepos;  // object id



         object_t oid = file_object_t(ino, objectno);

         ObjectExtent *ex = 0;//主要由下面的兩個引數組成

        ex->oloc = objecter->osdmap->file_to_object_locator(*layout);

               ex->oid = oid;



               object_locator_t file_to_object_locator(const ceph_file_layout& layout) const {

              return object_locator_t(layout.fl_pg_pool, layout.fl_pg_preferred);

              }

Objecter.h

void sg_read_trunc(vector& extents, snapid_t snap, bufferlist *bl, int flags,

               uint64_t trunc_size, __u32 trunc_seq, Context *onfinish)

     //對集合中的每個ObjectExtent進行處理

Objecter.h tid_t read_trunc(const object_t& oid, const object_locator_t& oloc,

          uint64_t off, uint64_t len, snapid_t snap, bufferlist *pbl, int flags,

          uint64_t trunc_size, __u32 trunc_seq,

          Context *onfinish,

          eversion_t *objver = NULL, ObjectOperation *extra_ops = NULL)      



     //該函式發出請求

Objecter.h tid_t read_trunc(const object_t& oid, const object_locator_t& oloc,

          uint64_t off, uint64_t len, snapid_t snap, bufferlist *pbl, int flags,

          uint64_t trunc_size, __u32 trunc_seq,

          Context *onfinish,

          eversion_t *objver = NULL, ObjectOperation *extra_ops = NULL)

2.2 OSD的op_tp執行緒處理資料讀取
處理的過程如下:

OpWQ的 void process(PG *pg) 到 osd->dequeueop(pg);中的程式碼如下:

if (op->get_type() == CEPH_MSG_OSD_OP) {

if (op_is_discardable((MOSDOp*)op))

  op->put();

else

  pg->do_op((MOSDOp*)op); // do it now

àvoid ReplicatedPG::do_op(MOSDOp *op)

à ReplicatedPG::do_op(MOSDOp *op)

à prepare_transaction(ctx); int ReplicatedPG::prepare_transaction(OpContext *ctx)

àint result = do_osd_ops(ctx, ctx->ops, ctx->outdata);

int ReplicatedPG::do_osd_ops(OpContext *ctx, vector& ops, bufferlist& odata)

     該函式的case CEPH_OSD_OP_READ:   分支

              int r = osd->store->read(coll, soid, op.extent.offset, op.extent.length, bl);

     可以看到最終到了FileStore物件中。

     int FileStore::read(coll_t cid, const hobject_t& oid,

                uint64_t offset, size_t len, bufferlist& bl)

     read函式中主要呼叫了int fd = lfn_open(cid, oid, O_RDONLY);

     我們可以看到定位一個物件需要的引數:

int FileStore::lfn_open(coll_t cid, const hobject_t& oid, int flags, mode_t mode)

       r = get_index(cid, &index);

     get_index的過程:在當前正在使用的index集合中判斷是否正在被使用,如果被使用需要等待釋放,否則建立索引。

               int IndexManager::get_index(coll_t c, const char *path, Index *index) {

              Mutex::Locker l(lock);

              while (1) {

       /// Currently in use CollectionIndices

// map > col_indices;

              if (!col_indices.count(c)) {

              int r = build_index(c, path, index);

                     if (r < 0)

                        return r;

              (*index)->set_ref(*index);

                     col_indices[c] = (*index);

                  break;

            }else {

                  cond.Wait(lock);

            }

              }

              return 0;

}

建立索引的過程:

int IndexManager::build_index(coll_t c, const char *path, Index *index) {

*index = Index(new FlatIndex(path),

                    RemoveOnDelete(c, this));

或者:

*index = Index(new HashIndex(path, g_conf->filestore_merge_threshold,

                                    g_conf->filestore_split_multiple, version),

                    RemoveOnDelete(c, this));

     這裡coll_t的定義為:

     class coll_t {

public:

const static coll_t META_COLL;

const static coll_t TEMP_COLL;

coll_t()

: str("meta")

{ }

std::string str;

coll_t實際上代表了一個目錄,目錄中是物件的集合。HashIndex在一定的條件下會拆分或者合併其擁有的子集合。

       r = index->lookup(oid, &path, &exist);

       r = ::open(path->path(), flags, mode);

3 OSD中的日誌、事務
這裡對物件的寫或者修改操作最終會交給FileStore物件處理,提交到該物件的巢狀類OpSequencer中的連結串列q中,日誌的序列號加入到連結串列jq中。在flush時,根據日誌的序列號保證了日誌未flush前,操作不會寫入磁碟。

在一個操作的處理過程中,最終由PG發出處理該動作。上述的序列關係記錄在PG物件中的ObjectStore::Sequencer osr;中。

3.1 對於物件的操作的處理過程
對object的操作最終由PG類進行處理,過程如下:

ReplicatedPG::do_op

1 如果是CEPH_OSD_FLAG_PGOP,由do_pg_op處理返回。

2 如果該pg狀態為: finalizing_scrub並且有寫操作(CEPH_OSD_FLAG_WRITE),加入到waiting_for_active。

3 如果該物件在missing列表中:is_missing_object,加入等待列表wait_for_missing_object。

4 如果該物件在degraded列表並且有寫操作,加入對一個的等待列表wait_for_degraded_object。

5 從磁碟或者快取中讀取物件的屬性資訊:find_object_context

6 如果失敗,不能找到,將操作加入到miss等待列表:wait_for_missing_object

7 根據得到的物件的資訊判斷,如果是讀請求並且是lost狀態,返回出錯

8 根據pg的mode判斷該osd_op的合法性,如果不成功加入到mode的等待列表中

9 遍歷該op中的ops,獲得每個操作涉及的物件的資訊,加入集合src_obc中。

10 如果是write操作,相應的檢查snap version

11 通過加讀鎖,進行操作prepare_transaction,操作完後解除讀鎖。ObjectContext:: ondisk_read_lock

該函式中如果是讀操作讀取該物件的資訊

寫操作只進行基本的檢查

     ReplicatedPG::prepare_transaction 執行操作,此時資料、日誌都在記憶體中。

1> do_osd_ops

int ReplicatedPG::do_osd_ops(OpContext *ctx, vector& ops,bufferlist& odata)

CEPH_OSD_OP_WRITE分支:

/**將資料寫入到事務快取中*/

t.write(coll, soid, op.extent.offset, op.extent.length, nbl);

2> do_osd_op_effects

3> 如果是讀請求返回

4> 修改操作新增日誌

ctx->log.push_back(Log::Entry(logopcode, soid, ctx->at_version, old_version, ctx->reqid, ctx->mtime));

12 準備迴應MOSDOpReply,如果是read操作或者是上一步出錯,迴應。

13 執行到這裡只能是寫操作。

append_log(ctx->log, pg_trim_to, ctx->local_t);

     PG::append_log

1> 將ctx中的log加入到事務ctx->local_t中的快取中。

建立新的RepGather,rep_op,並執行:

14 向該pg的副本傳送此次請求:

ReplicatedPG::issue_repop

     向PG的acting列表中的osd傳送訊息MOSDSubOp。

當其他的osd收到該請求後:

1> OSD::handle_sub_op此時只是將該op壓入佇列中

2> 在函式OSD::dequeue_op處理該請求:

     ReplicatedPG::do_sub_op

               ReplicatedPG::sub_op_modify         ------------------------此時執行對osd的資料修改動作

將修改操作作為事務提交到佇列中:

int r = osd->store->queue_transactions(&osr, rm->tls, onapply, oncommit);

這裡將該操作提交給了兩個執行緒池的,第一個執行緒池負責將日誌寫入磁碟。第二個負責執行該操作。如果沒有使用btrfs檔案系統作為osd儲存,會先進行日誌的過程,即將操作加入到日誌佇列中,當日志寫入磁碟後,通過回撥將操作加入到操作佇列中。

這裡註冊的兩個回撥:

Context *oncommit = new C_OSD_RepModifyCommit(rm); 當日志寫入磁碟後被呼叫

Context *onapply = new C_OSD_RepModifyApply(rm); 當該操作被處理後被呼叫

ReplicatedPG::sub_op_modify_applied

     MOSDSubOpReply   CEPH_OSD_FLAG_ACK

ReplicatedPG::sub_op_modify_commit

     MOSDSubOpReply   CEPH_OSD_FLAG_ONDISK

當收到其他的osd的迴應時:

OSD::handle_sub_op_reply

ReplicatedPG::do_sub_op_reply

sub_op_modify_reply(r);

ReplicatedPG::repop_ack

     如果是CEPH_OSD_FLAG_ONDISK,則從下面集合中刪除:

               repop->waitfor_disk.erase(fromosd);

                                 repop->waitfor_ack.erase(fromosd);

                        否則:

                            repop->waitfor_ack.erase(fromosd);

                        每收到一次ack,都會呼叫函式eval_repop

15 eval_repop

當已經收到其他的osd迴應時(程式碼中的註釋的意思):

     apply_repop 執行此次動作。執行的過程與其他的osd執行過程類似。該函式將  repop->applying = true;

     多註冊了一個回撥:ReplicatedPG::C_OSD_OndiskWriteUnlock::finish

當repop->waitfor_disk.empty()為空時:

此時向請求的發出者回應:MOSDOpReply CEPH_OSD_FLAG_ACK | CEPH_OSD_FLAG_ONDISK

當repop->waitfor_ack.empty()為空時:

向此次請求的發出者回應:MOSDOpReply CEPH_OSD_FLAG_ACK

此時寫入的資料已經可讀,但未commit

注意,兩個迴應中,第一個如果迴應了就包含了第二個。兩種迴應只存在一個。

當repop->waitfor_ack.empty() && repop->waitfor_disk.empty()兩者都為空時,將此次的repop操作從佇列中刪除。

3.2 修改操作的處理
可以看到對於修改操作,需要通過日誌、事務進行處理,將操作加入到日誌,事務的過程為:

FileStore::queue_transactions的過程:

這裡將該操作提交給了兩個執行緒池的,第一個執行緒池負責將日誌寫入磁碟。第二個負責執行該操作。如果沒有使用btrfs檔案系統作為osd儲存,會先進行日誌的過程,即將操作加入到日誌佇列中,當日志寫入磁碟後,通過回撥將操作加入到操作佇列中。

當日志可寫時:

1 建立FileStore:: Op op = build_op(tls, onreadable, onreadable_sync);

2 op_queue_reserve_throttle(o);

     ==> FileStore::_op_queue_reserve_throttle 當佇列的運算元過多,或者佇列中操作資料長度過大,阻塞等待。在某個操作處理結束後,_void_process_finish會喚醒。

3 o->op = op_submit_start(); ==>ops_submitting.push_back 獲得操作的序列號

4如果m_filestore_journal_parallel,即這裡將該操作同時加入到日誌佇列和FileStore的操作佇列中。

1>opjournal_transactions(o->tls, o->op, ondisk); 日誌提交到日誌佇列的過程

     如果日誌可寫

journal->submit_entry(op, tbl, data_align, onjournal);

                        ->completions.push_back(onjournal)

                        -> writeq.push_back (write_item(seq, e, alignment))

     否則加入等待佇列:commit_waiters[op].push_back(onjournal);

2>queue_op(osr, o);

     _op_apply_start(o->op);àJournalingObjectStore::_op_apply_start

               當不是blocked狀態時,沒有處理,如果是blocked狀態,等待被喚醒

     osr->queue(o);          加入到OpSequencer的佇列q中

op_wq.queue(osr); 此時將該操作加入到FileStore物件的op_wq佇列中。

5如果m_filestore_journal_writeahead(當btrfs沒有enable時為true)


            
           

相關推薦

ceph原始碼解析--osd

Ceph分散式檔案系統的程式碼分析的文章網上是比較少的,本團隊成員對ceph做過詳細的程式碼閱讀,包括mds、osd、client等模組,但是缺少條理清晰的文件總結。暫且先放上OSD的程式碼分析,等後續整理陸續放上其它模組的。 1 OSD的基本結構 主要的類,涉及的執行

dubbo原始碼解析——概要

這次原始碼解析借鑑《肥朝》前輩的dubbo原始碼解析,進行原始碼學習。總結起來就是先總體,後區域性.也就是先把需要注意的概念先丟擲來,把整體架構圖先畫出來.讓讀者拿著"地圖"跟著我的腳步,並且每一步我都

ss-libev 原始碼解析local(2):ss_local和socks5客戶端握手

上一篇說到ss-libev建立listen_ctx_t物件用於監聽客戶端連線,呼叫accept_cb處理來自客戶端的新連線,建立server_t物件用於處理和客戶端之間的互動。本篇分析來自客戶端的SOCK5連線的建立以及傳輸資料的過程。 首先,回憶一下使用ne

Multibit原始碼解析學習之---傳送比特幣

/**package org.multibit.viewsystem.swing.action; * Complete the transaction to work out the fee) and then show the send bitcoin confirm dialog.

史上最全Universal-Image-Loader原始碼解析————快取

背景 在我寫下這篇部落格的時候,我還是一名二本學校計算機專業大四的應屆畢業生,自學Android開發快兩年了,這兩年的時間裡面,其實真的是感慨萬千,兩年的時間裡面,Android的很多事情也都見識過了,其實Android對於新手入門在就業方面是相當不友好的事情。都說第一個吃螃蟹的人最

ss-libev 原始碼解析local(5):ss-local之remote_send_cb

remote_send_cb這個回撥函式的工作是將從客戶端收取來的資料轉發給ss-server。在之前閱讀server_recv_cb程式碼時可以看到,在STAGE_STREAM階段有幾種可能都會開啟remote->fd的寫事件的監聽,從而當有寫事件觸發時

ss-libev 原始碼解析udp (2)

UDP relay的程式碼基本都在udprelay.c中,無論ss-local還是ss-server的程式碼都在一起,使用巨集MODULE_LOCAL,MODULE_REMOTE等區分開。程式碼雖然不是很多,但是由於ss-local和ss-server以及ss-

Andfix熱修復框架原理及原始碼解析-上

1.不知道如何使用的同學,建議看看我上一篇寫的介紹熱補丁和Andfix的使用,這樣你才有一個大概的框架。通過使用Andfix,其實我們心中會有一個大概的輪廓,它的工作原理,大概就是,所謂的補丁檔

ElementUI 簡要原始碼解析——Basic

Layout 佈局 row 佈局元件中的父元件,用於控制子元件。很簡單的一個佈局標籤,主要通過 justify 和 align 控制子元素的對齊方式,使用 render 函式通過傳入的 tag 屬性控制生成的標籤。 在這裡推薦學習下 render 函式和 JSX 的寫法,因為之後比較複雜的元件都是通過 ren

myBatis原始碼解析-日誌(1)

上半年在進行知識儲備,下半年爭取寫一點好的部落格來記錄自己原始碼之路。在學習原始碼的路上也掌握了一些設計模式,可所謂一舉兩得。本次打算寫Mybatis的原始碼解讀。 準備工作 1. 下載mybatis原始碼 下載地址:https://github.com/mybatis/mybatis-3  2.

myBatis原始碼解析-快取(2)

上一章分析了mybatis的原始碼的日誌模組,像我們經常說的mybatis一級快取,二級快取,快取究竟在底層是怎樣實現的。此次開始分析快取模組 1. 原始碼位置,mybatis原始碼包位於org.apache.ibatis.cache下,如圖 2. 先從org.apache.ibatis.cache下的cac

myBatis原始碼解析-資料來源(3)

前言:我們使用mybatis時,關於資料來源的配置多使用如c3p0,druid等第三方的資料來源。其實mybatis內建了資料來源的實現,提供了連線資料庫,池的功能。在分析了快取和日誌包的原始碼後,接下來分析mybatis中的資料來源實現。 類圖:mybatis中關於資料來源的原始碼包路徑如下:  

myBatis原始碼解析-反射(4)

前沿 前文分析了mybatis的日誌包,快取包,資料來源包。原始碼實在有點難頂,在分析反射包時,花費了較多時間。廢話不多說,開始原始碼之路。 反射包feflection在mybatis路徑如下:      原始碼解析 1  property包-主要對類的屬性進行操作的工

文章徹底讀懂HashMap之HashMap原始碼解析(下)

put函式原始碼解析 //put函式入口,兩個引數:key和value public V put(K key, V value) { /*下面分析這個函式,注意前3個引數,後面 2個引數這裡不太重要,因為所有的put 操作後面的2個引數預設值都一樣 */

文章徹底讀懂HashMap之HashMap原始碼解析(上)

就身邊同學的經歷來看,HashMap是求職面試中名副其實的“明星”,基本上每一加公司的面試多多少少都有問到HashMap的底層實現原理、原始碼等相關問題。 在秋招面試準備過程中,博主閱讀過很多關於HashMap原始碼分析的文章,漫長的拼湊式閱讀之後,博主沒有看到過

.NET Core實戰專案之CMS 第三章 入門-原始碼解析配置檔案及依賴注入

作者:依樂祝 原文連結:https://www.cnblogs.com/yilezhu/p/9998021.html 寫在前面 上篇文章我給大家講解了ASP.NET Core的概念及為什麼使用它,接著帶著你一步一步的配置了.NET Core的開發環境並建立了一個ASP.NET Core的mvc專

Dubbo原始碼解析之consumer呼叫

閱讀須知 dubbo版本:2.6.0 spring版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 在分析consumer初始化時,我們看到了關聯服務引用建立代理的過程,最終會呼叫JavassistProxyFactory的getP

rxJava和rxAndroid原始碼解析系列四之subscribeOn和observeOn的理解(學習終結)

本篇文章主要解決subscribeOn和observeOn這兩個方法為什麼subscribeOn只有一次有效果,observeOn切換多次回撥的都有效果。 不知道朋友有沒有看過rxandroid的原始碼,如果看過的話,就會迎刃而解,沒什麼疑慮啦。沒看過原始碼的朋友,可以看看我這個系列的前幾篇文章

Dubbo原始碼解析之provider呼叫

閱讀須知 dubbo版本:2.6.0 spring版本:4.3.8 文章中使用/* */註釋的方法會做深入分析 正文 在之前的原始碼分析文章中,我們看到了dubbo用netty作為底層的網路通訊框架,熟悉netty的同學應該知道,使用netty時我們會使用它

JDK8 HashMap原始碼解析,一文章徹底讀懂HashMap

    在秋招面試準備中博主找過很多關於HashMap的部落格,但是秋招結束後回過頭來看,感覺沒有一篇全面、通俗易懂的講解HashMap文章(可能是博主沒有找到),所以在秋招結束後,寫下了這篇文章,盡最大的努力把HashMap原始碼講解的通俗易懂,並且儘量涵蓋面試中HashM