The ability to use the Hiredis asynchronous API for Sub/pub messaging subscriptions and releases under Linux

Source: Internet
Author: User
Tags semaphore redis server

 recently, using Redis's C interface--hiredis, which enables clients to communicate with Redis servers for message subscription and publishing (PUB/SUB), I've listed some of the problems and workarounds that I've encountered for everyone to learn. don't say much nonsense, put the code first.
Redis_publisher.h
/************************************************************************* > File name:redis_publisher.h > Au Thor:chenzengba > Mail: [email protected] > Created time:sat Apr 10:15:09 PM CST > DESCR Iption: Encapsulates Hiredis, enabling messages to be published to Redis features ************************************************************************/# ifndef redis_publisher_h#define redis_publisher_h#include <stdlib.h> #include 
Redis_publisher.cpp
/************************************************************************* > File Name:redis_publisher.cpp > Author:chenzengba > Mail: [email protected] > Created time:sat Apr 10:15:09 PM CST > De Scription: ************************************************************************/#include <stddef.h># Include <assert.h> #include <string.h> #include "redis_publisher.h" Credispublisher::credispublisher (): _ Event_base (0), _event_thread (0), _redis_context (0) {}credispublisher::~credispublisher () {}bool CRedisPublisher::    Init () {//Initialize the event _event_base = Event_base_new ();        Create Libevent Object if (NULL = = _event_base) {printf (": Create Redis event failed.\n");    return false;    } memset (&_event_sem, 0, sizeof (_EVENT_SEM));    int ret = sem_init (&_event_sem, 0, 0);        if (ret! = 0) {printf (": Init sem failed.\n");    return false; } return true; BOOL Credispublisher::uniNIT () {_event_base = NULL;       Sem_destroy (&_event_sem); return true;}    BOOL Credispublisher::connect () {//connect Redis _redis_context = Redisasyncconnect ("127.0.0.1", 6379);        Asynchronously connects to the Redis server, using the default port if (NULL = = _redis_context) {printf (": Connect Redis failed.\n");    return false;  } if (_redis_context->err) {printf (": Connect Redis Error:%d,%s\n", _redis_context->err,    _REDIS_CONTEXT-&GT;ERRSTR);    Output error message return false;    }//Attach the event Redislibeventattach (_redis_context, _event_base); Bind events to Redis context so that callbacks set to Redis are associated with events//create event-handling thread int ret = pthread_create (&_event_thread, 0, &credi    Spublisher::event_thread, this);        if (ret! = 0) {printf (": Create Event Thread failed.\n");        Disconnect ();    return false; }//Sets the connection callback, which is called after the server processes the connection request after the asynchronous call, notifies the caller of the status of the connection Redisasyncsetconnectcallback (_redis_context, &credispublisher :: ConnecT_callback)///Set disconnect callback, when the server disconnects, notifies the caller that the connection is broken, the caller can use this function to implement the reconnection Redisasyncsetdisconnectcallback (_redis_context, &amp ;    Credispublisher::d isconnect_callback);//Start Event thread sem_post (&AMP;_EVENT_SEM); return true;}        BOOL Credispublisher::d isconnect () {if (_redis_context) {redisasyncdisconnect (_redis_context);        Redisasyncfree (_redis_context);    _redis_context = NULL; } return true; BOOL Credispublisher::p ublish (const std::string &channel_name, const std::string &message) {int ret = Redisa Synccommand (_redis_context, &credispublisher::command_callback, this, "PUBLISH%s%s", channel_name.c_    STR (), MESSAGE.C_STR ());        if (Redis_err = = ret) {printf ("Publish command failed:%d\n", ret);    return false; } return true;    void Credispublisher::connect_callback (const redisasynccontext *redis_context, int status) {if (Status! = REDIS_OK)    {printf (": Error:%s\n", REDIS_CONTEXT-&GT;ERRSTR);  }  else {printf (": Redis connected!\n"); }}void credispublisher::d isconnect_callback (const redisasynccontext *redis_context, int status) {if (Status! = Redi    S_OK) {//Here abnormally exits, you can try to re-connect printf (": Error:%s\n", REDIS_CONTEXT-&GT;ERRSTR);    }}//message receive callback function void Credispublisher::command_callback (Redisasynccontext *redis_context, void *reply, void *privdata) {        printf ("Command callback.\n");//Do not perform any operations}void *credispublisher::event_thread (void *data) {if (NULL = = data) {        printf (": error!\n");        ASSERT (FALSE);    return NULL;    } credispublisher *self_this = Reinterpret_cast<credispublisher *> (data); return Self_this->event_proc ();} void *credispublisher::event_proc () {sem_wait (&AMP;_EVENT_SEM);//Turn on event distribution, Event_base_dispatch block Event_base_    Dispatch (_event_base); return NULL;}

