Bouw een realtime chatapplicatie met modulus en veerboot

In deze tutorial gebruiken we Spring Boot voor de webontwikkelingsomgeving, WebSockets voor real-time communicatie, Tomcat voor de Java-toepassingscontainer, Gradle voor het bouwen en beheren van de afhankelijkheden, Thymeleaf voor sjabloonweergave, MongoDBvoor gegevensopslag en tot slot is er geen XML voor bonenconfiguraties. Om je geïnspireerd te laten worden, zul je aan het eind van dit artikel een volledig werkende applicatie zien zoals die hieronder wordt getoond.

1. Scenario

  1. Doe opent de chatpagina om met zijn vrienden te communiceren.
  2. Hij wordt gevraagd om een ​​bijnaam te kiezen.
  3. Hij komt de chatpagina binnen en stuurt een bericht. Het bericht wordt verzonden naar het veer MVC-eindpunt om in de database te worden opgeslagen en uitgezonden.
  4. Het opgegeven eindpunt verwerkt het bericht en verzendt dat bericht naar alle clients die zijn verbonden met het chatsysteem.

2. Bouw afhankelijkheden en Gradle-configuratie

Alvorens verder te gaan met de interne structuur van het project, wil ik u uitleggen welke bibliotheken we zullen gebruiken voor de hierboven genoemde projectfuncties en deze beheren met behulp van Gradle. Wanneer u het project vanuit GitHub kloont, ziet u een bestand met de naam build.gradle in de hoofdmap van het project, zoals hieronder.

buildscript repositories mavenCentral () afhankelijkheden classpath ("org.springframework.boot: spring-boot-gradle-plugin: 1.2.4.RELEASE") plug-in toepassen: 'java' apply plugin: 'eclipse' apply plugin : 'idea' apply plugin: 'spring-boot' apply plugin: 'war' jar baseName = 'realtime-chat' versie = '0.1.0' war baseName = 'ROOT' sourceCompatibility = 1.7 targetCompatibility = 1.7 repositories mavenCentral () sourceCompatibility = 1.7 targetCompatibility = 1.7 dependencies providedRuntime 'org.springframework.boot: spring-boot-starter-tomcat' compile ("org.springframework.boot: spring-boot-starter-web") compile (" org.springframework.boot: spring-boot-starter-thymeleaf ") compile (" org.springframework.boot: spring-boot-starter-data-mongodb ") compile (" org.springframework.boot: spring-boot-starter- websocket ") compile (" org.springframework: spring-messaging ") testCompile (" junit: junit ") taakwikkelaar (type: Wrapper) gradleVersion = '2.3'

Ik zal niet in de Gradle-internals duiken, maar laat me de onderdelen uitleggen die we nodig hebben voor ons project. Spring Boot is voornamelijk gebouwd voor het ontwikkelen van stand-alone applicaties in pot formaat. In ons project zullen we een genereren oorlog project in plaats van pot. Dat komt omdat Modulus een war-bestand nodig heeft om het project automatisch in zijn cloud te implementeren. 

Om een ​​oorlogsbestand te genereren, hebben we het gebruikt plug-in toepassen: 'oorlog'. Modulus verwacht ook dat de naam van de oorlog is ROOT.war standaard, en dat is waarom we hebben gebruikt:

oorlog baseName: 'ROOT.war'

Wanneer je de Gradle uitvoert bouwen taak, het genereert een war-bestand om in de Tomcat-container te implementeren. En tot slot, zoals u kunt raden, is het gebied afhankelijkheden voor bibliotheken van derden voor specifieke acties. 

Dat is alles voor de sectie met projectafhankelijkheden, en u kunt de gebruikershandleiding van Gradle raadplegen voor meer informatie over Gradle.

3. Softwareontwerp

Als u een goede toepassing wilt ontwikkelen, is het verstandig om uw projectstructuur in kleine stukjes te definiëren. U kunt de onderdelen van de volledige architectuur van onze applicatie bekijken.

3.1. Model

We zijn een chattoepassing aan het ontwikkelen, dus we kunnen zeggen dat we een ChatMessageModel model (d.w.z. domeinobject). Terwijl we het chatbericht opslaan of bekijken, kunnen we het chat-object van of naar dit casten ChatMessageModel model. We kunnen ook de Gebruiker model voor chatgebruikers, maar om de applicatie eenvoudiger te maken, zullen we gewoon gebruiken bijnaam als tekst. De ChatMessageModel model heeft de volgende velden: tekst, schrijver, en CreateDate. De klassenweergave van dit model is als volgt:

pakket realtime.domain; import org.springframework.data.annotation.Id; importeer java.util.Date; / ** * @author huseyinbabal * / public class ChatMessageModel @Id private String id; private String-tekst; private String auteur; privé Datum createDate; public ChatMessageModel ()  public ChatMessageModel (String-tekst, String-auteur, Date createDate) this.text = text; this.author = auteur; this.createDate = createDate;  public String getText () retour tekst;  public void setText (String text) this.text = text;  public String getAuthor () return author;  public void setAuthor (String author) this.author = auteur;  public Datum getCreateDate () return createDate;  public void setCreateDate (Date createDate) this.createDate = createDate;  @Override public String toString () return "" + "\" id \ ": \" "+ id + '\"' + ", \" text \ ": \" "+ text + '\"' + ", \" auteur \ ": \" "+ author + '\"' + ", \" createDate \ ": \" "+ createDate +" \ "" + '';  

Dit domeinobject helpt ons om het chatbericht als JSON te vertegenwoordigen wanneer dat nodig is. Ons model is in orde, dus laten we doorgaan met de controllers.

3.2. controleur

De controller is het gedrag van uw applicatie. Dit betekent dat u uw controller eenvoudig moet kunnen houden en in staat bent om eenvoudig te communiceren met domeinmodellen en andere services. We verwachten dat onze controllers omgaan met:

  1. Verzoeken om chatberichten te bewaren
  2. Een lijst met de nieuwste chatberichten
  3. Presenteren op de pagina van de chat-app
  4. Presenteren van de inlogpagina
  5. Chatberichten verzenden naar klanten

Hier kunt u de algemene eindpunten bekijken:

pakket realtime.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import realtime.domain.ChatMessageModel; import realtime.message.ChatMessage; import realtime.repository.ChatMessageRepository; importeer java.util.Date; importeer java.util.List; / ** * @author huseyinbabal * / @Controller public class ChatMessageController @Autowired private ChatMessageRepository chatMessageRepository; @RequestMapping ("/ login") public String login () terug "login";  @RequestMapping ("/ chat") public String chat () terug "chat";  @RequestMapping (value = "/ messages", method = RequestMethod.POST) @MessageMapping ("/ newMessage") @SendTo ("/ topic / newMessage") public ChatMessage save (ChatMessageModel chatMessageModel) ChatMessageModel chatMessage = nieuw ChatMessageModel (chatMessageModel .getText (), chatMessageModel.getAuthor (), nieuwe datum ()); ChatMessageModel-bericht = chatMessageRepository.save (chatMessage); Lijst chatMessageModelList = chatMessageRepository.findAll (nieuwe PageRequest (0, 5, Sort.Direction.DESC, "createDate")). getContent (); return nieuwe ChatMessage (chatMessageModelList.toString ());  @RequestMapping (value = "/ messages", method = RequestMethod.GET) public Http Entity list () Lijst chatMessageModelList = chatMessageRepository.findAll (nieuwe PageRequest (0, 5, Sort.Direction.DESC, "createDate")). getContent (); retourneer nieuwe ResponseEntity (chatMessageModelList, HttpStatus.OK);  

De eerste en tweede eindpunten zijn alleen voor het weergeven van de inlog- en hoofdchatpagina. De derde actie is voor het verwerken van nieuwe berichtenopslag en het uitzenden van berichten. Nadat het bericht is opgeslagen, wordt het via de / Onderwerp / berichtkanaal. Voor het opslaan van berichtgegevens in MongoDB gebruiken we een MongoDB-repository.  

Zoals u kunt zien, zijn er twee soorten eindpunt / messages: GET en POST. Wanneer u een POST-aanvraag naar eindpunt doet / messages met de juiste payload van berichten wordt deze automatisch naar de klasse ChatMessageModel cast en wordt het bericht in MongoDB opgeslagen. Na een succesvolle opslag wordt deze automatisch naar de clients gepusht. Maar hoe? In die actie is er een annotatie @SendTo ( "/ topic / newMessage"). Hiermee wordt de inhoud verzonden van de functie naar de clients verzonden. En de geretourneerde inhoud is als volgt:

... return new ChatMessage (chatMessageModelList.toString ()); ... 

Dit is het laatste bericht uit de database:

Het bovenstaande bericht wordt geconverteerd naar een indeling voor WebSocket-communicatie. Dit kanaalbericht wordt aan de clientzijde afgehandeld met een JavaScript-bibliotheek van derden en wordt in de volgende secties verwerkt. 

Voor bericht db-bewerkingen, veer-boot-starter-data-MongoDB is gebruikt. Deze bibliotheek helpt ons bij repository-bewerkingen en het maken van een repository-object voor MongoDB is heel eenvoudig. Je kunt het voorbeeld zien ChatMessageRepository hieronder:

pakket realtime.repository; import org.springframework.data.mongodb.repository.MongoRepository; import realtime.domain.ChatMessageModel; importeer java.util.List; / ** * @author huseyinbabal * / public interface ChatMessageRepository verlengt MongoRepository  Lijst findAllByOrderByCreateDateAsc ();  

Als u een interface maakt en uitbreidt MongoRepository, je zult in staat zijn om automatisch CRUD-operaties te gebruiken zoals vind(), vind alle(), opslaan(), enz. 

Zoals je kunt zien, MongoRepository verwacht een domeinobject. We hebben dit model al gedefinieerd in het gedeelte Model van de zelfstudie. In deze repository hebben we een aangepaste functie gedefinieerd met de naam findAllByOrderByCreateDateAsc ()

Als je ooit JPA hebt gebruikt voordat je dit gemakkelijk kunt begrijpen, maar laat me dit even uitleggen. Als u een functienaam definieert in een interface die wordt uitgebreid MongoRepository, deze functienaam wordt automatisch automatisch doorgestuurd naar een vraag aan de achterkant van Spring. Het zal iets zijn als:

SELECT * FROM ChatMessageModel WAAR 1 BESTELLING VAN createDate ASC

In ChatMessageController, we hebben deze functie gebruikt, en ook hebben we de standaardfuncties van de MongoRepository:

chatMessageRepository.findAll (nieuwe PageRequest (0, 5, Sort.Direction.DESC, "createDate")). getContent ()

vind alle wordt een parameter gebruikt voor sorteren en paginering. U kunt de gids op de Spring-website bekijken voor meer informatie over Spring JPA.

3.3. Uitzicht

In het viewgedeelte hebben we slechts twee pagina's. Een daarvan is de inlogpagina om de bijnaam van de gebruiker te krijgen, en de tweede is de belangrijkste chatpagina om berichten naar chatgebruikers te verzenden. 

Zoals je kunt zien in het gedeelte over de controller hierboven, worden ze weergegeven met behulp van twee eindpunten, /Log inen / chattenOm interactieve pagina's te maken, gebruiken we JavaScript-bibliotheken van derden. We zullen ze gebruiken van CDN-pagina's. U kunt de inlogpagina hieronder bekijken:

             

Kies een bijnaam om naar de chat te gaan

Op de inlogpagina hebben we een voorbeeldtekstvak. Wanneer u klikt Voer Chat in, je bijnaam wordt opgeslagen in een cookie. Deze bijnaam zal worden gebruikt om het veld voor het auteurschap van het chatbericht in te stellen. Wanneer u klikt Voer Chat in, de chatpagina wordt geopend. Als u al bent ingelogd en naar de inlogpagina gaat, wordt u doorgestuurd naar de chatpagina. 

Dit is de chatpagina:

                 

