Mirantis 收購 Docker EE | 雲原生生態周報 Vol. 28

作者 | 禪鳴、進超、心水、心貴

業界要聞

Mirantis 是一家紮根於 OpenStack 的雲公司,最近專註於 Kubernetes。該公司剛剛收購了 Docker 的企業部門,該業務部門包括 Docker Enterprise 技術平台及所有相關的知識產權、約 400 名員工中的 300 人、750 家企業客戶以及所有企業夥伴關係。

Project Quay 包含一系列在 Apache 2.0 和其他開源許可證下許可的開源軟件。它遵循一個帶有維護者委員會的開源治理模型。

KubeCon + CloudNativeCon North America 2019 於 11 月 18 日 在 San Diego 正式召開。

上游重要進展

KEP

主要為了解決以下問題:

  • PreSidecars 將在普通容器之前啟動,但在 init 容器之後啟動,這樣它們就可以在您的主進程開始之前準備好;
  • PostSidecars 將在普通容器之後啟動,以便它們在您的主進程啟動后可以執行某些操作,例如更新 css 文件,轉發日誌等。

主要為了解決以下問題:

  • Client/Server 版本偏移;
  • API 擴展支持;
  • 提供更簡單的選項來與 cli 工具進行集成(例如 jq);
  • 提供與 unix cli 標準集成的接口(xargs/find -exec/globbing);
  • 保留配置註釋,結構等。

IP地址類型分解為IPv4IPv6。並逐步棄用原有地址類型,其在 1.17 中對新的 EndpointSlices 無效,然後在 1.18 中變得完全無效。

K8S PR

提供一種在 --show-enable-metrcis-for-version 設置時重新註冊隱藏指標的機制。

有兩個原因:

  • 新版本中 http.CloseNotifier 已經被廢棄;
  • 如果請求協議為 HTTP/2.x,原始代碼使用 http.CloseNotifier 的情況下,每一個 Watch 將多花費 1 個 goruntine。在大規模場景下,過多的 goruntine 對 API Server 是一個非常大的負擔和性能瓶頸。

在 Windows 上使用 Containerd 時,將由 kubelet 管理“ C: Windows  System32  drivers  etc  hosts”文件。

為了減少 service controller 在節點有更新時,更新 backend 的延遲。

當提供客戶端證書證書文件后,始終保持從磁盤重新啟動證書文件以進行新連接,並在證書更改時關閉連接。

Knative

當前 Kubernetes 社區(Kubebuilder 和 Metacontroller)正在研究控制平面可伸縮性,認為雖然用於 Kubernetes 工作的”無服務器控制器”是一個思想實驗,但距離我們並不遠,並且在技術上也是可行的。

開源項目推薦

阿里雲容器服務團隊自研 CNI 網絡插件,支持 VPC 和 ENI 等。

Vmware 開源基於 OVS 的 Kubernetes 網絡方案。

KubeSphere 是在 Kubernetes 之上構建的以應用為中心的多租戶容器管理平台,目前已經達到 GA 狀態。

具有硬件資源感知工作負載放置策略的 Kubernetes Container Runtime Interface 代理服務。

本周閱讀推薦

CRDs/controllers 是 Kubernetes 中重要的組件,它們會將集群內的各種資源調整到期望狀態。學習 Reconciling 可以幫助我們更好的理解 CRDs/controllers 是如何工作的。

通過漫畫的形式對 Openshift 及相關產品加以介紹,比較有趣。

隨着時間的推移,Docker 開始根植於我們的日常生活當中。然而,Docker 一切輝煌的背後,技術社區中開始有不少人認為 Docker 正一路朝着沉沒的方向前進。那麼,這樣的判斷有沒有依據?Docker 真的快要不行了嗎?或者說,這隻是技術領域當中部分小年輕們一廂情願的偏執?

由於目前國內並沒有比較好的 Go 語言書籍,而國外的優秀書籍因為英文的緣故在一定程度上也為不少 Go 語言愛好者帶來了一些學習上的困擾。

為了加快擴散 Go 愛好者的國內群體,譯者在完成 《The Way to Go》 一書的閱讀後,決定每天抽出一點時間來進行翻譯工作,並以開源的形式免費分享給有需要的 Go 語言愛好者。

在 Istio 服務網格中,每個 Envoy 佔用的內存也許並不算多,但所有 sidecar 增加的內存累積起來則是一個不小的数字。在進行商用部署時,我們需要考慮如何優化並減少服務網格帶來的額外內存消耗。

Buoyant 創始人、Service Mesh 技術的提出者、第一個 Service Mesh Linkerd 的作者 Willian Morgan 為您解析 Service Mesh 現狀。

