成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

剝開比原看代碼08:比原的Dashboard是怎么做出來(lái)的?

CHENGKANG / 973人閱讀

摘要:所以本文本來(lái)是想去研究一下,當(dāng)別的節(jié)點(diǎn)把區(qū)塊數(shù)據(jù)發(fā)給我們之后,我們應(yīng)該怎么處理,現(xiàn)在換成研究比原的是怎么做出來(lái)的。進(jìn)去后會(huì)看到大量的與相關(guān)的配置。它的功能主要是為了在訪問(wèn)與的函數(shù)之間增加了一層轉(zhuǎn)換。

作者:freewind

比原項(xiàng)目倉(cāng)庫(kù):

Github地址:https://github.com/Bytom/bytom

Gitee地址:https://gitee.com/BytomBlockc...

在前面的幾篇文章中,我們一直在研究如何與一個(gè)比原節(jié)點(diǎn)建立連接,并且從它那里請(qǐng)求區(qū)塊數(shù)據(jù)。然而我很快就遇到了瓶頸。

因?yàn)楫?dāng)我處理拿到的區(qū)塊數(shù)據(jù)時(shí),發(fā)現(xiàn)我已經(jīng)觸及到了比原鏈的核心,即區(qū)塊鏈的數(shù)據(jù)結(jié)構(gòu)以及分叉的處理。如果不能完全理解這一塊,就沒有辦法正確的處理區(qū)塊數(shù)據(jù)。然而它涉及的內(nèi)容太多了,在短時(shí)間之內(nèi)把它理解透徹是一件非常困難的事情。

之前我的做法就好像我想了解一個(gè)城市,于是沿著一條路從外圍向市中心進(jìn)發(fā)。前面一直很順利,但等到了市中心時(shí),發(fā)現(xiàn)這里人多路雜,有點(diǎn)迷失了。在這種情況下,我覺得我應(yīng)該暫停研究核心,而是從另外一條路開始,由外向內(nèi)再來(lái)一遍。因?yàn)樵谛羞M(jìn)的過(guò)程中,我可以慢慢的積累更多的知識(shí),讓自己處于學(xué)習(xí)區(qū)而非恐慌區(qū)。這條路的終點(diǎn)也將是觸及到核心,但是不深入進(jìn)去。這樣的話,等我多走了幾條路之后,積累的知識(shí)夠了,再研究核心就不會(huì)覺得迷茫了。

所以本文本來(lái)是想去研究一下,當(dāng)別的節(jié)點(diǎn)把區(qū)塊數(shù)據(jù)發(fā)給我們之后,我們應(yīng)該怎么處理,現(xiàn)在換成研究比原的Dashboard是怎么做出來(lái)的。為什么選擇這個(gè)呢?因?yàn)樗浅R砸环N非常直觀的方式,展示了比原向我們提供的各種信息和功能。在本文中,我們并不過(guò)多的講解它上面的功能,而是把關(guān)注點(diǎn)放在比原到底是如何在代碼層面上實(shí)現(xiàn)了這樣的一個(gè)Dashboard。它上面的功能,將會(huì)在以后慢慢研究。

我們今天的問(wèn)題是“比原的Dashboard是怎么做出來(lái)的”,但是這個(gè)問(wèn)題有點(diǎn)大,并且不夠具體,所以我們還是跟以前一樣,先來(lái)把它細(xì)分一下:

我們?cè)鯓釉诒仍袉⒂肈ashboard功能?

Dashboard中提供了哪些信息和功能?

比原是如何實(shí)現(xiàn)了http服務(wù)器?

Dashboard使用了什么樣的前端框架?

Dashboard上面的數(shù)據(jù),是以什么樣的方式從后臺(tái)拿到的?

我們下面開始一一探討。

我們?cè)鯓釉诒仍袉⒂肈ashboard功能?

當(dāng)我們使用bytomd node啟動(dòng)比原節(jié)點(diǎn)的時(shí)候,不需要任何配置,它就會(huì)自動(dòng)啟用Dashboard功能,并且會(huì)在瀏覽器中打開頁(yè)面,非常方便。

