- 浏览: 475317 次
- 性别:
- 来自: 上海
-
文章分类
最新评论
-
kc_hxd_jp:
博主问个问题,这个篇幅下的python代码无法达到应有的作用, ...
zeroMQ初体验-14.命名机制 进阶 -
kobe1029:
Map<String, Object> args ...
rabbitmq 队列长度预设的曲线方案 -
Sasoritattoo:
LZ,这都13年了,抽空把这篇文章的下文给表完了吧,这一口气喘 ...
nginx + gridfs + mongodb 大事记(残) -
3GQQ2012:
引用前文已经说过,XREP其实用以平衡负载,所以这里由它对请求 ...
zeroMQ初体验-15.应答模式进阶(一)-数据的封装 -
iyuan:
ustclz 写道图片怎么显示不了了。。我这看是可以显示的。不 ...
zeroMQ初体验-1.简介及C/S模式
在发布/订阅模式中,特别是现实应用中,总会因为这样那样的问题导致订阅者丢失了所需的数据,如此,便有了重新获得的需求。通常来说,这个会由订阅者来完成,不过"千百个哈姆雷特"从工程的角度来看,实在不忍睹,完全违背了"复用"的概念。于是乎,"克隆模式"便呼之待出了。在发布端存储下这些消息,为了避免队列的堆积这样的杯具,也为了更好的订阅体验,kev-value似乎是不错的选择。
注意:这里的kev-value并非目前红火的nosql(虽然有些类似),可以理解成发布者的数据仓库(应该可以这么理解吧)。
为了简单明了,这里将会对整个机制做一个拆解。
更新数据的存储
模型图:
服务器:
客户端:
key-value库:
根据key获取数据
其实,当订阅者可以发出key来获取数据的时候,它已经不是一个纯粹的订阅者了,或许客户端的称谓会更合适些。
模型图:
服务器:
客户端:
重新发布更新
上面的模型中,数据都集中在一点,或许会有服务器崩溃而导致数据丢失的顾虑,那么,把数据放到客户端呢?
模型图:
服务器:
客户端:
克隆子树
事实上,并不是所有的消费者都愿意消费发布者所提供的所有信息,那么,针对特别的群体,只需提供一个子集就可以了。
服务器:
客户端:
(未完待续)
注意:这里的kev-value并非目前红火的nosql(虽然有些类似),可以理解成发布者的数据仓库(应该可以这么理解吧)。
为了简单明了,这里将会对整个机制做一个拆解。
更新数据的存储
模型图:

