Dfinity Dapp开发——Canister实现Publisher/Subscriber(发布/订阅者)消息模式

前言

1.Publisher/Subscriber是因特网计算机上一个特别有价值的模式,该模式的作用是发布者和订阅者可以相互发送消息。
2.Publisher(发布者)定义一个协议,以便能够随时向一个或多个它的订阅者传输一系列消息值。但它有一个缺点是,它发出的一个或者多个消息值,不管是正常完成还是消息传递失败,一旦发布者发出完成事件,它就算任务完成了,不能再给出任何异常的消息事件。
3.Subscriber(订阅者)是一种协议,它定义了能够从发布者接收消息值的类型的接口,如果声明了Publisher,但是没有任何订阅者订阅它消息的话,Publisher也是无法发出任何消息值。

实现代码

1.创建一个项目

dfx new PubSub

之后用vscode打开

2.创建一个Publisher canister,在Publisher公开了一个订阅方法,其他canister可以调用这个方法来注册一个回调函数,每当Publisher发布一个消息值时,这个回调就被执行。

Publisher canister 代码:

import Array "mo:base/Array";

actor Pub
{
    
    
    type Counter = 
    {
    
    
        topic:Text;
        value:Nat;
        info_text : Text;
    };

    type Sub = 
    {
    
    
        topic : Text;
        callback : shared Counter ->();
    };

    var subs : [Sub] = [];

    public func subscribe (sub : Sub) 
    {
    
    
        subs := Array.append<Sub>(subs,[sub]); //数组附加
    };

    public func publish(counter : Counter) 
    {
    
    
        for(sub in subs.vals())
        {
    
    
            //判断订阅的主题
            if(sub.topic == counter.topic)
            {
    
    
                sub.callback(counter);
            };
        };
    };
}

3.创建消息订阅的Subscriber canister,当Publisher canister发布一个消息值时,执行Subscriber canister的updateInfo就判断是否与订阅的消息匹配,如果匹配则返回获取消息值,为了更好的演示这个功能,这里创建了两个Subscriber canister 。

sub_one.mo

import Pub "canister:pub";

actor SubOne
{
    
    
    type Counter = 
    {
    
    
        topic : Text;
        value : Nat;
        info_text : Text;
    };

    type Information = 
    {
    
    
        count : Nat;
        info_text : Text;
    };

    var count : Nat = 0;
    var text : Text = "";

    public func init(topic0 : Text)
    {
    
    
        Pub.subscribe({
    
    topic = topic0; callback = updateInfo;});
    };

    public func updateInfo(counter : Counter)
    {
    
    
        count += counter.value;
        info_text := counter.text;
    };

    public query func getCount() : async Information
    {
    
    
       let info : Information = {
    
    
           count = count;
           info_text = text;
       };
       return info;
    };
};

sub_two.mo

import Pub "canister:pub";

actor SubTwo
{
    
    
    type Counter = 
    {
    
    
        topic : Text;
        value : Nat;
    };

    var count : Nat = 0;

    public func init(topic0 : Text)
    {
    
    
        Pub.subscribe({
    
    topic = topic0; callback = updateCount;});
    };

    public func updateCount(counter : Counter)
    {
    
    
        count += counter.value;
    };

    public query func getCount() : async Nat
    {
    
    
        return count;
    };
};

4.在dfx.json文件里面添加canister

{
    
    
  "canisters": {
    
    
    "pub": {
    
    
      "main": "src/pubsub/pub/pub.mo"
    },
    "sub_one": {
    
    
      "main": "src/pubsub/sub_one/sub_one.mo"
    },
    "sub_two": {
    
    
      "main": "src/pubsub/sub_two/sub_two.mo"
    }
  },
  "defaults": {
    
    
    "build": {
    
    
      "args": "",
      "packtool": ""
    }
  },
  "dfx": "0.9.2",
  "networks": {
    
    
    "local": {
    
    
      "bind": "127.0.0.1:8000",
      "type": "ephemeral"
    }
  },
  "version": 1
}

5.部署项目
运行环境

dfx start

部署项目

dfx deploy

6.订阅/发布信息

sub_one 订阅的是A这个主题

dfx canister call sub_one init '("A")'

sub_two 订阅的是B这个主题

dfx canister call sub_two init '("B")'

pub发布第1个A消息值

dfx canister call pub publish '(record { "topic" = "A"; "value" = 2;"info_text" = "This is theme A" })'

sub_one获取消息值

dfx canister call sub_one  getCount

会得到以下结果
在这里插入图片描述

sub_tow获取消息值

dfx canister call sub_two  getCount

在这里插入图片描述
pub发布第2个B消息值

dfx canister call pub publish '(record { "topic" = "B"; "value" = 2;"info_text" = "This is theme B" })'

sub_one获取的消息值还是原来的消息值:
在这里插入图片描述
sub_two获取消息值:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/matt45m/article/details/124590822