首页 文章 精选 留言 我的

精选列表

搜索[过拟合],共10002篇文章
优秀的个人博客,低调大师

ThinkPhp多语言如何实现你了解过吗?

❝ 最近项目比较忙,就没有怎么写文章。本文主要内容为多语言的后台是如何实现的。 ❞ 前言 公司需要写一个多语言后台,以前也没做过,于是到面向百度了一下。这是用了TP框架俩年了,第一次发现框架是自带的多语言切换功能。这里着实给点一个赞。 接下来我们来实现一下这个功能。 一、后台实现多语言 使用框架为v5.1.38 LTS 来到配置文件config/app.php可以看到关于语言的配置信息就只有这俩个参数。 lang_switch_on这个参数是自动监听的浏览器语言, default_lang关于这个参数就是在没有切换其它语言之前就直接默认中文配置文件了解之后我们需要在需要的模块创建lang目录,并且配置对应语言的语言包。 这里咔咔只配置了英文和中文 如还需要其它语言可自行添加即可。到这一步你会发现从头到尾都没有一个参数说是配置语言列表的参数。配置文件也没有找到。 但是在文档里你可以看到这行代码。我们去源码看看 在源码中就可以看得明白,语言的cookie的name值也是固定的,并且cookie的有效期时间为3600,这些都是需要根据自己项目进行调整的。 最重要的是看允许语言列表。我们继续追溯这个变量在文件thinkphp/library/think/Lang.php最后我们就可以看到一个方法,这个方法也正是文档给出的一个方法由于我们整个后台都需要使用多语言,所以我们需要在common中进行配置 二、视图切换语言 在视图模板里直接调用languageChange方法在视图中有一个变量为$languageName,这个变量是咔咔直接从配置文件直接取出来的,后期会调整到数据库中。 然后在控制器写方法changeLanguage,用来处理语言切换后的cookie改变此时我们的后台页面就是这样的 三、测试 在经过上面的操作之后,我们就可以根据自己设置的语言包来进行语言切换了 在模板中我们就需要使用lang这个模板标签进行获取数据这个是英文状态这个是中文状态到这关于多语言的实现就完成了。 四、总结 上面说了这么多,只是把每一步的步骤给大家写出来了。 其中关键点如下 application/config.php配置文件修改 在对应的模块里添加lang目录,并且创建需要的语言包 视图进行语言切换调用后台接口存储cookie值。保存在think_var 模板使用lang标签即可 ❝ 坚持学习、坚持写博、坚持分享是咔咔从业以来一直所秉持的信念。希望在诺大互联网中咔咔的文章能带给你一丝丝帮助。 ❞ 本文分享自微信公众号 - PHP初学者必看(PHP0022)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

记录下死磕过的一个坑

写在前面 最近公司项目提了个很正常的需求,为什么说是很正常的需求呢,保守点说,大多数的项目都会默认有这样的功能,那就是tomcat指定404页面,具体情况请听我再啰嗦啰嗦。有这么一场景,有人想攻击一家的项目,但是攻击前需要了解该公司项目的具体组成部分才可以进行真正的攻击吧,但是我也不知道该项目都有哪些目录?目录下有哪些文件?可以用笨方法就是去试,用一个大概常用的路径去访问该项目地址,比如ip/项目名/xxx,不存在的路径会看到默认的404页面会显示tomcat的版本和其它的一些信息,敏感信息会暴露,有点危险,所以我公司不想让别人看到这些,那就需要指定一个页面,找不到的路径都要转发这个页面。 1.修改web.xml 修改的文件路径如下,只要放在tomcat下的都走这个配置 /*/*/tomcat/conf/web.xml 在web.xml文件最后添加内容如下,error-code是浏览器状态码为404就转发404.jsp页面,这里需要注意一下,我就载在了这里,location是当前运行项目的下的路径,通俗点讲就是,一个项目名为songo,那么404.jsp页面就要放在songo项目下,对应的路径就是/songo/404.jsp,其实我也不知道为什么是这样设置,但是就是这样好使了 <error-page> <error-code>404</error-code> <location>/404.jsp</location> </error-page> </web-app> 这样的话就有个问题,如果tomcat下有多个项目,那每个项目根下都要放一个相同的404.jsp页面,如果您有什么更好的办法欢迎留言 2.404页面注意问题 404.jsp需要添加2行内容,如下 <%@ page language="java" contentType="text/html; charset=gbk" pageEncoding="gbk" isErrorPage="true"%> <%response.setStatus(HttpServletResponse.SC_OK);%> 这2行主要是告诉浏览器访问服务器不存在的资源跳转的错误处理页面,但是返回正常的状态码,并且正常显示页面内容 另外,具体页面请到 github 自行下载 说在最后,如果有哪里有问题或写的不对的,请留言,我会尽量在第一时间给您答复

优秀的个人博客,低调大师

安心过春节 智能家居为安防护航

无论是回家团聚,有商铺无人看管,还是一家人走亲访友,外出旅行,家里没人看守,亦或是办公室、仓库等重要区域,在春节这个重要节日,安全防范肯定会有所松懈,给犯罪分子留下可趁之机,带来的将是巨大的财产损失。 那么在春节,怎么才能不为安防担心,开开心心过大年呢?不用担心,Wulian智能家居可以帮忙。春节临近,家庭失窃率也在升高,安全防范自然不可轻视。传统安防设备使用复杂,需要人24小时监控,发现问题总在事情出现之后,不够智能。随着物联网的发展,智能家居以安全、便捷、智能等诸多特点走进人们生活。作为国内智能家居系统化品牌厂商,Wulian智能家居产品除了给用户带来高品质的生活环境之外,更能为用户安防需求提供全方位立体化解决方案,具备灵活可靠、安全及时等特点,让用户安防免除后顾之忧。 无论是日常生活还是春节,安防产品对生活非常重要。最简单来说,门、窗户都是家庭安防的防线。但是,道高一尺,魔高一丈,传统的安防手段和措施已经比较落后了,即使小区有监控和保安,同样有疏忽的时候。更何况,春节属于财产失窃高发时期,大家在新闻中一定不陌生。Wulian智能家居产品能为用户安防带来哪些可靠和保障呢? 安防第一步,智能门锁 Wulian智能门锁相较于传统门锁在安全性上有了重大提升,同时更智能。单从技术上来说,采用智能家居主流的ZigBee技术,加密程度比银行卡还高10倍,以目前的技术想暴力破解智能锁系统几乎不可能。Wulian智能锁具有NFC、手机远程、密码、指纹等多种开锁方式,还能绑定智能家居场景享受智能生活。 重要的是,用户每一次开锁,智能家居APP上都能实时反馈门锁的状态,用户能够心中有数。无论是家庭、仓库还是重要地方,Wulian智能锁为安防建立起第一道防线。 智能摄像头再把关 Wulian旗下有丰富的安防监控设备,包括云台摄像机、企鹅摄像机和随便看智能摄像机等,为家庭安防再添一道防线。以Wulian轻量型安防设备随便看为例,该设备是一款带有家庭网关功能的高清摄像机,充当智能家居系统枢纽,统一管理家中智能设备。 在配置方面,它拥有大广角,720P高清录像,手动水平360°、前后超180°旋转,以优秀的成像质量和灵活性为家庭安防提供便利。用户通过手机能够随时查看家里的情况,对家里的状况了如指掌。而其所拥有的移动侦测功能,能够在探测到有人非法闯入侦测区域时,向手机推送报警信息。另外,它还支持视频随时回放和双向分时语音对讲等功能。 相较于传统的监控,Wulian智能摄像头无论是灵活性、安全性还是智能性上都具有更大优势。 传感设备,安防无死角 无论是店铺、仓库还是家庭,Wulian推出的红外入侵和门窗磁感应都能为安防再加一道屏障,做到安防无死角,用户大可放心。Wulian红外入侵探测器能够对设备周边范围内状况进行有效探测,当有人非法靠近时探测范围时,红外入侵立即向手机推送报警信息,同时可以联动声光报警器发出警报,提醒陌生人知难而退,用户也能及时掌握家庭状况。 门窗磁探测器适合安装在窗户、门及抽屉、保险箱等两侧,当这些地方出现异常,手机第一时间收到报警信息,用户可以通过智能摄像头查看家庭状况,异常情况及时知晓。 只需要这三招,安防问题就能得到有效解决,Wulian智能安防产品运用先进的物联网技术给用户带来系统化的解决方案,对于用户而言无疑是吃了一颗定心丸。 家居安全还需要注意什么 以上设备解决了外在因素对家庭财产造成的威胁,其实在生活中还有很多不容忽视的隐患,不仅造成财产损失,甚至危及生命,例如火灾隐患、可燃气中毒等。 Wulian推出的无线烟雾火警探测器和可燃气泄露探测器可以做到危险情况提前预知,规避或者最小降低用户损失。 可燃气泄漏和烟雾火警探测器分别对可燃气和火灾烟雾进行探测,发现异常第一事件通知到用户手机,而可燃气泄漏探测还能联动机械手阀关闭阀门,同时打开通风系统及时通风透气,帮用户解决安全隐患。 从用户实际需求出发,Wulian智能家居安防设备为用户省心。目前有些消费者认为智能家居不是“刚需”,其实不然,智能家居在春节一定能够发挥它的价值,或成为智能家居落地突破口。 本文转自d1net(转载)