Redis_subscriber.h
/************************************************************************* > File name:redis_subscriber.h > A Uthor:chenzengba > Mail: [email protected] > Created time:sat Apr 10:15:09 PM CST > Desc Ription: Package Hiredis for message subscription Redis functionality ************************************************************************/# ifndef redis_subscriber_h#define redis_subscriber_h#include <stdlib.h> #include 
Redis_subscriber.cpp:
/************************************************************************* > File Name:redis_subscriber.cpp ; Author:chenzengba > Mail: [email protected] > Created time:sat Apr 10:15:09 PM CST > Des Cription: ************************************************************************/#include <stddef.h># Include <assert.h> #include <string.h> #include "redis_subscriber.h" Credissubscriber::credissubscriber ( ): _event_base (0), _event_thread (0), _redis_context (0) {}credissubscriber::~credissubscriber () {}bool    Credissubscriber::init (const NOTIFYMESSAGEFN &AMP;FN) {//Initialize the event _NOTIFY_MESSAGE_FN = FN;    _event_base = Event_base_new ();        Create Libevent Object if (NULL = = _event_base) {printf (": Create Redis event failed.\n");    return false;    } memset (&_event_sem, 0, sizeof (_EVENT_SEM));    int ret = sem_init (&_event_sem, 0, 0);    if (ret! = 0) {printf (": Init sem failed.\n");    return false; } return true;        BOOL Credissubscriber::uninit () {_event_base = NULL;       Sem_destroy (&_event_sem); return true;}    BOOL Credissubscriber::connect () {//connect Redis _redis_context = Redisasyncconnect ("127.0.0.1", 6379);        Asynchronously connects to the Redis server, using the default port if (NULL = = _redis_context) {printf (": Connect Redis failed.\n");    return false;  } if (_redis_context->err) {printf (": Connect Redis Error:%d,%s\n", _redis_context->err,    _REDIS_CONTEXT-&GT;ERRSTR);    Output error message return false;    }//Attach the event Redislibeventattach (_redis_context, _event_base); Bind events to Redis context so that callbacks set to Redis are associated with events//create event-handling thread int ret = pthread_create (&_event_thread, 0, &credi    Ssubscriber::event_thread, this);        if (ret! = 0) {printf (": Create Event Thread failed.\n");        Disconnect ();    return false; }//Sets the connection callback, which is called after the server processes the connection request when the connection is called asynchronously, notifying the caller of the status of the connection redisasyncsetcOnnectcallback (_redis_context, &credissubscriber::connect_callback);//Set the disconnect callback, when the server disconnects, notifies the caller that the connection is broken, The caller can use this function to implement the re-connected Redisasyncsetdisconnectcallback (_redis_context, &credissubscriber::d isconnect_callback);//Kai    Dynamic event thread Sem_post (&AMP;_EVENT_SEM); return true;}        BOOL Credissubscriber::d isconnect () {if (_redis_context) {redisasyncdisconnect (_redis_context);        Redisasyncfree (_redis_context);    _redis_context = NULL; } return true;         BOOL Credissubscriber::subscribe (const std::string &channel_name) {int ret = Redisasynccommand (_redis_context,    &credissubscriber::command_callback, this, "SUBSCRIBE%s", Channel_name.c_str ());        if (Redis_err = = ret) {printf ("Subscribe command failed:%d\n", ret);    return false;    } printf (": Subscribe success:%s\n", Channel_name.c_str ()); return true;} void Credissubscriber::connect_callback (const redisasynccontext *redis_context, int status) {if(Status! = REDIS_OK)    {printf (": Error:%s\n", REDIS_CONTEXT-&GT;ERRSTR);    } else {printf (": Redis connected!"); }}void credissubscriber::d isconnect_callback (const redisasynccontext *redis_context, int status) {if (Status! = RED    IS_OK) {//This exits unexpectedly, you can try to re-connect printf (": Error:%s\n", REDIS_CONTEXT-&GT;ERRSTR);    }}//message receive callback function void Credissubscriber::command_callback (Redisasynccontext *redis_context, void *reply, void *privdata) { if (NULL = = Reply | |    NULL = = Privdata) {return; }//static functions, to use the class's member variables, the current this pointer is passed in, with the this pointer indirectly access Credissubscriber *self_this = Reinterpret_cast<credissubscriber *    > (privdata); Redisreply *redis_reply = reinterpret_cast<redisreply *> (reply);//Subscribe to the received message is an array with triples if (Redis_reply->type = = Redis_reply_array && redis_reply->elements = = 3) {printf (": Recieve message:%s:%d:%s:%d:%s:%d\n    ", Redis_reply->element[0]->str, Redis_reply->element[0]->len,    Redis_reply->element[1]->str, Redis_reply->element[1]->len, Redis_reply->element[2]->str, red Is_reply->element[2]->len);//Call the Function object to notify the outer self_this->_notify_message_fn of the message (redis_reply->element[1]-&    Gt;str, Redis_reply->element[2]->str, Redis_reply->element[2]->len);        }}void *credissubscriber::event_thread (void *data) {if (NULL = = data) {printf (": error!\n");        ASSERT (FALSE);    return NULL;    } credissubscriber *self_this = Reinterpret_cast<credissubscriber *> (data); return Self_this->event_proc ();} void *credissubscriber::event_proc () {sem_wait (&AMP;_EVENT_SEM);//Turn on event distribution, Event_base_dispatch block Event_base_    Dispatch (_event_base); return NULL;}

