<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          萬(wàn)字長(zhǎng)文,20-50K前端工程師部分面試題集錦 - 附答案(收藏!)

          共 14524字,需瀏覽 30分鐘

           ·

          2020-10-29 12:23

          現(xiàn)在20-50K的招聘,我們先看看是什么要求???


          螞蟻金服招聘要求:


          蝦皮招聘:




          騰訊:


          明源云:






          毫無(wú)疑問(wèn),這些公司都是招聘的大前端技術(shù)棧的職位,之前文章提到過(guò)2020年大前端最理想的技術(shù)棧,其實(shí)真的弄得很明白那些,出去面試基本上不會(huì)有什么問(wèn)題。



          小提示:如果發(fā)現(xiàn)小公司面試套你的技術(shù)和架構(gòu),迅速結(jié)束,開(kāi)出天價(jià)薪資走人




          ?下面正式公布部分面試題,以及答案


          出于對(duì)各個(gè)公司的尊重,不公布是哪家公司的面試題,以及面試技巧。只公布部分面試題和答案,以及分析問(wèn)題的角度,學(xué)習(xí)方向,面試中考察的不僅僅技術(shù)深度,還有廣度,每個(gè)人不可能技術(shù)面面俱到,前端學(xué)習(xí)的東西太多,忘掉一部分也是正常。記住核心就是關(guān)鍵,這些都是一些基礎(chǔ)面試題,比較通用。





          一般面試都會(huì)要做題,據(jù)我經(jīng)驗(yàn)看,一般都是6頁(yè),三張紙。考察的大部分是前端技術(shù)棧,原生Javascript的內(nèi)容,當(dāng)然,有的外企的面試體驗(yàn)更棒,技術(shù)一面規(guī)定是半個(gè)小時(shí),國(guó)內(nèi)公司可能有5輪,甚至6、7輪。



          面試題我會(huì)歸納成原生JavaScript、Node.js、React、Vue、通信協(xié)議、運(yùn)維部署、CI自動(dòng)化部署、Docker、性能優(yōu)化、前端架構(gòu)設(shè)計(jì)、后端常見(jiàn)的架構(gòu)等來(lái)分開(kāi)寫





          原生JavaScript


          以下代碼跟我寫的有點(diǎn)不一樣,但是大致差不多,最終都是在紙上手寫實(shí)現(xiàn)

          手寫一個(gè)深拷貝



          此處省略了一些其他類型的處理,可以在題目旁注釋、





          手寫一個(gè)reduce:





          Array.isArray的原理:



          手寫一個(gè)的防抖函數(shù):




          手寫一個(gè)Promise:


          const PENDING = "pending";const FULFILLED = "fulfilled";const REJECTED = "rejected";
          function MyPromise(fn) { const self = this; self.value = null; self.error = null; self.status = PENDING; self.onFulfilledCallbacks = []; self.onRejectedCallbacks = [];
          function resolve(value) { if (value instanceof MyPromise) { return value.then(resolve, reject); } if (self.status === PENDING) { setTimeout(() => { self.status = FULFILLED; self.value = value; self.onFulfilledCallbacks.forEach((callback) => callback(self.value)); }, 0) } }
          function reject(error) { if (self.status === PENDING) { setTimeout(function() { self.status = REJECTED; self.error = error; self.onRejectedCallbacks.forEach((callback) => callback(self.error)); }, 0) } } try { fn(resolve, reject); } catch (e) { reject(e); }}
          function resolvePromise(bridgepromise, x, resolve, reject) { if (bridgepromise === x) { return reject(new TypeError('Circular reference')); }
          let called = false; if (x instanceof MyPromise) { if (x.status === PENDING) { x.then(y => { resolvePromise(bridgepromise, y, resolve, reject); }, error => { reject(error); }); } else { x.then(resolve, reject); } } else if (x != null && ((typeof x === 'object') || (typeof x === 'function'))) { try { let then = x.then; if (typeof then === 'function') { then.call(x, y => { if (called) return; called = true; resolvePromise(bridgepromise, y, resolve, reject); }, error => { if (called) return; called = true; reject(error); }) } else { resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { resolve(x); }}
          MyPromise.prototype.then = function(onFulfilled, onRejected) { const self = this; let bridgePromise; onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value; onRejected = typeof onRejected === "function" ? onRejected : error => { throw error }; if (self.status === FULFILLED) { return bridgePromise = new MyPromise((resolve, reject) => { setTimeout(() => { try { let x = onFulfilled(self.value); resolvePromise(bridgePromise, x, resolve, reject); } catch (e) { reject(e); } }, 0); }) } if (self.status === REJECTED) { return bridgePromise = new MyPromise((resolve, reject) => { setTimeout(() => { try { let x = onRejected(self.error); resolvePromise(bridgePromise, x, resolve, reject); } catch (e) { reject(e); } }, 0); }); } if (self.status === PENDING) { return bridgePromise = new MyPromise((resolve, reject) => { self.onFulfilledCallbacks.push((value) => { try { let x = onFulfilled(value); resolvePromise(bridgePromise, x, resolve, reject); } catch (e) { reject(e); } }); self.onRejectedCallbacks.push((error) => { try { let x = onRejected(error); resolvePromise(bridgePromise, x, resolve, reject); } catch (e) { reject(e); } }); }); }}MyPromise.prototype.catch = function(onRejected) { return this.then(null, onRejected);}
          MyPromise.deferred = function() { let defer = {}; defer.promise = new MyPromise((resolve, reject) => { defer.resolve = resolve; defer.reject = reject; }); return defer;}try { module.exports = MyPromise} catch (e) {}


          promisify原理:


          promisify = function(fn) {  return function() {    var args = Array.from(arguments);    return new MyPromise(function(resolve, reject) {      fn.apply(        null,        args.concat(function(err) {          err ? reject(err) : resolve(arguments[1]);        })      );    });  };};


          Redux核心源碼解析:

          bindActionCreator源碼解析:

          export default function bindActionCreator(actions, dispatch) {    let newActions = {};    for (let key in actions) {        newActions[key] = () => dispatch(actions[key].apply(null, arguments));    }    return newActions;}


          核心:將多個(gè)actiondispatch傳入,合并成一個(gè)全新的actions對(duì)象


          combineReducers源碼:


          export default combineReducers = reducers => (state = {}, action) => Object.keys(reducers).reduce((currentState, key) => {    currentState[key] = reducers[key](state[key], action);    return currentState;}, {});


          核心:跟上面有點(diǎn)類似,遍歷生成一個(gè)全新的state,將多個(gè)state合并成一個(gè)state


          createStore源碼結(jié)合applyMiddleware講述如何實(shí)現(xiàn)處理多個(gè)中間件:

          export default function createStore(reducer, enhancer) {    if (typeof enhancer !== 'undefined') {        return enhancer(createStore)(reducer)    }    let state = null    const listeners = []    const subscribe = (listener) => {        listeners.push(listener)    }    const getState = () => state    const dispatch = (action) => {        state = reducer(state, action)        listeners.forEach((listener) => listener())    }    dispatch({})    return { getState, dispatch, subscribe }}


          applyMiddleware:

          export default function applyMiddleware(...middlewares) {    return (createStore) => (reducer) => {        const store = createStore(reducer)        let dispatch = store.dispatch        let chain = []
          const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch)
          return { ...store, dispatch } }}


          核心:當(dāng)發(fā)現(xiàn)傳入createStore的第二個(gè)或者第三個(gè)參數(shù)存在時(shí)候(這里沒(méi)有像原生redux支持SSR代碼注水,不支持第二個(gè)參數(shù)initState),就去返回它的調(diào)用結(jié)果


          整個(gè)Redux這里是最繞的,這里不做過(guò)分的源碼講解,其實(shí)核心就是一點(diǎn):


          實(shí)現(xiàn)多個(gè)中間件原理,就是將dispatch當(dāng)作最后一個(gè)函數(shù)傳入,利用compose這個(gè)工具函數(shù),最終實(shí)現(xiàn)多個(gè)中間件同時(shí)起作用,當(dāng)你源碼看得比較多的時(shí)候會(huì)發(fā)現(xiàn),大多數(shù)的源碼是跟redux相似


          compose工具函數(shù)實(shí)現(xiàn):


          export default function compose(...funcs) {    return funcs.reduce((a, b) => (...args) => a(b(...args)));}


          核心:其實(shí)就是一個(gè)reduce函數(shù)實(shí)現(xiàn),每次返回一個(gè)新的函數(shù),再將新的參數(shù)傳入


          redux下次會(huì)專門出個(gè)文章講解,它的源碼太重要了~



          原生JavaScript考察點(diǎn)比較多,這里只列出一部分,還有像結(jié)合TypeScript一起問(wèn)的,組合繼承,對(duì)象創(chuàng)建模式、設(shè)計(jì)模式等,但是那些本次不做講解




          Node.js篇幅:


          簡(jiǎn)述Node.jsEventLoop:



          現(xiàn)場(chǎng)出題,項(xiàng)目里有下面這段代碼,輸出是什么,穩(wěn)定嗎,說(shuō)明原因:

          setTimeout(() => {  console.log(1);});//----若干代碼邏輯new Promise((resolve, reject) => {  resolve();}).then(() => {  console.log(2);});


          答案:先輸出2,再輸出1,但是不穩(wěn)定。因?yàn)槎〞r(shí)器的執(zhí)行時(shí)間不確定,node.js的輪詢相當(dāng)于一個(gè)定時(shí)器,一直從上往下6個(gè)階段輪詢,此時(shí)如果中間代碼比較耗時(shí),還沒(méi)運(yùn)行到Promise時(shí)候,已經(jīng)輪詢到第一階段,定時(shí)器的回調(diào)就會(huì)被觸發(fā)。




          Node.js為什么處理異步IO快?


          答:Node 底層采用線程池的原理管理異步 IO,所以我們通常所的 單線程是指 Node 中 JavaScript 的執(zhí)行是單線程的,但 Node 本身是多線程的。Node.js 中異步 IO 是通過(guò)事件循環(huán)的方式實(shí)現(xiàn)的,異步 IO 事件主要來(lái)源于網(wǎng)絡(luò)請(qǐng)求和文件 IO。但是正因?yàn)槿绱耍琋ode.js處理很多計(jì)算密集型的任務(wù),就比較吃力,當(dāng)然有多進(jìn)程方式可以解決這個(gè)問(wèn)題。(自己給自己挖坑)



          以前聽(tīng)過(guò)一個(gè)很形象的回答:Java是一百個(gè)服務(wù)員對(duì)應(yīng)一百個(gè)用餐客人,Node是一個(gè)服務(wù)員對(duì)應(yīng)一百個(gè)用餐客人(因?yàn)榭腿瞬恍枰址昼姺?wù),可能只要三分鐘----好像,東哥?)





          Node.jsclusterfork兩種模式多進(jìn)程,那么這兩種情況下,主進(jìn)程負(fù)責(zé)TCP通信,怎樣才可以讓子進(jìn)程共享用戶的Socket對(duì)象?


          答案:cluster模式,多實(shí)例、自動(dòng)共享端口鏈接、自動(dòng)實(shí)現(xiàn)負(fù)載均衡。fork模式實(shí)現(xiàn)的多進(jìn)程,單實(shí)例、多進(jìn)程,可以通過(guò)手動(dòng)分發(fā)socket對(duì)象給不同子進(jìn)程進(jìn)行定制化處理、實(shí)現(xiàn)負(fù)載均衡




          Node.js多進(jìn)程維護(hù),以及通信方式:

          答案:原生的clusterfork模式都有API封裝好的進(jìn)行通信。如果是execfile這樣形式調(diào)起第三方插件形式,想要與第三方插件進(jìn)行通信,可以自己封裝一個(gè)類似promisyfy形式進(jìn)行通信,維護(hù)這塊,子進(jìn)程可以監(jiān)聽(tīng)到異常,一旦發(fā)現(xiàn)異常,立刻通知主進(jìn)程,殺死這個(gè)異常的子進(jìn)程,然后重新開(kāi)啟一個(gè)子進(jìn)程~




          簡(jiǎn)單談?wù)劊?span style="color: rgb(255, 41, 65);">Node.js搭建TCP、restful、websocket、UDP服務(wù)器,遇到過(guò)哪些問(wèn)題,怎么解決的


          答案:這里涉及的問(wèn)題比較多,考察全方位的通信協(xié)議知識(shí),需要出個(gè)專題后期進(jìn)行編寫





          看你簡(jiǎn)歷上寫,對(duì)koa源碼系統(tǒng)學(xué)習(xí)過(guò),請(qǐng)簡(jiǎn)述核心洋蔥圈的實(shí)現(xiàn):


          答案:洋蔥圈的實(shí)現(xiàn),有點(diǎn)類似Promise中的then實(shí)現(xiàn),每次通過(guò)use方法定義中間件函數(shù)時(shí)候,就會(huì)把這個(gè)函數(shù)存入一個(gè)隊(duì)列中,全局維護(hù)一個(gè)ctx對(duì)象,每次調(diào)用next(),就會(huì)調(diào)用隊(duì)列的下一個(gè)任務(wù)函數(shù)。偽代碼實(shí)現(xiàn)~:


          use (fn) {    // this.fn = fn 改成:    this.middlewares.push(fn) // 每次use,把當(dāng)前回調(diào)函數(shù)存進(jìn)數(shù)組}compose(middlewares, ctx){ // 簡(jiǎn)化版的compose,接收中間件數(shù)組、ctx對(duì)象作為參數(shù)    function dispatch(index){ // 利用遞歸函數(shù)將各中間件串聯(lián)起來(lái)依次調(diào)用        if(index === middlewares.length) return // 最后一次next不能執(zhí)行,不然會(huì)報(bào)錯(cuò)        let middleware = middlewares[index] // 取當(dāng)前應(yīng)該被調(diào)用的函數(shù)        middleware(ctx, () => dispatch(index + 1)) // 調(diào)用并傳入ctx和下一個(gè)將被調(diào)用的函數(shù),用戶next()時(shí)執(zhí)行該函數(shù)    }    dispatch(0)}



          所以這里說(shuō),源碼看多了會(huì)發(fā)現(xiàn),其實(shí)大都差不多,都是你抄我的,我抄你的,輪子上搭積木






          你對(duì)TCP系統(tǒng)學(xué)習(xí)過(guò),請(qǐng)你簡(jiǎn)述下SYN flood攻擊:

          小提示:我的TCP是跟張師傅學(xué)習(xí)的,在某金的小冊(cè)上有賣。~推薦購(gòu)買


          答案:攻擊方偽造源地址發(fā)送SYN報(bào)文,服務(wù)端此時(shí)回復(fù)syn+ack,但是真正的IP地址收到這個(gè)包之后,有可能直接回復(fù)了RST包,但是如果不回復(fù)RST包,那就更嚴(yán)重了,可能服務(wù)端會(huì)在幾十秒后才關(guān)閉這個(gè)socket鏈接(時(shí)間根據(jù)每個(gè)系統(tǒng)不一樣)



          抓包可見(jiàn)~:







          TCP可以快速握手嗎?


          答案:可以? -- 內(nèi)容來(lái)自? ?張師傅的小冊(cè)



          TCP鏈接和UDP的區(qū)別,什么時(shí)候選擇使用UDP鏈接?


          http://www.xiuchuang.com/question/4019.html


          總結(jié)就是:TCP面向鏈接,UDP面向消息,TCPACK作用是確認(rèn)已經(jīng)收到上一個(gè)包,UDP只管發(fā)送,一些無(wú)人機(jī)的操作,就用UDP鏈接,每個(gè)無(wú)人機(jī)就是一個(gè)服務(wù)器,跟地面通訊。



          通信協(xié)議還是要系統(tǒng)學(xué)習(xí),通信這里也問(wèn)了大概半個(gè)小時(shí),包括密鑰交換等





          看你簡(jiǎn)歷上有寫自己實(shí)現(xiàn)了一個(gè)mini-react,請(qǐng)簡(jiǎn)述實(shí)現(xiàn)原理,以及diff算法實(shí)現(xiàn)


          倉(cāng)庫(kù)地址:https://github.com/JinJieTan/mini-react


          答案:利用了babel,將虛擬dom轉(zhuǎn)換成了我想要的對(duì)象格式,然后實(shí)現(xiàn)了異步setState、component diff 、 element diff 、props 更新等。類似PReact的將真實(shí)dom和虛擬dom對(duì)比的方式進(jìn)行diff,這里結(jié)合代碼講了大概半個(gè)小時(shí)~ 大家可以看源碼,這個(gè)對(duì)于學(xué)習(xí)React是非常好的資料,當(dāng)時(shí)我花了半個(gè)多月學(xué)習(xí)





          看你對(duì)Vue的源碼有系統(tǒng)學(xué)習(xí)過(guò),請(qǐng)簡(jiǎn)述下Vue2.x版本的數(shù)據(jù)綁定:



          答案:Vue里面的{{}}寫法,?會(huì)用正則匹配后,拿到數(shù)據(jù)跟data里的做對(duì)比-解析指令,觀察數(shù)據(jù)變化是利用defineProperty來(lái)實(shí)現(xiàn),因?yàn)楸O(jiān)聽(tīng)不到數(shù)組的變化,所以尤大大只重寫了6個(gè)數(shù)組API。源碼解析,后面就是拼細(xì)節(jié),主要講一些核心點(diǎn)的實(shí)現(xiàn)。




          為什么VuenextTick不穩(wěn)定?


          答案:VuenextTick原理是:

          優(yōu)雅降級(jí):

          首選promise.then

          然后是setImmediate

          然后是一個(gè)瀏覽器目前支持不好的API

          最后是setTimeout


          dom真正更新渲染好的時(shí)間,不能真正確定,不論是框架還是原生,都存在這個(gè)問(wèn)題。所以用nextTick并不能保證拿到最新的dom





          談?wù)勀銓?duì)微前端的看法,以及實(shí)踐:


          答案:將Vue和React一起開(kāi)發(fā),其實(shí)一點(diǎn)都不難,只要自己能造出Redux這樣的輪子,熟悉兩個(gè)框架原理,就能一起開(kāi)發(fā),難的是將這些在一個(gè)合適的場(chǎng)景中使用。之前看到網(wǎng)上有微前端的實(shí)踐,但是并不是那么完美,當(dāng)然,類似Electron這樣的應(yīng)用,混合開(kāi)發(fā)很正常,微前端并不是只單單多個(gè)框架混合開(kāi)發(fā),更多是多個(gè)框架引入后解決了什么問(wèn)題、帶來(lái)的問(wèn)題怎么解決?畢竟5G還沒(méi)完全普及,數(shù)據(jù)傳輸還是不那么快。過(guò)大的包容易帶來(lái)客戶端的過(guò)長(zhǎng)白屏?xí)r間(自己給自己挖坑)





          你有提到白屏?xí)r間,有什么辦法可以減少嗎?都是什么原理


          答案: GZIP,SSR同構(gòu)、PWA應(yīng)用、預(yù)渲染、localStorage緩存js文件等、


          下面就是細(xì)分拆解答案,無(wú)限的連帶問(wèn)題,這里非常耗時(shí),這些內(nèi)容大都網(wǎng)上能搜到,我這里就不詳細(xì)說(shuō)


          其中有問(wèn)到PWA的原理,我的回答是:

          ?Service Worker 有一套自己的聲明周期,當(dāng)安裝并且處于激活狀態(tài)時(shí)候,網(wǎng)站在https或者localhost的協(xié)議時(shí)候,可以攔截過(guò)濾發(fā)出的請(qǐng)求,會(huì)先把請(qǐng)求克隆一份(請(qǐng)求是流,消費(fèi)就沒(méi)有了),然后判斷請(qǐng)求的資源是否在?Service Worker?緩存中,如果存在那么可以直接從?Service Worker?緩存中取出,如果不存在,那么就真正的發(fā)出這個(gè)請(qǐng)求。


          ?



          看你的技術(shù)棧對(duì)Electron比較熟悉,有使用過(guò)React-native,請(qǐng)你談?wù)勈褂玫母惺埽?/p>


          答案:React-native的坑還是比較多,但是目前也算擁有成熟的生態(tài)了,開(kāi)發(fā)簡(jiǎn)單的APP可以使用它。但是復(fù)雜的應(yīng)用還是原生比較好,Electron目前非常受歡迎,它基本上可以完成桌面應(yīng)用的大部分需求,重型應(yīng)用開(kāi)發(fā)也是完全沒(méi)問(wèn)題的,可以配合大量C# C++插件等。?





          Node.js的消息隊(duì)列應(yīng)用場(chǎng)景是什么?原理是什么


          答案:我們公司之前用的kafka,消息隊(duì)列的核心概念,異步,提供者,消費(fèi)者。例如IM應(yīng)用,每天都會(huì)有高峰期,但是我們不可能為了高峰期配置那么多服務(wù)器,那樣就是浪費(fèi),所以使用消息隊(duì)列,在多長(zhǎng)時(shí)間內(nèi)流量達(dá)到多少,就控制消費(fèi)頻率,例如客戶端是流的提供者,有一個(gè)中間件消費(fèi)隊(duì)列,我們的服務(wù)器是消費(fèi)者,每次消費(fèi)一個(gè)任務(wù)就回復(fù)一個(gè)ACK給消費(fèi)隊(duì)列,消費(fèi)頻率由我們控制,這樣任務(wù)不會(huì)丟失,服務(wù)器也不會(huì)掛。?還有一個(gè)異步問(wèn)題,一個(gè)用戶下單購(gòu)買一件商品,可能要更新庫(kù)存,已購(gòu)數(shù)量,支付,下單等任務(wù)。不可能同步進(jìn)行,這時(shí)候需要異步并行,事務(wù)方式處理。這樣既不耽誤時(shí)間,也能確保所有的任務(wù)成功才算成功,不然沒(méi)有支付成功,但是已購(gòu)數(shù)量增長(zhǎng)了就有問(wèn)題。



          此處省略、、、一萬(wàn)字




          用戶就是要上傳10個(gè)G的文件,服務(wù)器存儲(chǔ)允許的情況下,你會(huì)怎么處理保證整體架構(gòu)順暢,不影響其他用戶?



          答案:我會(huì)準(zhǔn)備兩個(gè)服務(wù)器上傳接口,前端或者原生客戶端上傳文件可以拿到文件大小,根據(jù)文件大小,分發(fā)不同的對(duì)應(yīng)服務(wù)器接口處理上傳,大文件可以進(jìn)行斷點(diǎn)續(xù)傳,原理是md5生成唯一的hash值,將分片的hash數(shù)組先上傳到后端,然后將文件分片上傳,對(duì)比hash值,相同的則丟棄。不一致的話,根據(jù)數(shù)組內(nèi)容進(jìn)行buffer拼接生成文件。


          關(guān)于服務(wù)器性能,大文件上傳的服務(wù)器允許被阻塞,小文件的服務(wù)器不會(huì)被阻塞。




          談?wù)勀銓?duì)前端、客戶端架構(gòu)的認(rèn)識(shí)?


          答案:前端的架構(gòu),首先明確項(xiàng)目的兼容性,面向?yàn)g覽器編程,是否做成PC、移動(dòng)端的響應(yīng)式布局。根據(jù)項(xiàng)目規(guī)模、后期可能迭代的需求制定技術(shù)方案,如果比較重型的應(yīng)用應(yīng)該選用原生開(kāi)發(fā),盡量少使用第三方庫(kù)。


          客戶端架構(gòu):是否跨平臺(tái),明確兼容系統(tǒng),例如是否兼容XP ,如果兼容XP就選擇nw.js,再然后根據(jù)項(xiàng)目復(fù)雜度招聘相應(yīng)技術(shù)梯度人員,安排系統(tǒng)學(xué)習(xí)相關(guān)內(nèi)容,招聘人員或者購(gòu)買定制開(kāi)發(fā)相關(guān)原生插件內(nèi)容。


          雖然說(shuō)只是談?wù)劊歉杏X(jué)面試的職位越高級(jí)、輪數(shù)越往后,越考驗(yàn)?zāi)愕募軜?gòu)能力,前面考察基礎(chǔ),后面考察你的技術(shù)廣度以及邏輯思維,能否在復(fù)雜的應(yīng)用中保持清醒頭腦,定位性能這類型的細(xì)節(jié)能力。很多人基礎(chǔ)面試面得很好,但是拿不到offer,原因就是沒(méi)有這種架構(gòu)能力,只能自己寫代碼,不能帶領(lǐng)大家學(xué)習(xí)、寫代碼。這也是我在面試時(shí)偶然聽(tīng)到某個(gè)大公司HR之間的對(duì)話,原話是:他面試還可以,看起來(lái)是很老實(shí)(某個(gè)之前的面試者),但是他對(duì)之前項(xiàng)目整體流程并不是那么清楚,連自己做的項(xiàng)目,前后端流程都不清楚,感覺(jué)不合適。




          介紹一下Redis,為什么快,怎么做持久化存儲(chǔ),什么叫緩存擊穿?


          答案:Redis將數(shù)據(jù)存儲(chǔ)在內(nèi)存中,key-value形式存儲(chǔ),所以獲取也快。支持的key格式相對(duì)于memorycache更多,而且支持RDB快照形式、AOF





          1.RDB持久化是指在指定的時(shí)間間隔內(nèi)將內(nèi)存中的數(shù)據(jù)集快照寫入磁盤,實(shí)際操作過(guò)程是fork一個(gè)子進(jìn)程,先將數(shù)據(jù)集寫入臨時(shí)文件,寫入成功后,再替換之前的文件,用二進(jìn)制壓縮存儲(chǔ)。RDB是Redis默認(rèn)的持久化方式,會(huì)在對(duì)應(yīng)的目錄下生產(chǎn)一個(gè)dump.rdb文件,重啟會(huì)通過(guò)加載dump.rdb文件恢復(fù)數(shù)據(jù)。


          2、優(yōu)點(diǎn)

          1)只有一個(gè)文件dump.rdb,方便持久化;

          2) 容災(zāi)性好,一個(gè)文件可以保存到安全的磁盤;

          3) 性能最大化,fork子進(jìn)程來(lái)完成寫操作,讓主進(jìn)程繼續(xù)處理命令,所以是IO最大化(使用單獨(dú)子進(jìn)程來(lái)進(jìn)行持久化,主進(jìn)程不會(huì)進(jìn)行任何IO操作,保證了redis的高性能) ;

          4)如果數(shù)據(jù)集偏大,RDB的啟動(dòng)效率會(huì)比AOF更高。



          1)數(shù)據(jù)安全性低。(RDB是間隔一段時(shí)間進(jìn)行持久化,如果持久化之間redis發(fā)生故障,會(huì)發(fā)生數(shù)據(jù)丟失。所以這種方式更適合數(shù)據(jù)要求不是特別嚴(yán)格的時(shí)候)

          2)由于RDB是通過(guò)fork子進(jìn)程來(lái)協(xié)助完成數(shù)據(jù)持久化工作的,因此,如果當(dāng)數(shù)據(jù)集較大時(shí),可能會(huì)導(dǎo)致整個(gè)服務(wù)器停止服務(wù)幾百毫秒,甚至是1秒鐘。




          AOF持久化是以日志的形式記錄服務(wù)器所處理的每一個(gè)寫、刪除操作,查詢操作不會(huì)記錄,以文本的方式記錄,文件中可以看到詳細(xì)的操作記錄。她的出現(xiàn)是為了彌補(bǔ)RDB的不足(數(shù)據(jù)的不一致性),所以它采用日志的形式來(lái)記錄每個(gè)寫操作,并追加到文件中。Redis 重啟的會(huì)根據(jù)日志文件的內(nèi)容將寫指令從前到后執(zhí)行一次以完成數(shù)據(jù)的恢復(fù)工作。




          2、優(yōu)點(diǎn)

          1)數(shù)據(jù)安全性更高,AOF持久化可以配置appendfsync屬性,其中always,每進(jìn)行一次命令操作就記錄到AOF文件中一次。

          2)通過(guò)append模式寫文件,即使中途服務(wù)器宕機(jī),可以通過(guò)redis-check-aof工具解決數(shù)據(jù)一致性問(wèn)題。

          3)AOF機(jī)制的rewrite模式。(AOF文件沒(méi)被rewrite之前(文件過(guò)大時(shí)會(huì)對(duì)命令進(jìn)行合并重寫),可以刪除其中的某些命令(比如誤操作的flushall))

          3、缺點(diǎn)

          1)AOF文件比RDB文件大,且恢復(fù)速度慢;數(shù)據(jù)集大的時(shí)候,比rdb啟動(dòng)效率低。

          2)根據(jù)同步策略的不同,AOF在運(yùn)行效率上往往會(huì)慢于RDB。


          具體可看、

          https://baijiahao.baidu.com/s?id=1631700601579800845&wfr=spider&for=pc


          Redis可以配合session等做服務(wù)端持久化存儲(chǔ)、還介紹了下session的場(chǎng)景,


          介紹下緩存擊穿和穿透:



          緩存穿透,是指查詢一個(gè)數(shù)據(jù)庫(kù)一定不存在的數(shù)據(jù)。正常的使用緩存流程大致是,數(shù)據(jù)查詢先進(jìn)行緩存查詢,如果key不存在或者key已經(jīng)過(guò)期,再對(duì)數(shù)據(jù)庫(kù)進(jìn)行查詢,并把查詢到的對(duì)象,放進(jìn)緩存。如果數(shù)據(jù)庫(kù)查詢對(duì)象為空,則不放進(jìn)緩存。



          緩存擊穿,是指一個(gè)key非常熱點(diǎn),在不停的扛著大并發(fā),大并發(fā)集中對(duì)這一個(gè)點(diǎn)進(jìn)行訪問(wèn),當(dāng)這個(gè)key在失效的瞬間,持續(xù)的大并發(fā)就穿破緩存,直接請(qǐng)求數(shù)據(jù)庫(kù),就像在一個(gè)屏障上鑿開(kāi)了一個(gè)洞。





          介紹下你會(huì)用的自動(dòng)化構(gòu)建的方式:


          答案

          1. Jenkins自動(dòng)化構(gòu)建

          2. 自己搭建Node.js服務(wù)器,實(shí)現(xiàn)Jenkins

          3. Docker配合Travis CI實(shí)現(xiàn)自動(dòng)化構(gòu)建


          細(xì)化答案:


          Jenkins自動(dòng)化構(gòu)建:


          配置,自動(dòng)同步某個(gè)分支代碼,打包構(gòu)建。


          自己搭建Node.js服務(wù)器,實(shí)現(xiàn)Jenkins


          自己搭建Node.js的服務(wù)器,在GitLab上指定webhook地址,分支代碼更新觸發(fā)事件,服務(wù)器接受到post請(qǐng)求,里面附帶分支的信息,執(zhí)行自己的shell腳本命令,指定文件夾,構(gòu)建打包。


          服務(wù)器上使用Docker-compose指定鏡像,每次代碼推送到gitHub,通過(guò)自己編寫的ymldockerfile文件構(gòu)建打包,服務(wù)器自動(dòng)拉取最新鏡像并且發(fā)布到正式環(huán)境



          代碼實(shí)現(xiàn):


          .travis.yml

          language: node_jsnode_js:  - '12'services:  - docker
          before_install: - npm install - npm install -g parcel-bundler
          script: - parcel build ./index.js - echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin - docker build -t jinjietan/mini-react:latest . - docker push jinjietan/mini-react:latest


          dockerfile:


          FROM nginxCOPY ./index.html /usr/share/nginx/html/COPY ./dist /usr/share/nginx/html/distEXPOSE 80





          問(wèn)完Redis,肯定會(huì)問(wèn)數(shù)據(jù)庫(kù),mysql mongodb sqlite都問(wèn)了,這里就暫時(shí)不寫了


          數(shù)據(jù)庫(kù)需要系統(tǒng)的學(xué)習(xí),特別是mysql,我這里就不班門弄斧了,推薦某金上面的小孩子的小冊(cè)。零蛋學(xué)mysql(本文沒(méi)有收取任何廣告費(fèi),自己買了看完才推薦給大家)





          附帶的一些問(wèn)題:


          Linux常見(jiàn)操作


          云端部署



          等。


          當(dāng)然有人會(huì)問(wèn)20-50K的問(wèn)題怎么這么簡(jiǎn)單,因?yàn)槊總€(gè)問(wèn)題都是串起來(lái)的,需要你的知識(shí)面足夠廣,才能一路面下去,直到拿到offer。而且每個(gè)問(wèn)題都是有坑,例如pm2 start 如果不指定參數(shù)到底會(huì)啟動(dòng)多少個(gè)進(jìn)程?? 在云端和自己的電腦上是不一樣的,這些都需要你去有實(shí)際操作經(jīng)驗(yàn)才能應(yīng)對(duì)。??


          本文的初衷,不為了面試而背面試題,只是讓大家知道應(yīng)該學(xué)習(xí)什么方向。純粹為了面試去背誦面試題,是很容易被識(shí)破,只有不斷積累學(xué)習(xí),你才會(huì)輕易拿下offer



          座右銘:你想要在外人看來(lái)毫不費(fèi)力,那你就要平時(shí)不斷努力~






          最后



          如果你覺(jué)得這篇內(nèi)容對(duì)你挺有啟發(fā),我想邀請(qǐng)你幫我三個(gè)小忙:

          1. 點(diǎn)個(gè)「在看」,讓更多的人也能看到這篇內(nèi)容(喜歡不點(diǎn)在看,都是耍流氓 -_-)

          2. 歡迎加我微信「qianyu443033099」拉你進(jìn)技術(shù)群,長(zhǎng)期交流學(xué)習(xí)...

          3. 關(guān)注公眾號(hào)「前端下午茶」,持續(xù)為你推送精選好文,也可以加我為好友,隨時(shí)聊騷。


          點(diǎn)個(gè)在看支持我吧,轉(zhuǎn)發(fā)就更好了


          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋无码专区 | 成人自拍在线视频 | 久久国产精品国产色婷婷 | 久久香蕉网 | 男女操逼视频在线观看 |