优秀的个人博客,低调大师

Python 官方团队在打包项目中踩过的坑

花下猫语:这是 packaging 系列的第三篇译文,该系列是全网关于此话题的最详尽(水平也很高)的一个系列。原作者是 Python 官方打包团队成员,是 virtualenv 和 tox 项目的维护者,及 setuptools 和 pip 项目的贡献者。 英文 | Python packaging - Growing Pains【1】 原作 | BERNAT GABOR 译者 | 豌豆花下猫 声明 :本文获得原作者授权翻译,转载请保留原文出处,请勿用于商业或非法用途。 在前两篇文章中,我介绍了Python 具有的包类型以及包的构建方式,尤其介绍了 PEP-517/518。尽管这些更改主要是为了使打包变得更健壮,但是在实施和发布时,我们却遇到了一些问题。这篇文章将介绍一部分,希望可以为大家提供经验教训,并提出一些有趣的问题以待将来解决。 查看 PEP-517 和 PEP-518 的改动,可以认为构建后端(亦即 setuptools、flit)几乎没有做什么,只是通过 Python 模块提供了功能接口。大部分繁重的工作都在构建前端上,它需要生成隔离的 Python,然后以新的方式调用构建后端。如今当我们谈论构建前端时,我们的选项主要是 pip 或 poetry(和开发者的 tox)。 这些项目由社区维护,由少数活跃的开发者在空闲时间维护。他们并没有因此获得报酬,而且需要谨慎考虑这些工具被使用的多种方式。考虑到这一点,在 PEP 被接受之后,还花了几乎两年时间才首次实施就不足为奇了。计划、测试和实施已经在背后进行了一年多。 但是,尽管做了所有准备工作,不可避免的是,第一版确实破坏了一些软件包,在大多数情况下,人们做的某些操作使维护人员感到惊讶。让我们试着了解其中一些例子,以及它们是如何被解决的。 Mink Mingle摄/Unsplash--准备好出发! PEP-518 此 PEP 引入了TOML文件格式。 【2】一种专门为了易于读/写配置而创建的格式。尽管在build-system部分下介绍了打包配置,但其它工具可以自由地将其配置放在tool:name部分下,因为它们拥有 PyPi 命名空间中的名字。各种工具立即开始利用这一点(例如Towncrier【3】、 black【4】等)。 当pip 18.0(于2018年7月22日发布) 【5】添加对 PEP-518 包的支持时,使用 pyproject.toml 最早出问题,因为 PEP-518 要求所有带 pyproject.toml 的软件包必须指定 build-backend 部分。但是,软件包事先仅将其用于其它项目的配置文件,由于它们没有事先指定它,当 pip 碰到这些文件时,就会引发错误,提示 pyproject.toml 文件无效。 PEP-517 pip wheel 缓存问题 pip 在 PEP-517 世界中的安装方式是首先生成一个 wheel,然后将其提取。要进入 PEP-517 世界,必须指定 build-backend 键,否则每条声明都需要退回到使用 setup.py 命令。 当 pip 构建 wheel 时,默认情况下会通过缓存系统完成。这是一种提速机制,为了在多个虚拟环境需要同一个 wheel 时,我们不用对其进行重建,而是重复使用它。PEP-517 wheel 的构建操作也利用了这一机制。 但是,当你禁用缓存时,这就变得很麻烦。因为没有目标文件夹可用于构建 wheel。所以构建过程将失败,请参阅附录的问题。【6】这个问题虽然很早就显现出来了,但由于大多数 CI 系统都在启用该选项的情况下运行。仅在一天后,pip 19.0.1 修复了该问题。 pyproject.toml 没有加入 setuptools 中 事实证明,构建后端实际上要做的工作不仅仅是 PEP-517 中描述的公开其 API。后端还需要确保 pyproject.toml 被附加到已构建的源码包中,否则用户计算机上的构建后端将无法使用它。setuptools 1650【7】将为setuptools【8】修复此问题,在早期版本中,只需在 MANIFEST.in 中指定 pyproject.toml 即可。 Jorge Zapata摄/Unsplash--什么?!那永远不会发生 从 setup.py 中导入构建的包 另一个意外问题是从 setup.py 内导入软件包时。按照约定,软件包的版本既作为软件包的元数据公开(setup.py 中的 setuptools,setup 函数的 version 参数),也在软件包根目录的__version__ 变量公开。可以在两个地方都指定变量的内容,但是要使其保持同步就很麻烦。 一种解决方法:许多程序包将其放在根目录的 version.py 中,然后同时从 setup.py 和程序包根目录导入它,像这样from mypy.version import __version__ as version。这能起作用,因为当有人调用 Python 脚本时,当前的工作目录会自动被添加到 sys.path 中(因此你可以导入公开在其下的内容)。 但是,这种添加当前工作目录的行为从来不是强制的,更多的是通过python setup.py sdist 调用构建时,产生的副作用。由于这种行为是副作用(并非保证),因此从 setup.py 导入的所有项目都应在构建开始时,将脚本文件夹显式地添加到 sys 路径。 是否该在打包期间(当尚未构建/分发时)导入已编译的软件包,这尚有争议(尽管 Python 打包组倾向于这样做)。然而,实际上当 setuptools 通过 setuptools.build_meta 暴露其接口时,它选择不把当前工作目录添加到系统路径。 PEP 从未要求后端做此添加,因为大多数构建后端(本质上是声明式的)根本不需要它。因此,此类功能被认为是前端的责任。setuptools 认为,如果用户需要此功能,则应在 setup.py 中明确指出,并提前手动在 sys.path 中添加相应的路径。 为了简化 pip 代码库,pip 决定加入 PEP-517,让所有人在 setuptools 后端加上 pyproject.toml。现在因为这个问题,即使没有选择加入 PEP-517 的程序包也出现崩溃。为了解决这个问题,setuptools 添加了一个新的构建后端(setuptools.build_meta:__ legacy__),当未指定构建后端时,前端可将其用作默认值;当项目添加 build-backend 键时,它们还必须更改其 setup.py,要么将源码根目录添加到 sys.path,要么避免从源码根目录导入。 自举的后端 还出现了另一个有趣的问题,该问题的用户群更加紧密,但是却暴露了一个有趣的问题。如果我们不想使用 wheel,我们只能通过源发行版进行设置;我们应该如何解决”如何提供构建后端的构建后端的问题“?例如,setuptools 通过setuptools 打包自身。也即当 setuptools 通过 PEP-517 指定了这一点时,构建前端将被放入无限循环内。 要安装 pugs 库,它首先会尝试创建一个隔离的环境。这个环境需要 setuptools ,因此构建前端就需要构建一个 wheel 来满足它。wheel 构建本身将触发隔离环境的创建,该环境又依赖于 setuptools。 如何打破这个循环?要求所有构建后端必须暴露为 wheel?允许后端构建自身?这些自建后端是否应该负担依赖项?漫长的各种观点间争论,利与弊,所以如果你有兴趣,请进入python Discourse board【9】,发表你的意见。 Sneaky Elbow摄/Unsplash--我们是一伙的 小结 打包是很难的。在业余时间完善打包系统,使用户可以在打包期间编写和运行任意代码,但还不引起任何破坏,这几乎是不可能的。 现在有了 PEP-518,构建时依赖项是明确的,并且构建环境易于创建。有了 PEP-517,我们可以使用更具声明性的打包命名空间,这减少了用户犯错的可能,当错误不可避免时,也能提供更好的消息。 诚然,在进行这些更改时,某些程序包可能会损坏,并且我们可能令曾经有效的方法失效。但是,我们(PyPa 的维护者)并不是出于恶意而这样做的,因此,当出现错误时,请务必填写详细的错误报告,例如什么错误、你的使用方法,以及你的用例。 我们努力在真诚地改善打包生态系统,为此我们创建了集成测试【10】存储库,以确保将来至少可以捕获到其中的一些边缘用例,免得它们落入到你的机器中。如果你对打包有任何建议或诉求,请随时在“ 讨论Python论坛【11】”的打包部分进行讨论,或者为相关工具提一个 issue。 Milan Popovic摄/ Unsplash--结束了 先到此为止了,谢谢阅读完!我要感谢Paul Ganssle【12】审阅了打包系列文章,并要感谢Tech At Bloomberg【13】允许我在工作期间作开源贡献。 相关链接 [1]Python packaging - Growing Pains:https://www.bernat.tech/growing-pain/ [2]TOML文件格式。:https://github.com/toml-lang/toml [3]Towncrier:https://pypi.org/project/towncrier/ [4]black:https://pypi.org/project/black/ [5]pip 18.0(于2018年7月22日发布):https://pip.pypa.io/en/stable/news/%23id61#id61 [6]请参阅附录的问题。:https://github.com/pypa/pip/issues/6158 [7]setuptools 1650:https://github.com/pypa/setuptools/pull/1650 [8]setuptools:https://github.com/pypa/setuptools/pull/1650 [9]python Discourse board:https://discuss.python.org/t/pep-517-backend-bootstrapping [10]集成测试:https://github.com/pypa/integration-test [11]讨论Python论坛:https://discuss.python.org/c/packaging [12]Paul Ganssle:https://twitter.com/pganssle [13]Tech At Bloomberg:https://twitter.com/techatbloomberg 公众号【Python猫】, 本号连载优质的系列文章,有喵星哲学猫系列、Python进阶系列、好书推荐系列、技术写作、优质英文推荐与翻译等等,欢迎关注哦。