The problem 1:hiredis website does not have an implementation example of an asynchronous interface. Hiredis provides several APIs for asynchronous communication, starting with an understanding of the API's name, we implemented the ability to connect, subscribe to, and publish to the Redis server, and in practice, the program did not, as we expected, have anything to do except to establish a connection. A lot of information on the Internet, the original Hiredis asynchronous implementation is through the event to distribute Redis sent over the message, Hiredis can use any one of libae, Libev, LIBUV and libevent to achieve the distribution of events, the online information prompts the use of libae , Libev and LIBUV may occur other problems, here for the convenience of the selection of libevent.Hireds Official website does not have any introduction to the Libevent, also does not explain the use asynchronous mechanism needs to introduce the event the interface, so a lot of detours began to go. About the use of libevent here will not repeat, details can be seen Libevent official website. Libevent official website: Http://libevent.org/libevent API Document: Https://www.monkey.org/~provos/libevent/doxygen-2.0.1/include_ 2event2_2event_8h.html#6e9827de8c3014417b11b48f2fe688ae
Initialization of Credispublisher and Credissubscriber: Initialize event handling and get an instance of event handling:
_event_base = Event_base_new ();

After obtaining Redisasynccontext *, call the
Redislibeventattach (_redis_context, _event_base);
This associates the event processing with Redis, and finally calls on another thread.
Event_base_dispatch (_event_base);
The launch event is distributed, which is a blocking function, so a new thread-handling event distribution is created, and it's worth noting that the _event_sem control thread starts with a semaphore, intended to be called by the program
    Redisasyncsetconnectcallback (_redis_context,         &credissubscriber::connect_callback);    Redisasyncsetdisconnectcallback (_redis_context,        &credissubscriber::d isconnect_callback);