服务器:
// // Clone server Model One // // Lets us build this source without creating a library #include "kvsimple.c" int main (void) { // Prepare our context and publisher socket zctx_t *ctx = zctx_new (); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5556"); zclock_sleep (200); zhash_t *kvmap = zhash_new (); int64_t sequence = 0; srandom ((unsigned) time (NULL)); while (!zctx_interrupted) { // Distribute as key-value message kvmsg_t *kvmsg = kvmsg_new (++sequence); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_store (&kvmsg, kvmap); } printf (" Interrupted\n%d messages out\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
客户端:
// // Clone client Model One // // Lets us build this source without creating a library #include "kvsimple.c" int main (void) { // Prepare our context and updates socket zctx_t *ctx = zctx_new (); void *updates = zsocket_new (ctx, ZMQ_SUB); zsocket_connect (updates, "tcp://localhost:5556"); zhash_t *kvmap = zhash_new (); int64_t sequence = 0; while (TRUE) { kvmsg_t *kvmsg = kvmsg_recv (updates); if (!kvmsg) break; // Interrupted kvmsg_store (&kvmsg, kvmap); sequence++; } printf (" Interrupted\n%d messages in\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
key-value库:
/* ===================================================================== kvsimple - simple key-value message class for example applications --------------------------------------------------------------------- Copyright (c) 1991-2011 iMatix Corporation <www.imatix.com> Copyright other contributors as noted in the AUTHORS file. This file is part of the ZeroMQ Guide: http://zguide.zeromq.org This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. ===================================================================== */ #include "kvsimple.h" #include "zlist.h" // Keys are short strings #define KVMSG_KEY_MAX 255 // Message is formatted on wire as 4 frames: // frame 0: key (0MQ string) // frame 1: sequence (8 bytes, network order) // frame 2: body (blob) #define FRAME_KEY 0 #define FRAME_SEQ 1 #define FRAME_BODY 2 #define KVMSG_FRAMES 3 // Structure of our class struct _kvmsg { // Presence indicators for each frame int present [KVMSG_FRAMES]; // Corresponding 0MQ message frames, if any zmq_msg_t frame [KVMSG_FRAMES]; // Key, copied into safe C string char key [KVMSG_KEY_MAX + 1]; }; // --------------------------------------------------------------------- // Constructor, sets sequence as provided kvmsg_t * kvmsg_new (int64_t sequence) { kvmsg_t *self; self = (kvmsg_t *) zmalloc (sizeof (kvmsg_t)); kvmsg_set_sequence (self, sequence); return self; } // --------------------------------------------------------------------- // Destructor // Free shim, compatible with zhash_free_fn void kvmsg_free (void *ptr) { if (ptr) { kvmsg_t *self = (kvmsg_t *) ptr; // Destroy message frames if any int frame_nbr; for (frame_nbr = 0; frame_nbr < KVMSG_FRAMES; frame_nbr++) if (self->present [frame_nbr]) zmq_msg_close (&self->frame [frame_nbr]); // Free object itself free (self); } } void kvmsg_destroy (kvmsg_t **self_p) { assert (self_p); if (*self_p) { kvmsg_free (*self_p); *self_p = NULL; } } // --------------------------------------------------------------------- // Reads key-value message from socket, returns new kvmsg instance. kvmsg_t * kvmsg_recv (void *socket) { assert (socket); kvmsg_t *self = kvmsg_new (0); // Read all frames off the wire, reject if bogus int frame_nbr; for (frame_nbr = 0; frame_nbr < KVMSG_FRAMES; frame_nbr++) { if (self->present [frame_nbr]) zmq_msg_close (&self->frame [frame_nbr]); zmq_msg_init (&self->frame [frame_nbr]); self->present [frame_nbr] = 1; if (zmq_recvmsg (socket, &self->frame [frame_nbr], 0) == -1) { kvmsg_destroy (&self); break; } // Verify multipart framing int rcvmore = (frame_nbr < KVMSG_FRAMES - 1)? 1: 0; if (zsockopt_rcvmore (socket) != rcvmore) { kvmsg_destroy (&self); break; } } return self; } // --------------------------------------------------------------------- // Send key-value message to socket; any empty frames are sent as such. void kvmsg_send (kvmsg_t *self, void *socket) { assert (self); assert (socket); int frame_nbr; for (frame_nbr = 0; frame_nbr < KVMSG_FRAMES; frame_nbr++) { zmq_msg_t copy; zmq_msg_init (©); if (self->present [frame_nbr]) zmq_msg_copy (©, &self->frame [frame_nbr]); zmq_sendmsg (socket, ©, (frame_nbr < KVMSG_FRAMES - 1)? ZMQ_SNDMORE: 0); zmq_msg_close (©); } } // --------------------------------------------------------------------- // Return key from last read message, if any, else NULL char * kvmsg_key (kvmsg_t *self) { assert (self); if (self->present [FRAME_KEY]) { if (!*self->key) { size_t size = zmq_msg_size (&self->frame [FRAME_KEY]); if (size > KVMSG_KEY_MAX) size = KVMSG_KEY_MAX; memcpy (self->key, zmq_msg_data (&self->frame [FRAME_KEY]), size); self->key [size] = 0; } return self->key; } else return NULL; } // --------------------------------------------------------------------- // Return sequence nbr from last read message, if any int64_t kvmsg_sequence (kvmsg_t *self) { assert (self); if (self->present [FRAME_SEQ]) { assert (zmq_msg_size (&self->frame [FRAME_SEQ]) == 8); byte *source = zmq_msg_data (&self->frame [FRAME_SEQ]); int64_t sequence = ((int64_t) (source [0]) << 56) + ((int64_t) (source [1]) << 48) + ((int64_t) (source [2]) << 40) + ((int64_t) (source [3]) << 32) + ((int64_t) (source [4]) << 24) + ((int64_t) (source [5]) << 16) + ((int64_t) (source [6]) << 8) + (int64_t) (source [7]); return sequence; } else return 0; } // --------------------------------------------------------------------- // Return body from last read message, if any, else NULL byte * kvmsg_body (kvmsg_t *self) { assert (self); if (self->present [FRAME_BODY]) return (byte *) zmq_msg_data (&self->frame [FRAME_BODY]); else return NULL; } // --------------------------------------------------------------------- // Return body size from last read message, if any, else zero size_t kvmsg_size (kvmsg_t *self) { assert (self); if (self->present [FRAME_BODY]) return zmq_msg_size (&self->frame [FRAME_BODY]); else return 0; } // --------------------------------------------------------------------- // Set message key as provided void kvmsg_set_key (kvmsg_t *self, char *key) { assert (self); zmq_msg_t *msg = &self->frame [FRAME_KEY]; if (self->present [FRAME_KEY]) zmq_msg_close (msg); zmq_msg_init_size (msg, strlen (key)); memcpy (zmq_msg_data (msg), key, strlen (key)); self->present [FRAME_KEY] = 1; } // --------------------------------------------------------------------- // Set message sequence number void kvmsg_set_sequence (kvmsg_t *self, int64_t sequence) { assert (self); zmq_msg_t *msg = &self->frame [FRAME_SEQ]; if (self->present [FRAME_SEQ]) zmq_msg_close (msg); zmq_msg_init_size (msg, 8); byte *source = zmq_msg_data (msg); source [0] = (byte) ((sequence >> 56) & 255); source [1] = (byte) ((sequence >> 48) & 255); source [2] = (byte) ((sequence >> 40) & 255); source [3] = (byte) ((sequence >> 32) & 255); source [4] = (byte) ((sequence >> 24) & 255); source [5] = (byte) ((sequence >> 16) & 255); source [6] = (byte) ((sequence >> 8) & 255); source [7] = (byte) ((sequence) & 255); self->present [FRAME_SEQ] = 1; } // --------------------------------------------------------------------- // Set message body void kvmsg_set_body (kvmsg_t *self, byte *body, size_t size) { assert (self); zmq_msg_t *msg = &self->frame [FRAME_BODY]; if (self->present [FRAME_BODY]) zmq_msg_close (msg); self->present [FRAME_BODY] = 1; zmq_msg_init_size (msg, size); memcpy (zmq_msg_data (msg), body, size); } // --------------------------------------------------------------------- // Set message key using printf format void kvmsg_fmt_key (kvmsg_t *self, char *format, …) { char value [KVMSG_KEY_MAX + 1]; va_list args; assert (self); va_start (args, format); vsnprintf (value, KVMSG_KEY_MAX, format, args); va_end (args); kvmsg_set_key (self, value); } // --------------------------------------------------------------------- // Set message body using printf format void kvmsg_fmt_body (kvmsg_t *self, char *format, …) { char value [255 + 1]; va_list args; assert (self); va_start (args, format); vsnprintf (value, 255, format, args); va_end (args); kvmsg_set_body (self, (byte *) value, strlen (value)); } // --------------------------------------------------------------------- // Store entire kvmsg into hash map, if key/value are set // Nullifies kvmsg reference, and destroys automatically when no longer // needed. void kvmsg_store (kvmsg_t **self_p, zhash_t *hash) { assert (self_p); if (*self_p) { kvmsg_t *self = *self_p; assert (self); if (self->present [FRAME_KEY] && self->present [FRAME_BODY]) { zhash_update (hash, kvmsg_key (self), self); zhash_freefn (hash, kvmsg_key (self), kvmsg_free); } *self_p = NULL; } } // --------------------------------------------------------------------- // Dump message to stderr, for debugging and tracing void kvmsg_dump (kvmsg_t *self) { if (self) { if (!self) { fprintf (stderr, "NULL"); return; } size_t size = kvmsg_size (self); byte *body = kvmsg_body (self); fprintf (stderr, "[seq:%" PRId64 "]", kvmsg_sequence (self)); fprintf (stderr, "[key:%s]", kvmsg_key (self)); fprintf (stderr, "[size:%zd] ", size); int char_nbr; for (char_nbr = 0; char_nbr < size; char_nbr++) fprintf (stderr, "%02X", body [char_nbr]); fprintf (stderr, "\n"); } else fprintf (stderr, "NULL message\n"); } // --------------------------------------------------------------------- // Runs self test of class int kvmsg_test (int verbose) { kvmsg_t *kvmsg; printf (" * kvmsg: "); // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *output = zsocket_new (ctx, ZMQ_DEALER); int rc = zmq_bind (output, "ipc://kvmsg_selftest.ipc"); assert (rc == 0); void *input = zsocket_new (ctx, ZMQ_DEALER); rc = zmq_connect (input, "ipc://kvmsg_selftest.ipc"); assert (rc == 0); zhash_t *kvmap = zhash_new (); // Test send and receive of simple message kvmsg = kvmsg_new (1); kvmsg_set_key (kvmsg, "key"); kvmsg_set_body (kvmsg, (byte *) "body", 4); if (verbose) kvmsg_dump (kvmsg); kvmsg_send (kvmsg, output); kvmsg_store (&kvmsg, kvmap); kvmsg = kvmsg_recv (input); if (verbose) kvmsg_dump (kvmsg); assert (streq (kvmsg_key (kvmsg), "key")); kvmsg_store (&kvmsg, kvmap); // Shutdown and destroy all objects zhash_destroy (&kvmap); zctx_destroy (&ctx); printf ("OK\n"); return 0; }
根据key获取数据
其实,当订阅者可以发出key来获取数据的时候,它已经不是一个纯粹的订阅者了,或许客户端的称谓会更合适些。
模型图:

服务器:
// // Clone server Model Two // // Lets us build this source without creating a library #include "kvsimple.c" static int s_send_single (char *key, void *data, void *args); static void state_manager (void *args, zctx_t *ctx, void *pipe); int main (void) { // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5557"); int64_t sequence = 0; srandom ((unsigned) time (NULL)); // Start state manager and wait for synchronization signal void *updates = zthread_fork (ctx, state_manager, NULL); free (zstr_recv (updates)); while (!zctx_interrupted) { // Distribute as key-value message kvmsg_t *kvmsg = kvmsg_new (++sequence); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_send (kvmsg, updates); kvmsg_destroy (&kvmsg); } printf (" Interrupted\n%d messages out\n", (int) sequence); zctx_destroy (&ctx); return 0; } // Routing information for a key-value snapshot typedef struct { void *socket; // ROUTER socket to send to zframe_t *identity; // Identity of peer who requested state } kvroute_t; // Send one state snapshot key-value pair to a socket // Hash item data is our kvmsg object, ready to send static int s_send_single (char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; // Send identity of recipient first zframe_send (&kvroute->identity, kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_t *kvmsg = (kvmsg_t *) data; kvmsg_send (kvmsg, kvroute->socket); return 0; } // This thread maintains the state and handles requests from // clients for snapshots. // static void state_manager (void *args, zctx_t *ctx, void *pipe) { zhash_t *kvmap = zhash_new (); zstr_send (pipe, "READY"); void *snapshot = zsocket_new (ctx, ZMQ_ROUTER); zsocket_bind (snapshot, "tcp://*:5556"); zmq_pollitem_t items [] = { { pipe, 0, ZMQ_POLLIN, 0 }, { snapshot, 0, ZMQ_POLLIN, 0 } }; int64_t sequence = 0; // Current snapshot version number while (!zctx_interrupted) { int rc = zmq_poll (items, 2, -1); if (rc == -1 && errno == ETERM) break; // Context has been shut down // Apply state update from main thread if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (pipe); if (!kvmsg) break; // Interrupted sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); } // Execute state snapshot request if (items [1].revents & ZMQ_POLLIN) { zframe_t *identity = zframe_recv (snapshot); if (!identity) break; // Interrupted // Request is in second frame of message char *request = zstr_recv (snapshot); if (streq (request, "ICANHAZ?")) free (request); else { printf ("E: bad request, aborting\n"); break; } // Send state snapshot to client kvroute_t routing = { snapshot, identity }; // For each entry in kvmap, send kvmsg to client zhash_foreach (kvmap, s_send_single, &routing); // Now send END message with sequence number printf ("Sending state shapshot=%d\n", (int) sequence); zframe_send (&identity, snapshot, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, snapshot); kvmsg_destroy (&kvmsg); } } zhash_destroy (&kvmap); }
客户端:
// // Clone client Model Two // // Lets us build this source without creating a library #include "kvsimple.c" int main (void) { // Prepare our context and subscriber zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_DEALER); zsocket_connect (snapshot, "tcp://localhost:5556"); void *subscriber = zsocket_new (ctx, ZMQ_SUB); zsocket_connect (subscriber, "tcp://localhost:5557"); zhash_t *kvmap = zhash_new (); // Get state snapshot int64_t sequence = 0; zstr_send (snapshot, "ICANHAZ?"); while (TRUE) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { sequence = kvmsg_sequence (kvmsg); printf ("Received snapshot=%d\n", (int) sequence); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, kvmap); } // Now apply pending updates, discard out-of-sequence messages while (!zctx_interrupted) { kvmsg_t *kvmsg = kvmsg_recv (subscriber); if (!kvmsg) break; // Interrupted if (kvmsg_sequence (kvmsg) > sequence) { sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); } else kvmsg_destroy (&kvmsg); } zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
重新发布更新
上面的模型中,数据都集中在一点,或许会有服务器崩溃而导致数据丢失的顾虑,那么,把数据放到客户端呢?
模型图:

