WAMP-网络程序消息协议

WAMP-网络程序消息协议

wamp是一个开放式的标准的websocket子协议,在一个统一协议中提供两种应用程序的消息模式: 远程过程调用  +  发布&订阅

它在使用不同语言的开放的WebSocket协议中提供统一应用程序路由。使用WAMP,可以用松耦合实现实时通信的应用程序组件构建分布式系统。

因为应用程序通常对两种形式的通信都有自然的需要,并且不应该要求为这些形式使用不同的协议/手段, 所以这就是为什么WAMP提供这这两种通信模式。

WAMP的核心是为应用程序组件提供两种通信模式,以便彼此通信:

         发布&订阅  + 远程过程调用

一、统一应用路由

WAMP提供给了我们所谓的应用程序的统一应用路由:在一个协议中的应用组件之间路由两个事件(用于PubSub)和路由呼叫(用于RPC)。

统一路由可能最好通过与传统方法进行对比来解释。 在客户端 - 服务器模型中,远程过程调用直接从调用者到被调用者:

  调用者-->被调者

在客户端 - 服务器模型中,Caller需要知道Callee驻留在哪里以及如何到达它,这引入Caller和Callee之间的强耦合。 这是很麻烦的,因为应用程序可能很快变得复杂和无法维护。

所以WAMP如何解决这一点的呢?

在发布 - 订阅模型中,发布者向抽象的“主题”提交信息,并且订阅者仅通过它们对应“主题”的兴趣而间接地接收到信息。 两个人不知道彼此。 它们通过“主题”和通常称为代理的中间体解耦: 发布者-->代理商-->订阅者

代理商保留订阅:谁目前订阅了哪个主题。 当发布者向主题发布一些信息(“事件”)时,代理将查找当前对该主题订阅的用户:确定发布到主题上的订阅者的集合,然后将信息(“事件”)转发给所有这些订阅者。 而确定信息接收者(独立于提交的信息)和将信息转发到接收者的行为就被称为路由。

现在,WAMP将松耦合的好处转化为RPC。与客户端 - 服务器模型不同,WAMP还通过引入中介:经销商来解除Caller和Callees的联系: 调用者-->经销商-->被调者

类似于Broker在PubSub中的作用,经销商负责将来自Caller的呼叫路由到被叫方,并路由返回结果或者错误,反之亦然。 两者不知道彼此:对等体驻留在哪里以及如何到达它,这些内容被封装在经销商中。

使用WAMP,被叫方在经销商处以抽象名称注册过程:标识过程的URI。 当调用者想要调用远程过程时,它与经销商谈话,并且仅提供要调用的过程的URI加上任何调用参数。 经销商将在他的注册程序书中查找要援引的程序。 书中的信息包括执行程序的Callee所在的位置,以及如何到达它。

实际上,Caller和Callees是分离的,应用程序可以使用RPC并仍然受益于松耦合。

WAMP调用路由器:路由 = 代理商 + 经销商,路由器能够路由呼叫,因此可以支持使用RPC和PubSub的灵活的解耦架构。

这里是一个例子。 想象一下,你有一个像Arduino Yun这样的小型嵌入式设备,具有传感器(如温度传感器)和致动器(如灯或电机)连接。 您希望将设备集成到整个系统中,用户面向前端以控制执行器,并连续处理后端组件中的传感器值。

 使用WAMP,可以有一个基于浏览器的UI及嵌入式设备和后端实时交谈,从基于浏览器的UI打开设备上的灯自然通过在设备(1)上调用远程过程来完成。 并且由设备连续生成的传感器值通过发布和订阅(2)自然地传输到后端组件(并且可能是其他组件)。

二、多语言化

WAMP的设计有一流的支持,支持不同的语言。 WAMP中没有要求特定于单个编程语言。 只要编程语言具有WAMP实现,它就可以与使用WAMP支持的任何其他语言编写的应用程序组件实现透明通信。

三、什么是RPC?什么是发布-订阅?

远程过程调用(RPC)是一种涉及三个角色的同级的消息模式:

呼叫者、被叫者、经销商

调用者通过提供过程URI和调用的任何参数来向远程过程发出调用。 被调者将使用提供的调用参数执行该过程,并将调用结果返回给调用者。

