標籤: 網頁設計公司

  • 可拖拽圓形進度條組件(支持移動端)

    可拖拽圓形進度條組件(支持移動端)

    好久之前寫過一個可拖拽圓形進度條的dome,中間有網友反饋過一些問題,最近比較閑有時間修改了一些問題也做了一些優化,並封裝成組件,基於canvas實現,只需傳入放置組件dom容器,任何框架均可直接使用;

    codepen 示例如下:https://codepen.io/pangyongsheng/pen/XRmNRK

     

    一、如何使用

    npm下載

    執行 npm i drag-arc -S 或 cnpm i drag-arc -S

     
    import DragArc from 'drag-arc';
     new DragArc({
        el: dom,
        value: 10,
        change: (v) => {
            console.log(v)
        },
        ...
    })
    或者 也可從項目下載dist/dist/drag-arc.min.js,直接通過srcipt標籤引入

    其中dom為放置組件HTML容器,可通過ref獲取;

    主要屬性方法(詳見github/npm)

    項目地址:https://github.com/pangyongsheng/canvas-arc-draw
    npm地址:https://www.npmjs.com/package/drag-arc

    Name Description Type Default Required
    el 放置組件的DOM元素 Element none Y
    change 當前值變化時觸發的事件,回調參數為當前進度值Number(0-100) Function ()=>{} N
    startDeg 滑動圓弧的起始弧度 Number  0 N
    endDeg 滑動圓弧的結束弧度 Number 1 N
    value 默認值 Number (0-100) 0 N
    textShow 显示文字 Boolean true N
    color 外側圓弧顏色 String,Array [“#06dabc”, “#33aaff”] N
    slider 滑塊半徑 Number #FFF N
    innerColor 內側弧度的顏色 String #ccc N
    outColor 外側圓弧背景顏色 String,Array #ccc N
    innerLineWidth 內側弧線寬 Number 1 N
    outLineWidth 外側弧線寬 Number 20 N
    counterclockwise 逆時針方向 Boolean true N
    sliderColor 滑塊顏色 String #CCC N
    sliderBorderColor 滑塊邊框顏色 String #fff N

    二、實現方法簡介

    1、繪製位置幾何關係

    如圖所示,以canvas畫布中心點建立坐標系,則有:

    滑塊位置與弧度關係:

    由圓的參數方程得出
    x=rcosφ
    y=rsinφ

    鼠標移動位置與弧度關係:

    通過事件回調參數 我們可以獲得 鼠標mousemove事件或者移動端touchmove事件的x,y坐標,可計算tan值為
    tanφ = y/x;
    再通過反三角函數有可得:
    φ=arctan(tanφ)

    以上基本的位置關係已經得出;

    2、js實現中的幾個問題

    (1)坐標的轉化方法

    由於上述位置關係是基於中心坐標實現的,而canvas繪製坐標是以左上角為原點實現的,故需要實現兩種坐標的轉化關係;

    (2)canvas弧度位置與正常弧度位置的轉化

    下圖是canvas的弧度位置恰好與我們正常計算的方向是相反的,同樣需考慮弧度的轉換;

    (3)Math.atan方法返回值與實際弧度的關係

    由於Math.atan() 函數返回一個數值的反正切[- π/2 , π/2 ],
    而實際中我們需要獲得到[0-2π]直接的值,所以在通過鼠標位置獲取弧度值時需要通過Math.atan(y/x)和xy在中心坐標的正負綜合判斷其所在象限從何獲取實際的獲取弧度值;

    (4)弧度與進度條值得關係

    由於鼠標移動觸發繪圖方法是較為連續的動畫效果,而進度是間隔的,
    這裏我們需要實現個類似d3js中domain和range的比例關係。
    這裏我們將值[0,100]對應弧度比例為[startDeg, endDeg]

    (5)終點的判斷

    由於鼠標移動的位置是任意的,可能導致滑塊到達終點後由於鼠標移動到了起點時,滑塊也直接從終點移動到起點,故需對起點終點做判斷,到達起點后不可再向後滑動,到達終點后不可再向前滑動;

    3、詳細實現方法可以參考這篇文章

     https://www.cnblogs.com/pangys/p/6837344.html

     

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

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

    ※別再煩惱如何寫文案,掌握八大原則!

  • 解Bug之路-中間件”SQL重複執行”

    解Bug之路-中間件”SQL重複執行”

    前言

    我們的分庫分表中間件在線上運行了兩年多,到目前為止還算穩定。在筆者將精力放在處理各種災難性事件(例如中間件物理機宕機/數據庫宕機/網絡隔離等突發事件)時。竟然發現還有一些奇怪的corner case。現在就將排查思路寫成文章分享出來。

    Bug現場

    應用拓撲

    應用通過中間件連後端多個數據庫,sql會根據路由規則路由到指定的節點,如下圖所示:

    錯誤現象

    應用在做某些數據庫操作時,會發現有比較大的概率失敗。他們的代碼邏輯是這樣:

    	int count = updateSql(sql1);
    	...
    	// 偽代碼
    	int count = updateSql("update test set value =1 where id in ("100","200") and status = 1;
    	if( 0 == count ){
    		throw new RuntimeException("更新失敗");
    	}
    	......
    	int count = updateSql(sql3);
    	...
    

    即每做一次update之後都檢查下是否更新成功,如果不成功則回滾並拋異常。
    在實際測試的過程中,發現經常報錯,更新為0。而實際那條sql確實是可以更新到的(即報錯回滾后,我們手動執行sql可以執行並update count>0)。

    中間件日誌

    筆者根據sql去中間件日誌裏面搜索。發現了非常奇怪的結果,日誌如下:

    2020-03-13 11:21:01:440 [NIOREACTOR-20-RW] frontIP=>ip1;sqlID=>12345678;rows=>0;sql=>update test set value =1 where id in ("1","2") and status = 1;start=>11:21:01:403;time=>24266;
    2020-03-13 11:21:01:440 [NIOREACTOR-20-RW] frontIP=>ip1;sqlID=>12345678;rows=>2;sql=>update test set value =1 where id in ("1","2") and status = 1;start=>11:21:01:403;time=>24591;
    

    由於中間件對每條sql都標識了唯一的一個sqlID,在日誌表現看來就好像sql執行了兩遍!由於sql中有一個in,很容易想到是否被拆成了兩條執行了。如下圖所示:

    這條思路很快被筆者否決了,因為筆者explain並手動執行了一下,這條sql確實只路由到了一個節點。真正完全否決掉這條思路的是筆者在日誌裏面還發現,同樣的SQL會打印三遍!即看上去像執行了三次,這就和僅僅只in了兩個id的sql在思路上相矛盾了。

    數據庫日誌

    那到底數據真正執行了多少條呢?找DBA去撈一下其中的sql日誌,由於線下環境沒有日誌切割,日誌量巨大,搜索時間太慢。沒辦法,就按照現有的數據進行分析吧。

    日誌如何被觸發

    由於當前沒有任何思路,於是筆者翻看中間件的代碼,發現在update語句執行后,中間件會在收到mysql okay包后打印上述日誌。如下圖所示:

    注意到所有出問題的update出問題的時候都是同一個NIOREACTOR線程先後打印了兩條日誌,所以筆者推斷這兩個okay大概率是同一個後端連接返回的。

    什麼情況會返回多個okay?

    這個問題筆者思索了很久,因為在筆者的實際重新執行出問題的sql並debug時,永遠只有一個okay返回。於是筆者聯想到,我們中間件有個狀態同步的部分,而這些狀態同步是將set auto_commit=0等sql拼接到應用發送的sql前面。即變成如下所示:

    sql可能為
    set auto_commit=0;set charset=gbk;>update test set value =1 where id in ("1","2") and status = 1;
    

    於是筆者細細讀了這部分的代碼,發現處理的很好。其通過計算出前面拼接出的sql數量,再在接收okay包的時候進行遞減,最後將真正執行的那條sql處理返回。其處理如下圖所示:

    但這裏確給了筆者一個靈感,即一條sql文本確實是有可能返回多個okay包的。

    真相大白

    在筆者發現(sql1;sql2;)這樣的拼接sql會返回多個okay包后,就立刻聯想到,該不會業務自己寫了這樣的sql發給中間件,造成中間件的sql處理邏輯錯亂吧。因為我們的中間件只有在對自己拼接(同步狀態)的sql做處理,明顯是無法處理應用傳過來即為拼接sql的情況。
    由於看上去有問題的那條sql並沒有拼接,於是筆者憑藉這條sql打印所在的reactor線程往上搜索,發現其上面真的有拼接sql!

    2020-03-1311:21:01:040[NIOREACTOR-20RW]frontIP=>ip1;sqlID=>12345678;rows=>1;
    sql=>update test_2 set value =1 where id=1 and status = 1;update test_2 set value =1 where id=2 and status = 1;
    

    如上圖所示,(update1;update2)中update1的okay返回被驅動認為是所有的返回。然後應用立即發送了update3。前腳剛發送,update2的okay返回就回來了而其剛好是0,應用就報錯了(要不是0,這個錯亂邏輯還不會提前暴露)。那三條”重複執行”也很好解釋了,就是之前的拼接sql會有三條。

    為何是概率出現

    但奇怪的是,並不是每次拼接sql都會造成update3″重複執行”的現象,按照筆者的推斷應該前面只要是多條拼接sql就會必現才對。於是筆者翻了下jdbc驅動源碼,發現其在發送命令之前會清理下接收buffer,如下所示:

    MysqlIO.java
    final Buffer sendCommand(......){
    	......
    	// 清理接收buffer,會將殘存的okay包清除掉
    	clearInputStream();
    	......
    	send(this.sendPacket, this.sendPacket.getPosition());
    	......
    }
    

    正是由於clearInputStream()使得錯誤非必現(暴露),如果okay(update2)在應用發送第三條sql前先到jdbc驅動會被驅動忽略!
    讓我們再看一下不會讓update3″重複執行”的時序圖:

    即根據okay(update2)返回的快慢來決定是否暴露這個問題,如下圖所示:

    同時筆者觀察日誌,確實這種情況下”update1;update2″這條語句在中間件裏面日誌有兩條。

    臨時解決方案

    讓業務開發不用這些拼接sql的寫法后,再也沒出過問題。

    為什麼不連中間件是okay的

    業務開發這些sql是就在線上運行了好久,用了中間件后才出現問題。
    既然不連中間件是okay的,那麼jdbc必然有這方面的完善處理,筆者去翻了下mysql-connect-java(5.1.46)。由於jdbc裏面存在大量的兼容細節處理,筆者這邊只列出一些關鍵代碼路徑:

    MySQL JDBC 源碼
    MySQLIO
    stack;
    executeUpdate
    	|->executeUpdateInternel
    		|->executeInternal
    			|->execSQL
    				|->sqlQueryDirect
    					|->readAllResults (MysqlIO.java)
    readAllResults: //核心在這個函數的處理裏面
    ResultSetImpl readAllResults(......){
    		......
           while (moreRowSetsExist) {
    			  ......
    			  // 在返回okay包的保中其serverStatus字段中如果SERVER_MORE_RESULTS_EXISTS置位
    			  // 表明還有更多的okay packet
                moreRowSetsExist = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0;
            }
            ......
    }
    

    正確的處理流程如下圖所示:

    而我們中間件的源碼確實這麼處理的:

    @Override
    public void okResponse(byte[] data, BackendConnection conn) {
    	......
    	// 這邊僅僅處理了autocommit的狀態,沒有處理SERVER_MORE_RESULTS_EXISTS
    	// 所以導致了不兼容拼接sql的現象
    	ok.serverStatus = source.isAutocommit() ? 2 : 1;
    	ok.write(source);
    	......
    }
    

    select也”重複執行”了

    解決完上面的問題后,筆者在日誌里竟然發現select盡然也有重複的,這邊並不會牽涉到okay包的處理,難道還有問題?日誌如下所示:

    2020-03-13 12:21:01:040[NIOREACTOR-20RW]frontIP=>ip1;sqlID=>12345678;rows=>1;select abc;
    2020-03-13 12:21:01:045[NIOREACTOR-21RW]frontIP=>ip2;sqlID=>12345678;rows=>1;select abc;
    

    從不同的REACTOR線程號(20RW/21RW)和不同的frontIP(ip1,ip2)來看是兩個連接執行了同樣的sql,但為何sqlID是一樣的?任何一個詭異的現象都必須一查到底。於是筆者登錄到應用上看了下應用日誌,確實應用有兩個不同的線程運行了同一條sql。
    那肯定是中間件日誌打印的問題了,筆者很快就想通了其中的關竅,我們中間件有個對同樣sql緩存其路由節點結構體的功能(這樣下一次同樣sql就不必解析,降低了CPU),而sqlID信息正好也在那個路由節點結構體裏面。如下圖所示:

    這個緩存功能感覺沒啥用(因為線上基本是沒有相同sql的),於是筆者在筆者優化的閃電模式下(大幅度提高中間件性能)將這個功能禁用掉了,沒想到為了排查問題而開啟的詳細日誌碰巧將這個功能開啟了。

    總結

    任何系統都不能說百分之百穩定可靠,尤其是不能立flag。在線上運行了好幾年的系統也是如此。只有對所有預料外的現象進行細緻的追查與深入的分析並解決,才能讓我們的系統越來越可靠。

    公眾號

    關注筆者公眾號,獲取更多乾貨文章:

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

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

  • 多圖解釋Redis的整數集合intset升級過程

    多圖解釋Redis的整數集合intset升級過程

    redis源碼分析系列文章

    [Redis源碼系列]在Liunx安裝和常見API 

    為什麼要從Redis源碼分析 

    String底層實現——動態字符串SDS 

    雙向鏈表都不懂,還說懂Redis?

    面試官:說說Redis的Hash底層 我:……(來自閱文的面試題)

    Redis的跳躍表確定不了解下

     

    前言

    大噶好,今天仍然是元氣滿滿的一天,拋開永遠寫不完的需求,拒絕要求賊變態的客戶,單純的學習技術,感受技術的魅力。(哈哈哈,皮一下很開森)

    前面幾周我們一起看了Redis底層數據結構,如動態字符串SDS雙向鏈表Adlist字典Dict跳躍表,如果有對Redis常見的類型或底層數據結構不明白的請看上面傳送門。

    今天來說下set的底層實現整數集合,如果有對set不明白的,常見的API使用這篇就不講了,看上面的傳送門哈。

    整數集合概念

    整數集合是Redis設計的一種底層結構,是set的底層實現,當集合中只包含整數值元素,並且這個集合元素數據不多時,會使用這種結構。但是如果不滿足剛才的條件,會使用其他結構,這邊暫時不講哈。

    下圖為整數集合的實際組成,包括三個部分,分別是編碼格式encoding,包含元素數量length,保存元素的數組contents。(這邊只需要簡單看下,下面針對每個模塊詳細說明哈)

    整數集合的實現

    我們看下intset.h裏面關於整數集合的定義,上代碼哈:

    //整數集合結構體
    typedef struct intset {
        uint32_t encoding;  //編碼格式,有如下三種格式,初始值默認為INTSET_ENC_INT16
        uint32_t length;    //集合元素數量
        int8_t contents[];  //保存元素的數組,元素類型並不一定是ini8_t類型,柔性數組不佔intset結構體大小,並且數組中的元素從小到大排列。
    } intset;               
    
    #define INTSET_ENC_INT16 (sizeof(int16_t))   //16位,2個字節,表示範圍-32,768~32,767
    #define INTSET_ENC_INT32 (sizeof(int32_t))   //32位,4個字節,表示範圍-2,147,483,648~2,147,483,647
    #define INTSET_ENC_INT64 (sizeof(int64_t))   //64位,8個字節,表示範圍-9,223,372,036,854,775,808~9,223,372,036,854,775,807

     

     

    編碼格式encoding

    包括INTSET_ENC_INT16,INTSET_ENC_INT32,INTSET_ENC_INT64三種類型,其分別對應着不同的範圍,具體看上面代碼的註釋信息。

    因為插入的數據的大小是不一樣的,為了盡可能的節約內存(畢竟都是錢,平時要省着點用),所以我們需要使用不同的類型來存儲數據。

    集合元素數量length

    記錄了保存數據contents的長度,即有多少個元素。

    保存元素的數組contents

    真正存儲數據的地方,數組是按照從小到大有序排序的,並且不包含任何重複項(因為set是不含重複項,所以其底層實現也是不含包含項的)。

    整數集合升級過程(重點,手動標星)

    上面的圖我們重新看下,編碼格式encoding為INTSET_ENC_INT16,即每個數據佔16位。長度length為4,即數組content裏面有四個元素,分別是1,2,3,4。如果我們要添加一個数字位40000,很明顯超過編碼格式為INTSET_ENC_INT16的範圍-32,768~32,767,應該是編碼格式為INTSET_ENC_INT32。那麼他是如何升級的呢,從INTSET_ENC_INT16升級到INTSET_ENC_INT32的呢?

    1.了解舊的存儲格式

    首先我們看下1,2,3,4這四個元素是如何存儲的。首先要知道一共有多少位,計算規則為length*編碼格式的位數,即4*16=64。所以每個元素佔用了16位。

    2.確定新的編碼格式

    新的元素為40000,已經超過了INTSET_ENC_INT16的範圍-32,768~32,767,所以新的編碼格式為INTSET_ENC_INT32。

    3.根據新的編碼格式新增內存

    上面已經說明了編碼格式為INTSET_ENC_INT32,計算規則為length*編碼格式的位數,即5*32=160。所以新增的位數為64-159。

    4.根據編碼格式設置對應的值

    從上面知道按照新的編碼格式,每個數據應該佔用32位,但是舊的編碼格式,每個數據佔用16位。所以我們從後面開始,每次獲取32位用來存儲數據。

    這樣說太難懂了,看下圖。

    首先,那最後32位,即128-159存儲40000。那麼第49-127是空着的。

    接着,取空着的49-127最後的32位,即96到127這32位,用來存儲4。那麼之前4存儲的位置48-6349-127剩下的64-95這兩部分組成了一個大部分,即48-95,現在空着啦。

    在接着在48-95這個大部分,再取后32位,即64-95,用來存儲3。那麼之前3存儲位置32-4748-95剩下的48-63這兩部分組成了一個大部分,即32-63,現在空着啦。

    再接着,將32-63這個大部分,再取后32位,即還是32-63,用來存儲2。那麼之前2存儲位置16-31空着啦。

    最後,將16-31和原來0-31合起來,存儲1。

    至此,整個升級過程結束。整體來說,分為3步,確定新的編碼格式,新增需要的內存空間,從后往前調整數據。

    這邊有個小問題,為啥要從后往前調整數據呢?

    原因是如果從前往後,數據可能會覆蓋。也拿上面個例子來說,數據1在0-15位,數據2在16-31位,如果從前往後,我們知道新的編碼格式INTSET_ENC_INT32要求每個元素佔用32位,那麼數據1應該佔用0-31,這個時候數據2就被覆蓋了,以後就不知道數據2啦。

    但是從后往前,因為後面新增了一些內存,所以不會發生覆蓋現象。

    升級的優點

     節約內存

    整數集合既可以讓集合保存三種不同類型的值,又可以確保升級操作只在有需要的時候進行,這樣就節省了內存。 

    不支持降級

    一旦對數組進行升級,編碼就會一直保存升級后的狀態。即使後面把40000刪掉了,編碼格式還是不會將會INTSET_ENC_INT16。

    整數集合的源碼分析

    創建一個空集合 intsetnew

    這個方法比較簡單,是初始化整數集合的步驟,即下圖部分。

    主要的步驟是分配內存空間,設置默認編碼格式,以及初始化數組長度length。

    intset *intsetNew(void) {
        intset *is = zmalloc(sizeof(intset));//分配內存空間 
        is->encoding = intrev32ifbe(INTSET_ENC_INT16);//設置默認編碼格式INTSET_ENC_INT16 
        is->length = 0;//初始化length 
        return is;
    }

    添加元素並升級insetAdd流程圖(重點)

    添加元素並升級insetAdd源碼分析

    可以根據上面的流程圖,對照着下面的源碼分析,這邊就不寫啦哈。

    //添加元素
    //輸入參數*is為原整數集合
    //value為要添加的元素
    //*success為是否添加成功的標誌量 ,1表示成功,0表示失敗 
    intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
        //確定要添加的元素的編碼格式 
        uint8_t valenc = _intsetValueEncoding(value);
        
        uint32_t pos;
        //如果success沒有初始值,則初始化為1 
        if (success) *success = 1;
    
       //如果新的編碼格式大於現在的編碼格式,則升級並添加元素 
        if (valenc > intrev32ifbe(is->encoding)) {
            //調用另一個方法 
            return intsetUpgradeAndAdd(is,value);
        } else {
            //如果編碼格式不變,則調用查詢方法 
            //輸入參數is為原整數集合 
            //value為要添加的數據
            //pos為位置 
            if (intsetSearch(is,value,&pos)) {//如果找到了,則直接返回,因為數據是不可重複的。 
                if (success) *success = 0;
                return is;
            }
    
            //設置length 
            is = intsetResize(is,intrev32ifbe(is->length)+1);
            if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
        }
        //設置數據 
        _intsetSet(is,pos,value);
        is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
        return is;
    }
    
    
    //#define INT8_MAX 127
    //#define INT16_MAX 32767
    //#define INT32_MAX 2147483647
    //#define INT64_MAX 9223372036854775807LL 
    static uint8_t _intsetValueEncoding(int64_t v) {
        if (v < INT32_MIN || v > INT32_MAX)
            return INTSET_ENC_INT64;
        else if (v < INT16_MIN || v > INT16_MAX)
            return INTSET_ENC_INT32;
        else
            return INTSET_ENC_INT16;
    }
    
    
    //根據輸入參數value的編碼格式,對整數集合is的編碼格式升級 
    static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
        //當前集合的編碼格式 
        uint8_t curenc = intrev32ifbe(is->encoding);
        //根據對value解析獲取新的編碼格式 
        uint8_t newenc = _intsetValueEncoding(value);
        //獲取集合元素數量 
        int length = intrev32ifbe(is->length);
        //如果要添加的數據小於0,則prepend為1,否則為0 
        int prepend = value < 0 ? 1 : 0;
    
       //設置集合為新的編碼格式,並根據編碼格式重新設置內存 
        is->encoding = intrev32ifbe(newenc);
        is = intsetResize(is,intrev32ifbe(is->length)+1);
    
        //逐步循環,直到length小於0,挨個重新設置每個值,從后往前 
        while(length--)
            _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
    
        //如果value為負數,則放在最前面 
        if (prepend)
            _intsetSet(is,0,value);
        else//如果value為整數,設置最末尾的元素為value 
            _intsetSet(is,intrev32ifbe(is->length),value);
        //重新設置length 
        is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
        return is;
    }
    
    
    //找到is集合中值為value的下標,返回1,並保存在pos中,沒有找到返回0,並將pos設置為value可以插入到數組的位置
    static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
        int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
        int64_t cur = -1;
    
        //如果集合為空,那麼位置pos為0 
        if (intrev32ifbe(is->length) == 0) { 
            if (pos) *pos = 0;
            return 0;
        } else {
            //因為數據是有序集合,如果要添加的數據大於最後一個数字,那麼直接把要添加的值放在最後即可,返回最大值下標 
            if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
                if (pos) *pos = intrev32ifbe(is->length);
                return 0;
            } else if (value < _intsetGet(is,0)) { //如果這個數據小於數組下標為0的數據,即為最小值 ,返回0 
                if (pos) *pos = 0;
                return 0;
            }
        }
        //有序集合採用二分法 
        while(max >= min) {
            mid = ((unsigned int)min + (unsigned int)max) >> 1;
            cur = _intsetGet(is,mid);
            if (value > cur) {
                min = mid+1;
            } else if (value < cur) {
                max = mid-1;
            } else {
                break;
            }
        }
    
        //確定找到 
        if (value == cur) {
            if (pos) *pos = mid;//設置參數pos,返回1,即找到位置 
            return 1;
        } else {//如果沒找到,則min和max相鄰,隨便設置都行,並返回0 
            if (pos) *pos = min; 
            return 0;
        }
    }

     

    結語

    該篇主要講了Redis的SET數據類型的底層實現整數集合,先從整數集合是什麼,,剖析了其主要組成部分,進而通過多幅過程圖解釋了intset是如何升級的,最後結合源碼對整數集合進行描述,如創建過程,升級過程,中間穿插例子和過程圖。

    如果覺得寫得還行,麻煩給個贊,您的認可才是我寫作的動力!

    如果覺得有說的不對的地方,歡迎評論指出。

    好了,拜拜咯。

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

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※別再煩惱如何寫文案,掌握八大原則!

  • 香港環保署:去年及今年首9月 空氣中二噁英濃度沒異常

    摘錄自2019年11月17日香港電台網站、頭條日報報導

    港警近月大量使用催淚彈,其中催淚彈不時出現大量火光,有民眾關注高溫會產生二噁英。二噁英是常見的持久性環境污染物,能在環境中長存數十年,不受破壞,毒性極高,會導致生育和發育問題、破壞免疫系統、干擾荷爾蒙分泌以及致癌。

    香港環保署在中西區和荃灣設監測站,監測空氣中二噁英的濃度,但上月數字至今未更新。香港環保署表示,去年及今年至9月的數據顯示,二噁英日均濃度是每立方米0.009至0.086皮克,並沒發現有異常情況出現。香港環保署表示,現在沒有空氣中二噁英的濃度指引,但過去3年香港取得的二噁英濃度,遠低於加拿大安大略省的二噁英每立方米0.1皮克,和日本每立方米0.6皮克的指標。

    參選「啟德中及南」選區候選人梁咏欣表示,近日收到居民反映,指有大量懷疑含有「二噁英」的雜物,被棄置在距離啟德約400米的宏照道路政署地盤內。她要求當局提供環境調查報告及實地分析數據,以確保附近環境不被有害物質污染。

    香港環保署稱已將上月份收集的二噁英樣本,送交政府化驗所作化學分析,一般需數星期,預期於本月(11月)底會完成,收到報告後會盡快公布。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?

  • 印尼蘇門答臘象身首異處慘死 象牙遭盜採

    摘錄自2019年11月8日自由時報屏東報導

    印尼保育官員今天表示,1頭列為極危(critically endangered)物種的蘇門答臘象屍體被發現,牠的頭被砍下且象牙被拔走,顯然是一宗盜獵案件。

    蘇門答臘島廖內省(Riau)一名農園工人昨天發現這頭40歲公象腐爛的屍體。當地保育機關主管蘇哈尤諾(Suharyono)在聲明中說:「大象的頭被砍下,切斷的象鼻落在距離象身1公尺處。」當局正在追查犯案人士。

    森林濫伐造成蘇門答臘象的天然棲地縮減,導致牠們和人類的衝突加劇。另一方面,蘇門答臘象象牙在野生動物黑市交易中價值連城。

    去年在印尼亞齊省(Aceh)也發現一具顯然被毒死的蘇門答臘象屍體,當時牠的象牙也不見。印尼環境部估計,境內野生蘇門答臘象只剩不到2000頭。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    ※為什麼 USB CONNECTOR 是電子產業重要的元件?

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    新北清潔公司,居家、辦公、裝潢細清專業服務

    ※推薦評價好的iphone維修中心

  • 法國黃背心運動滿週年 萬人上街抗議

    摘錄自2019年11月19日公視報導

    11月17號適逢法國黃背心運動一週年,去年不滿總統馬克宏調漲油價的民眾發起抗議行動,在各大城市遍地開花,並逐漸演變成反對馬克宏政府政策的社運。直到現在,仍有部分示威者,每星期六都會固定集會,提醒政府他們怒火難平。今年再度出現幾萬人走上街頭的盛大場面。有群眾和鎮暴警察發生衝突,還有人闖進巴黎的老佛爺百貨,讓業者被迫停業一天。

    抗議民眾表示,「我很高興能夠在這向馬克宏宣告:我們就在這裡,我們還在這裡,黃背心運動不死。儘管他們多方試圖摧毀黃背心,但黃背心屹立不搖,我們都是為了法國。」

    近幾個月來,黃背心運動趨於和緩,但週年紀念又讓情勢再度激化,數萬人走上街頭。部分抗議人士推翻路邊車輛,點燃垃圾桶等物,還向鎮暴警察扔擲石頭,而警方也以催淚瓦斯和水柱還擊和驅散人群。

    根據法國內政部的說法,法國全國各地星期六一共逮捕了264人,其中巴黎就佔了六成以上。黃背心支持者表示,目前他們考慮加入其他工會行動,參與12月5號開始反對馬克宏年金改革的無限期大罷工。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 大自然工程師河狸將修築堤壩 助英格蘭抗水患

    摘錄自2019年11月20日中央通訊社倫敦報導

    業務涵蓋歷史古蹟與鄉村管理的英國保育組織「國家信託」(National Trust)今天(20日)宣布,預定明年初在英格蘭南部兩地施放天生會修築堤壩的歐亞河狸,協助對抗水患。其中一地的計畫經理伊爾德利(Ben Eardley)指出:「河狸修築的堤壩在乾季可儲水,此外還有助降低下游暴洪、減少河岸侵蝕,攔截淤泥也可改善水質。」

    河狸素有「大自然工程師」美譽,牠創造的濕地環境可供小至昆蟲、大至野禽等許多物種棲息。這些河狸將生活在有柵欄隔離林地,專家將監測棲地變化。

    「國家信託」計畫於2025年前讓2萬5000公頃土地重新成為大量野生動植物的棲地。英國氣象局(Met Office)資料顯示,英格蘭北部近幾週遭逢嚴重水患,部分地區創下有紀錄以來最潮濕秋季。英格蘭光是今天早上就有18起水患警報,另有58起可能淹水警告。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

    ※Google地圖已可更新顯示潭子電動車充電站設置地點!!

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

    ※別再煩惱如何寫文案,掌握八大原則!

  • 敢反抗開發就坐牢 原民反土地改革惡法 印尼動亂中媒體忽略的訴求

    敢反抗開發就坐牢 原民反土地改革惡法 印尼動亂中媒體忽略的訴求

    環境資訊中心外電;姜唯 翻譯;林大利 審校;稿源:Mongabay

    9月下旬,印尼史上繼1998年獨裁者蘇哈托倒台以來最大規模的抗爭行動,佔據國際新聞版面。英國、美國]、和其他外國媒體頭條報導成千上萬民眾在全國各地大城市示威遊行,抗議婚前性行為納入刑法。

    撤銷新刑法法條,包括不得侮辱總統和提供避孕資訊,只是抗議者的訴求之一。根據網上流傳的七點聲明,訴求還包括反對廢除一條弱化反貪腐機構的新法、停止蘇門答臘和婆羅洲的森林大火,以及從印尼東邊的巴布亞地區撤軍。印尼對分裂主義的軍事鎮壓行動已經進行了數十年。

    撤銷土地利用新法也在訴求清單上。

    這條尚未被媒體大幅報導,但觀察者表示,它的重要性不亞於其他幾乎立法完成的爭議性法案。

    評論家說,該法案定義了新的罪行,並增加了刑罰,使當局更容易監禁抵抗開發商入侵的農村居民,使農業公司能保留土地特許經營權更長的時間。

    印尼巴布亞省一條切開森林的公路,涉嫌剝奪了原住民的土地所有權。Nanang Sujana攝;來源:Mongabay。

    在批評者眼中,最令人髮指的是,該法案設定了一個兩年期限,要求公民必須在期限內向政府註冊土地,否則土地將收歸國有,成為佐科威總統土地改革計畫的一部分重新分配,或授權私人公司使用。

    但是,原住民尋求正式承認其土地就起碼要花兩年,通常還要更久,才能通過層層官僚關卡。因此,兩年期限對印尼弱勢的原住民權利來說更是一記沈重的打擊,印尼原住民組織「AMAN」副秘書長卡亞迪(Erasmus Cahyadi)說。

    2013年印尼憲法法院作出歷史性裁決,駁回了州政府對原住民森林所有權的主張,此後總統佐科威陸續承認55個原住民群體對森林的權利,總面積達248平方公里(96平方英里)。但是AMAN說他們欲承認的原民土地有77,600平方公里(30,000平方英里),屬於704個原住民族。

    AMAN法律與人權事務負責人穆罕默德(Arman Muhammad)說,該法案違反了憲法的精神。

    印尼大學生走上雅加達街頭,反對新法律弱化反貪腐機構。Hans Nicholas Jong攝;來源:Mongabay。

    該法案的支持者則認為,通過該法案對於佐科威的大型土地改革計畫來說是必要的。

    佐科威於今年4月當選第二個五年任期,他已承諾賦予農村社區更大的權力,控制其21.7萬平方公里(84,000平方英里)的土地,但是實踐進度緩慢。

    截至10月,控制該國約一半土地的環境和林業部僅分配了總計28,000平方公里的土地,遠低於目標127,000平方公里。

    民主黨國會議員卡隆(Herman Khaeron):「土地改革計畫的土地很難找。」

    為了解決這個問題,卡隆說,該法案要求建立一個新的機構,稱為土地管理署,負責收購、管理和分配兩年的時間內沒有被公民登記的土地,這些土地將自動收歸國有。

    根據該法案,土地管理署將充當「土地銀行」,能夠透過租賃或出售土地產生收入,同時仍以非營利組織的形式運作。該機構必須保證為「社會利益」和「發展利益」提供土地。

    法案的措詞含糊,批評者擔心該機構會將土地當作商品出售給強勢投資者,以犧牲普通市民為代價。

    「誰將能夠使用這個土地銀行?小農嗎?當然不是,」印尼茂物農業研究所(Bogor Institute of Agriculture, IPB)人類生態學系研究員卡友諾(Eko Cahyono)說,「這個土地銀行將為有大量資本、企業和開發計畫的人服務。」

    評論家說,該法案中的其他條款將使企業受益,傷害農村社區。

    該法案將允許農業公司持有「HGU」耕種權許可證,有效期長達90年,而現行規定為60年。油棕公司可以拖更久的時間才將小農地釋出給當地社區。

    此外,該法條還規定,凡訂定「引起土地糾紛之惡意協議」者,將被判處5至15年監禁,「妨礙土地機關僱員和/或執法人員執行任務」者可判處兩年監禁。

    倡議組織土地改革聯盟(Consortium for Agrarian Reform, KPA)秘書長卡蒂卡(Dewi Kartika)向記者,根據後項條款,反抗土地掠奪的原住民、行動人士等人可被入罪。

    「它賦予警察將任何人定罪的合法性。最極端的詮釋之下,可被用來逮捕任何人。例如,當居民試圖阻止他們的土地被用來建造機場時,就可能會被逮捕。」

    成百上千的印尼村莊在和農業公司對抗時陷入困境,社區成員常常以肉身擋推土機,甚至縱火燒毀公司設備。

    9月26日,一名21歲的大學生在蘇拉威西省東南部省會肯達里的大規模抗議活動中被警察槍殺。抗議活動演變成暴亂後,另一名19歲學生卡德哈維(Yusuf Qardhawi)因鈍器重創頭部而死亡。

    「我們非常沮喪和失望,」參加抗議活動的社區組織者、23歲的馬斯庫里(Mando Maskuri)說,「國家應該保護人民,但他們卻在殺死人民。」

    。與印尼其他地方一樣,當地人往往缺乏證明土地所有權的文件,這使得州政府很容易在未經他們同意的情況下引入企業投資者。

    馬斯庫里說,許多旺尼居民嘗試向州政府登記其土地。但他擔心土地法案設定的時間表不切實際,最終導致居民失去土地,逼他們搬走。

    大學生在肯達里用行動劇抗議礦場開發。Kamaruddin攝,來源:Mongabay

    在9月抗議活動的高峰期,對土地法案和其他爭議性法案的審議被。跛鴨議會會期即將結束,新的議員宣誓就職。

    現任議員在最後一刻同意將土地法案移交至新議會會期,這表示新任議員可以從同一階段繼續審議,不必從頭開始。

    根據調查性新聞機構Tempo和非政府組織Auriga Nusantara的,接下來五年負責立法工作的575名議員中,將近一半是與至少1,016家企業有聯繫的商人,其中包括採礦公司和油棕公司。

    佐科威總統說,他想,使之對投資者更加友善。許多觀察家表示,不少與已列入清單。

    研究人員卡友諾說,如果議員試圖通過土地法案,反對者可以在最後一道關卡提出司法審查。

    同時,馬斯庫里說他準備再次走上街頭,「如果議會要強行通過該法案,將面臨來自農民、漁民和民間社會團體的巨大阻力。」

    Indonesia protests: Land bill at center of unrest by Basten Gokkon, Hans Nicholas Jong, Philip Jacobson on 3 November 2019

    • In recent weeks, Indonesia has seen its largest mass protests since the “people power” movement that forced President Suharto to step down in 1998.
    • Among a variety of pro-democracy demands, the protesters want lawmakers to scrap a controversial bill governing land use in the country.
    • The bill defines new crimes critics say could be used to imprison indigenous and other rural citizens for defending their lands against incursions by private companies.
    • It also sets a two-year deadline by which citizens must register their lands with the government, or else watch them pass into state control. Activists say the provision would deal a “knockout blow” to the nation’s indigenous rights movement.

    JAKARTA — In late September, international news outlets caught flak for their coverage of Indonesia’s largest mass protests since the 1998 uprising that led to the fall of the dictator Suharto.

    Headlines published by the , , and other foreign media implied the demonstrations, involving tens of thousands of people in major cities across the country, had arisen in response to a proposed new criminal code that would ban sex before marriage.

    “I did not get tear-gassed so Australians could keep having sex in Bali,” one netizen on Twitter, among a barrage of reactions to the reductive reports. “This is about the future of the country.”

    Scrapping the criminal code changes — which also include new penalties for insulting the president and providing information about contraception — was just one of the protesters’ demands, enumerated in a seven-point declaration that has circulated online. They also want the government to repeal a new law weakening the nation’s anti-corruption agency, stop forest fires in Sumatra and Borneo, and withdraw troops from Indonesia’s easternmost Papua region, where a military crackdown against separatists has been going on for decades.

    Also on the list: scrap a proposed new law governing land use.

    Though the land bill has gotten scant media coverage, observers say it is among the most potentially transformative of a raft of on the verge of being passed into law.

    The bill defines new crimes and introduces increased penalties that, critics say, would make it easier for authorities to imprison rural citizens for defending their lands against incursions by developers. It would also allow plantation companies to retain vast land concessions for longer periods of time.

    Most damningly in the eyes of critics, the bill sets a two-year deadline by which citizens must register their lands with the government, or else watch them pass into state control, where they could be redistributed as part of President Joko Widodo’s land reform program or licensed out to private firms.

    But indigenous groups seeking formal recognition of their lands already spend at least that long, and often far longer, jumping through bureaucratic hoops. The two-year deadline would therefore constitute a “knockout blow” for the nation’s embattled indigenous rights movement, Erasmus Cahyadi, deputy secretary-general of AMAN, Indonesia’s main advocacy group for indigenous peoples, told Mongabay.

    Since 2013, when a landmark Constitutional Court ruling struck down the state’s claim to indigenous peoples’ forests, President Joko Widodo has recognized the rights of 55 indigenous groups to forests spanning a total of 248 square kilometers (96 square miles). But AMAN says it has mapped more than 77,600 square kilometers (30,000 square miles) of land it says belongs to 704 indigenous communities.

    “The bill is contrary to the spirit of the constitution,” said Arman Muhammad, AMAN’s law and human rights director.



    University students protest the new corruption law in Jakarta. Image by Hans Nicholas Jong/Mongabay.

    The bill’s supporters argue its passage is necessary to support President Widodo’s flagship land reform program.

    Widodo, who was elected to a second five-year term in April, has promised to give rural communities greater control over 217,000 square kilometers (84,000 square miles) of land. But progress has been slow.

    As of October, the Ministry of Environment and Forestry, which controls around half of the nation’s land, had only distributed a total of 28,000 square kilometers (10,800 square miles), far short of its target of 127,000 square kilometers (49,000 square miles).

    “It’s hard to find land for the agrarian reform [program],” Democrat Party lawmaker Herman Khaeron at a recent panel event in Jakarta.

    To solve that, Herman said, the bill calls for the creation of a new body called the Land Management Agency to acquire, manage and distribute land that had gone unclaimed by citizens during the two-year window, that therefore automatically fell under state control.

    The bill says the agency will function as a “land bank,” implying it will be able to generate an income from leasing or selling lands, while still operating as a “nonprofit,” according to the bill. The agency must guarantee the availability of land for “social interests” as well as “development interests.”

    The language in the bill is vague, but critics fear the agency would treat land as a commodity to be sold to powerful investors at the expense of ordinary citizens.

    “Who would be able to access this land bank? Small farmers? Of course not,” Eko Cahyono, a researcher in the Department of Human Ecology at the Bogor Institute of Agriculture (IPB), told Mongabay. “The ‘land bank’ would serve those with big capital, companies and development projects.”

    Other provisions in the bill would benefit corporations at the expense of rural communities, critics say.

    The bill would allow plantation companies to hold a right-to-cultivate permit, known as an HGU, for 90 years, up from 60 years under the current rules.

    It would also let oil palm firms wait longer before providing smallholdings to local communities, a requirement under existing laws.

    Furthermore, the legislation stipulates prison time of five to 15 years for anyone who makes an “evil agreement that gives rise to a land dispute,” and a jail term of two years for those who “obstruct an employee and/or law enforcement officer from carrying out tasks in the land sector.”

    The latter provision could be used to “criminalize indigenous peoples, activists or anyone who tries to organize” against a land grab, Dewi Kartika, the secretary-general of the Consortium for Agrarian Reform (KPA), an advocacy group, reporters in Jakarta recently.

    “It grants the police legal legitimacy to criminalize anyone,” she said. “Of course this will be interpreted to the maximum extent possible, to freely arrest anyone. For example, if residents try to stand in the way of their land being used to build an airport.”

    Hundreds, if not thousands, of Indonesian villages are embroiled in conflict with natural resources firm, with community members often resorting to physically blocking bulldozers or even setting fire to company facilities.

    On Sept. 26, a 21-year-old college student in Kendari, the capital of Southeast Sulawesi province and one of the cities where mass protests took place in September, was shot dead by police. Another student in Kendari, 19-year-old Yusuf Qardhawi, died of blunt-force head injuries after a protest turned into a violent riot.

    “We were all so upset and disappointed,” Mando Maskuri, 23, a community organizer who joined the protests in Kendari, told Mongabay. “The state is supposed to protect us, but they’re killing us.”

    Residents of Mando’s home island of Wawonii are with mining firms that hold permits to operate on their lands. As elsewhere in Indonesia, locals tend to lack documents backing their land claims, making it easy for the state to bring in corporate investors without their consent.

    Many people in Wawonii are trying to register their lands with the state, Mando said. But he fears the land bill sets an unrealistic timeline that will eventually cause residents to lose their lands, forcing them to migrate to other parts of the country.



    Students in Kendari stage a mock burial in early 2019 to express their opposition to the mining in Wawonii. Image by Kamaruddin for Mongabay.

    At the height of the protests in September, deliberations on the land bill and other controversial legislation were . The lame-duck parliament was nearing the end of its session. New lawmakers have since been sworn in.

    In their final hour, however, the previous lawmakers agreed to “carry over” the land bill to the current parliament session, meaning deliberations can be resumed from the same stage by the new batch of legislators, rather than having to start all over again.

    Nearly half of the 575 lawmakers for the next five years are businesspeople who are affiliated with at least 1,016 companies, including mining and oil palm, according to an by investigative journalism outlet Tempo and Auriga Nusantara, an NGO.

    President Widodo says he wants to to make them friendlier to investors; many observers have said are on the list.

    If lawmakers try to pass the land bill, opponents could file a judicial review in a last-ditch attempt to oppose it, said Eko, the researcher.

    In the meantime, Mando says he is ready to take to the streets again.

    “If parliament tries to pass the bill, there will be massive resistance from farmers, fishermen, and civil society groups,” he said.

    ※ 全文及圖片詳見:

    作者

    如果有一件事是重要的,如果能為孩子實現一個願望,那就是人類與大自然和諧共存。

    於特有生物研究保育中心服務,小鳥和棲地是主要的研究對象。是龜毛的讀者,認為龜毛是探索世界的美德。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    新北清潔公司,居家、辦公、裝潢細清專業服務

  • 韓媒:台灣值得被納入全球氣候變遷體系

    摘錄自2019年11月19日中央社報導

    韓國「韓民族新聞」和英文報The Korea Times今(19日)同步刊載中華民國行政院環保署長張子敬署名的投書專文,呼籲國際社會接納台灣成為全球氣候變遷體系的一員。張子敬在分別以Taiwan: valuable partner in fighting climate change和「台灣也應當參與全球氣候變遷協約」為標題,向韓國報紙投書闡述台灣欲加入全球氣候變遷體系的立場。

    專文指出,台灣整合中央相關部會工作,制訂「國家氣候變遷調適行動方案」,從災害、維生基礎設施、水資源、國土安全、海岸、能源及產業、農業、健康等8個面向,建構因應氣候變遷的韌性體制;在醫療領域上特別著重強化醫療衛生及防疫系統預防、減災、應變及復原能力,維護全民健康並優先保障弱勢住民。

    另外在生態保育領域上,將維護農業生產資源及生物多樣性,加強監測與預警機制、強化天然災害救助及保險體系、整合科技提升農林漁牧產業抗逆境能力,並完善自然保護區經營管理、建構長期生態監測體系、強化物種及基因的多樣性保存與合理利用,以確保糧食安全並建構適應氣候風險的永續農業。

    專文認為,台灣因政治成見被排除在國際組織之外,是相當不公平的,非但不符合氣候公約籲請所有國家對全球氣候變遷進行廣泛合作的精神,忽視巴黎協定強調「氣候正義」及呼籲各國採取氣候行動的重要性,更違背聯合國憲章宗旨,也弱化國際架構而對世界造成傷害。

    專文強調,面對國際社會,台灣是負責任、肯貢獻的真誠朋友,樂於分享在環境治理制度、防災預警系統、能源效率提升技術、科技創新運用等相關領域的經驗,台灣努力希望能讓世界更美好,而台灣也真的值得被納入全球氣候變遷體系的一員。

    本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※幫你省時又省力,新北清潔一流服務好口碑

    ※別再煩惱如何寫文案,掌握八大原則!

  • 前端工程師必備:從瀏覽器的渲染到性能優化

    前端工程師必備:從瀏覽器的渲染到性能優化

    摘要:本文主要講談及瀏覽器的渲染原理、流程以及相關的性能問題。

    問題前瞻

    1. 為什麼css需要放在頭部?
    2. js為什麼要放在body後面?
    3. 圖片的加載和渲染會阻塞頁面DOM構建嗎?
    4. dom解析完才出現頁面嗎?
    5. 首屏時間根據什麼來判定?

    瀏覽器渲染

    1.瀏覽器渲染圖解

    [來自google開發者文檔]

    瀏覽器渲染頁面主要經歷了下面的步驟:

    1.處理 HTML 標記並構建 DOM 樹。
    2.處理 CSS 標記並構建 CSSOM 樹。
    3.將 DOM 與 CSSOM 合併成一個渲染樹。
    4.根據渲染樹來布局,以計算每個節點的幾何信息。
    5.將各個節點繪製到屏幕上。

    為構建渲染樹,瀏覽器大體上完成了下列工作:

    從 DOM 樹的根節點開始遍歷每個可見節點。
    
    某些節點不可見(例如腳本標記、元標記等),因為它們不會體現在渲染輸出中,所以會被忽略。
    某些節點通過 CSS 隱藏,因此在渲染樹中也會被忽略,例如,上例中的 span 節點---不會出現在渲染樹中,---因為有一個顯式規則在該節點上設置了“display: none”屬性。
    對於每個可見節點,為其找到適配的 CSSOM 規則並應用它們。
    
    發射可見節點,連同其內容和計算的樣式。

    根據以上解析,DOM樹和CSSOM樹的構建對於頁面性能有非常大的影響,沒有DOM樹,頁面基本的標籤塊都沒有,沒有樣式,頁面也基本是空白的。所以具體css的解析規則是什麼?js是怎麼影響頁面渲染的?了解了這些,我們才能有的放矢,對頁面性能進行優化。

    2.css解析規則

    1
    <div id="div1">
    2
    <div class="a">
    3
    <div class="b">
    4
    ...
    5
    </div>
    6
    <div class="c">
    7
    <div class="d">
    8
    ...
    9
    </div>
    10
    <div class="e">
    11
    ...
    12
    </div>
    13
    </div>
    14
    </div>
    15
    <div class="f">
    16
    <div class="c">
    17
    <div class="d">
    18
    ...
    19
    </div>
    20
    </div>
    21
    </div>
    22
    </div>

     

    1
    #div1 .c .d {}
    2
    .f .c .d {}
    3
    .a .c .e {}
    4
    #div1 .f {}
    5
    .c .d{}

    從左向右的匹配規則

    從右向左的匹配規則

    如果css從左向右解析,意味着我們需要遍歷更多的節點。不管樣式規則寫得多細緻,每一個dom結點仍然需要遍歷,因為整個style rules還會有其它公共樣式影響。如果從右向左解析,因為子元素只有一個父元素,所以能夠很快定位出當前dom符不符合樣式規則。

    3.js加載和執行機制

    首先明確一點,我們可以通過js去修改網頁的內容,樣式和交互等,這一意味着js會影響頁面的dom結構,如果js和dom構建并行執行,那麼很容易會出現衝突,所以js在執行時必然會阻塞dom和cssom的構建過程,不論是外部js還是內聯腳本。

    js的位置是否影響dom解析?

    首先我們為什麼提倡把js放在body標籤的後面去加載,因為從demo上看無論是放在head還是放在body后加載js,頁面domcontentload的時間都是一樣的:

    我們從圖中可以看出js的加載和執行是阻塞dom解析的,但是因為頁面並不是一次就渲染完成,所以我們需要做的是盡量讓用戶看到首屏的部分被渲染出來,js放在頭部,則頁面的內容區域還沒有解析到就被阻塞了,導致用戶看到的是白屏,而js放在body後面,儘管此時頁面dom仍然沒有解析完成,但是已經渲染出一部分樓層了,這也是為什麼我們比較看重頁面的首屏時間。

    只有DOM和CSSOM樹構建好后併合並成渲染樹才能開始繪製頁面圖形,那是不是把整個DOM樹和CSSOM樹構建好后才能開始繪製頁面?這顯然是不符合我們平時訪問頁面的認知的,實際上:

    為達到更好的用戶體驗,呈現引擎會力求儘快將內容显示在屏幕上。它不必等到整個 HTML 文檔解析完畢之後,就會開始構建呈現樹和設置布局。在不斷接收和處理來自網絡的其餘內容的同時,呈現引擎會將部分內容解析並显示出來。

    具體瀏覽器什麼時候進行首次繪製?可以查看本文對瀏覽器首次渲染時間點的探究。

    4.圖片的加載和渲染機制

    首先我們解答一下上面的問題:圖片的加載與渲染會不會阻塞頁面渲染?答案是圖片的加載和渲染不會影響頁面的渲染。

    那麼標籤中的圖片和樣式中的圖片的加載和渲染時間是什麼樣的呢?

    解析HTML【遇到標籤加載圖片】 —> 構建DOM樹
    加載樣式 —> 解析樣式【遇到背景圖片鏈接不加載】 —> 構建樣式規則樹
    加載javascript —> 執行javascript代碼
    把DOM樹和樣式規則樹匹配構建渲染樹【遍歷DOM樹時加載對應樣式規則上的背景圖片】
    計算元素位置進行布局
    繪製【開始渲染圖片】

    當然把DOM樹和樣式規則樹匹配構建渲染樹時,只會把可見元素和它對應的樣式規則結合一起產出到渲染樹,這就意味有不可見元素,當匹配DOM樹和樣式規則樹時,若發現一個元素的對應的樣式規則上有display:none,瀏覽器會認為該元素是不可見的,因此不會把該元素產出到渲染樹上。

    性能優化

    css優化

    1.盡量減少層級

    1
    #div p.class {
    2
    color: red;
    3
    }
    4
    
    5
    .class {
    6
    color: red;
    7
    }

    層級減少,意味者匹配時遍歷的dom就少。
    關於less嵌套的書寫規範也基於這個道理。

    2.使用類選擇器而不是標籤選擇器

    減少匹配次數

    3.按需加載css

    1
    (function(){
    2
    window.gConfig = window.gConfig || {};
    3
    window.gConfig.isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
    4
    var hClassName;
    5
    if(window.gConfig.isMobile){
    6
    hClassName = ' phone';
    7
    
    8
    document.write('<link rel="stylesheet" href="https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.css" />');
    9
    document.write('<link rel="preload" href="//res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/m/index.js" crossorigin="anonymous" as="script" />');
    10
    
    11
    }else{
    12
    hClassName = ' pc';
    13
    
    14
    document.write('<link rel="stylesheet" href="https://res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.css" />');
    15
    document.write('<link rel="preload" href="//res.hc-cdn.com/cpage-pep-discount-area-v6/2.0.24/pc/index.js" crossorigin="anonymous" as="script" />');
    16
    
    17
    }
    18
    var root = document.documentElement;
    19
    root.className += hClassName ;
    20
    
    21
    })();

    async 與 defer

    [來自https://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html]

    使用

    • 如果腳本是模塊化的並且不依賴於任何腳本,請使用async。
    • 如果該腳本依賴於另一個腳本或由另一個腳本所依賴,則使用defer。

    減少資源請求

    瀏覽器的併發數量有限,所以為了減少瀏覽器因為優先加載很多不必要資源,以及網絡請求和響應時間帶來的頁面渲染阻塞時間,我們首先應該想到的是減少頁面加載的資源,能夠盡量用壓縮合併,懶加載等方法減少頁面的資源請求。

    延遲加載圖像

    儘管圖片的加載和渲染不會影響頁面渲染,但是為了盡可能地優先展示首屏圖片和減少資源請求數量,我們需要對圖片做懶加載。

    1
    document.addEventListener("DOMContentLoaded", function() {
    2
    let lazyImages = [].slice.call(document.querySelectorAll("img.lazy"));
    3
    let active = false;
    4
    
    5
    const lazyLoad = function() {
    6
    if (active === false) {
    7
    active = true;
    8
    
    9
    setTimeout(function() {
    10
    lazyImages.forEach(function(lazyImage) {
    11
    if ((lazyImage.getBoundingClientRect().top <= window.innerHeight && lazyImage.getBoundingClientRect().bottom >= 0) && getComputedStyle(lazyImage).display !== "none") {
    12
    lazyImage.src = lazyImage.dataset.src;
    13
    lazyImage.srcset = lazyImage.dataset.srcset;
    14
    lazyImage.classList.remove("lazy");
    15
    
    16
    lazyImages = lazyImages.filter(function(image) {
    17
    return image !== lazyImage;
    18
    });
    19
    
    20
    if (lazyImages.length === 0) {
    21
    document.removeEventListener("scroll", lazyLoad);
    22
    window.removeEventListener("resize", lazyLoad);
    23
    window.removeEventListener("orientationchange", lazyLoad);
    24
    }
    25
    }
    26
    });
    27
    
    28
    active = false;
    29
    }, 200);
    30
    }
    31
    };
    32
    
    33
    document.addEventListener("scroll", lazyLoad);
    34
    window.addEventListener("resize", lazyLoad);
    35
    window.addEventListener("orientationchange", lazyLoad);
    36
    });

    詳情參考延遲加載圖像和視頻

    大促活動實踐

    2.1 懶加載與異步加載

    懶加載與異步加載是大促活動性能優化的主要手段,直白的說就是把用戶不需要或者不會立即看到的頁面數據與內容全都挪到頁面首屏渲染完成之後去加載,極限減小頁面首屏渲染的數據加載量與js,css執行帶來的性能損耗。

    2.1.1 導航下拉的異步加載

    導航的下拉內容是一塊結構非常複雜的html片段,如果直接加載,瀏覽器渲染的時間會拖慢頁面整體的加載時間:

    所有我們需要通過異步加載方式來獲取這段html片段,等頁面首屏渲染結束后再添加到頁面上,大致的代碼如下:

    1
    $.ajax({
    2
    url: url, async: false, timeout: 10000,
    3
    success: function (data) {
    4
    container.innerHTML = data;
    5
    var appendHtml = $('<div class="footer-wrapper">' + container.querySelector('#footer').innerHTML + '</div>');
    6
    var tempHtml = '<div style="display:none;">' + '<script type="text/html" id="header-lazyload-html-drop" class="header-lazyload-html" data-holder="#holder-drop">' + appendHtml.find('#header-lazyload-html-drop').html() + '<\/script><script type="text/html" id="header-lazyload-html-mbnav" class="header-lazyload-html" data-holder="#holder-mbnav">' + appendHtml.find('#header-lazyload-html-mbnav').html() + '<\/script></div>';
    7
    $('#footer').append(tempHtml);
    8
    feloader.onLoad(function () {
    9
    feloader.use('@cloud/common-resource/header', function () {
    10
    });
    11
    $('#footer').css('display', 'block');
    12
    });
    13
    },
    14
    error: function (XMLHttpRequest, textStatus, errorThrown) {
    15
    console.log(XMLHttpRequest.status, XMLHttpRequest.readyState, textStatus);
    16
    },
    17
    });

    2.1.2 圖片懶加載

    官網的cui套件中已經有lazyload的插件支持圖片懶加載,使用方法頁非常簡單:

    1
    <div class="list">
    2
    <img class="lazyload" data-src="http://www.placehold.it/375x200/eee/444/1" src="佔位圖片URL" />
    3
    <img class="lazyload" data-src="http://www.placehold.it/375x200/eee/444/2" src="佔位圖片URL" />
    4
    <img class="lazyload" data-src="http://www.placehold.it/375x200/eee/444/3" src="佔位圖片URL" />
    5
    <div class="lazyload" data-src="http://www.placehold.it/375x200/eee/444/3"></div>
    6
    ...
    7
    </div>

    從代碼我們差不多可以猜出圖片懶加載的原理,其實就是我們通過覆蓋img標籤src屬性,使得img標籤開始加載時由於沒有src的具體圖片地址而不去加載圖片,等到重要資源加載完之後,通過監聽onload的時間或者滾動條的滾動時機再去重寫對應標籤的src值來達到圖片懶加載:

    1
    /**
    2
    * load image
    3
    * @param {HTMLElement} el - the image element
    4
    * @private
    5
    */
    6
    _load(el) {
    7
    let source = el.getAttribute(ATTR_IMAGE_URL);
    8
    if (source) {
    9
    let processor = this._config.processor;
    10
    if (processor) {
    11
    source = processor(source, el);
    12
    }
    13
    
    14
    el.addEventListener('load', () => {
    15
    el.classList.remove(CLASSNAME);
    16
    });
    17
    // 判斷是否是什麼元素
    18
    if (el.tagName === 'IMG') {
    19
    el.src = source;
    20
    } else {
    21
    // 判斷source是不是一個類名,如果是類名的話,則加到class裏面去
    22
    if (/^[A-Za-z0-9_-]+$/.test(source)) {
    23
    el.classList.add(source);
    24
    } else {
    25
    let styles = el.getAttribute('style') || '';
    26
    styles += `;background-image: url(${source});`;
    27
    el.setAttribute('style', styles);
    28
    el.style.backgroundImage = source; // = `background-image: url(${source});`;
    29
    }
    30
    }
    31
    
    32
    el.removeAttribute(ATTR_IMAGE_URL);
    33
    }
    34
    }

    具體的插件代碼大家可以查看https://git.huawei.com/cnpm/lazyload

    同時官網的頁腳部分也採用了採用其它的加載方式也實現了懶加載的效果,頁腳的圖片都在css中引用,想要延遲加載頁腳圖片就需要延遲加載頁腳的css,但是延遲加載css造成的後果就是頁面加載的一瞬間頁腳會因為樣式確實而显示錯亂,所以我們可以在css樣式加載前強勢隱藏掉頁腳部分,等css加載完成后,頁腳dom自帶的display:block會自動显示頁腳。(==因為頁腳的seo特性沒有對其進行懶加載==)

    2.1.3 樓層內容的懶加載

    基於xtpl自帶的懶加載能力,配合pep定製頁面模板的邏輯,我們可以實現html的懶加載。在頁面初次渲染的時候,只有每個樓層的大體框架和標題等關鍵信息,如果需要的話可以給默認圖片等佔位,或設置最小高度佔位,防止錨點定位失效。
    當頁面滾動到該樓層的位置,js代碼方會執行,在初始化函數中,對該樓層的html進行加載,渲染,實現樓層圖片和html的懶加載,減少了首屏時間。
    具體代碼如下:

    1
    <div class="nov-c6-cards j-content">
    2
    </div>

     

    1
    public render(){
    2
    this.$el.find('.j-content').html(new Xtemplate(tpl).render(mockData))
    3
    ...
    4
    }

    2.1.4 套餐數據懶加載

    套餐數據的加載一直以來都是令人頭疼的,本次雙十一對於套餐腳本也做了優化,不僅對數據進行了緩存,同時也可以在指定的範圍進行套餐數據的渲染——和上述所說的樓層懶加載配合,可以做到未展示的樓層,套餐數據不請求,下拉框不渲染,詢價接口不調用,在首屏不出現大量套餐的情況下,可以大大提升首屏加載的性能。

    2.2.資源整合

    2.2.1.頁頭頁尾資源統一維護

    基礎模板的優化涉及到資源的合併,壓縮與異步加載,dom的延遲加載和圖片的懶加載。首先我們給出官網基礎模板引用的一部分js資源的表格:

    這部分js存在問題是分散在pep的各個資產庫路徑維護,有些壓縮了,有些沒有壓縮,js的加載也基本是順序執行,所以我們對這個部分的js和css資源進行了一個整合,進行的操作是遷移,合併,壓縮。

    建立common-resource倉庫去統一維護管理頁頭頁腳及公共資源代碼。

    2.2.2.合併加載方式相同的基礎功能js並壓縮

    common.js

    1
    import './common/js/AGrid';
    2
    import './common/js/jquery.base64';
    3
    import './common/js/lang-tips';
    4
    import './common/js/setLocaleCookie';
    5
    import './common/js/pepDialog';

    如上面代碼,將官網中用的分散的基礎功能js合併成一個common.js,經過伏羲流水線發布,cui套件會自動將js壓縮,這樣做的效果當然是減少官網頁面請求資源數,減小資源大小。

    2.2.3.資源異步加載

    觀察2.2.1中的表格可以發現,官網大部分js都是放在頭部或者是body后順序加載的,這些資源的加載時間必定是在DOMOnLoad之前

    這些js都是會阻塞頁面的渲染,導致頁面首屏加載變慢,我們需要做的就是通過之前頭尾資源的整理得出哪些資源是可以在onload之後去加載的,這些我們就可以把頁面加載時不需要執行的js和css全部移到頁面渲染完成後去加載,少了這部分的js邏輯執行時的阻塞,頁面首屏渲染的時間也會大大降低。

    通過cui套件中的feloader插件,我們可以比較便捷的控制js和css加載的時機:

    1
    feloader.onLoad(function () {
    2
    feloader.use([
    3
    '@cloud/link-to/index',
    4
    '@cloud/common-resource/uba',
    5
    '@cloud/common-resource/footer',
    6
    '@cloud/common-resource/header',
    7
    '@cloud/common-resource/common',
    8
    '@cloud/common-resource/prompt.css',
    9
    '@cloud/common-resource/footer.css',
    10
    ]);
    11
    });

    下圖可以明顯看到js的加載都轉移到onload之後了:

    2.2.4 圖片壓縮

    除了對設計給出的圖片有壓縮要求外,我們還通過對一部分不常更新的小圖標圖片進行base64編碼來減少頁面的圖片請求數量。

    2.3預解析與預加載

    除了延遲加載外,基礎模板還進行了諸如dns預解析,資源預加載的手段來提前解析dns和加載頁面資源。

    2.3.1 DNS 預解析

    當用戶訪問過官網頁面后,DNS預解析能夠使用戶在訪問雙十一活動頁之前提前進行DNS解析,從而減少雙十一活動頁面的dns解析時間,提高頁面的訪問性能,其實寫法也很簡單:

    1
    <link rel="dns-prefetch" href="//res.hc-cdn.com">
    2
    <link rel="dns-prefetch" href="//res-static1.huaweicloud.com">
    3
    <link rel="dns-prefetch" href="//res-static2.huaweicloud.com">
    4
    <link rel="dns-prefetch" href="//res-static3.huaweicloud.com">

    2.3.2 preload 預加載

    活動頁的部分js還使用了preload預加載的方式來提升頁面加載性能,preload的為什麼可以達到這種效果,我們需要看下面這段摘錄:

    Preloader 簡介

    HTML 解析器在創建 DOM 時如果碰上同步腳本(synchronous script),解析器會停止創建 DOM,轉而去執行腳本。所以,如果資源的獲取只發生在解析器創建 DOM時,同步腳本的介入將使網絡處於空置狀態,尤其是對外部腳本資源來說,當然,頁面內的腳本有時也會導致延遲。

    預加載器(Preloader)的出現就是為了優化這個過程,預加載器通過分析瀏覽器對 HTML 文檔的早期解析結果(這一階段叫做“令牌化(tokenization)”),找到可能包含資源的標籤(tag),並將這些資源的 URL 收集起來。令牌化階段的輸出將會送到真正的 HTML 解析器手中,而收集起來的資源 URLs 會和資源類型一起被送到讀取器(fetcher)手中,讀取器會根據這些資源對頁面加載速度的影響進行有次序地加載。

    基於以上原理,我們對官網相對重要的js資源進行preload預加載,以使得瀏覽器可以儘快地加載頁面所需的重要資源。

    1
    <link rel="preload" href="//res.hc-cdn.com/cnpm-feloader/1.0.6/feloader.js" as="script"/>
    2
    <link rel="preload" href="//polyfill.alicdn.com/polyfill.min.js?features=default,es6" as="script"/>
    3
    <link rel="preload" href="https://res-static3.huaweicloud.com/content/dam/cloudbu-site/archive/commons/3rdlib/jquery/jquery-1.12.4.min.js" as="script"/>
    4
    <link rel="preload" href="//res.hc-cdn.com/cnpm-wpk-reporter/1.0.6/wpk-performance.js" as="script"/>
    5
    
    6
    <link rel="preload" href="//res.hc-cdn.com/cpage-pep-2019nov-promotion/1.1.15/components/activity-banner/images/banner_mb.jpg" as="image" media="(max-width: 767px)">

    優化效果

    3.總結

    前端性能優化的方法手段並不僅限於文章陳述,官網前端團隊還會在前端性能優化的道路上學習更多,探索更多,將華為雲官網頁面的加載性能做到極致!

     

    點擊關注,第一時間了解華為雲新鮮技術~

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

    【【其他文章推薦】

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

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

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

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

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

    ※教你寫出一流的銷售文案?