使用socket.io构建强大的节点。JS应用程序:最佳实践
#javascript #node #socketio #express

简介

本文将指导您使用node.js and socket.io构建基本的AI聊天机器人,同时结合了一些用于node.js开发的最佳实践。 Socket.io凭借其双向通信功能,可以轻松构建需要服务器和客户端之间连续互动的实时应用程序。

在整个开发过程中,我们将遵循最佳实践,以确保我们的应用程序安全,可扩展和可维护。

设置环境

要设置使用node.js构建Web应用程序的环境,您将需要遵循一些简单的步骤:

1安装node.js.
2设置项目目录。

my-chatbot/
├── node_modules/
├── public/
│   ├── css/
│   │   ├── style.css
│   ├── js/
│   │   ├── chat.js
│   │   ├── socket.io.js
│   ├── index.html
├── app/
│   ├── services/
│   |     ├── store.js
│   ├── socket/
│   |    ├── socket.js
│   |    ├── socketHelper.js
│   ├── utils/
│   |     ├── constants.js
├── config/
│   |      ├── app.js
├── app.js
├── .env
├── .gitignore
├── package-lock.json
├── package.json
└── README.md

3安装必要的依赖项。

npm i express socket

socket.io

socket.io是一个流行的库,用于构建需要服务器和客户端之间双向通信的实时Web应用程序。它建立在Websockets API顶部,并提供了一个简单而优雅的API,用于构建具有广播,名称空间和房间等功能的实时应用程序。

socket.io将是聊天机器人的关键部分,它将主要处理我们的服务器和客户端之间的通信。

创建Express Server

app/:此目录包含我们应用程序的所有后端代码。

在此文件中,我们将初始化我们的Express应用程序。

const express = require('express');
const { appConfig, Logger } = require('./config');
const app = express();

global.logger = Logger.createLogger({ label: 'E-commerce Chatbot' });

appConfig(app);

module.exports = app;

这是一种简单的配置,在初始化应用程序配置和记录器之后,将导出Express应用程序的实例。

首先,我们导入Express Framework模块,然后我们从配置模块导入appConfig, Logger

global.logger = Logger.createLogger({ label: 'E-commerce Chatbot' });

在上面的行中,logger定义为全局变量。这允许从Node.js应用程序中的任何模块访问它,提供了登录的集中位置。

配置Express App

const express = require('express');
const http = require('http');
const path = require('path');
const Socket = require('../app/socket/socket');

const appConfig = async (app) => {
    // Set up server
    const server = http.createServer(app);

    // Set up socket
    const { SocketInstance } = Socket.createSocket(server);
    SocketInstance(server);
    // Set static files
    app.use(express.static(path.join(__dirname, '../public')));

    const PORT = 9000;
    // Listen to port
    server.listen(PORT, () => {
        logger.info(`Server running on port ${PORT}`);
    });
};

module.exports = appConfig;

这是配置Express App的AppConfig函数。

AppConfig函数在配置聊天机器人的Express应用程序中起着至关重要的作用。它使用http.createServer(app)创建HTTP服务器,使用Socket.createSocket(server)配置套接字实例,并使用express.static()设置静态文件。然后,该功能使用server.listen()倾听到指定的端口。这些操作遵循Node.js的最佳实践,例如设置服务器以在特定端口上收听并使用路径模块服务静态文件。

此外,最好将应用程序配置保持在node.js中的函数范围内。它有助于隔离配置代码,并防止其在函数范围之外访问或修改,这可以帮助提高应用程序的安全性。

这样做,您可以限制对应用程序正常工作所需的重要变量或配置的访问,从而使未经授权的访问或修改更加困难。此外,它还可以通过保持井井有条且易于阅读来帮助提高您的代码的可维护性。

配置socket.io

后端开发的关键方面之一是数据偏见和封装。实施此目的的最佳方法之一是使用基于类的方法。