如果是第一次運(yùn)行,還沒有創(chuàng)建過(guò)帳戶,它會(huì)提示我們創(chuàng)建一個(gè)帳戶及相關(guān)的私鑰:

我們可以通過(guò)填寫帳戶別名、密鑰別名和相應(yīng)的密碼來(lái)創(chuàng)建,或者點(diǎn)擊下面的"Restore wallet"來(lái)恢復(fù)之前的帳號(hào)(如果之前備份過(guò)的話):

點(diǎn)擊"Register"后,就會(huì)創(chuàng)建成功,并進(jìn)入管理頁(yè)面:

注意它的地址是:http://127.0.0.1:9888/dashboard

如果我們查看配置文件config.toml,可以在其中看到它的身影:

fast_sync = true
db_backend = "leveldb"
api_addr = "0.0.0.0:9888"
chain_id = "solonet"
[p2p]
laddr = "tcp://0.0.0.0:46658"
seeds = ""

注意其中的api_addr,就是dashboard以及web-api的地址。比原在啟動(dòng)之后,其BaseConfig.ApiAddress會(huì)從配置文件中取到相應(yīng)的值:

config/config.go#L41-L85

type BaseConfig struct {
    // ...
    ApiAddress string `mapstructure:"api_addr"`
    // ...
}

然后在啟動(dòng)時(shí),比原的web api以及dashboard會(huì)使用該地址,并且在瀏覽器中打開dashboard。

然而此處有一個(gè)奇怪的問(wèn)題,就是不論這里的值是什么,瀏覽器總是打開http://localhost:9888這個(gè)地址。為什么呢?因?yàn)樗鼘懰涝诹舜a中。

在代碼中,http://localhost:9888一共出現(xiàn)在了三個(gè)地方,一個(gè)是用來(lái)表示dashboard的訪問(wèn)地址,位于node/node.go中:

node/node.go#L33-L37

const (
    webAddress               = "http://127.0.0.1:9888"
    expireReservationsPeriod = time.Second
    maxNewBlockChSize        = 1024
)

這里的webAddress,只在從代碼中打開瀏覽器顯示dashboard時(shí)使用:

node/node.go#L153-L159

func lanchWebBroser() {
    log.Info("Launching System Browser with :", webAddress)
    if err := browser.Open(webAddress); err != nil {
        log.Error(err.Error())
        return
    }
}

比原通過(guò)"github.com/toqueteos/webbrowser"這個(gè)第三方的庫(kù),可以在節(jié)點(diǎn)啟動(dòng)的時(shí)候,調(diào)用系統(tǒng)默認(rèn)的瀏覽器,并打開指定的網(wǎng)址,方便了用戶。(注意這段代碼中有不少錯(cuò)別字,比如lanch、broser,已在后續(xù)版本中修正了)

另一個(gè)地方,是用于bytomcli這個(gè)命令行工具的,只是奇怪的是它放在了util/util.go下面:

util/util.go#L26-L28

var (
    coreURL = env.String("BYTOM_URL", "http://localhost:9888")
)

為什么說(shuō)它是屬于bytomcli的呢?因?yàn)檫@個(gè)coreURL最終被用在util包下的一個(gè)ClientCall(...)函數(shù)中,用于從代碼中向指定的web api發(fā)送請(qǐng)求,并使用其回復(fù)信息。但是這個(gè)方法在bytomcli所在的包使用。如果是這樣的話,coreURL及相關(guān)的函數(shù),應(yīng)該移到bytomcli包里才對(duì)。

第三個(gè)地方,跟第二個(gè)非常像,但是位于tools/sendbulktx/core/util.go中,它是用于另一個(gè)命令行工具sendbulktx的:

tools/sendbulktx/core/util.go#L26-L28

var (
    coreURL = env.String("BYTOM_URL", "http://localhost:9888")
)

一模一樣,對(duì)吧。其實(shí)不光是這里,還有一堆相關(guān)的方法和函數(shù),也是一模一樣的,一看就是跟第二處互相復(fù)制過(guò)來(lái)的。

關(guān)于這里的問(wèn)題,我提了兩個(gè)issue:

dashboard和web api的地址寫在配置文件config.toml中,但是同時(shí)寫死在代碼中:這里在實(shí)現(xiàn)上的確是有一定難度的,原因是在配置文件中,寫的是0.0.0.0:9998,但是從瀏覽器或者命令行工具中去訪問(wèn)時(shí),需要使用一個(gè)具體的ip(而不是0.0.0.0),否則某些功能會(huì)不正常。另外,在后面的代碼分析處會(huì)看到,除了配置文件中的這個(gè)地址,比原還會(huì)優(yōu)先從環(huán)境變量中取得LISTEN所對(duì)應(yīng)的地址web api的地址。所以這里需要更多的研究才能正確修復(fù)。

與讀取webapi相關(guān)的代碼出現(xiàn)大量重復(fù):官方解釋說(shuō)sendbulktx這個(gè)工具在未來(lái)將從bytom項(xiàng)目中獨(dú)立出去,所以代碼是重復(fù)的,如果是這樣的話,可以接受。

Dashboard中提供了哪些信息和功能?

下面我們快速過(guò)一遍比原的Dashboard提供了哪些信息和功能。由于在本文中,我們關(guān)注的重點(diǎn)不是這些具體的功能,所以會(huì)不會(huì)細(xì)究。另外,前面剛創(chuàng)建好的帳號(hào)里,很多數(shù)據(jù)都是沒有的,為了展示方便,我事先做了一些數(shù)據(jù)。

首先是密鑰:

這里顯示了當(dāng)前有幾個(gè)密鑰,其別名是什么,并且顯示出來(lái)了主公鑰。我們可以點(diǎn)擊右上角的“新建”按鈕創(chuàng)建多個(gè)密鑰,但是這里不再展示。

帳戶:

資產(chǎn):

默認(rèn)只定義了BTM這一種資產(chǎn),可以通過(guò)“新建”按鈕增加多種資產(chǎn)。

余額:

看起來(lái)我還是相當(dāng)有錢的(可惜不能用)。

交易:

展示了多筆交易,實(shí)際上是在本機(jī)挖礦挖出來(lái)的。由于挖礦出來(lái)的BTM是由系統(tǒng)直接轉(zhuǎn)到我們的帳戶上的,所以也可以看作是一種交易。

創(chuàng)建交易:

我們也可以像這樣自己創(chuàng)建交易,把我們持有的某種資產(chǎn)(比如BTM)轉(zhuǎn)到另一個(gè)地址。

未花費(fèi)輸出:

簡(jiǎn)單的理解就是與我相關(guān)的每一筆交易都被記錄下來(lái),有輸入和輸出部分,其中的輸出可能又是另一個(gè)交易的輸入。這里顯示的是還沒有花費(fèi)掉的輸出(可以根據(jù)它來(lái)計(jì)算我當(dāng)前到底還剩下多少余額)

查看核心狀態(tài):

定義訪問(wèn)控制:

備份和還原操作:

另外每個(gè)頁(yè)面左側(cè)欄的下面,還有關(guān)于連接的鏈的類型(此處為solonet),以及同步情況和與當(dāng)前節(jié)點(diǎn)連接的其它節(jié)點(diǎn)數(shù)。

這里展示的信息和功能我們還不需要細(xì)究,但是這里出現(xiàn)的名詞卻是要留意的,因?yàn)樗鼈兌际潜仍暮诵母拍?。等我們以后研究比原?nèi)部區(qū)塊鏈核心功能的時(shí)候,實(shí)際上都是圍繞著它們來(lái)的。這里的每一個(gè)概念,可能都需要一到多篇文章專門討論。

我們?cè)诮裉礻P(guān)注的是技術(shù)實(shí)現(xiàn)層面,下面我們要開始進(jìn)入代碼時(shí)間了。

比原是如何實(shí)現(xiàn)了http服務(wù)器?

首先讓我們從比原節(jié)點(diǎn)啟動(dòng)開始,一直找到啟動(dòng)http服務(wù)的地方:

cmd/bytomd/main.go#L54-L57

func main() {
    cmd := cli.PrepareBaseCmd(commands.RootCmd, "TM", os.ExpandEnv(config.DefaultDataDir()))
    cmd.Execute()
}

