Docker部署 直播实时弹幕+聊天室 vue+websocket+spring-boot

Docker部署 实现弹幕+聊天室 vue+websocket+spring-boot

Docker 部署 实现直播后续
参考文章 https://blog.csdn.net/qq_36710522/article/details/90547819
https://blog.csdn.net/qq_36710522/article/details/97103229

做了一些小修改

首先从弹幕功能开始

vue实现弹幕功能很简单,有一个插件叫vue-baberrage,我们只需要安装这个插件就能实现弹幕

cnpm i vue-baberrage

main.js加入

import { vueBaberrage } from 'vue-baberrage'
Vue.use(vueBaberrage);

界面

<vue-baberrage
		:isShow="barrageIsShow"
		:barrageList="barrageList"
		:maxWordCount="maxWordCount"
		:throttleGap="throttleGap"
		:loop="barrageLoop"
		:boxHeight="boxHeight"
		:messageHeight="messageHeight"
		:boxWidth="boxWidth-300"
		>
	</vue-baberrage>
<i-input v-model="sendmsg" placeholder="请输入..." style="width: 300px"></i-input>
<i-button type="primary" @click="addToList(2)">发送弹幕</i-button>
import { MESSAGE_TYPE } from 'vue-baberrage'
			this.barrageList.push({//把数据加进去就可以了
          		id: ++this.currentId,
          		avatar: this.msgHeadImageURL,
          		msg: messageData.chatContent,
          		barrageStyle: "normal",
          		time: 5,
          		type: MESSAGE_TYPE.NORMAL,
          		position: "bottom"
          	});

然后是聊天室

聊天室需要websocket来实现,后端我用的spring-boot 搭建比较快捷

先是前端页面

<template>    
	<div>         
		WebSocket//这是一个样例
	</div>    
</template>
<script>
export default {        
	created() { 
		this.initWebSocket(); //进入页面开始初始化websocket建立连接      
	},        
	destroyed() {
		this.websocketclose(); //离开页面断开连接       
	},        
	methods: {            
		initWebSocket() {                
			// WebSocket与普通的请求所用协议有所不同,ws等同于http,wss等同于https                
			this.websock = new WebSocket("wss://localhost"); //这里是websocket服务地址,这里的地址可以前端根据后台地址参数规则拼成,也可以向后端请求         
			this.websock.onopen = this.websocketonopen;                
			this.websock.onerror = this.websocketonerror;                
			this.websock.onmessage = this.websocketonmessage;                
			this.websock.onclose = this.websocketclose;              
		},              
		websocketonopen() {                
			console.log("WebSocket连接成功");              
		},              
		websocketonerror(e) {                
			console.log("WebSocket连接发生错误");              
		},              
		websocketonmessage(e) {                
			console.log(e.data);                // console.log(e);              
		},              
		websocketclose(e) {                
			console.log("connection closed (" + e.code + ")");              
		},              
	 }    
}
</script>

页面源码

<template>
  <div>

    <div  class="barrages-drop">
      <video
      id="videoId"
      controls = "true"
      :height="boxHeight+350"
      :width="boxWidth">
    </video>
    <vue-baberrage
    :isShow="barrageIsShow"
    :barrageList="barrageList"
    :maxWordCount="maxWordCount"
    :throttleGap="throttleGap"
    :loop="barrageLoop"
    :boxHeight="boxHeight"
    :messageHeight="messageHeight"
    :boxWidth="boxWidth-300"
    >
  </vue-baberrage>
  <Card style="float: right;height: 500px;width: 500px;">
    <Scroll  height="450">
  <ul v-for="item in msgs">
  <li>{{item.chatAvatar}}   {{item.chatTime}}</li>
  <li>  {{item.chatContent}}</li>
</ul>
</Scroll>
  <p  ><i-input v-model="sendmsg" placeholder="请输入..." style="width: 300px"></i-input>
<i-button type="primary" @click="addToList(1)">发送消息</i-button></p>
</Card>
</div>

<i-input v-model="sendmsg" placeholder="请输入..." style="width: 800px"></i-input>
<i-button  type="primary" @click="addToList(2)">发送弹幕</i-button>

<p>当前在线:{{onlineNumber}}</p>
<div v-for="item in onlineUserList">
  <p>{{item}}</p>