在套接字类中,使用私有方法来处理套接字事件,例如用户发送消息或用户加入或离开聊天室时。私人方法的使用有助于使实施细节隐藏在外部模块中,从而提高了代码的整体安全性和可维护性。

此外,无论是来自用户还是机器人,插座类的公共方法都用于向聊天室发出消息。通过分开这些方法,管理套接字实例的逻辑并提高代码的整体可读性变得更加容易。

套接字类的构造函数用必要的属性(例如服务器和套接字实例)初始化了实例。此外,设置了机器人和用户名的默认值,提供了更可自定义的实现。

initializeSocket方法负责处理连接事件并初始化必要的事件侦听器,以获取用户消息,机器人消息,通知和断开连接。通过以单独的方法组织这些事件,代码更易于理解和维护。

最后,使用createSocket静态方法来创建套接字类的新实例并初始化套接字实例。此方法为其他模块创建和管理自己的套接字实例提供了易于使用的接口。

*套接字配置

const socket = require('socket.io');
const constants = require('../utils/constants');

const {
    CHAT_BEGINNING,
    NEW_CONNECTION,
    BOT_NAME,
    BOT_INTRO,
    BOT_INTRO_2,
} = constants;

class Socket {
    constructor(server) {
        this.server = server;
        this.io = socket(server, { cors: { origin: '*' } });
        this.botName = BOT_NAME;
        this.userName = 'User';
    }

    /**
     * @private
     * @function _emitUserMessage
     * @param {object} socket
     * @param {object} message
     * @emits userMessage - when a user sends a message
     * @memberof Socket
     * @returns {object} message - returns user message to the chatroom
     */
    _emitUserMessage(socket, message) {
        socket.emit('userMessage', Helper(this.userName, message));
    }

    /**
     * @private
     * @function _emitBotMessage
     * @param {object} socket
     * @param {object} message
     * @emits botMessage - when the chatbot sends a message
     * @memberof Socket
     * @returns {object} message - returns bot message to the chatroom
     */
    _emitBotMessage(socket, message) {
        socket.emit('botMessage', Helper(this.botName, message));
    }

    /**
     * @private
     * @function _emitNotification
     * @param {object} socket
     * @param {object} message
     * @emits notification - when a user joins or leaves the chatroom
     * @memberof Socket
     * @returns {object} message - returns notification to the chatroom
     */
    _emitNotification(socket, message) {
        socket.emit('notification', Helper(this.botName, message));
    } 

    /**
     * @private
     * @function _broadcastNotification
     * @param {object} socket
     * @param {object} message
     * @emits notification - when a user joins or leaves the chatroom
     * @memberof Socket
     * @returns {function} _emitNotification - returns notification to the chatroom
     */
    _broadcastNotification(socket, message) {
        this._emitNotification(socket.broadcast, message);
    }

    /**
     * @private
     * @function _emitDisconnect
     * @param {object} socket
     * @emits disconnect - when a user disconnects from the chatroom
     * @memberof Socket
     * @returns {function} _broadcastNotification - returns notification to the chatroom
     */
    _emitDisconnect(socket) {
        socket.on('disconnect', (reason) => {
            this._broadcastNotification(socket, reason);
        });
    }


    /**
     * @private
     * @function initializeSocket
     * @memberof Socket
     * @returns {function} _listenRegister - listens for register
     * @returns {function} _emitNotification - emits notification to the chatroom
     * @returns {function} _emitBotMessage - emits bot message to the chatroom
     * @returns {function} _listenToChatMessage - listens for chat message
     * @description Initializes socket
     * @listens for connection
     * @emits userMessage
     * @emits botMessage
     * @emits notification
     * @emits disconnect
     * @emits access
     */
    initializeSocket() {
        this.io.on('connection', (socket) => {
            logger.info(NEW_CONNECTION(socket.id));


            // Emit to the new user only
            this._emitNotification(socket, CHAT_BEGINNING);

            // Emit bot message
            this._emitBotMessage(socket, BOT_INTRO);

            this._emitBotMessage(socket, BOT_INTRO_2);


        });


    }