cmd/bytomd/commands/run_node.go#L41-L54

func runNode(cmd *cobra.Command, args []string) error {
    // Create & start node
    n := node.NewNode(config)
    if _, err := n.Start(); err != nil {
    // ..
}

node/node.go#L169-L180

func (n *Node) OnStart() error {
    // ...
    n.initAndstartApiServer()
    // ...
}

很快找到了,initAndstartApiServer

node/node.go#L161-L167

func (n *Node) initAndstartApiServer() {
    // 1.
    n.api = api.NewAPI(n.syncManager, n.wallet, n.txfeed, n.cpuMiner, n.miningPool, n.chain, n.config, n.accessTokens)

    // 2. 
    listenAddr := env.String("LISTEN", n.config.ApiAddress)
    env.Parse()

    // 3.
    n.api.StartServer(*listenAddr)
}

可以看到,該方法分成了三部分:

通過(guò)傳入大量的參數(shù),來(lái)構(gòu)造一個(gè)API對(duì)象。進(jìn)去后會(huì)看到大量的與url相關(guān)的配置。

先從環(huán)境中取得LISTEN對(duì)應(yīng)的值,如果沒有的話,再使用config.toml中指定的api_addr值,作為api服務(wù)的入口地址

真正啟動(dòng)服務(wù)

由于2比較簡(jiǎn)單,所以我們下面將仔細(xì)分析1和3.

先找到1處所對(duì)應(yīng)的api.NewAPI方法:

api/api.go#L143-L157

func NewAPI(sync *netsync.SyncManager, wallet *wallet.Wallet, txfeeds *txfeed.Tracker, cpuMiner *cpuminer.CPUMiner, miningPool *miningpool.MiningPool, chain *protocol.Chain, config *cfg.Config, token *accesstoken.CredentialStore) *API {
    api := &API{
        sync:          sync,
        wallet:        wallet,
        chain:         chain,
        accessTokens:  token,
        txFeedTracker: txfeeds,
        cpuMiner:      cpuMiner,
        miningPool:    miningPool,
    }
    api.buildHandler()
    api.initServer(config)

    return api
}

它主要就是把傳進(jìn)來(lái)的各參數(shù)拿住,供后面使用。然后就是api.buildHandler來(lái)配置各個(gè)功能點(diǎn)的路徑和處理函數(shù),以及用api.initServer來(lái)初始化服務(wù)。

進(jìn)入api.buildHandler()。這個(gè)方法有點(diǎn)長(zhǎng),把它分成幾部分來(lái)講解:

api/api.go#L164-L244

func (a *API) buildHandler() {
    walletEnable := false
    m := http.NewServeMux()

看來(lái)http服務(wù)使用的是Go自帶的http包。

向下是,當(dāng)用戶的錢包功能沒有禁用的話,就會(huì)配置與錢包相關(guān)的各功能點(diǎn)(比如帳號(hào)、交易、密鑰等):

    if a.wallet != nil {
        walletEnable = true

        m.Handle("/create-account", jsonHandler(a.createAccount))
        m.Handle("/list-accounts", jsonHandler(a.listAccounts))
        m.Handle("/delete-account", jsonHandler(a.deleteAccount))

        m.Handle("/create-account-receiver", jsonHandler(a.createAccountReceiver))
        m.Handle("/list-addresses", jsonHandler(a.listAddresses))
        m.Handle("/validate-address", jsonHandler(a.validateAddress))

        m.Handle("/create-asset", jsonHandler(a.createAsset))
        m.Handle("/update-asset-alias", jsonHandler(a.updateAssetAlias))
        m.Handle("/get-asset", jsonHandler(a.getAsset))
        m.Handle("/list-assets", jsonHandler(a.listAssets))

        m.Handle("/create-key", jsonHandler(a.pseudohsmCreateKey))
        m.Handle("/list-keys", jsonHandler(a.pseudohsmListKeys))
        m.Handle("/delete-key", jsonHandler(a.pseudohsmDeleteKey))
        m.Handle("/reset-key-password", jsonHandler(a.pseudohsmResetPassword))

        m.Handle("/build-transaction", jsonHandler(a.build))
        m.Handle("/sign-transaction", jsonHandler(a.pseudohsmSignTemplates))
        m.Handle("/submit-transaction", jsonHandler(a.submit))
        m.Handle("/estimate-transaction-gas", jsonHandler(a.estimateTxGas))

        m.Handle("/get-transaction", jsonHandler(a.getTransaction))
        m.Handle("/list-transactions", jsonHandler(a.listTransactions))

        m.Handle("/list-balances", jsonHandler(a.listBalances))
        m.Handle("/list-unspent-outputs", jsonHandler(a.listUnspentOutputs))

        m.Handle("/backup-wallet", jsonHandler(a.backupWalletImage))
        m.Handle("/restore-wallet", jsonHandler(a.restoreWalletImage))
    } else {
        log.Warn("Please enable wallet")
    }

錢包功能默認(rèn)是啟用的,用戶如何才能禁用它呢?方法是在配置文件config.toml中,加上這一節(jié)代碼:

[wallet]
disable = true

在前面的代碼中,在配置功能點(diǎn)時(shí),使用了大量的m.Handle("/create-account", jsonHandler(a.createAccount))這樣的代碼,它是什么意思呢?

/create-account:該功能的路徑,比如對(duì)于這個(gè),用戶需要在瀏覽器或者命令行中,使用地址http://localhost:9888/create-account來(lái)訪問(wèn)

a.createAccount:用于處理用戶的訪問(wèn),比如拿到用戶提供的數(shù)據(jù),處理完后再返回某個(gè)數(shù)據(jù)給用戶,會(huì)在下面詳解

jsonHandler:是一個(gè)中間層,把用戶發(fā)送的JSON數(shù)據(jù)轉(zhuǎn)成第2步handler需要的Go類型參數(shù),或者把2返回的Go數(shù)據(jù)轉(zhuǎn)成JSON給用戶

m.Handle(path, handler):用來(lái)把功能點(diǎn)路徑和相應(yīng)的處理函數(shù)對(duì)應(yīng)起來(lái)

這里先看第3步中的jsonHandler的代碼:

api/api.go#L259-L265

func jsonHandler(f interface{}) http.Handler {
    h, err := httpjson.Handler(f, errorFormatter.Write)
    if err != nil {
        panic(err)
    }
    return h
}

它里面用到了httpjson,它是比原代碼中提供的一個(gè)包,位于net/http/httpjson 。它的功能主要是為了在http訪問(wèn)與Go的函數(shù)之間增加了一層轉(zhuǎn)換。通常用戶通過(guò)http與api交互的時(shí)候,發(fā)送和接收的都是JSON數(shù)據(jù),而我們?cè)诘?步的handler中定義的是Go函數(shù),通過(guò)httpjson,可以在兩者之間自動(dòng)轉(zhuǎn)換,使得我們?cè)趯慓o代碼的時(shí)候,不需要考慮JSON以及http協(xié)議相關(guān)的問(wèn)題。相應(yīng)的,為了與jsonhttp配合使用,第2步中的handler在格式上也會(huì)有一些要求,詳情可參見這里的詳細(xì)注釋:net/http/httpjson/doc.go#L3-L40 。由于httpjson所涉及的代碼還比較多,這里就不詳述,以后有機(jī)會(huì)專開一篇。