</div>
</div>
</template>
<script>
  import { onlineList } from '@/api/data'
  import { MESSAGE_TYPE } from 'vue-baberrage'
  export default {
    name: 'level_2_1',
    data () {
      return {
        type:0,
        sendmsg: '土狗就是个辣鸡~',
        barrageIsShow: true,
        messageHeight: 3,
        boxHeight: 150,
        boxWidth:1000,
        barrageLoop: true,
        maxWordCount: 3,
        throttleGap: 5000,
        barrageList: [],
        t:'',
        userId:0,
        userName:'游客',
        msgs:[],
        currentId:1,
        msgHeadImageURL:'https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3064584167,3502823640&fm=26&gp=0.jpg',
        sendTime:'',
        chatRoomWebsocket:'',
        onlineUserList:[],
        onlineNumber:0

      }
    },
    created() {
      this.userId=parseInt(Math.random()*1000000);
      this.userName=this.userName+this.userId;
      console.log(this.userName);
      this.t = window.setInterval(this.getChatUserList, 5000);
    },
    beforeDestroy() {
      clearInterval(this.t);
    },
    destroyed() {
      this.websocketClose();
    },
    mounted(){
      console.log(this.$route.query.id);
      if (flvjs.isSupported()){
        var videoElement = document.getElementById('videoId');
        var flvPlayer = flvjs.createPlayer({
          type: 'flv',
          "isLive": true,
          url: 'http://62.234.60.111:7001/stream/'+this.$route.query.id+'.flv'
        });
        flvPlayer.attachMediaElement(videoElement);
        flvPlayer.load();
          // flvPlayer.play();
      }
  },
  methods: {       
    initWebSocket() {
      if (typeof WebSocket != "undefined") {
        this.supported = "支持 websocket";
      } else {
        this.supported = "不支持 websocket";
      }
      //ws地址
      const wsuri = "ws://62.234.60.111:8888/websocket/" + this.$route.query.id + "/" + this.type + "/" + this.userId; 
      this.chatRoomWebsocket = new WebSocket(wsuri);
      this.chatRoomWebsocket.onerror = this.websocketOnError;
      this.chatRoomWebsocket.onmessage = this.websocketOnMessage;
      this.chatRoomWebsocket.onclose = this.websocketOnClose;       
  },              
    //连接发生错误的回调方法
    websocketOnError() {
      console.log("WebSocket 连接发生错误");
    },
    //接收到消息的回调方法
    websocketOnMessage(event) {
      console.log(event);
      let data = JSON.parse(event.data);
      // this.msgHeadImageURL = data.chatImage ? data.chatImage : userPhoto;
      if (data.chatUser != this.userId&&data.chatType=='1') {
        this.msgs.push(data);
      }
      if (data.chatUser != this.userId&&data.chatType=='2') {
        this.barrageList.push({
          id: ++this.currentId,
          avatar: this.msgHeadImageURL,
          msg: data.chatContent,
          barrageStyle: "normal",
          time: 5,
          type: MESSAGE_TYPE.NORMAL,
          position: "bottom"
        });
      }
  },
    //连接关闭的回调方法
    websocketOnClose(e) {
      console.log("WebSocket 连接关闭", e);
    },
    //关闭 WebSocket 连接
    websocketClose() {
      this.chatRoomWebsocket.close();
    },       
    addToList(type) {
      if (this.sendmsg.split(" ").join("").length != 0) {
        //获取当前时间
        var time = new Date();
        this.sendTime =
        time.getHours() +
        ":" +
        (time.getMinutes() < 10
          ? "0" + time.getMinutes()
          : time.getMinutes());
        let messageData = {
          chatContent: type==2?this.userName+":"+this.sendmsg:this.sendmsg,
          chatUser: this.userId,
          chatAvatar: this.userName,
          chatImage: this.msgHeadImageURL,
          chatTime: this.sendTime,
          chatType:type
        };
        if (this.chatRoomWebsocket.readyState != "1") {
          // 如果按下按钮时不是连接状态,重连并等到连接成功再发送消息
          this.initWebSocket();
          this.chatRoomWebsocket.onopen = () => {
            this.chatRoomWebsocket.send(JSON.stringify(messageData));
            //发送消息
            if (type==1) {
              this.msgs.push({
                chatContent: this.sendmsg,
                chatUser: this.userId,
                chatAvatar: this.userName,
                chatImage: this.msgHeadImageURL,
                chatTime: this.sendTime,
              });
            }else{
              this.barrageList.push({
                id: ++this.currentId,
                avatar: this.msgHeadImageURL,
                msg: messageData.chatContent,
                barrageStyle: "normal",
                time: 5,
                type: MESSAGE_TYPE.NORMAL,
                position: "bottom"
              });
            }
            this.sendmsg = "";
        };
    } else {
      this.chatRoomWebsocket.send(JSON.stringify(messageData));
          //发送消息
          if (type==1) {
            this.msgs.push({
              chatContent: this.sendmsg,
              chatUser: this.userId,
                chatAvatar: this.userName,
                chatImage: this.msgHeadImageURL,
                chatTime: this.sendTime,
            });
          }else{
            this.barrageList.push({
              id: ++this.currentId,
              avatar: this.msgHeadImageURL,
              msg: messageData.chatContent,
              barrageStyle: "normal",
              time: 5,
              type: MESSAGE_TYPE.NORMAL,
              position: "bottom"
            });
          }
          this.sendmsg = "";
      }
  }
      // list.forEach((v) => {
      //  this.barrageList.push({
      //    id: v.id,
      //    avatar: v.avatar,
      //    msg: v.msg,
      //    time: v.time,
      //    type: MESSAGE_TYPE.NORMAL,
      //    barrageStyle: v.barrageStyle
      //  });
      // });
    },
    getChatUserList() {
      let data = {
        sid: this.$route.query.id,
        type: 1
      };
      onlineList(data).then(res => {
        // console.log(res);
        // if (res.data.state == 200) {
          this.onlineNumber = res.data.count;
          this.onlineUserList = res.data.userList;
          console.log(this.onlineNumber);
          console.log(this.onlineUserList);
        // }
      });
  },

}

}
</script>
<style  lang="less">
.barrages-drop {
  .blue {
    border-radius: 100px;
    background: #e6ff75;
    color: #fff;
  }

  .green {
    border-radius: 100px;
    background: #75ffcd;
    color: #fff;
  }
  .red {
    border-radius: 100px;
    background: #e68fba;
    color: #fff;
  }
  .yellow {
    border-radius: 100px;
    background: #dfc795;
    color: #fff;
  }
  .baberrage-stage {
    position: absolute;
    width: 100%;
    height: 212px;
    overflow: hidden;
    top: 0;
    margin-top: 130px;
  }
}
</style>

