首页 文章 精选 留言 我的

精选列表

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

[雪峰磁针石博客]python工具书籍下载-持续更新

呕心沥血,持续收集python精品书籍,觉得不错的话,请在下面点喜欢收藏,谢谢! python数据结构基础工具书籍下载-持续更新 2018最佳人工智能机器学习工具书籍及下载(持续更新) 2018最佳人工智能图像处理工具OpenCV书籍下载 web开发工具flask中文英文书籍下载-持续更新 python网络基础工具书籍下载-持续更新 python 3.7极速入门教程9最佳python中文工具书籍下载 机器学习工具pytorch中文英文工具书籍下载-持续更新 2018最佳selenium工具书籍汇总下载(持续更新) python数据分析数据科学中文英文工具书籍下载-持续更新 2018最佳人工智能数据采集(爬虫)工具书下载 部分示例 pandas 0.23.4 官方文档.pdf Python Data Science Handbook - 2017.pdf 中文版待上传 《Python数据科学手册》是对以数据深度需求为中心的科学、研究以及针对计算和统计方法的参考书。本书共五章,每章介绍一到两个Python数据科学中的重点工具包。首先从IPython和Jupyter开始,它们提供了数据科学家需要的计算环境;第 2章讲解能提供ndarray对象的NumPy,它可以用Python高效地存储和操作大型数组;第3章主要涉及提供DataFrame对象的Pandas,它可以用Python高效地存储和操作带标签的/列式数据;第4章的主角是Matplotlib,它为Python提供了许多数据可视化功能;第5章以Scikit-Learn为主,这个程序库为重要的机器学习算法提供了高效整洁的Python版实现。 《Python数据科学手册》适合有编程背景,并打算将开源Python工具用作分析、操作、可视化以及学习数据的数据科学研究人员 https://github.com/jakevdp/PythonDataScienceHandbook 近12000+ 星 Python for Data Analysis, 2nd Edition - 2017.pdf 《利用Python进行数据分析·第2版.pdf 阅读本书可以获得关于在Python下操作、处理、清洗、规整数据集的完整说明。本书第2版针对Python3.6进行了更新,并增加了实际案例向你展示如何高效地解决一系列数据分析问题。你将在阅读过程中学习到新版本的pandas、NumPy、IPython和Jupyter。 https://github.com/wesm/pydata-book 近8000星 Data.Science.from.Scratch.First.Principles.with.Python - 2015.pdf 亚马逊 4星 108评 https://github.com/joelgrus/data-science-from-scratch 3200+ 星 Python编程:从入门到实践 - 2016.pdf 初级 亚马逊 4.5星 345评论 https://github.com/ehmatthes/pcc 1000左右星 本书是一本针对所有层次的Python读者而作的Python入门书。全书分两部分:首部分介绍用Python 编程所必须了解的基本概念,包括matplotlib、NumPy和Pygal等强大的Python库和工具介绍,以及列表、字典、if语句、类、文件与异常、代码测试等内容;第 二部分将理论付诸实践,讲解如何开发三个项目,包括简单的Python 2D游戏开发,如何利用数据生成交互式的信息图,以及创建和定制简单的Web应用,并帮读者解决常见编程问题和困惑。 海报: 英文版:Python Crash Course - 2016.pdf 流畅的python - 2017.pdf 中级 《流畅的Python》一书致力于帮助Python开发人员挖掘这门语言及相关程序库的优秀特性,避免重复劳动,同时写出简洁、流畅、易读、易维护,并且具有地道Python风格的代码。本书尤其深入探讨了Python语言的高级用法,涵盖数据结构、Python风格的对象、并行与并发,以及元编程等不同的方面。 海报: https://github.com/fluentpython/example-code 2000+ 星 强烈推荐 英文版本:Fluent Python - 2014.pdf Solutions Review审查了许多机器学习书籍,根据相关性,流行度,评分,发布日期以及增加业务价值的能力,精心挑选了顶级机器学习书籍。 列出的每本书至少有15个亚马逊用户评论,评分为4.0或更高。 您将在下面找到该领域公认的领导者,专家和技术专业人员的图书库。 从数据科学到神经网络,这些出版物甚至可以提供最终端的数据和分析专业人员。 个人建议优先考虑github星级1000以上的书。 Python深度学习 Deep Learning with Python François Chollet - 2018.pdf -- 强烈推荐 Python深度学习 - 2018.pdf 源码 github星级 5000左右 页数:386 Deep Learning with Python使用Python语言和强大的Keras库引入深度学习。 本书由Keras作者,Google AI研究员FrançoisChollet撰写,通过直观的解释和实际例子帮助您的理解,涉及计算机视觉,自然语言处理和生成模型。 中文版(文字版)可以在qq群144081101等找到。 机器学习实战:基于Scikit-Learn和TensorFlow Hands-On Machine Learning with Scikit-Learn and TensorFlow - 2017.pdf 机器学习实战:基于Scikit-Learn和TensorFlow - 2018.pdf 通过使用具体的例子,最小的理论和两个可用于生产的Python框架 - scikit-learn和TensorFlow,作者AurélienGéron帮助您直观地理解构建智能系统的概念和工具。 您将学习一系列技术,从简单的线性回归开始,逐步深入到神经网络。 通过每一章的练习来帮助您应用所学的知识,有编程经验就能上手。 源码地址 github星级 10000左右 页数: 564 Machine Learning For Absolute Beginners, 2nd Edition-Oliver Theobald(2017).pdf Machine Learning for Absolute Beginners Second Edition为完全初学者编写和设计的。简单的英语解释,无需编码经验。 在引入核心算法的地方,添加了清晰的解释和可视化示例,以便在家中轻松实现。新版本包含了第一版中未涉及的许多主题,包括交叉验证,数据清理和集合建模。 源码地址:后期找到补上 github星级 页数: 128 深度学习(deep learning) - 2017.pdf 提供了数学和概念背景,涵盖了线性代数,概率论和信息论,数值计算和机器学习中的相关概念。 它描述了工业从业者使用的深度学习技术,包括深度前馈网络,正则化,优化算法,卷积网络,序列建模和实用方法; 分析了自然语言处理,语音识别,计算机视觉和在线推荐系统等应用。 源码地址:暂无 github星级 20000左右 页数: 738 中文书 本书没有英文电子版本,但是有中文的 Python机器学习基础教程 - 2018.pdf Introduction to Machine Learning with Python - 2017.pdf 如果您使用Python,即使是初学者,本书也将用务实的方法教您构建自己的机器学习解决方案。 您将学习使用Python和scikit-learn库创建成功的机器学习。 作者AndreasMüller和Sarah Guido专注于使用机器学习算法的应用,而不是背后的数学。 熟悉NumPy和matplotlib库将帮助您从本书中获得更多信息。 源码 github星级 3000左右 页数: 392 使用TensorFlow进行机器学习 Machine Learning with TensorFlow - 2017.pdf 使用TensorFlow进行机器学习为读者提供了机器学习概念的坚实基础,以及使用Python编写TensorFlow的实践经验。 您将通过使用经典预测,分类和聚类算法来学习基础知识。 然后,您将进入金钱章节:探索深度学习概念,如自动编码器,循环神经网络和强化学习。 摘要本书,您将准备好使用TensorFlow进行自己的机器学习和深度学习应用。“ 源码 github星级 4000左右 页数: 244

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

[雪峰磁针石博客]python应用性能监控工具简介

