presence

package module
v0.0.0-...-943ffb0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 8, 2014 License: MIT Imports: 8 Imported by: 1

README

Presence GoDoc Build Status

Presence in Redis

One of the most important and resource intensive operation in chat systems is handling user presence. Because in order to start the chat one should provide the presence information. And keeping this data up-to-date is another challenge.

There are 3 main parts for handling user presence,

  • Notification
  • Persistency
  • Data source
Starting with number one:

If we calculate the message count with a standart architecture for sending notifications to the online users: count = (online user count) * (channel participant count) * (events), since every part is a multiplication even increasing by one will cause huge effects.

Number two:

Transient presence data should be commited and updated regularly, after an inactive duration presence data should be set to offline either by the application or by the persistency layer

Number three:

Online clients should notify the presence system with their unique identifier repeatedly. We can call it as heartbeat.

"This package aims to handle the persistency layer for presence"

For usage see examples below or go to godoc page.

Install and Usage

Install the package with:

go get github.com/cihangir/presence

Import it with:

import "github.com/cihangir/presence"

Examples

Initialization of a new Redisence

// create a presence system
backend, err := NewRedis(serverAddr, dbNumber, timeoutDuration)
if err != nil {
    return err
}

s, err := New(backend)
if err != nil {
    return err
}

Basic Operations

// send online presence data to system - user log in
err = s.Online("id")
err = s.Online("id2")


// send offline presence data to system - user log out
err = s.Offline("id")
err = s.Offline("id2")

// get status of some ids
status, err := s.Status([]string{"id20", "id21"}...)
if err != nil {
    return err
}

for _, st := range status {
    if st.Status != Offline {
        //....
    }
}

Listening for events

go func() {
    s.Online("id")
    time.Sleep(time.Second * 1)
    s.Online("id")
    s.Online("id2")
}()

// start listening to them
for event := range s.ListenStatusChanges() {
    switch event.Status {
    case Online:
        // ....
    case Offline:
        // ....
    }
}

Redis configuration

To get the events from the redis database we should uptade the redis config with the following data

redis-cli config set notify-keyspace-events Ex$

Or set in redis.conf notify-keyspace-events "Ex$"

for more info http://redis.io/topics/notifications

License

The MIT License (MIT) - see LICENSE for more details

Documentation

Overview

Package presence provides an advanced presence system

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// Prefix for presence package
	Prefix = "presence"

	// ErrInvalidID for stating the event id is not valid
	ErrInvalidID = errors.New("invalid id")

	// ErrInvalidStatus for stating the event status is not valid
	ErrInvalidStatus = errors.New("invalid status")
)

Functions

This section is empty.

Types

type Backend

type Backend interface {
	Online(...string) error
	Offline(...string) error
	Status(...string) ([]Event, error)
	Close() error
	Error() chan error
	ListenStatusChanges() chan Event
}

Backend represents basic interface for all required backend operations for presence package

func NewRedis

func NewRedis(
	server string,
	db int,
	inactiveDuration time.Duration,
) (Backend, error)

NewRedis creates a Redis presence system

type Error

type Error map[string]error

Error holds the non-thread safe errors for given specific ids

func (*Error) Append

func (m *Error) Append(id string, err error)

Append adds an error to the aggregated errors with an id

func (*Error) Each

func (m *Error) Each(f func(id string, err error))

Each iterates over error set with calling the given function

func (Error) Error

func (m Error) Error() string

Error implements the error interface

func (*Error) Has

func (m *Error) Has(id string) bool

Has checks if the Error has an error for the given id

func (*Error) Len

func (m *Error) Len() int

Len returns the registered error count

type Event

type Event struct {
	// ID is the given key by the application
	ID string

	// Status holds the changing type of event
	Status Status
}

Event is the data type for occuring events in the system

type Redis

type Redis struct {
	// contains filtered or unexported fields
}

Redis holds the required connection data for redis

Example (ListenStatusChanges)
connStr := os.Getenv("REDIS_URI")
if connStr == "" {
	connStr = "localhost:6379"
}

backend, err := NewRedis(connStr, 10, time.Second*1)
if err != nil {
	fmt.Println(err.Error())
}

// adjust config for redis instance
c := backend.(*Redis).redis.Pool().Get()
if _, err := c.Do("CONFIG", "SET", "notify-keyspace-events", "Ex$"); err != nil {
	fmt.Println(err)
}

if err := c.Close(); err != nil {
	fmt.Println(err)
}

s, err := New(backend)
if err != nil {
	fmt.Println(err.Error())
}

go func() {
	for event := range s.ListenStatusChanges() {
		switch event.Status {
		case Online:
			fmt.Println(event)
		case Offline:
			fmt.Println(event)
		}
	}
}()

go func() {
	s.Online("id")
}()

// wait for events
<-time.After(time.Second * 2)
Output:

{id ONLINE}
{id OFFLINE}

func (*Redis) Close

func (s *Redis) Close() error

Close closes the redis connection gracefully

func (*Redis) Error

func (s *Redis) Error() chan error

Error returns error if it happens while listening to status changes

func (*Redis) ListenStatusChanges

func (s *Redis) ListenStatusChanges() chan Event

ListenStatusChanges subscribes with a pattern to the redis and gets online and offline status changes from it

func (*Redis) Offline

func (s *Redis) Offline(ids ...string) error

Offline sets given ids as offline

func (*Redis) Online

func (s *Redis) Online(ids ...string) error

Online resets the expiration time for any given key. If key doesnt exists, it means key (user) become online and should be set as online. Whenever application gets any probe from a client should call this function. This method performs way better when there is a throttling mechanism implemented on top of it, please refer to benchmarks

func (*Redis) Status

func (s *Redis) Status(ids ...string) ([]Event, error)

Status returns the current status of multiple keys from system

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session holds the backend and provides accessor methods for communication

func New

func New(backend Backend) (*Session, error)

New creates a session for any broker system that is architected to use, communicate, forward events to the presence system

func (*Session) Close

func (s *Session) Close() error

Close closes the backend connection gracefully

func (*Session) Error

func (s *Session) Error() chan error

Error returns error if it happens while listening to status changes

func (*Session) ListenStatusChanges

func (s *Session) ListenStatusChanges() chan Event

ListenStatusChanges subscribes the backend and gets online and offline status changes from it

func (*Session) Offline

func (s *Session) Offline(ids ...string) error

Offline sets given ids as offline

func (*Session) Online

func (s *Session) Online(ids ...string) error

Online sets given ids as online

func (*Session) Status

func (s *Session) Status(ids ...string) ([]Event, error)

Status returns the current status of multiple keys from system

type Status

type Status int

Status defines what is the current status of a user in presence system

const (
	// Unknown is for errored requests
	Unknown Status = iota

	// Offline is for displaying user as offline in the system
	Offline // do not handle unset variable as offline

	// Online is for displaying user as online in the system
	Online
)

func (Status) String

func (s Status) String() string

String implements the Stringer interface

Directories

Path Synopsis
example
client
Package main show a simple client implementation
Package main show a simple client implementation

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL