草庐IT

WebSocket&SSE实时动态数据展示

XiuL 2023-04-12 原文

需求

  • 服务器主动推送数据给浏览器,实现数据实时更新展示

实现方式

  • WebSocket
  • SSE

SSE案例(Springboot项目)

  • maven依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
  • 创建一个controller
@RestController
@RequestMapping("/sse")
public class SseController {
    //SSE返回数据格式是固定的以data:开头,以\n\n结束
    @RequestMapping(value = "/get_data", produces = "text/event-stream;charset=UTF-8")
    public String push() throws InterruptedException {
        Thread.sleep(1000);
        return "data:测试数据:" + Math.random() + "\n\n";
    }
}
  • resource目录下创建public目录,然后创建index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <script type="text/javascript">
        // /sse/get_data
        let source = new EventSource("http://localhost:8080/sse/get_data");

        source.addEventListener('message',function (t){
            var data = t.data;
            document.getElementById("result").innerText = data;
        })
        source.addEventListener('open', function(e) {
            console.log("连接打开.");
        }, false);
        source.addEventListener('error', function(e) {
            if (e.readyState == EventSource.CLOSED) {
                console.log("连接关闭");
            } else {
                console.log(e.readyState);
            }
        }, false);
        
    </script>
</head>
<body>
    模拟数据:
        <div>测试</div>
        <div id="result"></div>
</body>
</html>
  • 创建Application启动类,启动项目
  • 浏览器访问: localhost:8080/index.html

WebSocket案例(Springboot项目)

  • maven依赖
  <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--websocket -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
    </dependencies>
  • 创建config包,创建WebSocketConfig.java
/**
 * 开启WebSocket支持
 */
@Configuration
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    /**
     * 扫描@ServerEndpoint,将@ServerEndpoint修饰的类注册为websocket
     * 如果使用外置tomcat,则不需要此配置
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
  • 创建server包,创建MysqlSocketServer.java MysqlUtil.java User.java
@ServerEndpoint("/wsMysql")
@Component
public class MysqlSocketServer {

    private static final ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);

    @OnOpen
    public void onOpen(Session session) throws IOException {
        System.out.println(String.format("Socket Connection  onOpen ....%s",Thread.currentThread().getId()));
        executor.scheduleAtFixedRate(() -> {
            try {
                MysqlUtil.pushLog(session);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }, 2, 1, TimeUnit.SECONDS);

    }

    @OnClose
    public void onClose(Session session) {
        System.out.println(String.format("Socket Connection onClose ..... %s",Thread.currentThread().getId()));
    }

    @OnError
    public void onError(Throwable e) {

        System.out.println("Socket Connection onError .....");
        e.printStackTrace();
    }

}
@Configuration
@EnableScheduling
public class MysqlUtil {

    public static List<User> users = new ArrayList<>();

    public static void pushLog(Session session) throws IOException {
        //查询数据:
        List<User> users = findAll();
        session.getBasicRemote().sendText(JSON.toJSONString(users));

    }

    private static List<User> findAll() {
        return users;
    }

    static {
        int num = (int) (1 + Math.random() * (3 - 1 + 1));
        users.add(new User("1", "tom", 12));
        users.add(new User("2", "jim", 22));
        users.add(new User("3", "jack", 32));

        ArrayList<User> rs = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            rs.add(users.get(i));
        }

    }


    @Scheduled(fixedRate = 2000)
    public void configureTasks() throws IOException {
        int num = (int) Math.random();
        users.add(new User(String.valueOf(num), "name" + num, num * 2));
    }



}

public class User {
    private String id;
    private String name;
    private Integer age;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public User(String id, String name, Integer age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
}

  • resources目录下创建static目录,创建index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>mysql中获取数据,实时展示</title>
</head>
<body>

<div id="logContainer" >

</div>

<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script>
    $(document).ready(function () {
        if (typeof (WebSocket) == "undefined") {
            console.log("您使用的浏览器不支持WebSocket");
        } else {
            //连接到websocket端点,测试服、正式服环境要写应用所在机器的具体域名|ip
            let socket = new WebSocket("ws://127.0.0.1:7500/wsMysql");
            socket.onopen = () => {
                console.log("已连接到wsServer");
            };
            socket.onclose = () => {
                console.log("已断开与wsServer的连接");
            };
            socket.onerror = (e) => {
                console.log("发生错误", e);
            }
            socket.onmessage = function (event) {
                console.log("接收到的数据:" + event.data);
                document.getElementById("logContainer").innerText = event.data
            };
        }
    });
</script>

</body>
</html>

  • 创建application.yml
server:
  port: 7500
  • 启动服务,访问 localhost:7500/index.html

有关WebSocket&SSE实时动态数据展示的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  6. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  8. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  9. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