闲来无事,偶尔看到一片文章,讲的是websocket和http轮询的优势,正好,我对此有一定的兴趣,决定搭建一个项目来实现web页面下的即时通信。
先给大家介绍一下websocket和轮训长轮询,直接粘贴。
轮询
轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP request,然后由服务器返回最新的数据给客户端的浏览器。这种传统的HTTP request 的模式带来很明显的缺点 – 浏览器需要不断的向服务器发出请求,然而HTTP request 的header是非常长的,里面包含的有用数据可能只是一个很小的值,这样会占用很多的带宽。
var xhr = new XMLHttpRequest(); setInterval(function(){ xhr.open('GET','/user'); xhr.onreadystatechange = function(){ }; xhr.send(); },1000)
长轮询:
ajax实现:在发送ajax后,服务器端会阻塞请求直到有数据传递或超时才返回。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
function ajax(){ var xhr = new XMLHttpRequest(); xhr.open('GET','/user'); xhr.onreadystatechange = function(){ ajax(); }; xhr.send(); }
Web Socket:
轮询与长轮询都是基于HTTP的,两者本身存在着缺陷:轮询需要更快的处理速度;长轮询则更要求处理并发的能力;两者都是“被动型服务器”的体现:服务器不会主动推送信息,而是在客户端发送ajax请求后进行返回的响应。而理想的模型是"在服务器端数据有了变化后,可以主动推送给客户端",这种"主动型"服务器是解决这类问题的很好的方案。Web Sockets就是这样的方案。
//需要先npm install ws//服务器端var Server = require('ws').Server;var wss = new Server({ port:2000}); wss.on('connection',function(ws){ ws.on('message',function(data){ ws.send('你好,客户端,我是服务器!'); console.log(data); }) });//node客户端var WebSocket = require('ws');var socket = new WebSocket('ws://localhost:2000/'); socket.on('open',function(){ socket.send('你好,服务器,我是客户端'); }); socket.on('message',function(event){ console.log(event); })//html客户端(注:浏览器客户端与node客户端只需要一种)<script> var socket = new WebSocket('ws://localhost:2000/'); socket.onopen = function(){ }; socket.onmessage = function(event){ console.log(event.data) } </script>
以上呢,只是个简单的例子,给大家了解一下在企业级项目中,消息发送的几种方式。
一、项目框架的选取
由于第一次一个做这种项目,我想的是为了以后高并发的处理,打算使用分布式,但是分布式很难一下就上手。暂时就先用maven聚合工程来做了,。到后期有时间再改。
技术包括:maven+springboot+spring security+admin lte(前端)
首先建了一个父工程,新建一个pom.xml文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> </parent> <groupId>com.fyd</groupId> <artifactId>smart-web-im</artifactId> <packaging>pom</packaging> <version>1.0.0-SNAPSHOT</version> <modules> <module>smart-web-im-bin</module> <module>smart-web-im-common</module> <module>smart-web-im-web</module> <module>smart-web-im-login</module> <module>smart-web-im-base</module> </modules> <!-- FIXME change it to the project's website --> <url>https://www.javagoing.com</url> <build> <finalName>smart-web-im-root</finalName> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>${project.build.jdk}</source> <target>${project.build.jdk}</target> <encoding>UTF-8</encoding> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.6</version> <configuration> <encoding>UTF-8</encoding> </configuration> </plugin> </plugins> </build> <!-- 配置 --> <properties> <!-- base setting --> <project.build.jdk>1.8</project.build.jdk> <example.version>1.0.0-SNAPSHOT</example.version> <!-- spring --> <spring.boot.version>1.5.6.RELEASE</spring.boot.version> <!-- mybatis --> <mybatis-spring-boot>1.3.0</mybatis-spring-boot> <mybatis.pagehelper.version>1.1.3</mybatis.pagehelper.version> <!-- druid --> <druid.version>1.1.2</druid.version> <!-- DataBase version --> <mysql.connector.version>6.0.6</mysql.connector.version> <!-- log4j --> <slf4j.version>1.7.25</slf4j.version> <log4j.version>2.8.2</log4j.version> <!-- commons --> <commons.fileupload.version>1.3.3</commons.fileupload.version> <commons.codec.version>1.10</commons.codec.version> <commons.net.version>3.6</commons.net.version> <commons.logging.version>1.2</commons.logging.version> <commons.collections.version>3.2.2</commons.collections.version> <commons.lang3.version>3.6</commons.lang3.version> <!-- fastjson --> <fastjson.version>1.2.36</fastjson.version> </properties> <dependencies> <!-- spring start --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 这里指定打包的时候不再需要tomcat相关的包 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <!--idea需要注释掉,idea编译的时候找不到provided包--> <!--<scope>provided</scope>--> </dependency> <!-- test --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- mybatis start --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>${mybatis-spring-boot}</version> </dependency> <dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>${mybatis.pagehelper.version}</version> </dependency> <!-- mysql-connector --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.connector.version}</version> </dependency> <!-- Druid--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>${druid.version}</version> </dependency> <!-- Alibaba Fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>${fastjson.version}</version> </dependency> <!-- log start --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- apache start --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons.fileupload.version}</version> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>${httpclient.version}</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>${commons.codec.version}</version> </dependency> <dependency> <groupId>commons-net</groupId> <artifactId>commons-net</artifactId> <version>${commons.net.version}</version> </dependency> <dependency> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> <version>${commons.logging.version}</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>${commons.collections.version}</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>${commons.lang3.version}</version> </dependency> <!--thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--使其不必严格校验html标签--> <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> </dependency> <!-- spring security依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> </dependencies> </project>
父工程就这一个文件。
接下来是启动模块 我给它命名为smart_web_bin,它的pom文件如下:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>smart-web-im</artifactId> <groupId>com.fyd</groupId> <version>1.0.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <version>${example.version}</version> <artifactId>smart-web-im-bin</artifactId> <packaging>war</packaging> <name>smart-web-im-bin</name> <!-- FIXME change it to the project's website --> <url>https://www.javagoing.com</url> <dependencies> <!--web模块引入--> <dependency> <groupId>com.fyd</groupId> <artifactId>smart-web-im-web</artifactId> <version>${example.version}</version> </dependency> <!--base模块引入,一些配置类和共用类--> <dependency> <groupId>com.fyd</groupId> <artifactId>smart-web-im-base</artifactId> <version>${example.version}</version> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>com.fyd.Application</mainClass> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle --> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle --> <plugin> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> </plugin> <plugin> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- maven打包的时候告诉maven不需要web.xml,否刚会报找不到web.xml错误 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <version>3.1.0</version> <configuration> <failOnMissingWebXml>false</failOnMissingWebXml> </configuration> </plugin> </plugins> </pluginManagement> </build> </project>
此时的项目结构如下:
application.yml的内容:
# [Server] server: address: 127.0.0.1 port: 9900 context-path: /im session: timeout: -1 tracking-modes: cookie tomcat: max-threads: 100 uri-encoding: UTF-8 # [Spring] spring: datasource: url: jdbc:mysql://javagoing.com:33061/smart_web_im?useUnicode=true&characterEncoding=UTF-8 data-username: root data-password: fuyadong driver-class-name: com.mysql.cj.jdbc.Driver schema: classpath*:schema-*.sql initialize: false continue-on-error: false # [thymeleaf静态资源配置] thymeleaf: prefix: classpath:/WEB-INF/templates/ suffix: .html mode: LEGACYHTML5 encoding: UTF-8 content-type: text/html cache: false
Application.java内容如下:
/* * All rights Reserved, Designed By DataDriver * Copyright: DataDriver.Inc * Company: Zhuo Wo Infomation Technology (ShangHai) CO.LTD */ package com.fyd; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @SpringBootApplication @EnableAutoConfiguration public class Application extends WebMvcConfigurerAdapter { /*** * 配置静态访问资源 * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { if(!registry.hasMappingForPattern("/static/**")){ registry.addResourceHandler("/static/**").addResourceLocations("classpath:/WEB-INF/static/"); } super.addResourceHandlers(registry); } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
至此。运行main方法就可以启动该项目。
ps:敲黑板!!!!!(在idea中会碰到各种各样的问题)
碰到很多坑,聚合项目有时候改完不能立刻生效:
需要先:clean,compile,install,才行。。。
还有时候,新建的模块不能被扫描到controller,server等注解类:
需要下面这样操作:reimport,更新一下maven项目