优秀的个人博客,低调大师

你有多久没下载过新的手机应用了?

【大咖・来了 第7期】10月24日晚8点观看《智能导购对话机器人实践》 智能手机用户对新应用的热情正在消退,这不是最近才发生的事。 来自 Hulu 和 Flipboard 的前***技术官,现任投资公司 KPCB 合伙人的埃里克·冯(Eric Feng)最近撰文重申了一个观点,即移动应用的淘金时代已经终结,大部分人都习惯使用那些相对成熟的应用,却很少愿意尝试新的。 他还带来了一些数据支撑,除了 AppAnnie 的分析外,还有对苹果官方 iTunes App Charts(中国区也有类似的页面)的统计,这是一个每周更新的应用列表,会按照下载量排名。 结果显示,在过去 5 年里,App Store 商店中排名前 30 应用的每周下载量都没有出现太大的变化。 具体点说,无论是 2014 年还是 2019 年,你都得达到平均 23 万次的周下载量,才有机会进入 TOP30 的行列。 另外,Eric Feng 还统计了 TOP30 应用的「平均在架时长」,你也可以理解为是它们的「年龄」,也就是应用上架后存在于商店中的时间。 时间越长,证明老应用越多,反之则意味着榜单内有很多刚上架不久的新应用。 在 2014 年,TOP30 应用的「平均在架时长」为 625 天,大概还不到 2 年。 但在 2019 年的今天,TOP30 应用的「平均在架时长」已经上升至 1853 天,超过了 5 年。 由此也能看到,人们确实会更热衷于下载那些上架已久的老牌应用,而且这种现象同样存在于 Google Play 商店中——在那里,TOP30 应用的「平均在架时长」已经从 2014 年的 18 个月,上涨至 2019 年的近 4 年。 如果再进一步细化到子项,比如社交类应用、购物类应用等,各类别的 TOP30 应用「平均在架时长」走势基本和上述一致,每周 TOP30 榜单里,只有约 13% 的应用是近 2 年内才上架的,其余则是些已经存在了 4-5 年的老面孔。 只有手机游戏是个例外。趋势图显示,2019 年每周 TOP30 榜单里,有近 85% 的游戏都是新出现的,这比 2015 年的 72% 还要更高,意味着大部分用户对于新游戏的热情远大于那些工具型应用。 但总体归结下来,新应用出现在榜单中的频率,仍然比那些老应用要低得多。 说到这里不由得扪心自问一下,当你换上新手机,重新下载应用时,首先想到的是否也是那几款「必备应用」呢? 这和 10 年前 App Store 刚出现时,人们都热衷于寻找各种限免、新奇特应用时的状况很不一样。 我还记得刚刚用上 iPhone 3GS 时 ,因为好奇心和新鲜感,每天都会点开 App Store 刷一下有什么新应用、新游戏上架,也会花时间盯着各种限免、App 推荐网站,只要是看着顺眼的都会下载下来试用一番。 但现在不同了,除了看到弹出的红色数字角标后,会打开 App Store 更新一下,很多人已经不再主动寻找、发现和下载新的应用。 而历经十多年的发展,App Store 也早已经从最初只有 500 款应用的时代演变成 200 万款,你几乎可以在任何一个特定领域,找到几十个拥有相似功能的应用。 但真正会下载下来并长期使用的,可能只有一款而已。 早在 2011 年,特斯拉 CEO 埃隆·马斯克(Elon Musk)接受 《Daily Beast》采访时也有过表态,他称「互联网创新仍然会继续,但很多重要问题已经得到了解决」,说的就是移动应用对传统互联网的影响。 这也是为什么那些「必备应用」的地位很难动摇:提到社交,你就会很自然联想到微信微博,找美食则会用大众点评,点外卖会用美团,打车用滴滴,导航用百度高德,购物会用淘宝京东,看视频就用优酷爱奇艺,听音乐会用网易云音乐等等。 对于这些拥有千万乃至上亿级用户量的应用来说,它们不仅培养了智能手机这代用户的使用习惯,还串联起他们日常生活中的方方面面。 更不要说像微信和支付宝这类「超级 App」了,它们所集成的服务种类繁多,早已经超越了本身「聊天工具」和「支付工具」的范畴和定位,更省去你下载那些单一功能应用的必要性。 哪怕每年年终盘点时,我们都可以找到几款表现突出的新应用,但一年过后,它们还能否保留在你的手机中,以及是否会被其它应用所取代,都是未知数。 毕竟,最终抢占时间和注意力的应用,早已经固定在那十几款 App 中。都说人们喜新厌旧,但在 App 上更多表现出来的是「人不如故」,这是一个有些悲伤的事实。 另一方面,榜单的固化问题,也和当下应用商店的呈现方式有一定的关系。 大部分应用商店的 UI 层级和结构基本是一样的,不管是类似 TOP30 这样的下载榜单,还是搜索栏弹出的热搜词,亦或者是各个页面的头图推荐位,都可以视为是应用下载流量的重要通道。 问题就在于,如果单纯以下载量作为衡量标准,那么最终带来的结果就是明显的头部效应。 加上商店的展示空间和位置就那么多,还存在「刷榜」这样的手段,中小厂商的产品更难获得应有的曝光,个人开发者的作品则直接易石沉大海,更别说让用户主动知悉自己的存在。 种种问题促使了苹果在 iOS 11 对原有 App Store 商店的大改版,比如说弱化排行榜和畅销榜,新增包括「今日 App」这样的栏目,让人工编辑参与到应用推荐的环节中,而不止是单纯依靠机器算法和下载量来作为应用好坏的衡量标准。 还有一个值得关注的是苹果上个月宣布的 Apple Arcade 游戏订阅制服务,同样可以视为是一个让小众独立游戏获得更高曝光量的新手段。 当然,对中小型开发者来说,和那些拥有巨额资本的厂商抢夺 TOP30 的位置显然不明智,Eric Feng 也列出了三项建议。 一个是找到并吸引你的「超级用户」,它们可能只占据大众用户量的 10% 不到,但在合理的商业模式下,说不定能确保自己健康地发展下去;其次是寻求新的应用平台,无需盯着 App Store 不放。 ***是开辟一个全新的应用领域,提前解决一个潜在的大众需求点,当然这也是最难实现的。毕竟,能预判并精准抓住大众需求的,不是穿越者,就是真的天才吧。

