Сайт знакомств на Angular и Django. Чат-мессенжер по типу телеграм./ Использование сервера Centrifuge для сокет-сообщений.

Использование сервера Centrifuge для сокет-сообщений.

Установка сервера.

link to download

Загружаем centrifugo_2.5.1_linux_386.tar.gz под Ubuntu.

Распаковываем архив.

Следующей командой генерируем конфигурационный файл.

./centrifugo genconfig

Запуск сервера с веб-интерфейсом на 9999 порту.

./centrifugo --config=config.json -p 9999 --log_level=debug --admin

Вид веб-интерфейса.

admin

admin

admin

Генерация jwt токена для python клиента.

pip install pyjwt

Создадим маленький скриптик генерации gettoken.py

import jwt

token = jwt.encode({"sub": "42"}, "41355f96-1c88-468e-862f-db1e7546281e").decode()

print(token)

Где 1355f96-1c88-468e-862f-db1e7546281e берем из ранее сгенерированных настроек центрифуги config.json.

Отправка сообщения в центрифугу.

pip install cent

from cent import Client

url = "http://localhost:9999"
api_key = "e904b803-f695-4286-8af1-063b346a3955"

client = Client(url, api_key=api_key, timeout=1)
channel = "news"
data = {"input": "test"}
client.publish(channel, data)

Фронтенд. Прием сообщений из js.

Установка библиотеки-клиента.

npm install centrifuge --save

Пример компонента с коннектом и подпиской на событие поступления сообщения.

import * as Centrifuge from 'centrifuge';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent {
  message = 'test message';
  centrifuge: any;

  constructor(private cent: CentrifugeService) {

    this.centrifuge = new Centrifuge('ws://localhost:9999/connection/websocket');
    this.centrifuge.setToken('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI0MiJ9.H9s-s6jxXo4jK6zJLEnp73S9hss94TEoSPXPnoikeRs');
    this.centrifuge.subscribe("news", function(message) {
      console.log(message);
    });
    this.centrifuge.connect();
  }

}

Генерируем сервис под центрифугу.

ng g s services/cent --project=core

Добавим url сервера центрифуги и токен в окружение core/src/environments/environment.ts

export const environment = {
  production: false,
  backendUrl: 'http://localhost:7777/',
  centUrl: 'ws://localhost:9999/connection/websocket',
  centToken: 'eyJ0eXAiOi....'
};

Опишем сервис.

import { Subscription } from 'rxjs';
import { Injectable } from '@angular/core';


import {environment} from '../../environments/environment';
import * as Centrifuge from 'centrifuge';
import { SessionService } from './session.service';

// Store
import { Store } from '@ngrx/store';
import * as chatMessageActions from './../../store/actions/chat-message.action';
import { ChatMessageState } from './../../store/states/chat-message.state';


@Injectable({
  providedIn: 'root'
})
export class CentService {

  centrifuge: any;
  token: string;
  cent_subscription: Subscription;

  constructor(
    private sessionService: SessionService,
    private chatMessageStore: Store<ChatMessageState>
    ) {
    // this.set_reconnector();
    this.connect();
  }



  connect() {
    this.token = this.sessionService.getToken();
    if (this.token) {
      console.log('Centrifugo connection');
      this.centrifuge = new Centrifuge(environment.centUrl);
      this.centrifuge.setToken(environment.centToken);
      this.centrifuge.connect();
      this.dispatcher();
      this.centrifuge.on('disconnect', (context) => {
          console.log('Disconnect....');
      });
      this.centrifuge.on('connect', (context) => {
        console.log('Connection succesfull.');
      });
    }

  }

  dispatcher(){
    this.cent_subscription = this.centrifuge.subscribe(this.token, (message) => {
      console.log(message);
      if ( message.data.type === 'chat_message') {
        this.chatMessageStore.dispatch(new chatMessageActions.UpdateChatMessage(message.data.message));
      }
    });
  }

  disconnect(){
    console.log('Centrifugo disconnection');
    this.centrifuge.disconnect();
  }
}

Для того, чтобы этот сервис был синглтоном т.е. единственным на все приложение необходимо чтобы он был включен в такой модуль, который импортируется один раз в корневом модуле приложения.

Т.к. core модуль у нас импортируется везде то мы не можем включить в него этот сервис.

Поэтому для этого создадим новый модуль projects/core/src/lib/singletone.module.ts.

import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';

import { CentService } from './services/cent.service';

@NgModule({
  declarations: [],
  imports: [

  ],
  exports: [

  ],
  providers: [
    CentService
  ]
})
export class SingletoneModule { }

И включим его в главный модуль projects/mobi/src/app/app.module.ts

import { SingletoneModule } from './../../../core/src/lib/singletone.module';

… @NgModule({ … providers: [ … SingletoneModule,

  ...

Также нам необходимо инициировать подключение к центрифуге после логина и дисконектится при разлогине.

export class AuthService {

  ...

  public login(data: any) {
    this.api.login(data).subscribe((rez: any) => {
      this.sessionService.setToken(rez.token);
      this.centService.connect();
      ...
  }

  public logout() {
     this.centService.disconnect();
     ...
  }

admin

Бекенд. Django.

Создадим класс клиента backend/backend/cent_client.py.

from cent import Client

from backend.settings import CENT_URL, CENT_KEY


class CentClient(object):

    def __init__(self):
        print('Init connection')
        self.con = Client(CENT_URL, api_key=CENT_KEY, timeout=1)

    def send(self,token,message):
        self.con.publish(token, message)

Используем этот класс в модели для отправки сообщения.

from backend.cent_client import CentClient
...
@app.task
def send_chat_message(id):
    ...
    for user in room.get_participants():
        token, created = Token.objects.get_or_create(user=user)
        payload =  { \
                    'type': 'chat_message', \
                    'message': ChatRoomMessageSerializer(message).data \
                   }       
        cent_client.send(token.key, payload)
Задать вопрос, прокомментировать.