整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          「day-ui」 Alert 組件學習

          「day-ui」 Alert 組件學習


          樣式功能來看,整體不是很復雜,alert 組件主要包括了主題色,title,關閉按鈕,關閉事件,居中,加粗等

          源碼

          • template
          <template>
            <!-- 顯示隱藏有動畫效果 -->
            <!-- 開發沒用過,不是很理解為什么使用v-show判斷顯示 -->
            <transition name="d-alert-fade">
              <div
                class="d-alert"
                :class="[typeClass, center ? 'is-center' : '', 'is-' + effect]"
                v-show="visible"
                role="alert"
              >
                <!-- 左側圖標 -->
                <i
                  class="d-alert__icon"
                  :class="[iconClass, isBigIcon]"
                  v-if="showIcon"
                ></i>
                <!-- title 和 描述 -->
                <div class="d-alert__content">
                  <span
                    class="d-alert__title"
                    :class="[isBoldTitle]"
                    v-if="title || $slots.title"
                  >
                    <slot name="title">{{ title }}</slot>
                  </span>
                  <p v-if="$slots.default && !description" class="d-alert__description">
                    <slot></slot>
                  </p>
                  <p v-if="description && !$slots.default" class="d-alert__description">
                    {{ description }}
                  </p>
                  <i
                    class="d-alert__closebtn"
                    :class="{
                      'is-customed': closeText !=='',
                      'd-icon-close': closeText===''
                    }"
                    v-show="closable"
                    @click="close"
                    >{{ closeText }}</i
                  >
                </div>
              </div>
            </transition>
          </template>
          

          使用 role 屬性告訴輔助設備(如屏幕閱讀器)這個元素所扮演的角色。本質上是增強語義性,當現有的 HTML標簽不能充分表達語義性的時候,就可以借助 role 來說明。

          這里不是很理解為什么 titledescription 使用了屬性和 slot 判斷,有清楚的朋友可以幫忙解答

          • props 屬性比較常規,這里就不介紹了哈
          setup(props, { emit, slots }) {
              // 接受的屬性轉為響應式
              const { description, type }=toRefs(props)
              // 使用 v-show 顯示隱藏
              const visible=ref(true)
              // 關閉事件
              const close=()=> {
                visible.value=false
                emit('close')
              }
              const typeClass=computed(()=> {
                return `d-alert--${type.value}`
              })
          
              const iconClass=computed(()=> {
                return TYPE_CLASSES_MAP[type.value] || 'd-icon-info'
              })
          
              const isBigIcon=computed(()=> {
                return description.value || slots.default ? 'is-big' : ''
              })
              const isBoldTitle=computed(()=> {
                return description.value || slots.default ? 'is-bold' : ''
              })
              return {
                close,
                visible,
                typeClass,
                iconClass,
                isBigIcon,
                isBoldTitle
              }
            }
          

          組件介紹到這里就結束了,比較簡單。為了湊字呢,這里在介紹下 transition 組件

          transition

          大部分朋友都了解這是設置組件動畫的內置動畫組件。通常有三種使用方式:

          1. CSS 過渡
          2. CSS 動畫
          3. Javascript 鉤子

          CSS 過渡

          我們通常使用的方法,css 配置 enterleave

          <template>
            <div class="app">
              <button @click="show=!show">
                Toggle render
              </button>
              <transition name="fade">
                <p v-if="show">我是測試</p>
              </transition>
            </div>
          </template>
          <script>
            export default {
              data() {
                return {
                  show: true
                }
              }
            }
          </script>
          <style>
            .fade-enter-active,
            .fade-leave-active {
              transition: opacity 0.5s ease;
            }
            .fade-enter-from,
            .fade-leave-to {
              opacity: 0;
            }
          </style>
          

          CSS 動畫

          <template>
            <div class="app">
              <button @click="show=!show">Toggle show</button>
              <transition name="bounce">
                <p v-if="show">我是測試</p>
              </transition>
            </div>
          </template>
          <script>
            export default {
              data() {
                return {
                  show: true
                }
              }
            }
          </script>
          <style>
            .bounce-enter-active {
              animation: bounce-in 0.5s;
            }
            .bounce-leave-active {
              // reverse 很關鍵
              animation: bounce-in 0.5s reverse;
            }
            @keyframes bounce-in {
              0% {
                transform: scale(0);
              }
              50% {
                transform: scale(1.5);
              }
              100% {
                transform: scale(1);
              }
            }
          </style>
          

          js 鉤子

          監聽 transition 組件的內置方法,js 控制動畫

          <template>
            <div class="app">
              <button @click="show=!show">
                Toggle render
              </button>
              <transition
                @before-enter="beforeEnter"
                @enter="enter"
                @before-leave="beforeLeave"
                @leave="leave"
                css="false"
              >
                <p v-if="show">hello</p>
              </transition>
            </div>
          </template>
          <script>
            export default {
              data() {
                return {
                  show: true
                }
              },
              methods: {
                beforeEnter(el) {
                  el.style.opacity=0
                  el.style.transition='opacity 0.5s ease'
                },
                enter(el) {
                  this.$el.offsetHeight
                  el.style.opacity=1
                },
                beforeLeave(el) {
                  el.style.opacity=1
                },
                leave(el) {
                  el.style.transition='opacity 0.5s ease'
                  el.style.opacity=0
                }
              }
            }
          </script>
          

          如果形參不指定 done ,則表明用戶不手動控制動畫的結束,而轉由節點的 transition 或者 animationEnd 來標識動畫結束,開始回調 afterEnter

          鉤子函數的形參的個數大于1,表示形參中有 done, 也就是說用戶必須手動控制動畫何時結束。所以一旦你配置了 done 形參,則轉由你告訴框架,動畫何時結束。需要在合適的時機調用 done,否則 afterEnter 接口就沒法被調用了。

          動畫觸發條件

          • 條件渲染(v-if)
          • 條件展示(v-show)
          • 動態組件
          • 組件根節點

          執行原理

          實例

          <template>
            <div class="app">
              <button @click="show=!show">
                Toggle render
              </button>
              <transition name="fade">
                <p v-if="show">hello</p>
              </transition>
            </div>
          </template>
          

          編譯生成的 render 函數(不是使用的模板組件)

          import {
            createVNode as _createVNode,
            openBlock as _openBlock,
            createBlock as _createBlock,
            createCommentVNode as _createCommentVNode,
            Transition as _Transition,
            withCtx as _withCtx,
          } from "vue";
          export function render(_ctx, _cache, $props, $setup, $data, $options) {
            return (
              // 收集動態節點 如 v-if v-for
              _openBlock(),
              _createBlock("template", null, [
                _createVNode("div", { class: "app" }, [
                  _createVNode(
                    "button",
                    {
                      onClick: ($event)=> (_ctx.show=!_ctx.show),
                    },
                    " Toggle render ",
                    8 /* PROPS */,
                    ["onClick"]
                  ),
                  _createVNode(
                    _Transition,
                    { name: "fade" },
                    {
                      // transition 只有一個子節點,默認插槽。多個子節點報錯
                      default: _withCtx(()=> [
                        _ctx.show
                          ? (_openBlock(), _createBlock("p", { key: 0 }, "hello"))
                          : _createCommentVNode("v-if", true),
                      ]),
                      _: 1,
                    }
                  ),
                ]),
              ])
            );
          }
          

          那么如何在組建創建和銷毀的時候執行事件呢?————創建鉤子函數 transition 組件返回的是處理過的第一個子節點

          • 如果 Transition 組件內部嵌套的是 KeepAlive 組件,那么它會繼續查找 KeepAlive 組件嵌套的第一個子元素節點,來作為渲染的元素節點。
          • 如果 Transition 組件內部沒有嵌套任何子節點,那么它會渲染空的注釋節點。

          trantion 組件定義

          const Transition=(props, { slots })=>
            //esolveTransitionProps 函數主要作用是,在我們給 Transition 傳遞的 Props 基礎上做一層封裝,然后返回一個新的 Props 對象,由于它包含了所有的 Props 處理
            h(BaseTransition, resolveTransitionProps(props), slots);
          const BaseTransition={
            name: `BaseTransition`,
            props: {
              mode: String,
              appear: Boolean,
              persisted: Boolean,
              // enter
              onBeforeEnter: TransitionHookValidator,
              onEnter: TransitionHookValidator,
              onAfterEnter: TransitionHookValidator,
              onEnterCancelled: TransitionHookValidator,
              // leave
              onBeforeLeave: TransitionHookValidator,
              onLeave: TransitionHookValidator,
              onAfterLeave: TransitionHookValidator,
              onLeaveCancelled: TransitionHookValidator,
              // appear
              onBeforeAppear: TransitionHookValidator,
              onAppear: TransitionHookValidator,
              onAfterAppear: TransitionHookValidator,
              onAppearCancelled: TransitionHookValidator,
            },
            setup(props, { slots }) {
              const instance=getCurrentInstance();
              const state=useTransitionState();
              let prevTransitionKey;
              return ()=> {
                const children=slots.default && getTransitionRawChildren(slots.default(), true);
                if (!children || !children.length) {
                  return;
                }
                // Transition 組件只允許一個子元素節點,多個報警告,提示使用 TransitionGroup 組件
                if (process.env.NODE_ENV !=="production" && children.length > 1) {
                  warn(
                    "<transition> can only be used on a single element or component. Use " +
                      "<transition-group> for lists."
                  );
                }
                // 不需要追蹤響應式,所以改成原始值,提升性能
                const rawProps=toRaw(props);
                const { mode }=rawProps;
                // 檢查 mode 是否合法
                if (
                  process.env.NODE_ENV !=="production" &&
                  mode &&
                  !["in-out", "out-in", "default"].includes(mode)
                ) {
                  warn(`invalid <transition> mode: ${mode}`);
                }
                // 獲取第一個子元素節點
                const child=children[0];
                if (state.isLeaving) {
                  return emptyPlaceholder(child);
                }
                // 處理 <transition><keep-alive/></transition> 的情況
                const innerChild=getKeepAliveChild(child);
                if (!innerChild) {
                  return emptyPlaceholder(child);
                }
                const enterHooks=resolveTransitionHooks(
                  innerChild,
                  rawProps,
                  state,
                  instance
                );
                setTransitionHooks(innerChild, enterHooks);
                const oldChild=instance.subTree;
                const oldInnerChild=oldChild && getKeepAliveChild(oldChild);
                let transitionKeyChanged=false;
                const { getTransitionKey }=innerChild.type;
                if (getTransitionKey) {
                  const key=getTransitionKey();
                  if (prevTransitionKey===undefined) {
                    prevTransitionKey=key;
                  } else if (key !==prevTransitionKey) {
                    prevTransitionKey=key;
                    transitionKeyChanged=true;
                  }
                }
                if (
                  oldInnerChild &&
                  oldInnerChild.type !==Comment &&
                  (!isSameVNodeType(innerChild, oldInnerChild) || transitionKeyChanged)
                ) {
                  const leavingHooks=resolveTransitionHooks(
                    oldInnerChild,
                    rawProps,
                    state,
                    instance
                  );
                  // 更新舊樹的鉤子函數
                  setTransitionHooks(oldInnerChild, leavingHooks);
                  // 在兩個視圖之間切換
                  if (mode==="out-in") {
                    state.isLeaving=true;
                    // 返回空的占位符節點,當離開過渡結束后,重新渲染組件
                    leavingHooks.afterLeave=()=> {
                      state.isLeaving=false;
                      instance.update();
                    };
                    return emptyPlaceholder(child);
                  } else if (mode==="in-out") {
                    leavingHooks.delayLeave=(el, earlyRemove, delayedLeave)=> {
                      const leavingVNodesCache=getLeavingNodesForType(
                        state,
                        oldInnerChild
                      );
                      leavingVNodesCache[String(oldInnerChild.key)]=oldInnerChild;
                      // early removal callback
                      el._leaveCb=()=> {
                        earlyRemove();
                        el._leaveCb=undefined;
                        delete enterHooks.delayedLeave;
                      };
                      enterHooks.delayedLeave=delayedLeave;
                    };
                  }
                }
                return child;
              };
            },
          };
          

          在渲染的過程中,Transition 組件還會通過 resolveTransitionHooks 去定義組件創建和刪除階段的鉤子函數對象,然后再通過 setTransitionHooks 函數去把這個鉤子函數對象設置到 vnode.transition 上。

          hooks定義

          const hooks={
              mode,
              persisted,
              beforeEnter(el) {
                let hook=onBeforeEnter;
                if (!state.isMounted) {
                  if (appear) {
                    hook=onBeforeAppear || onBeforeEnter;
                  } else {
                    return;
                  }
                }
                if (el._leaveCb) {
                  el._leaveCb(true /* cancelled */);
                }
                const leavingVNode=leavingVNodesCache[key];
                if (
                  leavingVNode &&
                  isSameVNodeType(vnode, leavingVNode) &&
                  leavingVNode.el._leaveCb
                ) {
                  leavingVNode.el._leaveCb();
                }
                callHook(hook, [el]);
              },
              enter(el) {
                let hook=onEnter;
                let afterHook=onAfterEnter;
                let cancelHook=onEnterCancelled;
                if (!state.isMounted) {
                  if (appear) {
                    hook=onAppear || onEnter;
                    afterHook=onAfterAppear || onAfterEnter;
                    cancelHook=onAppearCancelled || onEnterCancelled;
                  } else {
                    return;
                  }
                }
                let called=false;
                const done=(el._enterCb=(cancelled)=> {
                  if (called) return;
                  called=true;
                  if (cancelled) {
                    callHook(cancelHook, [el]);
                  } else {
                    callHook(afterHook, [el]);
                  }
                  if (hooks.delayedLeave) {
                    hooks.delayedLeave();
                  }
                  el._enterCb=undefined;
                });
                if (hook) {
                  hook(el, done);
                  if (hook.length <=1) {
                    done();
                  }
                } else {
                  done();
                }
              },
              leave(el, remove) {
                const key=String(vnode.key);
                if (el._enterCb) {
                  el._enterCb(true /* cancelled */);
                }
                if (state.isUnmounting) {
                  return remove();
                }
                callHook(onBeforeLeave, [el]);
                let called=false;
                const done=(el._leaveCb=(cancelled)=> {
                  if (called) return;
                  called=true;
                  remove();
                  if (cancelled) {
                    callHook(onLeaveCancelled, [el]);
                  } else {
                    callHook(onAfterLeave, [el]);
                  }
                  el._leaveCb=undefined;
                  if (leavingVNodesCache[key]===vnode) {
                    delete leavingVNodesCache[key];
                  }
                });
                leavingVNodesCache[key]=vnode;
                if (onLeave) {
                  onLeave(el, done);
                  if (onLeave.length <=1) {
                    done();
                  }
                } else {
                  done();
                }
              },
              clone(vnode) {
                return resolveTransitionHooks(vnode, props, state, instance);
              },
            };
          

          鉤子函數對象定義了 4 個鉤子函數,分別是 beforeEnterenterleaveclone。在節點 patch 階段的 mountElement 函數中,在插入節點前且存在過度會執行 vnode.transition 中的 beforeEnter 函數

          //beforeEnter 鉤子函數主要做的事情就是根據 appear 的值和 DOM 是否掛載,來執行 onBeforeEnter 函數或者是 onBeforeAppear 函數。appear 是否節點現實的時候執行動畫
          beforeEnter(el) {
            let hook=onBeforeEnter
            if (!state.isMounted) {
              if (appear) {
                hook=onBeforeAppear || onBeforeEnter
              }
              else {
                return
              }
            }
            if (el._leaveCb) {
              el._leaveCb(true /* cancelled */)
            }
            const leavingVNode=leavingVNodesCache[key]
            if (leavingVNode &&
              isSameVNodeType(vnode, leavingVNode) &&
              leavingVNode.el._leaveCb) {
              leavingVNode.el._leaveCb()
            }
            callHook(hook, [el])
          }
          

          resolveTransitionProps 函數

          function resolveTransitionProps(rawProps) {
            let {
              name="v",
              type,
              css=true,
              duration,
              enterFromClass=`${name}-enter-from`,
              enterActiveClass=`${name}-enter-active`,
              enterToClass=`${name}-enter-to`,
              appearFromClass=enterFromClass,
              appearActiveClass=enterActiveClass,
              appearToClass=enterToClass,
              leaveFromClass=`${name}-leave-from`,
              leaveActiveClass=`${name}-leave-active`,
              leaveToClass=`${name}-leave-to`,
            }=rawProps;
            const baseProps={};
            for (const key in rawProps) {
              if (!(key in DOMTransitionPropsValidators)) {
                baseProps[key]=rawProps[key];
              }
            }
            if (!css) {
              return baseProps;
            }
            const durations=normalizeDuration(duration);
            const enterDuration=durations && durations[0];
            const leaveDuration=durations && durations[1];
            const {
              onBeforeEnter,
              onEnter,
              onEnterCancelled,
              onLeave,
              onLeaveCancelled,
              onBeforeAppear=onBeforeEnter,
              onAppear=onEnter,
              onAppearCancelled=onEnterCancelled,
            }=baseProps;
            const finishEnter=(el, isAppear, done)=> {
              removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
              removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
              done && done();
            };
            const finishLeave=(el, done)=> {
              removeTransitionClass(el, leaveToClass);
              removeTransitionClass(el, leaveActiveClass);
              done && done();
            };
            const makeEnterHook=(isAppear)=> {
              return (el, done)=> {
                const hook=isAppear ? onAppear : onEnter;
                const resolve=()=> finishEnter(el, isAppear, done);
                hook && hook(el, resolve);
                nextFrame(()=> {
                  removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass);
                  addTransitionClass(el, isAppear ? appearToClass : enterToClass);
                  if (!(hook && hook.length > 1)) {
                    if (enterDuration) {
                      setTimeout(resolve, enterDuration);
                    } else {
                      whenTransitionEnds(el, type, resolve);
                    }
                  }
                });
              };
            };
            return extend(baseProps, {
              onBeforeEnter(el) {
                onBeforeEnter && onBeforeEnter(el);
                addTransitionClass(el, enterActiveClass);
                addTransitionClass(el, enterFromClass);
              },
              onBeforeAppear(el) {
                onBeforeAppear && onBeforeAppear(el);
                addTransitionClass(el, appearActiveClass);
                addTransitionClass(el, appearFromClass);
              },
              onEnter: makeEnterHook(false),
              onAppear: makeEnterHook(true),
              onLeave(el, done) {
                const resolve=()=> finishLeave(el, done);
                addTransitionClass(el, leaveActiveClass);
                addTransitionClass(el, leaveFromClass);
                nextFrame(()=> {
                  removeTransitionClass(el, leaveFromClass);
                  addTransitionClass(el, leaveToClass);
                  if (!(onLeave && onLeave.length > 1)) {
                    if (leaveDuration) {
                      setTimeout(resolve, leaveDuration);
                    } else {
                      whenTransitionEnds(el, type, resolve);
                    }
                  }
                });
                onLeave && onLeave(el, resolve);
              },
              onEnterCancelled(el) {
                finishEnter(el, false);
                onEnterCancelled && onEnterCancelled(el);
              },
              onAppearCancelled(el) {
                finishEnter(el, true);
                onAppearCancelled && onAppearCancelled(el);
              },
              onLeaveCancelled(el) {
                finishLeave(el);
                onLeaveCancelled && onLeaveCancelled(el);
              },
            });
          }
          

          我們來看 onBeforeEnter 函數,它的內部執行了基礎 props 傳入的 onBeforeEnter 鉤子函數,并且給 DOM 元素 el 添加了 enterActiveClassenterFromClass 樣式。

          其中,props 傳入的 onBeforeEnter 函數就是我們寫 Transition 組件時添加的 beforeEnter 鉤子函數。enterActiveClass 默認值是 v-enter-activeenterFromClass 默認值是 v-enter-from,如果給 Transition 組件傳入了 nameprop,比如 fade,那么 enterActiveClass 的值就是 fade-enter-activeenterFromClass 的值就是 fade-enter-from。(onBeforeAppearonBeforeEnter 的邏輯類似,就不贅述了,它是在我們給 Transition 組件傳入 appearProp,且首次掛載的時候執行的。執行完 beforeEnter 鉤子函數,接著插入元素到頁面,然后會執行 vnode.transition 中的 enter 鉤子函數,上面的 hooks 中)

          enter 函數內部,首先執行基礎 props 傳入的 onEnter 鉤子函數,然后在下一幀給 DOM 元素 el 移除了 enterFromClass,同時添加了 enterToClass 樣式(動畫也就是所謂的樣式交替改變)

          Transition 組件允許我們傳入 enterDuration 這個 prop,它會指定進入過渡的動畫時長,當然如果你不指定,Vue.js 內部會監聽動畫結束事件,然后在動畫結束后,執行 finishEnter 函數

          來看它的實現

          const finishEnter=(el, isAppear, done)=> {
            removeTransitionClass(el, isAppear ? appearToClass : enterToClass);
            removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass);
            done && done();
          };
          

          其實就是給 DOM 元素移除 enterToClass 以及 enterActiveClass,同時執行 done 函數,進而執行 onAfterEnter 鉤子函數

          leave 鉤子主要功能和 enter 相反。小伙伴們可自行查閱。

          以上就是對 alert 組件的學習。如有不對歡迎指正。

          么是JavaScript

          JavaScript是一種基于對象和事件驅動的、并具有安全性能的腳本語言,已經被廣泛用于Web應用開發,常用來為網頁添加各式各樣的動態功能,為用戶提供更流暢美觀的瀏覽效果。通常JavaScript腳本是通過嵌入在HTML中來實現自身的功能的。

          JavaScript特點

          是一種解釋性腳本語言(代碼不進行預編譯)。

          主要用來向HTML(標準通用標記語言下的一個應用)頁面添加交互行為。

          可以直接嵌入HTML頁面,但寫成單獨的js文件有利于結構和行為的分離

          跨平臺特性,在絕大多數瀏覽器的支持下,可以在多種平臺下運行(如Windows、Linux、Mac、Android、iOS等)。

          JavaScript組成


          JavaScript日常用途

          1、嵌入動態文本于HTML頁面。

          2、對瀏覽器事件做出響應。

          3、讀寫HTML元素

          4、在數據被提交到服務器之前驗證數據。

          5、檢測訪客的瀏覽器信息。

          6、控制cookies,包括創建和修改等。

          7、基于Node.js技術進行服務器端編程。

          JavaScript的基本結構

          <script type="text/javascript">
           <!—
           JavaScript 語句;
           —>
          </script >
          


          示例:

          ……
          <title>初學JavaScript</title>
          </head>
          <body>
          <script type="text/javascript">
           document.write("初學JavaScript");
           document.write("<h1>Hello,JavaScript</h1>");
          </script>
          </body>
          </html>
          


          <script>…</script>可以包含在文檔中的任何地方,只要保證這些代碼在被使用前已讀取并加載到內存即可

          JavaScript的執行原理


          網頁中引用JavaScript的方式

          1、使用<script>標簽

          2、外部JS文件

          <script src="export.js" type="text/javascript"></script>
          


          3.直接在HTML標簽中

          <input name="btn" type="button" value="彈出消息框" 
           onclick="javascript:alert('歡迎你');"/>
          


          JavaScript核心語法:


          1. 變量

          ①先聲明變量再賦值

          var width;
          width=5;
          var - 用于聲明變量的關鍵字
          width - 變量名
          


          ②同時聲明和賦值變量

          var catName="皮皮";
          var x, y, z=10;
          


          ③不聲明直接賦值【一般不使用】

          width=5;
          


          變量可以不經聲明而直接使用,但這種方法很容易出錯,也很難查找排錯,不推薦使用。

          2. 數據類型

          ①undefined:示例:var width;

          變量width沒有初始值,將被賦予值undefined

          ②null:表示一個空值,與undefined值相等

          ③number:

          var iNum=23; //整數

          var iNum=23.0; //浮點數

          ④Boolean:true和false 但是JS會把他們解析成1;0

          ⑤String:一組被引號(單引號或雙引號)括起來的文本 var string1="This is a string";

          3. typeof運算符

          typeof檢測變量的返回值;typeof運算符返回值如下:

          ①undefined:變量被聲明后,但未被賦值.

          ②string:用單引號或雙引號來聲明的字符串。

          ③boolean:true或false。

          ④number:整數或浮點數。

          ⑤object:javascript中的對象、數組和null。

          rometheus發出告警時分為兩部分。首先,Prometheus按告警規則(rule_files配置塊)向Alertmanager發送告警(即告警規則是在Prometheus上定義的)。然后,由Alertmanager來管理這些告警,包括去重(Deduplicating)、分組(Grouping)、靜音(silencing)、抑制(inhibition)、聚合(aggregation ),最終將面要發出的告警通過電子郵件、webhook等方式將告警通知路由(route)給對應的聯系人。

          分組:就是將具有相同性質的告警先分類,然后當作單個通知發送出來。比如A和B兩臺主機的磁盤(CPU/內存)使用率都在告警,則磁盤的告警就可以合并在一個通知中發送出來。可以想像某個服務部署了100個節點,在一次升版后因為bug,日志中均報同樣一類錯誤,如果不合并這類通知,那就是告警風暴。

          抑制:就是某些告警觸發后,則抑制(禁止)另一些告警。比如收到一條告警提示集群故障無法訪問,那么在該集群上的所有其他警告應該被抑制。

          靜音(默):將某些在預期內的告警設置為靜默(即不發送告警)。靜默是基于配置匹配規則。Alertmanager會檢查從Prometheus推送過來的告警事件,看這些告警事件是否與配置的靜默規則能匹配上,如果匹配則不發送任何通知。配置靜默方法是在Alertmanager的Web界面中,也可以使用amtool工具。

          總之:Alertmanager制定這一系列規則目的只有一個,就是提高告警質量。


          配置Alertmanager來做告警通知主要分三個步驟:

          一、安裝并配置Alertmanager

          # tar -xvf alertmanager-0.20.0.linux-amd64.tar.gz

          mv alertmanager-0.20.0.linux-amd64 /usr/local/alertmanager


          # cat alertmanager.yml

          global:

          resolve_timeout: 5m

          smtp_smarthost: 'smtp.mxhichina.com:465'

          smtp_from: 'noreply@demo.com'

          smtp_auth_username: 'noreply@demo.com'

          smtp_auth_password: '1235698'

          smtp_require_tls: false

          templates:

          - '/usr/local/alertmanager/template/default.tmpl'

          route:

          group_by: ['alertname']

          group_wait: 30s

          group_interval: 2m

          repeat_interval: 10m

          receiver: 'email'

          # continue default is false

          continue: true

          receivers:

          - name: 'email'

          email_configs:

          - to: 'cookingit222@163.com,itcooking222@163.com'

          send_resolved: true

          html: '{{ template "default.html" . }}'

          headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }

          inhibit_rules:

          - source_match:

          severity: 'critical'

          target_match:

          severity: 'warning'

          equal: ['alertname', 'dev', 'instance']


          alertmanager配置簡要說明:

          global:全局配置,主要配置告警方式,如郵件、webhook等。

          route:Prometheus的告警先是到達alertmanager的根路由(route),alertmanager的根路由不能包含任何匹配項,因為根路由是所有告警的入口點。另外,根路由需要配置一個接收器(receiver),用來處理那些沒有匹配到任何子路由的告警(如果沒有配置子路由,則全部由根路由發送告警),即缺省接收器。告警進入到根route后開始遍歷子route節點,如果匹配到,則將告警發送到該子route定義的receiver中,然后就停止匹配了。因為在route中continue默認為false,如果continue為true,則告警會繼續進行后續子route匹配。如果當前告警仍匹配不到任何的子route,則該告警將從其上一級(匹配)route或者根route發出(按最后匹配到的規則發出郵件)。

          group_by:用于分組聚合,對告警通知按標簽(label)進行分組,將具有相同標簽或相同告警名稱(alertname)的告警通知聚合在一個組,然后作為一個通知發送。如果想完全禁用聚合,可以設置為group_by: [...]

          group_wait: 當一個新的告警組被創建時,需要等待'group_wait'后才發送初始通知。這樣可以確保在發送等待前能聚合更多具有相同標簽的告警,最后合并為一個通知發送。

          group_interval: 當第一次告警通知發出后,在新的評估周期內又收到了該分組最新的告警,則需等待'group_interval'時間后,開始發送為該組觸發的新告警,可以簡單理解為,group就相當于一個通道(channel)。

          repeat_interval: 告警通知成功發送后,若問題一直未恢復,需再次重復發送的間隔。


          查看你的告警路由樹,將alertmanager.yml配置文件復制到對話框,然后點擊"Draw Routing Tree"

          https://www.prometheus.io/webtools/alerting/routing-tree-editor/


          修改好配置文件后,可以使用amtool工具檢查配置

          # ./amtool check-config alertmanager.yml

          Checking 'alertmanager.yml' SUCCESS


          # cat >/usr/lib/systemd/system/alertmanager.service <<EOF

          [Unit]

          Description=alertmanager

          [Service]

          ExecStart=/usr/local/alertmanager/alertmanager --config.file=/usr/local/alertmanager/alertmanager.yml --storage.path=/usr/local/alertmanager/data --web.listen-address=:9093 --data.retention=120h

          Restart=on-failure

          [Install]

          WantedBy=multi-user.target


          EOF


          # systemctl enable alertmanager

          # systemctl restart alertmanager


          alertmanager默認運行端口是:9093

          alertmanager也可以同prometheus一樣熱加載配置

          1)向alertmanager進程發送SIGHUP信號,如:kill -SIGHUP alertmanager_pid

          2)curl -X POST http://prometheus_ip:9093/-/reload


          二、修改prometheus的配置,關聯Alertmanager服務,同時添加對alertmanager的監控

          # Alertmanager configuration

          alerting:

          alertmanagers:

          - static_configs:

          - targets:

          - monitor01:9093

          rule_files:

          - "rules/*_rules.yml"

          - "rules/*_alerts.yml"

          ......

          - job_name: 'alertmanager'

          static_configs:

          - targets: ['localhost:9093']

          labels:

          app: alertmanager


          三、修改prometheus的配置,添加記錄規則和告警規則。

          # cat node_rules.yml

          groups:

          - name: node_rules

          #interval: 15s

          rules:

          - record: instance:node_cpu_usage

          expr: 100 - avg(irate(node_cpu_seconds_total{mode="idle"}[1m])) by (nodename) * 100

          labels:

          metric_type: CPU_monitor

          - record: instance:node_1m_load

          expr: node_load1

          labels:

          metric_type: load1m_monitor

          - record: instance:node_mem_usage

          expr: 100 - (node_memory_MemAvailable_bytes)/(node_memory_MemTotal_bytes) * 100

          labels:

          metric_type: Memory_monitor

          - record: instance:node_root_partition_predit

          expr: round(predict_linear(node_filesystem_free_bytes{device="rootfs",mountpoint="/"}[2h],12*3600)/(1024*1024*1024), 1)

          labels:

          metric_type: root_partition_monitor


          # cat node_alerts.yml

          groups:

          - name: node_alerts

          rules:

          - alert: cpu_usage_over_threshold

          expr: instance:node_cpu_usage > 80

          for: 1m

          labels:

          severity: warning

          annotations:

          summary: 主機 {{ $labels.nodename }} 的 CPU使用率持續1分鐘超出閾值,當前為 {{humanize $value}} %

          - alert: system_1m_load_over_threshold

          expr: instance:node_1m_load > 20

          for: 1m

          labels:

          severity: warning

          annotations:

          summary: 主機 {{ $labels.nodename }} 的 1分負載超出閾值,當前為 {{humanize $value}}

          - alert: mem_usage_over_threshold

          expr: instance:node_mem_usage > 80

          for: 1m

          annotations:

          summary: 主機 {{ $labels.nodename }} 的 內存 使用率持續1分鐘超出閾值,當前為 {{humanize $value}} %

          - alert: root_partition_usage_over_threshold

          expr: instance:node_root_partition_predit < 60

          for: 1m

          annotations:

          summary: 主機 {{ $labels.nodename }} 的 根分區 預計在12小時使用將達到 {{humanize $value}}GB,超出當前可用空間,請及時擴容!


          for 表示告警持續的時長,若持續時長小于該時間就不發給alertmanager了,大于該時間再發。for的值不要小于prometheus中的scrape_interval,例如scrape_interval為30s,for為15s,如果觸發告警規則,則再經過for時長后也一定會告警,這是因為最新的度量指標還沒有拉取,在15s時仍會用原來值進行計算。另外,要注意的是只有在第一次觸發告警時才會等待(for)時長。

          例如:10:43:00 觸發了集群A中的h1告警;10:43:10又觸發了集群A中的h2告警。則在10:44:10后發生一條告警是只包含h1的郵件,在10:44:20時會收到h1和h2聚合后的告警郵件,若h1和h2持續未恢復,則在repeat_interval后仍以聚合方式發送告警。

          注:h1和h2告警名相同,只是在不同主機上。


          完成上述配置后,主機CPU、負載、內存、磁盤超出閾值時就發觸發郵件告警。

          上述配置僅是對主機資源做了監控,并且告警只發到了缺省聯系人組。設想一下,在實際生產環境中,可能會按產品線或業務功能進行分組來研發,不同的服務出現告警時只發送通知到對應的聯系人組,其他不相關的組不需要接告警收通知,這就需要我們修改告警路由規則,而Alertmanager的核心和最復雜的地方就在告警路由規則的設置上。


          Prometheus的告警規則在推送給Alertmanager前有的三種狀態:

          1、沒有觸發告警閾值時,狀態為:inactive

          2、觸發了告警閾值但未滿足告警持續時間(for)時,狀態為:pending,如果不配置for或者指定for的值為0,則將跳過pending狀態。

          3、已觸發告警閾值并達到告警持續時間,開始將告警事件推送到Alertmanager,此時狀態為:firing。


          配置告警靜默(silence),用于在預期內的維護升級等操作。

          當我們在對系統進行維護升級時,通常不希望觸發告警通知;另外,當上游服務出現異常,想讓下游的服務不觸發告警,Prometheus將用于這種配置稱為silence(靜默)。silence用于設定一個(維護)時間段,如1小時,也可提前手動觸發silence過期,表示維護結束,恢復對應服務的告警通知功能。

          設置silence的方式有以下2種:

          1、登錄到alertmanager的控制臺操作

          2、通過amtool命令進行操作


          配置告警模板

          Alertmanager的通知模板是基于Go模板系統,詳細可參考官網。

          https://golang.org/pkg/text/template/

          https://prometheus.io/docs/alerting/latest/notifications/#alert

          https://prometheus.io/docs/prometheus/latest/configuration/template_reference/

          模板配置步驟:

          1、修改alertmanager.yml,配置模板地址,然后在每個receiver引用模板

          templates:

          - '/usr/local/alertmanager/template/default.tmpl'

          ...

          ...

          html: '{{ template "default.html" . }}'

          headers: { Subject: "{{ .GroupLabels.SortedPairs.Values }} [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]" }


          2、配置模板

          default.tmpl

          {{ define "default.html" }}

          {{- if gt (len .Alerts.Firing) 0 -}}

          [{{ .Status | toUpper }}:{{ .Alerts.Firing | len }}]

          {{ range $i, $alert :=.Alerts }}

          <pre>

          告警節點:{{ index $alert.Labels "nodename" }}

          告警服務:{{ index $alert.Labels "alertname" }}

          報警詳情:{{ index $alert.Annotations "summary" }}

          開始時間:{{ $alert.StartsAt }}

          </pre>

          {{ end }}

          {{ end }}

          {{- if gt (len .Alerts.Resolved) 0 -}}

          [{{ .Status | toUpper }}:{{ .Alerts.Resolved | len }}]

          {{ range $i, $alert :=.Alerts }}

          <pre>

          恢復節點:{{ index $alert.Labels "nodename" }}

          恢復服務:{{ index $alert.Labels "alertname" }}

          狀 態:{{ index $alert.Status }}

          開始時間:{{ $alert.StartsAt }}

          恢復時間:{{ $alert.EndsAt }}

          </pre>

          {{ end }}

          {{ end }}

          {{- end }}


          注:告警模板如果配置有問題,會導致郵件發送失敗,注意觀察日志。


          以下是靜默設置步驟:

          1、登錄到alertmanager控制臺(http://IP:9093/#/alerts),點擊上方菜單欄中Alerts,可看到當前有2個告警。

          2、點擊右上角"New Silence",創建Silence任務,具體設置如下,匹配規則支持正則。

          注:可按instance或job_name等方式來進行正則匹配。


          3、Silence創建成功后,可以看到處于Active狀態的Silence

          總之,多實踐。


          主站蜘蛛池模板: 亚洲福利视频一区二区三区| 亚洲电影一区二区| 在线精品亚洲一区二区三区| 国产成人高清精品一区二区三区| 一区二区三区四区在线观看视频| 日本在线一区二区| 日韩免费一区二区三区| 无码免费一区二区三区免费播放| 国产成人一区二区三区| 日韩人妻一区二区三区蜜桃视频| 天堂不卡一区二区视频在线观看| 一区二区免费视频| 精品日韩在线视频一区二区三区 | 91一区二区三区| 日韩精品一区二区三区中文版| 亚洲AV无一区二区三区久久| 日韩一区二区三区在线观看| 欧美日韩精品一区二区在线观看 | 国产亚洲欧洲Aⅴ综合一区| 国产一区二区三区乱码在线观看| 久久精品一区二区国产| 亚洲av无码天堂一区二区三区 | 日韩视频一区二区在线观看| 高清国产AV一区二区三区| 亚洲国产一区视频| 国产一区二区三区精品久久呦| 亚洲一区欧洲一区| 国产香蕉一区二区在线网站 | 国产一区二区三区精品视频| 狠狠爱无码一区二区三区| 四虎在线观看一区二区| 亚洲日韩AV一区二区三区中文| 亚洲欧洲一区二区| 99精品国产一区二区三区不卡| 日本中文字幕一区二区有码在线| 国精产品一区一区三区免费视频| 精品伦精品一区二区三区视频| 国产精品免费大片一区二区| 无码人妻久久一区二区三区蜜桃 | 国产精品香蕉在线一区| 人妻夜夜爽天天爽爽一区|