After that, the two callbacks can be captured completely.
question 2 peculiar ' ERR only (P) SUBSCRIBE/(p) unsubscribe/quit allowed in this context ' ERRORSome people think that the two-class design is a bit redundant, we found that Credispublisher and credissubscriber a lot of logic is the same, why not integrate them into a class, both can publish messages and can subscribe to messages. In fact, this is how I did it at first, when I found it in use,messages are subscribed and published with the same Redisasyncontex * object, and the Redis service connection is automatically disconnected, the Disconnect_callback callback is called and returns a strange error: ERR only (P) SUBSCRIBE/(p) unsubscribe/quit allowed in the this context, therefore, You cannot use the same Redisasynccontext * object for publishing and subscriptions.        To reduce the complexity of the design, the logic of the two classes is separated. Of course, you can also abstract the same logic into a base class and implement the publish and subscribe interfaces.
Issue 3 related dependent librariesBefore compiling, I need to install the Hiredis, libevent and boost libraries, I am using the Ubuntu x64 system. Hiredis official website: Https://github.com/redis/hiredis Download source extract, go to unzip directory, execute make && make install command. Libevent official website: http://libevent.org/Download the latest stable version to unzip the directory and execute the command./configure-prefix=/usrsudo make && make Installboost Library: Install directly: sudo apt-get install Libboost-dev If you do not use the Std::tr1::function function object to give the outer notification message, you do not need the boost library. You can implement the callback in the form of an interface, pass the interface to the Credissubscribe class, and let it invoke the interface callback after receiving the message, notifying the outer layer.
question 4 How to useFinally, the example code is posted. Publisher.cpp, implementing the Publish message:
/*************************************************************************    > File Name:publisher.cpp    > Author:chenzengba    > Mail: [email protected]     > Created time:sat Apr 12:13:24 PM CST ********** /#include "redis_publisher.h" int main (int argc, Char *argv[]) {    credispublisher publisher;    BOOL ret = Publisher.init ();    if (!ret)     {        printf ("Init failed.\n");        return 0;    }    ret = Publisher.connect ();    if (!ret)    {        printf ("Connect failed.");        return 0;    }    while (true)    {        publisher.publish ("Test-channel", "test Message");        Sleep (1);    }    Publisher.disconnect ();    Publisher.uninit ();    return 0;}

Subscriber.cpp Implementing a subscription message:
/************************************************************************* > File Name:subscriber.cpp > Autho R:chenzengba > Mail: [email protected] > Created time:sat Apr 12:26:42 PM CST **************** /#include "redis_subscriber.h" void Recieve_message (const Char *channel_name, const char *message, int len) {printf ("Recieve message:\n Channel name:%s\n Message:%s\n ", channel_name, message);}    int main (int argc, char *argv[]) {Credissubscriber subscriber; CREDISSUBSCRIBER::NOTIFYMESSAGEFN fn = bind (Recieve_message, std::tr1::p laceholders::_1, std::tr1::p lacehol    Ders::_2, STD::TR1::p laceholders::_3);    BOOL ret = Subscriber.init (FN);        if (!ret) {printf ("Init failed.\n");    return 0;    } ret = Subscriber.connect ();        if (!ret) {printf ("Connect failed.\n");    return 0;  } subscriber.subscribe ("Test-channel");  while (true) {sleep (1);    } subscriber.disconnect ();    Subscriber.uninit (); return 0;}

Questions about compiling: compiling in g++,Note To add the-lhiredis-levent parameter, here is a simple makefile:
Exe=server_main CLIENT_MAINCC=G++FLAG=-LHIREDIS-LEVENTOBJ=REDIS_PUBLISHER.O PUBLISHER.O REDIS_SUBSCRIBER.O subscriber.oall:$ (EXE) $ (EXE): $ (OBJ) $ (cc)-O publisher redis_publisher.o PUBLISHER.O $ (FLAG) $ (cc)-O subscriber Redis_ SUBSCRIBER.O SUBSCRIBER.O $ (FLAG) Redis_publisher.o:redis_publisher.hredis_subscriber.o:redis_ subscriber.hpublisher.o:publisher.cpp$ (CC)-C publisher.cppsubscriber.o:subscriber.cpp$ (CC)-C Subscriber.cppclean : RM Publisher Subscriber *.O


Acknowledgements: Redis Asynchronous API uses LIBEVENT:HTTP://WWW.TUICOOL.COM/ARTICLES/N73UUU


The ability to use the Hiredis asynchronous API for Sub/pub messaging subscriptions and releases under Linux

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.