Mengobrol dengan Spring Boot dan WebSockets





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









Dalam Membuat Notifikasi Mirip Facebook yang Skalabel dengan menggunakan Server-Sent Event dan Redis, kami menggunakan Event yang dikirim Server untuk mengirim pesan dari server ke klien. Ia juga menyebutkan WebSocket, teknologi komunikasi dua arah antara server dan klien.



Pada artikel ini, kita akan melihat salah satu kasus penggunaan umum untuk WebSocket. Kami akan menulis aplikasi perpesanan pribadi.



Video di bawah ini menunjukkan apa yang akan kami lakukan.





Pengantar WebSockets dan STOMP



WebSocket adalah protokol untuk komunikasi dua arah antara server dan klien.

WebSocket, tidak seperti HTTP, protokol lapisan aplikasi, adalah protokol lapisan transport (TCP). Meskipun HTTP digunakan untuk sambungan awal, sambungan tersebut kemudian "ditingkatkan" ke sambungan TCP yang digunakan di WebSocket.



WebSocket adalah protokol tingkat rendah yang tidak menentukan format pesan. Oleh karena itu, WebSocket RFC mendefinisikan subprotocol yang menjelaskan struktur dan standar pesan. Kami akan menggunakan STOMP melalui WebSockets (STOMP melalui WebSockets).



Protocol STOMP (Simple / the Streaming the Text Oriented the Message Protocol) mendefinisikan aturan untuk pertukaran pesan antara server dan klien.



STOMP mirip dengan HTTP dan berjalan di atas TCP menggunakan perintah berikut:



  • MENGHUBUNG
  • LANGGANAN
  • BERHENTI BERLANGGANAN
  • KIRIM
  • MULAI
  • MELAKUKAN
  • ACK


Spesifikasi dan daftar lengkap perintah STOMP dapat ditemukan di sini .



Arsitektur







  • The Auth Layanan bertanggung jawab untuk otentikasi dan mengelola pengguna. Kami tidak akan menemukan kembali roda di sini dan akan menggunakan layanan otentikasi dari JWT dan Autentikasi Sosial menggunakan Spring Boot .
  • The Layanan Chat bertanggung jawab untuk mengkonfigurasi WebSocket, penanganan pesan STOMP, dan menyimpan dan memproses pesan pengguna.
  • The Obrolan Klien adalah aplikasi ReactJS yang menggunakan klien STOMP untuk menghubungkan dan berlangganan chatting. Antarmuka pengguna juga terletak di sini.


Model pesan



Hal pertama yang harus dipikirkan adalah model pesannya. ChatMessage terlihat seperti ini:



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;
}


Kelasnya ChatMessagecukup sederhana, dengan bidang yang diperlukan untuk mengidentifikasi pengirim dan penerima.



Ini juga memiliki bidang status yang menunjukkan apakah pesan telah dikirim ke klien.



public enum MessageStatus {
    RECEIVED, DELIVERED
}


Ketika server menerima pesan dari obrolan, itu tidak mengirim pesan langsung ke penerima, tetapi mengirim pemberitahuan ( ChatNotification ) untuk memberi tahu klien bahwa pesan baru telah diterima. Setelah itu, klien sendiri dapat menerima pesan baru. Segera setelah klien menerima pesan, itu ditandai sebagai TERSEDIA.



Notifikasinya terlihat seperti ini:



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


Notifikasi berisi ID pesan baru dan informasi tentang pengirim sehingga klien dapat menampilkan informasi tentang pesan baru atau jumlah pesan baru, seperti yang ditunjukkan di bawah ini.











Mengonfigurasi WebSocket dan STOMP di Spring



Langkah pertama adalah mengkonfigurasi titik akhir STOMP dan broker pesan.



Untuk melakukan ini, kami membuat kelas WebSocketConfig dengan anotasi @Configurationdan @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;
    }
}


Metode pertama mengkonfigurasi perantara pesan dalam memori sederhana dengan satu alamat awalan /useruntuk mengirim dan menerima pesan. Alamat awalan /appadalah untuk pesan yang diproses dengan metode beranotasi @MessageMapping, yang akan kita bahas di bagian selanjutnya.



Metode kedua mendaftarkan titik akhir STOMP /ws. Titik akhir ini akan digunakan oleh klien untuk terhubung ke server STOMP. Ini juga termasuk SockJS fallback yang akan digunakan jika WebSocket tidak tersedia.



Metode terakhir mengonfigurasi konverter JSON yang digunakan Spring untuk mengonversi pesan ke / dari JSON.



Pengontrol untuk menangani pesan



Di bagian ini, kami akan membuat pengontrol yang akan menangani permintaan. Ini akan menerima pesan dari pengguna dan mengirimkannya ke penerima.



@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()));
    }
}


Kami menggunakan anotasi @MessageMappinguntuk mengonfigurasi bahwa ketika pesan dikirim ke /app/chat, metode ini dipanggil processMessage. Harap dicatat bahwa awalan aplikasi yang dikonfigurasi sebelumnya akan ditambahkan ke pemetaan /app.



Metode ini menyimpan pesan di MongoDB dan kemudian memanggil metode tersebut convertAndSendToUseruntuk mengirim notifikasi ke target.



Metode convertAndSendToUsermenambahkan awalan /userdan recipientIdalamat /queue/messages. Alamat akhir akan terlihat seperti ini:



/user/{recipientId}/queue/messages


Semua pelanggan ke alamat ini (dalam kasus kami, satu) akan menerima pesan.



Menghasilkan chatId



Untuk setiap percakapan antara dua pengguna, kami membuat ruang obrolan dan membuat yang unik untuk mengidentifikasinya chatId.



Kelas ChatRoom terlihat seperti ini:



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


Nilainya chatIdsama dengan penggabungan senderId_recipientId. Untuk setiap percakapan, kami menyimpan dua entitas dengan yang sama chatId: satu antara pengirim dan penerima, dan yang lainnya antara penerima dan pengirim, sehingga kedua pengguna menerima yang sama chatId.



Klien JavaScript



Di bagian ini, kita akan membuat klien JavaScript yang akan mengirim dan menerima pesan dari server WebSocket / STOMP.



Kami akan menggunakan SockJS dan Stomp.js untuk berkomunikasi dengan server menggunakan STOMP melalui 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);
  };


Metode ini connect()membuat koneksi ke /ws, di mana server kami menunggu koneksi, dan juga menentukan fungsi panggilan balik onConnectedyang akan dipanggil setelah koneksi berhasil, dan onErrordipanggil jika terjadi kesalahan saat menghubungkan ke server.



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

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


Metode onConnected()berlangganan ke alamat tertentu dan menerima semua pesan yang dikirim ke sana.



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));
    }
  };


Di akhir metode, sendMessage()pesan dikirim ke alamat /app/chatyang ditentukan di pengontrol kami.



Kesimpulan



Pada artikel ini, kami telah membahas semua poin penting dalam membuat obrolan menggunakan Spring Boot dan STOMP melalui WebSocket.



Kami juga membangun klien JavaScript menggunakan pustaka SockJs dan Stomp.js .



Contoh kode sumber dapat ditemukan di sini .






Pelajari lebih lanjut tentang kursus tersebut.











All Articles