    /**
     * @static
     * @function createSocket
     * @param {object} server- server instance
     * @memberof Socket
     * @returns {object} socketInstance - returns socket instance
     * @description Creates a socket instance
     */
    static createSocket(server) {
        const _createSocketInstance = (server) => {
            const socketInstance = new Socket(server);
            return socketInstance.initializeSocket();
        };

        return {
            SocketInstance: _createSocketInstance,
        };
    }
}

module.exports = Socket;

完整分解

服务器启动时,将调用套接字构造函数并传递HTTP服务器实例。在构造函数内部,socket.io库用于创建一个新的套接字实例,并且此实例存储为插座对象的属性。套接字对象还初始化了一些属性来存储机器人和用户的名称,这些属性将在以后使用。

套接字类有几种用于向连接插座发出消息的私人方法。这些方法包括_emitUserMessage_emitBotMessage_emitNotification_broadcastNotification。这些方法都将套接字对象和一个消息对象作为参数,并将消息发射到称为该方法的套接字或所有插座的套接字,除了称为方法的套接字。

使用私人类方法的优势

  • 将这些方法定义为类中的私人方法,为代码提供了封装和模块化。通过将这些方法保留在同类中,它们不会暴露于全局范围,只能在类本身中访问。这样可以防止全局范围中的其他方法或变量的潜在命名冲突。

  • 此外,通过将这些方法封装在类中,它使维护和修改代码变得更加容易。由于这些方法与班级的功能密切相关,因此将它们保留在班级中以更好地组织和易于访问是很有意义的。

  • 将这些方法定义为同类中的私有的另一个优点是,它有助于抽象使用socket.io库的一些复杂性。通过创建来处理发出特定事件和消息的详细信息的方法,代码的其余部分可以专注于应用程序的高级逻辑,而无需担心socket.io的API的细节。

  • 最后,它还有助于代码可重复使用性,因为可以从类中的其他方法中调用这些方法,而不是在多个函数中复制。这有助于减少代码重复,并使维护代码库更容易。

套接字类还具有称为initializeSocket的公共方法,该方法被称为启动套接字服务器。在此方法中,socket.io库的方法用于侦听连接事件。当新客户端连接到套接字服务器时,将为该客户端创建一个新的套接字对象,并使用记录器来登录已建立新连接的。

然后,initializeSocket方法调用了一些私人方法来向客户端发送消息,例如来自机器人的欢迎消息,新用户已加入的通知,以及来自机器人的介绍消息。该方法还会听取断开事件的声音,并在客户端与服务器断开连接时向所有连接的插座广播通知。

最后,使用createSocket静态方法来创建套接字类的新实例并初始化套接字实例。此方法为其他模块创建和管理自己的套接字实例提供了易于使用的接口。

socket.io的基于类的方法对后端开发人员可能特别有益,因为它使他们能够维护更清洁,更有条理的代码结构。这对于后端开发尤其重要,随着应用程序的大小和复杂性的增长,代码可能变得复杂且难以管理。

通过将与插座相关的逻辑和类事件封装在类中,后端开发人员可以有效地将应用程序的关注点分开,从而更容易管理和维护代码。当使用需要多个开发人员在代码库的不同部分工作的大型应用程序时,这可能特别有用。

除了改善代码的组织外,使用基于类的socket.io的方法还可以帮助防止冲突并改善模块化。这是因为应用程序的不同部分可以使用插座而不相互干扰,从而使开发人员可以并行处理应用程序的不同组件。

此外,通过采用基于班级的方法,后端开发人员可以利用面向对象的编程概念,例如继承和多态性,这可以导致更有效和可维护的代码。例如,通过使用继承,开发人员可以为公共插座相关的逻辑和事件创建一个基类,然后扩展此类以在应用程序的不同部分添加特定功能。

总的来说,使用基于类的方法进行socket.io可以成为后端开发人员的宝贵工具,帮助简化开发,改善代码组织并提高模块化和效率。