كتابة محادثة باستخدام Spring Boot و WebSockets





. « Spring Framework» . , , : « Spring», .









في إنشاء إعلام يشبه Facebook قابل للتطوير باستخدام حدث مرسل من الخادم و Redis ، استخدمنا الأحداث المرسلة من الخادم لإرسال الرسائل من الخادم إلى العميل. كما ذكرت WebSocket ، وهي تقنية اتصال ثنائية الاتجاه بين الخادم والعميل.



في هذه المقالة ، سنلقي نظرة على إحدى حالات الاستخدام الشائعة لـ WebSocket. سنكتب تطبيق مراسلة خاص.



يوضح الفيديو أدناه ما سنفعله.





مقدمة إلى WebSockets و STOMP



WebSocket هو بروتوكول للاتصال ثنائي الاتجاه بين الخادم والعميل.

WebSocket ، بخلاف HTTP ، فإن بروتوكول طبقة التطبيق هو بروتوكول طبقة النقل (TCP). على الرغم من استخدام HTTP للاتصال الأولي ، يتم "ترقية" الاتصال بعد ذلك إلى اتصال TCP المستخدم في WebSocket.



WebSocket هو بروتوكول ذو مستوى منخفض لا يحدد تنسيقات الرسائل. لذلك ، يحدد WebSocket RFC البروتوكولات الفرعية التي تصف بنية ومعايير الرسائل. سنستخدم STOMP عبر WebSockets (STOMP عبر WebSockets). يحدد



البروتوكول STOMP (البسيط / تدفق النص الموجه إلى بروتوكول الرسائل) قواعد تبادل الرسائل بين الخادم والعميل.



STOMP مشابه لـ HTTP ويتم تشغيله أعلى TCP باستخدام الأوامر التالية:



  • الاتصال
  • الإشتراك
  • إلغاء الاشتراك
  • إرسال
  • ابدأ
  • ارتكب
  • ACK


يمكن العثور على المواصفات والقائمة الكاملة لأوامر STOMP هنا .



هندسة معمارية







  • في خدمة مصادقة هي المسؤولة عن توثيق وإدارة المستخدمين. لن نعيد اختراع العجلة هنا وسنستخدم خدمة المصادقة من JWT والمصادقة الاجتماعية باستخدام Spring Boot .
  • في خدمة الدردشة هي المسؤولة عن تكوين WebSocket، والتعامل مع الرسائل ستومب، وتخزين ومعالجة الرسائل المستخدم.
  • و الدردشة العملاء هو تطبيق ReactJS يستخدم العميل ستومب للتواصل والاشتراك في الدردشة. هنا أيضا واجهة المستخدم.


نموذج الرسالة



أول شيء يجب التفكير فيه هو نموذج الرسالة. يبدو ChatMessage مثل هذا:



public class ChatMessage {
   @Id
   private String id;
   private String chatId;
   private String senderId;
   private String recipientId;
   private String senderName;
   private String recipientName;
   private String content;
   private Date timestamp;
   private MessageStatus status;
}


الفصل ChatMessageبسيط للغاية ، مع وجود الحقول اللازمة لتحديد المرسل والمستقبل.



كما يحتوي أيضًا على حقل حالة يشير إلى ما إذا تم تسليم الرسالة إلى العميل.



public enum MessageStatus {
    RECEIVED, DELIVERED
}


عندما يتلقى الخادم رسالة من دردشة ، فإنه لا يرسل الرسالة مباشرة إلى المستلم ، ولكنه يرسل إشعارًا ( ChatNotification ) لإخطار العميل باستلام رسالة جديدة. بعد ذلك ، يمكن للعميل نفسه استلام رسالة جديدة. بمجرد أن يتلقى العميل الرسالة ، يتم تمييزها على أنها تم التسليم.



يبدو الإخطار كالتالي:



public class ChatNotification {
    private String id;
    private String senderId;
    private String senderName;
}


يحتوي الإخطار على معرف الرسالة الجديدة ومعلومات عن المرسل بحيث يمكن للعميل إظهار معلومات حول الرسالة الجديدة أو عدد الرسائل الجديدة كما هو موضح أدناه.











تكوين WebSocket و STOMP في الربيع



تتمثل الخطوة الأولى في تكوين نقطة نهاية STOMP ووسيط الرسائل.



للقيام بذلك، ونحن إنشاء WebSocketConfig الطبقة مع الشروح @Configurationو @EnableWebSocketMessageBroker.



@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker( "/user");
        config.setApplicationDestinationPrefixes("/app");
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry
                .addEndpoint("/ws")
                .setAllowedOrigins("*")
                .withSockJS();
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
        DefaultContentTypeResolver resolver = new DefaultContentTypeResolver();
        resolver.setDefaultMimeType(MimeTypeUtils.APPLICATION_JSON);
        MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
        converter.setObjectMapper(new ObjectMapper());
        converter.setContentTypeResolver(resolver);
        messageConverters.add(converter);
        return false;
    }
}


تقوم الطريقة الأولى بتكوين وسيط بسيط للرسائل في الذاكرة بعنوان واحد مسبق /userلإرسال الرسائل واستلامها. العناوين البادئة /appمخصصة للرسائل التي تمت معالجتها بواسطة الطرق التوضيحية @MessageMapping، والتي سنناقشها في القسم التالي.



