Когда уже совсем много запросов на сервер или…

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Когда “еще один пуллинг каждый N секунд” стучится вам в код. Время подумать про вебсокеты a.k.a полнодуплексное соединение.

Речь пойдет про socket.io , не совсем web socket а скорее микс при участии web socket. Но очень удобный в использовании сразу из коробки. 

Кейс состоит в следующем: 

  • Есть многопользовательское приложение в котором пользователи запускают асинхронные операции на сервере. Другими словами нажимают на кнопку в приложении и ждут когда сервер выполнит все операции а походу еще и расскажет про текущее состояние.

Вроде бы можно обойтись лонг пуллингом, но некоторые действия хочется заблокировать пользователям которые находятся за другими мониторами на той же странице. Да и вообще говоря сама библиотека при отсутствии возможности ws/wss соединения будет использовать пуллинг. Так что вроде бы только плюсы, из минусов только еще один пакет на клиенте и сервере.

Для клиента необходима библиотека socket-io.client, для фронта все написано на React и само соединение можно установить/разорвать через хуки. Для тех кто использует redux-toolkit все можно сделать через RTK.

У вас должен быть базовый объект createApi({ … }) который описывает все эндпоинты и который потом, так же можно расширить.

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const baseApi = createApi({
    reducerPath: 'baseApi',
    baseQuery: fetchBaseQuery({ baseUrl: '/' }),
    endpoints: () => ({}),
});

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

const appReducer = combineReducers({
	…,
[baseApi.reducerPath]: baseApi.reducer,
	…
});

В middleware добавляем =>  baseApi.middleware

И расширяем свой базовый baseApi функционалом для создания вебсокета.

import { baseApi } from '../api';
import { io } from 'socket.io-client';

export const wsApi = baseApi.injectEndpoints({
    endpoints: build => ({
        subscribeToEvents: build.query<any, void>({
            queryFn: () => ({ data: [] }),
            async onCacheEntryAdded(_arg, { dispatch, updateCachedData, cacheEntryRemoved }) {
                // Path is a prefix that will be used right after domain name
                const socket = io(`${your_url}/events`, {
                    path: '/socket.io',
                });

                socket.on('disconnect', reason => {
                    if (reason === 'io server disconnect') {
                        // the disconnection was initiated by the server, you need to reconnect manually
                        socket.connect();
                    }
                    // else the socket will automatically try to reconnect
                });

                socket.on(‘EVENT_TYPE’, (event: ServerEvent) => {
                    // Here we should add the logic
                    updateCachedData(draft => {
                        draft.push(event);
                    });
                });

                await cacheEntryRemoved;
                socket.close();
            },
        }),
    }),
    overrideExisting: false,
});

export const { useSubscribeToEventsQuery } = wsApi;

injectEndpoints работает как раз для расширения эндпоинтов, только не забудьте добавить overrideExisting: false чтобы расширить а не переопределить существующий функционал.

useSubscribeToEventsQuery(); можно использовать как обычный хук, в том компоненте в котором желаете подписаться на события, например в App. Еще в api есть свойство keepUnusedDataFor для того чтобы задать время в секундах существования подключения/данных после последнего unsubscribe, по умолчанию 60 секунд.

На сервере используется Ts.Ed (Node js), но также существуют готовые библиотеки на других языках. Нужно проинсталлировать пакеты связанные с socket.io для сервера. И дела за малым, добавить конфиг:

socketIO: {
	path: '/socket.io',
	cors: {
		origin: '*' // put your servers
	}
} 

И добавить сервис который выполняет подключение/отключение клиента а так же отправляет и принимает сообщения.

import { IO, Nsp, Socket, SocketService, SocketSession } from "@tsed/socketio";
import * as SocketIO from "socket.io";

@SocketService("/events") // namespace right after path ‘socket.io’
export class MySocketService {

    @Nsp nsp: SocketIO.Namespace | undefined;

    // a map to keep clients by any id you like, a userId or whatever.
    public clients: Map<string, SocketIO.Socket> = new Map();


    constructor(@IO private io: SocketIO.Server) {
    }

    /**
     * Triggered when a new client connects to the Namespace.
     */
    $onConnection(@Socket socket: SocketIO.Socket, @SocketSession session: SocketSession) {
        this.clients.set(socket.id, socket);
    }

    // setup a method to send data to all clients
    // you can use this from any other service or controller.
    broadcast(someData: any): void {
        this.nsp.emit(‘EVENT_TYPE’, { … }: ServerEvent);
    }

    // method to send to a targeted client
    sendToSingleClient(idToSendTo: string, someData: any): void {
        const socket = this.clients.get(idToSendTo);
        if (!socket) return;
        socket.emit(‘EVENT_TYPE’, { … }: ServerEvent);
    }

}

Namespace в socket.io это путь "/event" после основного пути в настройках socketIO: { path: 'socket.io' }. Теперь можно в любом необходимом месте заинжектить сервис и отправить сообщение клиентам.

Вообщем то как-то так, после этого клиент может получать сообщения и выполнять необходимые side effects.

Источник: https://habr.com/ru/post/594307/


Интересные статьи

Интересные статьи

CBPolicyD является универсальным инструментом для управления политиками в Postfix. Он входит в комплект поставки Zimbra OSE и ранее мы рассказывали о том, как настраивать CBPolicyD на...
Многие компании в определенный момент приходят к тому, что ряд процессов в бизнесе нужно автоматизировать, чтобы не потерять свое место под солнцем и своих заказчиков. Поэтому все...
Недавно я заметил кое-что. Раньше мне было все равно, теперь я это знаю — и это мне не понравилось. Во всяких этих ваших корпоративных тренингах, а также начиная с младших классов в школе нам рас...
Представитель нашего клиента, стек приложений которого обитает в облаке от Microsoft (Azure), обратился с проблемой: с недавнего времени часть запросов некоторых клиентов из Европы стала заве...
Наверняка большинство из вас нет-нет да и встречало в научно-популярной литературе упоминания о «многомировой интерпретации» квантовой механики (ММИ). Ее любят помянуть и в комментариях на Хабре,...