调用者注册它们向经销商提供的程序,呼叫者首先向经销商发起程序呼叫,经销商将来自呼叫者的呼叫路由实现到被调者,并将呼叫结果从被调者路由返回到呼叫者。

Caller和Callee通常运行应用程序代码,而经销商作为一个通用路由器,用于远程过程调用解耦Caller和Callees。

发布和订阅(PubSub)是一种包含三个角色的同行的消息模式:

发布者、订阅者、经纪人

发布者通过提供主题URI和事件的任何有效内容将主题发布,主题的订阅者将事件内容接收。

订阅者订阅他们对Brokers感兴趣的主题,发布者首先在Brokers发布。 代理将发布者传入的事件路由到订阅相应主题的订阅者。

发布者和订阅者通常运行应用程序代码,而代理作为通用路由器用于将发布者与订阅者分离。

下面是一个简单的WAMP应用小程序,搭建一个CrossBar服务器,开启服务器,就可以运行wamp小程序了。

发布者(Publish):

package ws.wamp.jawampa;

import java.io.IOException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import rx.Scheduler;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import ws.wamp.jawampa.ApplicationError;
import ws.wamp.jawampa.Request;
import ws.wamp.jawampa.WampError;
import ws.wamp.jawampa.WampClient;
import ws.wamp.jawampa.WampClientBuilder;

public class Publish {
    
    String url;
    String realm;
    int flag = 0;
    WampClient client;
    
    Subscription addProcSubscription;
    Subscription counterPublication;
    Subscription onHelloSubscription;
    
    // Scheduler for this example
    ExecutorService executor = Executors.newSingleThreadExecutor();
    Scheduler rxScheduler = Schedulers.from(executor);
    
    static final int TIMER_INTERVAL = 1000; // 1s
    int counter = 0;
    
    Publish(String url, String realm) throws Exception {
        this.url = url;
        this.realm = realm;
    }
    
    
    public static void main(String[] args) throws Exception {
      if (args.length != 2) {
          System.out.println("Need 2 commandline arguments: Router URL and ream name");
          return;
      }
      String args1 ="ws://172.16.100.55:8080/ws";
      String args2 = "realm1";
      new Publish(args1, args2).run();
  }
  
  void run() {
      
      WampClientBuilder builder = new WampClientBuilder();
      try {
          builder.witUri(url)
                 .withRealm(realm)
                 .withInfiniteReconnects()
                 .withCloseOnErrors(true)
                 .withReconnectInterval(5, TimeUnit.SECONDS);
          client = builder.build();
      } catch (WampError e) {
          e.printStackTrace();
          return;
      }

      // Subscribe on the clients status updates
      client.statusChanged()
            .observeOn(rxScheduler)
            .subscribe(new Action1<WampClient.Status>() {
          @Override
          public void call(WampClient.Status t1) {
              System.out.println("Session status changed to " + t1);

              if (t1 == WampClient.Status.Connected) {
                  
               
                  // PUBLISH and CALL every second .. forever
                  counter = 0;
                  final int published = 4;
                  onHelloSubscription= client.publish("glf", published)
                                .observeOn(rxScheduler)
                                .subscribe(new Action1<Long>() {
                                  @Override
                                  public void call(Long t1) {
                                      System.out.println("published to 'oncounter' with counter " + published);
                                  }
                              }, new Action1<Throwable>() {
                                  @Override
                                  public void call(Throwable e) {
                                      System.out.println("Error during publishing to 'oncounter': " + e);
                                  }
                              });
                          
                           CALL a remote procedure
                            
                              flag =1;
                               System.out.println(flag);
                          counter++;
                     
              }
              else if (t1 == WampClient.Status.Disconnected) {
                  closeSubscriptions();
              }
          }
      }, new Action1<Throwable>() {
          @Override
          public void call(Throwable t) {
              System.out.println("Session ended with error " + t);
          }
      }, new Action0() {
          @Override
          public void call() {
              System.out.println("Session ended normally");
          }
      });

      client.open();
      
      waitUntilKeypressed();
      while (flag == 0){
          try {
              Thread.sleep(1000);
          } catch (InterruptedException e) {
              
              e.printStackTrace();
          }
          
          System.out.println("waiting");
          }
      System.out.println("Shutting down");
      closeSubscriptions();
      client.close();
      try {
          client.getTerminationFuture().get();
      } catch (Exception e) {}
      
      executor.shutdown();
  }
  