الطريقة الثانية تسجل نقطة نهاية STOMP /ws. سيتم استخدام نقطة النهاية هذه من قبل العملاء للاتصال بخادم STOMP. يتضمن هذا أيضًا SockJS احتياطيًا سيتم استخدامه في حالة عدم توفر WebSocket.



الطريقة الأخيرة تهيئ محول JSON الذي يستخدمه Spring لتحويل الرسائل من / إلى JSON.



تحكم للتعامل مع الرسائل



في هذا القسم ، سننشئ وحدة تحكم تتعامل مع الطلبات. ستتلقى رسالة من المستخدم وترسلها إلى المستلم.



@Controller
public class ChatController {

    @Autowired private SimpMessagingTemplate messagingTemplate;
    @Autowired private ChatMessageService chatMessageService;
    @Autowired private ChatRoomService chatRoomService;

    @MessageMapping("/chat")
    public void processMessage(@Payload ChatMessage chatMessage) {
        var chatId = chatRoomService
                .getChatId(chatMessage.getSenderId(), chatMessage.getRecipientId(), true);
        chatMessage.setChatId(chatId.get());

        ChatMessage saved = chatMessageService.save(chatMessage);
        
        messagingTemplate.convertAndSendToUser(
                chatMessage.getRecipientId(),"/queue/messages",
                new ChatNotification(
                        saved.getId(),
                        saved.getSenderId(),
                        saved.getSenderName()));
    }
}


نستخدم التعليق التوضيحي @MessageMappingلتكوين أنه عند إرسال الرسالة إلى /app/chat، يتم استدعاء الطريقة processMessage. يرجى ملاحظة أنه ستتم إضافة بادئة التطبيق التي تم تكوينها مسبقًا إلى التعيين /app.



تخزن هذه الطريقة الرسالة في MongoDB ثم تستدعي الطريقة convertAndSendToUserلإرسال الإخطار إلى الهدف.



طريقة convertAndSendToUserإضافة البادئة /userو recipientIdعلى العنوان /queue/messages. سيبدو العنوان النهائي كما يلي:



/user/{recipientId}/queue/messages


سيتلقى جميع المشتركين في هذا العنوان (في حالتنا واحد) الرسالة.



إنشاء معرف الدردشة



لكل محادثة بين مستخدمين ، نقوم بإنشاء غرفة دردشة وإنشاء غرفة دردشة فريدة للتعرف عليها chatId. تبدو



فئة ChatRoom كما يلي:



public class ChatRoom {
    private String id;
    private String chatId;
    private String senderId;
    private String recipientId;
}


القيمة chatIdتساوي التسلسل senderId_recipientId. لكل محادثة ، نحتفظ بكيانين متشابهين chatId: أحدهما بين المرسل والمتلقي ، والآخر بين المستلم والمرسل ، بحيث يحصل كلا المستخدمين على نفس الشيء chatId.



عميل JavaScript



في هذا القسم ، سننشئ عميل JavaScript يرسل رسائل إلى خادم WebSocket / STOMP ويستقبله من هناك.



سنستخدم SockJS و Stomp.js للتواصل مع الخادم باستخدام STOMP عبر WebSocket.



const connect = () => {
    const Stomp = require("stompjs");
    var SockJS = require("sockjs-client");
    SockJS = new SockJS("http://localhost:8080/ws");
    stompClient = Stomp.over(SockJS);
    stompClient.connect({}, onConnected, onError);
  };


connect()تنشئ الطريقة اتصالاً /ws، حيث ينتظر خادمنا الاتصالات ، كما تحدد وظيفة رد الاتصال onConnectedالتي سيتم استدعاؤها عند الاتصال الناجح ، ويتم onErrorاستدعاؤها في حالة حدوث خطأ أثناء الاتصال بالخادم.



const onConnected = () => {
    console.log("connected");

    stompClient.subscribe(
      "/user/" + currentUser.id + "/queue/messages",
      onMessageReceived
    );
  };


الطريقة onConnected()تشترك في عنوان محدد وتستقبل جميع الرسائل المرسلة هناك.



const sendMessage = (msg) => {
    if (msg.trim() !== "") {
      const message = {
        senderId: currentUser.id,
        recipientId: activeContact.id,
        senderName: currentUser.name,
        recipientName: activeContact.name,
        content: msg,
        timestamp: new Date(),
      };
        
      stompClient.send("/app/chat", {}, JSON.stringify(message));
    }
  };


في نهاية الطريقة ، sendMessage()يتم إرسال رسالة إلى العنوان /app/chatالمحدد في وحدة التحكم الخاصة بنا.



خاتمة



في هذه المقالة ، قمنا بتغطية جميع النقاط المهمة لإنشاء محادثة باستخدام Spring Boot و STOMP عبر WebSocket.



قمنا أيضًا ببناء عميل JavaScript باستخدام مكتبات SockJs و Stomp.js .



يمكن العثور على نموذج التعليمات البرمجية المصدر هنا .






تعلم المزيد عن الدورة.











All Articles