引子
简单的程序,不许要考虑通信问题,在一个进程中函数调用即可满足程序功能的分治工作,
但是当程序规模达到一定程序之后, 就必须将程序划分为独立的逻辑体, 逻辑体之间需要进行通信。
本文列举程序通信方式,从微观到宏观。
事件
通过事件,可以将单线程的程序,做好逻辑切分的情况下,产生通信过程,同时保持逻辑解耦。
JS - DOM标准
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/EventTarget
EventTarget 的 addEventListener 方法添加事件, dispatchEvent 分发事件。
class MyEventTarget extends EventTarget { constructor(mySecret) { super(); this._secret = mySecret; } get secret() { return this._secret; } }; let myEventTarget = new MyEventTarget(5); let value = myEventTarget.secret; // == 5 myEventTarget.addEventListener("foo", function(e) { this._secret = e.detail; }); let event = new CustomEvent("foo", { detail: 7 }); myEventTarget.dispatchEvent(event); let newValue = myEventTarget.secret; // == 7
标准:
https://dom.spec.whatwg.org/#dom-eventtarget-eventtarget
target = new EventTarget();
Creates a new
EventTarget
object, which can be used by developers to dispatch and listen for events.target . addEventListener(type, callback [, options])
Appends an event listener for events whose
type
attribute value is type. The callback argument sets the callback that will be invoked when the event is dispatched.The options argument sets listener-specific options. For compatibility this can be a boolean, in which case the method behaves exactly as if the value was specified as options’s
capture
.When set to true, options’s
capture
prevents callback from being invoked when the event’seventPhase
attribute value isBUBBLING_PHASE
. When false (or not present), callback will not be invoked when event’seventPhase
attribute value isCAPTURING_PHASE
. Either way, callback will be invoked if event’seventPhase
attribute value isAT_TARGET
.When set to true, options’s
passive
indicates that the callback will not cancel the event by invokingpreventDefault()
. This is used to enable performance optimizations described in §2.8 Observing event listeners.When set to true, options’s
once
indicates that the callback will only be invoked once after which the event listener will be removed.The event listener is appended to target’s event listener list and is not appended if it has the same type, callback, and capture.
target . removeEventListener(type, callback [, options])
Removes the event listener in target’s event listener list with the same type, callback, and options.
target . dispatchEvent(event)
Dispatches a synthetic event event to target and returns true if either event’s
cancelable
attribute value is false or itspreventDefault()
method was not invoked, and false otherwise.
JS - commonjs标准
http://javascript.ruanyifeng.com/nodejs/events.html
const EventEmitter = require('events'); class MyEmitter extends EventEmitter {} const myEmitter = new MyEmitter(); myEmitter.on('event', () => { console.log('an event occurred!'); }); myEmitter.emit('event');
标准:
https://nodejs.org/api/events.html#events_class_eventemitter
Much of the Node.js core API is built around an idiomatic asynchronous event-driven architecture in which certain kinds of objects (called "emitters") emit named events that cause
Function
objects ("listeners") to be called.For instance: a
net.Server
object emits an event each time a peer connects to it; afs.ReadStream
emits an event when the file is opened; a stream emits an event whenever data is available to be read.All objects that emit events are instances of the
EventEmitter
class. These objects expose aneventEmitter.on()
function that allows one or more functions to be attached to named events emitted by the object. Typically, event names are camel-cased strings but any valid JavaScript property key can be used.When the
EventEmitter
object emits an event, all of the functions attached to that specific event are called synchronously. Any values returned by the called listeners are ignored and will be discarded.The following example shows a simple
EventEmitter
instance with a single listener. TheeventEmitter.on()
method is used to register listeners, while theeventEmitter.emit()
method is used to trigger the event.
Spring - 注解
https://www.dev2qa.com/spring-event-publisher-listener-example/
Spring Event Publisher / Listener
Vue.js -- EventBUS
借助Vue API
https://alligator.io/vuejs/global-event-bus/
event-bus.js
import Vue from 'vue'; export const EventBus = new Vue();
linstener
// Import the EventBus. import { EventBus } from './event-bus.js'; // Listen for the i-got-clicked event and its payload. EventBus.$on('i-got-clicked', clickCount => { console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`) });
publisher
// Import the EventBus we just created. import { EventBus } from './event-bus.js'; // The event handler function. const clickHandler = function(clickCount) { console.log(`Oh, that's nice. It's gotten ${clickCount} clicks! :)`) } // Listen to the event. EventBus.$on('i-got-clicked', clickHandler); // Stop listening. EventBus.$off('i-got-clicked', clickHandler);
Vue API
https://cn.vuejs.org/v2/api/#%E5%AE%9E%E4%BE%8B%E6%96%B9%E6%B3%95-%E4%BA%8B%E4%BB%B6
这种用法有点hacker味道, 非Vue的原本设计。
所以有人专门为Vue实现了 EventBus 库。
Vue-Event
https://github.com/cklmercer/vue-events
import Vue from 'vue' import VueEvents from 'vue-events' Vue.use(VueEvents)
new Vue({ data() { return { eventData: { foo: 'baz' } } }, mounted() { this.$events.fire('testEvent', this.eventData); this.$events.emit('testEvent', this.eventData); this.$events.$emit('testEvent', this.eventData); } })
new Vue({ mounted() { this.$events.on('testEvent', eventData => console.log(eventData)); }, beforeDestroy() { this.$events.$off('testEvent') this.$events.off('testEvent') this.$events.remove('testEvent') } })
Vue-Bus
https://github.com/yangmingshan/vue-bus
// ... created() { this.$bus.on('add-todo', this.addTodo); this.$bus.once('once', () => console.log('This listener will only fire once')); }, beforeDestroy() { this.$bus.off('add-todo', this.addTodo); }, methods: { addTodo(newTodo) { this.todos.push(newTodo); } }
// ... methods: { addTodo() { this.$bus.emit('add-todo', { text: this.newTodoText }); this.$bus.emit('once'); this.newTodoText = ''; } }
EventBus
https://github.com/google/guava/wiki/EventBusExplained
EventBus
allows publish-subscribe-style communication between components without requiring the components to explicitly register with one another (and thus be aware of each other). It is designed exclusively to replace traditional Java in-process event distribution using explicit registration. It is not a general-purpose publish-subscribe system, nor is it intended for interprocess communication.he
EventBus
system and code use the following terms to discuss event distribution:
Event Any object that may be posted to a bus. Subscribing The act of registering a listener with an EventBus
, so that its handler methods will receive events.Listener An object that wishes to receive events, by exposing handler methods. Handler method A public method that the EventBus
should use to deliver posted events. Handler methods are marked by the@Subscribe
annotation.Posting an event Making the event available to any listeners through the EventBus
.
https://www.jianshu.com/p/f9ae5691e1bb
EventBus能够简化各组件间的通信,让我们的代码书写变得简单,能有效的分离事件发送方和接收方(也就是解耦的意思),能避免复杂和容易出错的依赖性和生命周期问题。
三要素
- Event 事件。它可以是任意类型。
- Subscriber 事件订阅者。在EventBus3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe(),并且指定线程模型,默认是POSTING。
- Publisher 事件的发布者。我们可以在任意线程里发布事件,一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
http://greenrobot.org/eventbus/
EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
Your benefits using EventBus: It…
- simplifies the communication between components
- decouples event senders and receivers
- performs well with UI artifacts (e.g. Activities, Fragments) and background threads
- avoids complex and error-prone dependencies and life cycle issues
- is fast; specifically optimized for high performance
- is tiny (<50k jar)
- is proven in practice by apps with 100,000,000+ installs
- has advanced features like delivery threads, subscriber priorities, etc.
事件模式(发布、订阅)
https://www.zhihu.com/question/23486749
链接:https://www.zhihu.com/question/23486749/answer/314072549
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
发布订阅模式属于广义上的观察者模式
发布订阅模式是最常用的一种观察者模式的实现,并且从解耦和重用角度来看,更优于典型的观察者模式
发布订阅模式多了个事件通道
在观察者模式中,观察者需要直接订阅目标事件;在目标发出内容改变的事件后,直接接收事件并作出响应
╭─────────────╮ Fire Event ╭──────────────╮
│ │─────────────>│ │
│ Subject │ │ Observer │
│ │<─────────────│ │
╰─────────────╯ Subscribe ╰──────────────╯
在发布订阅模式中,发布者和订阅者之间多了一个发布通道;一方面从发布者接收事件,另一方面向订阅者发布事件;订阅者需要从事件通道订阅事件
以此避免发布者和订阅者之间产生依赖关系
╭─────────────╮ ╭───────────────╮ Fire Event ╭──────────────╮
│ │ Publish Event │ │───────────────>│ │
│ Publisher │────────────────>│ Event Channel │ │ Subscriber │
│ │ │ │<───────────────│ │
╰─────────────╯ ╰───────────────╯ Subscribe ╰──────────────╯
作者:我曾跨过山和大海
链接:https://www.zhihu.com/question/23486749/answer/186049081
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
订阅/发布 模式重点是广播外的消息,这个模式并不关心谁接收事件,只管发送事件。
Let’s talk about two concepts that seem similar: the Observer pattern and the Pub-sub pattern.
Observer pattern
This is a pattern of development in which your class or primary object (known as the Observable) notifies other interested classes or objects (known as Observers) with relevant information (events).
Pub-sub pattern
The objective of the pub-sub pattern is exactly the same as the Observer pattern viz. you want some other class to know of certain events taking place.
There’s an important semantic difference between the Observer and Pub-sub patterns though: in the pub-sub pattern the focus is on “broadcasting” messages outside. The Observable here doesn’t want to know who the events are going out to, just that they’ve gone out. In other words the Observable (a.k.a Publisher) doesn’t want to know who the Observers (a.k.a Subscribers) are.
https://www.cnblogs.com/lovesong/p/5272752.html
var pubsub = {}; (function(myObject) { // Storage for topics that can be broadcast // or listened to var topics = {}; // An topic identifier var subUid = -1; // Publish or broadcast events of interest // with a specific topic name and arguments // such as the data to pass along myObject.publish = function( topic, args ) { if ( !topics[topic] ) { return false; } var subscribers = topics[topic], len = subscribers ? subscribers.length : 0; while (len--) { subscribers[len].func( topic, args ); } return this; }; // Subscribe to events of interest // with a specific topic name and a // callback function, to be executed // when the topic/event is observed myObject.subscribe = function( topic, func ) { if (!topics[topic]) { topics[topic] = []; } var token = ( ++subUid ).toString(); topics[topic].push({ token: token, func: func }); return token; }; // Unsubscribe from a specific // topic, based on a tokenized reference // to the subscription myObject.unsubscribe = function( token ) { for ( var m in topics ) { if ( topics[m] ) { for ( var i = 0, j = topics[m].length; i < j; i++ ) { if ( topics[m][i].token === token ) { topics[m].splice( i, 1 ); return token; } } } } return this; }; }( pubsub ));
进程间通信-DBus
https://www.freedesktop.org/wiki/Software/dbus/
D-Bus is a message bus system, a simple way for applications to talk to one another. In addition to interprocess communication, D-Bus helps coordinate process lifecycle; it makes it simple and reliable to code a "single instance" application or daemon, and to launch applications and daemons on demand when their services are needed.
D-Bus supplies both a system daemon (for events such as "new hardware device added" or "printer queue changed") and a per-user-login-session daemon (for general IPC needs among user applications). Also, the message bus is built on top of a general one-to-one message passing framework, which can be used by any two apps to communicate directly (without going through the message bus daemon). Currently the communicating applications are on one computer, or through unencrypted TCP/IP suitable for use behind a firewall with shared NFS home directories. (Help wanted with better remote transports - the transport mechanism is well-abstracted and extensible.)
The dbus low-level API reference implementation and the D-Bus protocol have been heavily tested in the real world over several years, and are now "set in stone." Future changes will either be compatible or versioned appropriately.
https://www.ibm.com/developerworks/cn/linux/l-dbus.html
D-BUS 本质上是 进程间通信(inter-process communication)(IPC)的一个实现。不过,有一些 特性使得 D-BUS 远远不是“只是另一个 IPC 实现”。有很多不同的 IPC 实现,因为每一个都定位于解决 特定的明确定义的问题。CORBA 是用于面向对象编程中复杂的 IPC 的一个强大的解决方案。DCOP 是一个 较轻量级的 IPC 框架,功能较少,但是可以很好地集成到 K 桌面环境中。SOAP 和 XML-RPC 设计用于 Web 服务,因而使用 HTTP 作为其传输协议。D-BUS 设计用于桌面应用程序和 OS 通信。
网络间通信-RabbitMQ
http://www.rabbitmq.com/#features
RabbitMQ is the most widely deployed open source message broker.
With more than 35,000 production deployments of RabbitMQ world-wide at small startups and large enterprises, RabbitMQ is the most popular open source message broker.
RabbitMQ is lightweight and easy to deploy on premises and in the cloud. It supports multiple messaging protocols. RabbitMQ can be deployed in distributed and federated configurations to meet high-scale, high-availability requirements.
http://www.rabbitmq.com/getstarted.html
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
The major difference between RabbitMQ and the post office is that it doesn't deal with paper, instead it accepts, stores and forwards binary blobs of data ‒ messages.
Introduction
Prerequisites
This tutorial assumes RabbitMQ is installed and running on localhost on standard port (5672). In case you use a different host, port or credentials, connections settings would require adjusting.
Where to get help
If you're having trouble going through this tutorial you can contact us through the mailing list.
RabbitMQ is a message broker: it accepts and forwards messages. You can think about it as a post office: when you put the mail that you want posting in a post box, you can be sure that Mr. or Ms. Mailperson will eventually deliver the mail to your recipient. In this analogy, RabbitMQ is a post box, a post office and a postman.
The major difference between RabbitMQ and the post office is that it doesn't deal with paper, instead it accepts, stores and forwards binary blobs of data ‒ messages.
RabbitMQ, and messaging in general, uses some jargon.
-
Producing means nothing more than sending. A program that sends messages is a producer :
-
A queue is the name for a post box which lives inside RabbitMQ. Although messages flow through RabbitMQ and your applications, they can only be stored inside a queue. A queue is only bound by the host's memory & disk limits, it's essentially a large message buffer. Many producers can send messages that go to one queue, and many consumers can try to receive data from one queue. This is how we represent a queue:
-
Consuming has a similar meaning to receiving. A consumer is a program that mostly waits to receive messages:
Note that the producer, consumer, and broker do not have to reside on the same host; indeed in most applications they don't.
"Hello World"
(using the amqp.node client)
In this part of the tutorial we'll write two small programs in Javascript; a producer that sends a single message, and a consumer that receives messages and prints them out. We'll gloss over some of the detail in the amqp.node API, concentrating on this very simple thing just to get started. It's a "Hello World" of messaging.
In the diagram below, "P" is our producer and "C" is our consumer. The box in the middle is a queue - a message buffer that RabbitMQ keeps on behalf of the consumer.
https://github.com/rabbitmq/rabbitmq-tutorials/blob/master/javascript-nodejs/src/receive.js
#!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(err, conn) { conn.createChannel(function(err, ch) { var q = 'hello'; ch.assertQueue(q, {durable: false}); console.log(" [*] Waiting for messages in %s. To exit press CTRL+C", q); ch.consume(q, function(msg) { console.log(" [x] Received %s", msg.content.toString()); }, {noAck: true}); }); });
#!/usr/bin/env node var amqp = require('amqplib/callback_api'); amqp.connect('amqp://localhost', function(err, conn) { conn.createChannel(function(err, ch) { var q = 'hello'; var msg = 'Hello World!'; ch.assertQueue(q, {durable: false}); ch.sendToQueue(q, Buffer.from(msg)); console.log(" [x] Sent %s", msg); }); setTimeout(function() { conn.close(); process.exit(0) }, 500); });