监控简介 监视工具捕获,分析和显示Web应用程序执行信息。每个应用程序在Web堆栈的所有层都可能出现问题。监控工具通过展示帮助开发人员和运营团队响应并修复问题。 捕获和分析有关生产环境的数据对于主动处理Web应用程序的稳定性,性能和错误至关重要。 监视和日志记录的目的非常相似,它们有助于诊断应用程序的问题并帮助调试。主要差异为日志基于事件触发,而监视是被动的背景数据集合。 例如发生错误时,通过异常处理程序中的代码显式记录该事件。同时,监视代理程序对代码进行检测,并且不仅收集有关已记录异常的数据,还收集有关函数和方法性能的数据。 操作系统和网络的监控关注: CPU、内存和磁盘利用率,网络带宽和延迟 应用程序级别监视包含几个方面。 基于应用程序是重读,重写还是受到快速波动的影响而有所不同。涉及应用程序警告和错误(500级HTTP错误)、应用代码性能、模板渲染时间、浏览器渲染应用程序的时间、数据库查询性能等。 开源监控项目 错误跟踪 Sentry 最初只是一个Python监控项目,但现在可以用于任何编程语言。 图片.png 度量 statsd 是一个node.js网络守护程序,它监听指标并聚合它们以便转移到另一服务(如Graphite)。 Graphite 存储时间序列数据,并通过Django Web应用程序在图形中显示它们。 图片.png Sensu 是一个用Ruby编写的开源监控框架,但适用于任何编程语言Web应用程序。 Graph Explorer Vimeo的Graph Explorer是一个基于Graphite的仪表板,具有附加功能和平滑的设计。 Munin 基于客户端插件的监控系统,可以将监控流量发送到Munin节点,在那里可以分析和可视化数据。请注意,此项目是用Perl编写的,因此必须在收集数据的节点上安装Perl 5。 用户监控 Bucky 测量来自最终用户浏览器的Web应用程序的性能,并将该数据发送回服务器以进行收集。 本文最新版本地址 https://china-testing.github.io/python_monitor.html 第三方监控服务 第三方监控软件没有部署和操作软件的负担。但是,托管监控成本不低,应用程序数据有危险,因此这些服务并不适合每个项目。 错误跟踪 Rollbar 用于服务器端和客户端,用于捕获和报告异常。 pyrollbar代码库为Python Web应用程序提供了快速集成。还有一些常见Web框架的具体说明,例如Django和Pyramid。 Sentry 是开源工具的托管版本,用于货币化并支持二次开发。 应用程序性能监控(APM) New Relic 提供应用程序和数据库监控以及用于捕获和分析堆栈中其他devleoper工具(如Twilio)的数据的插件。 Opbeat 专为django而建。 Opbeat将性能指标,发布跟踪和错误记录结合到一个简单的服务中。 Scout 监控Django和Flask应用程序的性能,自动检测视图,SQL查询,模板等。 状态页面 Status.io Web应用程序的正常运行时间和响应指标可视化。 StatusPage.io 简单的设置状态页面,用于监控应用程序的运行时间。 事件管理 PagerDuty 如果应用程序存在稳定性,性能或正常运行时间问题,PagerDuty会向指定人员或组发出警报。 监控资源 How to Add Hosted Monitoring to Flask Web Applications及[How to Monitor Bottle Web Applications] (https://www.fullstackpython.com/blog/monitor-python-web-applications.html)系列文章,介绍如何将托管监控添加到主要的Python Web框架应用程序。 The Virtues of Monitoring Effortless Monitoring with collectd, Graphite, and Docker Practical Guide to StatsD/Graphite Monitoring是一个详细的指南,其中包含用于监控基础架构的代码示例。 "10 Things They Forgot to Monitor 描述了超出磁盘和内存使用等标准。 四个Linux服务器监控工具 Four Linux server monitoring tools 如何设计有用的监控和图形可视化 How to design useful monitoring and graphing visualizations 5 years of metrics and monitoring 是一个很好的演示,突出了可视化。线图通常不是最佳解决方案,而且它们被过度使用。 Collector Highlight Series有一篇关于StatsD 的文章,解释了如何安装它以及它是如何工作的。 survey on monitoring tools有一些关于开发人员和运营人员在其环境中使用的数据和图表。 Ryan Frantz写了一篇关于Solving Monitoring 的好文章,其中新的定义是基于当今复杂系统的监控手段,以及该实践应如何发展。 监控学习清单 查看下面的软件即服务和开源监控工具。第三方服务往往更容易设置和托管数据。开源项目为您提供了更多控制权,但您需要为监控准备其他服务器。 我的建议是在试用期内安装New Relic的免费选项,以了解它如何与您的应用配合使用。它将使您对应用程序级监视工具的功能有所了解。 作为您的应用程序比例,请查看如何使用Graphite设置其中一个开源监视项目,例如StatsD。这两个项目的组合将为您提供对您正在收集和可视化的系统指标的细粒度控制。 python监控库参考:https://github.com/china-testing/python-api-tesing#%E7%9B%91%E6%8E%A7 参考资料 讨论 钉钉群21745728 qq群144081101 567351477 本文最新版本地址 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 Monitoring

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

[雪峰磁针石博客]2018最佳ssh免费登陆工具