然后我們?cè)倏吹?步的a.createAccount的代碼:

api/accounts.go#L16-L30

func (a *API) createAccount(ctx context.Context, ins struct {
    RootXPubs []chainkd.XPub `json:"root_xpubs"`
    Quorum    int            `json:"quorum"`
    Alias     string         `json:"alias"`
}) Response {
    acc, err := a.wallet.AccountMgr.Create(ctx, ins.RootXPubs, ins.Quorum, ins.Alias)
    if err != nil {
        return NewErrorResponse(err)
    }

    annotatedAccount := account.Annotated(acc)
    log.WithField("account ID", annotatedAccount.ID).Info("Created account")

    return NewSuccessResponse(annotatedAccount)
}

這個(gè)函數(shù)的內(nèi)容我們?cè)谶@里不細(xì)究,需要注意的反而是它的格式,因?yàn)榍懊嬲f(shuō)了,它需要跟jsonHandler配合使用。格式的要求大概就是,第一個(gè)參數(shù)是Context,第二個(gè)參數(shù)是可以從JSON數(shù)據(jù)轉(zhuǎn)換過(guò)來(lái)的參數(shù),返回值是一個(gè)Response以及一個(gè)Error,但是這四個(gè)又全部是可選的。

讓我們回到api.buildHandler(),繼續(xù)往下:

    m.Handle("/", alwaysError(errors.New("not Found")))
    m.Handle("/error", jsonHandler(a.walletError))

    m.Handle("/create-access-token", jsonHandler(a.createAccessToken))
    m.Handle("/list-access-tokens", jsonHandler(a.listAccessTokens))
    m.Handle("/delete-access-token", jsonHandler(a.deleteAccessToken))
    m.Handle("/check-access-token", jsonHandler(a.checkAccessToken))

    m.Handle("/create-transaction-feed", jsonHandler(a.createTxFeed))
    m.Handle("/get-transaction-feed", jsonHandler(a.getTxFeed))
    m.Handle("/update-transaction-feed", jsonHandler(a.updateTxFeed))
    m.Handle("/delete-transaction-feed", jsonHandler(a.deleteTxFeed))
    m.Handle("/list-transaction-feeds", jsonHandler(a.listTxFeeds))

    m.Handle("/get-unconfirmed-transaction", jsonHandler(a.getUnconfirmedTx))
    m.Handle("/list-unconfirmed-transactions", jsonHandler(a.listUnconfirmedTxs))

    m.Handle("/get-block-hash", jsonHandler(a.getBestBlockHash))
    m.Handle("/get-block-header", jsonHandler(a.getBlockHeader))
    m.Handle("/get-block", jsonHandler(a.getBlock))
    m.Handle("/get-block-count", jsonHandler(a.getBlockCount))
    m.Handle("/get-difficulty", jsonHandler(a.getDifficulty))
    m.Handle("/get-hash-rate", jsonHandler(a.getHashRate))

    m.Handle("/is-mining", jsonHandler(a.isMining))
    m.Handle("/set-mining", jsonHandler(a.setMining))

    m.Handle("/get-work", jsonHandler(a.getWork))
    m.Handle("/submit-work", jsonHandler(a.submitWork))

    m.Handle("/gas-rate", jsonHandler(a.gasRate))
    m.Handle("/net-info", jsonHandler(a.getNetInfo))