Realtime-chatapplicatie met Spring Boot, Websockets en MongoDB



Chatgeschiedenis

Deze pagina is voor het eenvoudig bekijken en verzenden van berichten. Berichten worden via WebSockets op deze pagina afgeleverd. Op deze pagina kunt u zien sockjs en stompjs. Die zijn voor het verwerken van meldingen. Wanneer een nieuw bericht wordt ontvangen, wordt het laatste berichtengebied opnieuw bevolkt. 

Overigens, wanneer u de chatpagina voor het eerst opent, worden de nieuwste berichten opgehaald in het berichtengebied. Zoals je aan de JavaScript-kant kunt zien, is ons berichtkanaal dat nieuw bericht. Dus we luisteren naar dit kanaal en wanneer u op de knop klikt Sturen knop, wordt het bericht in het tekstvak naar het eindpunt verzonden en wordt dat bericht na succesvolle opslag naar de verbonden clients verzonden.

Zoals u kunt zien, is de softwarearchitectuur hier heel eenvoudig en gemakkelijk te ontwikkelen. We hebben een productiegereed code en laten we deze toepassen op Modulus.

Modulus is een van de beste PaaS voor het implementeren, schalen en bewaken van uw toepassing in de taal van uw keuze.

4. Implementatie 

4.1. voorwaarden

Voordat we de applicatie gaan inzetten, kunnen we een database maken met behulp van het Modulus admin-paneel. U hebt een Modulus-account nodig voor het maken en implementeren van dba. Maak daarom een ​​account aan als u er nog geen heeft. 

Ga naar het Modulus-dashboard en maak een database aan:

Geef in het scherm voor het maken van de database een databasenaam op, selecteer de MongoDB-versie (ik heb 2.6.3 gebruikt, dus het is beter als u ook 2.6.3 kiest) en definieer uiteindelijk een gebruiker om lees / schrijfbewerkingen voor de database toe te passen. 

U kunt een MongoDB-URL krijgen nadat u met succes de database hebt gemaakt. We gebruiken de MongoDB-URL in de omgevingsvariabelen die moeten worden gebruikt door de toepassing Spring Boot.

Als u de omgevingsvariabelen voor MongoDB wilt instellen, moet u een toepassing hebben. Ga naar Dashboard en klik projecten. Klik op deze pagina Maak een nieuw project.

Ga naar om door te gaan met het configureren van de omgevingsvariabelen Dashboard en klik projecten. Selecteer uw project en klik op Toediening. Blader naar beneden op de pagina en stel omgevingsvariabelen in met de toets SPRING_DATA_MONGODB_URI en waarde van uw database-URI:

Wanneer u uw toepassing implementeert, gebruikt Spring die omgevingsvariabele. We hebben gedaan met de vereisten en laten we doorgaan met het implementatiedeel.

4.2. Implementatie met CLI

Om het project in te zetten, voert u een taak uit in stappen:

gradle build

Deze taak genereert een war-bestand met de naam ROOT.war. Kopieer dit bestand naar een nieuwe map en installeer modulus CLI als dat niet het geval is.

npm install-g modulus

Log in op het systeem;

modulus login

Voer nu de volgende opdracht uit om te implementeren ROOT.war naar Modulus.

modulus deploy

Hiermee wordt het oorlogsbestand geïmplementeerd en kunt u projectlogboeken achterhalen om de status van uw implementatie te bekijken door de volgende opdracht uit te voeren:

modulus project logs staart

Dat is alles met de inzet!

5. Conclusie

Het belangrijkste doel van deze zelfstudie is om u te laten zien hoe u een real-time chat-applicatie kunt maken met Spring Boot, WebSockets en MongoDB. 

Om het project in productie te houden, wordt Modulus gebruikt als een PaaS-provider. Modulus heeft zeer eenvoudige implementatiestappen en heeft ook een interne database (MongoDB) voor onze projecten. Daarnaast kunt u zeer nuttige tools gebruiken in het Modulus-dashboard zoals Logs, Notifications, Auto-Scaling, Db Administration en meer.