Linux 终端 PAC 参考资料 讨论qq群144081101 591302926 567351477 钉钉免费群21745728 本文涉及的python测试开发库 谢谢点赞! 本文相关海量书籍下载 Windows git终端 https://git-scm.com/download/win MobaXterm http://mobaxterm.mobatek.net/ mRemoteN https://mremoteng.org/download 有些windows7不能安装,windows10的大多可以。 其他 https://www.chiark.greenend.org.uk/~sgtatham/putty/ 不能保存密码https://www.openssh.com/ 官方ssh实现https://alternativeto.net/software/kitty/ putty增强 但还是不能保存密码https://connectbot.org/ 安卓上最佳的连接工具 概览 名称 开发者 状态 最初版本 最新版本 基于 许可证 可获得源代码 AbsoluteTelnet Celestial Software (Brian Pence) 活跃 1996 2012-11-25 9.53 专有 否 Bitvise SSH Client/Tunnelier Bitvise 活跃 5 2002 2014-09-08 7.22 专有 否 ConnectBot Kenny Root / Jeffrey Sharkey 活跃 11 2007 2010-10-08 1.7.1 Apache 是 DameWare SolarWinds 活跃 2012年11月28日 专有 否 Dropbear Matt Johnston 活跃 2003年04月06日 2016-07-21 2016.74 MIT 是 MindTerm Cryptzone 活跃 1998年11月13日 2014年02月25日 专有 是 MobaXterm Mobatek 活跃 2008 2014-08-04 7.2 OpenSSH GPL Yes eSSH Client Ecode Software 不活跃 07 2002 专有 否 Private Shell Imposant 活跃 04 2003 2013-07-29 3.3 专有 否 ProxyCap Proxy Labs 活跃 2002 专有 否 FileZilla Tim Kosse 活跃 2001年06月22日 2016-12-06 3.23.0.2 PuTTY GPL 是 lsh Niels Möller 活跃 1999年05月23日 2013-06-26 2.0.4 GPL 是 OpenSSH The OpenBSD project 活跃 1999年12月01日 2016-12-19 7.4 ossh BSD 是 PACManager David Torrejón Vaquerizas 活跃 2010年01月01日 2016-12-12 4.5.5.5 OpenSSH, Telnet, FTP, vncviewer, rdesktop, cu, remote-tty, mosh, cadaver GPL 是 PuTTY Simon Tatham 活跃 1999年01月22日 2016-03-05 0.67 MIT 是 Reflection for Secure IT Attachmate 活跃 2012-11-12 F-Secure SSH 专有 否 Salt Maarten L. Hekkelman 活跃 11 2011 2013-06-22 3.2.2 专有 否 SecureCRT VanDyke Software 活跃 06 1998 2016-12-06 8.0.4 专有 否 SmartFTP SmartSoft Ltd 活跃 1998 专有 否 SSH Tectia Client/ConnectSecure SSH Communications Security(前身:Tectia) 活跃 07 1995 2013-07 以C语言独立实现 专有 否 SunSSH Open Solaris 活跃 2001 OpenSSH 2.3 CDDL 是 Terminals CodePlex Terminals CodePlex Project 活跃 2006 2013-07-16 3.1 Microsoft MS-CL 是 Tera Term TeraTerm Project 活跃 2004 2013-02-28 4.77 TeraTerm 2.3 (1994–1998) BSD 是 TN3270 Plus SDI USA, Inc. 活跃 2006 专有 否 Token2Shell Choung Networks 活跃 2008 2014-03-06 6.9.1 专有 否 TtyEmulator FCS Software 活跃 05 2002 专有 否 WinSCP Martin Prikryl 活跃 2000 2016-11-30 5.9.3 PuTTY GPL 是 Xshell NetSarang Computer, Inc. 活跃 2002-11-02 2018-09-10 6.0.0098 专有 否 ZOC Terminal EmTec, Innovative Software 活跃 1995年07月01日 2016-12-01 7.11.1 专有 否 Penguinet siliconcircus ? 1998? 2011-07-24 2.54 专有 否 名称 OS X Windows Cygwin BSD Linux Solaris) Java OpenVMS z/OS AIX HP-UX iOS Android Maemo Windows Phone AbsoluteTelnet 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? ConnectBot 否 否 否 否 否 否 ? ? ? 否 否 否 是 否 ? Dropbear 是 否 是 是 是 是 ? ? ? 是 是 否 是 是 ? eSSH Client 是 是 ? 是 是 是 ? ? ? 否 否 否 否 否 ? JuiceSSH 否 否 否 否 否 否 否 否 否 否 否 否 是 否 否 lsh 是 否 否 部分[注 1] 是 是 ? ? ? 否 否 否 否 否 ? MindTerm 是 是 ? 是 是 是 是 是 ? 是 是 否 否 否 ? MobaXterm 否 是 否 否 否 否 否 否 否 否 否 否 否 否 否 OpenSSH 集成 部分[注 2] 集成 集成 集成[注 3] 是 ? 是 是 是 是 是[注 4] 否 是 ? Private Shell 否 是 否 否 否 否 否 否 否 否 否 否 否 否 否 PACManager 否 否 否 部分 是 否 否 否 否 否 否 否 否 否 否 ProxyCap 是 是 否 否 否 否 否 否 否 否 否 否 否 否 ? PuTTY 部分 是 ? 是 是 ? ? ? ? 否 否 否 否 否 Beta[注 5] Salt 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? SecureCRT 是 是 否 否 是 否 否 否 否 否 否 否 否 否 ? SmartFTP 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? SSH Tectia Client/ConnectSecure 否 是 否 否 是 是 是[注 6] 否 是 是 是 否 否 否 ? Tera Term 否 是 否 否 否 否 ? ? ? ? 否 否 否 否 ? TN3270 Plus 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? Token2Shell 否 是 否 否 否 否 否 否 否 否 否 否 否 否 是[注 7] TtyEmulator 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? WinSCP 否 是 否 否 否 否 否 否 否 否 否 是[注 4] 否 否 ? Xshell 否 是 否 否 否 否 否 否 否 否 否 否 否 否 否 ZOC 是 是 否 否 否 否 否 否 否 否 否 否 否 否 ? Penguinet 否 是 否 否 否 否 否 否 否 否 否 否 否 否 ? Name OS X Windows Cygwin BSD Linux Solaris) Java OpenVMS z/OS AIX HP-UX iOS Android Maemo Windows Phone

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

[雪峰磁针石博客]python与java对接工具Jython介绍

