Web项目版本更新及时通知

背景

单页应用,项目更新时,部分用户会出更新不及时,导致异常的问题。

技术方案

给出版本号,项目每次更新时通知用户,版本已经更新需要刷新页面。

  • 版本号更新方案
  • 版本号变更后通知用户
  • 哪些用户需要通知?
    • 刷新页面进入的不通知
    • 第一次进入页面的不通知
    • 只有正在页面上使用的用户,项目发知变更时通知

拉取最新版本号

1. 创建版本号文件

文件名 web.version.json

{"name": "web-site","commitId": "GitCommitId","date": "BuildDate","nexttick": 10000
}

2. 更新版本号文件

每次部署时对版本号进行更新
commit ID 当前项目的最新的提交ID
当前日期
多久检查一次

#!/bin/bash
# 修改日期
sed -i "s/\"BuildDate\"/\"$(date '+%Y%m%d')\"/g" ./public/web.version.json;
# commit id
sed -i "s/\"GitCommitId\"/\"$(git rev-parse HEAD)\"/g" ./public/web.version.json;

替换完成的结果

{"name": "web-site","commitId": "c09ecb450f4fb214143121769b0aa1546991dab6","date": "20241112","nexttick": 10000
}

3. 部署文件到 生产环境

内部上线流程,将文件部署到生产环境。 自由发挥这里不做细述。

4. 获取版本号文件

由于是跟静态文件直接部署在一起可以直接通过 http 请求获取到它。

4.1 http 轮询
  • 获取版本文件
  • 比对版本文件
  • 通知用户消息弹窗

提取配置的主要代码

const LOCALVERSIONNAME = 'version';interface IVersion {name: string;commitId: string;date: string;nexttick: number;
}export const getRemoteVersion = () =>fetch('/web.version.json', {headers: {'Content-Type': 'application/json','Cache-Control': 'no-cache',},}).then((response) => {if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}return response.blob();}).then((data) => {return data.text().then((jsonStr) => {return JSON.parse(jsonStr);});});export const getLocalVersion = (current: IVersion): IVersion => {const version = window.localStorage.getItem(LOCALVERSIONNAME);if (version === null) {window.localStorage.setItem(LOCALVERSIONNAME, JSON.stringify(current));return current;}try {return JSON.parse(version) as IVersion;} catch {return current;}
};export const checkVersion = () => {return getRemoteVersion().then((remoteVersion: IVersion) => {const localVersion = getLocalVersion(remoteVersion) as IVersion;return { localVersion, remoteVersion };}).then(({ localVersion, remoteVersion }) => {return new Promise((resolve, reject) => {if (localVersion.date !== remoteVersion.date ||localVersion.commitId !== remoteVersion.commitId) {window.localStorage.setItem(LOCALVERSIONNAME,JSON.stringify(remoteVersion));resolve(remoteVersion);} else {reject(remoteVersion);}});});
};export default { getRemoteVersion, getLocalVersion, checkVersion };
4.2 websocket 长链接通知。

配置 openrestry 主要逻辑

将 文件 web.version.json 返回回来。

location /ws {content_by_lua_block {local ws = require "resty.websocket.server"local wss, err = ws:new()if not wss thenngx.log(ngx.ERR, "failed to new websocket: ", err)return ngx.exit(500)end-- 函数用于读取文件内容local function read_file_content(file_path)local file, err = io.open(file_path, "r")if not file thenngx.log(ngx.ERR, "failed to open file: ", err)return nil, errendlocal content = file:read("*all")file:close()return contentend-- 文件路径local file_path = "/data/web-site/dist/web.version.json"-- 读取文件内容local file_content, err = read_file_content(file_path)if not file_content thenngx.log(ngx.ERR, "failed to read file: ", err)wss:send_close(1000, "file not found")returnendwhile true do-- 接收客户端消息local message, typ, err = wss:recv_frame()if not message thenngx.log(ngx.ERR, "failed to receive frame: ", err)return ngx.exit(444)endif typ == "close" then-- 当客户端发送关闭信号时,关闭连接wss:send_close()breakelseif typ == "text" then-- 当客户端发送文本信息时,对其进行处理ngx.log(ngx.INFO, "received message: ", message)-- 发送文本消息给客户端wss:send_text(file_content)endend-- 关闭 WebSocket 连接wss:send_close(1000, "bye")}}