服务器:
// // Clone server Model Three // // Lets us build this source without creating a library #include "kvsimple.c" static int s_send_single (char *key, void *data, void *args); // Routing information for a key-value snapshot typedef struct { void *socket; // ROUTER socket to send to zframe_t *identity; // Identity of peer who requested state } kvroute_t; int main (void) { // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_ROUTER); zsocket_bind (snapshot, "tcp://*:5556"); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5557"); void *collector = zsocket_new (ctx, ZMQ_PULL); zsocket_bind (collector, "tcp://*:5558"); int64_t sequence = 0; zhash_t *kvmap = zhash_new (); zmq_pollitem_t items [] = { { collector, 0, ZMQ_POLLIN, 0 }, { snapshot, 0, ZMQ_POLLIN, 0 } }; while (!zctx_interrupted) { int rc = zmq_poll (items, 2, 1000 * ZMQ_POLL_MSEC); // Apply state update sent from client if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (collector); if (!kvmsg) break; // Interrupted kvmsg_set_sequence (kvmsg, ++sequence); kvmsg_send (kvmsg, publisher); kvmsg_store (&kvmsg, kvmap); printf ("I: publishing update %5d\n", (int) sequence); } // Execute state snapshot request if (items [1].revents & ZMQ_POLLIN) { zframe_t *identity = zframe_recv (snapshot); if (!identity) break; // Interrupted // Request is in second frame of message char *request = zstr_recv (snapshot); if (streq (request, "ICANHAZ?")) free (request); else { printf ("E: bad request, aborting\n"); break; } // Send state snapshot to client kvroute_t routing = { snapshot, identity }; // For each entry in kvmap, send kvmsg to client zhash_foreach (kvmap, s_send_single, &routing); // Now send END message with sequence number printf ("I: sending shapshot=%d\n", (int) sequence); zframe_send (&identity, snapshot, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) "", 0); kvmsg_send (kvmsg, snapshot); kvmsg_destroy (&kvmsg); } } printf (" Interrupted\n%d messages handled\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; } // Send one state snapshot key-value pair to a socket // Hash item data is our kvmsg object, ready to send static int s_send_single (char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; // Send identity of recipient first zframe_send (&kvroute->identity, kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_t *kvmsg = (kvmsg_t *) data; kvmsg_send (kvmsg, kvroute->socket); return 0; }
客户端:
// // Clone client Model Three // // Lets us build this source without creating a library #include "kvsimple.c" int main (void) { // Prepare our context and subscriber zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_DEALER); zsocket_connect (snapshot, "tcp://localhost:5556"); void *subscriber = zsocket_new (ctx, ZMQ_SUB); zsocket_connect (subscriber, "tcp://localhost:5557"); void *publisher = zsocket_new (ctx, ZMQ_PUSH); zsocket_connect (publisher, "tcp://localhost:5558"); zhash_t *kvmap = zhash_new (); srandom ((unsigned) time (NULL)); // Get state snapshot int64_t sequence = 0; zstr_send (snapshot, "ICANHAZ?"); while (TRUE) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { sequence = kvmsg_sequence (kvmsg); printf ("I: received snapshot=%d\n", (int) sequence); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, kvmap); } int64_t alarm = zclock_time () + 1000; while (!zctx_interrupted) { zmq_pollitem_t items [] = { { subscriber, 0, ZMQ_POLLIN, 0 } }; int tickless = (int) ((alarm - zclock_time ())); if (tickless < 0) tickless = 0; int rc = zmq_poll (items, 1, tickless * ZMQ_POLL_MSEC); if (rc == -1) break; // Context has been shut down if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (subscriber); if (!kvmsg) break; // Interrupted // Discard out-of-sequence kvmsgs, incl. heartbeats if (kvmsg_sequence (kvmsg) > sequence) { sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); printf ("I: received update=%d\n", (int) sequence); } else kvmsg_destroy (&kvmsg); } // If we timed-out, generate a random kvmsg if (zclock_time () >= alarm) { kvmsg_t *kvmsg = kvmsg_new (0); kvmsg_fmt_key (kvmsg, "%d", randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_destroy (&kvmsg); alarm = zclock_time () + 1000; } } printf (" Interrupted\n%d messages in\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
克隆子树
事实上,并不是所有的消费者都愿意消费发布者所提供的所有信息,那么,针对特别的群体,只需提供一个子集就可以了。
服务器:
// // Clone server Model Four // // Lets us build this source without creating a library #include "kvsimple.c" static int s_send_single (char *key, void *data, void *args); // Routing information for a key-value snapshot typedef struct { void *socket; // ROUTER socket to send to zframe_t *identity; // Identity of peer who requested state char *subtree; // Client subtree specification } kvroute_t; int main (void) { // Prepare our context and sockets zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_ROUTER); zsocket_bind (snapshot, "tcp://*:5556"); void *publisher = zsocket_new (ctx, ZMQ_PUB); zsocket_bind (publisher, "tcp://*:5557"); void *collector = zsocket_new (ctx, ZMQ_PULL); zsocket_bind (collector, "tcp://*:5558"); int64_t sequence = 0; zhash_t *kvmap = zhash_new (); zmq_pollitem_t items [] = { { collector, 0, ZMQ_POLLIN, 0 }, { snapshot, 0, ZMQ_POLLIN, 0 } }; while (!zctx_interrupted) { int rc = zmq_poll (items, 2, 1000 * ZMQ_POLL_MSEC); // Apply state update sent from client if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (collector); if (!kvmsg) break; // Interrupted kvmsg_set_sequence (kvmsg, ++sequence); kvmsg_send (kvmsg, publisher); kvmsg_store (&kvmsg, kvmap); printf ("I: publishing update %5d\n", (int) sequence); } // Execute state snapshot request if (items [1].revents & ZMQ_POLLIN) { zframe_t *identity = zframe_recv (snapshot); if (!identity) break; // Interrupted // Request is in second frame of message char *request = zstr_recv (snapshot); char *subtree = NULL; if (streq (request, "ICANHAZ?")) { free (request); subtree = zstr_recv (snapshot); } else { printf ("E: bad request, aborting\n"); break; } // Send state snapshot to client kvroute_t routing = { snapshot, identity, subtree }; // For each entry in kvmap, send kvmsg to client zhash_foreach (kvmap, s_send_single, &routing); // Now send END message with sequence number printf ("I: sending shapshot=%d\n", (int) sequence); zframe_send (&identity, snapshot, ZFRAME_MORE); kvmsg_t *kvmsg = kvmsg_new (sequence); kvmsg_set_key (kvmsg, "KTHXBAI"); kvmsg_set_body (kvmsg, (byte *) subtree, 0); kvmsg_send (kvmsg, snapshot); kvmsg_destroy (&kvmsg); free (subtree); } } printf (" Interrupted\n%d messages handled\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; } // Send one state snapshot key-value pair to a socket // Hash item data is our kvmsg object, ready to send static int s_send_single (char *key, void *data, void *args) { kvroute_t *kvroute = (kvroute_t *) args; kvmsg_t *kvmsg = (kvmsg_t *) data; if (strlen (kvroute->subtree) <= strlen (kvmsg_key (kvmsg)) && memcmp (kvroute->subtree, kvmsg_key (kvmsg), strlen (kvroute->subtree)) == 0) { // Send identity of recipient first zframe_send (&kvroute->identity, kvroute->socket, ZFRAME_MORE + ZFRAME_REUSE); kvmsg_send (kvmsg, kvroute->socket); } return 0; }
客户端:
// // Clone client Model Four // // Lets us build this source without creating a library #include "kvsimple.c" #define SUBTREE "/client/" int main (void) { // Prepare our context and subscriber zctx_t *ctx = zctx_new (); void *snapshot = zsocket_new (ctx, ZMQ_DEALER); zsocket_connect (snapshot, "tcp://localhost:5556"); void *subscriber = zsocket_new (ctx, ZMQ_SUB); zsocket_connect (subscriber, "tcp://localhost:5557"); zsockopt_set_subscribe (subscriber, SUBTREE); void *publisher = zsocket_new (ctx, ZMQ_PUSH); zsocket_connect (publisher, "tcp://localhost:5558"); zhash_t *kvmap = zhash_new (); srandom ((unsigned) time (NULL)); // Get state snapshot int64_t sequence = 0; zstr_sendm (snapshot, "ICANHAZ?"); zstr_send (snapshot, SUBTREE); while (TRUE) { kvmsg_t *kvmsg = kvmsg_recv (snapshot); if (!kvmsg) break; // Interrupted if (streq (kvmsg_key (kvmsg), "KTHXBAI")) { sequence = kvmsg_sequence (kvmsg); printf ("I: received snapshot=%d\n", (int) sequence); kvmsg_destroy (&kvmsg); break; // Done } kvmsg_store (&kvmsg, kvmap); } int64_t alarm = zclock_time () + 1000; while (!zctx_interrupted) { zmq_pollitem_t items [] = { { subscriber, 0, ZMQ_POLLIN, 0 } }; int tickless = (int) ((alarm - zclock_time ())); if (tickless < 0) tickless = 0; int rc = zmq_poll (items, 1, tickless * ZMQ_POLL_MSEC); if (rc == -1) break; // Context has been shut down if (items [0].revents & ZMQ_POLLIN) { kvmsg_t *kvmsg = kvmsg_recv (subscriber); if (!kvmsg) break; // Interrupted // Discard out-of-sequence kvmsgs, incl. heartbeats if (kvmsg_sequence (kvmsg) > sequence) { sequence = kvmsg_sequence (kvmsg); kvmsg_store (&kvmsg, kvmap); printf ("I: received update=%d\n", (int) sequence); } else kvmsg_destroy (&kvmsg); } // If we timed-out, generate a random kvmsg if (zclock_time () >= alarm) { kvmsg_t *kvmsg = kvmsg_new (0); kvmsg_fmt_key (kvmsg, "%s%d", SUBTREE, randof (10000)); kvmsg_fmt_body (kvmsg, "%d", randof (1000000)); kvmsg_send (kvmsg, publisher); kvmsg_destroy (&kvmsg); alarm = zclock_time () + 1000; } } printf (" Interrupted\n%d messages in\n", (int) sequence); zhash_destroy (&kvmap); zctx_destroy (&ctx); return 0; }
(未完待续)
发表评论
-
IM选型(初)
2016-08-23 19:12 1666主要参考文章: https://r ... -
关于python和rabbitmq的那点事儿
2011-10-19 14:15 7998rabbitmq是一个消息中间件,在之前的zmq介绍中有略带提 ... -
zeroMQ初体验-34.发布/订阅模式进阶-克隆模式-下,结言
2011-05-26 16:09 4240服务器: // // Clone server Mod ... -
zeroMQ初体验-33.发布/订阅模式进阶-克隆模式-中
2011-05-26 15:37 2953临时缓存 现实中,比如 ... -
zeroMQ初体验-31.发布/订阅模式进阶-黑盒的高速订阅者
2011-05-25 16:55 2784作为发布/订阅模式的一个常用场景,大数据量的组播是有必要的。虽 ... -
zeroMQ初体验-30.发布/订阅模式进阶-自裁的蜗牛订阅者
2011-05-25 16:24 4580在初次介绍发布/订阅模式的时候,就已经抖出了这个包袱:如果订阅 ... -
zeroMQ初体验-29.可靠性-自由模式
2011-05-24 17:02 5450好吧,本以为这可能是一个更靠谱的模式,谁知(其实是我一厢情愿了 ... -
zeroMQ初体验-28.可靠性-主从模式
2011-05-23 14:47 5580虽然"硬盘模式" ... -
zeroMQ初体验-27.可靠性-硬盘模式
2011-05-23 13:44 3830在之前的种种模式中, ... -
zeroMQ初体验-26.可靠性-管家模式
2011-05-12 19:05 5685上一节末尾有说到协议,zeromq自然做了充沛的封装,&quo ... -
zeroMQ初体验-26.可靠性-管家模式
2011-05-12 19:03 1上一节末尾有说到协议,zeromq自然做了充沛的封装,&quo ... -
zeroMQ初体验-25.可靠性-偏执的海盗模式
2011-05-05 19:05 3636虽然说“简单的海盗模 ... -
zeroMQ初体验-24.可靠性-简单的海盗模式
2011-05-05 16:41 3255相较于“懒惰的”做了 ... -
zeroMQ初体验-23.可靠性-懒惰的海盗模式
2011-05-05 16:15 5100相较于通常的阻塞模式,这里只是做了一点简单的动作来加强系统的可 ... -
zeroMQ初体验-22.可靠性-总览
2011-04-26 19:25 5987在开篇就从曾对zeromq的可靠性做过质疑,不过,作为一个雄心 ... -
rabbitmq 队列长度预设的曲线方案
2011-04-21 14:36 3449zeromq中倒是直接支持这个功能的。 类似于设定队列长度或 ... -
zeroMQ初体验-21.应答模式进阶(七)-云计算
2011-04-18 19:14 3566这里给出了一个最近很火的"云计算"案例。 ... -
zeroMQ初体验-20.应答模式进阶(六)-多对多路由模式
2011-04-18 17:22 3915某些时候,为了冗余的需要,可能会有这样的需求: impo ... -
zeroMQ初体验-19.应答模式进阶(五)-异步式应答
2011-04-15 15:23 4885恩,这应该算是比较实 ... -
zeroMQ初体验-18.应答模式进阶(四)-定制路由3
2011-04-02 15:39 5217从经典到超越经典。 首 ...
相关推荐
克隆模式是一种发布-订阅模式的变体,它确保消息被可靠地传递给所有的订阅者。 #### 六、ZeroMQ社区 **ZeroMQ社区的体系结构:** ZeroMQ社区是一个由开发者组成的全球网络,他们共同维护和发展ZeroMQ项目。社区的...
内容概要:本文介绍了如何使用Python识别图片和扫描PDF中的文字。首先,文章讲解了使用Spire.OCR for Python库来识别图片中的文字,包括安装库、配置OCR模型路径和语言设置、扫描图片以及保存识别后的文本。其次,详细描述了从图片中提取文字及其坐标位置的方法,使用户不仅能够获取文本内容,还能知道文本在图片中的具体位置。最后,文章还介绍了如何结合Spire.PDF for Python将PDF文件转换为图片格式,再通过OCR技术从中提取文字,适用于处理扫描版PDF文件。文中提供了完整的代码示例,帮助读者理解和实践。 适合人群:对Python编程有一定基础,希望学习或提高光学字符识别(OCR)技术的应用开发者,尤其是需要处理大量图片或PDF文档中文字信息的工作人员。 使用场景及目标:① 开发者可以利用这些方法自动化处理图片或PDF文档中的文字信息,提高工作效率;② 实现从非结构化数据(如图片、扫描件)到结构化数据(如文本文件)的转换,便于后续的数据分析和处理;③ 提供了一种解决纸质文档数字化的有效途径,特别是对于历史档案、书籍等资料的电子化保存。 其他说明:需要注意的是,OCR的准确性很大程度上取决于图片的质量,清晰度高、对比度好的图片可以获得更好的识别效果。此外,不同OCR库可能对特定语言或字体的支持程度不同,选择合适的库和配置参数能显著提升识别精度。在实际应用中,建议先进行小规模测试,优化参数后再大规模应用。
2025中小企业数字化转型指南
1.版本:matlab2014/2019a/2024a 2.附赠案例数据可直接运行matlab程序。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。
2025-04-10 兜兜\(^o^)/~
内容概要:本文详细介绍了永磁同步电机(PMSM)无位置传感器控制中滑模观测器(SMO)的设计与仿真实现。首先阐述了系统的整体架构,包括速度环和电流环的工作机制。接着深入探讨了滑模观测器的关键实现步骤,如滑模增益选择、符号函数的应用以及低通滤波器的作用。文中还涉及了坐标变换(Clarke变换和Park变换)、PI参数整定、SVPWM调制等重要环节的具体实现方法和技术难点。此外,作者分享了多个调试过程中遇到的问题及解决方案,如波形抖振、电流采样延时、扇区判断错误等。最后展示了仿真结果,证明了所提方案的有效性和可行性。 适合人群:从事电机控制系统研究与开发的技术人员,尤其是对无位置传感器控制感兴趣的工程师。 使用场景及目标:适用于希望深入了解并掌握永磁同步电机无位置传感器控制技术的研究人员和工程师。目标是帮助读者理解滑模观测器的工作原理,掌握其设计与调试技巧,从而应用于实际项目中。 其他说明:文中提供了丰富的代码片段和实践经验,有助于读者更好地理解和应用理论知识。同时提醒读者注意离散化步长的选择和滑模增益的调整,这些都是确保系统稳定运行的重要因素。
内容概要:本文详细介绍了航天器姿态控制中的关键技术,包括时变滑模控制、自适应控制、执行器饱和处理及推力器安装偏差补偿。针对航天器在复杂环境中面临的不确定性,提出了时变滑模面设计、自适应参数更新、饱和控制和安装偏差补偿的具体实现方法,并提供了相应的MATLAB代码。通过合理的控制策略设计,确保航天器在面对多种干扰因素时仍能保持高精度的姿态控制。 适合人群:航空航天领域的研究人员、控制系统工程师、高校相关专业师生。 使用场景及目标:适用于航天器姿态控制系统的开发与优化,旨在提升系统的鲁棒性、稳定性和控制精度。具体应用场景包括卫星姿态调整、深空探测器姿态控制等。 其他说明:文中提供的MATLAB代码可以直接用于实验验证和教学演示,帮助读者深入理解各控制环节的工作原理和技术细节。此外,文中还分享了许多实用的工程经验,如参数整定技巧、常见问题处理等,有助于指导实际项目开发。
内容概要:本文详细介绍了基于Simulink-Simscape平台构建的四轮转向汽车模型预测控制(MPC)路径跟踪系统。首先,文章阐述了车辆动力学模型的核心结构,包括前轮和后轮转向角的计算方法以及魔术轮胎模型的配置。接着,深入探讨了MPC控制器的设计,特别是代价函数的配置和QP优化的具体实现。此外,还讨论了路面切换模块的设计,确保不同路况下的稳定性和响应性。最后,文章分享了一些调参经验和仿真参数设置的技巧,如积分限幅、随机质量块的应用以及解算器的选择。 适合人群:从事自动驾驶、车辆控制系统研究的工程师和技术人员,尤其是对路径跟踪算法和MPC有浓厚兴趣的研究者。 使用场景及目标:适用于开发和优化四轮转向汽车的路径跟踪系统,旨在提高车辆在各种复杂路况下的行驶稳定性和精确度。具体目标包括减小方向盘转角误差、控制横向位移偏差、优化轮胎力计算等。 其他说明:文中提供了大量MATLAB代码片段和具体的参数配置,帮助读者更好地理解和复现实验结果。同时,作者分享了许多实际调试过程中遇到的问题及其解决方案,使读者能够避开常见陷阱并高效完成模型搭建。
全国银行代码和支行代城市地区mysql
内容概要:本文详细介绍了如何利用动态规划(Dynamic Programming, DP)在MATLAB/SIMULINK环境中实现自动驾驶车辆的动态避障功能。首先,文章解释了动态规划的核心思想及其在路径规划中的应用,特别是通过状态转移方程来解决避障问题。接着,讨论了运动学模型(如自行车模型)的建立方法,以及如何通过PID和MPC控制算法进行路径跟踪和避障。此外,文章还探讨了联合仿真平台(MATLAB + Carsim + Prescan)的搭建和配置,展示了如何将理论转化为实际的仿真效果。最后,提供了完整的代码实现和调试技巧,帮助读者快速上手并优化性能。 适合人群:对自动驾驶技术和路径规划感兴趣的科研人员、工程师和技术爱好者。 使用场景及目标:适用于研究和开发自动驾驶系统,特别是在复杂环境下实现高效的动态避障功能。目标是提高车辆的安全性和智能化水平,减少人为干预。 其他说明:文中提供的代码已在GitHub上开源,读者可以直接下载并运行。需要注意的是,某些高级功能(如深度强化学习)将在后续版本中继续探索。
内容概要:本文档是《拼多多Python面试题目.pdf》,涵盖了Python编程语言的面试题目及其参考答案。文档分为四个主要部分:选择题、填空题、代码编程题和综合题。选择题涉及Python基础知识、多线程、Redis使用场景、字符编码转换、协程特性、深拷贝操作等;填空题考察了列表推导式、对象序列化、文件操作、字符串处理等知识点;代码编程题包括统计热门商品、订单时间窗口校验、字符串压缩、异步批量请求、最长递增子序列等实际编程任务;综合题则要求设计一个高并发秒杀系统,涵盖缓存、数据库、消息队列的选择,库存扣减、分布式锁、限流策略等关键模块的实现思路,以及解决缓存相关问题的方法。 适合人群:有一定Python编程基础,准备求职或希望提升技术能力的开发者,尤其是对Python高级特性和实际应用感兴趣的工程师。 使用场景及目标:①帮助面试者熟悉Python常见知识点和技巧,提高面试成功率;②通过实际编程题目的练习,加深对Python语法和标准库的理解;③掌握高并发系统的架构设计思路,培养解决复杂业务问题的能力。 阅读建议:此文档不仅包含理论知识的选择题和填空题,还有实际编程题目的训练,建议读者在学习过程中不仅要关注正确答案,更要理解每个选项背后的原理,同时动手实践编程题目,结合实际案例进行思考和总结。
内容概要:本文档《Java 算法面试题目.pdf》涵盖了 Java 编程语言的核心知识点和常见面试题,分为选择题、填空题、代码编程题和综合题四个部分。选择题涉及 Java 内存模型、字符串处理、集合类、垃圾回收、线程池、设计模式、Spring 事务、流操作、类加载机制、锁机制、Netty 和 Redis 持久化等主题。填空题考察了线程同步、HashMap 负载因子、序列化、数学运算、Spring 注入、字符串对象创建、位移运算、字符串方法、动态代理和 JVM 参数配置等细节。代码编程题要求实现链表反转、线程安全单例模式、快速排序、二叉树层序遍历和 LRU 缓存等功能。综合题则要求设计一个无人机实时控制系统,涵盖并发控制、可靠传输、路径规划、高可用性和低延迟等方面。; 适合人群:具有 Java 编程基础,尤其是准备面试的中高级开发人员,以及对 Java 核心技术和框架有深入理解需求的技术人员。; 使用场景及目标:①帮助开发者复习和巩固 Java 基础知识和常见面试考点;②通过编程题提升实际编码能力;③通过综合题锻炼系统设计和架构思维,掌握分布式系统的设计要点。; 阅读建议:建议读者先复习相关知识点,再尝试解答题目,最后对照参考答案进行查漏补缺。对于综合题,应结合实际项目经验思考解决方案,重点理解系统设计的关键点和技术选型的理由。
内容概要:该专利提出了一种节能高效的双螺杆压缩机转子型线设计方案,属于动力传动与控制技术领域。核心创新点在于阴阳转子的齿曲线采用抛物线、圆弧、椭圆及其共轭包络线组合而成,形成二次曲线与二次曲线包络线的组合型线。设计特点包括非对称齿面(前齿面更宽,b/a>1.8)、各段曲线光滑连接无尖点、七段曲线组合结构。该设计旨在提高密封性、改善动力特性、降低损耗、提升效率,从而提高双螺杆压缩机的整体性能。文中提供了详细的Python代码实现,包括转子型线的计算和可视化,以及改进后的代码,以更好地反映专利的具体参数和技术细节。 适合人群:机械工程专业人员、从事压缩机设计与制造的技术人员、对双螺杆压缩机转子型线设计感兴趣的科研人员。 使用场景及目标:①用于研究和开发新型双螺杆压缩机,特别是在提高压缩机效率和性能方面;②作为教学案例,帮助学生和工程师理解双螺杆压缩机转子型线的设计原理和技术实现;③为企业提供参考,优化现有产品的设计和制造工艺。 其他说明:文中提供的代码基于专利描述进行了合理的假设和简化,实际应用中可能需要根据具体性能要求进行优化调整。专利技术通过非对称设计、多段曲线组合等方式,实现了高效的密封性和优良的动力特性,显著提升了双螺杆压缩机的性能。
内容概要:本文详细介绍了利用PFC3D5.0进行滑坡冲击建筑物仿真的全流程,涵盖滑坡体和建筑物的建模、参数设定、监测系统的构建以及灾后损伤评估。文中不仅提供了完整的Python代码示例,还解释了各个关键参数的选择依据及其对仿真结果的影响。通过实例展示了如何设置滑坡体的尺寸、密度、阻尼系数等属性,以及建筑物的材料属性、粘结强度等参数。同时,文章强调了实时监测系统的重要性,包括冲击力传感器的布置和预警机制的设计。此外,还探讨了不同条件下建筑物的易损性曲线生成方法及其应用价值。 适合人群:从事地质灾害研究、结构工程分析的专业人士,尤其是那些希望深入了解滑坡冲击建筑物过程并掌握相关数值模拟技术的研究人员和技术人员。 使用场景及目标:适用于需要进行滑坡灾害风险评估、建筑设计优化以及应急响应规划的项目。主要目标是帮助用户理解和预测滑坡对建筑物造成的潜在损害,从而提高建筑物的安全性和耐久性。 其他说明:文中提供的代码和参数设置均经过多次实验验证,确保了较高的准确性。对于想要进一步探索这一领域的读者来说,本文提供了一条从理论到实践的学习路径。
修复版本,改了短信接口到阿里云,现在好像国内短信接口管的最松的反而是阿里了,支付也已经接好了派特了。可以直接开户直接用了,带非常非常非常完整的搭建教程,萌新都可以随便装起来了。
内容概要:本文探讨了在自动驾驶领域中,利用Matlab/Simulink 2018b和Carsim 2020构建ACC(自适应巡航控制)系统的分层PID控制模型的方法和技术要点。首先介绍了软件选择的原因及其版本要求,强调了版本兼容性的重要性。接着详细讲解了模块化建模方法的应用,包括单独的Carsim配置文件、电机驱动模块、车辆巡航模块、车辆跟踪模块、切换逻辑、速度跟踪模块以及联合仿真模块等七个关键组成部分的作用和实现方式。文中提供了具体的MATLAB代码片段展示上层和下层PID控制器的工作机制,并分享了一些实用的小技巧,如安全距离动态补偿、积分限幅处理、死区设置等。此外,还讨论了联合仿真的注意事项,如接口版本匹配、远程调试设置等问题。最后提到模型验证的有效测试案例,并指出整套源码中最值得关注的部分是状态机实现。 适合人群:对自动驾驶技术感兴趣的科研人员、工程师以及相关专业的学生,尤其是那些希望深入了解ACC系统内部工作机制的人群。 使用场景及目标:①帮助读者掌握如何使用Matlab/Simulink和Carsim建立并优化ACC巡航控制系统;②提高读者对PID控制理论的理解及其在实际工程项目中的应用能力;③为从事智能交通系统研究的专业人士提供有价值的参考资料。 其他说明:本文不仅涵盖了理论知识,还包括大量实践经验分享,有助于初学者快速入门的同时也为资深从业者带来新的思考角度。
内容概要:本文详细介绍了基于西门子S7-1200 PLC和TIA Portal V16平台的五层电梯仿真系统。该系统利用博图V16的仿真环境,无需实体PLC即可实现电梯的动态动画运行。核心内容涵盖PLC程序的状态机设计、呼叫处理模块、HMI组态画面的设计以及安全回路处理等方面。文中展示了具体的代码片段,如电梯状态机的嵌套状态切换逻辑、呼叫信号的位操作处理、HMI动画的位置计算等。此外,还提到了一些实用技巧,如优化响应速度的方法、动画效果的实现细节以及仿真过程中可能遇到的问题及其解决方案。 适合人群:工控领域的工程师、培训讲师、学生及其他对电梯控制系统感兴趣的人员。 使用场景及目标:①作为教学工具,帮助学员理解和掌握电梯控制系统的逻辑设计;②作为验证工具,用于测试和优化电梯控制系统的性能;③为实际工程项目提供参考,减少硬件调试的时间和成本。 其他说明:该仿真系统不仅提供了完整的PLC程序和HMI项目文件,还包括详细的仿真参数配置指南和故障模拟功能,使得教学和实验更加生动有趣。
内容概要:本文详细介绍了Bandgap带隙基准电路的设计、仿真及其优化技巧。首先解释了启动电路的重要性和具体实现方式,确保电路能够稳定启动。接着深入探讨了如何通过仿真工具(如Cadence、Spectre等)进行抛物线曲线、电源抑制比(PSR)、稳定性等方面的仿真测试。提供了具体的Verilog、TCL和Spectre代码片段,帮助读者理解和实践这些复杂的电路特性。此外,还分享了一些实用的经验和技巧,如如何调整电阻比例、设置合适的仿真参数、处理潜在的振荡问题等。 适合人群:具有一定模拟电路基础知识的工程师和技术爱好者,尤其是对Bandgap带隙基准电路感兴趣的读者。 使用场景及目标:适用于希望深入了解Bandgap带隙基准电路的工作原理并掌握其仿真方法的人群。目标是在实践中提高电路设计能力,解决实际工程中的问题。 其他说明:文中不仅提供了理论知识,还包括大量实际操作步骤和代码示例,使读者能够在动手实践中加深理解。同时,强调了在GPDK45nm工艺下的特殊注意事项,为从事先进工艺电路设计的研究人员提供宝贵参考资料。
人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明,个人大四的毕业设计、经导师指导并认可通过的高分设计项目,评审分99分,代码完整确保可以运行,小白也可以亲自搞定,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的学习者,也可作为课程设计、期末大作业。 人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTorch实现高分遥感语义分割(地物分类)项目源码+文档说明人工智能-遥感-语义分割-PyTo
路宁-驾驭大模型开发真实项目代码.pdf