可以看到還是各種功能的定義,主要是跟區(qū)塊數(shù)據(jù)、挖礦、訪問(wèn)控制等相關(guān)的功能,這里就不詳述了。

再繼續(xù):

    handler := latencyHandler(m, walletEnable)
    handler = maxBytesHandler(handler)
    handler = webAssetsHandler(handler)
    handler = gzip.Handler{Handler: handler}

    a.handler = handler
}

這里是把前面定義的功能點(diǎn)配置包成了一個(gè)handler,然后在它外面包了一層又一層,添加上了更多的功能:

latencyHandler:我目前還不能準(zhǔn)確說(shuō)出它的作用,留待以后補(bǔ)充

maxBytesHandler:防止用戶提交的數(shù)據(jù)過(guò)大,目前值約為10MB。對(duì)于除signer/sign-block以外的url有效

webAssetsHandler:向用戶提供dashboard相關(guān)的前端頁(yè)面資源(比如網(wǎng)頁(yè)、圖片等等)??赡苁菫榱诵阅芎头奖阈苑矫娴目紤],前端文件都經(jīng)過(guò)混淆后,以字符串形式嵌入在dashboard/dashboard.go中,真正的代碼在另一個(gè)項(xiàng)目中 https://github.com/Bytom/dashboard,我們?cè)诤竺鏁?huì)看一下

gzip.Handler:對(duì)http客戶端進(jìn)行是否支持gzip的檢測(cè),并且在支持的情況下,傳輸數(shù)據(jù)時(shí)使用gzip壓縮

