據(jù)地圖平臺是字節(jié)跳動內(nèi)部的大數(shù)據(jù)檢索平臺,每天近萬的字節(jié)員工在此查找所需數(shù)據(jù)。數(shù)據(jù)地圖通過提供便捷的找數(shù),理解數(shù)服務(wù),大大節(jié)省了內(nèi)部數(shù)據(jù)的溝通和建設(shè)成本。
字節(jié)的數(shù)據(jù)可分為端數(shù)據(jù)和業(yè)務(wù)數(shù)據(jù),這些記錄往往需要通過加工處理才能產(chǎn)生業(yè)務(wù)價值。數(shù)據(jù)加工處理的流程一般是讀取原始數(shù)據(jù),進(jìn)行數(shù)據(jù)清洗,再經(jīng)過多種計算和存儲,最終匯入指標(biāo)、報表和數(shù)據(jù)服務(wù)系統(tǒng)。數(shù)據(jù)血緣描述了數(shù)據(jù)的來源和去向,以及數(shù)據(jù)在多個處理過程中的轉(zhuǎn)換,是組織內(nèi)使數(shù)據(jù)發(fā)揮價值的重要基礎(chǔ)能力。
數(shù)據(jù)地圖平臺在 2021 年接入了全鏈路核心元數(shù)據(jù),包括但不限于:Hive、Clickhouse、Kafka、BI 報表、BI 數(shù)據(jù)集、畫像、埋點、MySQL、Abase。這些數(shù)據(jù)全部要通過數(shù)據(jù)血緣連接起來,進(jìn)而可以進(jìn)行影響分析、內(nèi)部審計、SLA 保障、歸因分析、理解和查找數(shù)據(jù)、自動化推薦等操作。
隨著內(nèi)部數(shù)據(jù)不斷膨脹,簡單的數(shù)據(jù)血緣圖譜已經(jīng)無法滿足萬級表血緣的關(guān)系展示。一些突出的問題包括看不清單個表的直接上下游,看不清數(shù)據(jù)鏈路,整體情況等等。因此需要重構(gòu)一種更清晰、靈活、便利的方式。下圖簡單展示了優(yōu)化后的使用效果。
在新版血緣圖譜中,我們可以直接清晰的看到每個表的多層上下游依賴關(guān)系,甚至可以直接看到一些特殊場景下用戶關(guān)注的表屬性,通過點擊節(jié)點高亮查看數(shù)據(jù)鏈路,更可以看清每層的統(tǒng)計信息。在下文中我們將詳細(xì)拆解優(yōu)化的全過程。
要做出一個能滿足用戶需求的圖產(chǎn)品,首先是要清楚用戶想從圖中獲取什么信息,從而有針對性的將這些信息展示出來。從血緣圖譜的背景本身可以推斷出用戶希望在圖譜中查看表之間的關(guān)系,查看關(guān)系鏈路,而更多的使用場景待發(fā)掘。因此我們對內(nèi)部重度用戶進(jìn)行了訪談,整理得出了以下不同用戶角色使用數(shù)據(jù)血緣圖譜的用戶場景。
結(jié)合訪談結(jié)果和用戶的日常反饋,數(shù)據(jù)血緣圖譜的場景按目前用戶的使用頻率從大到小排序依次為:
場景 | 用戶關(guān)注 | 場景描述 |
影響分析 | 下游 | 當(dāng)處于血緣上游的研發(fā)同學(xué)修改任務(wù)前,通過查看自己的下游,通知對應(yīng)資產(chǎn)或任務(wù)的負(fù)責(zé)人,進(jìn)行相應(yīng)的修改,否則會造成嚴(yán)重的生產(chǎn)事故。 |
找數(shù)理解數(shù) | 上游 | 在找數(shù)據(jù)時,通過查看一份數(shù)據(jù)資產(chǎn)的血緣,來更多的了解它的“前世今生”,可以更好的判定當(dāng)前資產(chǎn)是不是自己需要的,或者是不是值得信賴的。就像了解一個人,可以從他周圍的朋友中得到很多信息一樣,是對這個人“生平”很好的補充。 |
鏈路梳理 | 鏈路 | 事先挑選已知的核心任務(wù),通過血緣關(guān)系,自動化的梳理出其所在的核心鏈路。多用于內(nèi)審和數(shù)據(jù)治理。 |
歸因分析 | 上游 | 當(dāng)某一個指標(biāo)或字段數(shù)據(jù)/產(chǎn)出時間等出問題時,通過查看血緣上游的任務(wù)或資產(chǎn),排查出造成問題的根因。 |
使用分析 | 下游 | 一個表的下游表越多,使用越頻繁,可以認(rèn)為價值越大。 |
抽象出幾個主要需求即為:
其實上述需求舊版血緣圖譜都有一定程度上的滿足,我們需要去找出舊版血緣圖譜提供的功能為什么不滿足用戶需求,有哪些問題需要在新版中注意避免。
節(jié)點較少,比較清晰
大量節(jié)點,查看困難
用戶在使用過程中看重的是查看關(guān)系的效率和屬性的完備度,因此在設(shè)計優(yōu)化方案時會盡量從這兩點出發(fā)去考慮。
首先是表數(shù)據(jù)查看的效率問題。看不清表名,無法區(qū)分相同前綴的表是用戶痛點之一。首先我們統(tǒng)計了現(xiàn)有表的平均字符數(shù)是 47 位,于是調(diào)寬了節(jié)點讓用戶能更直觀的區(qū)分表名。用數(shù)據(jù)地圖平臺中通用的類型圖表來代替色塊圖例,讓數(shù)據(jù)類型一目了然。
其次對于數(shù)據(jù)量大時看不清數(shù)據(jù)關(guān)系的問題,我們需要一個更緊湊清晰的數(shù)據(jù)呈現(xiàn)方式。通過需求分析和用戶調(diào)研,我們了解到用戶關(guān)心的是節(jié)點所在層級和節(jié)點之間的聯(lián)系。對于同一層級節(jié)點的先后順序,次層級節(jié)點之間的關(guān)系不是很看重。
說到緊湊的布局方式,自然而然我們就想到了列表。如果能用一個列表來承載層級血緣的節(jié)點,用連線來連接不同層級的節(jié)點,那么久可以表達(dá)節(jié)點之間的血緣關(guān)系了。當(dāng)節(jié)點較多超出一屏?xí)r可以拖動此列滾動條來查看更多節(jié)點,連線隨之刷新位置。當(dāng)層級不滿一屏?xí)r整體居中展示,層級過多超過一屏?xí)r可以左右滑動查看。這樣在保留層級結(jié)構(gòu)信息的同時最大程度的利用了可視區(qū)域,展示出了盡可能多的數(shù)據(jù)。
新版血緣圖譜支持了點擊任意節(jié)點則高亮該節(jié)點到主節(jié)點的鏈路功能。配合列滾動和連線刷新,不管數(shù)據(jù)量多大總能看清一整條數(shù)據(jù)鏈路。
我們還在每列列表頂部增加了層級信息和節(jié)點統(tǒng)計,讓用戶能同時查看每個節(jié)點細(xì)節(jié)和節(jié)點的整體分布。最終實現(xiàn)效果如下圖:
當(dāng)用戶想去找數(shù),理解數(shù)或做歸因分析時,不僅要了解一個表的上游依賴,更需要理解表的加工邏輯。因此我們在節(jié)點的連線上新增了任務(wù)信息。當(dāng)用戶 hover 到連線上后,連線會加粗高亮并彈出任務(wù)信息。我們還附上了大數(shù)據(jù)開發(fā)平臺的對應(yīng)任務(wù)鏈接,點擊鏈接即可跳轉(zhuǎn)到新頁面查看任務(wù)邏輯詳情。
在設(shè)計分組功能時,采用了每列獨立分組的方式。一般認(rèn)為用戶會關(guān)注有對應(yīng)分組數(shù)據(jù)的節(jié)點,因此總將有分組的數(shù)據(jù)放在上面,無分組數(shù)據(jù)的置底,這樣排序能提升用戶的瀏覽效率。
舊版血緣圖譜的篩選功能是在前端處理的,由于一些性能限制導(dǎo)致篩選后只能顯示部分?jǐn)?shù)據(jù),用戶無法得知符合條件的節(jié)點是否已經(jīng)全部展示。新版血緣圖譜針對這個用戶痛點,將前端篩選改為了服務(wù)端篩選,盡量展示全符合要求的數(shù)據(jù)。每個層級的頂欄對應(yīng)更新為篩選后的統(tǒng)計信息。同時更新連線,如果篩選后節(jié)點之間是有關(guān)聯(lián)的,也會展示關(guān)聯(lián)關(guān)系和高亮關(guān)系鏈路。
不同職能的用戶在不同場景下使用血緣圖譜時關(guān)注的節(jié)點屬性并不相同,如果血緣圖譜可以直接在圖上顯示用戶當(dāng)前想關(guān)注的表屬性就能幫助用戶更高效的解決問題。于是我們在血緣圖譜上設(shè)計了屬性展示功能,用戶可以勾選自己感興趣的屬性直接顯示到圖中。比如下圖中展示了每個節(jié)點表熱度和生命周期兩個屬性。
技術(shù)選型
在編碼實現(xiàn)之前,我們需要進(jìn)行技術(shù)選型。好的選型往往能讓編碼事半功倍。在做技術(shù)選型時,我們會主要考慮實現(xiàn)復(fù)雜度、研發(fā)周期、可擴展性三個角度。分析整個血緣圖譜的需求:
于是我們結(jié)合兩者之長,選用了 React + Canvas 的混合模式來實現(xiàn)血緣圖譜。Canvas 居于底部,僅負(fù)責(zé)畫連線。React 在上層負(fù)責(zé)渲染節(jié)點響應(yīng) hover 等交互。DOM 層疊關(guān)系如下:
整個血緣圖譜的初始化流程如下:
實現(xiàn)細(xì)節(jié)
用這種混合模式的一個挑戰(zhàn)就是 Canvas 和 DOM 的刷新率和同步率。在血緣圖譜中滾動橫向滾動條和每一列的縱向滾動條時 Canvas 要進(jìn)行及時的刷新以保證連線和節(jié)點的相對位置一定。
另一個挑戰(zhàn)是 DOM 節(jié)點在大數(shù)據(jù)量下的性能問題。通常情況下我們認(rèn)為 Canvas 在大數(shù)據(jù)量渲染有更好的性能,而萬級的 DOM 節(jié)點就會讓用戶在使用中感受到卡頓了。這時候我們想到了按需渲染。 用戶在圖譜可視區(qū)域中一屏能看到的節(jié)點數(shù)量是有限的,高度為 1120 的容器中,一列僅存在至多 30 個節(jié)點。如果僅渲染可見的節(jié)點,則能保證使用過程的流暢。具體做法是在節(jié)點布局時增加以下步驟:
在 React 渲染時更新每列容器的長度,將節(jié)點根據(jù)坐標(biāo)絕對定位到正確的位置上。看起來就跟全量渲染的效果一致,渲染效率大幅提升。
然而問題并不止于此。在進(jìn)行大數(shù)據(jù)量的縱向滾動時,會發(fā)現(xiàn)幀率很低,交互還是不流暢。分析得知是由于列表滾動時會在短時間內(nèi)進(jìn)行大量線條重計算和渲染。于是還要在 Canvas 繪制上進(jìn)行優(yōu)化。
我們從上圖可以看到在單層節(jié)點很多的情況下,主節(jié)點與不可見節(jié)點的連線可見,但是沒有任何價值,只是加重了用戶對當(dāng)前節(jié)點連線查看的負(fù)擔(dān)。因此我們對線條也進(jìn)行了渲染優(yōu)化,僅當(dāng)一條連線兩端的節(jié)點都在可見范圍中時才渲染連線,在連線的 Tooltip 上增加了來源去向的展示輔助查看。至此我們做到了在復(fù)雜情況下的流暢展示血緣數(shù)據(jù)。
以上就是數(shù)據(jù)血緣圖譜的整個優(yōu)化過程。在這個過程中,我總結(jié)起來就是在了解用戶訴求的前提下,克制地表達(dá)關(guān)系圖中的信息,在合適的場景下突出核心的內(nèi)容。做圖分析產(chǎn)品時不需要拘泥于某種形式,而是真正的從用戶需求出發(fā),為用戶服務(wù)。
一站式數(shù)據(jù)中臺套件,幫助用戶快速完成數(shù)據(jù)集成、開發(fā)、運維、治理、資產(chǎn)、安全等全套數(shù)據(jù)中臺建設(shè),幫助數(shù)據(jù)團隊有效的降低工作成本和數(shù)據(jù)維護成本、挖掘數(shù)據(jù)價值、為企業(yè)決策提供數(shù)據(jù)支撐。
歡迎加入字節(jié)跳動數(shù)據(jù)平臺官方群,進(jìn)行數(shù)據(jù)技術(shù)交流、獲取更多內(nèi)容干貨
點擊 大數(shù)據(jù)研發(fā)治理套件-火山引擎 了解產(chǎn)品詳情
今年國慶假期終于可以憋在家里了不用出門了,不用出去看后腦了,真的是一種享受。這么好的光陰怎么浪費,睡覺、吃飯、打豆豆這怎么可能(耍多了也煩),完全不符合我們程序員的作風(fēng),趕緊起來把文章寫完。
這篇文章比較基礎(chǔ),在國慶期間的業(yè)余時間寫的,這幾天又完善了下,力求把更多的前端所涉及到的關(guān)于文件上傳的各種場景和應(yīng)用都涵蓋了,若有疏漏和問題還請留言斧正和補充。
以下是本文所涉及到的知識點,break or continue ?
原理很簡單,就是根據(jù) http 協(xié)議的規(guī)范和定義,完成請求消息體的封裝和消息體的解析,然后將二進(jìn)制內(nèi)容保存到文件。
我們都知道如果要上傳一個文件,需要把 form 標(biāo)簽的enctype設(shè)置為multipart/form-data,同時method必須為post方法。
那么multipart/form-data表示什么呢?
multipart互聯(lián)網(wǎng)上的混合資源,就是資源由多種元素組成,form-data表示可以使用HTML Forms 和 POST 方法上傳文件,具體的定義可以參考RFC 7578。
multipart/form-data 結(jié)構(gòu)
看下 http 請求的消息體
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryDCntfiXcSkPhS4PN 表示本次請求要上傳文件,其中boundary表示分隔符,如果要上傳多個表單項,就要使用boundary分割,每個表單項由———XXX開始,以———XXX結(jié)尾。
每一個表單項又由Content-Type和Content-Disposition組成。
Content-Disposition: form-data 為固定值,表示一個表單元素,name 表示表單元素的 名稱,回車換行后面就是name的值,如果是上傳文件就是文件的二進(jìn)制內(nèi)容。
Content-Type:表示當(dāng)前的內(nèi)容的 MIME 類型,是圖片還是文本還是二進(jìn)制數(shù)據(jù)。
解析
客戶端發(fā)送請求到服務(wù)器后,服務(wù)器會收到請求的消息體,然后對消息體進(jìn)行解析,解析出哪是普通表單哪些是附件。
可能大家馬上能想到通過正則或者字符串處理分割出內(nèi)容,不過這樣是行不通的,二進(jìn)制buffer轉(zhuǎn)化為string,對字符串進(jìn)行截取后,其索引和字符串是不一致的,所以結(jié)果就不會正確,除非上傳的就是字符串。
不過一般情況下不需要自行解析,目前已經(jīng)有很成熟的三方庫可以使用。
至于如何解析,這個也會占用很大篇幅,后面的文章在詳細(xì)說。
使用 form 表單上傳文件
在 ie時代,如果實現(xiàn)一個無刷新的文件上傳那可是費老勁了,大部分都是用 iframe 來實現(xiàn)局部刷新或者使用 flash 插件來搞定,在那個時代 ie 就是最好用的瀏覽器(別無選擇)。
DEMO
這種方式上傳文件,不需要 js ,而且沒有兼容問題,所有瀏覽器都支持,就是體驗很差,導(dǎo)致頁面刷新,頁面其他數(shù)據(jù)丟失。
HTML
<form method="post" action="http://localhost:8100" enctype="multipart/form-data">
選擇文件:
<input type="file" name="f1"/> input 必須設(shè)置 name 屬性,否則數(shù)據(jù)無法發(fā)送<br/>
<br/>
標(biāo)題:<input type="text" name="title"/><br/><br/><br/>
<button type="submit" id="btn-0">上 傳</button>
</form>
復(fù)制代碼
服務(wù)端文件的保存基于現(xiàn)有的庫koa-body結(jié)合 koa2實現(xiàn)服務(wù)端文件的保存和數(shù)據(jù)的返回。
在項目開發(fā)中,文件上傳本身和業(yè)務(wù)無關(guān),代碼基本上都可通用。
在這里我們使用koa-body庫來實現(xiàn)解析和文件的保存。
koa-body 會自動保存文件到系統(tǒng)臨時目錄下,也可以指定保存的文件路徑。
然后在后續(xù)中間件內(nèi)得到已保存的文件的信息,再做二次處理。
NODE
/**
* 服務(wù)入口
*/
var http = require('http');
var koaStatic = require('koa-static');
var path = require('path');
var koaBody = require('koa-body');//文件保存庫
var fs = require('fs');
var Koa = require('koa2');
var app = new Koa();
var port = process.env.PORT || '8100';
var uploadHost= `http://localhost:${port}/uploads/`;
app.use(koaBody({
formidable: {
//設(shè)置文件的默認(rèn)保存目錄,不設(shè)置則保存在系統(tǒng)臨時目錄下 os
uploadDir: path.resolve(__dirname, '../static/uploads')
},
multipart: true // 開啟文件上傳,默認(rèn)是關(guān)閉
}));
//開啟靜態(tài)文件訪問
app.use(koaStatic(
path.resolve(__dirname, '../static')
));
//文件二次處理,修改名稱
app.use((ctx) => {
var file = ctx.request.files.f1;//得道文件對象
var path = file.path;
var fname = file.name;//原文件名稱
var nextPath = path+fname;
if(file.size>0 && path){
//得到擴展名
var extArr = fname.split('.');
var ext = extArr[extArr.length-1];
var nextPath = path+'.'+ext;
//重命名文件
fs.renameSync(path, nextPath);
}
//以 json 形式輸出上傳文件地址
ctx.body = `{
"fileUrl":"${uploadHost}${nextPath.slice(nextPath.lastIndexOf('/')+1)}"
}`;
});
/**
* http server
*/
var server = http.createServer(app.callback());
server.listen(port);
console.log('demo1 server start ...... ');
復(fù)制代碼
CODE
https://github.com/Bigerfe/fe-learn-code/
TML 字符集
如需正確地顯示 HTML 頁面,瀏覽器必須知道使用何種字符集。
萬維網(wǎng)早期使用的字符集是 ASCII。ASCII 支持 0-9 的數(shù)字,大寫和小寫英文字母表,以及一些特殊字符。
由于很多國家使用的字符并不屬于 ASCII,現(xiàn)代瀏覽器的默認(rèn)字符集是 ISO-8859-1。
如果網(wǎng)頁使用不同于 ISO-8859-1 的字符集,就應(yīng)該在 <meta> 標(biāo)簽進(jìn)行指定。
ISO 字符集
ISO 字符集是國際標(biāo)準(zhǔn)組織 (ISO) 針對不同的字母表/語言定義的標(biāo)準(zhǔn)字符集。
下面列出了世界各地使用的不同字符集:
字符集 | 描述 | 使用范圍 |
---|---|---|
ISO-8859-1 | Latin alphabet part 1 | 北美、西歐、拉丁美洲、加勒比海、加拿大、非洲 |
ISO-8859-2 | Latin alphabet part 2 | 東歐 |
ISO-8859-3 | Latin alphabet part 3 | SE Europe、世界語、其他雜項 |
ISO-8859-4 | Latin alphabet part 4 | 斯堪的納維亞/波羅的海(以及其他沒有包括在 ISO-8859-1 中的部分) |
ISO-8859-5 | Latin/Cyrillic part 5 | 使用古代斯拉夫語字母表的語言,比如保加利亞語、白俄羅斯文、俄羅斯語、馬其頓語 |
ISO-8859-6 | Latin/Arabic part 6 | 使用阿拉伯字母的語言 |
ISO-8859-7 | Latin/Greek part 7 | 現(xiàn)代希臘語,以及由希臘語衍生的數(shù)學(xué)符號 |
ISO-8859-8 | Latin/Hebrew part 8 | 使用希伯來語的語言 |
ISO-8859-9 | Latin 5 part 9 | 土耳其語。除了土耳其字符取代了冰島文字,其它與 ISO-8859-1 相同。 |
ISO-8859-10 | Latin 6 | 拉普蘭語、日耳曼語、愛斯基摩北歐語 |
ISO-8859-15 | Latin 9 (aka Latin 0) | 與 ISO 8859-1 類似,歐元符號和其他一些字符取代了一些較少使用的符號 |
ISO-2022-JP | Latin/Japanese part 1 | 日本語 |
ISO-2022-JP-2 | Latin/Japanese part 2 | 日本語 |
ISO-2022-KR | Latin/Korean part 1 | 韓語 |
Unicode 標(biāo)準(zhǔn)
由于上面列出的字符集都有容量限制,而且不兼容多語言環(huán)境,Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。
Unicode 標(biāo)準(zhǔn)涵蓋了世界上的所有字符、標(biāo)點和符號。
不論是何種平臺、程序或語言,Unicode 都能夠進(jìn)行文本數(shù)據(jù)的處理、存儲和交換。
Unicode 聯(lián)盟
Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。他們的目標(biāo)是用標(biāo)準(zhǔn)的 Unicode 轉(zhuǎn)換格式 (UTF) 來取代現(xiàn)有的字符集。
Unicode 標(biāo)準(zhǔn)已經(jīng)獲得了成功,在 XML、Java、ECMAScript (JavaScript)、LDAP、CORBA 3.0、WML 中,Unicode 已經(jīng)得到了實現(xiàn)。在許多操作系統(tǒng)以及所有的現(xiàn)代瀏覽器中,Unicode 同樣得到了支持。
Unicode 聯(lián)盟與領(lǐng)導(dǎo)性的標(biāo)準(zhǔn)發(fā)展組織進(jìn)行合作,比如 ISO、W3C 以及 ECMA。
Unicode 可以被不同的字符集兼容。最常用的編碼方式是 UTF-8 和 UTF-16:
字符集 | 描述 |
---|---|
UTF-8 | UTF8 中的字符可以是 1-4 個字節(jié)長。UTF-8 可以表示 Unicode 標(biāo)準(zhǔn)中的任意字符。UTF-8 向后兼容 ASCII。UTF-8 是網(wǎng)頁和電子郵件的首選編碼。 |
UTF-16 | 16 比特的 Unicode 轉(zhuǎn)換格式是一種 Unicode 可變字符編碼,能夠?qū)θ?Unicode 指令表進(jìn)行編碼。UTF-16 主要被用于操作系統(tǒng)和環(huán)境中,比如微軟的 Windows 2000/XP/2003/Vista/CE 以及 Java 和 .NET 字節(jié)代碼環(huán)境。 |
提示: 最前面的 256 個 Unicode 字符集字符對應(yīng)于 256 個 ISO-8859-1 字符。
提示: 所有 HTML 4 處理器均已支持 UTF-8,而所有 XHTML 和 XML 處理器支持 UTF-8 和 UTF-16!
如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。