整合營銷服務商

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

          免費咨詢熱線:

          有狀態部署慢?使用 openkruise 實現容器應

          有狀態部署慢?使用 openkruise 實現容器應用固定 id




          景說明

          我們在業務上容器的過程中遇到了如下問題:

          1. 以 deployment 部署的應用 pod,由于 id 經常變更,服務重啟,監控變得難以維護。這里只是以監控為切入點,事實上,還有諸多應用需要與id強綁定。
          2. statefulset 可以解決上面的問題,但是引入一個新的問題就是 statefulset 本身為了維護有狀態的應用,所有的應用 Pod 啟動是有嚴格的先后順序,也就是串行啟動,對于大規模的應用 pod 來講,啟動消耗時間太長,這是無法忍受的。

          為解決以上問題,我們在容器平臺當中引入了 openkruise。

          openkruise簡介

          項目地址:https://github.com/openkruise/kruise

          詳細的說明可以參考這篇文章:https://yq.aliyun.com/articles/706442

          從當前 github 上的文檔來看,目前 OpenKruise 支持五種改進的控制器:

          • CloneSet: CloneSet is a workload that mainly focuses on managing stateless applications. It provides full features for more efficient, deterministic and controlled deployment, such as inplace update, specified pod deletion, configurable priority/scatter update, preUpdate/postUpdate hooks.
          • Advanced StatefulSet: An enhanced version of default StatefulSet with extra functionalities such as inplace-update, pause and MaxUnavailable.
          • SidecarSet: A controller that injects sidecar containers into the Pod spec based on selectors and also is able to upgrade the sidecar containers.
          • UnitedDeployment: This controller manages application pods spread in multiple fault domains by using multiple workloads.
          • BroadcastJob: A job that runs Pods to completion across all the nodes in the cluster.

          UnitedDeployment 是在 StatefulSet 基礎上的更高級抽象,通過一個資源描述可以管理多個 StatefulSet 的實例組,可實現多實例組的灰度發布與滾動升級。

          Broadcast Job 實際上就是以 DaemonSet 的方式在所有節點上運行一次性 JobSidercarSet 用于 Sidercar 注入及管理

          而我們要使用到的正是其 Advanced StatefulSet 的特性。關于Advanced StatefulSet更詳細的描述如下:

          • 在kubernetes官方的statefulSet上做了功能擴展,更新策略由原來的只支持recreate,擴展為同時支持recreate和rollingupdate。rollingupdate還支持兩種策略,一種是InPlaceIfPossible,另一種是InPlaceOnly。InPlaceIfPossible會盡可能的保證應用在原地升級(只支持鏡像的升級,如果修改了yaml中的其他配置項,則無法保證);InPlaceOnly會保證應用一定在原地升級,但是它也只支持鏡像的升級,如果修改了yaml中的其他配置項,會直接拋出異常。另外,原生的StatefulSet只能做到串行啟動,Advanced StatefulSet可以做到并行啟動。


          部署openkruise

          官方的安裝文檔可以直接參考這里:
          https://github.com/openkruise/kruise/tree/master/docs/tutorial

          我簡單寫下安裝步驟:

          wget https://github.com/openkruise/kruise/releases/download/v0.4.0/kruise-chart.tgz
          tar xf kruise-chart.tgz
          cd kruise
          helm install openkruise ./ -n kube-system

          目前openkruise已經更新到了v0.5.0的版本。也可以直接通過阿里云的應用目錄來完成其安裝。

          下面說一下更詳細的安裝過程:

          1、獲取helm包

          helm repo add incubator  http://aliacs-k8s-cn-beijing.oss-cn-beijing.aliyuncs.com/app/charts-incubator/
          helm search repo ack-kruise
          helm fetch incubator/ack-kruise
          tar xf ack-kruise-0.5.0.tgz
          cd ack-kruise

          修改values.yml文件如下:

          # Default values for kruise.
          
          revisionHistoryLimit: 3
          
          manager:
            # settings for log print
            log:
              # log level for kruise-manager
              level: "4"
          
            # image settings
            image:
              # repository for kruise-manager image
              repository: hub.example.com/library/kruise-manager
              # tag for kruise-manager image
              tag: v0.5.0
          
            # resources of kruise-manager container
            resources:
              limits:
                cpu: 500m
                memory: 1Gi
              requests:
                cpu: 500m
                memory: 1Gi
          
            metrics:
              addr: localhost
              port: 8080
          
            custom_resource_enable: StatefulSet

          其實這里就改了兩個東西:

          • image:默認是docker hub上的地址,我這里改到了私有鏡像倉庫
          • custom_resource_enable:用于指定啟用哪幾種資源,如果不指定的話,openkruise支持的五種資源會全部啟用,我這里只用到了StatefulSet,所以這里只啟用了這一種資源

          然后執行安裝操作:

          helm install ack-kruise -n kube-system ./

          安裝完后,會生成以下五種crd:

          # kubectl get crds |grep kruise
          broadcastjobs.apps.kruise.io            2020-04-26T10:29:28Z
          clonesets.apps.kruise.io                2020-04-26T10:29:28Z
          sidecarsets.apps.kruise.io              2020-04-26T10:29:28Z
          statefulsets.apps.kruise.io             2020-04-26T10:29:28Z
          uniteddeployments.apps.kruise.io        2020-04-26T10:29:28Z

          同時會創建一個 kruise-system 的命名空間,并在里面生成一個 pod:

          # kubectl get pods -n kruise-system
          NAME                          READY   STATUS    RESTARTS   AGE
          kruise-controller-manager-0   1/1     Running   0          55m

          驗證 statefulset 資源的 webhook 是否被正常創建:

          # kubectl get mutatingwebhookconfiguration -o yaml
          
          apiVersion: v1
          items:
          - apiVersion: admissionregistration.k8s.io/v1
            kind: MutatingWebhookConfiguration
            metadata:
              creationTimestamp: "2020-04-26T10:29:28Z"
              generation: 3
              name: kruise-mutating-webhook-configuration
              resourceVersion: "622944921"
              selfLink: /apis/admissionregistration.k8s.io/v1/mutatingwebhookconfigurations/kruise-mutating-webhook-configuration
              uid: 303a7b7f-3a62-49d7-8ef6-082ea288eeb2
            webhooks:
            - admissionReviewVersions:
              - v1beta1
              clientConfig:
                caBundle: xxxxx
                service:
                  name: kruise-webhook-server-service
                  namespace: kruise-system
                  path: /mutating-create-update-statefulset
                  port: 443
              failurePolicy: Fail
              matchPolicy: Exact
              name: mutating-create-update-statefulset.kruise.io
              namespaceSelector:
                matchExpressions:
                - key: control-plane
                  operator: DoesNotExist
              objectSelector: {}
              reinvocationPolicy: Never
              rules:
              - apiGroups:
                - apps.kruise.io
                apiVersions:
                - v1alpha1
                operations:
                - CREATE
                - UPDATE
                resources:
                - statefulsets
                scope: '*'
              sideEffects: Unknown
              timeoutSeconds: 30
          ......

          也是確保其他未用到的相關 mutatingwebhook 是關閉的。。在實際測試中,SidecarSet 資源的 mutatingwebhook 可能會導致創建的 pod 出不來。

          這些webhook本質上都是kubernetes的admissioncontrol,只要你安裝了,哪怕沒有使用,當你在執行相關操作時,都需要被所有的adminssioncontrol檢測,如果admissioncontrol本身出了問題,就會導致請求無法響應的狀態。同時這些webhook類型的adminssioncontrol也會拖慢響應速度。

          用法示例

          下面是官方提供的一個基于 openkruise 提供的 statefulset 資源的部署文件示例:

          apiVersion: apps.kruise.io/v1alpha1
          kind: StatefulSet
          metadata:
            name: demo-v1-guestbook-kruise
            labels:
              app.kubernetes.io/name: guestbook-kruise
              app.kubernetes.io/instance: demo-v1
          spec:
            replicas: 3
            serviceName: demo-v1-guestbook-kruise
            selector:
              matchLabels:
                app.kubernetes.io/name: guestbook-kruise
                app.kubernetes.io/instance: demo-v1
            template:
              metadata:
                labels:
                  app.kubernetes.io/name: guestbook-kruise
                  app.kubernetes.io/instance: demo-v1
              spec:
                readinessGates:
                  # A new condition that ensures the pod remains at NotReady state while the in-place update is happening
                - conditionType: InPlaceUpdateReady
                containers:
                - name: guestbook-kruise
                  image: openkruise/guestbook:v1
                  imagePullPolicy: Always
                  ports:
                  - name: http-server
                    containerPort: 3000
            podManagementPolicy: Parallel  # allow parallel updates, works together with maxUnavailable
            updateStrategy:
              type: RollingUpdate
              rollingUpdate:
                # Do in-place update if possible, currently only image update is supported for in-place update
                podUpdatePolicy: InPlaceIfPossible
                # Allow parallel updates with max number of unavailable instances equals to 2
                maxUnavailable: 3

          執行部署之后,啟動 pod 示例如下:

          # kubectl get pods  |grep demo-v1
          demo-v1-guestbook-kruise-0                                 1/1     Running   0          62s
          demo-v1-guestbook-kruise-1                                 1/1     Running   0          62s
          demo-v1-guestbook-kruise-2                                 1/1     Running   0          62s

          也可通過如下操作查看資源狀態:

          # kubectl get sts.apps.kruise.io
          NAME                       DESIRED   CURRENT   UPDATED   READY   AGE
          demo-v1-guestbook-kruise   3         3         3         3       56s

          openkruise提供的statefulset的資源名為sts.apps.kruise.io

          更詳細的用法可參考:

          • Advanced StatefulSet具體的使用方法:https://github.com/openkruise/kruise/blob/master/docs/concepts/astatefulset/README.md
          • Advanced StatefulSet示例文件:https://github.com/openkruise/kruise/blob/master/docs/tutorial/v1/guestbook-statefulset.yaml
          • UnitedDeployment具體的使用方法:https://github.com/openkruise/kruise/blob/master/docs/tutorial/uniteddeployment.md
          • UnitedDeployment示例文件:https://raw.githubusercontent.com/kruiseio/kruise/master/docs/tutorial/v1/uniteddeployment.yaml

          來源:https://www.cnblogs.com/breezey/p/12925103.html

          篇挺好的文章,可以細品下

          內容摘要

          這一部關于區塊鏈開發及運維的電子書。

          為什么會寫區塊鏈電子書?因為2018年是區塊鏈年。

          這本電子書是否會出版(紙質圖書)? 不會,因為互聯網技術更迭太快,紙質書籍的內容無法實時更新,一本書動輒百元,很快就成為垃圾,你會發現目前市面的上區塊鏈書籍至少是一年前寫的,內容已經過時,很多例子無法正確運行。所以我不會出版,電子書的內容會追逐技術發展,及時跟進軟件版本的升級,做到內容最新,至少是主流。

          這本電子書與其他區塊鏈書籍有什么不同?市面上大部分區塊鏈書籍都是用2/3去講區塊鏈原理,只要不到 1/3 的干貨,干貨不夠理論來湊,通篇將理論或是大談特談區塊鏈行業,這些內容更多是頭腦風暴,展望區塊鏈,均無法落地實施。本書與那些書籍完全不同,不講理論和原理,面向應用落地,注重例子,均是干貨。

          電子書更新頻率?每天都會有新內容加入,更新頻率最遲不會超過一周,更新內容請關注 https://github.com/netkiller/netkiller.github.io/commits/master

          本文采用碎片化寫作,原文會不定期更新,請盡量閱讀原文。

          http://www.netkiller.cn/blockchain/index.html

          您的打賞是我的寫作動力:http://www.netkiller.cn/blockchain/donations.html

          ==============================

          33.2. 食品安全溯源案例

          下面的方案,同樣適合藥品安全溯源

          33.2.1. 背景

          需求是通過區塊鏈跟蹤產品,實現產品產地,生產,流通等環節溯源。

          需求歸納,需要實現下面幾點:

          產品具備通用的屬性,例如名稱,價格,重量,顏色,體積等等

          生產銷售鏈條跟蹤

          涉及環節,農產品的供應鏈是一個非常復雜的過程,涉及多方,農業局、衛生局、藥監局、工商局、環保局等多個部門交織其中。

          參與者角色,我們為每個環節的參與者分配一個以太坊賬號,例如每個供應商一個賬號,每個代理商一個賬號。這樣任何一方經手后都會使用自己的賬號想合約中添加數據。

          33.2.2. 安全問題

          我將安全劃分為六層,分別是:

          +----------+-----------------------------+| 實體層 | 物 |+----------+-----------------------------+| 用戶層 | 人 |+----------+-----------------------------+| 網絡層 | 網絡 |+----------+-----------------------------+| 應用層 | 操作系統,應用服務器 |+----------+-----------------------------+| 業務邏輯層 | 功能,業務邏輯 |+----------+-----------------------------+| 存儲層 | 物理存儲,硬盤 |+----------+-----------------------------+

          并不是實施了區塊鏈技術就安全無憂了,安全分為很多層,區塊鏈只能做到網絡層和存儲層的安全。區塊鏈無法解決用戶層,應用層,邏輯層等安全問題,他只能保證存儲在硬盤上的區塊不被修改。

          因為區塊鏈僅僅能解決數據存儲層的安全問題,不能保證上鏈的數據是真實的,上鏈前絕對不會被篡改;所以僅僅朔源,不考慮防偽是沒有意義的,防偽仍然是重中之重。

          33.2.3. 防偽問題

          如何做防偽呢,這個領域很多公司已經探索多年,各種高科技應用,武裝到牙齒,但仍沒有解決假貨問題。

          區塊鏈的出現很可能是一個突破,我們只需將現有成熟的防偽技術與區塊鏈結合即可。

          現在流行的訪問技術太多了,我傾向于采用二維碼技術,二維碼與互聯網緊密相連。


          33.2.4. 性能問題

          區塊鏈目前的底層只適合做,低頻高價值的業務。

          區塊鏈的讀取性能通常是沒有問題的,但是區塊鏈的寫入實際上無論你用多少個服務器節點都不能提升,因為寫入區塊需要做共識算法,這步操作,會在所有節點上進行,同時還需要加密運算,這些操作都是 CPU 密集型操作。所以寫入操作是存在瓶頸的。

          解決這個問題,我想出了幾種方案:

          性能解決方案

          • 通過消息隊列技術異步寫入,將需要寫入的區塊放入隊列,異步完成上鏈操作。
          • 并行寫入,我們可以建設多個區塊鏈平臺。多個平臺同時服務于業務。


          為了達到去中心化并行寫入,我們將在客戶端通過算法,匹配服務器。而不是在兩個平臺前面增加負載均衡。因為這樣又回到了中心化系統。

          33.2.5. 顆粒度問題

          朔源的顆粒度問題,例如“紅酒”的溯源,我們是將單位溯源做到箱呢?還是打,或是瓶呢?

          我們用“四象限法則”分析

          高價值o || o|低頻率 --------------+------------- 高頻率 操作頻率|o | o |低價值 物品價值

          通過觀察上面圖,我們可以看到可以有四種情況,低頻低價值,低頻高價值,高頻高價值,高頻低價值

          我認為對于低頻高價值和高頻高價值的業務,盡量做到最小顆粒度。

          而對于低頻低價值和高頻低價值的業務,可以顆粒度更粗。


          33.2.6. 存儲規劃

          如果是高頻低價值的業務,那么溯源數據源源將會不斷的被添加到區塊,以此同時區塊的訪問率極低。遲早會達到一個臨界值。

          所以你要規劃存儲,例如溯源數據的過期時間,對于 hyperledger 可以使用 DelState(key) 刪除歷史數據。

          如果是高頻高價值的業務是否要考慮永久保留數據呢?

          這些問題都是需要考慮的。因為目前我們還不知道區塊鏈的存儲臨界值。

          33.2.7. 大數據問題

          區塊鏈替代不了數據庫,它與數據庫是互補關系。

          對于低頻的業務,通常傳統數據庫足以應付。那么對于高頻操作的業務呢?暫時可能沒有問題,但總有一天會遇到瓶頸。

          綜上所述,溯源項目數據庫規劃決不能少。同時還要考慮數據倉庫和后期數據挖掘。因為用戶使用微信或者我們的APP掃描二維碼,我們可以獲得很多有價值的數據。

          手上沒有 Vision 使用文本簡單的繪制了一幅圖

          +------------------------+| User -> QR Code |+------------------------+| | V V+---------------+ +---------------+ +---------------+| Search Engine |<-- | Microservice | | Microservice |+---------------+ +---------------+ +---------------+| |+----------------------------------+ || | | |V V V V+----------+ +------------+ +-------------+ | Database | | Big Data | | Blockchain |+----------+ +------------+ +-------------+| MySQL | | Hadoop | | Hyperledger || NoSQL | | Hive/Hbase | | Chaincode |+----------+ +------------+ +-------------+ | | ^ ^| +------ ETL -----| || |+----------- Message Queue ----------o

          區塊鏈之外的很多復雜的需求我們需要借助大數據系統和搜索技術。

          區塊鏈的弱點是無法做復雜的查詢,這里我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。

          上圖數據寫入時,保存了四份,分別在搜索引擎,關系型數據庫,數據倉庫和區塊的

          具體怎么實現,有很多方式,這里就不討論了,否則就跑題了。

          33.2.8. BI商業智能

          數據采集,大數據分析

          溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。

          我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......

          有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。

          大數據能做什么?

          1. 用戶行為分析,用戶的喜好,這些數據能為后面精準推送提供支持。
          2. 消費與地理分析的關系
          3. 年齡段與購買力的關系
          4. 區域產品的存量,例如:用戶掃描了一次二維碼,可能用戶就已經使用了改產品。我們就知道該地區投放的1000件商品被消耗了意見。
          5. 性別與消費習慣
          6. 兩次間隔消費時間
          7. 活躍用戶和沉睡用戶


          33.2.9. 采集終端

          溯源數據怎么錄入呢?例如我們開發一個設備,二維碼掃描槍,內置安卓系統。

          我們不清楚他們的教育背景以及學習能力,所以設計原則是盡量傻瓜化,降低數據錄入難度和學習難度,終端開機后互動教學,走一遍流程即可上手。

          首先將溯源環節的每個節點通過后臺事先寫入數據庫,接下來通過GIS地理信息系統匹配。

          UUID -> 二維碼 -> 設備掃描二維碼激活-> 入數據庫 -> 異步消息隊列 -> 上鏈 > ---+^ || |+------------------- 追加數據 -----------------+

          終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。

          對于圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......

          33.2.10. 多媒體數據

          這里我們需要考慮是否需要記錄多媒體數據,這里的多媒體指圖像,聲音,甚至3D掃描數據等等......

          對于圖片、音頻與視頻,我們可以將它集成到采集終端上,然后異步上傳到去中心化的分布式文件系統中。

          去中心化的分布式文件系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。

          33.2.11. 物流接口

          使用物流單好通過物流公司提供的借口獲得物流數據,然后寫入到區塊。

          33.2.12. 如何激勵用戶

          防偽技術做了,區塊鏈溯源也做了,那么對于用戶來說,他可能懶得去掃你的二維碼,怎么辦呢?

          這里需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。

          首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經泛濫 99% 可能是空氣幣,這里我們需要將代幣的價值與物品對價,類似金本位/銀本位。

          怎樣操作呢?例如一個代幣等于一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...

          關于怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到

          根據業務需要,可以發行布置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。

          由于國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。

          另外掃描二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為后面大數據分析埋點。

          用戶激勵手段

          1. 分享激勵
          2. 好評激勵
          3. 用戶等級激勵
          4. 代幣激勵
          5. 用戶排名,PK排行榜
          6. 成就勛章
          7. 身份標簽,黃馬甲:)


          等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......

          33.2.13. 上鏈

          并不是所有數據都上鏈,哪些數據上鏈呢?

          產地(出生、生長)、采購、加工(檢疫、屠宰)、庫存、運輸、銷售、配送等等......

          33.2.14. 以太坊解決方案

          我們設計一個簡單的合約,模擬上面提到的解決方案

          pragma solidity ^0.4.20;contract Trace {enum State { Origin, Factory, QA, Shipping, Received, Pending }string name;uint price;uint weight;bool lock=false; //合約鎖bool close=false; //合約狀態uint number=1;uint attr_number=1;mapping (address=> string) guestbook; //客戶留言本 struct Attribute {address owner; // 供應商string name; // 屬性的名字string date; // 生產日期string desc; // 描述信息}mapping (uint=> Attribute) attribute;struct Logistics {address owner; // 中轉站string date; // 轉運日期State status; // 狀態string message; // 留言信息}mapping (uint=> Logistics) stations;function Trace(string _name, uint _price, uint _weight) public {name=_name;price=_price;weight=_weight;}// 名稱function getName() public view returns(string){return name;}// 價格function getPrice() public view returns(uint){return price;}// 重量function getWeight() public view returns(uint){return weight;}// 增加商品屬性function putAttribute(address _owner,string _name, string _date, string _desc ) public{if(lock==false){Attribute memory item=Attribute(_owner, _name,_date,_desc);attribute[attr_number]=item;attr_number=attr_number + 1;}}// 獲得屬性function getAttribute(uint _attr_number) public view returns(address, string, string, string) {require(_attr_number < attr_number);Attribute memory item=attribute[_attr_number];return (item.owner, item.name, item.date, item.desc);}// 增加物流中轉信息function putLogistics(address _owner,string _date, State _status, string _message ) public{if(close==false){Logistics memory node=Logistics(_owner,_date,_status,_message);stations[number]=node;number=number + 1;lock=true;}if (_status==State.Received) {close=true;}}// 獲得中轉信息function getLogistics(uint _number) public view returns(address, string, State, string) {require(_number < number);Logistics memory node=stations[_number];return (node.owner, node.date, node.status, node.message);}// 或者轉中站數量function getLogisticsCount() public view returns(uint){return number;}// 客戶留言function addGuestbook(address _owner, string message) public{guestbook[_owner]=message;}}

          怎樣使用這個合約呢?合約部署,需要輸入三個參數,分別是名稱,價格和裝量

          Trace(string _name, uint _price, uint _weight)

          產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。

          33.2.14.1. 應用場景一

          調用合約案例一,這是沒有經過深加工的原產品案例。例如 Trace("山羊肉", 25, 50)

          var contract;Trace.deployed().then(function(instance){contract=instance;});contract.getName();contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","顏色", "", "黑色")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","產地", "", "內蒙古")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","出生", "2017-01-12", "XXX牧場")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","宰殺", "2018-02-12", "XXX宰殺")contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX屠宰公司");contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX檢驗檢疫");contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");contract.getNode(); // 獲得物流經過的轉運站數量

          33.2.14.2. 應用場景二

          調用合約案例二,這是深加工的產品案例。例如 Trace("牦牛肉干", 80, 500)

          var contract;Trace.deployed().then(function(instance){contract=instance;});contract.getName();contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","調和油", "2016-10-10", "銀龍魚牌")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","辣椒粉", "2016-10-30", "西藏XXX公司生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","生抽", "2016-01-12", "XXX生抽,XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","山梨酸鉀", "2017-02-12", "XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","防腐劑", "2017-02-12", "XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","牦牛肉", "2017-02-12", "XXX牧場")contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX公司生產");contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX通過QA、QC");contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");contract.getNode(); // 獲得物流經過的轉運站數量

          33.2.14.3. 用戶留言

          contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","東西好吃,下次還買,給好評");

          33.2.15. Hyperledger 解決方案

          由于家里在刷墻,服務器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。

          33.2.15.1. 溯源合約涉及

          package mainimport "fmt"import "encoding/json"const (Origin=iota // 0Factory // 1QA // 2Shipping // 3Received // 4Pending // 5Supermarket // 6)type structElement struct {Name string `json:"name"`Company string `json:"company"`Description string `json:"description"`}type structLogistics struct {Stations string `json:"stations"` // 中轉站Date string `json:"date"` // 轉運日期Status uint8 `json:"status"` // 狀態Message string `json:"message"` // 留言信息}type Trace struct {Name string `json:"name"`Address string `json:"address"`Attribute map[string]string `json:"attribute"`Element []structElement `json:"element"`Logistics map[string]structLogistics `json:"logistics"`}func (trace *Trace) setName(_name string) {trace.Name=_name}func (trace *Trace) getName() string {return trace.Name}func (trace *Trace) putAttribute(_key string, _value string) {trace.Attribute[_key]=_value}func (trace *Trace) putLogistics(_key string, _value structLogistics) {trace.Logistics[_key]=_value}func main(){trace :=&Trace{Name: "牦牛肉干",Address: "內蒙古呼和浩特",Attribute: map[string]string{},Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料制品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "采摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "牦牛收購"})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "牦牛宰殺"})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})traceJson, _ :=json.Marshal(trace)fmt.Println(string(traceJson))}

          33.2.15.1.1. 食品安全朔源

          trace :=&Trace{Name: "牦牛肉干",Address: "內蒙古呼和浩特",Attribute: map[string]string{},Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料制品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "采摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "牦牛收購"})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "牦牛宰殺"})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})

          33.2.15.1.2. 水平移植

          這個方案可以水平移植到其他領域,例如 藥品安全溯源

          trace :=&Trace{Name: "強身大力丸",Address: "深圳是XXX制藥有限公司",Attribute: map[string]string{},Element: []structElement{structElement{Name:"枸杞",Company: "寧夏XXX農業有限公司", Description: "采摘年份2016-10-10,10g"},structElement{Name:"茯苓",Company: "河南XXX農業有限公司", Description: "采摘年份2016-10-10,20kg"},structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},......structElement{Name:"塑料包裝",Company: "XXX有限公司", Description: "生產日期2016-10-10"},structElement{Name:"包裝盒",Company: "XXX有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")......trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "原材料...."})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "生產...."})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("3", structLogistics{"XXX市藥品監督局","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})

          合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。

          33.2.15.2. 積分通正(代幣)

          我發現用以太坊思維,將以太坊代幣合約搬到 hyperledger 上,一樣可以實現代幣的功能,這個代幣除了不能上交易所,基本滿足我們替代積分系統的需求,下面是我寫了這樣一個合約,在超級賬本上實現類似以太坊的代幣轉賬功能。

          package mainimport ("bytes""encoding/json""fmt""strconv""http://github.com/hyperledger/fabric/core/chaincode/shim"sc "http://github.com/hyperledger/fabric/protos/peer")// Define the Smart Contract structuretype SmartContract struct {}type Token struct {Owner string `json:"Owner"`TotalSupply uint `json:"TotalSupply"`TokenName string `json:"TokenName"`TokenSymbol string `json:"TokenSymbol"`BalanceOf map[string]uint `json:"BalanceOf"`}func (token *Token) initialSupply(){token.BalanceOf[token.Owner]=token.TotalSupply;}func (token *Token) transfer (_from string, _to string, _value uint){if(token.BalanceOf[_from] >=_value){token.BalanceOf[_from] -=_value;token.BalanceOf[_to] +=_value;}}func (token *Token) balance (_from string) uint{return token.BalanceOf[_from]}func (token *Token) burn(_value uint) {if(token.BalanceOf[token.Owner] >=_value){token.BalanceOf[token.Owner] -=_value;token.TotalSupply -=_value;}}func (token *Token) burnFrom(_from string, _value uint) {if(token.BalanceOf[_from] >=_value){token.BalanceOf[_from] -=_value;token.TotalSupply -=_value;}}func (token *Token) mint(_value uint) {token.BalanceOf[token.Owner] +=_value;token.TotalSupply +=_value;}func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response {return shim.Success(nil)}func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response {token :=&Token{Owner: "netkiller",TotalSupply: 10000,TokenName: "代幣通正",TokenSymbol: "COIN",BalanceOf: map[string]uint{}}token.initialSupply()tokenAsBytes, _ :=json.Marshal(token)stub.PutState("Token", tokenAsBytes)fmt.Println("Added", tokenAsBytes)return shim.Success(nil)}func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {if len(args) !=3 {return shim.Error("Incorrect number of arguments. Expecting 2")}tokenAsBytes, _ :=stub.GetState(args[0])token :=Token{}json.Unmarshal(tokenAsBytes, &token)token.transfer(args[1],args[2],args[3])tokenAsBytes, _=json.Marshal(token)stub.PutState(args[0], tokenAsBytes)return shim.Success(nil)}func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {if len(args) !=1 {return shim.Error("Incorrect number of arguments. Expecting 1")}tokenAsBytes, _ :=stub.GetState(args[0])token :=Token{}json.Unmarshal(tokenAsBytes, &token)amount :=token.balance(args[1])return shim.Success(amount)}func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response {// Retrieve the requested Smart Contract function and argumentsfunction, args :=stub.GetFunctionAndParameters()// Route to the appropriate handler function to interact with the ledger appropriatelyif function=="balanceToken" {return s.balanceToken(stub, args)} else if function=="initLedger" {return s.initLedger(stub)} else if function=="transferToken" {return s.transferToken(stub, args)}return shim.Error("Invalid Smart Contract function name.")}// The main function is only relevant in unit test mode. Only included here for completeness.func main() {// Create a new Smart Contracterr :=shim.Start(new(SmartContract))if err !=nil {fmt.Printf("Error creating new Smart Contract: %s", err)}}

          合約代碼的測試

          func main(){token :=&Token{Owner: "netkiller", // 代幣管理者TotalSupply: 10000, // 代幣發行總量TokenName: "積分連", // 代幣名稱TokenSymbol: "NEO", // 代幣符號 NEOBalanceOf: map[string]uint{}}token.initialSupply() // 初始化代幣fmt.Println(token.balance("netkiller")) // 查詢余額token.transfer("netkiller","neo", 100) // 轉賬,這里賬號使用用戶ID,沒有使用以太坊錢包那樣的哈希值,因為哈希值不便于記憶。fmt.Println(token.balance("netkiller"))fmt.Println(token.balance("neo"))}

          我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...

          開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。

          篇挺好的文章,可以細品下

          內容摘要

          這一部關于區塊鏈開發及運維的電子書。

          為什么會寫區塊鏈電子書?因為2018年是區塊鏈年。

          這本電子書是否會出版(紙質圖書)? 不會,因為互聯網技術更迭太快,紙質書籍的內容無法實時更新,一本書動輒百元,很快就成為垃圾,你會發現目前市面的上區塊鏈書籍至少是一年前寫的,內容已經過時,很多例子無法正確運行。所以我不會出版,電子書的內容會追逐技術發展,及時跟進軟件版本的升級,做到內容最新,至少是主流。

          這本電子書與其他區塊鏈書籍有什么不同?市面上大部分區塊鏈書籍都是用2/3去講區塊鏈原理,只要不到 1/3 的干貨,干貨不夠理論來湊,通篇將理論或是大談特談區塊鏈行業,這些內容更多是頭腦風暴,展望區塊鏈,均無法落地實施。本書與那些書籍完全不同,不講理論和原理,面向應用落地,注重例子,均是干貨。

          電子書更新頻率?每天都會有新內容加入,更新頻率最遲不會超過一周,更新內容請關注 https://github.com/netkiller/netkiller.github.io/commits/master

          本文采用碎片化寫作,原文會不定期更新,請盡量閱讀原文。

          http://www.netkiller.cn/blockchain/index.html

          您的打賞是我的寫作動力:http://www.netkiller.cn/blockchain/donations.html

          ==============================

          33.2. 食品安全溯源案例

          下面的方案,同樣適合藥品安全溯源

          33.2.1. 背景

          需求是通過區塊鏈跟蹤產品,實現產品產地,生產,流通等環節溯源。

          需求歸納,需要實現下面幾點:

          產品具備通用的屬性,例如名稱,價格,重量,顏色,體積等等

          生產銷售鏈條跟蹤

          涉及環節,農產品的供應鏈是一個非常復雜的過程,涉及多方,農業局、衛生局、藥監局、工商局、環保局等多個部門交織其中。

          參與者角色,我們為每個環節的參與者分配一個以太坊賬號,例如每個供應商一個賬號,每個代理商一個賬號。這樣任何一方經手后都會使用自己的賬號想合約中添加數據。

          33.2.2. 安全問題

          我將安全劃分為六層,分別是:

          +----------+-----------------------------+| 實體層 | 物 |+----------+-----------------------------+| 用戶層 | 人 |+----------+-----------------------------+| 網絡層 | 網絡 |+----------+-----------------------------+| 應用層 | 操作系統,應用服務器 |+----------+-----------------------------+| 業務邏輯層 | 功能,業務邏輯 |+----------+-----------------------------+| 存儲層 | 物理存儲,硬盤 |+----------+-----------------------------+

          并不是實施了區塊鏈技術就安全無憂了,安全分為很多層,區塊鏈只能做到網絡層和存儲層的安全。區塊鏈無法解決用戶層,應用層,邏輯層等安全問題,他只能保證存儲在硬盤上的區塊不被修改。

          因為區塊鏈僅僅能解決數據存儲層的安全問題,不能保證上鏈的數據是真實的,上鏈前絕對不會被篡改;所以僅僅朔源,不考慮防偽是沒有意義的,防偽仍然是重中之重。

          33.2.3. 防偽問題

          如何做防偽呢,這個領域很多公司已經探索多年,各種高科技應用,武裝到牙齒,但仍沒有解決假貨問題。

          區塊鏈的出現很可能是一個突破,我們只需將現有成熟的防偽技術與區塊鏈結合即可。

          現在流行的訪問技術太多了,我傾向于采用二維碼技術,二維碼與互聯網緊密相連。


          33.2.4. 性能問題

          區塊鏈目前的底層只適合做,低頻高價值的業務。

          區塊鏈的讀取性能通常是沒有問題的,但是區塊鏈的寫入實際上無論你用多少個服務器節點都不能提升,因為寫入區塊需要做共識算法,這步操作,會在所有節點上進行,同時還需要加密運算,這些操作都是 CPU 密集型操作。所以寫入操作是存在瓶頸的。

          解決這個問題,我想出了幾種方案:

          性能解決方案

          • 通過消息隊列技術異步寫入,將需要寫入的區塊放入隊列,異步完成上鏈操作。
          • 并行寫入,我們可以建設多個區塊鏈平臺。多個平臺同時服務于業務。


          為了達到去中心化并行寫入,我們將在客戶端通過算法,匹配服務器。而不是在兩個平臺前面增加負載均衡。因為這樣又回到了中心化系統。

          33.2.5. 顆粒度問題

          朔源的顆粒度問題,例如“紅酒”的溯源,我們是將單位溯源做到箱呢?還是打,或是瓶呢?

          我們用“四象限法則”分析

          高價值o || o|低頻率 --------------+------------- 高頻率 操作頻率|o | o |低價值 物品價值

          通過觀察上面圖,我們可以看到可以有四種情況,低頻低價值,低頻高價值,高頻高價值,高頻低價值

          我認為對于低頻高價值和高頻高價值的業務,盡量做到最小顆粒度。

          而對于低頻低價值和高頻低價值的業務,可以顆粒度更粗。


          33.2.6. 存儲規劃

          如果是高頻低價值的業務,那么溯源數據源源將會不斷的被添加到區塊,以此同時區塊的訪問率極低。遲早會達到一個臨界值。

          所以你要規劃存儲,例如溯源數據的過期時間,對于 hyperledger 可以使用 DelState(key) 刪除歷史數據。

          如果是高頻高價值的業務是否要考慮永久保留數據呢?

          這些問題都是需要考慮的。因為目前我們還不知道區塊鏈的存儲臨界值。

          33.2.7. 大數據問題

          區塊鏈替代不了數據庫,它與數據庫是互補關系。

          對于低頻的業務,通常傳統數據庫足以應付。那么對于高頻操作的業務呢?暫時可能沒有問題,但總有一天會遇到瓶頸。

          綜上所述,溯源項目數據庫規劃決不能少。同時還要考慮數據倉庫和后期數據挖掘。因為用戶使用微信或者我們的APP掃描二維碼,我們可以獲得很多有價值的數據。

          手上沒有 Vision 使用文本簡單的繪制了一幅圖

          +------------------------+| User -> QR Code |+------------------------+| | V V+---------------+ +---------------+ +---------------+| Search Engine |<-- | Microservice | | Microservice |+---------------+ +---------------+ +---------------+| |+----------------------------------+ || | | |V V V V+----------+ +------------+ +-------------+ | Database | | Big Data | | Blockchain |+----------+ +------------+ +-------------+| MySQL | | Hadoop | | Hyperledger || NoSQL | | Hive/Hbase | | Chaincode |+----------+ +------------+ +-------------+ | | ^ ^| +------ ETL -----| || |+----------- Message Queue ----------o

          區塊鏈之外的很多復雜的需求我們需要借助大數據系統和搜索技術。

          區塊鏈的弱點是無法做復雜的查詢,這里我們會用到搜索引擎技術解決,實際上搜索引擎角色是給區塊鏈做索引。

          上圖數據寫入時,保存了四份,分別在搜索引擎,關系型數據庫,數據倉庫和區塊的

          具體怎么實現,有很多方式,這里就不討論了,否則就跑題了。

          33.2.8. BI商業智能

          數據采集,大數據分析

          溯源信息的查詢是通過用戶手機終端實現,有幾種途徑,微信掃二維碼,APP掃二維碼,微信小程序等等。

          我們可以收集到很多有價值的數據,例如地理位置,手機號碼,性別,年齡等等......

          有了這些數據便可以挖掘出有價值的數據,甚至可以將數據提供給生產企業作參考。

          大數據能做什么?

          1. 用戶行為分析,用戶的喜好,這些數據能為后面精準推送提供支持。
          2. 消費與地理分析的關系
          3. 年齡段與購買力的關系
          4. 區域產品的存量,例如:用戶掃描了一次二維碼,可能用戶就已經使用了改產品。我們就知道該地區投放的1000件商品被消耗了意見。
          5. 性別與消費習慣
          6. 兩次間隔消費時間
          7. 活躍用戶和沉睡用戶


          33.2.9. 采集終端

          溯源數據怎么錄入呢?例如我們開發一個設備,二維碼掃描槍,內置安卓系統。

          我們不清楚他們的教育背景以及學習能力,所以設計原則是盡量傻瓜化,降低數據錄入難度和學習難度,終端開機后互動教學,走一遍流程即可上手。

          首先將溯源環節的每個節點通過后臺事先寫入數據庫,接下來通過GIS地理信息系統匹配。

          UUID -> 二維碼 -> 設備掃描二維碼激活-> 入數據庫 -> 異步消息隊列 -> 上鏈 > ---+^ || |+------------------- 追加數據 -----------------+

          終端會幫助用戶欲錄入信息,用戶可以在信息基礎上修改或者重寫。同時終端支持圖片,圖像記錄上傳。

          對于圖片還能實現 EXIF 數據保存,包括圖片描述信息,地理信息等等......

          33.2.10. 多媒體數據

          這里我們需要考慮是否需要記錄多媒體數據,這里的多媒體指圖像,聲音,甚至3D掃描數據等等......

          對于圖片、音頻與視頻,我們可以將它集成到采集終端上,然后異步上傳到去中心化的分布式文件系統中。

          去中心化的分布式文件系統能實現,一張圖片一個hash值,通過hash值訪問圖片,圖片被同步到相鄰節點實現去中心化,圖片被修改hash值隨之變化數據便無效。

          33.2.11. 物流接口

          使用物流單好通過物流公司提供的借口獲得物流數據,然后寫入到區塊。

          33.2.12. 如何激勵用戶

          防偽技術做了,區塊鏈溯源也做了,那么對于用戶來說,他可能懶得去掃你的二維碼,怎么辦呢?

          這里需要激勵用戶,怎樣激勵用戶,我的方案是送代幣。

          首先代幣不僅能夠購買物品,還能交易,流通,形成一個小的商業閉環。其次目前代幣已經泛濫 99% 可能是空氣幣,這里我們需要將代幣的價值與物品對價,類似金本位/銀本位。

          怎樣操作呢?例如一個代幣等于一斤水果,無論代幣怎樣炒作,最終用戶不想玩下去了,就來換水果,也可以是大米,食用油等等...

          關于怎樣使用代幣來做積分系統請參考我的另一篇文章 《使用代幣替代傳統積分系統》 ,你可以在搜索引擎中找到

          根據業務需要,可以發行布置一套幣,例如水果幣,流量幣,話費幣,每種幣的功能不同,這些幣可以在交易所中撮合交易,例如賣出水果幣,換成流量幣等等。

          由于國家的法規問題,代幣系統設計原則一定是代幣只能用來購買商城中的物品,不能直接兌換成RMB,否則會觸碰到國家的紅線。但是通過交易所,幣幣之間兌換我們就控制不了了。

          另外掃描二維碼顯示溯源防偽信息的同時我們有很多可以操作空間,可以獲取用戶地理位置,手機號碼等等信息,為后面大數據分析埋點。

          用戶激勵手段

          1. 分享激勵
          2. 好評激勵
          3. 用戶等級激勵
          4. 代幣激勵
          5. 用戶排名,PK排行榜
          6. 成就勛章
          7. 身份標簽,黃馬甲:)


          等等,手段眾多,目的是讓用戶查詢溯源信息,手機用戶數據,鼓勵代幣消費等等.......

          33.2.13. 上鏈

          并不是所有數據都上鏈,哪些數據上鏈呢?

          產地(出生、生長)、采購、加工(檢疫、屠宰)、庫存、運輸、銷售、配送等等......

          33.2.14. 以太坊解決方案

          我們設計一個簡單的合約,模擬上面提到的解決方案

          pragma solidity ^0.4.20;contract Trace {enum State { Origin, Factory, QA, Shipping, Received, Pending }string name;uint price;uint weight;bool lock=false; //合約鎖bool close=false; //合約狀態uint number=1;uint attr_number=1;mapping (address=> string) guestbook; //客戶留言本 struct Attribute {address owner; // 供應商string name; // 屬性的名字string date; // 生產日期string desc; // 描述信息}mapping (uint=> Attribute) attribute;struct Logistics {address owner; // 中轉站string date; // 轉運日期State status; // 狀態string message; // 留言信息}mapping (uint=> Logistics) stations;function Trace(string _name, uint _price, uint _weight) public {name=_name;price=_price;weight=_weight;}// 名稱function getName() public view returns(string){return name;}// 價格function getPrice() public view returns(uint){return price;}// 重量function getWeight() public view returns(uint){return weight;}// 增加商品屬性function putAttribute(address _owner,string _name, string _date, string _desc ) public{if(lock==false){Attribute memory item=Attribute(_owner, _name,_date,_desc);attribute[attr_number]=item;attr_number=attr_number + 1;}}// 獲得屬性function getAttribute(uint _attr_number) public view returns(address, string, string, string) {require(_attr_number < attr_number);Attribute memory item=attribute[_attr_number];return (item.owner, item.name, item.date, item.desc);}// 增加物流中轉信息function putLogistics(address _owner,string _date, State _status, string _message ) public{if(close==false){Logistics memory node=Logistics(_owner,_date,_status,_message);stations[number]=node;number=number + 1;lock=true;}if (_status==State.Received) {close=true;}}// 獲得中轉信息function getLogistics(uint _number) public view returns(address, string, State, string) {require(_number < number);Logistics memory node=stations[_number];return (node.owner, node.date, node.status, node.message);}// 或者轉中站數量function getLogisticsCount() public view returns(uint){return number;}// 客戶留言function addGuestbook(address _owner, string message) public{guestbook[_owner]=message;}}

          怎樣使用這個合約呢?合約部署,需要輸入三個參數,分別是名稱,價格和裝量

          Trace(string _name, uint _price, uint _weight)

          產品屬性可以在出廠前設置,一旦出廠進入物流階段就不允許在更改了。

          33.2.14.1. 應用場景一

          調用合約案例一,這是沒有經過深加工的原產品案例。例如 Trace("山羊肉", 25, 50)

          var contract;Trace.deployed().then(function(instance){contract=instance;});contract.getName();contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","顏色", "", "黑色")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","產地", "", "內蒙古")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","出生", "2017-01-12", "XXX牧場")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","宰殺", "2018-02-12", "XXX宰殺")contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX屠宰公司");contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX檢驗檢疫");contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");contract.getNode(); // 獲得物流經過的轉運站數量

          33.2.14.2. 應用場景二

          調用合約案例二,這是深加工的產品案例。例如 Trace("牦牛肉干", 80, 500)

          var contract;Trace.deployed().then(function(instance){contract=instance;});contract.getName();contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","調和油", "2016-10-10", "銀龍魚牌")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","辣椒粉", "2016-10-30", "西藏XXX公司生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","生抽", "2016-01-12", "XXX生抽,XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","山梨酸鉀", "2017-02-12", "XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","防腐劑", "2017-02-12", "XXX生產")contract.putAttribute("0x627306090abab3a6e1400e9345bc60c78a8bef57","牦牛肉", "2017-02-12", "XXX牧場")contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",0,"XXX牧場");contract.putLogistics("0x627306090abab3a6e1400e9345bc60c78a8bef57","2018-02-20",1,"XXX公司生產");contract.putLogistics("0xc5fdf4076b8f3a5357c5e395ab970b5b54098fef","2018-02-22",2,"XXX通過QA、QC");contract.putLogistics("0xf17f52151ebef6c7334fad080c5704d77216b732","2018-02-21",3,"XXX一級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-23",3,"XXX二級經銷商");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-24",3,"XXX批發中心");contract.putLogistics("0x821aea9a577a9b44299b9c15c88cf3087f3b5544","2018-02-25",3,"XXX超市");contract.putLogistics("0x0d1d4e623d10f9fba5db95830f7d3839406c6af2","2018-02-26",4,"用戶包裹收到");contract.getNode(); // 獲得物流經過的轉運站數量

          33.2.14.3. 用戶留言

          contract.addGuestbook("0x0d1d423e623d10f9d10f9d10f9d10f9d10f9fba5","東西好吃,下次還買,給好評");

          33.2.15. Hyperledger 解決方案

          由于家里在刷墻,服務器收起來了,沒有開發環境,只能提供部分參考代碼,無法提供合約完整代碼,只是給大家一個思路,原理很上面以太坊的合約類似。

          33.2.15.1. 溯源合約涉及

          package mainimport "fmt"import "encoding/json"const (Origin=iota // 0Factory // 1QA // 2Shipping // 3Received // 4Pending // 5Supermarket // 6)type structElement struct {Name string `json:"name"`Company string `json:"company"`Description string `json:"description"`}type structLogistics struct {Stations string `json:"stations"` // 中轉站Date string `json:"date"` // 轉運日期Status uint8 `json:"status"` // 狀態Message string `json:"message"` // 留言信息}type Trace struct {Name string `json:"name"`Address string `json:"address"`Attribute map[string]string `json:"attribute"`Element []structElement `json:"element"`Logistics map[string]structLogistics `json:"logistics"`}func (trace *Trace) setName(_name string) {trace.Name=_name}func (trace *Trace) getName() string {return trace.Name}func (trace *Trace) putAttribute(_key string, _value string) {trace.Attribute[_key]=_value}func (trace *Trace) putLogistics(_key string, _value structLogistics) {trace.Logistics[_key]=_value}func main(){trace :=&Trace{Name: "牦牛肉干",Address: "內蒙古呼和浩特",Attribute: map[string]string{},Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料制品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "采摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "牦牛收購"})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "牦牛宰殺"})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})traceJson, _ :=json.Marshal(trace)fmt.Println(string(traceJson))}

          33.2.15.1.1. 食品安全朔源

          trace :=&Trace{Name: "牦牛肉干",Address: "內蒙古呼和浩特",Attribute: map[string]string{},Element: []structElement{structElement{Name:"塑料袋",Company: "XXX塑料制品有限公司", Description: "外包裝"},structElement{Name:"辣椒粉",Company: "XXX調味品有限公司", Description: "采摘年份2016-10-10"},structElement{Name:"調和油",Company: "XXX調味品有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "牦牛收購"})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "牦牛宰殺"})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})

          33.2.15.1.2. 水平移植

          這個方案可以水平移植到其他領域,例如 藥品安全溯源

          trace :=&Trace{Name: "強身大力丸",Address: "深圳是XXX制藥有限公司",Attribute: map[string]string{},Element: []structElement{structElement{Name:"枸杞",Company: "寧夏XXX農業有限公司", Description: "采摘年份2016-10-10,10g"},structElement{Name:"茯苓",Company: "河南XXX農業有限公司", Description: "采摘年份2016-10-10,20kg"},structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},structElement{Name:"XXX",Company: "XXX有限公司", Description: "生產日期2016-10-10"},......structElement{Name:"塑料包裝",Company: "XXX有限公司", Description: "生產日期2016-10-10"},structElement{Name:"包裝盒",Company: "XXX有限公司", Description: "生產日期2016-10-10"}},Logistics: map[string]structLogistics{}}trace.putAttribute("Color","Red")trace.putAttribute("Size","10")......trace.putAttribute("Weight","100kg")trace.putLogistics("1", structLogistics{"呼和浩特","2016-10-15", Origin, "原材料...."})trace.putLogistics("2", structLogistics{"呼和浩特","2016-10-18", Factory, "生產...."})trace.putLogistics("3", structLogistics{"呼和浩特","2016-10-15", QA, "經過質檢"})trace.putLogistics("3", structLogistics{"XXX市藥品監督局","2016-10-15", QA, "經過質檢"})trace.putLogistics("4", structLogistics{"北京市","2016-10-15", Shipping, "運輸中"})trace.putLogistics("5", structLogistics{"杭州市","2016-10-15", Shipping, "XXX冷庫"})trace.putLogistics("5", structLogistics{"深圳市","2016-10-15", Supermarket, "XXX超市"})trace.putLogistics("5", structLogistics{"龍華區","2016-10-15", Received, "用戶簽收"})

          合約落地,還需要做一些調整已適應實際場景。但基本思路是通的。

          33.2.15.2. 積分通正(代幣)

          我發現用以太坊思維,將以太坊代幣合約搬到 hyperledger 上,一樣可以實現代幣的功能,這個代幣除了不能上交易所,基本滿足我們替代積分系統的需求,下面是我寫了這樣一個合約,在超級賬本上實現類似以太坊的代幣轉賬功能。

          package mainimport ("bytes""encoding/json""fmt""strconv""http://github.com/hyperledger/fabric/core/chaincode/shim"sc "http://github.com/hyperledger/fabric/protos/peer")// Define the Smart Contract structuretype SmartContract struct {}type Token struct {Owner string `json:"Owner"`TotalSupply uint `json:"TotalSupply"`TokenName string `json:"TokenName"`TokenSymbol string `json:"TokenSymbol"`BalanceOf map[string]uint `json:"BalanceOf"`}func (token *Token) initialSupply(){token.BalanceOf[token.Owner]=token.TotalSupply;}func (token *Token) transfer (_from string, _to string, _value uint){if(token.BalanceOf[_from] >=_value){token.BalanceOf[_from] -=_value;token.BalanceOf[_to] +=_value;}}func (token *Token) balance (_from string) uint{return token.BalanceOf[_from]}func (token *Token) burn(_value uint) {if(token.BalanceOf[token.Owner] >=_value){token.BalanceOf[token.Owner] -=_value;token.TotalSupply -=_value;}}func (token *Token) burnFrom(_from string, _value uint) {if(token.BalanceOf[_from] >=_value){token.BalanceOf[_from] -=_value;token.TotalSupply -=_value;}}func (token *Token) mint(_value uint) {token.BalanceOf[token.Owner] +=_value;token.TotalSupply +=_value;}func (s *SmartContract) Init(stub shim.ChaincodeStubInterface) sc.Response {return shim.Success(nil)}func (s *SmartContract) initLedger(stub shim.ChaincodeStubInterface) sc.Response {token :=&Token{Owner: "netkiller",TotalSupply: 10000,TokenName: "代幣通正",TokenSymbol: "COIN",BalanceOf: map[string]uint{}}token.initialSupply()tokenAsBytes, _ :=json.Marshal(token)stub.PutState("Token", tokenAsBytes)fmt.Println("Added", tokenAsBytes)return shim.Success(nil)}func (s *SmartContract) transferToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {if len(args) !=3 {return shim.Error("Incorrect number of arguments. Expecting 2")}tokenAsBytes, _ :=stub.GetState(args[0])token :=Token{}json.Unmarshal(tokenAsBytes, &token)token.transfer(args[1],args[2],args[3])tokenAsBytes, _=json.Marshal(token)stub.PutState(args[0], tokenAsBytes)return shim.Success(nil)}func (s *SmartContract) balanceToken(stub shim.ChaincodeStubInterface, args []string) sc.Response {if len(args) !=1 {return shim.Error("Incorrect number of arguments. Expecting 1")}tokenAsBytes, _ :=stub.GetState(args[0])token :=Token{}json.Unmarshal(tokenAsBytes, &token)amount :=token.balance(args[1])return shim.Success(amount)}func (s *SmartContract) Invoke(stub shim.ChaincodeStubInterface) sc.Response {// Retrieve the requested Smart Contract function and argumentsfunction, args :=stub.GetFunctionAndParameters()// Route to the appropriate handler function to interact with the ledger appropriatelyif function=="balanceToken" {return s.balanceToken(stub, args)} else if function=="initLedger" {return s.initLedger(stub)} else if function=="transferToken" {return s.transferToken(stub, args)}return shim.Error("Invalid Smart Contract function name.")}// The main function is only relevant in unit test mode. Only included here for completeness.func main() {// Create a new Smart Contracterr :=shim.Start(new(SmartContract))if err !=nil {fmt.Printf("Error creating new Smart Contract: %s", err)}}

          合約代碼的測試

          func main(){token :=&Token{Owner: "netkiller", // 代幣管理者TotalSupply: 10000, // 代幣發行總量TokenName: "積分連", // 代幣名稱TokenSymbol: "NEO", // 代幣符號 NEOBalanceOf: map[string]uint{}}token.initialSupply() // 初始化代幣fmt.Println(token.balance("netkiller")) // 查詢余額token.transfer("netkiller","neo", 100) // 轉賬,這里賬號使用用戶ID,沒有使用以太坊錢包那樣的哈希值,因為哈希值不便于記憶。fmt.Println(token.balance("netkiller"))fmt.Println(token.balance("neo"))}

          我們可以建立很多套這樣的比,例如水果幣,蔬菜幣,流量幣...

          開發一個小型交易所難度也不大,讓用戶在交易所中交易這些幣。


          主站蜘蛛池模板: 亚洲一区免费在线观看| 亚洲AV无码一区二区乱孑伦AS| 亚洲av无码一区二区三区四区| 成人在线一区二区| 国产中文字幕一区| 亚洲av无码一区二区乱子伦as | 性色AV一区二区三区| 无码欧精品亚洲日韩一区| 天堂资源中文最新版在线一区 | 亚洲视频在线一区二区| 无码人妻精品一区二区三区不卡| 国产一区中文字幕在线观看| 亚洲夜夜欢A∨一区二区三区 | 精品福利一区二区三区免费视频 | 麻豆精品久久久一区二区| 少妇人妻偷人精品一区二区| 人妻少妇AV无码一区二区| 国产午夜精品一区二区三区 | 亚洲色精品三区二区一区| 色窝窝免费一区二区三区| 成人国产精品一区二区网站公司| 韩国福利视频一区二区| 国产精品无码一区二区三区免费| 一区免费在线观看| 国产一区麻豆剧传媒果冻精品| 亚洲一区二区三区AV无码| 91精品国产一区二区三区左线| 亚洲日韩中文字幕无码一区| AV天堂午夜精品一区二区三区| 日韩精品国产一区| 国产日韩精品一区二区三区在线| 国产波霸爆乳一区二区| 日本片免费观看一区二区| 国产伦精品一区二区三区免.费 | 无码人妻一区二区三区免费视频 | 东京热无码一区二区三区av| 精品一区二区在线观看| 国产精品一区在线播放| 亚洲Av无码国产一区二区| 一区二区三区四区国产| 无码精品人妻一区二区三区漫画|