然后讓我們回到主線,看看前面的NewAPI中最后調(diào)用的api.initServer(config)

api/api.go#L89-L122

func (a *API) initServer(config *cfg.Config) {
    // The waitHandler accepts incoming requests, but blocks until its underlying
    // handler is set, when the second phase is complete.
    var coreHandler waitHandler
    var handler http.Handler

    coreHandler.wg.Add(1)
    mux := http.NewServeMux()
    mux.Handle("/", &coreHandler)

    handler = mux
    if config.Auth.Disable == false {
        handler = AuthHandler(handler, a.accessTokens)
    }
    handler = RedirectHandler(handler)

    secureheader.DefaultConfig.PermitClearLoopback = true
    secureheader.DefaultConfig.HTTPSRedirect = false
    secureheader.DefaultConfig.Next = handler

    a.server = &http.Server{
        // Note: we should not set TLSConfig here;
        // we took care of TLS with the listener in maybeUseTLS.
        Handler:      secureheader.DefaultConfig,
        ReadTimeout:  httpReadTimeout,
        WriteTimeout: httpWriteTimeout,
        // Disable HTTP/2 for now until the Go implementation is more stable.
        // https://github.com/golang/go/issues/16450
        // https://github.com/golang/go/issues/17071
        TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
    }

    coreHandler.Set(a)
}

這個(gè)方法在本文不適合細(xì)講,因?yàn)樗嗟氖巧婕暗絟ttp層面的一些東西,不是本文的重點(diǎn)。值得關(guān)注的地方是,方法創(chuàng)建了一個(gè)Go提供的http.Server,把前面我們辛苦配置好的handler塞進(jìn)去,萬(wàn)事俱備,只欠啟動(dòng)。

下面就是啟動(dòng)啦。我們終于可以回到最新的initAndstartApiServer方法了,還記得它的第3塊內(nèi)容嗎?主要就是調(diào)用了n.api.StartServer(*listenAddr)

api/api.go#L125-L140

func (a *API) StartServer(address string) {
    // ...
    listener, err := net.Listen("tcp", address)
    // ...
    go func() {
        if err := a.server.Serve(listener); err != nil {
            log.WithField("error", errors.Wrap(err, "Serve")).Error("Rpc server")
        }
    }()
}

這塊比較簡(jiǎn)單,就是使用Go的net.Listen來(lái)監(jiān)聽傳入的web api地址,得到相應(yīng)的listener之后,把它傳給我們?cè)谇懊鎰?chuàng)建的http.ServerServe方法,就大功告成了。

這一塊代碼分析寫得十分痛苦,主要原因是它的web api這里幾乎涉及到了所有比原提供的功能,很龐雜。還有不少跟http協(xié)議相關(guān)的東西。同時(shí),因?yàn)楸┞冻隽私涌?,這里就容易出現(xiàn)安全風(fēng)險(xiǎn),所以代碼里面還有不少涉及到用戶輸入、安全檢查等。這些東西當(dāng)然是非常重要的,但是從代碼閱讀的角度上來(lái)講又難免枯燥,除非我們就是為了研究安全性。

本文的任務(wù)主要是研究比原是如何提供http服務(wù)的,關(guān)于比原在安全性方面做了哪些事情,以后會(huì)有專門的分析。

Dashboard使用了什么樣的前端框架?

比原的前端代碼是在另一個(gè)獨(dú)立的項(xiàng)目中:https://github.com/Bytom/dash...

本文我們并不去探討代碼細(xì)節(jié),而僅僅去看一下它使用了哪些前端框架,有個(gè)大概印象即可。

通過(guò)https://github.com/Bytom/dashboard/blob/master/package.json我們就可以大概了解到,比原前端使用了:

構(gòu)建工具:直接利用npmScripts

前端框架:React + Redux

CSS方面:bootstrap

JavaScript:ES6

http請(qǐng)求:fetch-ponyfill

資源打包:webpack

測(cè)試:mocha

Dashboard上面的數(shù)據(jù),是以什么樣的方式從后臺(tái)拿到的?

以Account相關(guān)的代碼為例:

src/sdk/api/accounts.js#L16

