首页 文章 精选 留言 我的

精选列表

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

个人开发者都可享受 无服务器计算颠覆收费

无服务器计算(Serverless Computing)来了,设备商先别急着跑,这其实是软件开发人员和管理者的春天。所谓“无服务器”是一种抽象概念,开发人员编好代码上传到云,无需配置或管理服务器即可运行代码,按照所编好的被管理代码计算时间付费。如果说DevOps(开发运维一体化)是敏捷协作的代表,那么无服务器计算强调的则是NoOps。 什么是NoOps?当然不是完全放弃运维,而是要突出自动化运维,避免更多的人工干预,与无服务器计算的思路有着异曲同工之妙。既然无法摆脱“显性”的基础设施,干脆就把应用程序从原有的框架中取出来。在一定程度上,算是改变了软件的开发和部署模式,对于开发者来说最直观的影响就是付费。事实上,随着虚拟机转向容器和微服务,这也为无服务器计算概念的推广奠定了基础,带来了更加细分化的需求。 原有整体交付的方式可以被拆分为单独功能或代码实现,即一段完整的业务流不仅能够体现为一段视频或一幅图片,也可以是一行代码,这些代码形成的片段均可实现完整功能。借助无服务器计算,系统能够自行编排代码片段。应用程序会直接在服务器上运行,几乎所有的管理工作交由服务商来负责,使用者无需进行预置、扩展、维护等操作,即可运行数据库、存储或软件程序。 无论是虚拟机还是容器,都可以看作是通过代码实现的方案,本质上与无服务器计算并不冲突。操作过程中,开发人员无需考虑选择什么容器,只要安心编写代码,由服务商将片段加以整合管理,节省了软件创建时的付出,让双方做各自擅长的事情,效率更高。在弹性基础架构中,开发者能够将应用“切成”小块通过高度扩展的方式部署。从某种程度上来说,无服务器计算为资源使用提供了新模式,其定位更像是介于IaaS和PaaS之间。 计费方面,开发者不用像原来那样为了数分钟或数小时的应用实例支付整套费用,而是只需支付某一段函数运行的几毫秒,这种方式是更精准的按需付费。按照这个逻辑,无服务器计算平台在云端似乎更适用于由众多微服务“拼接”而成的应用。无论是基础设施管理还是应用构建,均节省了不少“无用功”。 目前,涉足无服务器计算的不仅包括亚马逊(AWS Lambda)、微软(Azure Functions)、谷歌(Cloud Functions)、IBM(OpenWhisk)这样的巨头公司,还有Iron.io这样的垂直公司。这些服务各有特色,例如OpenWhisk主打开源,Azure Functions结合了微软IoT、SaaS方案,Iron.io则是希望构建主流公有云和私有云的全平台兼容,确保高度可移植性。 尽管有大厂商撑腰,无服务器计算也并非尽善尽美,至少在操作难易度、运行时间效率、开发语言支持等方面仍需完善。同时,由于开发者的代码控制权有所削弱,随之而来的安全性问题也必须引起重视。对于企业客户来说,微服务、容器、无服务器计算只是丰富了更多选项,至于如何在实践中提升效率,还要看开发者对架构和应用的评估,毕竟Serverless Computing不是万能药。 本文转自d1net(转载)

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

Objective-C中的Runtime各大帖子,个人感觉最后的最好

runtime的资料网上有很多了,部分有些晦涩难懂,我通过自己的学习方法总结一遍,主要讲一 些常用的方法功能,以实用为主,我觉得用到印象才是最深刻的。另外runtime的知识还有很多什么是runtime?runtime 是 OC底层的一套C语言的API(引入 <objc/runtime.h> 或<objc/message.h>),编译器最终都会将OC代码转化为运行时代码,通过终端命令编译.m 文件:clang -rewrite-objc xxx.m可以看到编译后的xxx.cpp(C++文件)。比如我们创建了一个对象 [[NSObject alloc]init],最终被转换为几万行代码。开这个帖子记录一下看到的一些比较好的runtime的文章。1、详解Runtime运行时机制http://www.code4app.com/blog-721976-204.html(亮点:本文详细整理了 Cocoa 的 Runtime 系统的知识,它使得 Objective-C 如虎添翼,具备了灵活的动态特性,使这门古老的语言焕发生机)2、OC实用的runtime总结http://www.code4app.com/home.php?mod=space&uid=721976&do=blog&quickforward=1&id=205 (亮点:通过自己的学习方法总结一遍,主要讲一些常用的方法功能,以实用为主)3、DEMO 按钮点击,利用runtime hook 实现iOS防止按钮连续响应点击http://www.code4app.com/forum.php?mod=viewthread&tid=7271&extra=page%3D1%26filter%3Dsortid%26sortid%3D1(利用runtime hook 实现iOS防止按钮连续响应点击 可以设置时间间隔,拖进工程立即生效)4、DEMOruntime 详解http://www.code4app.com/forum.php?mod=viewthread&tid=8241&extra=page%3D1%26filter%3Dsortid%26sortid%3D15、DEMOiOS Runtime 实践http://www.code4app.com/forum.php?mod=viewthread&tid=7638&extra=page%3D1%26filter%3Dsortid%26sortid%3D1(亮点:通过DEMO方式实践iOS中的黑魔法runtime)6、学习 runtime的简单项目http://www.code4app.com/forum.php?mod=viewthread&tid=7201&extra=page%3D1%26filter%3Dsortid%26sortid%3D17、根据规则跳转到指定的界面(runtime实用篇一)http://www.code4app.com/forum.php?mod=viewthread&tid=10131&extra=page%3D1%26filter%3Dsortid%26sortid%3D1 8、Objective-C 中的runtimehttp://www.code4app.com/home.php?mod=space&uid=800778&do=blog&quickforward=1&id=282 Objective-C中的Runtime http://www.jianshu.com/p/3e050ec3b759 Objective-C Runtime http://yulingtianxia.com/blog/2014/11/05/objective-c-runtime/

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

精选博客系列|面向公共安全的SD-WAN Edge:刷新VMware边缘计算栈