客户端获取配置

通过 websocket 将配置获取回来

const socket = new WebSocket('ws://localhost:8055/ws')
socket.addEventListener("open", (event) => {console.log(event); socket.send('v')
});
socket.addEventListener('message', (event) => {console.log(event.data)
})

通知用户

onMounted(() => {const toCheckVersion = () =>checkVersion().then(() => {const id = `${Date.now()}`;Notification.info({id,title: 'Site Update',content: 'Please refresh the page to use the latest version',duration: 0,footer: () =>h(Space, {}, [h(Button,{type: 'primary',size: 'small',onClick: () => window.location.reload(),},'OK'),]),});}).catch((remoteVersion) => {setTimeout(toCheckVersion, remoteVersion.nexttick);});toCheckVersion();});

在这里插入图片描述

完整 openrestry 代码

# Based on https://www.nginx.com/resources/wiki/start/topics/examples/full/#nginx-conf
# user              www www;  ## Default: nobodyworker_processes  auto;
error_log         "/opt/bitnami/openresty/nginx/logs/error.log";
pid               "/opt/bitnami/openresty/nginx/tmp/nginx.pid";events {worker_connections  1024;
}http {include       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    "/opt/bitnami/openresty/nginx/logs/access.log" main;add_header    X-Frame-Options SAMEORIGIN;client_body_temp_path  "/opt/bitnami/openresty/nginx/tmp/client_body" 1 2;proxy_temp_path        "/opt/bitnami/openresty/nginx/tmp/proxy" 1 2;fastcgi_temp_path      "/opt/bitnami/openresty/nginx/tmp/fastcgi" 1 2;scgi_temp_path         "/opt/bitnami/openresty/nginx/tmp/scgi" 1 2;uwsgi_temp_path        "/opt/bitnami/openresty/nginx/tmp/uwsgi" 1 2;sendfile           on;tcp_nopush         on;tcp_nodelay        off;gzip               on;gzip_http_version  1.0;gzip_comp_level    2;gzip_proxied       any;gzip_types         text/plain text/css application/javascript text/xml application/xml+rss;keepalive_timeout  65;ssl_protocols      TLSv1.2 TLSv1.3;ssl_ciphers        HIGH:!aNULL:!MD5;client_max_body_size 80M;server_tokens off;# HTTP Serverserver {# Port to listen on, can also be set in IP:PORT formatlisten  8080;location /status {stub_status on;access_log   off;allow 127.0.0.1;deny all;}location /ws {content_by_lua_block {local ws = require "resty.websocket.server"local wss, err = ws:new()if not wss thenngx.log(ngx.ERR, "failed to new websocket: ", err)return ngx.exit(500)end-- 函数用于读取文件内容local function read_file_content(file_path)local file, err = io.open(file_path, "r")if not file thenngx.log(ngx.ERR, "failed to open file: ", err)return nil, errendlocal content = file:read("*all")file:close()return contentend-- 文件路径local file_path = "/tmp/web.version.json"-- 读取文件内容local file_content, err = read_file_content(file_path)if not file_content thenngx.log(ngx.ERR, "failed to read file: ", err)wss:send_close(1000, "file not found")returnendwhile true do-- 接收客户端消息local message, typ, err = wss:recv_frame()if not message thenngx.log(ngx.ERR, "failed to receive frame: ", err)return ngx.exit(444)endif typ == "close" then-- 当客户端发送关闭信号时,关闭连接wss:send_close()breakelseif typ == "text" then-- 当客户端发送文本信息时,对其进行处理ngx.log(ngx.INFO, "received message: ", message)-- 发送文本消息给客户端wss:send_text(file_content)endend-- 关闭 WebSocket 连接wss:send_close(1000, "bye")}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/59439.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么牌子充电宝好用质量又好?2024年十款口碑质量最好充电宝推荐

​什么牌子充电宝好用质量又好?随着智能手机和其他移动设备的普及,充电宝已经成为了我们日常生活中必不可少的配件之一。然而,市面上的充电宝品牌众多,质量参差不齐,如何选择一款既好用又质量可靠的产品就成了一个难题…

网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持

在现代Web开发中,EasyPlayer.js H5流媒体播放器作为一款功能强大的H5播放器,其对于视频播放的优化和性能提升一直是开发者关注的焦点。特别是,随着Web技术的发展,OffscreenCanvas这一特性的出现为提升Canvas 2D/3D绘图的渲染性能和…

python爬虫实战案例——爬取A站视频,m3u8格式视频抓取(内含完整代码!)

1、任务目标 目标网站:A站视频(https://www.acfun.cn/v/ac40795151) 要求:抓取该网址下的视频,将其存入本地,视频如下: 2、网页分析 进入目标网站,打开开发者模式,我们发…

ES6标准-Promise对象

目录 Promise对象的含义 Promise对象的特点 Promise对象的缺点 Promise对象的基本用法 Promise对象的简单例子 Promise新建后就会立即执行 Promise对象回调函数的参数 Promise参数不会中断运行 Promise对象的then方法 Promise对象的catch()方法 Promise状态为resolv…

WSL 2 中 FastReport 与 FastCube 的设置方法与优化策略

软件开发人员长期以来一直在思考这个问题:“我们如何才能直接在 Windows 中运行 Linux 应用程序,而无需使用单独的虚拟机?” WSL 技术为这个问题提供了一个可能的答案。WSL 的历史始于 2016 年。当时,其实现涉及使用 Windows 内核…

Golang | Leetcode Golang题解之第556题下一个更大元素III

题目&#xff1a; 题解&#xff1a; func nextGreaterElement(n int) int {x, cnt : n, 1for ; x > 10 && x/10%10 > x%10; x / 10 {cnt}x / 10if x 0 {return -1}targetDigit : x % 10x2, cnt2 : n, 0for ; x2%10 < targetDigit; x2 / 10 {cnt2}x x2%10 -…

【EFK】Linux集群部署Elasticsearch最新版本8.x

【EFK】Linux集群部署Elasticsearch最新版本8.x 摘要环境准备环境信息系统初始化启动先决条件 下载&安装修改elasticsearch.yml控制台启动Linux服务启动访问验证查看集群信息查看es健康状态查看集群节点查询集群状态 生成service token验证service tokenIK分词器下载 Elast…

关于性能测试:数据库的 SQL 性能优化实战

在性能测试中&#xff0c;SQL性能优化往往是最具挑战性的任务之一。数据库作为系统的核心数据处理单元&#xff0c;其性能直接影响整体系统的响应速度。当面对复杂的业务需求和庞大的数据量时&#xff0c;如何高效执行SQL语句&#xff0c;减少查询耗时&#xff1f;今天&#xf…

力扣-每日温度

. - 力扣&#xff08;LeetCode&#xff09; 这是我的第一个思路 虽然可以得到正确答案 但是过于暴力 已经超出了时间限制 class Solution { public:vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int>ans;for (int i 0; i <…

1. ArkTS起步

ArkTS是HarmonyOS的主力应用开发语言&#xff0c;基于TypeScript扩展&#xff0c;强化了静态检查和分析&#xff0c;旨在提升程序稳定性和性能。它采用静态类型&#xff0c;禁止运行时改变对象布局&#xff0c;并对UI开发框架能力进行扩展&#xff0c;支持声明式UI描述和自定义…

Vue3 -- 项目配置之stylelint【企业级项目配置保姆级教程3】

stylelint为css的lint工具。可格式化css代码&#xff0c;检查css语法错误与不合理的写法&#xff0c;指定css书写顺序等。 配置stylelint&#xff1a; 我们项目使用scss作为预处理器 安装依赖&#xff1a; pnpm add sass sass-loader stylelint postcss postcss-scss postc…

androidstudio下载gradle慢

1&#xff0c;现象&#xff1a; 2&#xff0c;原因&#xff0c;国内到国外网址慢 3&#xff0c;解决方法&#xff1a;更改gradle-wrapper.properties #Wed Sep 26 20:01:52 CST 2018 distributionBaseGRADLE_USER_HOME distributionPathwrapper/dists zipStoreBaseGRADLE_USER…

golang分布式缓存项目 Day4 一致性哈希

注&#xff1a;该项目原作者&#xff1a;https://geektutu.com/post/geecache-day1.html。本文旨在记录本人做该项目时的一些疑惑解答以及部分的测试样例以便于本人复习 为什么使用一致性哈希 我该访问谁 对于分布式缓存来说&#xff0c;当一个节点接收到请求&#xff0c;如…

计算机网络:运输层 —— 运输层端口号

文章目录 运输层端口号的分类端口号与应用程序的关联应用举例发送方的复用和接收方的分用 运输层端口号的分类 端口号只具有本地意义&#xff0c;即端口号只是为了标识本计算机网络协议栈应用层中的各应用进程。在因特网中不同计算机中的相同端口号是没有关系的&#xff0c;即…

STM32H503开发(1)----开发板测试

STM32H503开发----1.开发板测试 概述硬件准备视频教学样品申请源码下载产品特性参考程序生成STM32CUBEMX串口配置LED配置堆栈设置串口重定向主循环演示 概述 STM32H503 & SENSOR是一款基于STM32H5系列微控制器的评估套件。该微控制器采用了40nm工艺制造&#xff0c;具有更…

#Swift Automatic Initializer Inheritance

在Swift中&#xff0c;**自动初始化器继承&#xff08;Automatic Initializer Inheritance&#xff09;**是一种机制&#xff0c;用于简化类的初始化器继承规则。它决定了在什么条件下子类可以自动继承父类的初始化器&#xff0c;而无需手动实现或重写。自动继承初始化器的机制…

Nacos黑马笔记

1. Nacos安装&#xff08;黑马教程安装材料&#xff09; 1.1 Windows安装 开发阶段采用单机安装即可。 1.1.1 下载安装包 在Nacos的GitHub页面&#xff0c;提供有下载链接&#xff0c;可以下载编译好的Nacos服务端或者源代码&#xff1a; GitHub主页&#xff1a;https://gith…

通过投毒Bingbot索引挖掘必应中的存储型XSS

简介 在本文中&#xff0c;我将讨论如何通过从外部网站对Bingbot进行投毒&#xff0c;来在Bing.com上实现持久性XSS攻击。 什么是存储型或持久性XSS&#xff1f;存储型攻击指的是将恶意脚本永久存储在目标服务器上&#xff0c;例如数据库、论坛、访问日志、评论栏等。受害者在…

【Pikachu】File Inclusion文件包含实战

永远也不要忘记能够笑的坚强&#xff0c;就算受伤&#xff0c;我也从不彷徨。 1.File Inclusion(文件包含漏洞)概述 File Inclusion(文件包含漏洞)概述 文件包含&#xff0c;是一个功能。在各种开发语言中都提供了内置的文件包含函数&#xff0c;其可以使开发人员在一个代码…

Stored procedures in PostgreSQL

select 存储过程&#xff0c;在现了解的情况&#xff0c;还是没有mysql,sqlserver等好写好用。 --postgreSQL 11.0 以下版本 create or replace FUNCTION procInsertSchool (pSchoolId Char(5),pSchoolName VarChar(100),pSchoolTelNo VarChar(8) ) RETURNS void language plp…