“ 阿里巴巴雲原生微信公眾號(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公眾號。”

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

台灣海運大陸貨務運送流程

兩岸物流進出口一站式服務

Tsx寫一個通用的button組件

一年又要到年底了,vue3.0都已經出來了,我們也不能一直還停留在過去的js中,是時候學習並且在項目中使用一下Ts了。

  如果說jsx是基於js的話,那麼tsx就是基於typescript的

  廢話也不多說,讓我們開始寫一個Tsx形式的button組件,

  ts真的不僅僅只有我們常常熟知的數據類型,還包括接口,類,枚舉,泛型,等等等,這些都是特別重要的

  項目是基於vue-cli 3.0 下開發的,可以自己配置Ts,不會的話那你真的太難了

  

 

     我們再compenonts中新建一個button文件夾,再建一個unit文件夾,button放button組件的代碼,unit,放一些公共使用模塊

  我們再button文件夾下創建 ,index .tsx放的button源碼,index.less放的是樣式,css也是不可缺少的

       

 

   分析一下button需要的一些東西

  第一個當然是props,還有一個是點擊事件,所以我們第一步就定義一下這兩個類型

type ButtonProps = {
  tag: string,
  size: ButtonSize,
  type: ButtonType,
  text: String
}

type ButtonEvents = {
  onClick?(event: Event) :void
}
type ButtonSize = 'large' | 'normal' | 'small' | 'mini'
type ButtonType = 'default' | 'primary' | 'info' | 'warning' | 'danger'

  因為button是很簡單的組件,內部也沒有一些特別的狀態需要改變,所以我們用函數式組件的方式去寫(之後的render會用到這個方法)

function Button (h: CreateElement, props: ButtonProps, slots: DefaultSlots, ctx: RenderContext<ButtonProps>) {
  const { tag, size, type } = props
  let text
  console.log(slots)
  text = slots.default ? slots.default() : props.text
  function onClick (event: Event) {
    emit(ctx, 'click', event)
  }
  let classes = [size,type]
  return (
    <tag
      onClick = {onClick}
      class = {classes}
    >
      {text}
    </tag>
  )
}

  h 是一個預留參數,這裏並沒有用到 ,CreateElement  這個是vue從2.5之後提供的一個類型,也是為了方便在vue項目上使用ts

  props 就是button組件的傳入的屬性,slots插槽,ctx,代表的是當前的組件,可以理解為當前rendercontext執行環境this

  DefaultSlots是我們自定義的一個插槽類型

export type ScopedSlot<Props = any> = (props?: Props) => VNode[] | VNode | undefined;

export type ScopedSlots = {
  [key: string]: ScopedSlot | undefined;
}

  插槽的內容我們都是需要從ctx中讀取的,默認插槽的key就是defalut,具名插槽就是具體的name

  button放發內部還有一個具體的點擊事件,還有一個emit方法,從名字我們也可以看的出,他是粗發自定義事件的,我們這裏當然不能使用this.emit去促發,

  所以我們需要單獨這個emit方法,我們知道組件內所以的自定義事件都是保存在listeners里的,我們從ctx中拿取到所以的listeners



  import { RenderContext, VNodeData } from ‘vue’ // 從vue中引入一些類型


function
emit (context: RenderContext, eventName: string, ...args: any[]) { const listeners = context.listeners[eventName] if (listeners) { if (Array.isArray(listeners)) { listeners.forEach(listener => { listener(...args) }) } else { listeners(...args) } }

  這樣我們組件內部的事件觸發就完成了

  我們的button肯定是有一些默認的屬性,所以,我們給button加上默認的屬性

Button.props = {
  text: String,
  tag: {
    type: String,
    default: 'button'
  },
  type: {
    type: String,
    default: 'default'
  },
  size: {
    type: String,
    default: 'normal'
  }
}

  我們定義一個通用的functioncomponent 類型

type FunctionComponent<Props=DefaultProps, PropsDefs = PropsDefinition<Props>> = {
  (h: CreateElement, Props:Props, slots: ScopedSlots, context: RenderContext<Props>): VNode |undefined,
  props?: PropsDefs
}

  PropsDefinition<T>  這個是vue內部提供的,對 props的約束定義

  不管怎麼樣我們最終返回的肯定是一個對象,我們把這個類型也定義一下

  ComponentOptions<Vue> 這個也是vue內部提供的

 interface DrmsComponentOptions extends ComponentOptions<Vue> {
  functional?: boolean;
  install?: (Vue: VueConstructor) => void;
}

  最終生成一個組件對象

function transformFunctionComponent (fn:FunctionComponent): DrmsComponentOptions {
  return {
    functional: true, // 函數時組件,這個屬性一定要是ture,要不render方法,第二個context永遠為underfine
    props: fn.props,
    model: fn.model,
    render: (h, context): any => fn(h, context.props, unifySlots(context), context)
  }
}

  unifySlots 是讀取插槽的內容

// 處理插槽的內容
export function unifySlots (context: RenderContext) {
  // use data.scopedSlots in lower Vue version
  const scopedSlots = context.scopedSlots || context.data.scopedSlots || {}
  const slots = context.slots()

  Object.keys(slots).forEach(key => {
    if (!scopedSlots[key]) {
      scopedSlots[key] = () => slots[key]
    }
  })

  return scopedSlots
}

  當然身為一個組件,我們肯定是要提供全局注入接口,並且能夠按需導入

  所以我們給組件加上名稱和install方法,install 是 vue.use() 方法使用的,這樣我們能全部註冊組件

export function CreateComponent (name:string) {
  return function <Props = DefaultProps, Events = {}, Slots = {}> (
    sfc:DrmsComponentOptions | FunctionComponent) {
    if (typeof sfc === 'function') {
      sfc = transformFunctionComponent(sfc)
    }
    sfc.functional = true
    sfc.name = 'drms-' + name
    sfc.install = install
    return sfc 
  }
}

  index.tsx 中的最後一步,導出這個組件

export default CreateComponent('button')<ButtonProps, ButtonEvents>(Button)

  還少一個install的具體實現方法,加上install方法,就能全局的按需導入了

function install (this:ComponentOptions<Vue>, Vue:VueConstructor) {
  const { name } = this
  Vue.component(name as string, this)
}

 

   最終實現的效果圖,事件的話也是完全ok的,這個我也是測過的

 

   代碼參考的是vant的源碼:

  該代碼已經傳到git:     dev分支應該是代碼全的,master可能有些並沒有合併

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步”網站設計“幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

面向對象和面向過程到底是怎麼回事?

今天下午在一個組的項目回顧會議上,這個同事講了自己用DDD思想對三個模塊的重構。把之前在Service層的一些業務邏輯下沉到了領域層里,由之而引起的討論。

部門經理:“其實你的業務邏輯總體並沒有少,只是把邊界重新劃分了一下。”

一起參与開發的同事:“在第二個模塊中(任務系統,包括了任務拆分,狀態跟蹤等)這種思想比較有優勢,在一三項目中不是很明顯。”

於是引出了我的一個問題:“到底什麼是面向對象,什麼是面向過程,在什麼情況下適合面向對象,什麼場景下適用於面向過程?”

  • 以C語言和Java語言為例: C語言沒有類,但是有結構體,結構體中不能有函數,只能有屬性。這說明了什麼?說明了在面向過程的思考方式中,數據和操作是嚴格分離的
  • C語言中為什麼函數需要定義到調用此函數的前面,也就是說先聲明后調用?如果按照流程化的思路來看這種設計方式,想要調用一個子流程,勢必要在調用之前就定義好
  • 而在java的類中,就沒有函數定義先後的問題,這與面向過程和面向對象的最小定義粒度有關,面向過程的最小定義粒度為流程(方法、操作、函數),而在面向對象中,最小定義粒度為對象,這個對象的行為沒有先後,包含在對象這個大的容器中。
  • 封裝、抽象、繼承、多態其實就是類比的對象進行的建模,比如以人為例,人有些屬性不想示人,有些屬性只能給指定的人了解,這就是封裝。人掌握的知識其實是現象的一種抽象。人繼承來來自父母的一些生活習慣,而又有所不同,這就是多態。
  • 歸總, 子類相對父類來說有不同的模型(對真實世界的建模),這是4種面向對象的終極原因。 
  • 為什麼面向對象的思考方式更有利於擴展維護?拿一個工作崗位為例,一個人在一個工作崗位上,如果有一天這個崗位有了更多的工作要求,如果改動量較小,那麼對該崗位的人進行技能培訓就可以了。如果要求多到一種程度,拆分成兩個人,或者拆分成多個崗位。而如果用面向過程的思路,那麼每次改動,都相當於多了一個流程?(這裏存疑,多流程的問題在哪?難維護的理由是什麼?這裏我沒有想明白
  • 面向過程要求人有更好的流程化思維方式,面向對象要求人有更好的抽象思維方式。那麼如果有一天出現一個“面向文檔編程”呢?要求人有更好的把問題描述清楚的表達能力。換句話說, 面向過程就是面向流程思考,面向對象就是針對模型思考

最後距離,如果我們描述入職流程,一個大牛的入職流程可能和一個應屆生的入職流程完全不一樣,如果把入職這個行為寫到employee的方法中,那麼這就是面向對象的寫法,如果維護一個入職流程的方法,根據不同的人用switch case的方式進行不同行為的跳轉,那麼就是面向過程。

面向過程就是面向流程思考,面向對象就是針對模型思考

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

GM 首款自動駕駛電動車將透過租車平台展開服務

2016 年年初GM 宣佈向線上租車服務平臺Lyft 投資5 億美元,雙方將在自動駕駛汽車的應用上展開合作,近日GM 高管Pam Fletcher 透露,GM 的首款自動駕駛電動車將透過Lyft 的租車服務平臺推出,能夠為用戶提供更好的乘坐體驗。

目前各大傳統汽車廠商紛紛進入無人駕駛領域,GM 也不例外,據GM 自動駕駛技術部門首席工程師Pam Fletcher 透露,雖然GM 還沒有正式宣佈自動駕駛車的發表日期,但這一切會比外界預期的更早到來,GM 正在和線上租車服務Lyft 展開合作,開發一個租車分享平臺,這不是一個停留在概念階段的專案,GM 的團隊已經準備好把這一服務推廣到市場。

GM 的首款自動駕駛汽車將是電動車,目前電動車是GM 重點推進的產品,2016 年年底該公司將推出可遠程行駛的電動車BoltEV,這是一款為城市通勤設計的電動車,同時也考慮了租車服務的需求,Pam Fletcher 認為將自動駕駛技術應用在電動車上非常有意義,能夠給用戶帶來更好的乘坐體驗,電動車行駛時平穩、安靜,乘客可以在車中休息或者處理工作事務。

2016 年3 月GM 收購自動駕駛技術研發公司Cruise Automation,以提升該公司在這一領域的技術實力,2016 年5 月Cruise 帶來的自動駕駛技術已經在Bolt 電動車上進行測試。目前GM、Lyft 共同研發的租車分享系統與Bolt 電動車的自動駕駛技術測試分屬不同的專案,但未來有可能會結合。

據悉GM 和Lyft 有可能在2016 年年底將自動駕駛電動車帶來公路上測試,Bolt 電動車有可能成為主要的測試車款。Bolt 電動車的許多設計看起來都像是為自動駕駛而設計的,未來自動駕駛與線上租車服務結合也並不讓人感到意外。

(本文授權自《》──〈〉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步”網站設計“幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

最強新能源汽車 奧迪發出核動力超跑

為什麼奧迪MesarthimF-Tron Quattro是最強新能源汽車?因為它的複雜程度,已經超出了你的物理知識認知。如果對新能源汽車大概分個等級,那麼“二次電池”驅動的純電動車是一級,插電式混合動力是二級,太陽能汽車三級,燃料電池汽車四級,那麼最強的是?當然是核動力汽車。  
  據稱,奧迪未來的超級跑車即是一款核動力汽車,代號Mesarthim F-Tron Quattro,由俄羅斯工程師操刀設計。外型前衛十足,頗有蝙蝠車的味道。   核動力主要部件核反應爐與離子發射器位於前後軸之間,旁邊是發熱裝置,產生的蒸汽進而驅動電機發電,動力電池安裝在前艙,使用四個輪轂電機驅動車輪前進,同時還有電機驅動離子發射器、冷凝器等,除了核燃料供給,其它整個系統構成一個閉環生態。  
  由於動力系統是個獨立部分,導致其它部件的安裝位置是個問題,為解決這個問題,工程師設計了一套叫做“Solid Cage”的獨立底盤,這個底盤是聚合物材料,可以3D列印出來,而且可以獨立拆卸。   除此以外,底盤上還有一個平底罐,裡面裝有磁流體,在有磁性的路面上行駛時,車子就會產生下壓力,過彎的時候能抵制側傾力,直行的時候抑制抬頭點頭。   雖然奧迪暫時沒有給出這款車具體推出時間表,總體來說還是值得稱讚的。   文章來源:蓋世汽車

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

mysql 索引筆記

MyISAM引擎的B+Tree的索引

通過上圖可以直接的看出, 在MyISAM對B+樹的運用中明顯的特點如下:

  • 所有的非恭弘=叶 恭弘子節點中存儲的全部是索引信息
  • 在恭弘=叶 恭弘子節點中存儲的 value值其實是 數據庫中某行數據的index

MyISAM引擎 索引文件的查看:

在 /var/lib/mysql目錄中

.myd 即 my data , 數據庫中表的數據文件

.myi 即 my index , 數據庫中 索引文件

.log 即 mysql的日誌文件

InnoDB引擎 索引文件的查看:

同樣在 /var/lib/mysql 目錄下面

InnoDB引擎的B+Tree的索引

InnoDB的實現方式業內也稱其為聚簇索引, 什麼是聚簇索引呢? 就是相鄰的行的簡直被存儲到一起, 對比上面的兩幅圖片就會發現, 在InnDB中, B+樹的恭弘=叶 恭弘子節點中存儲的是數據行中的一行行記錄, 缺點: 因為索引文件被存放在硬盤上, 所以很占硬盤的空間

一般我們會在每一個表中添加一列 取名 id, 設置它為primary key , 即將他設置成主鍵, 如果使用的存儲引擎也是InnoDB的話, 底層就會建立起主鍵索引, 也是聚簇索引, 並且會自動按照id的大小為我們排好序,(因為它的一個有序的樹)

局部性原理

局部性原理是指CPU訪問存儲器時,無論是存取指令還是存取數據,所訪問的存儲單元都趨於聚集在一個較小的連續區域中。 更進一步說, 當我們通過程序向操作系統發送指令讓它讀取我們指定的數據時, 操作系統會一次性讀取一頁(centos 每頁4kb大小,InnoDB存儲引擎中每一頁16kb)的數據, 它遵循局部性理論, 猜測當用戶需要使用某個數據時, 用戶很可能會使用這個數據周圍的數據,故而進行一次

InnoDB的頁格式

什麼是頁呢? 簡單說,就是一條條數據被的存儲在磁盤上, 使用數據時需要先將數據從磁盤上讀取到內存中, InnoDB每次讀出數據時同樣會遵循 局部性原理, 而不是一條條讀取, 於是InnoDB將數據劃分成一個一個的頁, 以頁作為和磁盤之間交互的基本單位

通過如下sql, 可以看到,InnoDB中每一頁的大小是16kb

show global status like 'Innodb_page_size';

名稱 簡述
File Header 文件頭部, 存儲頁的一些通用信息
Page Header 頁面頭部, 存儲數據頁專有的信息
Infinum + supremum 最大記錄和最小記錄, 這是兩個虛擬的行記錄
User Records 用戶記錄, 用來實際存儲行記錄中的內容
Free Space 空閑空間, 頁中尚位使用的空間
Page Directory 頁面目錄, 存儲頁中某些記錄的位置
File Tailer 文件尾部 , 用來校驗頁是否完整

InnoDB的行格式 compact

每一頁中存儲的行數據越多. 整體的性能就會越強

compact的行格式如下圖所示

可以看到在行格式中在存儲真正的數據的前面會存儲一些其他信息, 這些信息是為了描述這條記錄而不得不添加的一些信息, 這些額外的信息就是上圖中的前三行

  • 變長字段的長度列表

在mysql中char是固定長度的類型, 同時mysql還支持諸如像 varchar這樣可變長度的類型, 不止varchar , 想 varbinary text blob這樣的變長數據類型, 因為 變長的數據類型的列存儲的數據的長度是不固定的, 所以說我們在存儲真正的數據時, 也得將這些數據到底佔用了多大的長度也給保存起來

  • NULL標誌位

compact行格式會將值可以為NULL的列統一標記在 NULL標誌位中, 如果數據表中所有的字段都被標記上not null , 那麼就沒有NULL值列表

  • 記錄頭信息

記錄頭信息, 顧名思義就是用來描述記錄頭中的信息, 記錄頭信息由固定的5個字節組成, 一共40位, 不同位代表的意思也不同, 如下錶

名稱 單位 bit 簡介
預留位1 1 未使用
預留位2 1 未使用
delete_mark 1 標記改行記錄是否被刪除了
min_rec_mark 1 標記在 B+樹中每層的非恭弘=叶 恭弘子節點中最小的node
n_owned 4 表示當前記錄擁有的記錄數
heap_no 13 表示當前記錄在堆中的位置
record_type 3 表示當前記錄的類型 , 0表示普通記錄, 1表示B+樹中非恭弘=叶 恭弘子節點記錄, 2表示最小記錄 ,3表示最大記錄
next_record 16 表示下一條記錄的相對位置

行溢出

在mysql中每一行, 能存儲的最大的字節數是65535個字節數, 此時我們使用下面的sql執行時就會出現行溢出現象

CREATE TABLE test ( c VARCHAR(65535) ) CHARSET=ascii ROW_FORMAT=Compact;

給varchar申請最大65535 , 再加上compact行格式中還有前面三個非數據列佔用內存,所以一準溢出, 如果不想溢出, 可以適當的將 65535 – 3

頁溢出

前面說了, InnoDB中數據的讀取按照頁為單位, 每一頁的大小是 16kb, 換算成字節就是16384個字節, 但是每行最多存儲 65535個字節啊, 也就是說一行數據可能需要好幾個頁來存儲

怎麼辦呢?

  • compact行格式會在存儲真實數據的列中多存儲一部分數據, 這部分數據中存儲的就是下一頁的地址
  • dynamic行格式 中直接存儲數據所在的地址, 換句話說就是數據都被存儲在了其他頁上
  • compressed行格式會使用壓縮算法對行格式進行壓縮處理

一般我們都是將表中的id列設置為主鍵, 這就會形成主鍵索引, 於是我們需要注意了:

主鍵的佔用的空間越小,整體的檢索效率就會越高

為什麼這麼說呢? 這就可以結合頁的概念來解析, 在B+樹這種數據結果中, 恭弘=叶 恭弘子節點中用來存儲數據, 存儲數據的格式類似Key-value key就是索引值, value就是數據內容, 如果索引佔用的空間太大的話, 單頁16kb能存儲的索引就越小, 這就導致數據被分散在更多的頁上, 致使查詢的效率降低

建立索引的技巧

為某一列建立索引

給text表中的title列創建索引, 索引名字 my_index
alter table text add index my_index (title);

雖然建立索引能提升查詢的效率, 根據前人的經驗看, 這並不是一定的, 建立索引本身會直接消耗內存空間, 同時索, 插入,刪除, 這種寫操作就會打破B+樹的平衡面臨索引的重建, 一般出現如下兩種情況時,是不推薦建立索引的

  1. 表中的數據本身就很少
  2. 我們計算一下索引的選擇性很低

兼顧 – 索引的選擇性與前綴索引

所謂選擇性,其實就是說不重複出現的索引值(基數,Cardinality) 與 表中的記錄數的比值

即: 選擇性= 基數 / 記錄數

選擇性的取值範圍在(0,1]之間, 選擇性越接近1 , 說明建立索引的必要性就越強, 比如對sex列進行建立索引,這裏面非男即女, 如果對它建立索引的話, 其實是沒意義的, 還不如直接進行全表掃描來的快

如何使用sql計算選擇性呢? 嚴格遵循上面的公式

SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM employees.titles;
count(基數/記錄數)
DISTINCT(title) / /count(*)

更詳細的例子看下面的連接

索引失效問題

注意事項

  • 索引無法存儲null值

  • 如果條件中有or, 即使條件中存在索引也不會使用索引,如果既想使用or,又想使用索引, 就給所有or條件控制的列加上索引

  • 使用like查詢時, 如果以%開頭,肯定是進行全表掃描

  • 使用like查詢時, 如果%在條件後面

    • 對於主鍵索引, 索引失效
    • 對於普通索引, 索引不失效
  • 如果列的類型是字符串類型, 那麼一定要在條件中將數據用引號引起來,不然也會是索引失效

  • 如果mysql認為全表掃描比用索引塊, 同樣不會使用索引

聯合索引

什麼是聯合索引

聯合索引, 也叫複合索引,說白了就是多個字段一起組合成一個索引

像下面這樣使用 id + title 組合在一起構成一個聯合索引

CREATE TABLE `text` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`id`,`title`)
) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8
  • 如果我們像上圖那樣創建了索引,我們只要保證我們的 id+title 兩者結合起來全局唯一就ok
  • 建立聯合索引同樣是需要進行排序的,排序的規則就是按照聯合索引所有列組成的字符串的之間的先後順序進行排序, 如a比b優先

左前原則

使用聯合索引進行查詢時一定要遵循左前綴原則, 什麼是左前綴原則呢? 就是說想讓索引生效的話,一定要添加上第一個索引, 只使用第二個索引進行查詢的話會導致索引失效

比如上面創建的聯合索引, 假如我們的查詢條件是 where id = ‘1’ 或者 where id = ‘1’ and title = ‘唐詩宋詞’ 索引都會不失效

但是如果我們不使用第一個索引id, 像這樣 where title = ‘唐詩’ , 結果就是導致索引失效

聯合索引的分組&排序

還是使用這個例子:

CREATE TABLE `text` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  PRIMARY KEY (`id`,`title`)
) ENGINE=InnoDB AUTO_INCREMENT=3691 DEFAULT CHARSET=utf8

demo1: 當我們像下面這樣寫sql時, 就會先按照id進行排序, 當id相同時,再按照title進行排序

select * form text order by id, title;

demo2: 當我們像下面這樣寫sql時, 就會先將id相同的劃分為一組, 再將title相同的劃分為一組

select id,title form text group by id, title;

demo3: ASC和DESC混用, 其實大家都知道底層使用B+樹, 本身就是有序的, 要是不加限制的話,默認就是ASC, 反而是混着使用就使得索引失效

select * form text order by id ASC, title DESC;

如何定位慢查詢

相關參數

名稱 簡介
slow_query_log 慢查詢的開啟狀態
slow_query_log_file 慢查詢日誌存儲的位置
long_query_time 查詢超過多少秒才記錄下來

常用sql

# 查看mysql是否開啟了慢查詢
show variables like 'slow_query_log';   
# 將全局變量設置為ON
set global slow_query_log ='on';
# 查看慢查詢日誌存儲的位置
show variables like 'slow_query_log_file';
# 查看規定的超過多少秒才被算作慢查詢記錄下來
show variables like 'long_query_time';
show variables like 'long_query%';
# 超過一秒就記錄 , 每次修改這個配置都重新建立一次鏈接
set global long_query_time=1; 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

CSS(8)—通俗講解定位(position)

CSS(8)—通俗講解定位(position)

CSS有三種基本的定位機制: 普通流浮動定位。前面兩個之前已經講過,詳見博客:

1、

2、

3、

一、為什麼要用定位?

如果說浮動關鍵在一個 “浮” 字上面, 那麼 我們的定位,關鍵在於一個 “位” 上。

我們來思考下定位用於的場景。

1、打Log標籤

比如你想在商品的圖片想打個標籤比如:包郵、最新上架等等。

怎麼做比較好呢,如果你要粗暴那就直接ps在圖片上添加標籤。只是這樣有個很大的弊端,比如你要添加新標籤你需要重現修圖,比如商品之前包郵後面不包郵了,

那你又需要重新p圖。這樣肯定是不合適的。那怎麼做比較合適?

其實很簡單,將商品圖片和標籤的標籤分開來。然後通過css在商品圖片上添加標籤。這個時候通常會定位去完成。

2、切換Banner

有些商城的首頁都會有個Banner,這裏 左右的箭頭下面的小點點一般也是用定位來做。

3、廣告位窗口

有些位置在左右側會有固定的廣告窗口,不論怎麼滑動頁面這個廣告窗口都是在固定位置

這個就需要用到固定定位了。

二、定位概念

1、定位的分類

在CSS中,position 屬性用於定義元素的定位模式,其基本語法格式如下:

選擇器 {position:屬性值;}

屬性值

這裏還有個概念就是 邊偏移 因為你定位肯定要指定定位在哪裡,所以需要通過 邊偏移 來指定。

所以定位是要和邊偏移搭配使用的。不過對於static(靜態定位)設置邊偏移是無用的。

2、靜態定位(static)

static 是此屬性的默認值。這時候,我們稱那個元素沒有被定位。簡單來說就是網頁中所有元素都默認的是靜態定位。 其實就是標準流的特性。

所以如果需要使用定位那這裏就不能是這個默認值了。

注意 在靜態定位狀態下,此時 top, right, bottom, left 和 z-index 屬性無效。

3、相對定位(relative)

它的主要特點如下

1、 參照元素原來的位置進行移動。
2、 通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位。
3、 元素原有的空間位保留。
4、 元素在移動時會蓋住其他元素。

舉例說明

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>相對定位</title>
        <style type="text/css">
            #one {
                width: 120px;
                height: 120px;
                background: #E19D59;
            }
            #two {
                width: 120px;
                height: 120px;
                background: #FF0000;
                position: relative;   /*設置相對定位*/
                left: 20px;           /*設置距離左邊偏移位置*/
                top: 20px;            /*設置距離頂部偏移位置*/
            }
            #three {
                width: 120px;
                height: 120px;
                background: #008000;
            }
        </style>
    </head>
    <body>      
        <div id="one">div1</div>
        <div id="two">div2</div>
        <div id="three">div3</div>        
    </body>