在巴塞罗那举行的2023世界移动通信大会上,VMware展台展示了配备小型加固SD-WAN设备、搭配用于自动车牌识别等应用的Jenoptik软件的特斯拉汽车。VMware SD-WAN能够在车队中创建移动办公室,实现安全的移动通信和实时边缘计算。 萨里和苏塞克斯警方使用VMware SD-WAN实现现代化 这个演示背后的推手是一个非常有创新精神的VMware客户:英国萨里郡和苏塞克斯郡的联合警察部队。萨里和苏塞克斯警方在英国人口最稠密的两个县为超过200万人提供服务,拥有8千多名警官和工作人员。这些团队需要一个面向未来的解决方案来实施他们的云优先战略,以降低运维成本并专注于服务优先的方案。萨里和苏塞克斯警方希望确保他们的运维人员拥有向公众提供服务的工具。 萨里和苏塞克斯警方成为英国第一支低于预算且提前部署SD-WAN的警察部队,并将其持续运维成本降低了50%。他们用VMware SD-WAN取代了传统的点对点网络和硬件。这使服务更加高效、增加了可靠性、降低了成本,并拥有一个单一面板来管理与云服务提供商的网络。其他好处包括减少碳足迹和噪音污染。 “我有这个想法……” 一旦萨里和苏塞克斯警方看到软件定义连接的好处,他们就更进一步。一天,联合警察部队的技术项目经理Steve Conn博士正在喝茶,突然灵机一动。“我们为什么不将SD-WAN Edge设备放在警车上,将它连接到我们的随身摄像头,并在高速行驶时流式传输实时视频?” 他们连接了VMware SD-WAN 510 LTE设备、电源逆变器和SIM卡,实际上已经从高速行驶的警车上的随身摄像头传输视频。但这仅仅是个开始。 小型化设备中的SD-WAN连接具有从演示阶段过渡到政府机构可以为处理突发事件人员购买现成产品的巨大潜力。“我们希望能够减少硬件和软件占用空间,使设备适合任何交通工具,从警车到救护车和其他紧急公共服务,将车辆变成车轮上的加固办公室,”Conn 博士说。“我的愿景是拥有一辆虚拟化的汽车,以创建基于软件的虚拟版本的功能,包括计算、存储、网络、服务器或应用程序,这些功能的行为就像它们的物理功能一样——但在一个边缘设备中——创造这个车轮上的办公室。” 移动、加固技术的潜力无限 现代警车配备了各种技术,包括摄像头、收音机、自动车牌阅读器以及笔记本电脑或平板电脑。虽然这些技术有助于提高安全性、灵活性和感知,但“我们一直在将更多设备塞进更小的空间,”康恩博士说,“例如,这些设备消耗的功率太大,以至于车辆需要更大的电池。” 额外的重量不仅占用空间,还会增加油耗、加快轮胎磨损、并导致车辆整体磨损。 即使拥有所有这些设备,警察部队在现场可以执行的行动任务种类仍然有限。他们仍然需要返回办公室上传随身摄像头拍摄的视频。当巡逻区域没有良好的蜂窝网络覆盖时,警官可能不得不开车四处寻找连接信号。当官员面临紧急情况时,他们必须依靠向总部的语音呼叫来描述问题并请求资源或支持,而实时视频流可以传达更多信息。 Conn博士说:“我们的目标是提供统一通信和虚拟化服务,包括应用程序、语音、视频和数据,使官员能够执行他们需要全天完成的任何行动任务。”通过SD-WAN实现更好的连接,任何急救车辆都可以成为指挥控制车辆。例如,当警察巡逻车可以发送紧急情况或人群的实时视频时,总部可以将该视频放到大屏幕上以获取更多详细信息,并更好地了解现场需要哪些额外资源。带有Wi-Fi热点的安全移动SD-WAN Edge使警察能够从现场访问安全系统,完成必要的职责,并从他们的车辆共享信息,而无需返回办公室。 Conn博士说:“我们处于SD-WAN的前沿,很高兴看到接下来,尤其是VMware SD-WAN Client会发生什么。当您将该客户端软件装在笔记本电脑上时,它确实会增强对SD-WAN的整体投资。” Conn博士表示,将移动优势作为“车轮上的办公室”将改变我们开展警务工作的方式。“我期待继续与 VMware合作,以实现造福紧急公共服务及其客户的解决方案的愿景。” "在VMware Explore 2022大会上,VMware推出了新增强的、专为边缘原生应用及其独特的性能、自主性和延迟要求而构建的边缘解决方案。VMware边缘计算栈是一个高度复杂的产品组合,使组织能够在近边缘和远边缘位置跨多个云构建、运行、管理、连接和保护边缘原生应用程序。VMware边缘计算栈提供与您业务需求相对应的不同大小的方案:支持基于利用多云服务的虚拟机、容器或 Web 汇编构建的边缘原生应用程序;运行通用或实时操作系统,使IT和OT能够提供智能实时解决方案;在所有类型的有线和无线网络中安全部署。" 内容来源|公众号:VMware 中国研发中心

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

star 4k+ 的 JPress 博客系统发布新版,修复 Log4j 漏洞问题

JPress 是一个使用 Java 开发的类似 WordPress 的产品,具有完善的模板和插件功能,并在此基础上新增了在线商城、会员中心以及和微信深度整合的功能。 到目前为止, 已经有10w+ 网站使用JPress 进行驱动,其中包括多个政府机构,200+上市公司,中科院、红+字会等。然而,JPress不仅仅只是建站,我们认为不管世界的互联网发生什么样的变化,APP、小程序都需要有网站支持,这才是我们的切入点。 JPress v4.2.0 发布本来可以晚点发布的,但 Log4j 漏洞的爆出,为了保证用户能够及时升级,因此决定在第一时间发布了修复版本。 另外:JPress 正在参加2021年度OSC中国开源项目评选~ JPress 在 2022 也将会有大动作,来更好的服务大家,麻烦大家给 JPress 投一票。 JPress 投票地址: https://www.oschina.net/project/top_cn_2021/?id=12 JPress v4.2.0 更新内容如下: 新增:添加后台验证码开关,方便在某些场景下进行自动化测试 新增:支持发布0元的商品,支付金额为 0 时,直接支付成功,感谢@AlienJunX 新增:插件安装目录自定义的功能,方便安装插件后,在编译清空 target 目录时,插件仍然可用 优化:完善文章模块添加收藏功能,感谢@吓死猫的老鼠 优化:Option 系统配置允许传入空数据,用于清空配置 优化:调整上传默认附件大小,图片默认为 10MB,其他文件默认为 100MB 优化:升级 Jboot、Log4j2 等到最新版本 修复:后台自定义非法关键字无效的问题 修复:通过 API 删除 Option 可能不及时生效的问题 修复:插件依赖外部 jar ,在某些情况下可能会导致无法被安装的问题 修复:微信 H5 支付成功返回页面后出现 500 错误,感谢@AlienJunX 修复:文章插入附件,当出现附件文件过大错误时,页面没有提示的问题 修复:CKEditor 编辑器 图片无法粘贴上传的问题 说的再多,不如亲自一试。 在 阿里云(腾讯云)上一键通过 8080 端口运行 wget https://gitee.com/JPressProjects/jpress/raw/master/install.sh && bash install.sh 8080 一键运行视频教程:http://www.ketang8.com/course/study?chapterId=184 通过 Docker 上运行 curl -O https://gitee.com/JPressProjects/jpress/raw/master/docker-compose.yml && docker-compose up -d 通过 Eclipse 或者 Idea 等开发工具运行 1、在本地安装好 Java、Maven 等开发环境 2、将源码下载、并导入 eclipse 或者 idea 3、在项目的根目录,执行mvn clean install命令进行编译 4、在开发工具,右键运行starter/src/main/java/io.jpress.Starter下的main()方法 5、通过浏览器访问http://127.0.0.1:8080,进行自动安装 交流 官网:http://www.jpress.io 文档:http://doc.jpress.io 论坛社区:点击这里 插件列表:点击这里

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

[博客大赛]图文并茂!!讲解JUC重量级锁、轻量级锁、自旋、锁膨胀....