后端pom加入

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
            <version>1.3.5.RELEASE</version>
</dependency>

WebSocket配置类

package com.macro.mall.tiny.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    /**
     * 注入ServerEndpointExporter,
     * 这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     * @return
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebSocket操作类

package com.macro.mall.tiny.controller;

import com.alibaba.druid.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

@RestController
@ServerEndpoint(value = "/websocket/{sid}/{type}/{userId}")   //房间号、类型(1,直播聊天)、用户Id、
@Component
public class WebSocket {

    private static WebSocket webSocket;
    private static Logger logger = LoggerFactory.getLogger(WebSocket.class);

    //当前连接数
    private static int onlineCount = 0;

    //存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();

    //与某个客户端的连接会话
    private Session session;

    //客户端唯一标识sid(直播ID)
    private String sid = "";
    //用户类型
    private Integer type=0;
    //用户ID
    private Integer userId = 0;
    //用户昵称
    private String nickName="";
    //用户头像地址
    private String headImageUrl="";

    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }

    @PostConstruct
    public void init() {
        webSocket = this;
    }

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid,  @PathParam("type") Integer type,@PathParam("userId") Integer userId) {
        moreWindow(sid,userId,type);
        //在线数加1
        addOnlineCount();
        this.session = session;
        //加入set中
        webSocketSet.add(this);
        this.sid = sid;
        this.userId = userId;
        this.type=type;
        logger.info("用户ID:"+userId+"用户昵称:"+nickName+"新连接:sid=" + sid + " 当前在线人数" + getOnlineCount());
        try {
            sendMessage("连接成功");
        } catch (IOException e) {
            logger.error("websocket IO异常");
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        //群发消息
        for (WebSocket item : webSocketSet) {
            try {
                if(item.sid.equals(this.sid)){
                    item.sendMessage(message);
                    System.out.println("--------------------"+message+"总人数"+onlineCount);
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 同一用户打开多个窗口问题
     *
     * @param sid
     */
    public void moreWindow(String sid,Integer userId,Integer type) {
        if (StringUtils.isEmpty(sid)) {
            return;
        }
        if(webSocketSet.isEmpty()){
            return;
        }
        for (WebSocket item : webSocketSet) {
            if (item.sid.equals(sid)&&item.userId.equals(userId)&&item.type==type) {
                //已经有相同的了
                webSocketSet.remove(item);
                subOnlineCount();
            }
        }
    }

    /**
     * 发送消息给指定用户
     *
     * @param message
     * @param sid
     */
    public static void sendMessage(String message, @PathParam("sid") String sid) {
        logger.info("发送消息:sid=" + sid + " message:" + message);
        for (WebSocket item : webSocketSet) {
            try {
                if (sid == null) {
                    item.sendMessage(message);
                    System.out.println("+++++++++++++++"+message);
                } else if (item.sid.equals(sid)) {
                    logger.info("开始发送消息:sid=" + sid);
                    item.sendMessage(message);
                }
            } catch (IOException e) {
                logger.error("发送消息失败 sid:"+sid,e);
                continue;
            }
        }
    }

    @OnClose
    public void onClose() {
        logger.info("连接关闭:sid=" + sid + " 当前在线人数" + getOnlineCount());
        webSocketSet.remove(this);
        subOnlineCount();
    }

    @OnError
    public void onError(Session session, Throwable error) {
        error.printStackTrace();
    }

    /**
     * 当前在线人数
     * @return
     */
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    /**
     * 添加在线人数
     */
    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    /**
     * 减少在线人数
     */
    public static synchronized void subOnlineCount() {
        if (WebSocket.onlineCount <= 0) {
            WebSocket.onlineCount = 0;
            return;
        }
        WebSocket.onlineCount--;
    }

    /**
     *
     * 人数列表
     */
    @PostMapping(value = "/numberList")
    public  Map numberList(String sid,Integer type,@RequestBody Map data){
        sid=data.get("sid").toString();
        type=1;
        Map map=new HashMap<>();
        List<String> userList=new ArrayList<>();
        Integer count=0;
        for (WebSocket item : webSocketSet) {
            System.out.println(item.sid);
            if(item.sid!=null&&item.sid.equals(sid)){
                userList.add("游客"+item.userId.toString());
                count++;
            }
        }
        map.put("userList",userList);
        map.put("count",count);
        System.out.println(map.toString());
        return map;
    }
}