快速入门 下面我们使用jython来调用自定义jar包中的类。 编辑java文件:Beach.java public class Beach { private String name; private String city; public Beach(String name, String city){ this.name = name; this.city = city; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } } 编译成jar包: # javac Beach.java # echo Main-Class: Beach >manifest.txt # jar cvfm Craps.jar manifest.txt *.class 已添加清单 正在添加: Beach.class(输入 = 795) (输出 = 430)(压缩了 45%) 正在添加: Point.class(输入 = 708) (输出 = 445)(压缩了 37%) 添加Craps.jar到CLASSPATH,修改/etc/profile,修改CLASSPATH export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:/usr/local/jython/Craps.jar 使用. /etc/profile导入变量。然后使用jython调用java代码。 # ./jython Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.7.0_79 Type "help", "copyright", "credits" or "license" for more information. >>> import Beach >>> beach = Beach("Cocoa Beach","Cocoa Beach") >>> beach.getName() u'Cocoa Beach' >>> print beach.getName() Cocoa Beach >>> 基础 Linux安装: # java -jar jython-installer-2.7.0.jar # ./jython Jython 2.7.0 (default:9987c746f838, Apr 29 2015, 02:25:11) [Java HotSpot(TM) 64-Bit Server VM (Oracle Corporation)] on java1.7.0_79 Type "help", "copyright", "credits" or "license" for more information. >>> windows安装:双击jython-installer-2.7.0.jar, 执行jython.exe 变量定义: java int x = 0; jython: x = 0 x = 'Hello Jython' 是动态类型的。 保留字: and assert break class continue def del elif else except exec finally for from global or pass print raise return try while with yield 编码结构使用缩进。 函数名也可以作为函数的参数。 语句关键字: if-elif-else for while continue break try-except-finally assert def print del raise import 文件名和类名一样可以直接导入。 此部分参考资料: http://www.jython.org/jythonbook/en/1.0/JythonAndJavaIntegration.html http://www.skylit.com/javamethods/faqs/createjar.html 参考资料: http://www.jython.org/jythonbook/en/1.0/ 模块和包 查看命名空间 >>> dir() ['__builtins__', '__doc__', '__name__', '__package__'] Jython的导入不像java那样必须在头部。导入java以右边名优先,python以左边名优先。模块查找路径: >>> import sys >>> sys.path ['', '/usr/local/jython2.7.0/Lib', '__classpath__', '__pyclasspath__/', '/usr/local/jython2.7.0/Lib/site-packages'] >>> import sys >>> sys.path ['', '/usr/local/jython2.7.0/Lib', '__classpath__', '__pyclasspath__/', '/usr/local/jython2.7.0/Lib/site-packages'] >>> import sys >>> sys.path.append("/home/andrew/code/java/mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar") >>> import com.mysql *sys-package-mgr*: can't write cache file for '/home/andrew/code/java/mysql-connector-java-5.1.39/mysql-connector-java-5.1.39-bin.jar' *sys-package-mgr*: can't write index file >>> dir(com.mysql) ['__name__', 'fabric', 'jdbc'] >>> import java.util.zip >>> dir(java.util.zip) ['Adler32', 'CRC32', 'CheckedInputStream', 'CheckedOutputStream', 'Checksum', 'DataFormatException', 'Deflater', 'DeflaterInputStream', 'DeflaterOutputStream', 'GZIPInputStream', 'GZIPOutputStream', 'Inflater', 'InflaterInputStream', 'InflaterOutputStream', 'ZipEntry', 'ZipError', 'ZipException', 'ZipFile', 'ZipInputStream', 'ZipOutputStream', '__name__'] >>> dir(java.util.zip.ZipInputStream) ['__class__', '__copy__', '__deepcopy__', '__delattr__', '__doc__', '__ensure_finalizer__', '__eq__', '__format__', '__getattribute__', '__hash__', '__init__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__subclasshook__', '__unicode__', 'available', 'class', 'close', 'closeEntry', 'equals', 'getClass', 'getNextEntry', 'hashCode', 'mark', 'markSupported', 'nextEntry', 'notify', 'notifyAll', 'read', 'reset', 'skip', 'toString', 'wait'] Jython 查找Jar和类的方法: python.packages.paths python.packages.directories java.class.path java.ext.dirs python.cachedir.skip pycimport把python字节码转换成java字节码。Jython导入java包可能有多个包合并。 >>> import sys >>> sys.path_hooks [<type 'org.python.core.JavaImporter'>, <type 'zipimport.zipimporter'>, <type 'ClasspathPyImporter'>] import pycimport 数据类型 Jython特定的collection,多用于给java传递数据。 # Import and use a Java ArrayList >>> import java.util.ArrayList as ArrayList >>> arr = ArrayList() # Add method will add an element to the list and return a boolean to signify successsful addition >>> arr.add(1) True >>> arr.add(2) True >>> print arr [1, 2] 比较老的Jarray,不推荐使用,不过可以创建空数组: 类型: Character Java Equivalent z boolean b byte c char d Double f Float h Short i Int l Long >>> my_seq = (1,2,3,4,5) >>> from jarray import array >>> array(my_seq,'i') array('i', [1, 2, 3, 4, 5]) >>> myStr = "Hello Jython" >>> array(myStr,'c') array('c', 'Hello Jython') >>> arr = zeros(10,'z') >>> arr array('z', [False, False, False, False, False, False, False, False, False, False]) >>> arr2 = zeros(6, 'i') >>> arr2 array('i', [0, 0, 0, 0, 0, 0]) 求素数 `>>> ``nums = range(2, 50) ` `>>> ``for i in range(2, 8): ` `... `` nums = filter(lambda x: x == i or x % i, nums)` `... ` `>>> ``print nums` 读取输入: <pre># Obtain a value from the command line and store it into a variable >>> import sys >>> fav_team = sys.stdin.readline() Cubs >>> sys.stdout.write("My favorite team is: %s" % fav_team) My favorite team is: Cubs</pre> 函数 协程: **def** search_file(filename): **print** 'Searching file *%s*' % (filename) my_file = open(filename, 'r') file_content = my_file.read() my_file.close() **while** True: search_text = (**yield**) search_result = file_content.count(search_text) **print** 'Number of matches: *%d*' % (search_result)</pre> Jython脚本编程基础 os.chdir不会改变jvm的启动目录。 >>> import os >>> os.getcwd() '/Users/frank/Desktop/frank/hg/jythonbook~jython-book/src/chapter8' >>> from java.io import File >>> f = File(".") >>> for x in f.list(): ... print x ... args.py search.py >>> os.chdir("/Users/frank") >>> os.getcwd() '/Users/frank' >>> os.listdir(".") ['Desktop', 'Documents', 'Downloads', 'Dropbox', 'Library', 'Movies', 'Music', 'Pictures', 'Public', 'Sites'] >>> g = File(".") >>> for x in g.list(): ... print x ... args.py search.py 编译代码 !/usr/local/jython2.7.0/bin/jython from javax.tools import (ForwardingJavaFileManager, ToolProvider, DiagnosticCollector,)names = ["/home/andrew/code/python/jythonbook/src/chapter8/HelloWorld.java"]compiler = ToolProvider.getSystemJavaCompiler()diagnostics = DiagnosticCollector()manager = compiler.getStandardFileManager(diagnostics, None, None)units = manager.getJavaFileObjectsFromStrings(names)comp_task = compiler.getTask(None, manager, diagnostics, None, None, units)success = comp_task.call()manager.close() import osimport sysimport glob from javax.tools import (forwardingjavafilemanager, toolprovider, diagnosticcollector,) tasks = {} def task(func): tasks[func.func_name] = func @taskdef clean(): files = glob.glob("\*.class") **for** file **in** files: os.unlink(file) @taskdef compile(): files = glob.glob("\*.java") _log("compiling *%s*" % files) **if** **not** _compile(files): quit() _log("compiled") def _log(message): **if** options.verbose: **print** message def _compile(names): compiler = toolprovider.getsystemjavacompiler() diagnostics = diagnosticcollector() manager = compiler.getstandardfilemanager(diagnostics, none, none) units = manager.getjavafileobjectsfromstrings(names) comp_task = compiler.gettask(none, manager, diagnostics, none, none, units) success = comp_task.call() manager.close() **return** success if name == '__main__': **from** **optparse** **import** optionparser parser = optionparser() parser.add_option("-q", "--quiet", action="store_false", dest="verbose", default=true, help="don't print out task messages.") parser.add_option("-p", "--projecthelp", action="store_true", dest="projecthelp", help="print out list of tasks.") (options, args) = parser.parse_args() **if** options.projecthelp: **for** task **in** tasks: **print** task sys.exit(0) **if** len(args) < 1: **print** "usage: jython builder.py [options] task" sys.exit(1) **try**: current = tasks[args[0]] **except** KeyError: **print** "task *%s* not defined." % args[0] sys.exit(1) current()</pre> public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World"); } } [frank@pacman chapter8]$ jython builder.py --helpUsage: builder.py [options] Options: -h, --help show this help message and exit -q, --quiet Don't print out task messages. -p, --projecthelp Print out list of tasks. [frank@pacman chapter8]$ jython builder.py --projecthelpcompileclean[frank@pacman chapter8]$ jython builder.py compilecompiling ['HelloWorld.java']compiled[frank@pacman chapter8]$ lsHelloWorld.java HelloWorld.class builder.py[frank@pacman chapter8]$ jython builder.py clean[frank@pacman chapter8]$ lsHelloWorld.java builder.py[frank@pacman chapter8]$ jython builder.py --quiet compile[frank@pacman chapter8]$ lsHelloWorld.class HelloWorld.java builder.py[frank@pacman chapter8]$

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

[雪峰磁针石博客]multi-mechanize性能测试工具