在开始介绍今天的内容之前,我们先来认识java中的对象,对象想必大家都已经很熟悉,但是大家有想过java对象在内存中"长"什么样吗??它是怎么组成的吗?? 前言 java对象内存布局 java对象在内存中分为3部分,分别是对象头(Oject Header),实例数据(Instance Data),补齐填充(Padding)(1)对象头(Object Header) 注意:(1)这里暂不考虑指针压缩的场景(2)这里都是针对HotSpot虚拟机进行介绍 MarkWord :用来存放一些标志位(比如锁的标记位)和hashcode,分代年龄等,在32位虚拟机下,MarkWord占4个字节,在64位虚拟机下,占8个字节 Klass Pointer(或者Class Pointer) :用来存放该对象所对应的class类对象在方法区中的地址,在32位虚拟机下占4字节,在64位虚拟机下占8字节; Length :如果对象是数组对象,那么对象头中还有一个Length字段,用来记录数组的长度,占4个字节; (这里以HotSpot虚拟机为32位的情况举例) 普通对象的对象头:(包括MarkWord和klass word两部分) 数组对象的对象头:(包括MarkWord和klass word和数组长度三部分) JVM虚拟机可以通过Java对象的元数据信息确定Java对象的大小,但是无法从数组的元数据来确认数组的大小,所以用一块来记录数组长度。 MarkWord的状态变化(32位Hotspot虚拟机下) 在正常状态下(也即是无锁状态下):32位虚拟机中的MarkWord包含了25位的hashcode,4位的分代年龄,1位偏向锁标志位,2位锁标志位; MarkWord的状态变化(64位Hotspot虚拟机下) (2)实例数据(Instance Data) 实例数据部分是对象真正存储的有效信息,也就是我们在代码里面所定义的各种类型的字段内容(成员变量),无论是从父类继承下来的,还是在子类中定义的都需要记录下来; (3)对齐填充(Padding) 第三部分对齐填充并不是必然存在的,也没有特别的含义,它仅仅起着占位符的作用。由于HotSpot VM的自动内存管理系统要求对象起始地址必须是8字节的整数倍,换句话说就是对象的大小必须是8字节的整数倍。对象头正好是8字节的倍数(1倍或者2倍),因此当对象实例数据部分没有对齐的话,就需要通过对齐填充来补全。 在认识了对象头之后,我们接下来介绍JAVA中的几种锁以及优化,在此之前,我们有必要知道以下几点: 三者 优先级:(上锁消耗的资源越少,优先级越高) (1)偏向锁 > 轻量级锁 > 重量级锁 (这3个锁都是用synchronized关键字来上锁) (2)轻量级锁的使用场景:一个对象有多个线程需要加锁,但是加锁的时间是错开的(即没有竞争),此时可以使用轻量级锁来优化; (3)偏向锁的使用场景:轻量级锁在没有竞争,每次重入仍然需要CAS操作,Java 6 之后引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有 1 .在Java SE 1.6中为了减少获得锁和释放锁带来的性能消耗,引入了"偏向锁"和"轻量级锁",在Java SE 1.6中,锁一共有4中状态,级别从低到高依次是:无锁状态,偏向锁状态,轻量级锁状态,重量级锁状态,这几个状态会随着竞争情况逐渐升级;锁可以升级但是不能降级,意味着偏向锁升级为轻量级锁后不能降级成偏向锁.这种锁升级确不能降级的策略,目的是为了提高获得锁和释放锁的效率; 2.所谓的重量级锁和轻量级锁,是从性能消耗方面来命名的;轻量级锁是JDK1.6引入的,"轻量级"是相对于使用操作系统互斥量来实现的传统锁(重量级锁)而言的;重量级锁由于加锁和解锁性能消耗大,并且如果发生锁竞争,会发生线程的阻塞和唤醒,这个操作是借助操作系统的系统调用,然而系统调用会涉及到内核态和用户态的切换,因此正是由于重量级锁的性能消耗大,我们称作"重",而轻量级锁相比较而言性能消耗较少,我们称作"轻";轻量级锁并不是用来替代重量级锁的,它设计的初衷是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗 3.轻量级锁不会发生自旋,重量级锁才会有自旋点击链接查看源码分析 (1)重量级锁(Moniter) 每个 Java 对象都可以关联一个 Monitor 对象,如果使用 synchronized 给对象上锁(重量级)之后,该对象头的Mark Word 中前62位就会被设置为 Monitor 对象的地址,后两位表示此时对象的状态,对象头中的信息暂时保存在Moniter中,等到解锁时才取出;图解: 刚开始 Monitor 中 Owner 为 null 当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2,Monitor中只能有一个 Owner 在 Thread-2 上锁的过程中,如果 Thread-3,Thread-4,Thread-5 也来执行 synchronized(obj),就会进入EntryList进而BLOCKED Thread-2 执行完同步代码块的内容,然后唤醒 EntryList 中等待的线程来竞争锁,竞争时是公平的 图中 WaitSet 中的 Thread-0,Thread-1 是之前获得过锁,但条件不满足进入 WAITING 状态的线程,后面讲wait-notify 时会分析 注意:synchronized 必须是进入同一个对象的 monitor 才有上述的效果不加 synchronized 的对象不会关联监视器,不遵从以上规则 (2)轻量级锁 线程在执行同步代码块之前,JVM会在当前线程的中栈帧中创建用于存储锁记录(Lock Record)的空间,并将对象轻量级锁对使用者是透明的,即语法仍然是 synchronized (1)创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word (2)让锁记录中 Object reference 指向锁对象,并尝试用 CAS(原子操作) 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录 (3)如果 cas 替换成功,对象头中存储了锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下 (4)如果 cas 失败,有两种情况1 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程2 如果是自己执行了 synchronized 锁重入,那么再添加一条 Lock Record 作为重入的计数 解释:所谓锁重入,就是给一个对象上锁多次,每一次重入都要进行CAS操作,当发现对象头中已经有锁记录的地址了,则添加一条Lock Record 作为重入的计数 假设有两个方法同步块,利用同一个对象加锁 static final Object obj = new Object(); public static void method1() { synchronized( obj ) { // 同步块 A method2(); } } public static void method2() { synchronized( obj ) { // 同步块 B } } (5)当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录,表示重入计数减一当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头;如果操作成功,则解锁成功如果操作失败,说明轻量级锁进行了锁膨胀或已经升级为了重量级锁,那么此时进入重量级锁解锁流程 锁膨胀、自旋、自适应自旋 锁膨胀 如果在尝试加轻量级锁的过程中,CAS 操作无法成功,这时一种情况就是有其它线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁。 自旋 自旋是让CPU执行一些没有意义的指令,目的是让线程不放弃对CPU的占用 重量级锁竞争的时候,还可以使用自旋来进行优化; 正常情况下锁获取失败就应该阻塞入队,但是有时候可能刚一阻塞,别的线程就释放锁了,然后再唤醒刚刚阻塞的线程,这就没必要了。所以在线程竞争不是很激烈的时候,稍微自旋一会儿,指不定不需要阻塞线程就能直接获取锁,这样就避免了不必要的开销,提高了锁的性能。如果当前线程自旋成功(即这时候持锁线程已经退出了同步块,释放了锁),这时当前线程就可以避免阻塞。 自旋重试成功的情况自旋重试失败的情况注意:自旋会占用 CPU 时间,单核 CPU 自旋就是浪费,多核 CPU 自旋才能发挥优势。 自适应自旋 但是自旋的次数又是一个难点,在竞争很激烈的情况,自旋就是在浪费 CPU,因为结果肯定是自旋一会让之后阻塞。 在 Java 6 之后自旋锁是自适应的,比如对象刚刚的一次自旋操作成功过,那么认为这次自旋成功的可能性会高,就多自旋几次;反之,就少自旋甚至不自旋,总之,比较智能。Java 7 之后不能控制是否开启自旋功能 (3)偏向锁(以64位虚拟机为例) 轻量级锁在没有竞争时(就自己这个线程),每次重入仍然需要执行 CAS 操作。Java 6 中引入了偏向锁来做进一步优化:只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头,之后发现这个线程 ID 是自己的就表示没有竞争,不用重新 CAS。以后只要不发生竞争,这个对象就归该线程所有 几点说明: 在JDK1.6中.偏向锁是默认开启的,我们可以通过在VMoptions中设置参数(-XX:-UseBiasedLocking)来关闭偏向锁; 偏向锁默认是延迟的,不会在程序启动时立即生效,如果想避免延迟,可以在VMoptions中设置参数:-XX:BiasedLockingStartupDelay=0来禁用延迟 如果我们禁用了偏向锁,那么给对象上锁时,会加轻量级锁 我们通过以下实验来测试一下相关说明: 这里利用 jol 第三方工具来查看对象头信息(注意这里扩展了 jol 让它输出更为简洁)不设置延迟为0如果不设置延迟为0,那么新建的对象是处于正常状态,因为此时偏向锁还未生效,后3位为001,如果使当前线程睡眠4s,再新建一个对象,会发现此时偏向锁已经生效,后3位为101设置延迟为0禁用延迟后,偏向锁在程序启动时就生效,因此新建的两个对象都是处于可偏向状态但我们发现,以上除了后3位有值外,其余的61位都是0,那是因为此时的对象处于可偏向状态,我们还没有给对象上锁,所以这时它的 thread、epoch、age 都为 0 现在给对象上锁:// 添加虚拟机参数 -XX:BiasedLockingStartupDelay=0 class Dog { } public static void main(String[] args) throws IOException { Dog d = new Dog(); ClassLayout classLayout = ClassLayout.parseInstance(d); new Thread(() -> { log.debug("synchronized 前"); System.out.println(classLayout.toPrintableSimple(true)); synchronized (d) { log.debug("synchronized 中"); System.out.println(classLayout.toPrintableSimple(true)); } log.debug("synchronized 后"); System.out.println(classLayout.toPrintableSimple(true)); }, "t1").start(); } 输出: 11:08:58.117 c.TestBiased [t1] - synchronized 前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101 11:08:58.121 c.TestBiased [t1] - synchronized 中 00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 11:08:58.121 c.TestBiased [t1] - synchronized 后 00000000 00000000 00000000 00000000 00011111 11101011 11010000 00000101 注意:这里我们发现解锁后,MarkWord没有变化,这里就体现的是偏向锁的"偏"的特性:这个被上锁的对象,会偏向于第一次给他上锁的线程,即使退出了同步代码块,该对象的MarkWord中仍然会存储该线程ID(注意:这里的线程ID是JVM中的ID,不同与操作系统中线程ID),直到有其他线程使用该对象或者其他条件发生时才会改变 测试禁用偏向锁:在上面测试代码运行时在添加 VM 参数 -XX:-UseBiasedLocking 禁用偏向锁 11:13:10.018 c.TestBiased [t1] - synchronized 前 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 11:13:10.021 c.TestBiased [t1] - synchronized 中 00000000 00000000 00000000 00000000 00100000 00010100 11110011 10001000 11:13:10.021 c.TestBiased [t1] - synchronized 后 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000001 会发现此时进入Synchronized代码块,打印的后3位为000,说明此时给对象加的是轻量级锁 所以:如果我们禁用了偏向锁,那么给对象上锁时,会加轻量级锁 2.偏向锁撤销的3种情况 (1)当一个对象调用hashcode()方法时:会禁用偏向锁??? 通过图可以发现确实禁用了偏向锁,因为在加锁之前,Markword的后3位是001,代表正常状态,并且填充了31位的哈希码 那这是为什么呢??为什么调用了对象的hashcode方法后,偏向锁就失效了呢??? 我们知道在对象头的MarkWord中有一个字段是用来存放线程ID的;当对象进入偏向状态的时候,MarkWord的中的54bit会拿来存放持有锁的线程ID,那原来对象的哈希码怎么办呢??? 如果是轻量级锁会在锁记录中记录 hashCode如果是重量级锁会在 Monitor 中记录 hashCode如果是偏向锁会在当前对象的Markword中记录hashcode 对象新建时哈希码默认是0;当对象第一次调用hashcode()方法时才会产生哈希码,并填充到MarkWord中;此时64位的MarkWord已经被占用了31位了,如果此时要开启偏向锁,那么就还得在MarkWord拿54位来存储线程ID号,显然空间不够,因此JVM规定了如果一个可偏向对象调用了hashcode方法,会使偏向锁失效; 我们这里是可偏向对象调用hashcode方法,那如果是已经处于偏向状态的对象,又收到需要计算其一致性哈希码请求时^1。它的偏向状态会立即被撤销,并且锁会膨胀为重量级锁;在重量级锁的实现中,代表重量级锁的Moniter对象会存放非加锁状态下(标志位为"01")的MarkWord,其中自然可以存放原来的hashcode (2) 当有其他线程使用偏向锁对象时,会将偏向锁升级为轻量级锁 当一个对象加锁时,默认是加的偏向锁,当解锁时(syncronized中代码执行完),此时线程的ID号仍然会留在对象头的Markword中,因此当另一个线程再给这个对象加锁时,偏向锁就会失效,并升级为轻量级锁 (3)当使用wait(),notify()方法时,偏向锁也会撤销 wait()和notify()是在重量级锁中使用的方法,因此如果使用了这两个方法,表明偏向锁已经撤销 3.批量重偏向和批量撤销 JVM 基于一种启发式的做法判断是否应该触发批量撤销或批量重偏向 依赖三个阈值作出判断: # 批量重偏向阈值 -XX:BiasedLockingBulkRebiasThreshold=20 # 重置计数的延迟时间 -XX:BiasedLockingDecayTime=25000 # 批量撤销阈值 -XX:BiasedLockingBulkRevokeThreshold=40 简单总结,对于一个类,按默认参数来说:单个偏向撤销的计数达到 20,就会进行批量重偏向。距上次批量重偏向 25 秒内,计数达到 40,就会发生批量撤销。每隔 (>=) 25 秒,会重置在 [20, 40) 内的计数为0,这意味着可以发生多次批量重偏向。 注意:对于一个类来说,批量撤销只能发生一次,因为批量撤销后,该类禁用了可偏向属性,后面该类的对象都是不可偏向的,包括新创建的对象。 启发式源码: static HeuristicsResult update_heuristics(oop o, bool allow_rebias) { markOop mark = o->mark(); // 如果不是偏向模式直接返回 if (!mark->has_bias_pattern()) { return HR_NOT_BIASED; } // 获取锁对象的类元数据 Klass* k = o->klass(); // 当前时间 jlong cur_time = os::javaTimeMillis(); // 该类上一次批量重偏向的时间 jlong last_bulk_revocation_time = k->last_biased_lock_bulk_revocation_time(); // 该类单个偏向撤销的计数 int revocation_count = k->biased_lock_revocation_count(); // 按默认参数来说: // 如果撤销计数大于等于 20,且小于 40, // 且距上次批量撤销的时间大于等于 25 秒,就会重置计数。 if ((revocation_count >= BiasedLockingBulkRebiasThreshold) && (revocation_count < BiasedLockingBulkRevokeThreshold) && (last_bulk_revocation_time != 0) && (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) { // This is the first revocation we've seen in a while of an // object of this type since the last time we performed a bulk // rebiasing operation. The application is allocating objects in // bulk which are biased toward a thread and then handing them // off to another thread. We can cope with this allocation // pattern via the bulk rebiasing mechanism so we reset the // klass's revocation count rather than allow it to increase // monotonically. If we see the need to perform another bulk // rebias operation later, we will, and if subsequently we see // many more revocation operations in a short period of time we // will completely disable biasing for this type. k->set_biased_lock_revocation_count(0); revocation_count = 0; } if (revocation_count <= BiasedLockingBulkRevokeThreshold) { // 自增计数 revocation_count = k->atomic_incr_biased_lock_revocation_count(); } // 此时,如果达到批量撤销阈值,则进行批量撤销。 if (revocation_count == BiasedLockingBulkRevokeThreshold) { return HR_BULK_REVOKE; } // 此时,如果达到批量重偏向阈值,则进行批量重偏向。 if (revocation_count == BiasedLockingBulkRebiasThreshold) { return HR_BULK_REBIAS; } // 否则,仅进行单个对象的撤销偏向 return HR_SINGLE_REVOKE; }

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

【博客大赛】超硬核!11 个非常实用的 Python 和 Shell 拿来就用脚本实例!

大家好,我是JackTian。 在上一篇分享的原创文章《7 个非常实用的 Shell 拿来就用脚本实例!》中,从这篇文章的阅读、点赞、在看、留言的数据来看,非常受读者欢迎。不得不说,脚本在我们的日常工作中可以提高很大的工作效率,的确很香! 这次再来给大家分享一波我工作中用到的几个脚本,主要分为:Python和Shell两个部分。 Python 脚本部分实例: 企业微信告警、FTP 客户端、SSH 客户端、Saltstack 客户端、vCenter 客户端、获取域名 ssl 证书过期时间、发送今天的天气预报以及未来的天气趋势图; Shell 脚本部分实例: SVN 完整备份、Zabbix 监控用户密码过期、构建本地 YUM 以及上篇文章中有读者的需求(负载高时,查出占用比较高的进程脚本并存储或推送通知); 篇幅有些长,还请大家耐心翻到文末,毕竟有彩蛋。 Python 脚本部分 企业微信告警 此脚本通过企业微信应用,进行微信告警,可用于 Zabbix 监控。 # -*- coding: utf-8 -*- import requests import json class DLF: def __init__(self, corpid, corpsecret): self.url = "https://qyapi.weixin.qq.com/cgi-bin" self.corpid = corpid self.corpsecret = corpsecret self._token = self._get_token() def _get_token(self): ''' 获取企业微信API接口的access_token :return: ''' token_url = self.url + "/gettoken?corpid=%s&corpsecret=%s" %(self.corpid, self.corpsecret) try: res = requests.get(token_url).json() token = res['access_token'] return token except Exception as e: return str(e) def _get_media_id(self, file_obj): get_media_url = self.url + "/media/upload?access_token={}&type=file".format(self._token) data = {"media": file_obj} try: res = requests.post(url=get_media_url, files=data) media_id = res.json()['media_id'] return media_id except Exception as e: return str(e) def send_text(self, agentid, content, touser=None, toparty=None): send_msg_url = self.url + "/message/send?access_token=%s" % (self._token) send_data = { "touser": touser, "toparty": toparty, "msgtype": "text", "agentid": agentid, "text": { "content": content } } try: res = requests.post(send_msg_url, data=json.dumps(send_data)) except Exception as e: return str(e) def send_image(self, agentid, file_obj, touser=None, toparty=None): media_id = self._get_media_id(file_obj) send_msg_url = self.url + "/message/send?access_token=%s" % (self._token) send_data = { "touser": touser, "toparty": toparty, "msgtype": "image", "agentid": agentid, "image": { "media_id": media_id } } try: res = requests.post(send_msg_url, data=json.dumps(send_data)) except Exception as e: return str(e) FTP 客户端 通过 ftplib 模块操作 ftp 服务器,进行上传下载等操作。 # -*- coding: utf-8 -*- from ftplib import FTP from os import path import copy class FTPClient: def __init__(self, host, user, passwd, port=21): self.host = host self.user = user self.passwd = passwd self.port = port self.res = {'status': True, 'msg': None} self._ftp = None self._login() def _login(self): ''' 登录FTP服务器 :return: 连接或登录出现异常时返回错误信息 ''' try: self._ftp = FTP() self._ftp.connect(self.host, self.port, timeout=30) self._ftp.login(self.user, self.passwd) except Exception as e: return e def upload(self, localpath, remotepath=None): ''' 上传ftp文件 :param localpath: local file path :param remotepath: remote file path :return: ''' if not localpath: return 'Please select a local file. ' # 读取本地文件 # fp = open(localpath, 'rb') # 如果未传递远程文件路径,则上传到当前目录,文件名称同本地文件 if not remotepath: remotepath = path.basename(localpath) # 上传文件 self._ftp.storbinary('STOR ' + remotepath, localpath) # fp.close() def download(self, remotepath, localpath=None): ''' localpath :param localpath: local file path :param remotepath: remote file path :return: ''' if not remotepath: return 'Please select a remote file. ' # 如果未传递本地文件路径,则下载到当前目录,文件名称同远程文件 if not localpath: localpath = path.basename(remotepath) # 如果localpath是目录的话就和remotepath的basename拼接 if path.isdir(localpath): localpath = path.join(localpath, path.basename(remotepath)) # 写入本地文件 fp = open(localpath, 'wb') # 下载文件 self._ftp.retrbinary('RETR ' + remotepath, fp.write) fp.close() def nlst(self, dir='/'): ''' 查看目录下的内容 :return: 以列表形式返回目录下的所有内容 ''' files_list = self._ftp.nlst(dir) return files_list def rmd(self, dir=None): ''' 删除目录 :param dir: 目录名称 :return: 执行结果 ''' if not dir: return 'Please input dirname' res = copy.deepcopy(self.res) try: del_d = self._ftp.rmd(dir) res['msg'] = del_d except Exception as e: res['status'] = False res['msg'] = str(e) return res def mkd(self, dir=None): ''' 创建目录 :param dir: 目录名称 :return: 执行结果 ''' if not dir: return 'Please input dirname' res = copy.deepcopy(self.res) try: mkd_d = self._ftp.mkd(dir) res['msg'] = mkd_d except Exception as e: res['status'] = False res['msg'] = str(e) return res def del_file(self, filename=None): ''' 删除文件 :param filename: 文件名称 :return: 执行结果 ''' if not filename: return 'Please input filename' res = copy.deepcopy(self.res) try: del_f = self._ftp.delete(filename) res['msg'] = del_f except Exception as e: res['status'] = False res['msg'] = str(e) return res def get_file_size(self, filenames=[]): ''' 获取文件大小,单位是字节 判断文件类型 :param filename: 文件名称 :return: 执行结果 ''' if not filenames: return {'msg': 'This is an empty directory'} res_l = [] for file in filenames: res_d = {} # 如果是目录或者文件不存在就会报错 try: size = self._ftp.size(file) type = 'f' except: # 如果是路径的话size显示 - , file末尾加/ (/dir/) size = '-' type = 'd' file = file + '/' res_d['filename'] = file res_d['size'] = size res_d['type'] = type res_l.append(res_d) return res_l def rename(self, old_name=None, new_name=None): ''' 重命名 :param old_name: 旧的文件或者目录名称 :param new_name: 新的文件或者目录名称 :return: 执行结果 ''' if not old_name or not new_name: return 'Please input old_name and new_name' res = copy.deepcopy(self.res) try: rename_f = self._ftp.rename(old_name, new_name) res['msg'] = rename_f except Exception as e: res['status'] = False res['msg'] = str(e) return res def close(self): ''' 退出ftp连接 :return: ''' try: # 向服务器发送quit命令 self._ftp.quit() except Exception: return 'No response from server' finally: # 客户端单方面关闭连接 self._ftp.close() SSH 客户端 此脚本仅用于通过 key 连接,如需要密码连接,简单修改下即可。 # -*- coding: utf-8 -*- import paramiko class SSHClient: def __init__(self, host, port, user, pkey): self.ssh_host = host self.ssh_port = port self.ssh_user = user self.private_key = paramiko.RSAKey.from_private_key_file(pkey) self.ssh = None self._connect() def _connect(self): self.ssh = paramiko.SSHClient() self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) try: self.ssh.connect(hostname=self.ssh_host, port=self.ssh_port, username=self.ssh_user, pkey=self.private_key, timeout=10) except: return 'ssh connect fail' def execute_command(self, command): stdin, stdout, stderr = self.ssh.exec_command(command) out = stdout.read() err = stderr.read() return out, err def close(self): self.ssh.close() Saltstack 客户端 通过 api 对 Saltstack 服务端进行操作,执行命令。 #!/usr/bin/env python # -*- coding:utf-8 -*- import requests import json import copy class SaltApi: """ 定义salt api接口的类 初始化获得token """ def __init__(self): self.url = "http://172.85.10.21:8000/" self.username = "saltapi" self.password = "saltapi" self.headers = {"Content-type": "application/json"} self.params = {'client': 'local', 'fun': None, 'tgt': None, 'arg': None} self.login_url = self.url + "login" self.login_params = {'username': self.username, 'password': self.password, 'eauth': 'pam'} self.token = self.get_data(self.login_url, self.login_params)['token'] self.headers['X-Auth-Token'] = self.token def get_data(self, url, params): ''' 请求url获取数据 :param url: 请求的url地址 :param params: 传递给url的参数 :return: 请求的结果 ''' send_data = json.dumps(params) request = requests.post(url, data=send_data, headers=self.headers) response = request.json() result = dict(response) return result['return'][0] def get_auth_keys(self): ''' 获取所有已经认证的key :return: ''' data = copy.deepcopy(self.params) data['client'] = 'wheel' data['fun'] = 'key.list_all' result = self.get_data(self.url, data) try: return result['data']['return']['minions'] except Exception as e: return str(e) def get_grains(self, tgt, arg='id'): """ 获取系统基础信息 :tgt: 目标主机 :return: """ data = copy.deepcopy(self.params) if tgt: data['tgt'] = tgt else: data['tgt'] = '*' data['fun'] = 'grains.item' data['arg'] = arg result = self.get_data(self.url, data) return result def execute_command(self, tgt, fun='cmd.run', arg=None, tgt_type='list', salt_async=False): """ 执行saltstack 模块命令,类似于salt '*' cmd.run 'command' :param tgt: 目标主机 :param fun: 模块方法 可为空 :param arg: 传递参数 可为空 :return: 执行结果 """ data = copy.deepcopy(self.params) if not tgt: return {'status': False, 'msg': 'target host not exist'} if not arg: data.pop('arg') else: data['arg'] = arg if tgt != '*': data['tgt_type'] = tgt_type if salt_async: data['client'] = 'local_async' data['fun'] = fun data['tgt'] = tgt result = self.get_data(self.url, data) return result def jobs(self, fun='detail', jid=None): """ 任务 :param fun: active, detail :param jod: Job ID :return: 任务执行结果 """ data = {'client': 'runner'} data['fun'] = fun if fun == 'detail': if not jid: return {'success': False, 'msg': 'job id is none'} data['fun'] = 'jobs.lookup_jid' data['jid'] = jid else: return {'success': False, 'msg': 'fun is active or detail'} result = self.get_data(self.url, data) return result 更多关于 Saltstack 相关的原创文章可参考: Saltstack 集中化管理平台安装 利用 Saltstack 远程执行命令 跟我学 Saltstack 常用模块及 API 如何在 Saltstack 组件下收集被控主机的信息? 如何通过 Saltstack pillar 组件定义与被控主机相关的任何数据? vCenter 客户端 通过官方 SDK 对 vCenter 进行日常操作,此脚本是我用于 cmdb 平台的,自动获取主机信息,存入数据库。 from pyVim.connect import SmartConnect, Disconnect, SmartConnectNoSSL from pyVmomi import vim from asset import models import atexit class Vmware: def __init__(self, ip, user, password, port, idc, vcenter_id): self.ip = ip self.user = user self.password = password self.port = port self.idc_id = idc self.vcenter_id = vcenter_id def get_obj(self, content, vimtype, name=None): ''' 列表返回,name 可以指定匹配的对象 ''' container = content.viewManager.CreateContainerView(content.rootFolder, vimtype, True) obj = [ view for view in container.view ] return obj def get_esxi_info(self): # 宿主机信息 esxi_host = {} res = {"connect_status": True, "msg": None} try: # connect this thing si = SmartConnectNoSSL(host=self.ip, user=self.user, pwd=self.password, port=self.port, connectionPoolTimeout=60) except Exception as e: res['connect_status'] = False try: res['msg'] = ("%s Caught vmodl fault : " + e.msg) % (self.ip) except Exception as e: res['msg'] = '%s: connection error' % (self.ip) return res # disconnect this thing atexit.register(Disconnect, si) content = si.RetrieveContent() esxi_obj = self.get_obj(content, [vim.HostSystem]) for esxi in esxi_obj: esxi_host[esxi.name] = {} esxi_host[esxi.name]['idc_id'] = self.idc_id esxi_host[esxi.name]['vcenter_id'] = self.vcenter_id esxi_host[esxi.name]['server_ip'] = esxi.name esxi_host[esxi.name]['manufacturer'] = esxi.summary.hardware.vendor esxi_host[esxi.name]['server_model'] = esxi.summary.hardware.model for i in esxi.summary.hardware.otherIdentifyingInfo: if isinstance(i, vim.host.SystemIdentificationInfo): esxi_host[esxi.name]['server_sn'] = i.identifierValue # 系统名称 esxi_host[esxi.name]['system_name'] = esxi.summary.config.product.fullName # cpu总核数 esxi_cpu_total = esxi.summary.hardware.numCpuThreads # 内存总量 GB esxi_memory_total = esxi.summary.hardware.memorySize / 1024 / 1024 / 1024 # 获取硬盘总量 GB esxi_disk_total = 0 for ds in esxi.datastore: esxi_disk_total += ds.summary.capacity / 1024 / 1024 / 1024 # 默认配置4核8G100G,根据这个配置计算剩余可分配虚拟机 default_configure = { 'cpu': 4, 'memory': 8, 'disk': 100 } esxi_host[esxi.name]['vm_host'] = [] vm_usage_total_cpu = 0 vm_usage_total_memory = 0 vm_usage_total_disk = 0 # 虚拟机信息 for vm in esxi.vm: host_info = {} host_info['vm_name'] = vm.name host_info['power_status'] = vm.runtime.powerState host_info['cpu_total_kernel'] = str(vm.config.hardware.numCPU) + '核' host_info['memory_total'] = str(vm.config.hardware.memoryMB) + 'MB' host_info['system_info'] = vm.config.guestFullName disk_info = '' disk_total = 0 for d in vm.config.hardware.device: if isinstance(d, vim.vm.device.VirtualDisk): disk_total += d.capacityInKB / 1024 / 1024 disk_info += d.deviceInfo.label + ": " + str((d.capacityInKB) / 1024 / 1024) + ' GB' + ',' host_info['disk_info'] = disk_info esxi_host[esxi.name]['vm_host'].append(host_info) # 计算当前宿主机可用容量:总量 - 已分配的 if host_info['power_status'] == 'poweredOn': vm_usage_total_cpu += vm.config.hardware.numCPU vm_usage_total_disk += disk_total vm_usage_total_memory += (vm.config.hardware.memoryMB / 1024) esxi_cpu_free = esxi_cpu_total - vm_usage_total_cpu esxi_memory_free = esxi_memory_total - vm_usage_total_memory esxi_disk_free = esxi_disk_total - vm_usage_total_disk esxi_host[esxi.name]['cpu_info'] = 'Total: %d核, Free: %d核' % (esxi_cpu_total, esxi_cpu_free) esxi_host[esxi.name]['memory_info'] = 'Total: %dGB, Free: %dGB' % (esxi_memory_total, esxi_memory_free) esxi_host[esxi.name]['disk_info'] = 'Total: %dGB, Free: %dGB' % (esxi_disk_total, esxi_disk_free) # 计算cpu 内存 磁盘按照默认资源分配的最小值,即为当前可分配资源 if esxi_cpu_free < 4 or esxi_memory_free < 8 or esxi_disk_free < 100: free_allocation_vm_host = 0 else: free_allocation_vm_host = int(min( [ esxi_cpu_free / default_configure['cpu'], esxi_memory_free / default_configure['memory'], esxi_disk_free / default_configure['disk'] ] )) esxi_host[esxi.name]['free_allocation_vm_host'] = free_allocation_vm_host esxi_host['connect_status'] = True return esxi_host def write_to_db(self): esxi_host = self.get_esxi_info() # 连接失败 if not esxi_host['connect_status']: return esxi_host del esxi_host['connect_status'] for machine_ip in esxi_host: # 物理机信息 esxi_host_dict = esxi_host[machine_ip] # 虚拟机信息 virtual_host = esxi_host[machine_ip]['vm_host'] del esxi_host[machine_ip]['vm_host'] obj = models.EsxiHost.objects.create(**esxi_host_dict) obj.save() for host_info in virtual_host: host_info['management_host_id'] = obj.id obj2 = models.virtualHost.objects.create(**host_info) obj2.save() 获取域名 ssl 证书过期时间 用于 zabbix 告警 import re import sys import time import subprocess from datetime import datetime from io import StringIO def main(domain): f = StringIO() comm = f"curl -Ivs https://{domain} --connect-timeout 10" result = subprocess.getstatusoutput(comm) f.write(result[1]) try: m = re.search('start date: (.*?)\n.*?expire date: (.*?)\n.*?common name: (.*?)\n.*?issuer: CN=(.*?)\n', f.getvalue(), re.S) start_date = m.group(1) expire_date = m.group(2) common_name = m.group(3) issuer = m.group(4) except Exception as e: return 999999999 # time 字符串转时间数组 start_date = time.strptime(start_date, "%b %d %H:%M:%S %Y GMT") start_date_st = time.strftime("%Y-%m-%d %H:%M:%S", start_date) # datetime 字符串转时间数组 expire_date = datetime.strptime(expire_date, "%b %d %H:%M:%S %Y GMT") expire_date_st = datetime.strftime(expire_date,"%Y-%m-%d %H:%M:%S") # 剩余天数 remaining = (expire_date-datetime.now()).days return remaining if __name__ == "__main__": domain = sys.argv[1] remaining_days = main(domain) print(remaining_days) 发送今天的天气预报以及未来的天气趋势图 此脚本用于给老婆大人发送今天的天气预报以及未来的天气趋势图,现在微信把网页端禁止了,没法发送到微信了,我是通过企业微信进行通知的,需要把你老婆大人拉到企业微信,无兴趣的小伙伴跳过即可。 # -*- coding: utf-8 -*- import requests import json import datetime def weather(city): url = "http://wthrcdn.etouch.cn/weather_mini?city=%s" % city try: data = requests.get(url).json()['data'] city = data['city'] ganmao = data['ganmao'] today_weather = data['forecast'][0] res = "老婆今天是{}\n今天天气概况\n城市: {:<10}\n时间: {:<10}\n高温: {:<10}\n低温: {:<10}\n风力: {:<10}\n风向: {:<10}\n天气: {:<10}\n\n稍后会发送近期温度趋势图,请注意查看。\ ".format( ganmao, city, datetime.datetime.now().strftime('%Y-%m-%d'), today_weather['high'].split()[1], today_weather['low'].split()[1], today_weather['fengli'].split('[')[2].split(']')[0], today_weather['fengxiang'],today_weather['type'], ) return {"source_data": data, "res": res} except Exception as e: return str(e) + 获取天气预报趋势图 ```python # -*- coding: utf-8 -*- import matplotlib.pyplot as plt import re import datetime def Future_weather_states(forecast, save_path, day_num=5): ''' 展示未来的天气预报趋势图 :param forecast: 天气预报预测的数据 :param day_num: 未来几天 :return: 趋势图 ''' future_forecast = forecast dict={} for i in range(day_num): data = [] date = future_forecast[i]["date"] date = int(re.findall("\d+",date)[0]) data.append(int(re.findall("\d+", future_forecast[i]["high"])[0])) data.append(int(re.findall("\d+", future_forecast[i]["low"])[0])) data.append(future_forecast[i]["type"]) dict[date] = data data_list = sorted(dict.items()) date=[] high_temperature = [] low_temperature = [] for each in data_list: date.append(each[0]) high_temperature.append(each[1][0]) low_temperature.append(each[1][1]) fig = plt.plot(date,high_temperature,"r",date,low_temperature,"b") current_date = datetime.datetime.now().strftime('%Y-%m') plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['axes.unicode_minus'] = False plt.xlabel(current_date) plt.ylabel("℃") plt.legend(["高温", "低温"]) plt.xticks(date) plt.title("最近几天温度变化趋势") plt.savefig(save_path) ``` + 发送到企业微信 ```python # -*- coding: utf-8 -*- import requests import json class DLF: def __init__(self, corpid, corpsecret): self.url = "https://qyapi.weixin.qq.com/cgi-bin" self.corpid = corpid self.corpsecret = corpsecret self._token = self._get_token() def _get_token(self): ''' 获取企业微信API接口的access_token :return: ''' token_url = self.url + "/gettoken?corpid=%s&corpsecret=%s" %(self.corpid, self.corpsecret) try: res = requests.get(token_url).json() token = res['access_token'] return token except Exception as e: return str(e) def _get_media_id(self, file_obj): get_media_url = self.url + "/media/upload?access_token={}&type=file".format(self._token) data = {"media": file_obj} try: res = requests.post(url=get_media_url, files=data) media_id = res.json()['media_id'] return media_id except Exception as e: return str(e) def send_text(self, agentid, content, touser=None, toparty=None): send_msg_url = self.url + "/message/send?access_token=%s" % (self._token) send_data = { "touser": touser, "toparty": toparty, "msgtype": "text", "agentid": agentid, "text": { "content": content } } try: res = requests.post(send_msg_url, data=json.dumps(send_data)) except Exception as e: return str(e) def send_image(self, agentid, file_obj, touser=None, toparty=None): media_id = self._get_media_id(file_obj) send_msg_url = self.url + "/message/send?access_token=%s" % (self._token) send_data = { "touser": touser, "toparty": toparty, "msgtype": "image", "agentid": agentid, "image": { "media_id": media_id } } try: res = requests.post(send_msg_url, data=json.dumps(send_data)) except Exception as e: return str(e) main脚本 -- coding: utf-8 -- from plugins.weather_forecast import weatherfrom plugins.trend_chart import Future_weather_statesfrom plugins.send_wechat import DLFimport os 企业微信相关信息 corpid = "xxx"corpsecret = "xxx"agentid = "xxx" 天气预报趋势图保存路径 _path = os.path.dirname(os.path.abspath(file))save_path = os.path.join(_path ,'./tmp/weather_forecast.jpg') 获取天气预报信息 content = weather("大兴") 发送文字消息 dlf = DLF(corpid, corpsecret)dlf.send_text(agentid=agentid, content=content['res'], toparty='1') 生成天气预报趋势图 Future_weather_states(content['source_data']['forecast'], save_path) 发送图片消息 file_obj = open(save_path, 'rb')dlf.send_image(agentid=agentid, toparty='1', file_obj=file_obj) ### Shell 脚本部分 **SVN 完整备份** 通过 hotcopy 进行 SVN 完整备份,备份保留 7 天。 ```python #!/bin/bash # Filename : svn_backup_repos.sh # Date : 2020/12/14 # Author : JakeTian # Email : JakeTian@***.com # Crontab : 59 23 * * * /bin/bash $BASE_PATH/svn_backup_repos.sh >/dev/null 2>&1 # Notes : 将脚本加入crontab中,每天定时执行 # Description: SVN完全备份 set -e SRC_PATH="/opt/svndata" DST_PATH="/data/svnbackup" LOG_FILE="$DST_PATH/logs/svn_backup.log" SVN_BACKUP_C="/bin/svnadmin hotcopy" SVN_LOOK_C="/bin/svnlook youngest" TODAY=$(date +'%F') cd $SRC_PATH ALL_REPOS=$(find ./ -maxdepth 1 -type d ! -name 'httpd' -a ! -name 'bak' | tr -d './') # 创建备份目录,备份脚本日志目录 test -d $DST_PATH || mkdir -p $DST_PATH test -d $DST_PATH/logs || mkdir $DST_PATH/logs test -d $DST_PATH/$TODAY || mkdir $DST_PATH/$TODAY # 备份repos文件 for repo in $ALL_REPOS do $SVN_BACKUP_C $SRC_PATH/$repo $DST_PATH/$TODAY/$repo # 判断备份是否完成 if $SVN_LOOK_C $DST_PATH/$TODAY/$repo;then echo "$TODAY: $repo Backup Success" >> $LOG_FILE else echo "$TODAY: $repo Backup Fail" >> $LOG_FILE fi done # # 备份用户密码文件和权限文件 cp -p authz access.conf $DST_PATH/$TODAY # 日志文件转储 mv $LOG_FILE $LOG_FILE-$TODAY # 删除七天前的备份 seven_days_ago=$(date -d "7 days ago" +'%F') rm -rf $DST_PATH/$seven_days_ago zabbix 监控用户密码过期 用于 Zabbix 监控 Linux 系统用户(shell 为 /bin/bash 和 /bin/sh)密码过期,密码有效期剩余 7 天触发加自动发现用户。 #!/bin/bash diskarray=(`awk -F':' '$NF ~ /\/bin\/bash/||/\/bin\/sh/{print $1}' /etc/passwd`) length=${#diskarray[@]} printf "{\n" printf '\t'"\"data\":[" for ((i=0;i<$length;i++)) do printf '\n\t\t{' printf "\"{#USER_NAME}\":\"${diskarray[$i]}\"}" if [ $i -lt $[$length-1] ];then printf ',' fi done printf "\n\t]\n" printf "}\n" 检查用户密码过期 #!/bin/bash export LANG=en_US.UTF-8 SEVEN_DAYS_AGO=$(date -d '-7 day' +'%s') user="$1" # 将Sep 09, 2018格式的时间转换成unix时间 expires_date=$(sudo chage -l $user | awk -F':' '/Password expires/{print $NF}' | sed -n 's/^ //p') if [[ "$expires_date" != "never" ]];then expires_date=$(date -d "$expires_date" +'%s') if [ "$expires_date" -le "$SEVEN_DAYS_AGO" ];then echo "1" else echo "0" fi else echo "0" fi 构建本地YUM 通过 rsync 的方式同步 yum,通过 nginx 只做 http yum 站点; 但是 centos6 的镜像最近都不能用了,国内貌似都禁用了,如果找到合适的自行更换地址。 #!/bin/bash # 更新yum镜像 RsyncCommand="rsync -rvutH -P --delete --delete-after --delay-updates --bwlimit=1000" DIR="/app/yumData" LogDir="$DIR/logs" Centos6Base="$DIR/Centos6/x86_64/Base" Centos7Base="$DIR/Centos7/x86_64/Base" Centos6Epel="$DIR/Centos6/x86_64/Epel" Centos7Epel="$DIR/Centos7/x86_64/Epel" Centos6Salt="$DIR/Centos6/x86_64/Salt" Centos7Salt="$DIR/Centos7/x86_64/Salt" Centos6Update="$DIR/Centos6/x86_64/Update" Centos7Update="$DIR/Centos7/x86_64/Update" Centos6Docker="$DIR/Centos6/x86_64/Docker" Centos7Docker="$DIR/Centos7/x86_64/Docker" Centos6Mysql5_7="$DIR/Centos6/x86_64/Mysql/Mysql5.7" Centos7Mysql5_7="$DIR/Centos7/x86_64/Mysql/Mysql5.7" Centos6Mysql8_0="$DIR/Centos6/x86_64/Mysql/Mysql8.0" Centos7Mysql8_0="$DIR/Centos7/x86_64/Mysql/Mysql8.0" MirrorDomain="rsync://rsync.mirrors.ustc.edu.cn" # 目录不存在就创建 check_dir(){ for dir in $* do test -d $dir || mkdir -p $dir done } # 检查rsync同步结果 check_rsync_status(){ if [ $? -eq 0 ];then echo "rsync success" >> $1 else echo "rsync fail" >> $1 fi } check_dir $DIR $LogDir $Centos6Base $Centos7Base $Centos6Epel $Centos7Epel $Centos6Salt $Centos7Salt $Centos6Update $Centos7Update $Centos6Docker $Centos7Docker $Centos6Mysql5_7 $Centos7Mysql5_7 $Centos6Mysql8_0 $Centos7Mysql8_0 # Base yumrepo #$RsyncCommand "$MirrorDomain"/repo/centos/6/os/x86_64/ $Centos6Base >> "$LogDir/centos6Base.log" 2>&1 # check_rsync_status "$LogDir/centos6Base.log" $RsyncCommand "$MirrorDomain"/repo/centos/7/os/x86_64/ $Centos7Base >> "$LogDir/centos7Base.log" 2>&1 check_rsync_status "$LogDir/centos7Base.log" # Epel yumrepo # $RsyncCommand "$MirrorDomain"/repo/epel/6/x86_64/ $Centos6Epel >> "$LogDir/centos6Epel.log" 2>&1 # check_rsync_status "$LogDir/centos6Epel.log" $RsyncCommand "$MirrorDomain"/repo/epel/7/x86_64/ $Centos7Epel >> "$LogDir/centos7Epel.log" 2>&1 check_rsync_status "$LogDir/centos7Epel.log" # SaltStack yumrepo # $RsyncCommand "$MirrorDomain"/repo/salt/yum/redhat/6/x86_64/ $Centos6Salt >> "$LogDir/centos6Salt.log" 2>&1 # ln -s $Centos6Salt/archive/$(ls $Centos6Salt/archive | tail -1) $Centos6Salt/latest # check_rsync_status "$LogDir/centos6Salt.log" $RsyncComman "$MirrorDomain"/repo/salt/yum/redhat/7/x86_64/ $Centos7Salt >> "$LogDir/centos7Salt.log" 2>&1 check_rsync_status "$LogDir/centos7Salt.log" # ln -s $Centos7Salt/archive/$(ls $Centos7Salt/archive | tail -1) $Centos7Salt/latest # Docker yumrepo $RsyncCommand "$MirrorDomain"/repo/docker-ce/linux/centos/7/x86_64/stable/ $Centos7Docker >> "$LogDir/centos7Docker.log" 2>&1 check_rsync_status "$LogDir/centos7Docker.log" # centos update yumrepo # $RsyncCommand "$MirrorDomain"/repo/centos/6/updates/x86_64/ $Centos6Update >> "$LogDir/centos6Update.log" 2>&1 # check_rsync_status "$LogDir/centos6Update.log" $RsyncCommand "$MirrorDomain"/repo/centos/7/updates/x86_64/ $Centos7Update >> "$LogDir/centos7Update.log" 2>&1 check_rsync_status "$LogDir/centos7Update.log" # mysql 5.7 yumrepo # $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-5.7-community/el/6/x86_64/ "$Centos6Mysql5_7" >> "$LogDir/centos6Mysql5.7.log" 2>&1 # check_rsync_status "$LogDir/centos6Mysql5.7.log" $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-5.7-community/el/7/x86_64/ "$Centos7Mysql5_7" >> "$LogDir/centos7Mysql5.7.log" 2>&1 check_rsync_status "$LogDir/centos7Mysql5.7.log" # mysql 8.0 yumrepo # $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-8.0-community/el/6/x86_64/ "$Centos6Mysql8_0" >> "$LogDir/centos6Mysql8.0.log" 2>&1 # check_rsync_status "$LogDir/centos6Mysql8.0.log" $RsyncCommand "$MirrorDomain"/repo/mysql-repo/yum/mysql-8.0-community/el/7/x86_64/ "$Centos7Mysql8_0" >> "$LogDir/centos7Mysql8.0.log" 2>&1 check_rsync_status "$LogDir/centos7Mysql8.0.log" 读者需求解答 负载高时,查出占用比较高的进程脚本并存储或推送通知 这部分内容是上篇 7 个非常实用的 Shell 拿来就用脚本实例!中底部读者留言的需求,如下: #!/bin/bash # 物理cpu个数 physical_cpu_count=$(egrep 'physical id' /proc/cpuinfo | sort | uniq | wc -l) # 单个物理cpu核数 physical_cpu_cores=$(egrep 'cpu cores' /proc/cpuinfo | uniq | awk '{print $NF}') # 总核数 total_cpu_cores=$((physical_cpu_count*physical_cpu_cores)) # 分别是一分钟、五分钟、十五分钟负载的阈值,其中有一项超过阈值才会触发 one_min_load_threshold="$total_cpu_cores" five_min_load_threshold=$(awk 'BEGIN {print '"$total_cpu_cores"' * "0.8"}') fifteen_min_load_threshold=$(awk 'BEGIN {print '"$total_cpu_cores"' * "0.7"}') # 分别是分钟、五分钟、十五分钟负载平均值 one_min_load=$(uptime | awk '{print $(NF-2)}' | tr -d ',') five_min_load=$(uptime | awk '{print $(NF-1)}' | tr -d ',') fifteen_min_load=$(uptime | awk '{print $NF}' | tr -d ',') # 获取当前cpu 内存 磁盘io信息,并写入日志文件 # 如果需要发送消息或者调用其他,请自行编写函数即可 get_info(){ log_dir="cpu_high_script_log" test -d "$log_dir" || mkdir "$log_dir" ps -eo user,pid,%cpu,stat,time,command --sort -%cpu | head -10 > "$log_dir"/cpu_top10.log ps -eo user,pid,%mem,rss,vsz,stat,time,command --sort -%mem | head -10 > "$log_dir"/mem_top10.log iostat -dx 1 10 > "$log_dir"/disk_io_10.log } export -f get_info echo "$one_min_load $one_min_load_threshold $five_min_load $five_min_load_threshold $fifteen_min_load $fifteen_min_load_threshold" | \ awk '{ if ($1>=$2 || $3>=$4 || $5>=$6) system("get_info") }' 以上,就是今天分享的全部内容了。 希望大家通过这些案例能够学以致用,结合自身的实际场景进行运用,从而提高自己的工作效率。 如果你有更多脚本实例,也欢迎大家分享或通过本文留言区进行留言说说你具体的脚本实例需求,如果实例过多的话,下次杰哥在整一篇合集脚本文章实例来跟大家分享。 对了,如果你觉得上述这些脚本实例能够用到自己工作中的话,也希望能给予小小的支持(点赞、留言、转发分享随你便)。 作者:养乐多 编辑:JackTian来源:公众号「杰哥的IT之旅」ID:Jake_Internet原文链接:超硬核!11 个非常实用的 Python 和 Shell 拿来就用脚本实例!转载请联系授权(微信ID:Hc220088) 关注公众号:「杰哥的IT之旅」后台回复:「脚本合集」可获取本文全部脚本实例文件关注公众号:「杰哥的IT之旅」后台回复:「wx」 可邀请你加入读者交流群

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

[python作业AI毕业设计博客]比Selenium IDE更好用的录制工具: Katalon Recorder

Selenium IDE(集成开发环境 Integrated Development Environment)是用于构建测试脚本的原型工具。 它是一个Firefox插件,提供了易于使用的界面,用于开发自动化测试。 但是最新的Selenium IDE总是弹出一系列如下的对话框,不能直接录制,有点不爽。另外Selenium IDE也不支持最常用的Chrome浏览器。 python测试开发项目实战-目录 python工具书籍下载-持续更新 python 3.7极速入门教程 - 目录 Katalon Recorder是最新Chrome和Firefox上与Selenium IDE兼容的替代产品。 特点如下: 易于使用和安装 无需编程知识 它具有录制功能 同时支持Chrome和Firefox 可以马上开始录制: 回放 参考资料 本文最新版本地址 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 selenium 书籍下载: https://china-testing.github.io/selnium_books.html

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

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部分的功能。

用户登录
用户注册