</html>

運行結果

通過我們這個示例我們可以看出

1、它的左右,上下邊偏移的量是根據這個div2原始位置基礎上進行移動的。
2、這個div2它還是個標準流,並沒有浮起來,所以這個位置它還是佔有的。(如果div2浮動那麼div3就會向上移動,這裏顯然沒有)
3、當它偏移后 如果和其它元素有重疊,它會覆蓋其它元素。(div2覆蓋了部分div3元素)

作用 我的理解相對定位主要用途是用來給絕對定位的一個盒子。(下面會解釋這句話)

4、絕對定位absolute

特點

1、參照距離他最近的有定位屬性的父級元素進行移動
2、通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位
3、元素完全脫離文檔流,原有位置不再保留
4、元素在移動時會蓋住其他元素
5、一般我們設置絕對定位時,都會找一個合適的父級將其設置為相對定位。最好為這個具有相對定位屬性的父級設置寬高

舉例說明

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title></title>
        <style type="text/css">
           #father{
                width: 400px;
                height: 400px;
                margin: 100px;
               /*  position: relative;*/
                background: yellow;
            }
            #bd1{
                width: 120px;
                height: 120px;
                background: #E19D59;
            }
            #bd2{
                width: 120px;
                height: 120px;
                background: #FF0000;
                position: absolute;
                left: 20px;
                top: 20px;
            }
            #bd3{
                width: 120px;
                height: 120px;
                background: #008000;
            }
        </style>
    </head>
    <body>  
    <div id="father"> 
        <div id="bd1">div1</div>
        <div id="bd2">div2</div>
        <div id="bd3">div3</div>  
    </div>         
    </body>