Multi-Mechanize简介 Multi-Mechanize 是一个开源的性能和负载测试框架,它并发运行多个 Python 脚本对网站或者服务生成负载(组合事务)。测试输出报告保存为HTML或JMeter的兼容的XML。Multi-Mechanize最常用于web性能 和可扩展性(scalability)测试,也适用于任何python可以访问的API。尤其适合后台性能测试。稍微懂点编程的话,这个工具会远强过商业 的性能测试工具。 主要特性: 支持各种 HTTP methods 高级超链接和HTML表单支持 支持 SSL 自动处理 Cookies 可设置HTTP头 自动处理重定向 支持代理 支持 HTTP 认证 图片.png 安装 使 用标准的python安装方式。注意,需要安装matplotlib以支持作图,在centos6下面可以这样安装yum -y install python27-matplotlib。multi-mechanize采用标准的python安装方式pip install multi-mechanize或者easy_install multi-mechanize这里都以linux(centos)为例。 centos 5 安装注意: 注意安装依赖: python26 python26-devel python26-numpy* python26-tornado 要EPEL支持: http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm 快速入门 创建项目 # multimech-newproject my_project 执行项目 # multimech-run my_project user_groups: 2 threads: 6 [================100%==================] 30s/30s transactions: 119 timers: 119 errors: 0 waiting for all requests to finish... analyzing results... transactions: 125 errors: 0 test start: 2013-09-13 11:47:47 test finish: 2013-09-13 11:48:16 created: ./my_project/results/results_2013.09.13_11.47.46/results.html done. 如果有出现figure没有定义,请在相关文件的头部从matplotlib导入。 测试结果参见: image.png 图片.png 图片.png 目录结构 每个测试项目包含以下内容: config.cfg的配置文件。用于设定测试选项。 test_scripts/虚拟用户脚本的目录。在这里添加您的测试脚本。 results/:结果存储目录。对于每个测试都声称一个时间戳目录,里面包含结果的报告。 multimech-newproject,默认生成一个随机数的脚本。脚本v_user.py如下: import randomimport timeclass Transaction(object): def __init__(self): pass def run(self): r = random.uniform(1, 2) time.sleep(r) self.custom_timers['Example_Timer'] = rif __name__ == '__main__': trans = Transaction() trans.run() print trans.custom_timers 配置参数的含义如下: run_time: duration of test (seconds) 测试的执行时间 rampup: duration of user rampup (seconds) 多少秒内发完请求 results_ts_interval: time series interval for results analysis (seconds) 结果分析时间 progress_bar: turn on/off console progress bar during test run 是否显示进度条 console_logging: turn on/off logging to stdout 是否输出到stdout xml_report: turn on/off xml/jtl report 是否生成xml报告。 results_database: database connection string (optional) 保存结果的数据库连接字符串(可选) post_run_script: hook to call a script at test completion (optional) 调用的善后脚本(可选) 更多介绍参见: http://testutils.org/multi-mechanize/configfile.html 脚本书写 用Python书写,测试脚本模拟虚拟用户对网站/服务/ API的请求,脚本定义了用户事务,更多内容参见脚本手册:http://testutils.org/multi-mechanize/scripts.html#scripts-label。 每个脚本必须实现一个Transaction()类。这个类必须实现一个run()方法。基本的测试脚本结构如下: class Transaction(object): def run(self): # do something here return 运行期间,Transaction()实例化一次,run()方法则反复调用: class Transaction(object): def __init__(self): # do per-user user setup here # this gets called once on user creation return def run(self): # do user actions here # this gets called repeatedly return 从结构上看,如果每次run如果需要setup和teardown,时间也会计算在run里面,会显得事务处理的时间更长。这个就需要使用定时器来精确计时。 另外脚本建议先调试之后在运行,因为Multi-Mechanize有可能报错不够精准。可以这样运行:# python v_suds.py。v_suds.py是你实际使用的脚本名。另外suds这个库好像实现时性能一般,并发200时,客户端cpu占用率经常会100%,为此web service如果要上大量用户的话,建议用其他库替代,比如soapPy。进一步提升效率可以试用python的ctypes模块,或者cython(性能接近c语言)。不过Multi-Mechanize的多进程是基于python的,实在对性能有相当高的要求,就只能全部用c书写了。 下例使用mechanize进行web测试。 class Transaction(object): def __init__(self): pass def run(self): br = mechanize.Browser() br.set_handle_robots(False) resp = br.open('http://192.168.4.13/env.htm') assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.codes assert ('service name' in resp.get_data()) 下面用httplib库重写脚本,并增加定时器。通过定时器,可以分析各个步骤的耗时。 import httplib import time class Transaction(object): def run(self): conn = httplib.HTTPConnection('192.168.4.13') start = time.time() conn.request('GET', '/env.htm') request_time = time.time() resp = conn.getresponse() response_time = time.time() conn.close() transfer_time = time.time() self.custom_timers['request sent'] = request_time - start self.custom_timers['response received'] = response_time - start self.custom_timers['content transferred'] = transfer_time - start assert (resp.status == 200), 'Bad Response: HTTP %s' % resp.status if __name__ == '__main__': trans = Transaction() trans.run() for timer in ('request sent', 'response received', 'content transferred'): print '%s: %.5f secs' % (timer, trans.custom_timers[timer]) http://testutils.org/multi-mechanize/scripts.html#scripts-label 还有更多的实例。 import mechanize import time class Transaction(object): def __init__(self): pass def run(self): # create a Browser instance br = mechanize.Browser() # don't bother with robots.txt br.set_handle_robots(False) # add a custom header so wikipedia allows our requests br.addheaders = [('User-agent', 'Mozilla/5.0 Compatible')] # start the timer start_timer = time.time() # submit the request resp = br.open('http://www.wikipedia.org/') resp.read() # stop the timer latency = time.time() - start_timer # store the custom timer self.custom_timers['Load_Front_Page'] = latency # verify responses are valid assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code assert ('Wikipedia, the free encyclopedia' in resp.get_data()) # think-time time.sleep(2) # select first (zero-based) form on page br.select_form(nr=0) # set form field br.form['search'] = 'foo' # start the timer start_timer = time.time() # submit the form resp = br.submit() resp.read() # stop the timer latency = time.time() - start_timer # store the custom timer self.custom_timers['Search'] = latency # verify responses are valid assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code assert ('foobar' in resp.get_data()), 'Text Assertion Failed' # think-time time.sleep(2) import urllib2 import time class Transaction(object): def run(self): start_timer = time.time() resp = urllib2.urlopen('http://www.example.com/') content = resp.read() latency = time.time() - start_timer self.custom_timers['Example_Homepage'] = latency assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code assert ('Example Web Page' in content), 'Text Assertion Failed' import urllib2 import time class Transaction(object): def __init__(self): self.custom_timers = {} with open('soap.xml') as f: self.soap_body = f.read() def run(self): req = urllib2.Request(url='http://www.foo.com/service', data=self.soap_body) req.add_header('Content-Type', 'application/soap+xml') req.add_header('SOAPAction', 'http://www.foo.com/action') start_timer = time.time() resp = urllib2.urlopen(req) content = resp.read() latency = time.time() - start_timer self.custom_timers['Example_SOAP_Msg'] = latency assert (resp.code == 200), 'Bad Response: HTTP %s' % resp.code assert ('Example SOAP Response' in content), 'Text Assertion Failed' 参考资料 本文涉及的python测试开发库 谢谢点赞! * 讨论 钉钉免费群21745728 qq群144081101 567351477 本文相关海量书籍下载 https://pypi.python.org/pypi/multi-mechanize/1.2.0 http://testutils.org/multi-mechanize

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

ClickHouse集群搭建(二)