上线的话,解决跨域问题 可以用nginx反向代理 也可以在后端加个配置
跨域配置

package com.macro.mall.tiny.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


/**
 * Author:js
 * Date:2019/
 */
@Configuration
@EnableWebMvc
public class CorsConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }
}


带websocket的项目打包会报错 需要在pom加入

<plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>

然后点package
在这里插入图片描述
在这里插入图片描述
jar包完成
连接服务器rz传文件

[root@VM_0_2_centos ~]# rz

写Dockerfile文件

[root@VM_0_2_centos ~]# cat Dockerfile 
FROM java:8
VOLUME /tmp
ADD mall-tiny-02-0.0.1-SNAPSHOT.jar app.jar
RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

运行build命令 运行run命令

[root@VM_0_2_centos ~]# docker build -t mall .
[root@VM_0_2_centos ~]# docker run -d -p 8888:8081 mall
[root@VM_0_2_centos ~]# docker ps
CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS                  PORTS                                            NAMES
93a43f2ff97a        mall                   "java -Djava.secur..."   21 hours ago        Up 21 hours             0.0.0.0:8888->8081/tcp                           naughty_montalcini
b2c29dcef5ef        monelgq/rtmp-httpflv   "/livego"                2 days ago          Up 15 hours             0.0.0.0:1935->1935/tcp, 0.0.0.0:7001->7001/tcp   agitated_clarke

到这里java项目已经运行起来了
前端打包

cnpm run build

会生成dist文件夹,压缩上传服务器

[root@VM_0_2_centos ~]# rar x dist.rar//解压

然后运行nginx 把容器里的项目目录挂载到dist的解压目录 配置文件也挂载出来

[root@VM_0_2_centos ~]# docker run -d -p 443:443 -p 80:80 -v /root/filebrowser/sites/root/nginx配置/nginx.conf:/etc/nginx/nginx.conf -v /root/filebrowser/sites/root/nginx项目:/usr/share/nginx/html nginx

dece8fc43c67        nginx:latest           "nginx -g 'daemon ..."   5 months ago        Up 2 days               0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp         nginx

然后写nginx.conf

location / {
            root   /usr/share/nginx/html/xm1/dist;
            index  index.html index.htm;
            try_files $uri $uri/ /index.html;
        }

在这里插入图片描述
重启nginx
这些做完之后,就已经完成了。
在这里插入图片描述
测试地址 后面的id相当于一个房间号 也是推流的文件名
http://62.234.60.111/login?id=test

发布了2 篇原创文章 · 获赞 6 · 访问量 2560

猜你喜欢

转载自blog.csdn.net/qq_39190461/article/details/104278822
今日推荐