</html>

運行結果

從這幅圖可以看出一點

這裏因為父div沒有設置定位,所以它的位置是相對於body進行邊偏移。

這個時候我們將父標籤設置 position: relative;

再刷新頁面

從這張圖很直觀看到:

1、因為父div設置了定位,所以這裏的邊偏移變成都是相對於父div進行偏移(正常貼標籤就是這樣)
2、我們可以看出當設置絕對定位后,該元素已經脫離文檔流,已經浮上來了(因為div2上浮所有div3才會上移)
3、元素在移動時會蓋住其他元素 (div2覆蓋了部分div3)

5、固定定位(fixed)

特點

1、以body為定位時的對象,總是根據瀏覽器的窗口來進行元素的定位
2、通過"left"、 "top"、 "right"、 "bottom" 屬性進行定位
3、元素完全脫離文檔流,原有位置不再保留
4、元素不會隨着文檔流的滑動而滑動

固定定位最大的特點就是第一點,可以理解成它是以可視區域為準,會一直显示在可視區域,屏幕滑動也會显示在定位的位置。

6、四種定位總結

還有比較重要的三點

定位模式轉換

跟 浮動一樣, 元素添加了 絕對定位和固定定位之後, 元素模式也會發生轉換, 都自動轉換為 行內塊元素。