const accountsAPI = (client) => {
  return {
    create: (params, cb) => shared.create(client, "/create-account", params, {cb, skipArray: true}),

    createBatch: (params, cb) => shared.createBatch(client, "/create-account", params, {cb}),

    // ...

    listAddresses: (accountId) => shared.query(client, "accounts", "/list-addresses", {account_id: accountId}),
  }
}

這些函數(shù)主要是通過(guò)fetch-ponyfill庫(kù)中提供的方法,向向前面使用go創(chuàng)建的web api接口發(fā)送http請(qǐng)求,并且拿到相應(yīng)的回復(fù)數(shù)據(jù)。而它們又將在React組件中被調(diào)用,拿回來(lái)的數(shù)據(jù)用于填充頁(yè)面。

同樣,更細(xì)節(jié)的內(nèi)容在本文就不講啦。

終于,經(jīng)過(guò)這一大篇的分析,我覺得我對(duì)于比原的Dashboard是怎么做出來(lái)的,有了一些基本的印象。剩下的,就是在以后,針對(duì)其中的功能進(jìn)行細(xì)致的研究。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/24184.html

相關(guān)文章

  • 剝開原看代碼09:通過(guò)dashboard創(chuàng)建密鑰時(shí),前端數(shù)據(jù)如何傳到后端?

    摘要:下一步,將進(jìn)入比原的節(jié)點(diǎn)也就是后端。它具體是怎么創(chuàng)建密鑰的,這在以后的文章中將詳細(xì)討論。當(dāng)我們清楚了在本文中,前后端數(shù)據(jù)是如何交互的,就很容易推廣到更多的情景。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在前面一篇文章,我們粗略...

    MangoGoing 評(píng)論0 收藏0
  • 剝開原看代碼01:初始化時(shí)生成配置文件在哪兒

    摘要:所以這個(gè)文章系列叫作剝開比原看代碼。所以我的問(wèn)題是比原初始化時(shí),產(chǎn)生了什么樣的配置文件,放在了哪個(gè)目錄下下面我將結(jié)合源代碼,來(lái)回答這個(gè)問(wèn)題。將用來(lái)確認(rèn)數(shù)據(jù)目錄是有效的,并且將根據(jù)傳入的不同,來(lái)生成不同的內(nèi)容寫入到配置文件中。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee...

    felix0913 評(píng)論0 收藏0
  • 剝開原看代碼17:比原如何顯示交易詳細(xì)信息?

    摘要:作者比原項(xiàng)目倉(cāng)庫(kù)地址地址在上上篇文章里,我們還剩下一個(gè)小問(wèn)題沒有解決,即前端是如何顯示一個(gè)交易的詳細(xì)信息的。那我們?cè)诒疚目匆幌?,比原是如何顯示這個(gè)交易的詳細(xì)信息的。到今天為止,我們終于把比原是如何創(chuàng)建一個(gè)交易的這件事的基本流程弄清楚了。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https:/...

    魏明 評(píng)論0 收藏0
  • 剝開原看代碼12:比原如何通過(guò)/create-account-receiver創(chuàng)建地址?

    摘要:繼續(xù)看生成地址的方法由于這個(gè)方法里傳過(guò)來(lái)的是而不是對(duì)象,所以還需要再用查一遍,然后,再調(diào)用這個(gè)私有方法創(chuàng)建地址該方法可以分成部分在第塊中主要關(guān)注的是返回值。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 在比原的dashboard中...

    oneasp 評(píng)論0 收藏0
  • 剝開原看代碼14:比原挖礦流程什么樣?

    摘要:所以在今天我打算通過(guò)源代碼分析一下比原的挖礦流程,但是考慮到它肯定會(huì)涉及到比原的核心,所以太復(fù)雜的地方我就會(huì)先跳過(guò),那些地方時(shí)機(jī)成熟的時(shí)候會(huì)徹底研究一下。 作者:freewind 比原項(xiàng)目倉(cāng)庫(kù): Github地址:https://github.com/Bytom/bytom Gitee地址:https://gitee.com/BytomBlockc... 當(dāng)我們以bytom init ...

    BLUE 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<