优秀的个人博客,低调大师

那些年我们一起踩过的Dubbo坑

dubbo历史2011 年末,阿里巴巴在 GitHub 上开源了基于 Java 的分布式服务治理框架 Dubbo,之后它成为了国内该类开源项目的佼佼者,许多开发者对其表示青睐。同时,先后有不少公司在实践中基于 Dubbo 进行分布式系统架构,目前在 GitHub 上,它的 fork、star 数均已破万。2014 年 10 月 30 号发布版本 dubbo-2.4.11,修复了一个小 Bug,版本又陷入漫长的停滞到2017年九月份。在dubbo停滞的期间呢,当当网 Fork 了阿里的一个 Dubbo 版本开始维护,并命名为 dubbox-2.8.0。值得注意的是,当当网扩展 Dubbo 服务框架支持 REST 风格远程调用,并且跟随着 ZooKeepe 和 Spring 升级了对应的版本。之后 Dubbox 一直在小版本维护,2015 年 3 月 31 号发布了最后一个版本 dubbox-2.8.4。笔者公司用的也是这个版本,并稍微改造了下源码,下面会有提及。其实在当前说到微服务,可能大家第一反应是springcloud,spring全家桶带来的便捷是显而易见的,然而为什么我们这里聊的是dubbo呢?原因之一是因为笔者公司只用了dubbo(别扔鸡蛋....),其二呢其实rpc框架很多原理是相通的,当我们理解了其中一个,再去看其他的框架,会有一种似曾相识的感觉,最后也没必要去争论XX框架的好与坏,选择最适合自己业务的就是最好的。先交代下背景,我们这边是从2016年开始使用dubbo,使用的是dubbox-2.8.4 版本,然后因为一些场景不合适改了下代码,重新打包成2.8.5提交至公司的私服使用。好了,接下来就开始进入正文,聊聊这几年在dubbo使用过程中遇到坑,以及需要注意的地方吧。正文1、超时重试这是一个很经典的坑,当时由于刚使用dubbo,很多配置都是基于默认的。刚好此时在项目中,有一个机器人送礼的逻辑比较复杂,当遇到某些特定的条件时,该逻辑的耗时会比正常情况下变长,这时候就出现了一个很神奇的现象,为何我只触发了一次送礼的请求,而线上却送了三次?刚遇到这种情况可我惊呆了,重新审视了代码,发现并无问题。这就奇怪了,哪里来的3次?后来掉了几根头发以后,才在dubbo的文档中发现了服务这块有timeout跟retry属性,默认timeout=1000ms,retry=2。这下就豁然开朗,原来是第一次调用超时,导致又重试了2次,一共就是3次了。找到问题的原因,我们就有办法解决了。由于我们这个接口不是幂等性的,而且也不用返回什么信息给调用者,所以我们可以通过一个线程池来执行这段耗时的逻辑,让rpc调用可以比较快的返回给调用者。这样就不存在超时的问题了。或者可以配合增加timeout时间跟retry=0也能实现,具体的业务逻辑需要自己找到合适的解决方案。2、dubbo使用内网ip正常情况下,我们的服务调用推荐走内网连接的方式,效率是比较高的。但是有些特殊的情况,我们需要dubbo注册服务的时候使用外网ip,该怎么修改呢?这时候就需要修改我们的服务器上 /etc/hosts 文件了,新增一条 “外网ip 主机名”的记录,restart我们的服务即可。3、docker里面注册宿主机内网ip说到微服务,当然也少不了docker了,我们当前用的是docker+overlay网络一个结构,直接把dubbo服务丢进容器里面跑的话,注册进zk的ip是容器ip。所以我们采取了一种折中的方式。利用docker的特性,我们在创建容器的时候,把宿主机的ip以及需要暴露的端口写进容器的环境变量里面。然后就是修改dubbox的源码了,源码的com.alibaba.dubbo.registry.integration.RegistryProtocol类的getRegistedProviderUrl方法,此方法用于返回注册到注册中心的URL。 private URL getRegistedProviderUrl(final Invoker<?> originInvoker){ //targetUrl 注册中心看到的地址 URL targetUrl; URL providerUrl = getProviderUrl(originInvoker); //配置的容器环境变量 String envParameterHost=System.getenv(ENV_HOST_KEY); String envParameterPort=System.getenv(ENV_PORT_KEY); if (StringUtils.isBlank(envParameterHost)||StringUtils.isBlank(envParameterPort)){//非容器环境:执行原来的注册逻辑 targetUrl=providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY); }else {//容器环境,如果环境变量中DOCKER_NAT_HOST和DOCKER_NAT_PORT两个值都不为空则直接将这两个值作为url注册到zk //执行重新拼接url的操作,涉及敏感代码这里不展示了 targetUrl=dockerRegUrlWithHostAndPort; } return targetUrl; } 4、未注意服务重名其实这是我们开发人员粗心大意出现的情况,开发的时候注册了2个相同签名的服务,但是业务逻辑是完全不同的,这会导致一个之前运行的正常的业务会偶尔调用失败,原因是因为dubbo的负载均衡策略,把一部分流量转移到我们新注册上来的服务上了,但是处理逻辑不同,导致错误。5、版本的一致性dubbo当前的releases版本已经去到2.7.1了,项目中要注意一下不同项目间版本的一致性,或者是dubbo跟dubbox的一些差别,最好做到统一,不然出现问题解决的成本会比较高。6、属性配置的优先级我们在dubbo的过程中会发现,提供者跟消费者中,很多属性是一样的,我们该怎么配呢?在dubbo的文档当中其实有推荐的用法。在提供者端尽量多提供消费者端的属性。参考文档,原因如下:作服务的提供方,比服务消费方更清楚服务的性能参数,如调用的超时时间、合理的重试次数等在 Provider 端配置后,Consumer 端不配置则会使用 Provider 端的配置,即 Provider 端的配置可以作为 Consumer 的缺省值 。否则,Consumer 会使用 Consumer 端的全局设置,这对于 Provider 是不可控的,并且往往是不合理的Provider 端尽量多配置 Consumer 端的属性,让 Provider 的实现者一开始就思考 Provider 端的服务特点和服务质量等问题。结语其实在dubbo的使用过程中,还有挺多问题这里没列出来的,但是解决方法都差不多,首先文档要熟,做到心中有数,比如dubbo功能的成熟度,有些是不推荐在线上使用的,这时你就要谨慎了。然后文档里面确实是有遗漏的问题,我们有必要可以debug dubbo的源码,这个过程会比较痛苦,但是对于排查问题跟个人能力的提高是有很有帮助的。

优秀的个人博客,低调大师

JPress的ehcache缓存方案、以及踩过ehcache的坑

精通JFinal视频教程:http://www.roncoo.com/course/view/a033a4dff3a2473f9f9f6667cf069ffd 最近为了提高JPress的性能,减少数据查询的次数,JPress大量使用了ehcache缓存作为起内置缓存,同时session也是基于ehcache重新实现的支持分部署的session解决方案。 因为JPress是基于JFinal快速开发框架,而JFinal又内置了ehcache的插件,使用起来及其简单。 1、JFinal里配置ehcachePlugin插件。 publicvoidconfigPlugin(Pluginsme){me.add(newEhCachePlugin());//添加其他插件} 2、在classPath下添加ehcache的配置文件ehcache.xml <?xmlversion="1.0"encoding="UTF-8"?> <ehcachexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"monitoring="autodetect" dynamicConfig="true"> <diskStorepath="java.io.tmpdir"/> <defaultCache maxEntriesLocalHeap="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="20" timeToLiveSeconds="60"> </defaultCache> <!-- SamplecachenamedsampleCache1 Thiscachecontainsamaximuminmemoryof10000elements,andwillexpire anelementifitisidleformorethan5minutesandlivesformorethan10minutes. Iftherearemorethan10000elementsitwilloverflowtothe diskcache,whichinthisconfigurationwillgotowhereverjava.io.tmpis definedonyoursystem.OnastandardLinuxsystemthiswillbe/tmp"--> <cachename="sampleCache1" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="1000" eternal="false" overflowToDisk="true" diskSpoolBufferSizeMB="20" timeToIdleSeconds="300" timeToLiveSeconds="600" memoryStoreEvictionPolicy="LFU" transactionalMode="off" /> <!-- SamplecachenamedsampleCache2 Thiscachehasamaximumof1000elementsinmemory.Thereisnooverflowtodisk,so1000 isalsothemaximumcachesize.Notethatwhenacacheiseternal,timeToLiveand timeToIdlearenotusedanddonotneedtobespecified.--> <cachename="sampleCache2" maxEntriesLocalHeap="1000" eternal="true" overflowToDisk="false" memoryStoreEvictionPolicy="FIFO" /> <!-- SamplecachenamedsampleCache3.Thiscacheoverflowstodisk.Thediskstoreis persistentbetweencacheandVMrestarts.Thediskexpirythreadintervalissetto10 minutes,overridingthedefaultof2minutes.--> <cachename="sampleCache3" maxEntriesLocalHeap="500" eternal="false" overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="true" diskExpiryThreadIntervalSeconds="1" memoryStoreEvictionPolicy="LFU" /> </ehcache> 3、直接使用EhcacheKit操作缓存。 publicvoidyourMethod(){ Cachekit.put("cacheName","key","value") } 到此,一切很顺利的进行着,但随着JPress在大量的使用ehcache,ehcache的缓存数据操作与更新就变成了一个棘手的问题,更新数据库数据了,缓存若得不到及时更新,就会导致程序在运行的过程中有大量的bug、各种莫名其妙的问题。此时、缓存数据的更新,就需要一个良好更新的计划和方案。 首先,是数据颗粒度的问题,我们在缓存数据的时候,可能是根据数据库的ID,对单个model(单条数据)进行缓存,这种缓存以model的ID作为key进行缓存,这种缓存的颗粒度极细。 因此,我们在做数据的更新的时候非常简单,只需在model的更新和删除的时候从ehcache删除该ID即可。针对这一的问题,我们只需要重写Model的update和delete方法,删除其缓存。 代码如下: @Table(tableName="content",primaryKey="id")publicclassContentextendsBaseContent{privatestaticfinallongserialVersionUID=1L; @Overridepublicbooleanupdate(){ removeCache(getId());//移除ehcache缓存 returnsuper.update(); } @Overridepublicbooleandelete(){ removeCache(getId());//移除ehcache缓存 returnsuper.delete(); } } 通过这种方式,我们在通过ID来查询该数据的时候,不用担心缓存于数据库不同步的问题,因为我们在更新、删除的时候就已经把ehcache的缓存数据给清除掉了,当查询的时候发现ehcache里没有数据,自动会去数据库会获取,从而保证了ehcache的数据与数据库保持一致。 但是,我们在缓存数据的时候,不只是对单个model进行缓存,在程序的各种业务场景中,大量会使用到列表的查询,因此我们在存储的时候,肯定也会多列表进行缓存。例如: publicListfindByModule(finalStringmodule,finalBigIntegerparentId,Stringorderby){ finalStringBuildersqlBuilder=newStringBuilder("select*fromcontentc"); sqlBuilder.append("sql....");returnDAO.getFromListCache("cacheName","key",newIDataLoader(){ @OverridepublicObjectload(){returnDAO.find(sqlBuilder.toString(),module,parentId); } }); } 但是,一旦缓存了列表,问题就来了?这个列表的数据什么时候会被更新呢?这个缓存到ehcache的某条数据可能会被随时更新或删除了,怎么来同步? 一种粗糙的方案是:把所有缓存都缓存到同一个cacheName中,然后在model的update或delete的时候,对这个cacheName不管三七二十一直接全部清除,如下代码: @Table(tableName="content",primaryKey="id")publicclassContentextendsBaseContent{privatestaticfinallongserialVersionUID=1L; @Overridepublicbooleanupdate(){ removeCache(getId());//移除ehcache缓存 removeAllListCache();//移除所有保存列表数据的缓存 returnsuper.update(); } @Overridepublicbooleandelete(){ removeCache(getId());//移除ehcache缓存 removeAllListCache();//移除所有保存列表数据的缓存 returnsuper.delete(); } } 虽然这是一种粗糙的方案,但是也是有效解决了列表数据不同步的问题;其粗糙的原因是,当我们清除数据的时候,把所有的列表都删除了,这样会导致很多没有没有该列表的数据也被清楚了... 所以,更有效的解决方案应该是保留和该ID没有关系的数据,而只清除有关的数据。 那问题来了,什么数据才是该ID有关的数据呢? 1、列表有该ID的数据。 2、列表的排序等会受到该ID影响的数据,比如谋条数据的orderby_number更新了,可能某个缓存的列表数据虽然没有该ID,但是该ID更新后,可能是orderby_number,由于缓存的列表数据是根据orderby_number来排序的,此时该数据应该出现在列表里。 3、分页数据,比如某条数据被删除或更新了,可能分页的页码数据就会被改变。 那如何才能找到该ID关联的数据呢? 这是一个困难的问题,每个业务系统不一样,关联的数据肯定也不一样。在JPress里,每个content都有一个module字段,表示该数据所属的模型。 因此,在JPress的内容分类里,JPress针对某种类型的数据,都按照一定的规则来建立这个存储的key,比如文章模型的列表在存储的时候,存储的key值大概为:module:article-xxx-xxx这样的key。 当文章模型的数据被更新的时候,会去便利所有列表数据的key,如果发现key是以module:article开头,表示该数据是文章列表的缓存数据,应该清除。 于是,就有了如下的代码: publicvoidremoveAllListCache(){ Listlist=CacheKit.getKeys(CACHE_NAME);if(list!=null&&list.size()>0){for(ObjectkeyObj:list){ StringkeyStr=(String)keyObj;if(!keyStr.startsWith("module:")){ CacheKit.remove(CACHE_NAME,keyStr);continue; }//不清除其他模型的内容 if(keyStr.startsWith("module:"+getModule())){ CacheKit.remove(CACHE_NAME,keyStr); } } } } 大功告成,测试、运行。 然而,踩坑才刚刚开始。 ehcache的坑1:getKeys("cacheName")为空数据。 本以为理想的解决了我的方案,兴高采烈的查看测试结果,然而发现了一个致命的问题,更新或删除单条数据后,缓存的列表数据没有被更新,debug后才发现,通过CacheKit.getKeys("cacheName")得到的数据总是不正确,绝大多数的情况下返回了空列表,开始以为是JFinal的问题,然后跟进源代码后,JFinal根本没有对getKeys进行任何的操作,而直接返回了。 在查询资料的过程中,也曾发现在oschina上有人提供类型的问题:http://www.oschina.net/question/2298963_2141262,然后没有一个较好的答复。在spring的网站上(https://jira.spring.io/browse/SPR-8878)找到了这么一句话。 Consider a cache with 100k items - if you ask for the keys, most likely you'll end up with an OOM. 大概意思是,如果保存了很多数据,当去获取所有数据的keys的时候,可能会造成内存溢出。但无论如何,我始终觉得这是ehcache的一个大坑,如果ehcache的作者始终这么考虑的话,完全不用提供这个getKeys这个方法好了,为毛还要提供出来呢? 那getKeys这条路行不通,那我们就必须自己去维护这个keys。也就是自己来就来我记录我存了哪些key。 于是,在保存到cache的时候,有了如下的代码: publicListfindByModule(finalStringmodule,finalBigIntegerparentId,Stringorderby){ finalStringBuildersqlBuilder=newStringBuilder("select*fromcontentc"); sqlBuilder.append("sql....");returnDAO.getFromListCache("cacheName","key",newIDataLoader(){ @OverridepublicObjectload(){returnDAO.find(sqlBuilder.toString(),module,parentId); } }); }publicTgetFromListCache(Objectkey,IDataLoaderdataloader){ ListinCacheKeys=CacheKit.get(CACHE_NAME,"cachekeys"); ListcacheKeyList=newArrayList();if(inCacheKeys!=null){ cacheKeyList.addAll(inCacheKeys); } cacheKeyList.add(key.toString()); CacheKit.put(CACHE_NAME,"cachekeys",cacheKeyList);returnCacheKit.get("content_list",key,dataloader); } 在保存的时候,把keys全部保存到一个单独的缓存里面; 在删除缓存的时候,不通过getKeys了,而是去这个缓存里面查看有哪些key。代码如下: publicvoidremoveAllListCache(){ Listlist=CacheKit.get(CACHE_NAME,"cachekeys");if(list!=null&&list.size()>0){for(ObjectkeyObj:list){ StringkeyStr=(String)keyObj;if(!keyStr.startsWith("module:")){ CacheKit.remove("taxonomy_list",keyStr);continue; }//不清除其他模型的内容 if(keyStr.startsWith("module:"+getModule())){ CacheKit.remove("taxonomy_list",keyStr); } } } } 到此,ehcache的getKeys坑总算是告了一个段落。 ehcache的坑2:存储的list列表数据小心复用(或不能复用)。 大喜之余,Ehcache的坑又接踵而来。在使用的过程中,莫名其妙的不定时的出了一个错误.... net.sf.ehcache.CacheException:FailedtoserializeelementduetoConcurrentModificationException.Thisisfrequentlytheresultofinappropriatelysharingthreadunsafeobject(eg.ArrayList,HashMap,etc)betweenthreads atnet.sf.ehcache.store.disk.DiskStorageFactory.serializeElement(DiskStorageFactory.java:405) atnet.sf.ehcache.store.disk.DiskStorageFactory.write(DiskStorageFactory.java:385) atnet.sf.ehcache.store.disk.DiskStorageFactory$DiskWriteTask.call(DiskStorageFactory.java:477) atnet.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1071) atnet.sf.ehcache.store.disk.DiskStorageFactory$PersistentDiskWriteTask.call(DiskStorageFactory.java:1055) 一看,麻蛋!!多线程的问题啊...错误log没有具体到我自己项目中的哪一行代码,此项想到在JPress的设计中,由于为了解耦,JPress自行开发了一套消息机制,默认情况下全是开辟新的线程去执行的...... 此时,想哭。 抽了根烟后,脑子中灵光乍现,不对啊,在tomcat对servlet的处理模型中,每个请求其实都是开辟了新的线程去处理单独的请求,每个请求也都有可能对ehcache进行操作....不可能是多线程的问题。 此时,已经是深夜2点。 赶紧打开电脑,看看stackoverflow(一个国外知名的编程问答网站)上是否有有人遇到过类似的问题。经过半小时的检索阅读后,终于在http://stackoverflow.com/questions/35816456/error-serializing-element-in-ehcache找到了蛛丝马迹。 由于我们存储到ehcache的数据列表可能是一个list数据,此时的list数据可能还保存在内存里,读取的代码如下: publicListfindByModule(finalStringmodule,finalBigIntegerparentId,Stringorderby){ finalStringBuildersqlBuilder=newStringBuilder("select*fromcontentc"); sqlBuilder.append("sql....");returnDAO.getFromListCache("cacheName","key",newIDataLoader(){ @OverridepublicObjectload(){returnDAO.find(sqlBuilder.toString(),module,parentId); } }); } 在如上的代码中,DAO.getFromListCache可能得到的是内存里的数据,然而调用这个方法的controller很多,每个controller都有自己的业务逻辑,也就是说每个controller都有可能对保存在ehcache内存里的list进行操作(修改、删除、添加),因而出现了 "net.sf.ehcache.CacheException: Failed to serialize element due to ConcurrentModificationException. This is frequently the result of inappropriately sharing thread unsafe object (eg. ArrayList, HashMap, etc) between threads" 这个错误。 如果真的是这样,就好办了....赶紧修改代码测试。 publicListfindByModule(finalStringmodule,finalBigIntegerparentId,Stringorderby){ finalStringBuildersqlBuilder=newStringBuilder("select*fromcontentc"); sqlBuilder.append("sql...");//略... Listdata=DAO.getFromListCache(buildKey(module,parentId,orderby),newIDataLoader(){ @OverridepublicObjectload(){returnDAO.find(sqlBuilder.toString(),params.toArray()); } });if(data==null)returnnull; returnnewArrayList(data); } 如果能够从缓存中得到数据,重新new一个新的list返回。 经过两个小时的测试后,这个问题再也没有出现。 在使用ehcache中,记得一个小伙伴又给我反馈了一个问题,就是在他的一台服务器里,部署了多个JPress,导致后来出现了ehcache数据重合的情况,JPress应用A读到了JPress应用B的缓存数据。 不开源、不知道,开源吓一跳。 虽然是一个"小"问题,但是也很棘手,两个应用同时使用了一份ehcache的数据,原因就是ehcache把数据存储到磁盘的时候,存储到了同一个地方了,在ehcache的配置文件ehcache.xml中,如下代码: 在ehcache的配置中,diskStore都是指向了同一个地方,如果这个同学在部署的时候自己修改了diskStore,指定到具体的位置就可以。但是有没有什么办法让每个JPress应用的ehcache缓存保存在自己应用的webRoot目录下呢?这样,就无需用户自己去配置了。 赶紧去ehcache官网看看,怎么配置diskStore,才能让缓存保存在自己的webRoot目录.... 在ehcache的官方文档:http://www.ehcache.org/generated/2.10.2/pdf/Ehcache_Configuration_Guide.pdf的第15页中,找到了如下的内容: 可以配置user.home(用户的家目录)、user.dir(用户当前的工作目录)、java.io.tmpdir(默认的临时目录)、ehcache.disk.store.dir(ehcache的配置目录)和具体的目录,却不能配置成webRoot的目录.... 于是,我想到了自己去加载这个配置文件,然后自由指定diskStore的目录; 于是,在JFinal的配置文件中,就有了如下的代码: publicvoidconfigPlugin(Pluginsplugins){ plugins.add(createEhCachePlugin());//其他插件略...}publicEhCachePlugincreateEhCachePlugin(){ StringehcacheDiskStorePath=PathKit.getWebRootPath(); FilepathFile=newFile(ehcacheDiskStorePath,".ehcache"); Configurationcfg=ConfigurationFactory.parseConfiguration(); cfg.addDiskStore(newDiskStoreConfiguration().path(pathFile.getAbsolutePath()));returnnewEhCachePlugin(cfg); } 成功的把ehcache的存储目录保存在了webRoot的.ehcache目录下.... 此时,也感叹JFinal的ehcachePlugin插件的足够灵活。 到此,JPress在遇到的ehcache坑中解决完毕,终于松了一口气

优秀的个人博客,低调大师

Zabbix3.4通过shell脚本监控redis服务

公司最近网站改版,增加了redis服务器,现领导要求需要测试redis的监控,于是从网上找了个redis的监控脚本,简单的修改了一下,测试中没出现任何问题。生产环境中,可根据实际要监控的参数值修改脚本内容,并添加相应的触发器。Redis有自带的redis-cli客户端,通过info命令可以查询到redis的运行情况,我们可以写个shell脚本,通过zabbix来调用这个脚本实现redis的监控。 一、info命令的使用 要获得redis的当前情况,可以使用info命令。命令格式: redis-cli -h [hostname] -p [port] -a [password] info [参数] 1、查询server信息 redis-cli -h 127.0.0.1 -p 6379 -a 'password' info server 2、查询客户端连接情况 redis-cli -h 127.0.0.1 -p 6379 -a 'password' info clients 3、查询内存使用情况 redis-cli -h 127.0.0.1 -p 6379 -a 'password' info memory 4、查询CPU使用情况 redis-cli -h 127.0.0.1 -p 6379 -a 'password' info cpu 二、创建redis监控脚本 1、编写监控脚本vim /etc/zabbix/zabbix_agentd.d/redis_status.sh #!/bin/bash REDISCLI="/usr/local/bin/redis-cli" HOST="127.0.0.1" PORT=6379 PASS="password" if [[ $# == 1 ]];then case $1 in version) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info server | grep -w "redis_version" | awk -F':' '{print $2}'` echo $result ;; uptime) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info server | grep -w "uptime_in_seconds" | awk -F':' '{print $2}'` echo $result ;; connected_clients) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info clients | grep -w "connected_clients" | awk -F':' '{print $2}'` echo $result ;; blocked_clients) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info clients | grep -w "blocked_clients" | awk -F':' '{print $2}'` echo $result ;; used_memory) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info memory | grep -w "used_memory" | awk -F':' '{print $2}'` echo $result ;; used_memory_rss) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info memory | grep -w "used_memory_rss" | awk -F':' '{print $2}'` echo $result ;; used_memory_peak) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info memory | grep -w "used_memory_peak" | awk -F':' '{print $2}'` echo $result ;; used_memory_lua) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info memory | grep -w "used_memory_lua" | awk -F':' '{print $2}'` echo $result ;; used_cpu_sys) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info cpu | grep -w "used_cpu_sys" | awk -F':' '{print $2}'` echo $result ;; used_cpu_user) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info cpu | grep -w "used_cpu_user" | awk -F':' '{print $2}'` echo $result ;; used_cpu_sys_children) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info cpu | grep -w "used_cpu_sys_children" | awk -F':' '{print $2}'` echo $result ;; used_cpu_user_children) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info cpu | grep -w "used_cpu_user_children" | awk -F':' '{print $2}'` echo $result ;; rdb_last_bgsave_status) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info Persistence | grep -w "rdb_last_bgsave_status" | awk -F':' '{print $2}' | grep -c ok` echo $result ;; aof_last_bgrewrite_status) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info Persistence | grep -w "aof_last_bgrewrite_status" | awk -F':' '{print $2}' | grep -c ok` echo $result ;; aof_last_write_status) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info Persistence | grep -w "aof_last_write_status" | awk -F':' '{print $2}' | grep -c ok` echo $result ;; *) echo -e "\033[33mUsage: $0 {connected_clients|blocked_clients|used_memory|used_memory_rss|used_memory_peak|used_memory_lua|used_cpu_sys|used_cpu_user|used_cpu_sys_children|used_cpu_user_children|rdb_last_bgsave_status|aof_last_bgrewrite_status|aof_last_write_status}\033[0m" ;; esac elif [[ $# == 2 ]];then case $2 in keys) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info | grep -w "$1" | grep -w "keys" | awk -F'=|,' '{print $2}'` echo $result ;; expires) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info | grep -w "$1" | grep -w "keys" | awk -F'=|,' '{print $4}'` echo $result ;; avg_ttl) result=`$REDISCLI -h $HOST -a $PASS -p $PORT info | grep -w "$1" | grep -w "avg_ttl" | awk -F'=|,' '{print $6}'` echo $result ;; *) echo -e "\033[33mUsage: $0 {db0 keys|db0 expires|db0 avg_ttl}\033[0m" ;; esac fi 2、赋予脚本可执行权限 chmod +x /etc/zabbix/zabbix_agentd.d/redis_status.sh 3、脚本测试查看redis的客户端连接数 /etc/zabbix/zabbix_agentd.d/redis_status.sh connected_clients 三、创建redis监控配置文件 1、编写redis监控配置文件vim /etc/zabbix/zabbix_agentd.d/redis.conf UserParameter=Redis.Status,status=`/usr/local/bin/redis-cli -h 127.0.0.1 -a 'password' -p 6379 ping|grep -c PONG` &&echo $status UserParameter=Redis.Info[*],/etc/zabbix/zabbix_agentd.d/redis_status.sh $1 $2 2、重启zabbix-agent systemctl restart zabbix-agent 3、在zabbix server端测试 zabbix_get -s 192.168.2.235 -p 10050 -k "Redis.Info[used_cpu_user]" 四、创建并导入监控模板 1、创建监控模板文件redis-template.xml文件内容如下 <?xml version="1.0" encoding="UTF-8"?> <zabbix_export> <version>2.0</version> <date>2014-08-07T10:04:35Z</date> <groups> <group> <name>RedisMontior</name> </group> <group> <name>Templates</name> </group> </groups> <templates> <template> <template>RedisMontior</template> <name>RedisMontior</name> <groups> <group> <name>RedisMontior</name> </group> <group> <name>Templates</name> </group> </groups> <applications> <application> <name>Redis Clients</name> </application> <application> <name>Redis CPU</name> </application> <application> <name>Redis DbKey</name> </application> <application> <name>Redis Memory</name> </application> <application> <name>Redis WriteStatus</name> </application> </applications> <items> <item> <name>Redis.Info[aof_last_bgrewrite_status]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[aof_last_bgrewrite_status]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis WriteStatus</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[aof_last_write_status]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[aof_last_write_status]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis WriteStatus</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[blocked_clients]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[blocked_clients]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Clients</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[connected_clients]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[connected_clients]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Clients</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[db0,avg_ttl]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[db0,avg_ttl]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis DbKey</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[db0,expires]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[db0,expires]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis DbKey</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[db0,keys]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[db0,keys]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis DbKey</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[rdb_last_bgsave_status]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[rdb_last_bgsave_status]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis WriteStatus</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[uptime]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[uptime]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units>uptime</units> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications/> <valuemap/> </item> <item> <name>Redis.Info[used_cpu_sys]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_cpu_sys]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>0</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis CPU</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_cpu_sys_children]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_cpu_sys_children]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>0</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis CPU</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_cpu_user]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_cpu_user]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>0</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis CPU</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_cpu_user_children]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_cpu_user_children]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>0</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis CPU</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_memory]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_memory]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Memory</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_memory_lua]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_memory_lua]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Memory</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_memory_peak]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_memory_peak]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Memory</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[used_memory_rss]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[used_memory_rss]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications> <application> <name>Redis Memory</name> </application> </applications> <valuemap/> </item> <item> <name>Redis.Info[version]</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Info[version]</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>1</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications/> <valuemap/> </item> <item> <name>Redis Status</name> <type>0</type> <snmp_community/> <multiplier>0</multiplier> <snmp_oid/> <key>Redis.Status</key> <delay>30</delay> <history>90</history> <trends>365</trends> <status>0</status> <value_type>3</value_type> <allowed_hosts/> <units/> <delta>0</delta> <snmpv3_contextname/> <snmpv3_securityname/> <snmpv3_securitylevel>0</snmpv3_securitylevel> <snmpv3_authprotocol>0</snmpv3_authprotocol> <snmpv3_authpassphrase/> <snmpv3_privprotocol>0</snmpv3_privprotocol> <snmpv3_privpassphrase/> <formula>1</formula> <delay_flex/> <params/> <ipmi_sensor/> <data_type>0</data_type> <authtype>0</authtype> <username/> <password/> <publickey/> <privatekey/> <port/> <description/> <inventory_link>0</inventory_link> <applications/> <valuemap/> </item> </items> <discovery_rules/> <macros/> <templates/> <screens/> </template> </templates> <triggers> <trigger> <expression>{RedisMontior:Redis.Status.last(0)}=0</expression> <name>Redis is down</name> <url/> <status>0</status> <priority>5</priority> <description/> <type>0</type> <dependencies/> </trigger> </triggers> <graphs> <graph> <name>Redis Client</name> <width>900</width> <height>200</height> <yaxismin>0.0000</yaxismin> <yaxismax>100.0000</yaxismax> <show_work_period>1</show_work_period> <show_triggers>1</show_triggers> <type>0</type> <show_legend>1</show_legend> <show_3d>0</show_3d> <percent_left>0.0000</percent_left> <percent_right>0.0000</percent_right> <ymin_type_1>0</ymin_type_1> <ymax_type_1>0</ymax_type_1> <ymin_item_1>0</ymin_item_1> <ymax_item_1>0</ymax_item_1> <graph_items> <graph_item> <sortorder>0</sortorder> <drawtype>0</drawtype> <color>C80000</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[blocked_clients]</key> </item> </graph_item> <graph_item> <sortorder>1</sortorder> <drawtype>0</drawtype> <color>00C800</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[connected_clients]</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Redis CPU</name> <width>900</width> <height>200</height> <yaxismin>0.0000</yaxismin> <yaxismax>100.0000</yaxismax> <show_work_period>1</show_work_period> <show_triggers>1</show_triggers> <type>0</type> <show_legend>1</show_legend> <show_3d>0</show_3d> <percent_left>0.0000</percent_left> <percent_right>0.0000</percent_right> <ymin_type_1>0</ymin_type_1> <ymax_type_1>0</ymax_type_1> <ymin_item_1>0</ymin_item_1> <ymax_item_1>0</ymax_item_1> <graph_items> <graph_item> <sortorder>0</sortorder> <drawtype>2</drawtype> <color>C80000</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_cpu_sys]</key> </item> </graph_item> <graph_item> <sortorder>1</sortorder> <drawtype>2</drawtype> <color>00C800</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_cpu_user]</key> </item> </graph_item> <graph_item> <sortorder>2</sortorder> <drawtype>2</drawtype> <color>0000C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_cpu_sys_children]</key> </item> </graph_item> <graph_item> <sortorder>3</sortorder> <drawtype>2</drawtype> <color>C800C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_cpu_user_children]</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Redis DbKeys</name> <width>900</width> <height>200</height> <yaxismin>0.0000</yaxismin> <yaxismax>100.0000</yaxismax> <show_work_period>1</show_work_period> <show_triggers>1</show_triggers> <type>0</type> <show_legend>1</show_legend> <show_3d>0</show_3d> <percent_left>0.0000</percent_left> <percent_right>0.0000</percent_right> <ymin_type_1>0</ymin_type_1> <ymax_type_1>0</ymax_type_1> <ymin_item_1>0</ymin_item_1> <ymax_item_1>0</ymax_item_1> <graph_items> <graph_item> <sortorder>0</sortorder> <drawtype>2</drawtype> <color>C80000</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[db0,avg_ttl]</key> </item> </graph_item> <graph_item> <sortorder>1</sortorder> <drawtype>2</drawtype> <color>00C800</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[db0,expires]</key> </item> </graph_item> <graph_item> <sortorder>2</sortorder> <drawtype>2</drawtype> <color>0000C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[db0,keys]</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Redis Memory</name> <width>900</width> <height>200</height> <yaxismin>0.0000</yaxismin> <yaxismax>100.0000</yaxismax> <show_work_period>1</show_work_period> <show_triggers>1</show_triggers> <type>0</type> <show_legend>1</show_legend> <show_3d>0</show_3d> <percent_left>0.0000</percent_left> <percent_right>0.0000</percent_right> <ymin_type_1>0</ymin_type_1> <ymax_type_1>0</ymax_type_1> <ymin_item_1>0</ymin_item_1> <ymax_item_1>0</ymax_item_1> <graph_items> <graph_item> <sortorder>0</sortorder> <drawtype>2</drawtype> <color>C80000</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_memory]</key> </item> </graph_item> <graph_item> <sortorder>1</sortorder> <drawtype>2</drawtype> <color>00C800</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_memory_lua]</key> </item> </graph_item> <graph_item> <sortorder>2</sortorder> <drawtype>2</drawtype> <color>0000C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_memory_peak]</key> </item> </graph_item> <graph_item> <sortorder>3</sortorder> <drawtype>2</drawtype> <color>C800C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[used_memory_rss]</key> </item> </graph_item> </graph_items> </graph> <graph> <name>Redis WriteStatus</name> <width>900</width> <height>200</height> <yaxismin>0.0000</yaxismin> <yaxismax>100.0000</yaxismax> <show_work_period>1</show_work_period> <show_triggers>1</show_triggers> <type>0</type> <show_legend>1</show_legend> <show_3d>0</show_3d> <percent_left>0.0000</percent_left> <percent_right>0.0000</percent_right> <ymin_type_1>0</ymin_type_1> <ymax_type_1>0</ymax_type_1> <ymin_item_1>0</ymin_item_1> <ymax_item_1>0</ymax_item_1> <graph_items> <graph_item> <sortorder>0</sortorder> <drawtype>2</drawtype> <color>C80000</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[aof_last_bgrewrite_status]</key> </item> </graph_item> <graph_item> <sortorder>1</sortorder> <drawtype>2</drawtype> <color>0000C8</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[rdb_last_bgsave_status]</key> </item> </graph_item> <graph_item> <sortorder>2</sortorder> <drawtype>2</drawtype> <color>00C800</color> <yaxisside>0</yaxisside> <calc_fnc>2</calc_fnc> <type>0</type> <item> <host>RedisMontior</host> <key>Redis.Info[aof_last_write_status]</key> </item> </graph_item> </graph_items> </graph> </graphs> </zabbix_export> 2、导入监控模板配置—模板—导入点击“选择文件”,找到redis-template.xml文件,将其导入 五、给主机添加监控模板 六、监控效果图 附:配置文件、脚本和模板下载地址http://down.51cto.com/data/2454656

优秀的个人博客,低调大师

vs2015通过ODBC连接SQLSERVER2012

CString strConn = "SERVER=192.168.0.100;DATABASE=db_name;UID=sa;PWD=sa_pwd;DRIVER={Sql Server Native Client 11.0};"; CDatabase db; try { cout << "开始连接数据库..."; db.SetLoginTimeout(30); db.OpenEx(g_strSqlConnStr, CDatabase::noOdbcDialog); //forceOdbcDialog db.SetQueryTimeout(60); cout << "连接数据库成功..."; } catch (CDBException *e) { CString strErrMsg; char buffer[1024]; e->GetErrorMessage(buffer, 1024); strErrMsg.Format("连接数据库失败,原因:%s", buffer); db.Close(); e->Delete(); return false; } //里面的SERVER要填写IP名称,不需要填写ODBC的配置名称

优秀的个人博客,低调大师

关于Python的几个过坑指南(MySQL-python、tornado)

最近碰到的Python项目中,需要安装MySQL-python依赖,并且项目使用tornado,以下是记录在其中碰到的坑。 0x01 tornado在render渲染html页面时报错 访问页面时服务器响应500,查看错误日志,提示: 'utf8' codec can't decode byte 0xbf in position 15:invalid start byte 疑惑了很久文件编码格式等,后来debug跟踪发现错误,是项目路径出现问题,最好不要在包含中文路径中: snipaste20180809_155405.png 0x02 MySQL-python 1.2.3安装 项目需要使用MySQL-python 1.2.3,然而通过pip安装会一直失败: snipaste20180809_155857.png 似乎是注册表中找不到某个值 可以通过以下步骤解决: 1、使用要安装的python环境运行此脚本:register.py #!/usr/bin/env python # -*- coding:utf-8 -*- import sys from _winreg import * # tweak as necessary version = sys.version[:3] installpath = sys.prefix regpath = "SOFTWARE\\Python\\Pythoncore\\%s\\" % (version) installkey = "InstallPath" pythonkey = "PythonPath" pythonpath = "%s;%s\\Lib\\;%s\\DLLs\\" % ( installpath, installpath, installpath ) def RegisterPy(): try: reg = OpenKey(HKEY_CURRENT_USER, regpath) except EnvironmentError as e: try: reg = CreateKey(HKEY_CURRENT_USER, regpath) SetValue(reg, installkey, REG_SZ, installpath) SetValue(reg, pythonkey, REG_SZ, pythonpath) CloseKey(reg) except: print "*** Unable to register!" return print "--- Python", version, "is now registered!" return if (QueryValue(reg, installkey) == installpath and QueryValue(reg, pythonkey) == pythonpath): CloseKey(reg) print "=== Python", version, "is already registered!" return CloseKey(reg) print "*** Unable to register!" print "*** You probably have another Python installation!" RegisterPy() snipaste20180809_160020.png 2、下载MySQL-python-1.2.3.win-amd64-py2.7.exe,直接安装: http://www.codegood.com/archives/129 snipaste20180809_160456.png 以上步骤必须先进行注册表注册,运行MySQL-python-1.2.3.win-amd64-py2.7.exe才不会报错找不到环境

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册