絕對定位的盒子水平/垂直居中

注意 普通的盒子是左右margin 改為 auto就可, 但是對於絕對定位就無效了。

定位的盒子也可以水平或者垂直居中,有一個算法(下面會舉例說明)。

1. 首先left 50%   父盒子的一半大小
2. 然後走自己外邊距負的一半值就可以了 margin-left。

子絕父相

這句話的意思是 子級是絕對定位的話,父級要用相對定位

為什麼會有這個概念,那是因為絕對定位的邊偏移特點是

 如果父元素沒有設置定位,那麼它的位置是相對於body進行邊偏移。如果父元素設置定位,那就根據父元素偏移。

一般我們肯定是希望根據父元素偏移。就好比圖片打標籤,不可能跟着body偏移而是父元素進行定位。而且父元素相對定位最大的好處就是它會佔有位置,因此父親最好是 相對定位。

三、經典示例

1、打上log標記

示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>log標籤</title>
    <style>
    div {
        width: 310px;
        height: 190px;
        border: 1px solid #ccc;
        margin: 100px auto; 
        position: relative;  /*父選擇相對定位*/
    }
    .top {
        position: absolute; /*子取相對定位*/
        top: 0;             /*位置 左上*/
        left: 0;
    }
    
    </style>
</head>
<body>
    <div>
        <img src="images/log.jpg" alt="" class="top">     <!-- log的圖片 -->
        <img src="images/goods.jpg" height="190" width="310" alt=""> <!-- 商品圖片,長和寬和父div大小一致 -->
    </div>
</body>
</html>

運行結果就是上面的最終結果。

2、定位水平居中

加了定位 浮動的的盒子 margin 0 auto 失效了

代碼

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水平居中</title>
    <style>
    div {
        width: 200px;
        height: 200px;
        background-color: pink;
        position: absolute;
        /*加了定位 浮動的的盒子  margin 0 auto 失效了*/
        left: 50%;
        margin-left: -100px;  /*減去總寬度一般*/
        top: 50%;
        margin-top: -100px;   /*減去總高度一般*/
    }
    </style>
</head>
<body>
    <div></div>
</body>
</html>

這個這個div就處於整個頁面的居中了,這裏我們來說明下下面這兩個的意思

        left: 50%;
        margin-left: -100px;  /*減去總寬度一般*/