  void closeSubscriptions() {
      if (onHelloSubscription != null)
          onHelloSubscription.unsubscribe();
      onHelloSubscription = null;
      if (counterPublication != null)
          counterPublication.unsubscribe();
      counterPublication = null;
      if (addProcSubscription != null)
          addProcSubscription.unsubscribe();
      addProcSubscription = null;
  }
  
  private void waitUntilKeypressed() {
      try {
          System.in.read();
          while (System.in.available() > 0) {
              System.in.read();
          }
      } catch (IOException e) {
          e.printStackTrace();
      }
  }

}
View Code

订阅者(Subscribe):

package ws.wamp.jawampa;

import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import rx.Scheduler;
import rx.Subscription;
import rx.functions.Action0;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
import ws.wamp.jawampa.WampError;
import ws.wamp.jawampa.WampClient;
import ws.wamp.jawampa.WampClientBuilder;

public class Subscribe {

    String url;
    String realm;

    WampClient client;

    Subscription glfSubscription;

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Scheduler rxScheduler = Schedulers.from(executor);

    static final int TIMER_INTERVAL = 1000; // 1s
    int counter = 0;

    Subscribe(String url, String realm) throws Exception {
        this.url = url;
        this.realm = realm;
    }

    
    public static void main(String[] args) throws Exception {

        String args1 = "ws://172.16.100.55:8080/ws";
        String args2 = "realm1";
        new Subscribe(args1, args2).run();
    }

    void run() {

        WampClientBuilder builder = new WampClientBuilder();
        try {
            builder.witUri(url).withRealm(realm).withInfiniteReconnects().withCloseOnErrors(true)
                    .withReconnectInterval(5, TimeUnit.SECONDS);
            client = builder.build();
        } catch (WampError e) {
            e.printStackTrace();
            return;
        }

        // Subscribe on the clients status updates
        client.statusChanged().observeOn(rxScheduler).subscribe(new Action1<WampClient.Status>() {
            @Override
            public void call(WampClient.Status t1) {
                System.out.println("工作状态变为 " + t1);

                if (t1 == WampClient.Status.Connected) {
                    // SUBSCRIBE to a topic and receive events
                    glfSubscription = client.makeSubscription("glf", String.class).observeOn(rxScheduler)
                            .subscribe(new Action1<String>() {
                                 @Override
                                public void call(String msg) {
                                    System.out.println("主题 'glf' 接收: " + msg);
                                }
                            }, new Action1<Throwable>() {
                                 @Override
                                public void call(Throwable e) {
                                    System.out.println("failed to subscribe 'websocket': " + e);
                                }
                            }, new Action0() {
                                 @Override
                                public void call() {
                                    System.out.println("'websocket' subscription ended");
                                }
                            });

                } else if (t1 == WampClient.Status.Disconnected) {
                    closeSubscriptions();
                }
            }
        }, new Action1<Throwable>() {
             @Override
            public void call(Throwable t) {
                System.out.println("Session ended with error " + t);
            }
        }, new Action0() {
             @Override
            public void call() {
                System.out.println("工作正常结束");
            }
        });

        client.open();

        waitUntilKeypressed();
        System.out.println("关闭");
        closeSubscriptions();
        client.close();
        try {
            client.getTerminationFuture().get();
        } catch (Exception e) {
        }

        executor.shutdown();
    }
    void closeSubscriptions() {
        if (glfSubscription != null)
            glfSubscription.unsubscribe();
        glfSubscription = null;

    }

    private void waitUntilKeypressed() {
        try {
            System.in.read();
            while (System.in.available() > 0) {
                System.in.read();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

四、什么是WebSocket协议

WebSocket是一种新的Web协议,当需要双向,实时通信时,克服HTTP的限制。 WebSocket将是WAMP的理想基础,因为它提供了与Web和浏览器兼容的双向实时消息传递。 不仅如此 - 我们可以在非浏览器环境中运行WebSocket。 从技术上讲,WAMP是一个正式注册的WebSocket子协议(在WebSocket之上运行),它使用JSON作为消息序列化格式。

posted @ 2018-01-06 10:42  我无奈的怀旧风  阅读(1709)  评论(0编辑  收藏  举报