SpingCloud:Gateway+Nginx+Stomp+Minio构建聊天室并进行文件传输
注:本人使用阿里云服务器(安装mino)+本地虚拟机(安装nginx)进行,理论上完全在本地进行也可以。
1、前期准备:
1、将本地虚拟机设置为静态ip且能ping通外网,参考网址:https://www.cnblogs.com/wsongl/p/14534170.html(完全照做就行)
2、安装nginx:
#创建文件夹
mkdir -p /mydata/nginx #下载并启动
docker run -p 80:80 --name nginx -d nginx:1.10 #将容器内的配置文件拷贝到当前nginx目录(注意此时我们的位置在mydata文件夹下,不要忘了后面有个点)
docker container cp nginx:/etc/nginx . #停止nginx容器并删除nginx镜像
docker stop nginx
docker rm nginx #重命名nginx为conf
mv nginx conf #再次创建nginx文件夹
mkdir nginx #将conf移动到nginx
mv conf nginx/ #创建实例
docker run -p 80:80 --name nginx \
-v /mydata/nginx/html:/usr/share/nginx/html \
-v /mydata/nginx/logs:/var/log/nginx \
-v /mydata/nginx/conf:/etc/nginx \
-d nginx:1.10
3、安装minio:
docker run -it -p 9000:9000 -p 9001:9001
--name minio \
-d --restart=always \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=123456" \
-v /mydata/minio/data:/data \
-v /mydata/minio/config:/root/.minio \
minio/minio server /data \
--console-address ":9001" MINIO_ROOT_USER:登录账号
MINIO_ROOT_PASSWORD:登陆密码
2、Nginx反向代理配置:
2.1:官方文档参考:
1、minio官方文档:https://docs.min.io/(不要看中文文档,方法已经过期了)
2、gateway官方文档:https://spring.io/projects/spring-cloud-gateway
2.2:引入包:
gateway网关服务的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 https://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>2.5.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.social</groupId>
<artifactId>whales-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>whales-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2.2.1.RELEASE</version>
<!-- 使用spring loadbalancer,弃用ribbon -->
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
<version>2.2.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>
gateway的application.yml部分配置:
spring:
cloud:
gateway:
routes:
- id: community_host_route
uri: lb://whales-community
predicates:
- Host=community.whales.com
本人使用nacos为服务注册中心,在gateway的application.properties文件中配置:
spring.cloud.nacos.discovery.server-addr=119.xx.xx.xx:8848 #nacos安装的地址
spring.application.name= whales-gateway
server.port=88
2.3:nginx配置:
在linux上使用ifconfig检测其IP地址:
我们选取先前配置好的静态IP地址,在win10的hosts上配置相关域名,本人使用SwitchHosts软件直接进行配置:
随后在win上检查ip:
在linux上打开nginx.config进行配置,whales是可以替换成其他自己喜欢的命名,但必须与之后conf.d配置文件内容里的proxy_pass后的名字相同:
#进入nginx配置文件夹,打开nginx.config
cd /mydata/nginx/conf/
vi nginx.conf #编辑文件内容:添加黄色部分字体
user nginx;
worker_processes 1; error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid; events {
worker_connections 1024;
} http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'; access_log /var/log/nginx/access.log main; sendfile on;
#tcp_nopush on; keepalive_timeout 65; #gzip on;
upstream whales{
server 192.168.6.49:88;
}
include /etc/nginx/conf.d/*.conf;
}
配置conf.d下的配置文件:
#进入conf.d文件下
cd /mydata/nginx/conf/conf.d/ #复制一份default.conf并进行编辑
cp default.conf whales.conf
vi whales.conf #编辑文件内容:加量部分
server {
listen 80;
server_name whales.com *.whales.com; #charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main; location / {
proxy_set_header Host $host;
proxy_pass http://whales;
} #error_page 404 /404.html; # redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
} # proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
截止目前nginx+gateway的反向代理完成。
2.4:nginx反向代理原理图:
3、minio配置实现
3.1:MinioConfig配置类
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioConfig { private String endpoint; private String accessKey; private String secretKey; private String bucketName; @Bean
public MinioClient minioClient(){
MinioClient minioClient =
MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return minioClient;
}
}
进入Minio控制台生成密钥:
生成存储桶:
3.2:编写Minio工具类
/**
* 实体类
* 爪哇笔记:https://blog.52itstyle.vip
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "min.io")
public class MinIoComponent{ private String endpoint;
private String accessKey;
private String secretKey; @Bean
public MinioClient minClient(){
// 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象
MinioClient minioClient = new MinioClient.Builder().endpoint(endpoint).credentials(accessKey, secretKey).build();
return minioClient;
}
}
工具类中的方法可以在官方文档中找到然后进行实际应用的修改
@Component
public class MinioUtils {
@Autowired
MinioClient minioClient; @SneakyThrows
public void putObject(MultipartFile file, String bucket, String pathObject) {
InputStream stream = file.getInputStream();
String type = file.getContentType();
//System.out.println(type);
minioClient.putObject(
PutObjectArgs.builder().bucket(bucket).object(pathObject).stream(
stream, file.getSize(), -1)
.contentType(type)
.build());
} @SneakyThrows
public void putObject(File file, String bucket, String pathObject) {
InputStream stream = new FileInputStream(file);
String type = file.getName().split("\\.")[1];
System.out.println(type);
minioClient.putObject(
PutObjectArgs.builder().bucket(bucket).object(pathObject).stream(
stream, file.length(), -1)
.contentType(type)
.build());
}
}
这里着重讲解下stream中的意义,有文件流、文件大小、分块大小。由于minio在传输中是将文件分块传输,所以有分块大小设置:
application.properties进行参数配置:
min.io.endpoint = http://119.23.57.189:9000
#生成密钥
min.io.accessKey =3B-----------------61
min.io.secretKey =+ANgi6S+----------------------2pqff3gS
#生成存储桶
min.io.bucket = whales-picture
3.3:实际代码
@Override
public void sendPhotoToGroup(String groupId, String userId, MultipartFile file) throws ExecutionException, InterruptedException {
//设置组与当日时间:文件夹命名方式:groupxxxxxxx/yyyyMMdd
SimpleDateFormat s = new SimpleDateFormat("yyyyMMdd");//设置日期格式
String name = file.getOriginalFilename();
String type = file.getContentType();
String format = s.format(new Date());
//设置存储在minio的文件路径
String pathObject = "/Group" + groupId + "/" + format + "/" + name;
minioUtils.putObject(file, MINIO_BUCKET, pathObject);
}
4、基于Stomp协议搭建Websocket聊天室:
4.1:WebSocketStompConfig配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {
// 这个方法的作用是添加一个服务端点,来接收客户端的连接。
@Override
public void registerStompEndpoints(StompEndpointRegistry registry){
registry.addEndpoint("/whaleSocial").withSockJS();
}
@Override
// 这个方法的作用是定义消息代理,通俗一点讲就是设置消息连接请求的各种规范信息。
public void configureMessageBroker(MessageBrokerRegistry registry){
//服务端给客户端发消息的地址的前缀信息
//@SenTo/subscribe
registry.enableSimpleBroker("/member");
//客户端给服务端发消息的地址的前缀
//@SubscribeMapping/subscribe只接收此前缀的消息
//@MessageMapping/send
registry.setApplicationDestinationPrefixes("/group");
}
}
4.2:Controller类
@Controller
/*@RequestMapping("/")*/
public class CommunityController {
@ResponseBody
@PostMapping("/photos/{groupId}/{userId}")
public GraceJSONResult sendPhoto(@PathVariable String groupId, @PathVariable String userId, MultipartFile file) throws ExecutionException, InterruptedException{
sendMessageService.sendPhotoToGroup(groupId, userId, file);
return GraceJSONResult.ok();
}
}
4.3:Service类
@Service
public class CommunitySendMessageServiceServiceImpl implements CommunitySendMessageService { private final static String REDIS_MESSAGE_USER = "redis_message_user";
private final static String GROUP_MEMBER = "redis_group_member";
private final static String GROUP_MESSAGES = "redis_messages";
private final static String MINIO_BUCKET = "whales-picture"; @Resource
private SimpMessagingTemplate simpMessagingTemplate; @Autowired
private StringRedisTemplate redisTemplate; @Resource
private GroupMembersMapper groupMembersMapper; @Autowired
MinioUtils minioUtils; @Autowired
ThreadPoolExecutor executor; @Override
public void sendPhotoToGroup(String groupId, String userId, MultipartFile file) throws ExecutionException, InterruptedException {
//设置组与当日时间:文件夹命名方式:groupxxxxxxx/yyyyMMdd
SimpleDateFormat s = new SimpleDateFormat("yyyyMMdd");//设置日期格式
String name = file.getOriginalFilename();
String type = file.getContentType();
String format = s.format(new Date());
//设置存储在minio的文件路径
String pathObject = "/Group" + groupId + "/" + format + "/" + name;
minioUtils.putObject(file, MINIO_BUCKET, pathObject);
//获取可访问的url文件路径
//TODO 将地址更换为域名,photosUrl是将回显地址给传前端
String photosUrl = "http://119.xx.xx.189:9000/" + MINIO_BUCKET + pathObject;
simpMessagingTemplate.convertAndSend("/member/photos/" + groupId, photosUrl);
}
}
4.4:前端测试页面:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8" />
<title>Spring Boot+WebSocket+广播式</title>
<script src="https://cdn.bootcdn.net/ajax/libs/sockjs-client/1.5.1/sockjs.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.5.1.min.js"></script>
</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
<!-- <div>
<button id="enter" onclick="enter();">进入群聊</button>
<button id="esc" disabled="disabled" onclick="disconnect();">退出群聊</button>
</div>-->
<div>
<button id="connect" onclick="connect();">进入群聊</button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">退出群聊</button>
</div>
<div id="conversationDiv">
<form action="/photos/1148973713/18819776464" method="post" enctype="multipart/form-data" target="rfFrame">
<input type="file" id="file" name="file">
<input type="submit" value="提交" /><p>
</form>
<img id="photos"/>
</div>
</div>
<script type="text/javascript">
var stompClient = null; function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$('#response').html();
} function connect() {
// 连接 SockJs 的 endpoint 名称为 "/whaleSocial"
var socket = new SockJS('/whaleSocial',null,{timeout: 15000});
// 使用 STOMP 子协议的 WebSocket 客户端
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);// 通过 stompClient.subscribe 订阅
stompClient.subscribe('/member/photos/1148973713', function(respnose){
console.log("This is:"+respnose.body)
showPhotosResponse(respnose.body);
});
});
}function disconnect() {
// 断开连接
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}function showPhotosResponse(message){
$("#photos").attr("src",message)
}
</script>
</body>
</html>
此时如果使用域名访问会报错:Incompatibile SockJS! Main site uses: "1.1.5", the iframe: "1.0.0",因为websoket必须要使用HTTP/1.1通信协议,所以要到nginx再新增配置:
server {
listen 80;
server_name whales.com *.whales.com; #charset koi8-r;
#access_log /var/log/nginx/log/host.access.log main; location / {
proxy_set_header Host $host;
proxy_pass http://whalesocial;
}
#前缀whaleSocial必须与WebSocketConfig中Stomp服务端设置的节点名字相同
location /whaleSocial/{
proxy_pass http://192.168.6.49:11000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
} #error_page 404 /404.html; # redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
配置完后就能使用域名进入网页发送websocket消息,并将图片上传Minio并进行回显。
SpingCloud:Gateway+Nginx+Stomp+Minio构建聊天室并进行文件传输的更多相关文章
- 使用Beetle简单构建聊天室程序
之前已经讲解了Beetle简单地构建网络通讯程序,那程序紧紧是讲述了如何发送和接收数据:这一章将更深入的使用Beetle的功能,主要包括消息制定,协议分析包括消息接管处理等常用的功能.为了更好的描述所 ...
- 使用Angular和Nodejs搭建聊天室
一,利用Node搭建静态服务器 这个是这个项目的底层支撑部分.用来支持静态资源文件像html, css, gif, jpg, png, javascript, json, plain text等等静态 ...
- 真刀实战地搭建React+Webpack+Express搭建一个简易聊天室
一.前面bb两句 因为自惭(自残)webpack配置还不够熟悉,想折腾着做一个小实例熟悉.想着七夕快到了,做一个聊天室自己和自己聊天吧哈哈.好了,可以停止bb了,说一下干货. 二. 这个项目能学到啥? ...
- Python实现网络多人聊天室
网络多人聊天室 文件结构: chatroom ├── client.py # 客户端代码 ├── language.py # 语言文件 ├── server.py # 服务端代码 └── set ...
- 使用SignalR构建一个最基本的web聊天室
What is SignalR ASP.NET SignalR is a new library for ASP.NET developers that simplifies the process ...
- 百行go代码构建p2p聊天室
百行go代码构建p2p聊天室 百行go代码构建p2p聊天室 1. 上手使用 2. whisper 原理 3. 源码解读 3.1 参数说明 3.1 连接主节点 3.2 我的标识 3.2 配置我的节点 3 ...
- .net core下使用FastHttpApi构建web聊天室
一般在dotnet core下构建使用web服务应用都使用asp.net core,但通过FastHttpApi组建也可以方便地构建web服务应用,在FastHttpApi功能的支持下构建多人聊天室是 ...
- 以太坊系列之十八: 百行go代码构建p2p聊天室
百行go代码构建p2p聊天室 百行go代码构建p2p聊天室 1. 上手使用 2. whisper 原理 3. 源码解读 3.1 参数说明 3.1 连接主节点 3.2 我的标识 3.2 配置我的节点 3 ...
- 利用socket.io构建一个聊天室
利用socket.io来构建一个聊天室,输入自己的id和消息,所有的访问用户都可以看到,类似于群聊. socket.io 这里只用来做一个简单的聊天室,官网也有例子,很容易就做出来了.其实主要用的东西 ...
- BeetleX之快速构建Web多房间聊天室
其实构建一个Web多房间聊天室也并不是什么困难的技术,借助于websocket就可以轻松实现多用户在线实时通讯交互:在这里主要介绍一下在BeetleX和BeetleXjs的支持下如何让这个功能实现的更 ...
随机推荐
- 在线免费chatgpt网页版-支持gpt4
为了吸引更多的用户体验最先进的自然语言处理技术,我们推出了在线免费ChatGPT.这是一个基于OpenAI训练的大型语言模型,它可以提供智能响应.自然对话和语音识别等功能.不仅如此,我们还提供了完全免 ...
- HCL实验:5.单臂路由实现不同vlan通信
使用单臂路由实现不同vlan 互通 拓扑图 网关均为所在网段的第一个地址 交换机配置 创建vlan 划分端口 配置端口类型 显示简要信息 路由器配置 路由器的端口默认关闭,需要手动开启 进行子端口的划 ...
- requests高级用法、代理池搭建
requests高级用法 1.自动携带cookie的session对象 # session对象---->已经模拟登录上了一些网站--->单独把cookie 取出来 import reque ...
- Jenkins自动化测试构建完成 发送钉钉消息
背景 有时自动化测试完成后,我们可以通过构建完成后给钉钉群发消息,这样就能及时通知到所有人员了. 接入流程 1:建立钉钉机器人,可以通过群助手,添加机器人,增加WebHook自定义接入,然后添加完成会 ...
- Centos7 升级 Kubernetes(k8s) 集群
目录 一.系统环境 二.前言 三.Kubernetes(k8s) 集群升级简介 四.升级master主节点 4.1 升级kubeadm 4.2 升级各个组件 4.3 升级 kubelet 和 kube ...
- 活动干货|泛娱乐App出海东南亚深度解析
泛娱乐社交出海,还有哪些机会点? 为助力出海企业把握增长红利,即构科技特开设<出海"构"有料--泛娱乐出海系列直播>,从热门国家的特性洞察.玩法解决方案到技术服务经验分 ...
- Day03_Java_作业
A:选择题 1.给出以下代码,请问表达式grade==70在以下两条语句中是否获得执行? boolean flag = false && grade == 70;//第一条语句 boo ...
- Map集合_HashMap_TreeMap_等_小记
Map是一种依照键值对数据存储元素的容器. Map中的元素是两个对象,一个对象作为键,一个对象作为值.一个键(key)和它对应的值构成map集合中的一个元素.Map集合的数据结构只跟键有关,键不可以重 ...
- Linux 中设备的分类及网络设备接口路径
设备分类 字符设备 块设备 网络设备 参考文档: 手把手教Linux驱动 网络设备位置 [root@localhost ~]# cd /sys/class/net/ [root@localhost n ...
- UPS设备在物流机房中的应用浅析
1 UPS 简介 UPS 即不间断电源 (Uninterruptible Power Supply),是一种含有储能装置的不间断电源.主要用于给部分对电源稳定性要求较高的设备,提供不间断的电源. 当市 ...