3、輪播圖

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>輪播圖</title>
    <style>
    * {
        margin: 0;
        padding: 0;
    }
    li {
        list-style: none;
    }
    .tb {
        width: 520px;
        height: 280px;
        background-color: pink;
        margin: 100px auto;
        position: relative;
    }
    .tb a {
        width: 24px;
        height: 36px;
        
        display: block;
        position: absolute;
        top: 50%;
        margin-top: -18px;
    }
    .left {
        left: 0;
        background: url(images/left.png) no-repeat;
    }
    .right {
        right: 0;
        background: url(images/right.png) no-repeat;
    }
    .tb ul {
        width: 70px;
        height: 13px;
        background: rgba(255, 255, 255, .3);
        position: absolute; /* 加定位*/
        bottom: 18px;
        left: 50%; /*水平走父容器的一半*/
        margin-left: -35px; /*左走自己的一半*/
        border-radius: 8px;
    }
    .tb ul li {
        width: 8px;
        height: 8px;
        background-color: #fff;
        float: left;
        margin: 3px;
        border-radius: 50%;
    }
    .tb .current {
        background-color: #f40;
    }
    </style>
</head>
<body>
    <div class="tb">
        <img src="images/tb.jpg" >
        <a href="#" class="left"></a>
        <a href="#" class="right"></a>
        <ul>
            <li class="current"></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li>
        </ul>

    </div>
</body>
</html>

運行結果

參考

1、

2、

你如果願意有所作為,就必須有始有終。(10)

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

如何教會女友遞歸算法?

一到周末就開始放蕩自我,這不帶着女朋友去萬達電影院看電影(其實是由於整天呆在家敲代碼硬是

被女朋友強行拖拽去看電影,作為一個有理想的程序員,我想各位應該都能體諒我),一到電影院,

女朋友說要買爆米花和可樂,我當時二話沒說,臣本布衣躬耕於南陽,壤中羞澀,所以單買了爆米

花,買完都不帶回頭看老闆的那種,飲料喝多了不好,出門的時候我帶了白開水,還得虧我長得銷

魂,乍一看就能看出是個社會精神小伙,女朋友也沒多說什麼,只是對我微了微笑(我估計是被我的

顏值以及獨到的見解所折服),剛坐下沒多久,女朋友突然問我,咱們現在坐在第幾排啊?電影院里

面太黑了,看不清,沒法數,這個時候,如果是你現在你怎麼辦?別忘了你我是程序員,這個可難不

倒我,遞歸就開始排上用場了。於是我就問前面一排的人他是第幾排,你想只要在他的数字上加一,

就知道自己在哪一排了。但是,前面的人也看不清啊,所以他也問他前面的人。就這樣一排一排往前

問,直到問到第一排的人,說我在第一排,然後再這樣一排一排再把数字傳回來。直到你前面的人告

訴你他在哪一排,於是你就知道答案了。這就是一個非常標準的遞歸求解問題的分解過程,去的過程

叫“遞”,回來的過程叫“歸”。基本上,所有的遞歸問題都可以用遞推公式來表示。我們用遞推公式將

它表示出來就是這樣的

f ( n ) = f (n – 1) + 1 其中,f ( 1 ) = 1

f(n)表示你想知道自己在哪一排,f(n-1)表示前面一排所在的排數,f(1)=1表示第一排的人知道自己在

第一排。有了這個遞推公式,我們就可以很輕鬆地將它改為遞歸代碼,如下:

int f(int n) {
  if (n == 1) return 1;
  return f(n-1) + 1;
}

女朋友不懂遞歸,於是我給她講遞歸需要滿足的三個條件:

1.一個問題的解可以分解為幾個子問題的解

何為子問題?子問題就是數據規模更小的問題。就好比,在電影院,你要知道,“自己在哪一排”的問

題,可以分解為“前一排的人在哪一排”這樣一個子問題。

2.這個問題與分解之後的子問題,除了數據規模不同,求解思路完全一樣

你求解“自己在哪一排”的思路,和前面一排人求解“自己在哪一排”的思路,是一模一樣的。

3.存在遞歸終止條件

把問題分解為子問題,把子問題再分解為子子問題,一層一層分解下去,不能存在無限循環,這就需

要有終止條件。就好比,第一排的人不需要再繼續詢問任何人,就知道自己在哪一排,也就是

f(1)=1,這就是遞歸的終止條件。

如何教女友敲遞歸代碼?

剛剛鋪墊了這麼多,現在我們來看,如何來教女友敲遞歸代碼?個人覺得,寫遞歸代碼最關鍵的是寫

出遞推公式,找到終止條件,剩下將遞推公式轉化為代碼就很簡單了。

你先記住這個理論。我舉一個例子,帶你一步一步實現一個遞歸代碼,幫你理解。

假如這裡有n個台階,每次你可以跨1個台階或者2個台階,請問走這n個台階有多少種走法?如果有7個台階,你可以2,2,2,1這樣子上去,也可以1,2,1,1,2這樣子上去,總之走法有很多,那如何用編程求得總共有多少種走法呢?

我們仔細想下,實際上,可以根據第一步的走法把所有走法分為兩類,第一類是第一步走了1個台

階,另一類是第一步走了2個台階。所以n個台階的走法就等於先走1階后,n-1個台階的走法 加上先

走2階后,n-2個台階的走法。用公式表示就是:

f ( n ) = f (n – 1) + f ( n – 2 )

有了遞推公式,遞歸代碼基本上就完成了一半。我們再來看下終止條件。當有一個台階時,我們不需

要再繼續遞歸,就只有一種走法。所以f(1)=1。這個遞歸終止條件足夠嗎?我們可以用n=2,n=3這樣

比較小的數試驗一下。

n=2時,f(2)=f(1)+f(0)。如果遞歸終止條件只有一個f(1)=1,那f(2)就無法求解了。所以除了f(1)=1這一

個遞歸終止條件外,還要有f(0)=1,表示走0個台階有一種走法,不過這樣子看起來就不符合正常的

邏輯思維了。所以,我們可以把f(2)=2作為一種終止條件,表示走2個台階,有兩種走法,一步走完

或者分兩步來走。

所以,遞歸終止條件就是f(1)=1,f(2)=2。這個時候,你可以再拿n=3,n=4來驗證一下,這個終止條

件是否足夠並且正確。

我們把遞歸終止條件和剛剛得到的遞推公式放到一起就是這樣的:

f(1) = 1;
f(2) = 2;
f(n) = f(n-1)+f(n-2)

有了這個公式,我們轉化成遞歸代碼就簡單多了。最終的遞歸代碼是這樣的:

int f(int n) {
  if (n == 1) return 1;
  if (n == 2) return 2;
  return f(n-1) + f(n-2);
}

我總結一下,寫遞歸代碼的關鍵就是找到如何將大問題分解為小問題的規律,並且基於此寫出遞推公式,然後再推敲終止條件,最後將遞推公式和終止條件翻譯成代碼。

如果以後再遇到類似問題,A可以分解為若干子問題B、C、D情況,你可以假設子問題B、C、D已經

解決,在此基礎上思考如何解決問題A。而且,你只需要思考問題A與子問題B、C、D兩層之間的關

系即可,不需要一層一層往下思考子問題與子子問題,子子問題與子子子問題之間的關係。屏蔽掉遞

歸細節,這樣子理解起來就簡單多了。

因此,編寫遞歸代碼的關鍵是,只要遇到遞歸,我們就把它抽象成一個遞推公式,不用想一層層的調

用關係,不要試圖用人腦去分解遞歸的每個步驟

如何教女友玩轉漢羅塔

好了,講完了遞歸算法,再回到電影院,不說別的,我還真那麼做了,我真問了前面一排的人他是第

幾排如果不清楚並讓他跟我一樣問他的上一排,顯然,沒循環到第三人,我差點被認為是神經病,差

