程序组件通信方案集锦
引子
簡單的程序,不許要考慮通信問題,在一個進程中函數調用即可滿足程序功能的分治工作,
但是當程序規模達到一定程序之后, 就必須將程序劃分為獨立的邏輯體, 邏輯體之間需要進行通信。
本文列舉程序通信方式,從微觀到宏觀。
事件
通過事件,可以將單線程的程序,做好邏輯切分的情況下,產生通信過程,同時保持邏輯解耦。
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.
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’s eventPhase attribute value is BUBBLING_PHASE. When false (or not present), callback will not be invoked when event’s eventPhase attribute value is CAPTURING_PHASE. Either way, callback will be invoked if event’s eventPhase attribute value is AT_TARGET.
When set to true, options’s passive indicates that the callback will not cancel the event by invoking preventDefault(). 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.
Removes the event listener in target’s event listener list with the same type, callback, and options.
Dispatches a synthetic event event to target and returns true if either event’s cancelable attribute value is false or its preventDefault() 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; a fs.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 an eventEmitter.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. The eventEmitter.on() method is used to register listeners, while the eventEmitter.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:
| 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 tovar topics = {};// An topic identifiervar subUid = -1;// Publish or broadcast events of interest// with a specific topic name and arguments// such as the data to pass alongmyObject.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 observedmyObject.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 subscriptionmyObject.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 nodevar 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 nodevar 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); });?
轉載于:https://www.cnblogs.com/lightsong/p/9551761.html
總結
以上是生活随笔為你收集整理的程序组件通信方案集锦的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CVE-2021-3156:Sudo 堆
- 下一篇: Shell 判断数是否为某数的倍数