如果朋友走上了错误的人生道路,就算破坏友情也要阻止他
们将在这个小小的城镇中相互扶持,共同生活下去
今宵之月,绝不西沉。只此美梦,不再苏醒。从今往后,生生世世,长相厮守,为你立誓。
我们不能让这次冒险之旅没有意义没有收获
我们的本质就是血,一层层地重复,然后世代脉脉相传的血才是黑血的真实
当你知道自己被爱著的时候就不会自卑了asd
不要哭,我还没有努力到要哭的程度,不甘心就可以了das
既不回头,何必不忘;既然无缘,何必誓言;今日种种,似水无痕
空谈之类,是谈不久,也谈不出什么来的,它终必被事实的镜子照出原形,拖出尾巴而去
只愿涤荡四方,护得一世之隅。
你看你浪费了多少流星,哈哈……不牵个手也很浪费这样的夜晚呢
有形的东西迟早会凋零,但只有回忆是永远不会凋零的
已经无法回来的东西,得到和舍弃都很痛苦
Pain past is pleasure.11222
我因为后来离开村子,在远处看见这一村庄人的火焰。看见他们比熄灭还要寂静的那一场燃烧。我像一根逃出火堆的干柴,幸运而孤独地站在远处。
首页
统计
免费Chat GPT
关于
更多
友链
每日新闻
视频
高清壁纸
Search
1
2023彩虹易支付最新原版开源网站源码,完整的易支付源码,无后门
466 阅读
2
ThinkPHP6的常见问题解答
387 阅读
3
Spring Boot之七牛云分片上传
241 阅读
4
小狐狸ChatGPT付费创作系统V2.4.9独立版 +WEB端+ H5端 + 小程序端(支持分享朋友圈、破解弹窗)
230 阅读
5
国内最好用的六款虚拟机软件
212 阅读
技术分享
源码分享
课程分享
号卡套餐
移动专区
电信专区
联通专区
广电专区
软件仓库
电脑软件
安卓软件
活动线报
值得一看
Search
标签搜索
技术分享
源码
源码分享
css
安卓软件
活动线报
软件
课程分享
号卡
电脑软件
PHP
值得一看
HTML
js
教程
chatgpt
AI
小程序
ThinkPHP
联通
老K博客
累计撰写
420
篇文章
累计收到
339
条评论
今日撰写
0
篇文章
首页
栏目
技术分享
源码分享
课程分享
号卡套餐
移动专区
电信专区
联通专区
广电专区
软件仓库
电脑软件
安卓软件
活动线报
值得一看
页面
统计
免费Chat GPT
关于
友链
每日新闻
视频
高清壁纸
用户登录
登录
搜索到
117
篇与
的结果
2023-11-17
Dash应用浏览器端回调常用方法总结
回调函数是我们在Dash应用中实现各种交互功能的核心,在绝大多数情况下,我们只需要以纯Python的方式编写常规服务端回调函数即可,这也贯彻了Dash无需编写javascript即可构建web应用的理念。但这并不代表在Dash应用中我们只能使用Python,更自由地,Dash针对回调函数编写还提供了client side callback(我们通常称作浏览器端回调)相关功能,使得我们可以在仍然使用Python编排回调函数角色的基础上,嵌入自定义的javascript代码片段来执行相应的回调输入输出逻辑,从而解决一些特殊的需求。今天的文章中,我就将带大家一起学习Dash浏览器端回调常用的方法和技巧😎 浏览器端回调,顾名思义,其对应的函数体计算过程是在每个用户的本地浏览器中执行的,这在一些特殊的场景下,可以帮助我们节省服务器算力、网络传输带宽等消耗,还可以在用户网络状况很差时,提升一些用户交互功能的流畅度,亦或是可以让我们在Dash应用中额外引入javascript生态的功能(譬如在Dash应用中高效渲染原生echarts图表)。而在Dash中,我们主要有两种定义浏览器端回调的方式:一、基于app.clientside_callback编写简单浏览器端逻辑此种浏览器端回调定义方式适用于执行非常简单的javascript代码片段,只需要为app.clientside_callback()的第一个参数传入字符串形式的javascript函数体即可(推荐使用箭头函数),其中函数体内部参数的输入,以及结果的输出,原则类似常规的回调函数。举个例子,我们来实现一段非常简单的逻辑,通过按钮的点击,来触发对应模态框的打开:浏览器端回调)相关功能,使得我们可以在仍然使用Python编排回调函数角色的基础上,嵌入自定义的javascript代码片段来执行相应的回调输入输出逻辑,从而解决一些特殊的需求。今天的文章中,我就将带大家一起学习Dash浏览器端回调常用的方法和技巧😎。阅读本文大约需要15分钟浏览器端回调,顾名思义,其对应的函数体计算过程是在每个用户的本地浏览器中执行的,这在一些特殊的场景下,可以帮助我们节省服务器算力、网络传输带宽等消耗,还可以在用户网络状况很差时,提升一些用户交互功能的流畅度,亦或是可以让我们在Dash应用中额外引入javascript生态的功能(譬如在Dash应用中高效渲染原生echarts图表)。而在Dash中,我们主要有两种定义浏览器端回调的方式:1 基于app.clientside_callback编写简单浏览器端逻辑#此种浏览器端回调定义方式适用于执行非常简单的javascript代码片段,只需要为app.clientside_callback()的第一个参数传入字符串形式的javascript函数体即可(推荐使用箭头函数),其中函数体内部参数的输入,以及结果的输出,原则类似常规的回调函数。举个例子,我们来实现一段非常简单的逻辑,通过按钮的点击,来触发对应模态框的打开:对应app.clientside_callback的完整应用代码如下:app1.pyimport dash from dash import html import feffery_antd_components as fac from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = html.Div( [ fac.AntdButton( '打开模态框', id='open-modal', type='primary' ), fac.AntdModal( fac.AntdParagraph('测试内容'*100), id='modal', title='模态框示例' ) ], style={ 'padding': '50px 100px' } ) app.clientside_callback( '(nClicks) => true', Output('modal', 'visible'), Input('open-modal', 'nClicks'), prevent_initial_call=True ) if __name__ == '__main__': app.run(debug=True)可以看到,写法非常简单,对于编写此类简单浏览器端回调的需求,我们只需要用到javascript最基础的语法,非常的方便😇,再来个稍微复杂一点的例子,我们基于轮询组件,实现当前系统时间的实时更新:app2.pyimport dash from dash import html, dcc import feffery_antd_components as fac from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = html.Div( [ dcc.Interval( id='interval', interval=1000 # 每秒触发一次 ), fac.AntdStatistic( id='current-datetime', title='当前时间' ) ], style={ 'padding': '50px 100px' } ) app.clientside_callback( '''(n_intervals) => { return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}` }''', Output('current-datetime', 'value'), Input('interval', 'n_intervals') ) if __name__ == '__main__': app.run(debug=True)二、基于ClientsideFunction编写复杂浏览器端回调如果我们想要执行的浏览器端回调逻辑比较复杂和冗长,那么在app.clientside_callback里用字符串的方式写大段的javascript代码就不太高效了🙅♂️,相应的我们可以改为使用ClientsideFunction来定义。使用ClientsideFunction来定义浏览器端回调,我们首先需要在我们的Dash应用静态资源目录下(默认为assets)建立相应的js文件(名称随意,Dash应用会自动加载静态资源目录下的js文件到用户浏览器中),并在该js文件中按照下列格式定义若干javascript回调函数:window.dash_clientside = Object.assign({}, window.dash_clientside, { clientside: { func1: () => { // write your code logic } } });接着在相应的Python程序中配合ClientsideFunction按照下列格式关联编排回调函数即可:app.clientside_callback( ClientsideFunction( namespace='clientside', function_name='函数名称' ), # 照常编排回调角色 )废话不多说,我们直接将上文中实时刷新系统时间的示例改造成ClientsideFunction形式以便理解:assets/clientside_callbacks.jswindow.dash_clientside = Object.assign({}, window.dash_clientside, { clientside: { update_datetime: (n_intervals) => { return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}` } } });app3.pyimport dash from dash import html, dcc import feffery_antd_components as fac from dash.dependencies import Input, Output, ClientsideFunction app = dash.Dash(__name__) app.layout = html.Div( [ dcc.Interval( id='interval', interval=1000 # 每秒触发一次 ), fac.AntdStatistic( id='current-datetime', title='当前时间' ) ], style={ 'padding': '50px 100px' } ) app.clientside_callback( ClientsideFunction( namespace='clientside', function_name='update_datetime' ), Output('current-datetime', 'value'), Input('interval', 'n_intervals') ) if __name__ == '__main__': app.run(debug=True)这样做的好处在于,我们可以把相对复杂的javascript逻辑在原生的js程序里编写,从而配合现代化ide获得更高效的编程体验,并且利用ClientsideFunction形式,可以很方便地实现外部js框架的引入使用,譬如引入使用原生echarts,篇幅有限,今天先按下不表,之后另外发文举例介绍。三、 编写浏览器端回调的常用技巧通过上文,我们知晓了Dash中构建浏览器端回调的基本形式,下面我们补充一些有关浏览器端回调的实用技巧:3.1 配合插件快捷生成模板代码编写浏览器端回调,尤其是配合ClientsideFunction时,其代码格式还是有些特殊的,不过别担心,如果你恰好在使用vscode编写Dash应用,可以在拓展里安装由我开发维护的插件feffery-dash-snippets,安装完成后,可以通过输入一些快捷短语,进行相关代码模板的生成。目前针对浏览器端回调+ClientsideFunction,在py文件中可用的快捷短语有:callback-cs:oi:快速初始化具有Input和Output角色的浏览器端回调函数 callback-cs:ois:快速初始化具有Input、Output及State角色的浏览器端回调函数 在js文件中可用的快捷短语有:callback:init:快捷生成浏览器端回调函数定义模板3.2 常用对象在浏览器端回调中的写法在常规的服务端回调函数中我们经常会使用到dash.no_update、PreventUpdate、dash.callback_context等对象来辅助回调函数功能逻辑的完成,而在浏览器端回调中,这些对象的写法要做一定变化:dash.no_updatedash.no_update在浏览器端回调中写作window.dash_clientside.no_update,你也可以用feffery-dash-snippets插件中的dash.no_update快捷短语生成: dash.callback_contextdash.callback_context在浏览器端回调中写作window.dash_clientside.callback_context,你也可以用feffery-dash-snippets插件中的dash.callback_context快捷短语生成: 阅读本文大约需要15分钟浏览器端回调,顾名思义,其对应的函数体计算过程是在每个用户的本地浏览器中执行的,这在一些特殊的场景下,可以帮助我们节省服务器算力、网络传输带宽等消耗,还可以在用户网络状况很差时,提升一些用户交互功能的流畅度,亦或是可以让我们在Dash应用中额外引入javascript生态的功能(譬如在Dash应用中高效渲染原生echarts图表)。而在Dash中,我们主要有两种定义浏览器端回调的方式:1 基于app.clientside_callback编写简单浏览器端逻辑#此种浏览器端回调定义方式适用于执行非常简单的javascript代码片段,只需要为app.clientside_callback()的第一个参数传入字符串形式的javascript函数体即可(推荐使用箭头函数),其中函数体内部参数的输入,以及结果的输出,原则类似常规的回调函数。举个例子,我们来实现一段非常简单的逻辑,通过按钮的点击,来触发对应模态框的打开:对应app.clientside_callback的完整应用代码如下:app1.pyimport dashfrom dash import htmlimport feffery_antd_components as facfrom dash.dependencies import Input, Outputapp = dash.Dash(__name__)app.layout = html.Div([ fac.AntdButton( '打开模态框', id='open-modal', type='primary' ), fac.AntdModal( fac.AntdParagraph('测试内容'*100), id='modal', title='模态框示例' ) ], style={ 'padding': '50px 100px' })app.clientside_callback('(nClicks) => true', Output('modal', 'visible'), Input('open-modal', 'nClicks'), prevent_initial_call=True)if name == '__main__':app.run(debug=True)可以看到,写法非常简单,对于编写此类简单浏览器端回调的需求,我们只需要用到javascript最基础的语法,非常的方便😇,再来个稍微复杂一点的例子,我们基于轮询组件,实现当前系统时间的实时更新:app2.pyimport dashfrom dash import html, dccimport feffery_antd_components as facfrom dash.dependencies import Input, Outputapp = dash.Dash(__name__)app.layout = html.Div([ dcc.Interval( id='interval', interval=1000 # 每秒触发一次 ), fac.AntdStatistic( id='current-datetime', title='当前时间' ) ], style={ 'padding': '50px 100px' })app.clientside_callback('''(n_intervals) => { return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}` }''', Output('current-datetime', 'value'), Input('interval', 'n_intervals'))if name == '__main__':app.run(debug=True)2 基于ClientsideFunction编写复杂浏览器端回调#如果我们想要执行的浏览器端回调逻辑比较复杂和冗长,那么在app.clientside_callback里用字符串的方式写大段的javascript代码就不太高效了🙅♂️,相应的我们可以改为使用ClientsideFunction来定义。使用ClientsideFunction来定义浏览器端回调,我们首先需要在我们的Dash应用静态资源目录下(默认为assets)建立相应的js文件(名称随意,Dash应用会自动加载静态资源目录下的js文件到用户浏览器中),并在该js文件中按照下列格式定义若干javascript回调函数:window.dash_clientside = Object.assign({}, window.dash_clientside, {clientside: { func1: () => { // write your code logic } }});接着在相应的Python程序中配合ClientsideFunction按照下列格式关联编排回调函数即可:app.clientside_callback(ClientsideFunction( namespace='clientside', function_name='函数名称' ), # 照常编排回调角色)废话不多说,我们直接将上文中实时刷新系统时间的示例改造成ClientsideFunction形式以便理解:assets/clientside_callbacks.jswindow.dash_clientside = Object.assign({}, window.dash_clientside, {clientside: { update_datetime: (n_intervals) => { return `${new Date().toLocaleDateString().replaceAll("/", "-")} ${new Date().toLocaleTimeString()}` } }});app3.pyimport dashfrom dash import html, dccimport feffery_antd_components as facfrom dash.dependencies import Input, Output, ClientsideFunctionapp = dash.Dash(__name__)app.layout = html.Div([ dcc.Interval( id='interval', interval=1000 # 每秒触发一次 ), fac.AntdStatistic( id='current-datetime', title='当前时间' ) ], style={ 'padding': '50px 100px' })app.clientside_callback(ClientsideFunction( namespace='clientside', function_name='update_datetime' ), Output('current-datetime', 'value'), Input('interval', 'n_intervals'))if name == '__main__':app.run(debug=True)这样做的好处在于,我们可以把相对复杂的javascript逻辑在原生的js程序里编写,从而配合现代化ide获得更高效的编程体验,并且利用ClientsideFunction形式,可以很方便地实现外部js框架的引入使用,譬如引入使用原生echarts,篇幅有限,今天先按下不表,之后另外发文举例介绍。3 编写浏览器端回调的常用技巧#通过上文,我们知晓了Dash中构建浏览器端回调的基本形式,下面我们补充一些有关浏览器端回调的实用技巧:3.1 配合插件快捷生成模板代码#编写浏览器端回调,尤其是配合ClientsideFunction时,其代码格式还是有些特殊的,不过别担心,如果你恰好在使用vscode编写Dash应用,可以在拓展里安装由我开发维护的插件feffery-dash-snippets,安装完成后,可以通过输入一些快捷短语,进行相关代码模板的生成。目前针对浏览器端回调+ClientsideFunction,在py文件中可用的快捷短语有:callback-cs:oi:快速初始化具有Input和Output角色的浏览器端回调函数callback-cs:ois:快速初始化具有Input、Output及State角色的浏览器端回调函数在js文件中可用的快捷短语有:callback:init:快捷生成浏览器端回调函数定义模板3.2 常用对象在浏览器端回调中的写法#在常规的服务端回调函数中我们经常会使用到dash.no_update、PreventUpdate、dash.callback_context等对象来辅助回调函数功能逻辑的完成,而在浏览器端回调中,这些对象的写法要做一定变化:dash.no_updatedash.no_update在浏览器端回调中写作window.dash_clientside.no_update,你也可以用feffery-dash-snippets插件中的dash.no_update快捷短语生成:PreventUpdatePreventUpdate在浏览器端回调中写作PreventUpdate,你也可以用feffery-dash-snippets插件中的PreventUpdate快捷短语生成(注意,在浏览器端回调中throw window.dash_clientside.PreventUpdate等价于常规回调中的raise PreventUpdate):dash.callback_contextdash.callback_context在浏览器端回调中写作window.dash_clientside.callback_context,你也可以用feffery-dash-snippets插件中的dash.callback_context快捷短语生成:3.3 在浏览器端回调中返回组件元素我们在常规回调函数中,经常会以一些组件的children或其他组件型参数为Output目标,直接返回组件元素,在Python中这样做很稀疏平常,但是在浏览器端回调中,我们如果有此类需求,则需要返回规定的JSON数据格式,来表示一个组件元素:{ props: { // 定义当前组件的各属性,如 id: '组件id' }, type: '组件完整名称,如AntdButton', namespace: '组件所属组件库完整名称,如feffery_antd_components' }具体代码如下,可以看到只要我们按照格式返回相应的组件JSON数据,Dash就会在浏览器中自动进行转换及渲染:app4.pyimport dash from dash import html import feffery_antd_components as fac from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = html.Div( [ fac.AntdButton( '新的消息', id='new-message', type='primary' ), html.Div(id='new-message-container') ], style={ 'padding': '50px 100px' } ) app.clientside_callback( '''(nClicks) => ({ props: { content: "新的消息,nClicks:" + nClicks, type: "info" }, type: "AntdMessage", namespace: "feffery_antd_components" })''', Output('new-message-container', 'children'), Input('new-message', 'nClicks'), prevent_initial_call=True ) if __name__ == '__main__': app.run(debug=True)
2023年11月17日
76 阅读
0 评论
0 点赞
2023-11-17
Ripro子主题(夏系列)设置菜单上面悬浮new/host动态图标教程
设置教程设置new图标<span class="menu_new"></span> 设置host图标<span class="menu_hot"></span>
2023年11月17日
85 阅读
0 评论
0 点赞
2023-11-12
如何用PHP开发一个api数据接口
我一直觉得会写接口是一件很酷的事情,因为它可以实时修改前台数据,而不像App一样需要更新版本和接受审核。更重要的是,它意味着你的技术完成了一个闭环,可以独自完成一整个项目的开发。一、搭建环境PHP环境三要素,第一安装PHP,第二安装Web服务器,比如Apache,第三安装数据库,比如Mysql,PHP用来开发项目,服务器用来运行项目,数据库用来存储数据。对于初学者建议使用集成的服务器组件,它已经包含了 PHP、Apache、Mysql 等服务,免去了开发人员将时间花费在繁琐的配置环境过程。这种集成的组件有很多,比如WampServer、XAMPP,因为我是Mac OS系统,所以使用的是XAMPP,它同时支持Windows和Mac OS。下载之后直接安装即可,安装过程结束后,您可以使用 XAMPP 控制面板来启动/停止所有服务或安装/卸载所有服务。 启动服务,在浏览器里输入:localhost 或者 127.0.0.1 即可看到 XAMPP 欢迎界面。至此,PHP环境搭建完成。现在,试着在XAMPP文件夹下的htdocs文件夹下新建一个文件夹命名HelloWorld,在该文件夹下新建index.php文件,在index.php中编写如下代码:<?php echo "hello world";保存关闭后在浏览器访问: localhost/HelloWorld/index.php 界面打印出hello world,一个简单的接口就开发完成啦!接下来我们定义一个变量$a = array('status' => 'success’)然后对它进行编码$b = json_encode(array('status' => 'success’))替换掉hello world$a = array('status' => 'success’); $b = json_encode(array('status' => 'success’)); echo $b;保存后刷新浏览器,现在它更像一个接口了!
2023年11月12日
80 阅读
0 评论
0 点赞
2023-10-31
如何与缓存技术结合提升搜索速度Sphinx PHP
Apifox 是集 API 文档、API 调试、API Mock、API 自动化测试多项实用功能为一体的 API 管理平台,定位为 Postman + Swagger + Mock + JMeter。旨在通过一套系统、一份数据,解决多个工具之间的数据同步问题。只需在 Apifox 中定义 API 文档;API 调试、API 数据 Mock、API 自动化测试等功能就可以直接使用,无需再次定义。API 文档和 API 开发调试流程在同一个工具内闭环,API 调试完成后即可确保与 API 文档定义完全一致。高效、及时、准确! Sphinx 是一款强大的开源搜索引擎,可以用来快速搜索和分析大量的文本数据。然而,随着数据的增长,Sphinx 的搜索速度可能会变慢。为了提升搜索速度,我们可以将 Sphinx 与缓存技术结合使用。本文将介绍如何使用 Sphinx PHP 扩展以及缓存技术来提升搜索速度,并提供具体的代码示例。一、缓存概念和原理缓存是一种将计算结果或数据存储在快速访问的介质中的技术。当需要相同的计算结果或数据时,可以直接从缓存中获取,而不需要重新计算或查询。这样可以大大提高响应速度和性能。二、Sphinx PHP 扩展介绍Sphinx PHP 扩展是一个用于与 Sphinx 搜索引擎进行交互的 PHP 扩展。它提供了强大的功能,如索引管理、搜索查询和结果处理等。使用 Sphinx PHP 扩展,可以轻松地将 Sphinx 集成到 PHP 项目中。三、使用 Sphinx PHP 扩展首先,我们需要安装 Sphinx PHP 扩展。可以通过 pecl 命令来安装,如下所示:pecl install sphinx安装完成后,需要在 php.ini 文件中启用 Sphinx 扩展,添加以下行:extension=sphinx.so接下来,创建一个 PHP 文件,并引入 Sphinx PHP 扩展的命名空间,如下所示:157b96202e0bfd54f17fcc3d1e3b8c3asetServer("localhost", 9312);设置完连接信息后,可以进行搜索查询了。以下是一个简单的搜索示例:$result = $client->query('关键词', '索引名');在搜索结果处理方面,可以根据实际需求进行处理。可以通过 $result['matches'] 获取搜索结果列表,然后根据需要进行展示、过滤或其他操作。四、使用缓存技术为了提升 Sphinx 的搜索速度,我们可以使用缓存技术将搜索结果缓存起来。这样,当下次需要相同的搜索结果时,可以直接从缓存中获取,而不需要重新查询 Sphinx。PHP 中有很多缓存技术可供选择,如 Memcached、Redis 等。接下来,我们以 Memcached 作为示例来演示如何使用缓存技术结合 Sphinx。首先,需要安装 Memcached 扩展,可以通过以下命令来安装:pecl install memcached安装完成后,需要在 php.ini 文件中启用 Memcached 扩展,添加以下行:extension=memcached.so然后,在 PHP 代码中引入 Memcached 类,并创建一个 Memcached 对象,如下所示:0318358f7e19a117421f7611fccf7554get($memcachedKey);if (!$memcachedResult) {$result = $client->query('关键词','索引名'); //将搜索结果存储到缓存中 $memcached->set(memcachedKey,$result,过期时间); $memcachedResult = result;}最后,根据需要对搜索结果进行处理。如果从缓存中获取到了搜索结果,可以直接使用 $memcachedResult,否则需要执行 Sphinx 查询并存储到缓存中。通过使用缓存技术,我们可以避免重复查询 Sphinx 数据,从而显著提升搜索速度。五、总结本文介绍了如何使用 Sphinx PHP 扩展与缓存技术结合来提升搜索速度。通过将 Sphinx 的搜索结果存储到缓存中,可以避免重复查询,提高搜索性能。使用缓存技术能够为大型文本数据搜索和分析提供更好的用户体验。以上是关于使用 Sphinx PHP 扩展与缓存技术结合提升搜索速度的具体代码示例。希望对你有所帮助!
2023年10月31日
56 阅读
0 评论
0 点赞
2023-10-31
一个重量级HTTP api的304优化分析与突发失效问题解决
前言最近查看nginx log排查问题时,意外中发现重量级的主页 list api 304比例已暴跌至不到1%,之前该比例长期维持在30%以上,近期也未改动过相关逻辑,跟进后最终发现是服务端本地cache混用导致的问题。304优化原因app每次冷启初始化时都会请求重量级的HTTP主页list api,其会拉取全量1000个item(游戏关卡)组成的list数据一次性返回,返回的响应数据经gzip压缩之后依然有将近300KB大小(解压后近3MB),单api如此大的数据传输对于网络带宽和传输速度都有明显的影响。该api返回包括静态数据和动态数据部分,静态数据变动一般由运营在后台配置修改导致,一周变动次数有限,动态数据则和用户自身游玩行为有关,整体而言对于该api相当一部分用户多次请求得到的数据应该都是相同的,所以去年和客户端一起对该api进行了HTTP 304优化。304对客户端请求耗时与带宽影响分析之前测试了日本与美国东海岸之间主页list api返回304与完整300KB大小的耗时对比,两者耗时相差超过600ms--作为对比服务端处理耗时upstream_response_time一直稳定在100ms左右。从网络协议与数据传输的角度解释这一现象,C/S 首先建立TCP连接,而后基于TCP建立HTTPS连接,服务端收到客户端完整请求并通过一系列业务逻辑生成最终返回数据-无论是否启用304到这一步为止流程都是一样的。接下来返回数据这一步就差别大了,如果返回数据很少--如1KB,一个TCP包即可将完整响应数据发挥给客户端,日本到美国东海岸ping耗时(RTT)约为160~200ms(以下取160ms为例),网络良好的情况下客户端将在服务端发送数据包后80ms收到该响应,而如果300KB数据,则需要至少200个TCP包(根据RFC6928定义TCP滑动窗口初始值为10MSS,后续根据网络情况会动态调整),最终体现为客户端完全收到300KB响应数据需要4个乃至更多个RTT(>=640ms)的时间,也就是300KB数据传输上比1KB数据传输要多耗时至少560ms,而实际上包越多,在TCP通信过程中发生丢包、重传、拥塞控制等意外情况的概率也就越大,其受网络不稳定因素影响而耗时更长的可能也要大得多。至于所需带宽对比则很明显接近1:300。去年对该接口做完304支持优化后,当时的效果是超过30%的请求都可以通过304直接返回,一个简单的304优化就能使该接口使用带宽资源降低近30%,对于美国地区用户而言超过30%请求耗时减少600+ms,平均耗时降低200+ms,应该说还是相当划算的。304比例异常暴跌测试环境尝试复现偶然发现线上304比例暴跌后,首先尝试在测试环境复现问题,结果测试环境居然没法复现出来,无论是浏览器、curl命令还是app直接请求,结果都显示同一用户多次请求会正常返回304,陷入沉思==!线上环境复现测试环境无法复现,直接尝试线上环境验证,结果发现问题必现--对于同一个用户的连续两次请求,简单粗暴将两次api请求返回的数据copy到本地比对其变化,发现1000个item中有个别item有会多余的字段,看到字段名立刻明白了问题所在:本地cache被混用了。本地cache的引入对于该api返回的1000个item,每个item都需要单独调用normalize函数进行一系列的处理--如配置资源地址到完整资源包格式转换、动态文案组装等,即便单个item normalize调用只需要0.1ms,*1000之后也会变成100ms,所以服务端对于每个item的normalize结果都做了一个短期的本地cache,命中缓存的情况下1000个normalize所花费的时间由100ms减少成了数ms,这个优化已经上线很长时间,直到最近才发现它可能会导致304机制的失效。对normalize函数启用本地cache的Python代码实现大概类似如下:class ItemModel: local_item_cache = LocalCache(10000) @local_item_cache.cache(60) def normalize(self, phone_type, item_id): # 资源包格式转换、动态文案组装等 ... return selfcache混用问题的引入近期app新增加了一个游戏模式,服务端对于该模式下的用户每次均会从一个100+item的池子中按一定策略随机选定一个返回,该item在返回前一样会调用normalize函数,如果命中本地cache直接返回,否则执行normalize函数并将结果存入本地cache后再返回。问题出在这里:主页list item normalize与新游戏模式下item normalize使用的是同一个本地cache,出于节约拷贝开销的考虑,本地cache命中返回的其实是一个对象引用,而新模式在normalize之后,还会针对该游戏模式新增数个额外字段、并修改部分已有字段的内容-会有部分随机数值策略,这会直接修改掉底层本地cache缓存的实际对象内容,这样主页list api执行item的normalize时也会读到这些被额外修改过的缓存对象,同一个用户两次主页 list api请求如果读取到的1000 item中存在任意一个item受到新游戏模式修改影响就可能导致最终数据不一致--另外线上Python服务为多主机、多进程部署,每个进程都会维护自己独立的本地cache,此种情况下不同进程本地cache缓存的对象数据基本都是不一致的,用户请求可能落在任意进程上更加重了这种可能性--其返回的数据etag也会变化,也就不可能触发304机制了。测试环境为什么无法复现呢?因为测试环境就几个内部人员,同时存在请求主页list和新游戏模式游玩用户的时候很少--简单来说就是并发度不够,所以绝大部分情况下该问题很难触发。后续处理新游戏模式每次随机一个item返回其实并无太大性能开销,而且其请求量也并不算高,所以直接不使用本地cache每次都完整执行normalize函数即可,暂时也没必要为其单独开辟一个cache专用。按以上方案处理上线后,主页list api的304比例直接恢复至30%~50%之间浮动。参考https://www.cnblogs.com/xiaolincoding/p/12732052.htmlhttps://blog.csdn.net/sinat_20184565/article/details/104851413https://www.cnblogs.com/acac-t/p/http_304_and_local_cache_bug.html
2023年10月31日
50 阅读
0 评论
0 点赞
1
...
19
20
21
...
24
CC BY-NC-ND