標籤: 電動車

  • 1.5T超大空間只要6.98萬起的自主SUV車主滿意嗎

    1.5T超大空間只要6.98萬起的自主SUV車主滿意嗎

    9萬首先我是五菱的忠實粉絲,第一輛車就是五菱,跑了20萬公里發動機和變速箱還沒動過,這質量是我選擇560的原因,其次我要經常拉點貨,後排放倒后空間完全可以滿足我,而且一家人出去玩也方便很多,1。8L+5AMT開起來比手動擋省力得多,雖然有時候會有些頓挫感,但畢竟不是全自動,也能接受了。

    前言

    有什麼車上市不到一年,就可以做到超過330000輛的銷量成績呢,沒錯它就是自主SUV陣型中的又一神車-寶駿560,對於任何品牌和車型來說,這都是一個了不起的成績,從全系沒有自動擋,到增加了5AMT,隨後又推出了1.5T的車型,為了更適應消費者需求一直在改進,那麼車主對它的評價如何呢,一起來探討一下。

    上汽通用五菱-寶駿560

    指導價:6.98-9.48萬

    車主:菜鳥凌凌漆

    購買車型:2016款 1.8L 手動精英型

    裸車價格:7.68萬

    五菱的車子實用性是經過市場的長時間考驗的,性價比也是較高的,圓潤飽滿的外觀是非常耐看的,雖說沒什麼太大的亮點,內飾中控布局也趨向轎車化的設計水平,無論是前後排,空間都非常寬敞,後排想怎麼坐就怎麼坐,1.8L的動力還是挺不錯的,起油比較快,操控性精準,尤其是在鄉村道路,底盤高的優勢就很明顯。

    10月份提的車,目前行駛了2000多公里,油耗7.5L左右,續航能力很好。

    車主:三石哥

    購買車型:2016款 1.8L AMT智能手動豪華型

    裸車價格:8.9萬

    首先我是五菱的忠實粉絲,第一輛車就是五菱,跑了20萬公里發動機和變速箱還沒動過,這質量是我選擇560的原因,其次我要經常拉點貨,後排放倒后空間完全可以滿足我,而且一家人出去玩也方便很多,1.8L+5AMT開起來比手動擋省力得多,雖然有時候會有些頓挫感,但畢竟不是全自動,也能接受了。

    11月份提的車,目前走了1800公里,市區油耗7.8左右,高速6.7左右,比之前麵包車要省油。

    車主:青春的奧秘

    購買車型:2016款 1.5T 手動豪華型

    裸車價格:8.88萬

    首先外觀時尚大氣,寶駿560 1.5T手動豪華版配置很齊全,功能強大,無鑰匙進入/啟動、倒車影像、定速巡航什麼的都有配備,動力非常充足,渦輪介入得比較早,加速很順暢,後排空間秒殺同級,北京癱什麼的隨便坐,電動助力的方向盤很輕盈,一個手也可以輕鬆操控,轉向很精準,這麼低的價格買到這麼好的車子,超值了。

    9月份提的車,目前行駛4000公里,綜合油耗6.7,還是非常省油的。

    總結:寶駿560一上市的歡迎程度就非常誇張,從單一的動力總成擴展到兩個,從純手動擋車型發展到增加了5AMT,現在唯一欠缺的就是全自動擋車型,上升的空間還有很大,逐步完善後再創銷量奇迹也是指日可待。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

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

  • 最新研究:大氣二氧化碳總量逼近1500萬年來高峰

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

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

    【其他文章推薦】

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

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

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

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

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

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

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

  • 業界也認了 不是所有生質能都是碳中性 歐盟面臨減碳新挑戰

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

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

    【其他文章推薦】

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

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

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

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

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

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

  • 在k8s上部署日誌系統elfk

    在k8s上部署日誌系統elfk

    日誌系統elfk

    前言

    經過上周的技術預研,在本周一通過開會研究,根據公司的現有業務流量和技術棧,決定選擇的日誌系統方案為:elasticsearch(es)+logstash(lo)+filebeat(fi)+kibana(ki)組合。es選擇使用aliyun提供的es,lo&fi選擇自己部署,ki是阿里雲送的。因為申請ecs需要一定的時間,暫時選擇部署在測試&生產環境(吐槽一下,我司測試和生產公用一套k8s並且託管與aliyun……)。用時一天(前期有部署的差不多過)完成在kubernetes上部署完成elfk(先部署起來再說,優化什麼的後期根據需要再搞)。

    組件簡介

    es 是一個實時的、分佈式的可擴展的搜索引擎,允許進行全文、結構化搜索,它通常用於索引和搜索大量日誌數據,也可用於搜索許多不同類型的文。

    lo 主要的有點就是它的靈活性,主要因為它有很多插件,詳細的文檔以及直白的配置格式讓它可以在多種場景下應用。我們基本上可以在網上找到很多資源,幾乎可以處理任何問題。

    作為 Beats 家族的一員,fi 是一個輕量級的日誌傳輸工具,它的存在正彌補了 lo 的缺點fi作為一個輕量級的日誌傳輸工具可以將日誌推送到中心lo。

    ki是一個分析和可視化平台,它可以瀏覽、可視化存儲在es集群上排名靠前的日誌數據,並構建儀錶盤。ki結合es操作簡單集成了絕大多數es的API,是專業的日誌展示應用。

    數據採集流程圖

    日誌流向:logs_data—> fi —> lo —> es—> ki。

    logs_data通過fi收集日誌,輸出到lo,通過lo做一些過濾和修改之後傳送到es數據庫,ki讀取es數據庫做分析。

    部署

    根據我司的實際集群狀況,此文檔部署將完全還原日誌系統的部署情況。

    在本地MAC安裝kubectl連接aliyun託管k8s

    在客戶端(隨便本地一台虛機上)安裝和託管的k8s一樣版本的kubectl

    curl -LO https://storage.googleapis.com/kubernetes-release/release/v1.14.8/bin/linux/amd64/kubectl   
    chmod +x ./kubectl 
    mv ./kubectl /usr/local/bin/kubectl  
    將阿里雲託管的k8s的kubeconfig 複製到$HOME/.kube/config 目錄下,注意用戶權限的問題
    
    部署ELFK

    申請一個名稱空間(一般一個項目一個名稱空間)。

    # cat kube-logging.yaml 
    apiVersion: v1
    kind: Namespace
    metadata:
      name: loging
    

    部署es。網上找個差不多的資源清單,根據自己的需求進行適當的修改,運行,出錯就根據日誌進行再修改。

    # cat elasticsearch.yaml 
    apiVersion: storage.k8s.io/v1
    kind: StorageClass
    metadata:
      name: local-class
      namespace: loging
    provisioner: kubernetes.io/no-provisioner
    volumeBindingMode: WaitForFirstConsumer
    # Supported policies: Delete, Retain
    reclaimPolicy: Delete
    ---
    kind: PersistentVolume
    apiVersion: v1
    metadata:
      name: datadir1
      namespace: logging
      labels:
        type: local
    spec:
      storageClassName: local-class
      capacity:
        storage: 5Gi
      accessModes:
        - ReadWriteOnce
      hostPath:
        path: "/data/data1"
    --- 
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: elasticsearch
      namespace: loging
    spec:
      serviceName: elasticsearch
      selector:
        matchLabels:
          app: elasticsearch
      template:
        metadata:
          labels:
            app: elasticsearch
        spec:
          containers:
          - name: elasticsearch
            image: elasticsearch:7.3.1
            resources:
                limits:
                  cpu: 1000m
                requests:
                  cpu: 100m
            ports:
            - containerPort: 9200
              name: rest
              protocol: TCP
            - containerPort: 9300
              name: inter-node
              protocol: TCP
            volumeMounts:
            - name: data
              mountPath: /usr/share/elasticsearch/data
            env:
              - name: "discovery.type"
                value: "single-node"
              - name: cluster.name
                value: k8s-logs
              - name: node.name
                valueFrom:
                  fieldRef:
                    fieldPath: metadata.name
              - name: ES_JAVA_OPTS
                value: "-Xms512m -Xmx512m"
          initContainers:
          - name: fix-permissions
            image: busybox
            command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
            securityContext:
              privileged: true
            volumeMounts:
            - name: data
              mountPath: /usr/share/elasticsearch/data
          - name: increase-vm-max-map
            image: busybox
            command: ["sysctl", "-w", "vm.max_map_count=262144"]
            securityContext:
              privileged: true
          - name: increase-fd-ulimit
            image: busybox
            command: ["sh", "-c", "ulimit -n 65536"]
            securityContext:
              privileged: true
      volumeClaimTemplates:
      - metadata:
          name: data
          labels:
            app: elasticsearch
        spec:
          accessModes: [ "ReadWriteOnce" ]
          storageClassName: "local-class"
          resources:
            requests:
              storage: 5Gi
    ---
    kind: Service
    apiVersion: v1
    metadata:
      name: elasticsearch
      namespace: loging
      labels:
        app: elasticsearch
    spec:
      selector:
        app: elasticsearch
      clusterIP: None
      ports:
        - port: 9200
          name: rest
        - port: 9300
          name: inter-node
    

    部署ki。因為根據數據採集流程圖,ki是和es結合的,配置相對簡單。

    # cat kibana.yaml 
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: kibana
      namespace: loging
      labels:
        k8s-app: kibana
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: kibana
      template:
        metadata:
          labels:
            k8s-app: kibana
        spec:
          containers:
          - name: kibana
            image: kibana:7.3.1
            resources:
              limits:
                cpu: 1
                memory: 500Mi
              requests:
                cpu: 0.5
                memory: 200Mi
            env:
              - name: ELASTICSEARCH_HOSTS
    #注意value是es的services,因為es是有狀態,用的無頭服務,所以連接的就不僅僅是pod的名字了
                value: http://elasticsearch:9200   
            ports:
            - containerPort: 5601
              name: ui
              protocol: TCP
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: kibana
      namespace: loging
    spec:
      ports:
      - port: 5601
        protocol: TCP
        targetPort: ui
      selector:
        k8s-app: kibana
    

    配置ingress-controller。因為我司用的是阿里雲託管的k8s自帶的nginx-ingress,並且配置了強制轉換https。所以kibana-ingress也要配成https。

    # openssl genrsa -out tls.key 2048
    # openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=Beijing/L=Beijing/O=DevOps/CN=kibana.test.realibox.com
    # kubectl create secret tls kibana-ingress-secret --cert=tls.crt --key=tls.key
    

    kibana-ingress配置如下。提供兩種,一種是https,一種是http。

    https:
    # cat kibana-ingress.yaml 
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: kibana
      namespace: loging
    spec:
      tls:
      - hosts:
        - kibana.test.realibox.com
        secretName: kibana-ingress-secret
      rules:
      - host: kibana.test.realibox.com
        http:
          paths:
          - path: /
            backend:
              serviceName: kibana
              servicePort: 5601
    
    http:
    # cat kibana-ingress.yaml 
    apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: kibana
      namespace: loging
    spec:
      rules:
      - host: kibana.test.realibox.com
        http:
          paths:
          - path: /
            backend:
              serviceName: kibana
              servicePort: 5601
    

    部署lo。因為lo的作用是對fi收集到的日誌進行過濾,需要根據不同的日誌做不同的處理,所以可能要經常性的進行改動,要進行解耦。所以選擇以configmap的形式進行掛載。

    # cat logstash.yaml
    apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: logstash
      namespace: loging
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: logstash
      template:
        metadata:
          labels:
            app: logstash
        spec:
          containers:
          - name: logstash
            image: elastic/logstash:7.3.1
            volumeMounts:
            - name: config
              mountPath: /opt/logstash/config/containers.conf
              subPath: containers.conf
            command:
            - "/bin/sh"
            - "-c"
            - "/opt/logstash/bin/logstash -f /opt/logstash/config/containers.conf"
          volumes:
          - name: config
            configMap:
              name: logstash-k8s-config
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: logstash
      name: logstash
      namespace: loging
    spec:
      ports:
        - port: 8080       
          targetPort: 8080
      selector:
        app: logstash
      type: ClusterIP
    
    # cat logstash-config.yaml 
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: logstash
      name: logstash
      namespace: loging
    spec:
      ports:
        - port: 8080       
          targetPort: 8080
      selector:
        app: logstash
      type: ClusterIP
    ---
    
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: logstash-k8s-config
      namespace: loging
    data:
      containers.conf: |
        input {
          beats {
            port => 8080  #filebeat連接端口
          }
        }
        output {
          elasticsearch {
            hosts => ["elasticsearch:9200"]  #es的service
            index => "logstash-%{+YYYY.MM.dd}"
          }
        }
    注意:修改configmap 相當於修改鏡像。必須重新apply 應用資源清單才能生效。根據數據採集流程圖,lo的數據由fi流入,流向es。
    

    部署fi。fi的主要作用是進行日誌的採集,然後將數據交給lo。

    # cat filebeat.yaml 
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: filebeat-config
      namespace: loging
      labels:
        app: filebeat
    data:
      filebeat.yml: |-
        filebeat.config:
          inputs:
            # Mounted `filebeat-inputs` configmap:
            path: ${path.config}/inputs.d/*.yml
            # Reload inputs configs as they change:
            reload.enabled: false
          modules:
            path: ${path.config}/modules.d/*.yml
            # Reload module configs as they change:
            reload.enabled: false
        # To enable hints based autodiscover, remove `filebeat.config.inputs` configuration and uncomment this:
        #filebeat.autodiscover:
        #  providers:
        #    - type: kubernetes
        #      hints.enabled: true
        output.logstash:
          hosts: ['${LOGSTASH_HOST:logstash}:${LOGSTASH_PORT:8080}']   #流向lo
    ---
    apiVersion: v1
    kind: ConfigMap
    metadata:
      name: filebeat-inputs
      namespace: loging
      labels:
        app: filebeat
    data:
      kubernetes.yml: |-
        - type: docker
          containers.ids:
          - "*"
          processors:
            - add_kubernetes_metadata:
                in_cluster: true
    ---
    apiVersion: extensions/v1beta1
    kind: DaemonSet
    metadata:
      name: filebeat
      namespace: loging
      labels:
        app: filebeat
    spec:
      selector:
        matchLabels:
          app: filebeat
      template:
        metadata:
          labels:
            app: filebeat
        spec:
          serviceAccountName: filebeat
          terminationGracePeriodSeconds: 30
          containers:
          - name: filebeat
            image: elastic/filebeat:7.3.1
            args: [
              "-c", "/etc/filebeat.yml",
              "-e",
            ]
            env:   #注入變量
            - name: LOGSTASH_HOST
              value: logstash
            - name: LOGSTASH_PORT
              value: "8080"
            securityContext:
              runAsUser: 0
              # If using Red Hat OpenShift uncomment this:
              #privileged: true
            resources:
              limits:
                memory: 200Mi
              requests:
                cpu: 100m
                memory: 100Mi
            volumeMounts:
            - name: config
              mountPath: /etc/filebeat.yml
              readOnly: true
              subPath: filebeat.yml
            - name: inputs
              mountPath: /usr/share/filebeat/inputs.d
              readOnly: true
            - name: data
              mountPath: /usr/share/filebeat/data
            - name: varlibdockercontainers
              mountPath: /var/lib/docker/containers
              readOnly: true
          volumes:
          - name: config
            configMap:
              defaultMode: 0600
              name: filebeat-config
          - name: varlibdockercontainers
            hostPath:
              path: /var/lib/docker/containers
          - name: inputs
            configMap:
              defaultMode: 0600
              name: filebeat-inputs
          # data folder stores a registry of read status for all files, so we don't send everything again on a Filebeat pod restart
          - name: data
            hostPath:
              path: /var/lib/filebeat-data
              type: DirectoryOrCreate
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRoleBinding
    metadata:
      name: filebeat
    subjects:
    - kind: ServiceAccount
      name: filebeat
      namespace: loging
    roleRef:
      kind: ClusterRole
      name: filebeat
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: rbac.authorization.k8s.io/v1beta1
    kind: ClusterRole
    metadata:
      name: filebeat
      labels:
        app: filebeat
    rules:
    - apiGroups: [""] # "" indicates the core API group
      resources:
      - namespaces
      - pods
      verbs:
      - get
      - watch
      - list
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: filebeat
      namespace: loging
      labels:
        app: filebeat
    ---
    

    至此完成在k8s上部署es+lo+fi+ki ,進行簡單驗證。

    驗證

    查看svc、pod、ingress信息

    # kubectl get svc,pods,ingress -n loging
    NAME                    TYPE        CLUSTER-IP        EXTERNAL-IP   PORT(S)             AGE
    service/elasticsearch   ClusterIP   None              <none>        9200/TCP,9300/TCP   151m
    service/kibana          ClusterIP   xxx.168.239.2xx   <none>        5601/TCP            20h
    service/logstash        ClusterIP   xxx.168.38.1xx   <none>        8080/TCP            122m
    
    NAME                            READY   STATUS    RESTARTS   AGE
    pod/elasticsearch-0             1/1     Running   0          151m
    pod/filebeat-24zl7              1/1     Running   0          118m
    pod/filebeat-4w7b6              1/1     Running   0          118m
    pod/filebeat-m5kv4              1/1     Running   0          118m
    pod/filebeat-t6x4t              1/1     Running   0          118m
    pod/kibana-689f4bd647-7jrqd     1/1     Running   0          20h
    pod/logstash-76bc9b5f95-qtngp   1/1     Running   0          122m
    
    NAME                        HOSTS                       ADDRESS        PORTS     AGE
    ingress.extensions/kibana   kibana.test.realibox.com   xxx.xx.xx.xxx   80, 443   19h
    
    web配置

    配置索引

    發現

    至此算是簡單完成。後續需要不斷優化,不過那是後事了。

    問題總結

    這應該算是第一次親自在測試&生產環境部署應用了,而且是自己很不熟悉的日子系統,遇到了很多問題,需要總結。

    1. 如何調研一項技術棧;
    2. 如何選定方案;
    3. 因為網上幾乎沒有找到類似的方案(也不曉得別的公司是怎麼搞的,反正網上找不到有效的可能借鑒的)。需要自己根據不同的文檔總結嘗試;
    4. 一個組件的標籤盡可能一致;
    5. 如何查看公司是否做了端口限制和https強制轉換;
    6. 遇到IT的事一定要看日誌,這點很重要,日誌可以解決絕大多數問題;
    7. 一個人再怎麼整也會忽略一些點,自己先嘗試然後請教朋友,共同進步。
    8. 項目先上線再說別的,目前是這樣,一件事又百分之20的把握就可以去做了。百分之80再去做就沒啥意思了。
    9. 自學重點學的是理論,公司才能學到操作。

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

    【其他文章推薦】

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

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

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

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

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

  • 助長毀林?巴西銷往歐洲的黃豆 至少20%來自森林砍伐

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

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

    【其他文章推薦】

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

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

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

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

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

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

  • Redis學習筆記(十八) 集群(下)

    Redis學習筆記(十八) 集群(下)

    複製和故障轉移

    Redis集群中的節點分為主節點(master)和從節點(slave),其中主節點用於處理槽,而從節點則用於複製某個主節點,並在被複制 的主節點下線時,代替下線主節點繼續處理命令請求。

    設置從節點:CLUSTER REPLICATE < node_id >可以讓接收命令的節點稱為node_id 所指定節點的從節點,並開始對主節點進行複製。

    1)接收到該命令的節點首先會在自己的clusterState.nodes字典中找到node_id所對應節點的clusterNode結構,並將自己的clusterState.myself.slaveof指針指向這個結構,以此來記錄這個節點正在複製的主節點:

    struct clusterNode{
        //如果這個時一個從節點,那麼指向主節點
        struct clusterNode *slaveof;
    }

    2)節點修改自己的clusterState.myself.flags中的屬性,關閉原本的REDIS_NODE_MASTER標識,打開REDIS_NODE_SLAVE標識,標識這個節點已經由原來的主節點變成了從節點。

    3)節點會調用複製代碼,根據clusterState.myself.slaveof指向clusterNode結構所保存的IP地址和端口號,對節點進行複製。

    一個節點稱為從節點,並開始複製某個主節點這一信息會通過消息發送給集群中的其他節點,最終集群中的所有節點都會知道某個從節點正在複製某個主節點。

    集群中的所有節點都會在代表主節點的clusterNode結構的slaves屬性和numslaves屬性中記錄正在複製這個主節點的從節點名單:

    struct clusterNode{
        //正在複製這個主節點的從節點數量
        int numslaves;
        //數組,每個數組項指向一個正在複製這個主節點的從節點的clusterNode
        struct clusterNode **slaves;
    }

    集群中的每個節點都會定期地向集群中的其他節點發送PING消息,一次來檢測對方是否在線,如果接收PING消息的節點沒有在規定的時間內,向發送PING消息的節點返回PONG消息,那麼發送PING消息的節點就會將階段后PING消息的節點標記為疑似下線(PFAIL)。

    集群中的各個節點會通過相互發送消息的方式來交換集群中各個節點的狀態信息:某個節點處於在線狀態、疑似下線、已下線狀態。

    當一個主節點A通過罅隙得知主節點B認為主節點C進入疑似下線狀態時,主節點A會在自己的clusterState.nodes字典中找到主節點C所對應的clusterNode結構,並將主節點B的下線報告添加到clusterNode結構的fail_reports鏈表中

    status clusterNode{
        list *fali_reports;//鏈表,記錄所有其他節點對該節點的下線報告
    };

    下線報告結構:

     

    struct c;isterNodeFailReport{
        //報告目標節點已經下線的節點
        struct clusterNode *node;
        //最後一個從node節點收到下線報告的時間(程序使用這個時間戳來檢查下線報告是否過期)
        mstime_t time;
    } typedef clusterNodeFailReport;

    如果集群里半數以上負責處理槽的主節點都將某個主節點x報告未疑似下線,那麼這個主節點x將被標記未已下線,將主節點x標記為已下線的節點會向集群廣播一條關於主節點x的FAIL罅隙,所有收到這條罅隙的節點都會立即將主節點x標記為已下線。

    故障轉移的步驟:

    1)複製下線主節點的所有從節點裏面,會有一個從節點被選中,

    2)被選中的從節點會執行SLAVEOF no one命令,成為新的主節點。

    3)新的主節點會撤銷所有對已下線主節點的槽指派,並將這些槽指派給自己。

    4)新的主節點向集群廣播一條PONG消息,這條消息讓其他集群中的其他節點立即知道這個節點已經由從節點變為主節點,並且這個主節點已經接管了原本已下線節點負責處理的槽。

    5)新的主節點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成。

    選舉新的主節點:

    1)集群的配置紀元是一個計數器。他的初始值為0;

    2)當集群中的某個節點開始一次故障轉移操作時,集群配置紀元的值會被加1。

    3)集群裏面每個負責處理槽的主節點都有一次投票機會,而第一個向主節點要求投票的從節點將獲得主節點的投票。

    4)當從節點發現自己正在複製的主節點進入下線狀態時,從節點會向集群官博一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這個消息、並且具有投票權的主節點向這個從節點投票。

    5)如果一個主節點具有投票權,並且這個主節點尚未投票給其他從節點,那麼主節點將向要求投票的從節點返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,表示這個主節點支持從節點成為新的主節點。

    6)每個參与選舉的從節點都會收到CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,並根據自己收到了多少條這種消息來統計自己獲得了多少主節點支持。

    7)如果集群中有N個具有投票權的主節點,那麼當一個從節點大於等於N/2+1張支持票時,這個從節點就當選成為新的主節點。

    8)如果在一個配置紀元裏面沒有從節點收集到足夠多的支持票,那麼集群進入下一個紀元,再次進行選舉,直到選出新的主節點為止。

     

    消息

    集群中各個節點通過發送和接收消息來進行通信,我們稱發送消息的節點為發送者,接收消息的節點為接收者:

    1)MEET消息,當發送者接到客戶端發送的CLUSTER MEET命令時,發送者會向接收者發送MEET消息,請求接收者加入到發送者當前所處的集群裏面。

    2)PING消息,集群裏面的每個節點默認每隔一秒鐘就會從已知節點列表中隨機選出五個節點,然後對這五個節點中最長時間沒有發送過PING消息的節點發送PING消息,以此檢測被選中的節點是否在線。除此之外,如果節點A最後一次收到節點B發送的PONG消息的時間,距離當前時間已超過了節點A的cluster-node-timeout選項設置時長的一半,那麼節點A也會向節點B發送PING消息,這可以防止節點A因長時間沒有隨機選中節點B作為PING消息的發送對象而導致節點B的信息更新滯后。

    3)PONG消息,當接收者收到發送者發來的MEET消息或者PING時,為了向發送者確認這條MEET、PING消息已到達,接收者會向發送者返回一條PONG消息。另外,一個節點也可以通過向集群發送集群廣播自己的PONG消息來讓集群中的其他節點立即刷新關於這個節點的認識。

    4)FAIL消息,當一個主節點A判斷另一個主節點B已經進入FAIL狀態時,節點A會會向集群廣播一條關於節點B的FAIL消息,所有接收到這條消息的節點都會立即將節點B標記為已下線。

    5)PUBLISH消息,當節點接收到一個PUBLISH命令時,節點會執行這個命令,並向集群廣播一條PUBLISH消息,所有接收到這條PUBLISH消息的節點都會執行相同的PUBLISH命令。

    一條消息由消息頭(header)和消息正文(data組成)

    消息頭:

    typedef struct {
        //消息的長度(消息頭的長度和消息正文的長度)
        uint32_t totlen;
        //消息的類型
        uint16_t type;
        //消息正文包含的節點信息數量
        //只有發送MEET、PING、PONG這三種Gossip協議消息時使用
        uint16_t count;
        
        //薩松這所處的配置紀元
        uint64_t currentEpoch;
        //如果發送者是一個主節點,那麼這裏面記錄的時發送者的配置紀元
        //如果發送者時一個從節點,那麼這裏面記錄的時發送者正在複製的主節點的配置紀元
        uint64_t configEpoch;
        //發送者的名稱(ID)
        char sender[REDIS_CLUSTER_NAMELEN];
        //發送者目前的槽指派信息
        unsigned char myslots[REDIS_CLUSTER_SLOTS/8];
        //如果發送者是一個從節點,記錄的是發送者正在複製的主節點的名稱
        //如果發送者是一個主節點,那麼這裏記錄的是REDIS_NODE_NULL_NAME
        char slaveof[REDIS_CLUSTER_NAMELEN];
        //發送者的端口號
        uint16_t port;
        //發送者的標識值
        uint16_t flags;
        //發送者所處集群的狀態
        unsigned char state;
        //消息正文
        union clusterMsgData data;
    } clusterMsg;

    clusterMsg.data 結構:

    union clusterMsgData{
        //MEET PING PONG 消息正文
        struct{
            //每條MEET PING PONG消息都包含兩個 clusterMsgDataGossip 結構
            clusterMsgDataGossip gossip[1]
        } ping;
        //FAIL 消息正文
        struct{
            clusterMsgDataFail about;
        } fali;
        
        //PUBLISH消息正文
        struct{
            clusterMsgDataPublish msg;
        } publish;
    }

    clusterMsgDataGossip結構記錄了選中節點的名字,發送者與被選中節點最後一次發送和接收PING消息和PONG消息的時間戳,被選中節點的IP地址和端口號,以及被選中節點的標識值:

    typedef struct {
        //節點的名字
        char nodename[REDIS_CLUSTER_NAMELEN];
        //最後一次向該節點發送PING消息的時間戳
        uint32_t ping_sent;
        //最後一次從該 節點接收到PONG消息的時間戳
        uint32_t pong_received;
        //節點的IP地址
        char ip[16];
        //節點的端口號
        uint16_t port;
        //節點的標識值
        uint16_t flags;
    } clusterMsgDataGossip;

    每天學一點,總會有收穫。

     

    說明:尊重作者知識產權,文中內容參考《Redis設計與實現》,僅在此做學習與大家分享。

     

     

     

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

    【其他文章推薦】

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

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

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

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

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

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

  • 10萬以下產品力最強的兩款SUV!詳解風神AX5和遠景SUV!

    10萬以下產品力最強的兩款SUV!詳解風神AX5和遠景SUV!

    未來肯定不能以價格取勝。那麼要考慮的就是要從質量與技術可靠為主了。而且有了AX7在前面擋着,未來AX5隻要在宣傳上做得出色,未來還是可以期待的。而遠景SUV換裝了全新的家族式造型設計,與吉利汽車其他車型有了很好的延續性。

    目前SUV市場非常紅火,但對於國內的消費者來說;他們很多時候對SUV的購車預算大多數會考慮在10萬以下的車型,而這個區間最火熱的要屬最火熱的遠景SUV了。遠景SUV作為一款上市多年的車型,憑藉著良好的口碑與超高的性價比在三四線城市當中一直都受到追捧。其他品牌看它這麼紅火,肯定心有不甘;醞釀着推出競品與其競爭。在這個背景下,東風風神AX5應該是與它最接近的一款車型了。

    總結:AX5作為市場的新丁,未來要遇到的挑戰很多;首先它目前的市場肯可度比較一般,而且相對性價比較高的遠景SUV來說,價格相對高一些;未來肯定不能以價格取勝。那麼要考慮的就是要從質量與技術可靠為主了;而且有了AX7在前面擋着,未來AX5隻要在宣傳上做得出色,未來還是可以期待的。

    而遠景SUV換裝了全新的家族式造型設計,與吉利汽車其他車型有了很好的延續性。但它的車尾卻與前臉不太協調,但改款之後提價的策略也被不少消費者詬病;這是吉利急需要改變的不好影響。當然目前吉利汽車一系列熱門車型會把整個市場口碑營造上去,但遠景SUV卻不能以此為傲。雖不說前有前敵,但肯定後邊會有不少追兵。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

  • Spring Security 實戰乾貨:如何實現不同的接口不同的安全策略

    Spring Security 實戰乾貨:如何實現不同的接口不同的安全策略

    1. 前言

    歡迎閱讀 Spring Security 實戰乾貨 系列文章 。最近有開發小夥伴提了一個有趣的問題。他正在做一個項目,涉及兩種風格,一種是給小程序出接口,安全上使用無狀態的JWT Token;另一種是管理後台使用的是Freemarker,也就是前後端不分離的Session機制。用Spring Security該怎麼辦?

    2. 解決方案

    我們可以通過多次繼承WebSecurityConfigurerAdapter構建多個HttpSecurityHttpSecurity 對象會告訴我們如何驗證用戶的身份,如何進行訪問控制,採取的何種策略等等。

    如果你看過之前的教程,我們是這麼配置的:

    /**
     * 單策略配置
     *
     * @author felord.cn
     * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
     * @since 14 :58 2019/10/15
     */
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
    @EnableWebSecurity
    @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class CustomSpringBootWebSecurityConfiguration {
        
        /**
         * The type Default configurer adapter.
         */
        @Configuration
        @Order(SecurityProperties.BASIC_AUTH_ORDER)
        static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                super.configure(auth);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 配置 httpSecurity
    
            }
        }
    }
    

    上面的配置了一個HttpSecurity,我們如法炮製再增加一個WebSecurityConfigurerAdapter的子類來配置另一個HttpSecurity。伴隨而來的還有不少的問題要解決。

    2.1 如何路由不同的安全配置

    我們配置了兩個HttpSecurity之後,程序如何讓小程序接口和後台接口走對應的HttpSecurity

    HttpSecurity.antMatcher(String antPattern)可以提供過濾機制。比如我們配置:

         @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 配置 httpSecurity
                http.antMatcher("/admin/v1");
    
            }
    

    那麼該HttpSecurity將只提供給以/admin/v1開頭的所有URL。這要求我們針對不同的客戶端指定統一的URL前綴。

    舉一反三隻要HttpSecurity提供的功能都可以進行個性化定製。比如登錄方式,角色體系等。

    2.2 如何指定默認的HttpSecurity

    我們可以通過在WebSecurityConfigurerAdapter實現上使用@Order註解來指定優先級,數值越大優先級越低,沒有@Order註解將優先級最低。

    2.3 如何配置不同的UserDetailsService

    很多情況下我們希望普通用戶和管理用戶完全隔離,我們就需要多個UserDetailsService,你可以在下面的方法中對AuthenticationManagerBuilder進行具體的設置來配置UserDetailsService,同時也可以配置不同的密碼策略。

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
            @Override
            public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                // 自行實現
                return  null ;
            }
        });
        // 也可以設計特定的密碼策略
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
        auth.authenticationProvider(daoAuthenticationProvider);
    }
    

    2.4 最終的配置模板

    上面的幾個問題解決之後,我們基本上掌握了在一個應用中執行多種安全策略。配置模板如下:

    /**
     * 多個策略配置
     *
     * @author felord.cn
     * @see org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration
     * @since 14 :58 2019/10/15
     */
    @Configuration
    @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true)
    @EnableWebSecurity
    @ConditionalOnClass(WebSecurityConfigurerAdapter.class)
    @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
    public class CustomSpringBootWebSecurityConfiguration {
    
        /**
         * 後台接口安全策略. 默認配置
         */
        @Configuration
        @Order(1)
        static class AdminConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
                //用戶詳情服務個性化
                daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        // 自行實現
                        return null;
                    }
                });
                // 也可以設計特定的密碼策略
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
                auth.authenticationProvider(daoAuthenticationProvider);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 根據需求自行定製
                http.antMatcher("/admin/v1")
                        .sessionManagement(Customizer.withDefaults())
                        .formLogin(Customizer.withDefaults());
    
    
            }
        }
    
        /**
         * app接口安全策略. 沒有{@link Order}註解優先級比上面低
         */
        @Configuration
        static class AppConfigurerAdapter extends WebSecurityConfigurerAdapter {
    
            @Override
            protected void configure(AuthenticationManagerBuilder auth) throws Exception {
                DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
                //用戶詳情服務個性化
                daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() {
                    @Override
                    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
                        // 自行實現
                        return null;
                    }
                });
                // 也可以設計特定的密碼策略
                BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
                daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder);
                auth.authenticationProvider(daoAuthenticationProvider);
            }
    
            @Override
            public void configure(WebSecurity web) {
                super.configure(web);
            }
    
            @Override
            protected void configure(HttpSecurity http) throws Exception {
                // 根據需求自行定製
                http.antMatcher("/app/v1")
                        .sessionManagement(Customizer.withDefaults())
                        .formLogin(Customizer.withDefaults());
    
    
            }
        }
    }
    

    3. 總結

    今天我們解決了如何針對不同類型接口採取不同的安全策略的方法,希望對你有用,如果你有什麼問題可以留言。多多關注:碼農小胖哥,更多乾貨奉上。

    關注公眾號:Felordcn 獲取更多資訊

    個人博客:https://felord.cn

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

    【其他文章推薦】

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

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

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

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

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

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

  • 本田摘得Google2016年度搜尋冠軍,繽智在其中扮演怎樣的角色?

    本田摘得Google2016年度搜尋冠軍,繽智在其中扮演怎樣的角色?

    繽智的內飾也凸顯着先鋒人群的獨到品味:充滿朝氣的撞色設計,搭配非對稱航空式座艙,這一設計巧思,頗受年輕人青睞,傳達出他們的脫俗格調。懸浮式儀錶台、多變魔術杯架等設計同樣極為出彩,縱享人性化舒適的同時,又展現了他們務實的另一面。

    據Google Trends近日公布的2016年度搜尋清單來看,在汽車品牌這一類別中,位列榜首的並非德系三強,而是一向以技術著稱的日系本田。更值得一提的是,Google今年採用了與去年不同的計算方式,既結合了去年的搜尋次數,亦結合了“最高搜索流量和最持久峰值搜索流量”的數據,由此產生的榜單含金量相比往年來得更高。如此看來,在高含金量的2016榜單之中,日系本田力壓奔馳、特斯拉等一眾豪華德系美系品牌獲此殊榮,更具說服力。

    SUV表現突出,本田關注節節攀升

    那麼,本田奪冠的奧秘又在哪裡?這與本田SUV車型的賣座息息相關。SUV車型無論在國內抑或是全球市場來說均為大熱車型,其中更以價格親民、功能實用的小型SUV尤為突出,而本田旗下的小型SUV—— HR-V(國內繽智)則在全球範圍內掀起了“本田熱”。作為一款全球車型,HR-V在國內有着一個消費者更為熟悉的名字——繽智。繽智採用了全球化的概念設計,其身為SUV的多面實用性、類Coupe的時尚外觀、MpV化的空間配置,都極為貼切的迎合了國內乃至國外車主的需求。

    憑藉全球車型的特殊身份,繽智在國內市場早已擁有較高人氣。以即將過去的2016年為例,廣汽本田繽智穩坐1至11月熱門合資車型銷量桂冠寶座,其中以11月銷量為例,繽智當月終端銷量高達17,485輛,同比增長45.1%,成績喜人的同時又不免讓人深思:繽智憑什麼打動眾多消費者的心?

    先鋒產品力 打造引領潮流的時尚座駕

    面對來自年輕化的受眾群體與瞬息萬變的時尚風向的雙重壓力,單純追逐潮流的設計已顯得有些捉襟見肘,在更多時候,只有引領潮流才能受人追捧持久不衰,而繽智就深知此道。在這個“看顏”的時代,繽智外觀具有天生優勢,更引領着年輕時尚人群對車型外觀的美學要求。

    那麼,繽智的獨特優勢又體現在何處呢?外觀整體以鑽石切面的幾何視覺效果為理念,頗為符合當下主流時尚圈審美;稜角分明的前臉與LED投影式前大燈組的組合極具時尚美學;側身上揚的腰線和隱藏式後門把手設計則頗有幾分Coupe風味,符合時下國際車型設計風向及國際化大眾口味。

    繽智的內飾也凸顯着先鋒人群的獨到品味:充滿朝氣的撞色設計,搭配非對稱航空式座艙,這一設計巧思,頗受年輕人青睞,傳達出他們的脫俗格調;懸浮式儀錶台、多變魔術杯架等設計同樣極為出彩,縱享人性化舒適的同時,又展現了他們務實的另一面。

    同時,繽智作為以黑科技著稱的廣本首款SUV車型,智能配置自然不容小覷,輕鬆滿足受眾需求。SmartEntry智能無匙進入系統輕鬆拉開車門坐進車內,按下一鍵啟動功能,懸浮式儀錶盤隨即點亮,盡顯科技感更充分調動了駕馭的熱情。與此同時,兼具手機屏幕映射功能的智能屏互聯繫統也十分便捷。

    追本溯源,無論一款車其他表現如何,與受眾匹配的動力系統都必須具備,而繽智要迎合的是對速度與駕馭快感追求都較高的年輕時尚人群,要求不可謂不高。繽智用銷量證明了自身的優異,以1.8L i-VTEC發動機打入市場,配合CVT無級變速器與AWD四驅系統,不但滿足了先鋒人士速度的追求又兼顧了良好的通過性。而之後上市的1.5L車型則搭載了地球夢科技發動機,不只滿足了年輕人不同的動力需求,官方給出的6.8L/100km的綜合油耗也滿足了年輕群體經濟環保願望,並由此受到了熱捧。

    廣本里程碑 繽智後市值得期待

    作為廣汽本田首款SUV車型,繽智自上市起便肩負廣汽本田打入國內SUV市場的重任。在上市短短2年時間里,繽智就以累計快高達30萬用戶,一舉成為廣汽本田旗下主打車型,同時更奪得前十一個月合資品牌小型SUV銷量冠軍。在幫助本田力壓其他日系對手成就日系在華銷量第一的同時,繽智早已不負眾望地扛起了廣汽本田SUV市場的大梁。

    在SUV市場如日中天的今天,繽智作為小型SUV的旗幟車型,憑藉潮流先鋒的外觀、品味獨到的內飾、科技感十足的配置與洶湧澎湃的動力,不只滿足了年輕人的多元化購車需求,更肩擔起了引領市場潮流風向的重任,如此看來,繽智獲得銷量冠軍是綜合實力的體現,帶動本田博得Google年度熱搜。

    再看此次Google榜單,奔馳、特斯拉位列二三,相比旗下GLC與Model 3等熱門車型,像國外的HR-V抑或是國內的繽智這樣的小型車型來說,更為親民的價格,同樣豐富的空間,以及不錯的通過性和全面的配置表現,都是其更為大熱的理由。今後也會有越來越多的年輕消費者會選擇經濟性更高的小型SUV,繽智作為熱門小型SUV,熱銷勢將繼續升溫。本站聲明:網站內容來源於http://www.auto6s.com/,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

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

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

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

  • Vue —— 精講 VueX (1)

    Vue —— 精講 VueX (1)

    大綱

    這一講我們最主要的就是學習vue中的數據管理VueX,這個是一個大殺器

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

    一、回顧一些Promise相關的東西

    Promise 有幾個比較重要的方法,最重要的還是有一個叫做all的方法,這個也是非常的強大的

    假設我們目前要求,希望能按順序的拿到先後的兩個ajax那麼我應該怎麼處理呢

    Promse.all( [
    new Promose( ( resolve,rejcet ) => {
            $.ajax({
                url:'xxxx',
                data:'xxxx',
                sucess:(res) => {
                    resolve(res)
                }
            })
            $.ajax({
                url:'xxxx',
                data:'xxxx',
                sucess:(res) => {
                    resolve(res)
                }
            })
        })
    
    ]).then( results => {
        consel.log(results)
        // 這樣拿到的就是一個數組了, 先後的順序就是裏面的值
    } )
    

    注意啊這裏對promise的深入的解釋說明

    1. 首先我們的兩個回調resolve 還有reject注意啊,
    這兩個回調回調函數是 在傳入的時候定義的,但是調用是在promse里調的!這兩個參數是函數!!函數!!回調函數!
    
    

    一、概念

    Vue官方介紹
    絕大多數的管方都非常喜歡用概念來解釋概念,這就有點難搞了,我這個概念的都不懂,你又給我搞另一個概念
    實際上那個Vuex就是一個大管家,統一進行管理,全局的單例模式

    1.最通俗的解釋

    Vuex實際上就是一個 用來放 一些組件共享的數據的,實際上這可能是是下面這些情況

    1. 登錄
      假設我們目前有50+頁面。我們都每一個頁面都要發送接口請求而且這些請求需要token,那麼如果我是登錄的,我就需要在每一個頁面拿到我的登錄token這樣就造成了數據的傳來傳去非常麻煩,如果我們有一個公共的地方來放這些東西就好了

    2. 購物車。收藏
      也會有這種組件之間打出傳值的情況發生,那麼我如何管理這些東西呢,這個就是一個問題

    綜上所述,我們需要使用Vuex*

    二、如何入門的使用

    2.簡單的使用

    這裏假設有這樣的一個需求:我們目前有兩個組件App.vue 還有BMlaoli.vue 我呢,他們之間有層級的關係,app裏面有一個變量叫做contuend 我希望我在app裏面對countend的操作能夠動態的傳遞到我們的BMlaoli里,而且不使用父子組件傳值,那麼我們如何做呢?親看下面講演

    1. 首先我們需要有兩個組件
      他們都是最基礎的樣子

    App

    <template>
      <div id="app">
        <h1> 我是vueapp </h1>
      </div>
    </template>
    
    <script>
    
    export default {
      name: 'App',
      components: {
      }
    }
    </script>
    
    <style>
    </style>
    
    

    BMlaoli

    
    <template>
        <div>
            <h1>我是bm界面</h1>
        </div>
    </template>
    
    <script>
        export default {
            
        }
    </script>
    
    <style lang="sass" scoped>
    
    </style>
    
    1. app的業務邏輯
    <template>
      <div id="app">
        <p>{{contuned}}</p>
        
          <button @click="contuned ++" >+</button>
          <button @click="contuned --" >-</button>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          contuned: 100
        }
      },
    }
    </script>
    
    <style>
    </style>
    
    

    但是問題來了,我目前希望你們在app裏面做的更改可以反映到我的Bm組件里,而且不通過父子組件的方式,那麼我該怎麼做呢?實際上非常的簡單

    這個時候我們就需要一個 ‘第三者來處理這個東西’,這個第三者就是這個Vuex。

    1. vueX的引入

    實際上,如果你有手動的安裝使用配VueRouter的經驗的話。這Vuex也是差不多的都是一樣的使用方法

    第一步:npm install vuex
    第二步:創建一個文件夾sote里寫一個index.js
    第三部:在index裏面安裝
    第四部:在main里掛載就好了

    index.js

    import Vue from 'vue'
    import Vuex from 'vuex'
    // 安裝
    Vue.use(Vuex)
    
    // 使用
    const store = new Vuex.Store({
        state:{},
        mutations: {
        },
        actions:{},
        getters:{},
        modules:{}
    
    })
    
    // 倒出
    export default store
    
    
    

    main.js

    import Vue from 'vue'
    import App from './App.vue'
    
    // 導入
    import Store from './store'
    
    Vue.config.productionTip = false
    
    // 掛載
    new Vue({
      Store,
      render: h => h(App),
    }).$mount('#app')
    
    

    非常的簡單

    1. app里的業務邏輯
    <template>
      <div id="app">
        <p>{{ $store.state.contuned }}</p>
        
          <button @click="$store.state.contuned ++" >+</button>
          <button @click="$store.state.contuned --" >-</button>
        
        <h1>------bmlaoli的界面--------</h1>
    
        <bmlao></bmlao>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          // contuned: 100
        }
      },
    }
    </script>
    
    <style>
    </style>
    
    

    三、正確的操作state的方式

    1.需要注意的地方

    $store.state.contuned

    需要非常說的就是 請你不要這樣去修改vuex里的值,而是通過如下的方式去修改,詳細見官方api說明

    1. 概述我們的更改邏輯
      view視圖提交(Dispatch) —-> actions處理異步操作(commit) —–> Muations 記錄你的修改 ,方便以後追蹤(Mutate) —–> state修改(render)

    2. 代碼邏輯
      /state/index.js

        state:{
            contuned:1000
        },
        mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
        },
        actions:{},
        getters:{},
        modules:{}
    

    /app.vue

    <template>
      <div id="app">
        <p>{{ $store.state.contuned }}</p>
        
          <button @click="additon" >+</button>
          <button @click="subraction" >-</button>
        
        <h1>------bmlaoli的界面--------</h1>
    
        <bmlao></bmlao>
    
      </div>
    </template>
    
    <script>
    
    import bmlao from '@/components/Bmlaoli';
    
    export default {
      name: 'App',
      components: {
        bmlao,
      },
      data() {
        return {
          // contuned: 100
        }
      },
      methods: {
        additon() {
          this.$store.commit('increment')
        },
        subraction() {
          this.$store.commit('decrement')
          
        },
      },
    }
    </script>
    
    <style>
    </style>
    
    
    
    1. 除了使用this.$store.state.XXX或缺vuex的數據之外,我們還有一種方法,也是開發和工作中,比較常見的東西,那就是使用map進行各種數據的映射,它可以映射全部的vuex裏面的東西
    // 假設我們現在就使用map把東西數據,state裏面的東西,映射到我們的computed裏面
    improt { mapState } form 'vuex
    computed {
          ...mapState( ['XXX'] )
             // 但是我們不推薦使用上面得方式,我們更加推薦使用對象器別名的方式
          ...mapState( { xCount:'Count' } )      
    }
    
    ===> 這樣你就得到這些State ,除了state之外,其它的mutation 還有getter也是一樣的原理
    
    1. 深入立即map的映射原理,

    一個優秀的程序員,不應該只是停留在會用的層面,還應該靈活的掌握其中的原理,只有掌握了原理,才能做到行雲流水的開發.工具永遠只是工具,只有自己變強才是王道

    ====> 我們一點點的分析,
    // 1. 首先我們得computed需要接受函數
    computed:{
          XXXX:() => {  return this.$stroe.state.XXX }
     }
    // 2. 我們要寫一個方法mapState
    function mapState( array ){
         let obj = {}
         array.forEach( stateKey => { obj[stateKey] = () => this.$store.state[stateKey] }  )
         return obj
    }
    // 以上就是內部的實現原理
    

    這樣我們就能開發者工具追綜這些東西的變化了

    四、核心概念解讀

    vueX中有五個核心

    1.單一狀態樹

    1. 管理系統 現實生活中的例子
      我們先來舉一個例子,在我們國家一個人有很多的信息會被記錄到檔案管理的各個部門,車貸房貸,身份證 ,戶口 ,結婚登記,這些信息都分佈式的存放在各個局,地產局,戶口部門……,這樣對於我們的人 來說, 我們的數據來來源就是多樣的,多數據源,但是這樣有問題,就是一起管理的時候是不好管理的,你可能需要去這個地方蓋章,去哪個地方改造,如果不通過又要重新回來蓋章,XXXX太麻煩了。
    2. vuex的管理邏輯
      在我們的vue中確確實實 ,你可以new 多個Vuex但是,我們是不推薦的,因為這樣管理起來就會非常的麻煩,我們的vuex推薦是 只使用一個vuex來管理共享的數據源,這個設計理念就是;單一數據源(也叫單一狀態樹)

    2.getter

    這個東西類似於計算屬性
    有時候我們需要從 store 中的 state 中派生出一些狀態,例如對列表進行過濾並計數:
    高階函數 ,返回函數的調用

    1. 需求,還是原來的案例,我希望我獲取的contuned的平方

    當然了,你這樣也是可以的

      <h2>{{ $store.state.contuned * $store.state.contuned }}</h2>
    

    但是很low 是不啦,如果你要寫很多很多的複雜邏輯操作,那不就涼涼了嗎,所以這裏引申出我們的getter,字面理解就是獲取的時候,對數據做一些手腳,那麼我們看看如何使用

    1. 明確一下,我們的操作基本上都是在我們的vuex文件裏面進行的

    在getter裏面搞事情
    store/index.js

    
    import Vue from 'vue'
    import Vuex from 'vuex'
    // 安裝
    Vue.use(Vuex)
    
    // 使用
    const store = new Vuex.Store({
        state:{
            contuned:1000
        },
        mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
        },
        actions:{},
        getters:{
            powerCounter(state){
                return state.contuned * state.contuned 
            }
        },
        modules:{}
    
    })
    
    // 倒出
    export default store
    
    

    使用的時候就非常簡單了
    /bmlaoli.vue

      <h2>{{ $store.getters.powerCounter }}</h2>
    

    現在我們又有了另一個需求,如果我想傳遞參數,怎麼辦,我希望我過濾出一些數據,而且我們希望我們是指定條件的過濾
    這裏就涉及到我們的傳遞參數的問題了
    store/index.js

    
      fliter(state,getters){
        console.log(getters)//這裏的getters實際上就是你的整個外面的getters對象 
    
      // 如果你要傳遞參數,你只能返回函數的調用
          return age => {
            state.students.filter( s => s.age >= age )
          }
        }
    
    

    /bmlaoli.vue

    原數據
     <h2>{{ $store.getters.students }}</h2>
    過濾之後
     <h2>{{ $store.getters.fliter(40) }}</h2>
    

    3.mutation

    vuex唯一更新狀態的方式,就是在這裏,如果你要更改數據,vuex唯一的更改方式就是 mutation

    3.1 概念

    事件類型(函數名)
    回調函數(回調函數,具體的業務代碼)

    mutations: {
    //      increment 事件類型
    // (state){ 回調函數
                // state.contuned++
            // },
            increment(state){
                state.contuned++
            },
    
            decrement(state){
                state.contuned--
            },
        },
    

    3.2 傳遞參數payload負載

    1. 單個參數
    2. 多參數(傳遞對象)

    需求:我們希望點擊更改狀態的時候的時候可傳入參數
    /sotre/index.js

    
       mutations: {
            increment(state){
                state.contuned++
            },
            decrement(state){
                state.contuned--
            },
            incrementCour(state,palyload){
                consle.log(palyload)//拿到了一個傳遞過來的對象
            }
        },
    
    

    bmlaoliu.vue3

    addcCount(parmas){
    this.$sore.commit( 'incrementCour' ,palyload)
    }
    

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

    【其他文章推薦】

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

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

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

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

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

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