點沒被幾個社會精神小伙打si,座位事情暫時告一段落,話說這電影屬實夠無聊,於是我不知是趁熱

打鐵,還是心血來潮,非要給女朋友玩一個漢羅塔遊戲,我這暴脾氣,剛實踐遞歸算法被懟,是時候

挽回形象了,不秀一把遞歸算法我就不得勁。就是這個遊戲,至於遊戲規則,我覺得你體驗一兩把絕

對比我說的更加記憶深刻,,別看4399覺得有點弱zhi,再怎麼說也承

載着童年

果然,女朋友是個哈皮,剛過第三關就撲街了,這個時候,頭冒五丈光芒的我身披金甲挺身而出(貌

似有一點點小誇張,劇情需要嘛)一聲不吭地敲了幾行靚麗的代碼

public class TestHanoi {

    public static void main(String[] args) {
        hanoi(5,'A','B','C');  //可以理解為5個圈或者第5關
    }
    
    /**
     * @param n     共有N個圈
     * @param A    開始的柱子
     * @param B 中間的柱子
     * @param C 目標的柱子
     * 無論有多少個圈,都認為只有兩個。上面的所有圈和最下面一個圈。
     */
    public static void hanoi(int n,char A,char B,char C) {
        //只有一個圈。
        if(n==1) {
            System.out.println("第1個盤子從"+A+"移到"+C);
        //無論有多少個圈,都認為只有兩個。上面的所有圈和最下面一個圈。
        }else {
            //移動上面所有的圈到中間位置
            hanoi(n-1,A,C,B);
            //移動下面的圈
            System.out.println("第"+n+"個圈從"+A+"移到"+C);
            //把上面的所有圈從中間位置移到目標位置
            hanoi(n-1,B,A,C);
        }
    }

}

只要main方法一致行,對着結果移動即可,就跟開了掛一樣的,其實漢羅塔問題核心關鍵是無論有多少個圈,都認為只有兩個。上面的所有圈和最下面一個圈。

到這裏,教女友敲遞歸算法代碼,你學會了嗎?

哦豁,明天還是一個晴天~老天賜給宜春一個女朋友吧~畢竟我們程序員長得又帥敲代碼又好看,是吧哥幾個~~

如果本文對你有一點點幫助,那麼請點個讚唄,謝謝~

最後,若有不足或者不正之處,歡迎指正批評,感激不盡!如果有疑問歡迎留言,絕對第一時間回復!

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術,說好了來了就是盆友喔…

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

五分鐘學會HTML5的WebSocket協議

1、背景

  很多網站為了實現推送技術,所用的技術都是Ajax輪詢。輪詢是在特定的的時間間隔由瀏覽器對服務器發出HTTP請求,然後由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。HTML5新增的一些新協議WebSocket,可以提供在單個TCP連接上提供全雙工,雙向通信,能夠節省服務器資源和帶寬,並且能夠實時進行通信。

2、WebSocket介紹

  傳統的http也是一種協議,WebSocket是一種協議,使用http服務器無法實現WebSocket,

2.1.瀏覽器支持情況

基本主流瀏覽器都支持

2.2.優點

相對於http有如下好處:

  • 1.客戶端與服務器只建立一個TCP連接,可以使用更少的連接。
  • 2.WebSocket服務器端可以主動推送數據到客戶端,更靈活高效。
  • 3.更輕量級的協議頭,減少數據傳送量。

對比輪訓機制

3、WebSocket用法

  我們了解WebSocket是什麼,有哪些優點后,怎麼使用呢?

3.1.WebSocket創建

WebSocket使用了自定義協議,url模式與http略有不同,未加密的連接是ws://,加密的連接是wss://,WebSocket實例使用new WebSocket()方法來創建,

var ws = new WebSocket(url, [protocol] );

第一個參數 url, 指定連接的 URL。第二個參數 protocol 是可選的,指定了可接受的子協議。

3.2.WebSocket屬性

當創建ws對象后,readyState為ws實例狀態,共4種狀態

  • 0 表示連接尚未建立。

  • 1 表示連接已建立,可以進行通信。

  • 2 表示連接正在進行關閉。

  • 3 表示連接已經關閉或者連接不能打開。

Tips:在發送報文之前要判斷狀態,斷開也應該有重連機制。

3.3.WebSocket事件

在創建ws實例對象后,會擁有以下幾個事件,根據不同狀態可在事件回調寫方法。

  • ws.onopen 連接建立時觸發

  • ws.onmessage 客戶端接收服務端數據時觸發

  • ws.onerror 通信發生錯誤時觸發

  • ws.onclose 連接關閉時觸發

ws.onmessage = (res) => {
  console.log(res.data);
};

ws.onopen = () => {
  console.log('OPEN...');
};

ws.onclose=()=>{
 console.log('CLOSE...');
}

3.4.WebSocket方法

  • ws.send() 使用連接發送數據(只能發送純文本數據)

  • ws.close() 關閉連接

4、Demo演示

  了解WebSocket的一些API之後,趁熱打鐵,做一個小案例跑一下。

4.1.Node服務器端

WebSocket協議與Node一起用非常好,原因有以下兩點:

1.WebSocket客戶端基於事件編程與Node中自定義事件差不多。

2.WebSocket實現客戶端與服務器端長連接,Node基本事件驅動的方式十分適合高併發連接

創建一個webSocket.js如下:

const WebSocketServer = require('ws').Server;
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', function (ws) {
    console.log('client connected');
    ws.on('message', function (message) {
        ws.send('我收到了' + message);
    });
});

打開windows命令窗口運行

4.2.HTML客戶端

新建一個index.html頁面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webSocket小Demo</title>
</head>
<body>
    <div class="container">
        <div>
            <input type="text" id="msg">
            <button onclick="sendMsg()">發送報文</button>
        </div>
    </div>
    <script>
        const ws = new WebSocket('ws://localhost:8080');
        ws.onmessage = (res) => {
            console.log(res);
        };
        ws.onopen = () => {
            console.log('OPEN...');
        };
        ws.onclose = () => {
            console.log('CLOSE...');
        }
        function sendMsg() {
            let msg = document.getElementById('msg').value;
            ws.send(msg);
        }
    </script>
</body>

打開瀏覽器依次輸入字符1,2,3,每次輸入完點擊發送報體,可見在ws.onmessage事件中res.data中返回來我們發的報文

5、問題與總結

  以上只是簡單的介紹了下WebSocket的API與簡單用法,在處理高併發,長連接這些需求上,例如聊天室,可能WebSocket的http請求更加合適高效。

   但在使用WebSocket過程中發現容易斷開連接等問題,所以在每次發送報文前要判斷是否斷開,當多次發送報文時,由於服務器端返回數據量不同,返回客戶端前後順序也不同,所以需要在客戶端收到上一個報文返回數據后再發送下一個報文,為了避免回調嵌套過多,通過Promise ,async ,await等同步方式解決。關於WebSocket就寫這麼多,如有不足,歡迎多多指正!

參考資料:
《JavaScript高級教程》
《深入檢出NodeJs》

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

堡壘機的核心武器:WebSSH錄像實現

WebSSH終端錄像的實現終於來了

前邊寫了兩篇文章和深入介紹了終端錄製工具Asciinema,我們已經可以實現在終端下對操作過程的錄製,那麼在WebSSH中的操作該如何記錄並提供後續的回放審計呢?