重叠泪痕缄锦字,人生只有情难死。 分布式集群安装 在上一章我们已经完成ClickHouse分布式集群安装,也创建本地表和分布式表进行了测试,但是,假如停掉一个节点会发生神马情况? node03上kill掉clickhouse-server进程 [root@node03 ~]# ps -ef | grep clickhouse clickho+ 2233 1 73 13:07 ? 00:00:02 clickhouse-server --daemon --pid-file=/var/run/clickhouse-server/clickhouse-server.pid --config-file=/etc/clickhouse-server/config.xml root 2306 1751 0 13:07 pts/0 00:00:00 grep --color=auto clickhouse [root@node03 ~]# service clickhouse-server stop Stop clickhouse-server service: DONE [root@node03 ~]# ps -ef | grep clickhouse root 2337 1751 0 13:07 pts/0 00:00:00 grep --color=auto clickhouse node01上查询分布式表 node01 :) select * from cluster3s1r_all; # node03没有被杀掉时 SELECT * FROM cluster3s1r_all ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ ┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐ │ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │ └────┴──────────────────────┴────────┴────────────┴──────┘ 3 rows in set. Elapsed: 0.037 sec. node01 :) select * from cluster3s1r_all; # node03节点被杀掉时 SELECT * FROM cluster3s1r_all ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ↘ Progress: 1.00 rows, 59.00 B (8.87 rows/s., 523.62 B/s.) 0% Received exception from server (version 20.8.3): Code: 279. DB::Exception: Received from localhost:9000. DB::Exception: All connection tries failed. Log: Code: 32, e.displayText() = DB::Exception: Attempt to read after eof (version 20.8.3.18) Code: 210, e.displayText() = DB::NetException: Connection refused (node03:9000) (version 20.8.3.18) Code: 210, e.displayText() = DB::NetException: Connection refused (node03:9000) (version 20.8.3.18) : While executing Remote. 1 rows in set. Elapsed: 0.114 sec. 只返回了node01节点上的数据,node03节点上的两条数据丢失。 数据备份 但在ClickHouse中,replica是挂在shard上的,因此要用多副本,必须先定义shard。 最简单的情况:1个分片多个副本。 修改metrika.xml文件 node01 上修改 /etc/clickhouse-server/metrika.xml集群配置文件 <yandex> <!-- 集群配置 --> <clickhouse_remote_servers> <!-- 1分片2备份 --> <cluster_1shards_2replicas> <!-- 数据分片1 --> <shard> <!-- false代表一次性写入所有副本,true表示写入其中一个副本,配合zk来进行数据复制 --> <internal_replication>false</internal_replication> <replica> <host>node01</host> <port>9000</port> </replica> <replica> <host>node02</host> <port>9000</port> </replica> </shard> </cluster_1shards_2replicas> </clickhouse_remote_servers> </yandex> 将修改后的配置分发到node02机器上 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:$PWD metrika.xml 100% 674 618.9KB/s 00:00 如果配置文件没有问题,是不用重启clickhouse-server的,会自动加载配置文件,node01上查看集群信息 [root@node01 clickhouse-server]# clickhouse-client -m ClickHouse client version 20.8.3.18. Connecting to localhost:9000 as user default. Connected to ClickHouse server version 20.8.3 revision 54438. node01 :) select * from system.clusters; SELECT * FROM system.clusters ┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐ │ cluster_1shards_2replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ cluster_1shards_2replicas │ 1 │ 1 │ 2 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │ └───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘ 10 rows in set. Elapsed: 0.018 sec. 测试数据备份 在node01和node02上分别创建本地表cluster1s2r_local CREATE TABLE default.cluster1s2r_local ( `id` Int32, `website` String, `wechat` String, `FlightDate` Date, Year UInt16 ) ENGINE = MergeTree(FlightDate, (Year, FlightDate), 8192); 在node01机器上创建分布式表,注意集群名称 CREATE TABLE default.cluster1s2r_all AS cluster1s2r_local ENGINE = Distributed(cluster_1shards_2replicas, default, cluster1s2r_local, rand()); 往分布式表cluster1s2r_all插入数据,cluster1s2r_all 会全部插入到node01和node02节点的cluster1s2r_local里 插入数据 INSERT INTO default.cluster1s2r_all (id,website,wechat,FlightDate,Year)values(1,'https://niocoder.com/','java干货','2020-11-28',2020); INSERT INTO default.cluster1s2r_all (id,website,wechat,FlightDate,Year)values(2,'http://www.merryyou.cn/','javaganhuo','2020-11-28',2020); INSERT INTO default.cluster1s2r_all (id,website,wechat,FlightDate,Year)values(3,'http://www.xxxxx.cn/','xxxxx','2020-11-28',2020); 查询分布式表和本地表 node01 :) select * from cluster1s2r_all; # 查询分布式表 SELECT * FROM cluster1s2r_all ┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐ │ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │ └────┴──────────────────────┴────────┴────────────┴──────┘ ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 3 rows in set. Elapsed: 0.018 sec. node01 :) select * from cluster1s2r_local; # node01节点查询本地表 SELECT * FROM cluster1s2r_local ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ ┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐ │ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │ └────┴──────────────────────┴────────┴────────────┴──────┘ 3 rows in set. Elapsed: 0.015 sec. node02 :) select * from cluster1s2r_local; # node02节点查询本地表 SELECT * FROM cluster1s2r_local ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ ┌─id─┬─website──────────────┬─wechat─┬─FlightDate─┬─Year─┐ │ 3 │ http://www.xxxxx.cn/ │ xxxxx │ 2020-11-28 │ 2020 │ └────┴──────────────────────┴────────┴────────────┴──────┘ 3 rows in set. Elapsed: 0.007 sec. 查询node01和node02本地表cluster1s2r_local都是全量数据, 即使sotp到其中一个节点数据也不会丢失,数据副本已经生效。 数据副本一致性问题 既然有多副本,就有个一致性的问题:加入写入数据时,挂掉一台机器,会怎样? 模拟写入分布式表是某一个节点down机 停掉node02节点服务 service clickhouse-server stop 在node01节点上向分布式表cluster1s2r_all插入数据 sql INSERT INTO default.cluster1s2r_all (id,website,wechat,FlightDate,Year)values(4,'http://www.yyyyyy.cn/','yyyyy','2020-11-29',2020); 启动node02节点服务 查询验证是否同步 查看node01和node02机器的cluster1s2r_local、以及cluster1s2r_all,发现都是总数据量都增加了1条,说明这种情况下,集群节点之间能够自动同步 上面是通过向分布式表cluster1s2r_all插入数据,如果通过本地表cluster1s2r_local,数据还能同步吗? 在node01上往cluster1s2r_local插入1条数据; 查询node02,cluster1s2r_local数据没有同步 综上所述,通过分布表写入数据,会自动同步数据;而通过本地表表写入数据,不会同步;一般i情况下是没什么大问题。 但是生产情况总比理论复杂的多,以上配置可能会存在数据不一致的问题 官方文档描述如下: Each shard can have the internal_replication parameter defined in the config file. If this parameter is set to true, the write operation selects the first healthy replica and writes data to it. Use this alternative if the Distributed table “looks at” replicated tables. In other words, if the table where data will be written is going to replicate them itself. If it is set to false (the default), data is written to all replicas. In essence, this means that the Distributed table replicates data itself. This is worse than using replicated tables, because the consistency of replicas is not checked, and over time they will contain slightly different data. 翻译如下: 分片可在配置文件中定义 ‘internal_replication’ 参数。 此参数设置为«true»时,写操作只选一个正常的副本写入数据。如果分布式表的子表是复制表(*ReplicaMergeTree),请使用此方案。换句话说,这其实是把数据的复制工作交给实际需要写入数据的表本身而不是分布式表。 若此参数设置为«false»(默认值),写操作会将数据写入所有副本。实质上,这意味着要分布式表本身来复制数据。这种方式不如使用复制表的好,因为不会检查副本的一致性,并且随着时间的推移,副本数据可能会有些不一样。 简单理解如下: 这个为true代表zk会挑选一个合适的节点写入,然后在后台进行多个节点之间数据的同步. 如果是false,则是一次性写入所有节点,以这种重复写入的方法实现节点之间数据的同步. 自动数据备份 自动数据备份是表的行为,引擎为 ReplicatedXXX的表支持自动同步。 Replicated前缀只用于MergeTree系列(MergeTree是最常用的引擎)。 重点说明: Replicated表自动同步与之前的集群自动同步不同,是表的行为,与metrika.xml中的<clickhouse_remote_servers>配置没有关系,只要有zookeeper配置就行了。 node01修改metrika.xml配置 <yandex> <zookeeper-servers> <node index="1"> <host>node01</host> <port>2181</port> </node> <node index="2"> <host>node02</host> <port>2181</port> </node> <node index="3"> <host>node03</host> <port>2181</port> </node> </zookeeper-servers> </yandex> 将修改后的配置分发到node02机器上 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:$PWD metrika.xml 重启`clickhouse-server`,由于之前的表存在导致启动是失败。`error`日志 ​```shell [root@node01 clickhouse-server]# tail -f /var/log/clickhouse-server/clickhouse-server.err.log 7. DB::StorageDistributed::startup() @ 0x10f1bd40 in /usr/bin/clickhouse 8. ? @ 0x1151d922 in /usr/bin/clickhouse 9. ThreadPoolImpl<ThreadFromGlobalPool>::worker(std::__1::__list_iterator<ThreadFromGlobalPool, void*>) @ 0xa43d6ad in /usr/bin/clickhouse 10. ThreadFromGlobalPool::ThreadFromGlobalPool<void ThreadPoolImpl<ThreadFromGlobalPool>::scheduleImpl<void>(std::__1::function<void ()>, int, std::__1::optional<unsigned long>)::'lambda1'()>(void&&, void ThreadPoolImpl<ThreadFromGlobalPool>::scheduleImpl<void>(std::__1::function<void ()>, int, std::__1::optional<unsigned long>)::'lambda1'()&&...)::'lambda'()::operator()() const @ 0xa43dd93 in /usr/bin/clickhouse 11. ThreadPoolImpl<std::__1::thread>::worker(std::__1::__list_iterator<std::__1::thread, void*>) @ 0xa43cc4d in /usr/bin/clickhouse 12. ? @ 0xa43b3ff in /usr/bin/clickhouse 13. start_thread @ 0x7ea5 in /usr/lib64/libpthread-2.17.so 14. clone @ 0xfe8dd in /usr/lib64/libc-2.17.so (version 20.8.3.18) 2020.11.29 14:43:01.163530 [ 3643 ] {} <Error> Application: DB::Exception: Requested cluster 'cluster_1shards_2replicas' not found: while loading database `default` from path /var/lib/clickhouse/metadata/default 删除之前的建表语句 [root@node01 default]# rm -rf /var/lib/clickhouse/metadata/default/*.sql 启动clickhouse-server 在node01和node02节点上创建数据库表 -- node01 节点 CREATE TABLE `cluster_zk` ( `id` Int32, `website` String, `wechat` String, `FlightDate` Date, Year UInt16 ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/cluster_zk', 'replica01', FlightDate, (Year, FlightDate), 8192); -- node02节点 CREATE TABLE `cluster_zk` ( `id` Int32, `website` String, `wechat` String, `FlightDate` Date, Year UInt16 ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/cluster_zk', 'replica02', FlightDate, (Year, FlightDate), 8192); node01节点上插入数据 INSERT INTO default.cluster_zk (id,website,wechat,FlightDate,Year)values(1,'https://niocoder.com/','java干货','2020-11-28',2020); node01,node02节点上查询数据 node01 :) select * from cluster_zk; # node01节点 SELECT * FROM cluster_zk ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 1 rows in set. Elapsed: 0.004 sec. node02 :) select * from cluster_zk; # node02节点 SELECT * FROM cluster_zk ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 1 rows in set. Elapsed: 0.004 sec. 查询zk信息 [zk: localhost:2181(CONNECTED) 2] ls /clickhouse/tables/cluster_zk/replicas [replica02, replica01] [zk: localhost:2181(CONNECTED) 3] 自动数据备份集群配置 node01修改metrika.xml配置, 注意此处internal_replication为true <yandex> <clickhouse_remote_servers> <perftest_1shards_2replicas> <shard> <internal_replication>true</internal_replication> <replica> <host>node01</host> <port>9000</port> </replica> <replica> <host>node02</host> <port>9000</port> </replica> </shard> </perftest_1shards_2replicas> </clickhouse_remote_servers> <zookeeper-servers> <node index="1"> <host>node01</host> <port>2181</port> </node> <node index="2"> <host>node02</host> <port>2181</port> </node> <node index="3"> <host>node03</host> <port>2181</port> </node> </zookeeper-servers> </yandex> 将修改后的配置分发到node02机器上 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:$PWD metrika.xml 查询集群信息 node01 :) select * from system.clusters; SELECT * FROM system.clusters ┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐ │ perftest_1shards_2replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ perftest_1shards_2replicas │ 1 │ 1 │ 2 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │ └───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘ 10 rows in set. Elapsed: 0.018 sec. 创建分布式表 CREATE TABLE default.clusterzk_all AS cluster_zk ENGINE = Distributed(perftest_1shards_2replicas, default, cluster_zk, rand()); 分布式表查询数据 ode01 :) select * from clusterzk_all; SELECT * FROM clusterzk_all ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 1 rows in set. Elapsed: 0.020 sec. 分布式表写入 上文已经提到,internal_replication为true,则通过分布表写入数据时,会自动找到“最健康”的副本写入,然后其他副本通过表自身的复制功能同步数据,最终达到数据一致。 分片+数据备份整合 ip 主机 clickhouse 分片副本 192.168.10.100 node01 9000 01/01 192.168.10.100 node01 9001 03/02 192.168.10.100 node02 9000 02/01 192.168.10.100 node02 9001 01/02 192.168.10.100 node03 9000 03/01 192.168.10.100 node03 9001 02/02 3分片2副本. 在node01,node02,node03的9001端口再启动一个clickhouse-server实例。 即shard1的两个副本放到node01 9000、node02 9001两个机器上,shard2的两个副本放到node02 9000、node03 9001上,shard3的两个副本放到node03 9000、node01 9001上. node01创建并修改config1.xml [root@node01 clickhouse-server]# cp /etc/clickhouse-server/config.xml /etc/clickhouse-server/config1.xml [root@node01 clickhouse-server]# vim /etc/clickhouse-server/config1.xml 修改以下内容 <?xml version="1.0"?> <yandex> <!--省略其他 --> <http_port>8124</http_port> <tcp_port>9001</tcp_port> <mysql_port>9005</mysql_port> <interserver_http_port>9010</interserver_http_port> <log>/var/log/clickhouse-server/clickhouse-server-1.log</log> <errorlog>/var/log/clickhouse-server/clickhouse-server.err-1.log</errorlog> <!-- Path to data directory, with trailing slash. --> <path>/var/lib/clickhouse1/</path> <!-- Path to temporary data for processing hard queries. --> <tmp_path>/var/lib/clickhouse1/tmp/</tmp_path> <user_files_path>/var/lib/clickhouse1/user_files/</user_files_path> <format_schema_path>/var/lib/clickhouse1/format_schemas/</format_schema_path> <include_from>/etc/clickhouse-server/metrika1.xml</include_from> <!--省略其他 --> </yandex> node01创建并修改metrika.xml <yandex> <!--ck集群节点--> <clickhouse_remote_servers> <!--ck集群名称--> <perftest_3shards_2replicas> <shard> <internal_replication>true</internal_replication> <replica> <host>node01</host> <port>9000</port> </replica> <replica> <host>node02</host> <port>9001</port> </replica> </shard> <shard> <internal_replication>true</internal_replication> <replica> <host>node02</host> <port>9000</port> </replica> <replica> <host>node03</host> <port>9001</port> </replica> </shard> <shard> <internal_replication>true</internal_replication> <replica> <host>node03</host> <port>9000</port> </replica> <replica> <host>node01</host> <port>9001</port> </replica> </shard> </perftest_3shards_2replicas> </clickhouse_remote_servers> <!--zookeeper相关配置--> <zookeeper-servers> <node index="1"> <host>node01</host> <port>2181</port> </node> <node index="2"> <host>node02</host> <port>2181</port> </node> <node index="3"> <host>node03</host> <port>2181</port> </node> </zookeeper-servers> <macros> <shard>01</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node01</replica> <!--当前节点主机名--> </macros> <networks> <ip>::/0</ip> </networks> <!--压缩相关配置--> <clickhouse_compression> <case> <min_part_size>10000000000</min_part_size> <min_part_size_ratio>0.01</min_part_size_ratio> <method>lz4</method> <!--压缩算法lz4压缩比zstd快, 更占磁盘--> </case> </clickhouse_compression> </yandex> 复制metrika.xml文件为metrika1.xml,修改macros配置 node01``metrika.xml``macros配置 <macros> <shard>01</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node01</replica> <!--当前节点主机名--> </macros> node01``metrika1.xml``macros配置 <macros> <shard>03</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node01</replica> <!--当前节点主机名--> </macros> node02``metrika.xml``macros配置 <macros> <shard>02</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node02</replica> <!--当前节点主机名--> </macros> node02``metrika1.xml``macros配置 <macros> <shard>01</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node02</replica> <!--当前节点主机名--> </macros> node03``metrika.xml``macros配置 <macros> <shard>03</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node03</replica> <!--当前节点主机名--> </macros> node03``metrika.1xml``macros配置 <macros> <shard>02</shard> <!--分ID, 同一分片内的副本配置相同的分ID--> <replica>node03</replica> <!--当前节点主机名--> </macros> 创建并修改clickhouse-server-1 [root@node01 clickhouse-server]# cp /etc/rc.d/init.d/clickhouse-server /etc/rc.d/init.d/clickhouse-server-1 You have new mail in /var/spool/mail/root [root@node01 clickhouse-server]# vim /etc/rc.d/init.d/clickhouse-server-1 修改以下内容 CLICKHOUSE_CONFIG=$CLICKHOUSE_CONFDIR/config1.xml CLICKHOUSE_PIDFILE="$CLICKHOUSE_PIDDIR/$PROGRAM-1.pid" 分发配置文件到node02和node03节点 [root@node01 clickhouse-server]# scp /etc/rc.d/init.d/clickhouse-server-1 node02:/etc/rc.d/init.d/ clickhouse-server-1 100% 11KB 4.0MB/s 00:00 You have new mail in /var/spool/mail/root [root@node01 clickhouse-server]# scp /etc/rc.d/init.d/clickhouse-server-1 node03:/etc/rc.d/init.d/ clickhouse-server-1 100% 11KB 4.0MB/s 00:00 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/config1.xml node02:$PWD config1.xml 100% 33KB 10.2MB/s 00:00 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/config1.xml node03:$PWD config1.xml 100% 33KB 9.7MB/s 00:00 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node02:$PWD metrika.xml 100% 2008 1.0MB/s 00:00 You have new mail in /var/spool/mail/root [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika.xml node03:$PWD metrika.xml 100% 2008 1.1MB/s 00:00 [root@node01 clickhouse-server]# [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika1.xml node02:$PWD metrika1.xml 100% 2008 1.0MB/s 00:00 [root@node01 clickhouse-server]# scp /etc/clickhouse-server/metrika1.xml node03:$PWD metrika1.xml 修改node02和node03的macros配置 启动ClickHouse实例 node01的clickhouse-server-1实例 node02的clickhouse-server-1实例 node03的clickhouse-server实例 node03的clickhouse-server-1实例 service clickhouse-server restart service clickhouse-server-1 restart 查看集群信息 node01 :) select * from system.clusters; SELECT * FROM system.clusters ┌─cluster───────────────────────────┬─shard_num─┬─shard_weight─┬─replica_num─┬─host_name─┬─host_address───┬─port─┬─is_local─┬─user────┬─default_database─┬─errors_count─┬─estimated_recovery_time─┐ │ perftest_3shards_2replicas │ 1 │ 1 │ 1 │ node01 │ 192.168.10.100 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ perftest_3shards_2replicas │ 1 │ 1 │ 2 │ node02 │ 192.168.10.110 │ 9001 │ 0 │ default │ │ 0 │ 0 │ │ perftest_3shards_2replicas │ 2 │ 1 │ 1 │ node02 │ 192.168.10.110 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ perftest_3shards_2replicas │ 2 │ 1 │ 2 │ node03 │ 192.168.10.120 │ 9001 │ 0 │ default │ │ 0 │ 0 │ │ perftest_3shards_2replicas │ 3 │ 1 │ 1 │ node03 │ 192.168.10.120 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ perftest_3shards_2replicas │ 3 │ 1 │ 2 │ node01 │ 192.168.10.100 │ 9001 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 1 │ 1 │ 1 │ 127.0.0.1 │ 127.0.0.1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards │ 2 │ 1 │ 1 │ 127.0.0.2 │ 127.0.0.2 │ 9000 │ 0 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_cluster_two_shards_localhost │ 2 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_shard_localhost_secure │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9440 │ 0 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 1 │ 1 │ 1 │ localhost │ ::1 │ 9000 │ 1 │ default │ │ 0 │ 0 │ │ test_unavailable_shard │ 2 │ 1 │ 1 │ localhost │ ::1 │ 1 │ 0 │ default │ │ 0 │ 0 │ └───────────────────────────────────┴───────────┴──────────────┴─────────────┴───────────┴────────────────┴──────┴──────────┴─────────┴──────────────────┴──────────────┴─────────────────────────┘ 14 rows in set. Elapsed: 0.019 sec. 测试分片+副本集群 创建可复制表,node01节点执行即可,其他节点会自动创建。 CREATE TABLE `cluster32r_local` ON cluster perftest_3shards_2replicas ( `id` Int32, `website` String, `wechat` String, `FlightDate` Date, Year UInt16 ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/ontime','{replica}', FlightDate, (Year, FlightDate), 8192); node01 :) CREATE TABLE `cluster32r_local` ON cluster perftest_3shards_2replicas :-] ( :-] `id` Int32, :-] `website` String, :-] `wechat` String, :-] `FlightDate` Date, :-] Year UInt16 :-] ) :-] ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/ontime','{replica}', FlightDate, (Year, FlightDate), 8192); CREATE TABLE cluster32r_local ON CLUSTER perftest_3shards_2replicas ( `id` Int32, `website` String, `wechat` String, `FlightDate` Date, `Year` UInt16 ) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{shard}/ontime', '{replica}', FlightDate, (Year, FlightDate), 8192) ┌─host───┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐ │ node03 │ 9001 │ 0 │ │ 5 │ 0 │ │ node03 │ 9000 │ 0 │ │ 4 │ 0 │ │ node01 │ 9001 │ 0 │ │ 3 │ 0 │ │ node01 │ 9000 │ 0 │ │ 2 │ 0 │ │ node02 │ 9000 │ 0 │ │ 1 │ 0 │ └────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘ ┌─host───┬─port─┬─status─┬─error─┬─num_hosts_remaining─┬─num_hosts_active─┐ │ node02 │ 9001 │ 0 │ │ 0 │ 0 │ └────────┴──────┴────────┴───────┴─────────────────────┴──────────────────┘ 6 rows in set. Elapsed: 46.994 sec. 创建分布式表 CREATE TABLE cluster32r_all AS cluster32r_local ENGINE = Distributed(perftest_3shards_2replicas, default, cluster32r_local, rand()); 往第一个shard的副本插入数据(node01 9000),可以在第二个副本中查看数据(node02 9001) INSERT INTO default.cluster32r_local (id,website,wechat,FlightDate,Year)values(1,'https://niocoder.com/','java干货','2020-11-28',2020); INSERT INTO default.cluster32r_local (id,website,wechat,FlightDate,Year)values(2,'http://www.merryyou.cn/','javaganhuo','2020-11-28',2020); 使用客户端链接node02 9001实例查看 [root@node02 ~]# clickhouse-client --port 9001 -m ClickHouse client version 20.8.3.18. Connecting to localhost:9001 as user default. Connected to ClickHouse server version 20.8.3 revision 54438. node02 :) show tables; SHOW TABLES ┌─name─────────────┐ │ cluster32r_local │ └──────────────────┘ 1 rows in set. Elapsed: 0.010 sec. node02 :) select * from cluster32r_local; SELECT * FROM cluster32r_local ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 2 rows in set. Elapsed: 0.018 sec. 分布式表查询 node01 :) select * from cluster32r_all; SELECT * FROM cluster32r_all ┌─id─┬─website─────────────────┬─wechat─────┬─FlightDate─┬─Year─┐ │ 2 │ http://www.merryyou.cn/ │ javaganhuo │ 2020-11-28 │ 2020 │ └────┴─────────────────────────┴────────────┴────────────┴──────┘ ┌─id─┬─website───────────────┬─wechat───┬─FlightDate─┬─Year─┐ │ 1 │ https://niocoder.com/ │ java干货 │ 2020-11-28 │ 2020 │ └────┴───────────────────────┴──────────┴────────────┴──────┘ 2 rows in set. Elapsed: 0.030 sec. 所有副本节点均可本地表和分布式表均可读写数据 下载 关注微信公众号java干货回复 【clickhouse】

资源下载

更多资源
Mario

Mario

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

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

用户登录
用户注册