一種方式是文章最後介紹的自動錄製審計日誌的方法,在主機上添加個腳本,每次連接自動進行錄製,但這樣不僅要在每台遠程主機添加腳本,會很繁瑣,而且錄製的腳本文件都是放在遠程主機上的,後續播放也很麻煩

那該如何更好處理呢?下文介紹一種優雅的方式來實現,核心思想是不通過錄製命令進行錄製,而在Webssh交互執行的過程中直接生成可播放的錄像文件

設計思路

通過上邊兩篇文章的閱讀,我們已經知道了Asciinema錄像文件主要由兩部分組成:header頭和IO流數據

header頭位於文件的第一行,定義了這個錄像的版本、寬高、開始時間、環境變量等參數,我們可以在websocket連接創建時將這些參數按照需要的格式寫入到文件

header頭數據如下,只有開頭一行,是一個字典形式

{"version": 2, "width": 213, "height": 55, "timestamp": 1574155029.1815443, "env": {"SHELL": "/bin/bash", "TERM": "xterm-256color"}, "title": "ops-coffee"}

整個錄像文件除了第一行的header頭部分,剩下的就都是輸入輸出的IO流數據,從websocket連接建立開始,隨着操作的進行,IO流數據是不斷增加的,直到整個websocket長連接的結束,那就需要在整個WebSSH交互的過程中不斷的往錄像文件追加輸入輸出的內容

IO流數據如下,每一行一條,列表形式,分別表示操作時間,輸入或輸出(這裏我們為了方便就寫固定字符串輸出),IO數據

[0.2341010570526123, "o", "Last login: Tue Nov 19 17:11:30 2019 from 192.168.105.91\r\r\n"]

似乎很完美,按照上邊的思路錄像文件就應該沒有問題了,但還有一些細節需要處理

首先是需要歷史連接列表,在這個列表裡可以看到什麼時間,哪個用戶連接了哪台主機,當然也需要提供回放功能,新建一張表來記錄這些信息

class Record(models.Model):
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='創建時間')

    host = models.ForeignKey(Host, on_delete=models.CASCADE, verbose_name='主機')
    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用戶')

    filename = models.CharField(max_length=128, verbose_name='錄像文件名稱')

    def __str__(self):
        return self.host

其次還需要考慮的一個問題是header和後續IO數據流要寫入同一個文件,這就需要在整個websocket的連接過程中有一個固定的文件名可被讀取,這裏我使用了主機+用戶+當前時間作為文件名,同一用戶在同一時間不能多次連接同一主機,這樣可保證文件名不重複,同時避免操作寫入錯誤的錄像文件,文件名在websocket建立時初始化

def __init__(self, host, user, websocket):
    self.host = host
    self.user = user

    self.time = time.time()
    self.filename = '%s.%s.%d.cast' % (host, user, self.time)

IO流數據會持續不斷的寫入文件,這裏以一個獨立的方法來處理寫入

def record(self, type, data):
    RECORD_DIR = settings.BASE_DIR + '/static/record/'
    if not os.path.isdir(RECORD_DIR):
        os.makedirs(RECORD_DIR)

    if type == 'header':
        Record.objects.create(
            host=Host.objects.get(id=self.host),
            user=self.user,
            filename=self.filename
        )

        with open(RECORD_DIR + self.filename, 'w') as f:
            f.write(json.dumps(data) + '\n')
    else:
        iodata = [time.time() - self.time, 'o', data]
        with open(RECORD_DIR + self.filename, 'a', buffering=1) as f:
            f.write((json.dumps(iodata) + '\n'))

record接收兩個參數type和data,type標識本次寫入的是header頭還是IO流,data則是具體的數據

header只需要執行一次寫入,所以將其放在ssh的connect方法中,只在ssh連接建立時執行一次,在執行header寫入時同時往數據庫插入新的歷史記錄數據

調用record方法寫入header

def connect(self, host, port, username, authtype, password=None, pkey=None,
            term='xterm-256color', cols=80, rows=24):
    ...

    # 構建錄像文件header
    self.record('header', {
        "version": 2,
        "width": cols,
        "height": rows,
        "timestamp": self.time,
        "env": {
            "SHELL": "/bin/bash",
            "TERM": term
        },
        "title": "ops-coffee"
    })

IO流數據則需要與返回給前端的數據保持一致,這樣就能保證前端显示什麼錄像就播放什麼了,所以所有需要返回前端數據的地方都同時寫入錄像文件即可

調用record方法寫入io流數據

def connect(self, host, port, username, authtype, password=None, pkey=None,
            term='xterm-256color', cols=80, rows=24):
    ...

    # 連接建立一次,之後交互數據不會再進入該方法
    for i in range(2):
        recv = self.ssh_channel.recv(65535).decode('utf-8', 'ignore')
        message = json.dumps({'flag': 'success', 'message': recv})
        self.websocket.send(message)

        self.record('iodata', recv)

...

def _ssh_to_ws(self):
    try:
        with self.lock:
            while not self.ssh_channel.exit_status_ready():
                data = self.ssh_channel.recv(1024).decode('utf-8', 'ignore')
                if len(data) != 0:
                    message = {'flag': 'success', 'message': data}
                    self.websocket.send(json.dumps(message))

                    self.record('iodata', data)
                else:
                    break
    except Exception as e:
        message = {'flag': 'error', 'message': str(e)}
        self.websocket.send(json.dumps(message))
        self.record('iodata', str(e))
        
        self.close()

由於命令執行與返回都是多線程的操作,這就會導致在寫入文件時出現文件亂序影響播放的問題,典型的操作有vim、top等,通過加鎖self.lock可以順利解決

最後歷史記錄頁面,當用戶點擊播放按鈕時,調用js彈出播放窗口

<div class="modal fade" id="modalForm">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-body" id="play">
      </div>
    </div>
  </div>
</div>

// 播放錄像
function play(host,user,time,file) {
  $('#play').html(
    '<asciinema-player id="play" title="WebSSH Record" author="ops-coffee.cn" author-url="https://ops-coffee.cn" author-img-url="/static/img/logo.png" src="/static/record/'+file+'" speed="3" '+
    'idle-time-limit="2" poster="data:text/plain,\x1b[1;32m'+time+
    '\x1b[1;0m用戶\x1b[1;32m'+user+
    '\x1b[1;0m連接主機\x1b[1;32m'+host+
    '\x1b[1;0m的錄像記錄"></asciinema-player>'
  )

  $('#modalForm').modal('show');
}

asciinema-player標籤的詳細參數介紹可以看這篇文章

演示與總結

在寫入文件的方案中,考慮了實時寫入和一次性寫入,實時寫入就像上邊這樣,所有的操作都會實時寫入錄像文件,好處是錄像不丟失,且能在操作的過程中進行實時的播放,缺點也很明顯,就是會頻繁的寫文件,造成IO開銷

一次性寫入可以在用戶操作的過程中將錄像數據寫入內存,在websocket關閉時一次性異步寫入到文件中,這種方案在最終寫入文件時可能因為種種原因而失敗,從而導致錄像丟失,還有個缺點是當你WebSSH操作時間過長時,會導致內存的持續增加

兩種方案一種是對磁盤的消耗另一種是對內存的消耗,各有利弊,當然你也可以考慮批量寫入,例如每分鐘寫一次文件,一分鐘之內的保存在內存中,平衡內存和磁盤的消耗,期待你的實現

相關文章推薦閱讀:

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益