Intellij IDEA技巧-正则查询替换和纵向编辑

IntelliJ IDEA是一款非常强大的Java IDE,当然现在也支持多种语言,由JetBrains公司开发。IntelliJ IDEA在业界被公认为最好的java开发工具之一,尤其在智能代码助手、代码自动提示、重构、J2EE支持、各类版本工具(git、svn、github等)、JUnit、CVS整合、代码分析、 创新的GUI设计等方面的功能可以说是超常的。 使用IDEA的感觉就是,一开始可能不习惯(与Eclipse的设计思想完全不同),但是越用就会发现越强大!这里介绍IDEA的正则查询替换和纵向编辑两个非常强大的功能。 1. 正则查找替换 正则查找替换即:支持使用正则表达式来匹配或替换文本,并且可以通过<code>$n</code>来访问正则的分组。 举个例子:我想要将Mapper取值表达式后边的jdbcType=XXX去掉,怎么做?没有正则,那么我们只能使用多次替换。使用正则,那么我们只需将#{xxx,jdbctye=XXX}部分查询出来,并且按分组替换即可,如下图所示: 在IDEA按快捷键CTRL+R开启查找替换功能,输入正则{(w*),(w*=w*)},此时IDEA会自动高亮匹配的内容。然后再替换框中输入{},即替换后的内容保留匹配正则的第一个分组,此时,IDEA会有一个灰色的pop框来告诉你,该内容会被替换成什么内容(见图中的#{rank})。然后,直接点击replace all替换全部替换即可,也可以使用exclude和replace一个个排除和替换。 正则替换功能适用于需要在某些有规律的内容上进行编辑,只要正则能够匹配到这部分内容。 2. 纵向编辑 纵向编辑是我个人非常喜欢的功能之一,其实很多编辑器(例如VIM、SUBLIME)都支持。在IDEA中,要开启纵向编辑,先按住ALT键不松开,然后按住鼠标竖向移动选择即可。 同样举个简单的例子:将设我从某些地方拷贝了一串属性,现在想要将其定义到具体的Class属性中,如图所示: 假设字段类型都是String,我们需要做以下几步: 1、将首字母变为小写 2、在属性前边添加private String 3、在属性后边添加分号 我们看看使用纵向编辑怎么做: 如上图所示,先纵向选择过后,按HOME键,将光标跳转每一行首,然后SHIFT+→选择每一行首字母,再CTRL+SHIFT+U将选中部分转为大小写,然后在每一行前边添加内容,完成后按End键将光标跳转到每一行尾,输入分号,结束。整个过程中,光标始终处于纵向编辑状态。 这里用到了几个小技巧:SHIFT+左右箭头,可以选择一个字符;CTRL+SHIFT+左右箭头,可以选择一个单词;HOME和END分别跳转到行首和行尾。 接下来,我们看一个实际的综合使用这两个功能的例子。 3. 综合实例 3.1. 需求 我从API文档拷贝了请求API的内容片段,现在,我需要为这些字段建立Class,内容片段如下: "ParkOrder_ID": 131391,//订单ID "ParkOrder_OrderNo": "20170209150056708-B11110",//订单号码 "Parking_ID": 1,//停车场ID "Parking_Enable": 1,//停车场状态 "Parking_Key": "e0b3ca87caf3a415bb3b3f52ca8aa795",//停车场编码 "Parking_Name": "城市明珠停车场1",//停车场名字 "Parking_FreeTime": 2,//首次免费分钟 "Parking_FreeTimeout": 2,//超时免费分钟 "ParkOrder_CarNo": "粤B11110",//车牌号码 "ParkOrder_CarType": "3651",//停车缴费类型 "CarType_No": "3651",//车辆类型 "CarType_Name": "临时车",//车辆类型名字 "ParkOrder_EnterTime": "2017-02-09 15:00:56",//入场时间 "ParkOrder_EnterGateName": "入口车道1",//入口车道名字 "ParkOrder_EnterOperatorName": "管理员",//入场操作员 "ParkOrder_EnterImgPath": "/OrderImg/e0b3ca87caf3a415bb3b3f52ca8aa795/20170209/20170209150056708-B11110-0.jpg",//图片URL "ParkOrder_OutTime": null,//出场时间 "ParkOrder_OutGateName": null,//出口车道名字 "ParkOrder_OutOperatorName": null,//出口操作员名字 "ParkOrder_OutImgPath": null,//出口图片URL "ParkOrder_TotalAmount": null,//总金额 "ParkOrderStatus_No": "200",//停车状态200为 车辆入场 201为车辆出场 202为自动关闭 "ParkOrderStatus_Name": "已入场",//状态中文名字 "ParkOrder_Lock": 0,//车辆是否锁定 "WXUser_ID": null,//微信ID "WXUser_Openid": null,//微信openid "WXUser_Nickname": null,//微信nikename "WXUser_Headimg": null,//头像url "PayOrder_Status": "未支付",//支付状态 "PayOrder_PayedMoney": 0.0//支付金额 ...

2018-05-31 · 2 min · 394 words · Hank

微服务架构的理论基础 - 康威定律

可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律。 1. 概述 微服务是最近非常火热的新概念,大家都在追,也都觉得很对,但是似乎没有很充足的理论基础说明这是正确的,给人的感觉是 不明觉厉 。前段时间看了Mike Amundsen《远距离条件下的康威定律----分布式世界中实现团队构建》(是Design RESTful API的作者)在InfoQ上的一个分享,觉得很有帮助,结合自己的一些思考,整理了该演讲的内容。 可能出乎很多人意料之外的一个事实是,微服务很多核心理念其实在半个世纪前的一篇文章中就被阐述过了,而且这篇文章中的很多论点在软件开发飞速发展的这半个世纪中竟然一再被验证,这就是康威定律(Conway’s Law). 在康威的这篇文章中,最有名的一句话就是: Organizations which design systems are constrained to produce designs which are copies of the communication structures of these organizations. — Melvin Conway(1967) 中文直译大概的意思就是:设计系统的组织,其产生的设计等同于组织之内、组织之间的沟通结构**。看看下面的图片,再想想Apple的产品、微软的产品设计,就能形象生动的理解这句话。 用通俗的说法就是:组织形式等同系统设计。 这里的系统按原作者的意思并不局限于软件系统。据说这篇文章最初投的哈佛商业评论,结果程序员屌丝的文章不入商业人士的法眼,无情被拒,康威就投到了一个编程相关的杂志,所以被误解为是针对软件开发的。最初这篇文章显然不敢自称定律(law),只是描述了作者自己的发现和总结。后来,在Brooks Law著名的人月神话中,引用这个论点,并将其“吹捧”成了现在我们熟知“康威定律”。 2. 康威定律 Mike从他的角度归纳这篇论文中的其他一些核心观点,如下: 第一定律 Communication dictates design 组织沟通方式会通过系统设计表达出来 第二定律 There is never enough time to do something right, but there is always enough time to do it over 时间再多一件事情也不可能做的完美,但总有时间做完一件事情 第三定律 There is a homomorphism from the linear graph of a system to the linear graph of its design organization 线型系统和线型组织架构间有潜在的异质同态特性 ...

2018-05-29 · 2 min · 252 words · Hank

什么是微服务架构

在过去几年中,“微服务架构”这一术语如雨后春笋般涌现出来,它描述了一种将软件应用程序设计为一组可独立部署的服务的特定方式。虽然这种架构风格没有明确的定义,但在组织、业务能力上有一些共同的特征:自动化部署,端点智能化,语言和数据的去中心化控制。 “微服务” - 软件架构拥挤大街上的有一个新术语。虽然我们自然的倾向是轻蔑的一瞥将它一带而过,然而我们发现这一术语描述了一种越来越吸引人的软件系统风格。我们已看到,在过去的几年中有许多项目使用了这种风格,并且到目前为止结果都还不错,以致于这已变成了我们同事在构建企业级应用程序时默认使用的架构风格。然而,遗憾的是并没有太多的信息来概述什么是微服务风格以及怎样用这种风格。 简单来说,微服务架构风格 [1] 是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。 与单体风格作对比有助于开始解释微服务风格:单体应用程序被构建为单一单元。企业级应用程序通常由三部分组成:客户端侧用户接口(由运行于开发机上的浏览器里的HTML页面和Javascript组成),数据库(由插入到通用关系型数据库管理系统中的许多数据表格组成),服务端应用程序。服务端应用程序处理HTTP请求,执行领域逻辑,从数据库中检索、更新数据,选择、填充将要发送到浏览器的HTTP视图。服务端应用程序是一个单一的逻辑可执行单体 [2] 。系统的任何改变都将牵涉到重新构建和部署服务端的一个新版本。 这样的单体服务器是构建这样一个系统最自然的方式。处理请求的所有逻辑都运行在一个单一进程中,允许你使用编程语言的基本特性将应用程序划分类、函数和命名空间。你认真的在开发机上运行测试应用程序,并使用部署管道来保证变更已被正确地测试并部署到生产环境中。该单体的水平扩展可以通过在负载均衡器后面运行多个实例来实现。 单体应用程序可以是成功的,但人们日益对他们感到挫败,尤其是随着更多的应用程序被部署在云上。变更周期被捆绑在一起 —— 即使只变更应用程序的一部分,也需要重新构建并部署整个单体。长此以往,通常将很难保持一个良好的模块架构,这使得很难变更只发生在需要变更的模块内。程序扩展要求进行整个应用程序的扩展而不是需要更多资源的应用程序部分的扩展。 Figure 1. 单体和微服务 这些挫败导向了微服务架构风格:构建应用程序为服务套件。除了服务是可独立部署、可独立扩展的之外,每个服务都提供一个固定的模块边界。甚至允许不同的服务用不同的的语言开发,由不同的团队管理。 我们不会声称微服务风格是新颖的、创新的,其本质至少可以回溯到Unix的设计哲学。但我们的确认为没有足够的人仔细考虑微服务架构,并且如果使用它很多软件实现将会更好。 1. 微服务架构的特征 我们无法给出微服务架构风格的一个正式定义,但我们可以尝试去描述我们看到的符合该架构的一些共性。就概述共性的任何定义来说,并非所有的微服务架构风格都有这些共性,但我们期望大多数微服务架构风格展现出大多数特性。虽然本文作者一直是这个相当松散的社区的活跃用户,我们的目的是试图描述我们工作中和我们知道的一些团队的相似努力中的所见所闻。特别是我们不会制定一些可遵守的定义。 1.1. 通过服务组件化 只要我们一直从事软件行业,一个愿望就是通过把组件插在一起构建系统,如同我们看到的现实世界中事物的构造方式一样。在最近的二十年中,我们看到作为大多数语言平台一部分的公共库的大量汇编工作取得了很大的进展。 当谈到组件时,我们遭遇困难的定义:组件是什么。我们的定义是:组件是一个可独立替换和独立升级的软件单元。 微服务架构将使用库,但组件化软件的主要方式是分解成服务。我们把库定义为链接到程序并使用内存函数调用来调用的组件,而服务是一种进程外的组件,它通过web服务请求或rpc(远程过程调用)机制通信(这和很多面向对象程序中的服务对象的概念是不同的 [3] 。) 使用服务作为组件而不是使用库的一个主要原因是服务是可独立部署的。如果你有一个应用程序 [4] 是由单一进程里的多个库组成,任何一个组件的更改都导致必须重新部署整个应用程序。但如果应用程序可分解成多个服务,那么单个服务的变更只需要重新部署该服务即可。当然这也不是绝对的,一些变更将会改变服务接口导致一些协作,但一个好的微服务架构的目的是通过内聚服务边界和按合约演进机制来最小化这些协作。 使用服务作为组件的另一个结果是一个更加明确的组件接口。大多数语言没有一个好的机制来定义一个明确的 发布接口。通常只有文档和规则来预防客户端打破组件的封装,这导致组件间过于紧耦合。服务通过明确的远程调用机制可以很容易的避免这些。 像这样使用服务确实有一些缺点,远程调用比进程内调用更昂贵,因此远程API被设计成粗粒度,这往往更不便于使用。如果你需要更改组件间的责任分配,当你跨进程边界时,这样的行为动作更难达成。 直观的估计,我们观察到服务与运行时进程一一映射,但这仅仅是直观的估计而已。一个服务可能由多进程组成,这些进程总是被一起开发和部署,比如只被这个服务使用的应用进程和数据库。 1.2. 围绕业务能力组织 当想要把大型应用程序拆分成部件时,通常管理层聚焦在技术层面,导致UI团队、服务侧逻辑团队、数据库团队的划分。当团队按这些技术线路划分时,即使是简单的更改也会导致跨团队的时间和预算审批。一个聪明的团队将围绕这些优化,两害取其轻 - 只把业务逻辑强制放在它们会访问的应用程序中。换句话说,逻辑无处不在。这是Conway法则 [5] 在起作用的一个例子。 任何设计系统(广泛定义的)的组织将产生一种设计,他的结构就是该组织的通信结构。 — Melvyn Conway 1967 Figure 2. Conway法则在起作用 微服务采用不同的分割方法,划分成围绕业务能力组织的服务。这些服务采取该业务领域软件的宽栈实现,包括用户接口、持久化存储和任何外部协作。因此,团队都是跨职能的,包括开发需要的全方位技能:用户体验、数据库、项目管理。 Figure 3. 团队边界增强的服务边界 www.comparethemarket.com是按这种方式组织的一个公司。跨职能团队负责创建和运营产品,产品被划分成若干个体服务,这些服务通过消息总线通信。 大型单体应用程序也总是可以围绕业务能力来模块化,虽然这不是常见的情况。当然,我们将敦促创建单体应用程序的大型团队将团队本身按业务线拆分。我们看到这种情况的主要问题是他们趋向于围绕太多的上下文进行组织。如果单体横跨了多个模块边界,对团队个体成员来说,很难把它们装进他们的短期记忆里。另外,我们看到模块化的路线需要大量的规则来强制实施。服务组件所要求的更加明确的分离,使得它更容易保持团队边界清晰。 侧边栏:微服务有多大? 虽然,“微服务”已成为这种架构风格的代称,这个名字确实会导致不幸的聚焦于服务的大小,并为“微”由什么组成争论不休。在与微服务实践者的对话中,我们发现有各种大小的服务。最大的服务报道遵循亚马逊两匹萨团队(也就是,整个团队吃两个披萨就吃饱了)的理念,这意味着团队不超过12个人。在更小的规模大小上,我们看到这样的安排,6人团队将支持6个服务。 这导致这样一个问题,在服务每12个人和服务每1个人的大小范围内,是否有足够打的不同使他们不能被集中在同一微服务标签下。目前,我们认为最好把它们组合在一起。但随着深入探索这种风格,我们一定有可能改变我们的看法。 1.3. 是产品不是项目 我们看到大多数应用程序开发工作使用一个项目模式:目标是交付将要完成的一些软件。完成后的软件被交接给维护组织,然后它的构建团队就解散了。 微服务支持者倾向于避免这种模式,而是认为一个团队应该负责产品的整个生命周期。对此一个共同的启示是亚马逊的理念 “you build, you run it” ,开发团队负责软件的整个产品周期。这使开发者经常接触他们的软件在生产环境如何工作,并增加与他们的用户联系,因为他们必须承担至少部分的支持工作。 产品思想与业务能力紧紧联系在一起。要持续关注软件如何帮助用户提升业务能力,而不是把软件看成是将要完成的一组功能。 没有理由说为什么同样的方法不能用在单体应用程序上,但服务的粒度更小,使得它更容易在服务开发者和用户之间建立个人关系。 ...

2018-05-29 · 2 min · 343 words · Hank

Spring Boot之基础入门

最近在了解微服务和Spring Cloud相关的东西,Spring Cloud的微服务体系基于Spring boot,所以了解Spring Boot是前提和基础。本文将介绍Spring Boot基础内容,包括简介、环境搭建、示例代码等,所使用JDK版本为JDK8,构建工具为Maven3.5。 1. 简介 Spring Boot是Spring的开源项目,其目的是让开发者可以简单、快速地使用Spring框架创建生产级的应用,其目的不是为了替换Spring,而是简化Spring的使用。Spring Boot可以看做是对Spring框架的二次封装,不过,在封装的同时也还提供了许多高级功能。 Spring Boot特性如下: 创建独立的Spring应用程序 直接嵌入Tomcat、Jetty或Undertow(无需部署WAR文件) 为项目构建提供了许多的“starter”,适用于整合不同的框架,从而简化您的构建工具(Maven)配置 尽可能自动配置Spring 提供可生产的特性,如度量指标、健康检查和集中配置 绝对没有代码生成,也不需要XML配置 2. 系统需求 Spring Boot需要JDK8或以上版本,嵌入的tomcat8.5所支持的Servlet版本为3.1,如下表所示: 依赖 版本 JDK 8或以上版本 Maven 3.2或以上版本 Servlet 3.1+ 3. 构建应用 现在,我们来一步步构建一个Hello world应用。 3.1. 构建 Spring Boot项目是基于Maven构建,完全遵循Maven的项目构建规则和目录规范。所以,你可以直接使用开发工具或者maven命令行来创建Spring Boot项目。需要注意的饿是,如果是Web项目,而且打包的格式为jar,那么与标准Maven项目不同的是不能使用src/main/webapp目录。 也可以访问 https://start.spring.io/来快速构建应用,选择对应的构建工具、语言和版本信息,填写开发信息并加入依赖下载,然后导入开发工具即可: 3.2. 配置Pom.xml 应用创建好了,我们来配置pom.xml,引入Spring boot相关的依赖。 1、继承parent节点 Spring为我们提供了顶层的parent节点,用来定义Spring Boot使用的版本,配置如下: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.10.RELEASE</version> <relativePath/> </parent> ...

2018-05-22 · 2 min · 327 words · Hank

XMind8 pro版本下载和破解

1. 简介 XMind是一款比较易用的思维导图软件,开源且功能强大,有免费版本,能够满足基本使用。如果需要使用更高级的功能,可以付费购买收费版本。 官网: https://www.xmind.net/ 2. 下载和破解 1、从官网下载Xmind8 pro,或者可以从百度云盘下载: 链接 https://pan.baidu.com/s/1OkSXoqcZA4unFbR1Crzl4w 密码 wat9 2、安装软件到指定目录 3、下载破解文件: 链接 https://pan.baidu.com/s/1KJ0hkB_rl4qv1MS3a8L1Aw 密码 bgv4 4、开始破解,步骤如下: (1)将破解文件XMindCrack.jar复制到安装根目录, 如: D:\Program Files\XMind 8 Update 32,以文本格式打开安装目录中XMind.ini (2)在 XMind.ini 最后追加: -javaagent:D:/Program Files/XMind 8 Update 3/XMindCrack.jar (3)打开C:\Windows\System32\drivers\etc路径,在hosts最后添加 127.0.0.1 www.xmind.net点击保存 (4)断网情况下,打开XMind8→帮助→序列号→输入序列号,邮箱可以随便填写,序列号在附件内,点击注册即可完成破解! (5)打开 XMind, 点击帮助----序列号,然后输入以下序列号 ,邮箱任意填写: XAka34A2rVRYJ4XBIU35UZMUEEF64CMMIYZCK2FZZUQNODEKUHGJLFMSLIQMQUCUBXRENLK6NZL37JXP4PZXQFILMQ2RG5R7G4QNDO3PSOEUBOCDRYSSXZGRARV6MGA33TN2AMUBHEL4FXMWYTTJDEINJXUAV4BAYKBDCZQWVF3LWYXSDCXY546U3NBGOI3ZPAP2SO3CSQFNB7VVIY123456789012345 5、破解完成,可以使用任意的Pro版本的功能了,授权信息如下: 此时XMind是无法联网使用的,如果需要使用诸如在线图标的功能,需要恢复联网,即取消hosts的修改内容,建议关闭软件自动更新。 3. 对比分析 之前博主曾介绍过另一款 思维导图软件MindManager,个人觉得也非常强大易用,只是没有中文版本(较早的版本有中文版,喜欢的可以查阅相关资料),这两者的文字对比分析可以看 这里,这里不再详述。 有大神对三款思维导图软件做了详细分析对比,见下图: 图片来源: https://www.zhihu.com/question/22094277 4. 申明 本站提供的破解软件仅供个人学习用,切勿用于商业用途! 本站不对使用破解软件造成的任何损失承担责任! 经济允许的话,请支持正版软件!

2018-05-11 · 1 min · 59 words · Hank

使用visualvm监控Java程序性能三——浏览堆dump文件

堆dump,即堆转储,其实是一个当前JVM堆内存所有对象在某个时间点上的快照。堆dump用来将当前应用程序的堆内存中的数据信息保存为文件,并可以快速浏览文件中的内容,查询对象分配信息。 在VisualVM中,堆dump的入口很多,可以再应用程序右键,选择堆dump;也可以打开主窗口的“监视”tab页,点击右上角有堆dump按钮;还可以打开抽样器tab页,进行内存抽样后,点击工具栏上的堆dump按钮。 1. 打开堆dump 执行完堆dump后,主窗口会打开一个堆dump的页面,同时应用程序节点会增加一个headdump+时间子节点,可以直接打开该节点来浏览堆dump文件信息,如图所示: 堆dump文件存储后缀为.hprof,除了能够看到本地堆dump文件,也可以通过菜单栏的文件-装入打开他人分享的dump文件: 2. 浏览堆dump文件信息 堆dump tab页包含了几个子标签页面:概要、类、实例、OQL控制台,我们重点说下前三个。 2.1. 概要 显示了转储堆dump时的信息,包括基本信息、环境信息、系统属性和线程信息,例如你可以看到堆dump时间、文件存放位置、大小等。 2.2. 类 类视图显示类的列表,以及该类引用的实例的数量和百分占比、类所有实例的大小和百分占比。您可以通过在实例视图中右键单击名称和选择“在实例图中显示”或者直接双击类来查看特定类的实例列表: 通过类视图,可以整体上明确哪些类实例数多,占用资源高。 可以通过最下方的“类目过滤器”来搜索类,点击漏斗形状的图标可以选择筛选类型,例如包含/不包含类名称、正则匹配、搜索子类等。例如,搜索类名包含EsSyncTask的所有类,如下图所示: 2.3. 实例数 当在类视图选择查询具体某一个类的实例时(右键或者双击),此时就打开了该类的实例视图。当您从实例窗格中选择一个实例时,VisualVM将显示该类的字段,并在各自的窗格中引用该类。 除了显示了具体的类和概要信息(实例数量、大小、总大小等)外,实例视图还有几个面板: 实例数:显示当前类的实例列表,点击具体的某一个实例可以在字段和引用面板查看实例的字段信息和引用了的对象 字段:显示当前选择的实例的具体字段,包括字段名称、类型、字段值等 引用:递归显示当前所有引用了该类的类实例,该面板会一层层递归显示引用,包括引用实例的再引用。通过引用面板,可以跟踪对象的引用情况,以便找到具体的类,从而优化代码。 通过右键点击实例或者在引用面板右键,可以查看最近垃圾回收根节点。 接下来,我们来编一个实例程序,看看堆dump的情况,代码如下: public class HeaddumpTest { public static void main(String[] args) { Refrence ref = new Refrence(); int id = 0; while (true) { ref.add(new MyClass(id++, "myclass" + id)); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Refrence { private List<MyClass> myClasses = new ArrayList<>(); public void add(MyClass myClass) { myClasses.add(myClass); } public List<MyClass> getMyClasses() { return myClasses; } public void setMyClasses(List<MyClass> myClasses) { this.myClasses = myClasses; } } class MyClass { private int id; private String name; MyClass(int id, String name) { this.id = id; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ...

2018-04-24 · 1 min · 168 words · Hank

使用visualvm监控Java程序性能二——主窗口功能详解

无论是本地还是远程节点,双击或者右键打开应用程序,会直接在主窗口打开程序监控页。主窗口包括几个子页面:概述、监视、线程、抽样器,如果是本地程序且支持分析器还会显示Profiler页,根据安装插件的不同,还可能会显示一些插件的页面,例如MBeas等。 Figure 1. 主窗口界面 1. 概述页 概述页显示了应用程序和运行时环境的基本信息,如下图所示: Figure 2. 概述页界面 概述信息包括基本参数、保存的数据、详细信息几个部分。 1.1. 基本参数 基本参数描述了应用程序的一些参数信息,包括如下信息: PID:应用程序的进程ID 主机:应用程序运行的系统地址 主类:运行了main方法的类 参数:应用启动时所传递的参数信息,例如传递给main方法的参数列表 JVM:当前的JVM信息 Java:当前使用的JDK信息 Java Home:JDK的位置 JVM标志:启动JDK时JVM使用的的标志 出现OOME时生产堆dump:当前出现OOME时生产堆dump功能的开启/禁用状态 除了基本信息外,还包括两个可以自主选择显示或隐藏的功能(右上角):保存的数据和详细信息。 1.2. 保存的数据 显示VisualVM存储的当前应用程序的信息,例如线程dump的数量、堆dump和快照的数量等。 1.3. 详细信息 包括两个标签:JVM参数和系统属性。 JVM参数:配置的JVM启动的参数信息,例如堆大小; 系统属性:JVM运行的系统属性,例如用户目录、文件编码; 2. 监视页 监视tab页展示了监听了当前应用程序的整体情况,包括几个指标:CPU、内存、类、线程,它们都以直观的图形方式展现。 Figure 3. 监视页界面 2.1. CPU 该图展示了CPU的使用百分比走势,包括执行垃圾回收活动的时间等。可以从该图查看应用程序是否耗费CPU,是否频繁的进行垃圾回收,以便优化代码或者调整JVM内存设置。 2.2. 内存 反映了内存的占用情况,包括内存大小、最大值和已经使用的大小。内存情况又包括两部分:堆和Metaspace。 堆:该页展示了堆内存的大小和堆内存使用走势情况。 Metaspace:即元空间,JDK8移除了永久代(PermGen),而类的元数据信息被存储在了Metaspace,具体请看 这里。该标签反映了元空间内存的使用情况。 2.3. 类 类视图显示了已经加载的类数量和共享类的数量走势情况。 2.4. 线程 线程视图显示了应用程序在JVM中生存和守护线程的数量走势情况。如果您想在特定的时间点捕获和查看应用程序线程的精确数据,可以打开线程标签页,使用VisualVM进行线程转储(稍后详述)。 除了这四个图表,监视页还有两个重要的功能:执行垃圾回收和堆dump。 执行垃圾回收:立即触发垃圾回收 堆dump:执行堆dump,并在新的标签页打开,以查看对dump的详细信息(同右键应用程序–堆dump)。 通常,需要长期监控应用程序的情况,幸好这不会带来太大的开销。 我们看一个OOM的例子,看看VisualVM的监视页的监控情况。代码如下: public static void main(String[] args) { List<Object> objects = new ArrayList<>(); new Thread(() -> { while (true) { objects.add(new Object()); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } ...

2018-04-21 · 2 min · 402 words · Hank

使用VisualVM监控Java程序性能一——简介

Java应用程序难免遇到性能问题,例如常见的OutOfMemmoryError,又或者是出现内存泄漏的错误,又或是程序运行一段时间就卡死了,CPU或者内存占用率高,甚至造成系统崩溃,等等。出现性能问题时,往往难以分析和跟踪。不过,可以借助于一些性能分析工具,来监测程序性能,从而找出影响性能的问题所在,以便进行优化。本文的VisualVM就是一款比较强大的性能分析工具。 先来看看性能分析的几种方式: 性能分析的主要方式 性能分析常用的有以下几种方式 * 监视:监视是一种用来查看应用程序运行时行为的一般方法。通常会有多个视图(View)分别实时地显示 CPU 使用情况、内存使用情况、线程状态以及其他一些有用的信息,以便用户能很快地发现问题的关键所在。 * 转储(dump):性能分析工具从内存中获得当前状态数据并存储到文件用于静态的性能分析。Java 程序是通过在启动 Java 程序时添加适当的条件参数来触发转储操作的。它包括以下三种: 核心dump:JVM 生成的本地系统的转储。一般的,系统转储数据量大,需要平台相关的工具去分析,如 Windows 上的 windbg 和 Linux 上的 gdb。 Jvm dump:JVM 内部生成的格式化后的数据,包括线程信息,类的加载信息以及堆的统计数据。通常也用于检测死锁。 堆dump:JVM 将所有对象的堆内容存储到文件。 * 快照:应用程序启动后,性能分析工具开始收集各种运行时数据,其中一些数据直接显示在监视视图中,而另外大部分数据被保存在内部,直到用户要求获取快照,基于这些保存的数据的统计信息才被显示出来。快照包含了应用程序在一段时间内的执行信息,通常有 CPU 快照和内存快照两种类型。 CPU 快照:主要包含了应用程序中函数的调用关系及运行时间,这些信息通常可以在 CPU 快照视图中进行查看。 内存快照:主要包含了内存的分配和使用情况、载入的所有类、存在的对象信息及对象间的引用关系等。这些信息通常可以在内存快照视图中进行查看。 * 性能分析:性能分析是通过收集程序运行时的执行数据来帮助开发人员定位程序需要被优化的部分,从而提高程序的运行速度或是内存使用效率,主要有以下三个方面: CPU 性能分析:CPU 性能分析的主要目的是统计函数的调用情况及执行时间,或者更简单的情况就是统计应用程序的 CPU 使用情况。通常有 CPU 监视和 CPU 快照两种方式来显示 CPU 性能分析结果。 内存性能分析:内存性能分析的主要目的是通过统计内存使用情况检测可能存在的内存泄露问题及确定优化内存使用的方向。通常有内存监视和内存快照两种方式来显示内存性能分析结果。 线程性能分析:线程性能分析主要用于在多线程应用程序中确定内存的问题所在。一般包括线程的状态变化情况,死锁情况和某个线程在线程生命期内状态的分布情况等 — 使用 VisualVM 进行性能分析及调优 https://www.ibm.com/developerworks/cn/java/j-lo-visualvm 1. VisualVM简介 VisualVM是集成JDK命令行工具和轻量级分析功能的可视化分析工具,设计用于开发和生产时间的使用。它提供了一个可视化界面,用于查看基于Java技术、运行于JVM上的应用程序(Java应用程序)的详细信息。 VisualVM组织Java开发工具包(JDK)工具检索的JVM软件的数据,并以一种使您能够快速查看多个Java应用程序的数据的方式提供信息。您可以查看在远程主机上运行的本地应用程序和应用程序的数据。您还可以捕获有关JVM软件实例的数据,并将数据保存到您的本地系统中,以供后期查看或与其他用户共享。 VisualVM完全免费,它通过 jvmstat、JMX、SA(Serviceability Agent)以及 Attach API 等多种方式从程序运行时获得实时数据,从而进行动态的性能分析。同时,它能自动选择更快更轻量级的技术尽量减少性能分析对应用程序造成的影响,提高性能分析的精度。 2. VisualVM安装 从JDK6开始,VisualVM已经成为JDK自带工具,名称叫做jvisualvm,位于%JAVA_HOME%/bin/jvisualvm.exe,执行即可启动。 需要注意的是,从Oracle JDK 9开始,Java VisualVM迁移到GraalVM,这是在Oracle实验室开发的一种创新的、高性能的多语言虚拟机。详情请参阅 Graal VisualVM页面。 ...

2018-04-20 · 1 min · 114 words · Hank

RabbitMQ服务器管理(二)——权限管理

RabbitMQ有用一套专门的权限控制系统,用来控制不同用户对不同虚拟主机的访问控制。基本思路同大多系统一样:先创建用户,然后为用户授予权限。 大多数系统并没有严格区分认证和鉴权,但是在RabbitMQ中,对这两个概念做了明确的区分: 认证:识别用户身份,即:识别当前用户,认证其身份信息 鉴权:明确了用户身份,那么授权就是要检查该用户是否拥有相应的权限,即:检查授予用户的权限 1. 虚拟主机和guest用户 在 RabbitMQ基础(七)----虚拟主机vhost一篇,我们已经详细介绍了虚拟主机。我们说过,首次安装好RabbitMQ时,会拥有一个默认的虚拟主机“/”。同时,还有一个默认的用户“guest”,密码也为“guest”,该用户有用默认虚拟主机的全部权限。 如果你得RabbitMQ需要公网访问,出于安全性考虑,官方建议删掉该用户或者修改其密码。默认情况下,RabbitMQ禁止guest用户远程访问,只可以访问本地的mq服务。这个是通过loopback_users配置项决定的,如果需要取消该限制,仅需将该选项配置为none即可: loopback_users = none 如果是3.7之前的版本,还不支持这样key=value的配置格式,那么你需要配置成这样: ``[{rabbit, [{loopback_users, []}]}].`` 该配置想的意思是,取消所有用户的本地访问限制。按照 官方的示例配置文件,如果仅需要取消guest用户本地访问限制,那么进行以下配置: loopback_users.guest = false 否则设置为true即可。 关于MQ的配置文件,我们将在后续博文中详细讨论,官方文档见 这里。 2. 权限工作机制 当客户端与服务端建立连接时,第一级权限控制将会执行:服务器会检查连接的用户是否有用访问其连接的虚拟主机的权限,没有则会拒绝连接,否则连接建立成功。 用户操作虚拟主机的资源(路由器、队列、绑定等)时,RabbitMQ会启用第二级权限控制,验证用户是否具有访问虚拟机的资源的权限。 2.1. 权限定义 具体而言,在RabbitMQ中,对资源的操作定义了三种权限: 配置:创建和删除资源,或者改变它们的行为; 写:发布消息到资源; 读:从资源获取消息; 下表显示了对执行权限检查的所有AMQP命令所需的资源类型的权限: AMQP 0-9-1 Operation configure write read exchange.declare (passive=false) exchange exchange.declare (passive=true) exchange.declare (with AE) exchange exchange (AE) exchange exchange.delete exchange queue.declare (passive=false) queue queue.declare (passive=true) queue.declare (with DLX) queue exchange (DLX) queue queue.delete queue exchange.bind exchange (destination) exchange (source) exchange.unbind exchange (destination) exchange (source) queue.bind queue exchange queue.unbind queue exchange basic.publish exchange basic.get queue basic.consume queue queue.purge queue 说明: ...

2018-04-12 · 3 min · 504 words · Hank

RabbitMQ服务器管理(一)——ubuntu上安装MQ

在本篇,我们将在ubuntu上安装RabbitMQ,其他操作系统类似。 我们知道,RabbitMQ使用Erlang语言开发,所以需要先安装Erlang语言,在实践过程中,linux安装RabbitMQ还比较麻烦,涉及到很多依赖包的安装,官方的安装文档见 这里。 1. 正确的安装步骤 简单而言,正确的安装步骤如下: 1、安装erlang依赖包 wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 编辑/etc/apt/sources.list,添加以下地址的任意一个 deb https://packages.erlang-solutions.com/ubuntu trusty contrib deb https://packages.erlang-solutions.com/ubuntu saucy contrib deb https://packages.erlang-solutions.com/ubuntu precise contrib 然后执行: wget https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb sudo dpkg -i erlang-solutions_1.0_all.deb 2、安装erlang sudo apt-get update sudo apt-get install esl-erlang(或erlang) 3、下载RabbitMQ wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.7.12/rabbitmq-server_3.7.12-1_all.deb 4、安装RabbitMQ依赖包 apt-get install socat get-get install init-system-helpers get-get install adduser get-get install logrotate 5、安装RabbitMQ dpkg -i rabbitmq-server_3.7.12-1_all.deb 具体我的安装流程以及遇到的问题记录如下: 2. 安装过程 2.1. 下载安装包 ubuntu系统基于debian,所以我们要下载官方给的deb安装包: wget https://dl.bintray.com/rabbitmq/all/rabbitmq-server/3.7.4/rabbitmq-server_3.7.4-1_all.deb 等待下载完成。 2.2. 尝试安装 下载安装后,尝试直接安装: ...

2018-04-12 · 2 min · 255 words · Hank

RabbitMQ基础(七)——虚拟主机vhost

1. 简介 RabbitMQ是一个多租户系统:连接、交换器、队列、绑定、用户权限、策略和其他的东西都属于虚拟主机(virtual hosts,v_host),他们是整个RabbitMQ的逻辑分组。 虚拟主机类似于 Apache的虚拟主机和 Nginx的server块,最重要的区别是:Apache的虚拟主机通过配置文件定义,然而在RabbitMQ中,RabbitMQ只能通过rabbitmqctl控制台工具或者HTTP API来创建。 1.1. 逻辑和物理分隔 如果没有虚拟主机,当RabbitMQ中的数据越来越庞大,队列越来越多,随之而来的是令人头痛的管理问题,比如队列、交换器命名冲突,它们相互影响等等。虚拟主机能够解决这些问题,而不需要我们部署多个RabbitMQ来负责不同的业务。 虚拟主机提供了资源的逻辑分组和分隔,每一个虚拟主机本质上是mini版的RabbitMQ服务器,他们有用自己的连接、队列、绑定、交换器,更重要的是有用自己的权限机制,这有点类似服务器和运行在服务器上的虚拟机一样。 1.2. 客户端连接 通队列、交换器一样,虚拟主机必须定义名称。当AMQP 0-9-1的客户端连接到RabbitMQ时,需要指定虚拟主机名称,同时还需要提供用户名和密码,只有用户具有相关的权限才能建立连接(关于权限控制的详细信息可以看 这里)。 通常,一个虚拟主机的连接只能操作属于该虚拟主机的交换器、队列和绑定等等内容。但是,当客户端同时连接了多个虚拟主机时,可能产生多个虚拟主机内的交换器、队列相互连接的情况,这种情景典型的例子是虚拟主机处于不同的RabbitMQ集群或者同一个集群中, RabbitMQ Shovel plugin就是一个这种场景应用的实例。 1.3. 虚拟主机和STOMP、MQTT 同AMQP 0-9-1一样,STOMP协议也有虚拟主机的概念,具体参见 这里。 相反,MQTT并不支持虚拟主机,MQTT连接默认使用单一的RabbitMQ主机,有MQTT特定的约定和特性,使客户机能够连接到特定的虚拟主机,而无需修改任何客户端lib库。有关详细信息,请参阅 MQTT指南。 2. 虚拟主机管理 RabbitMQ包含一个默认的虚拟主机:“/”,我们默认操作的都是这个虚拟主机,其用户名和密码默认都是guest,为了安全起见我们应该修改其密码(后续文章将详细介绍权限管理)。 2.1. 查询 命令: rabbitmqctl list_vhosts 该命令会查询当前RabbitMQ服务中所有的虚拟机,由于我没有创建任何虚拟机,所以只能看到默认的: rabbitmqctl list_vhosts 2.2. 创建 命令: rabbitmqctl list_vhosts 创建时必须指定名称: rabbitmqctl list_vhosts 创建完成后,查询: rabbitmqctl list_vhosts 可以看到刚创建的虚拟主机。 2.3. 删除 命令: rabbitmqctl list_vhosts 同样必须指定要删除的vhost的名称: rabbitmqctl list_vhosts 查询: rabbitmqctl list_vhosts ...

2018-04-04 · 1 min · 140 words · Hank

windows全能终端神器MobaXterm9.4破解版下载

1. 简介 MobaXterm是远程计算的终极工具箱。在一个单独的Windows应用程序中,它为程序员、网站管理员、it管理员和几乎所有需要以更简单的方式处理远程工作的用户提供了大量的功能。 Mobaxterm为用户提供了多标签和多终端分屏选项,内置sftp文件传输以及xerver,让用户可以远程运行X窗口程序,ssh连接后会自动将远程目录展示在ssh面版中,方便用户上传下载文件。 MobaXterm提供了所有重要的远程网络工具、协议(SSH、X11、RDP、VNC、FTP、MOSH、…)和Unix命令(bash、ls、cat、sed、grep、awk、rsync、…)到Windows桌面。 MobaXterm小巧简单,仅有一个可执行文件。 官方地址: https://mobaxterm.mobatek.net/ 2. 功能介绍 1、支持各种连接SSH,X11,RDP,VNC,FTP,MOSH…… 2、内建XServer 3、连接SSH终端后支持SFTP传输文件 除了上边的一排工具,还可以拖拽上传哦。 4、友好的颜色设计 你已经看到了,MobaXterm配色看起来非常舒适,关键信息都以不同颜色区分,例如,ERROR的日志自动标记为红色,警告信息标记为黄色,等等。 5、支持多标签和多终端分屏 6、支持远程连接windows MobaXterm直接支持VNC/RDP/Xdmcp等多种远程连接方式。 7、支持多种工具 在Tool侧边栏里,有多种工具,例如文件夹比较工具MobaFoldersDiff,网络扫描器Network scanner,等等。 其他的功能不一一列举了,具体介绍和使用参见官方详细说明。 3. 下载 MobaXTerm有免费版本,但是保存的session有上限。这里提供的是专业破解版本下载,百度云有限速,下载链接如下: 链接 https://pan.baidu.com/s/1ONu6aVnV5vLFSNiUH8mDUA 密码 95gl 下载完成后解压,里边包含一个破解的MobaXterm.exe文件,执行即可。 4. 申明 1、破解软件博主亲测安全可用,不过建议下载后进行病毒查杀,博主不对软件的安全性负任何责任; 2、分享破解软件仅供个人使用,切勿用于商业用途; 3、开发一款好的产品真心不易,有条件的朋友请支持正版软件。

2018-04-02 · 1 min · 36 words · Hank

RabbitMQ基础(六)——实现RPC

第二篇中我们学习了如何在多个worker中使用工作队列来分配耗时的任务。现在,假设我们需要运行一个远程计算机上的一个方法并等待其返回结果,那么我们怎么实现?通常,这个过程被称为RPC(远程过程调用Remote Procedure Call)。 百度百科对RPC的介绍: RPC(Remote Procedure Call)--远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。 RPC采用客户机/服务器模式。请求程序就是一个客户机,而服务提供程序就是一个服务器。首先,客户机调用进程发送一个有进程参数的调用信息到服务进程,然后等待应答信息。在服务器端,进程保持睡眠状态直到调用信息到达为止。当一个调用信息到达,服务器获得进程参数,计算结果,发送答复信息,然后等待下一个调用信息,最后,客户端调用进程接收答复信息,获得进程结果,然后调用执行继续进行。 有多种 RPC模式和执行。最初由 Sun 公司提出。IETF ONC 宪章重新修订了 Sun 版本,使得 ONC RPC 协议成为 IETF 标准协议。现在使用最普遍的模式和执行是开放式软件基础的分布式计算环境(DCE)。 这里我们不过多讨论RPC相关的内容,有兴趣的可以查阅相关资料。 现在,我们将要使用RabbitMQ来构建一个RPC系统,包括可扩展的RPC客户端和服务端。由于我们并没有耗时的任务,所以们将模拟一些RPC服务。 1. Message properties Message properties即消息属性,AMQP-0-9-1预定义了14项消息属性,但是大部分都很少使用,常用的属性有如下几项: deliveryMode:标记消息是否持久化,值为2则持久化,其他为瞬态消息; contentType:描述编码的mime类型(mime-type),例如常用的JSON编码格式:application/json; replyTo:命名回调队列 correlationId:用于关联RPC的请求和响应 接下来,我们看看如何通过消息属性来设置回调队列和关联ID(correlationId)。 2. 回调队列 通常,在RabbitMQ上实现RPC很简单,客户端发送消息,而服务端响应消息即可。为了接收响应信息,客户端需要在请求中发送回调队列地址给服务端,告诉服务端我使用这个队列来接收消息,你将返回消息发送到这个队列即可。我们可以使用默认的队列(在java客户端中是独占的)。 callbackQueueName = channel.queueDeclare().getQueue(); BasicProperties props = new BasicProperties .Builder() .replyTo(callbackQueueName) .build(); channel.basicPublish("", "rpc_queue", props, message.getBytes()); 3. Correlation Id 上边的方法中,我们为每一个RPC客户端创建了回调队列,这是非常低效的。有没有更好的方式:为每一个客户端创建一个共享的回调队列?这样势必又带来新的问题:共享一个回调队列,请求和响应之间的对应关系并不明确。 CorrelationId属性解决了这个问题。 大致思路是这样:为每个请求设置一个 唯一的标识,然后通过回调队列接收响应消息时,获取这个唯一标识,如果这个标识和之前设定的相同,那么说明响应的确是当前请求的响应,可以获取请求的响应结果了。如果获取到的唯一标示并非我们所设定,说明它并不属于我们的任何请求,那么就可以丢弃这个消息。 为什么我们应该忽略回调队列中的未知消息,而不是进行失败处理?这是由于服务器端可能出现竞态条件。可能发生这样的情况,RPC服务器发送了响应消息后挂掉了,但是请求确认消息(ack)还没有发送,那么,重启后的RPC服务器将再次处理请求。这就是为什么在客户端我们必须优雅地处理重复的响应,保持RPC的幂等性。 消息定义correlationId代码: // 设置消息属性,响应后发送到回调队列中 final String correlationId = UUID.randomUUID().toString(); // 随机生成唯一标识 AMQP.BasicProperties properties = new AMQP.BasicProperties().builder() .correlationId(correlationId) // 每次请求都设定唯一标识,该标识用于将请求和响应进行匹配 .replyTo(callbackQueueName) // 回调队列 .build(); ...

2018-03-28 · 2 min · 355 words · Hank

RabbitMQ基础(五)——topic交换器

在上一篇,我们将日志系统做了改造,按照日志级别进行消息路由。我们还认识了direct类型的交换器,它是直接按照bindingKey与routingKey进行精确匹配,这两者分别在队列绑定和消息发送时进行设置。 Direct交换器每次仅能匹配一个精确的条件(bindingKey),如果要实现按照多个条件进行路由,或者按照条件进行模糊匹配,那么它就无能为力了。例如:前一篇你的日志程序,我们既要按照日志级别进行采集,还要根据打印日志的类来进行过滤,使用direct或者fanout都难以实现。 Topic交换器就是专门来处理这种场景的。在本篇,我们不再使用日志的例子,而是以动物为例,来了解topic交换器。 1. Topic交换器 发送到topic交换器的消息所设定的routingKey必须是一系列的单词列表,他们使用"."分隔。通常,这些单词会根据消息内容进行特殊定义,最大长度为255字节,举例:“stock.usd.nyse”、"syse.vmw"、"quick.range.rabbit"。 BindingKey也必须拥有相同的格式和遵循相同的规则。topic交换器和direct交换器处理逻辑上类似:带有特定routingKey的消息将被分发到绑定了匹配bindingKey的所有队列。但是,bindingKey还有两种特殊的通配符: *:能够模糊匹配一个单词 #:能够模糊匹配零个或多个单词 简单而言,topic交换器能够将消息的routingKey和队列绑定的bindingKey进行模糊匹配。如果不使用上边的两种通配符,那么topic交换器跟direct交换器没什么区别。 通配符举例: *.test.*:仅能匹配中间为test的三个单词的routingKey,例如mq.test.topic。 lazy.#:能够匹配以lazy开头的所有routingKey,单词个数不限,例如:lazy能匹配,lazy.test也能匹配 2. 示例 现在,我们来编写一个能够按照动物信息进行消息分发的程序。我们从速度、颜色和种类三个维度来描述动物信息,这里我们的key也是由这三个词语的具体描述组成,格式为“速度.颜色.种类”整体结构大致如下: 首选创建了一个类型为topic的交换器;然后我们定义了三个key,用于绑定到Q1和Q2两个队列,Q1绑定的key为*.orange.,Q2绑定的key为..rabbit和lazy.;消费者C1希望从Q1获取消息,而C2则希望从Q2获取消息。 整个程序的含义如下: 1、C1对颜色为orange(橙色)的动物感兴趣,希望获取它们的信息; 2、C2除了希望接收物种为rabbit(兔子)的消息外,还希望订阅速度为lazy(缓慢)的所有动物的信息。 很明显,C1、C2获取到的消息肯定存在重复的,它们接收消息的维度不同。 2.1. 生产者 创建交换器 // 创建交换器 channel.exchangeDeclare(exchangeName, "topic"); 模拟数据 String[] msgs = { "quick.orange.rabbit", "lazy.orange.elephant", "lazy.brown.fox", // 能匹配 "lazy.black.male.cat", // 四个单词也可以匹配 "orange", "quick.orange.male.rabbit" // 不能匹配,消息被丢弃 }; 发送消息 for (String msg : msgs) { System.out.println("发送:" + msg); channel.basicPublish(exchangeName, msg, null, msg.getBytes("utf-8")); } 为了简便,我将消息内容直接作为routingKey。实际上,routingKey需要根据消息内容进行特殊定制。 2.2. 消费者 创建随机队列 ...

2018-03-23 · 1 min · 130 words · Hank

RabbitMQ基础(四)——direct交换器与路由

前一篇我们构建了一个简单的日志记录系统,能够广播日志消息到所有已绑定的接收者。但是这样有一定的局限性,我们能够按照一定的条件来进行日志分发呢?例如,按照日志级别来分发日志消息,某个消费者只收到error级别的日志?在本章,我们将实现这个功能。 1. Bindings 上一篇,绑定队列的代码如下: channel.queueBind(queueName, EXCHANGE_NAME, ""); 第三个参数为routingKey,为了便于区分消息发送时的routingKey,我们将队列绑定的routingKey称为bindingKey。 BindingKey允许在队列绑定时设置额外的条件,路由器会按照这个key与消息的routingKey进行条件匹配,成功匹配的消息才会发送到该绑定队列。这个路由器按照bindingKey进行过滤的过程,我们称为消息路由(Routing)。 不同类型的交换器,bindingKey的作用和规则都有所不同。在fanout类型中,bindingKey会被忽略,因为这个交换器类型本身就是为了用于广播消息。在接下来我们要介绍的direct类型的交换器中,设定bindingKey的值有着重要意义。 2. Direct交换器 现在我们对上一篇的日志系统进行扩展,使其可以根据日志的级别进行过滤,假设我们的日志级别有debug、info、warning和error,我们想把error级别的消息单独使用一个消费者来接收,其他的由另外的消费者接收。 上篇中,我们使用的fanout交换器并不能满足上述需求----因为它只会把消息进行简单地广播。 我们将要用到的是 direct类型的交换器,它会按照绑定时给定的bindingKey与消息发布时的routingKey进行 精确匹配。即:当bindingKey与routingKey完成相同时,消息才会被交换器分发给队列。 在开始改造我们的日志程序前,我们先看看多重绑定。 3. 多重绑定 多重绑定,即将相同的bindingKey绑定到多个队列上。RabbitMQ允许这么做,这样与fanout交换器作用类似,可以将消息发送到多个队列。例如: 如图所示,Q1和Q2队列都绑定了black key,那么所有与black匹配的消息都会分发到Q1和Q2中,看起来与消息广播类似。 4. 程序改造 现在,我们开始来改造我们的日志程序,来实现上述的需求。 4.1. 生产者 创建direct类型的交换器: // 创建direct交换器 channel.exchangeDeclare(EXCHANGE_NAME, "direct"); 准备日志数据: static Log[] logs = { new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("error", "this is an error log."), new Log("warning", "this is a warning log."), new Log("info", "this is an info log."), new Log("info", "this is an info log."), new Log("info", "this is an info log."), new Log("debug", "this is a debug log."), new Log("debug", "this is a debug log.") }; ...

2018-03-23 · 2 min · 225 words · Hank

RabbitMQ基础(三)——fanout交换器和发布/订阅

前一篇,我们创建了工作队列,并将任务发布到队列,每一项任务都会发送给一个worker。接下来,我们将使用发布/订阅模式,将消息分发给多个消费者。 为了说明这一模式,我们将构建一个简单的日志记录系统。它将由两个程序组成----第一个将发出日志消息(生产者),第二个将接收并打印它们(消费者)。运行多个消费者,它们都可以接收消息。 1. 交换器 前边的部分我们都是通过消息队列来发布和接收消息,现在让我们看下RabbitMQ的全消息模型。让我们快速回顾一下 前边的内容: producer:用来发送消息 queue:用来缓存消息 consumer:用来接收消息 交换器,即Exchange,交换器是消息到达的第一站,所有的消息都先发送给交换器,交换器再按照不同的规则进行消息分发。RabbitMQ中的消息传递模型的核心思想是,生产者不会直接向队列发送任何消息。实际上,生产者甚至不知道消息是否会被传递到任何队列。相反,生产者只能发送消息给交换器。 交换器做的事情非常简单:一方面,它接收来自生产者的消息,另一边则将消息推送到队列中。交换器必须知道如何处理它接收到的消息。是否应该分发到特定的队列?是否应该分发到多个队列?或者应该被抛弃?其实,这些规则是由交换类型(exchange type)定义的。 交换类型:根据交换器的功能、用途和适用场景,将交换器进行类型定义,每种类型有各自的功能和适用场景。常见的交换类型有:direct、topic、headers、fanout。 接下来,我们学习下fanout类型的交换器。 fanout:将接收到的消息分发到所有能匹配的队列(广播)。简单而言,即:所有订阅了这些消息的队列,都能够收到消息。 创建名为logs的交换器,其类型为fanout: channel.exchangeDeclare("logs", "fanout"); 发送消息: String message = "中文日志信息"; channel.basicPublish("logs", "", null, message.getBytes("utf-8")); 注意,上边的代码队列名称为空。 2. 临时队列 对于队列,很重要的一点是,我们需要为其命名,因为生产者和消费者必须通过队列名称来定位到具体的队列,从中发送和获取消息(前几篇的"hello"队列和"task_queue"队列名称)。上边发送消息的代码中,我们并没有给队列命名,而是使用了""。 对于我们的日志记录系统,如果要每个消费者都能获取所有的日志消息,那么我们必须完成两点: 连接到RabbitMQ时,我们需要创建一个全新的队列,里边没有任何消息; 当消费者与RabbitMQ断开连接,那么队列应该被自动删除。 对于第1点,我们可以利用随机值来命名队列;对于第2点,我们只能检测断开连接后删除队列。但是,这都不是很好的做法。 其实,我们要做的是创建一个非持久的、独占的、自动删除的队列,这个队列的名称随机。RabbitMQ已经为我们提供了这个功能: String queueName = channel.queueDeclare().getQueue(); 如上边代码所示,我们创建了一个默认的队列,这队列具有上述特性,并且名称是随机生成的,格式为amq.gen-xxxx,例如amq.gen-JzTY20BRgKO-HjmUJj0wLg,我们称之为临时队列。 关键队列的非持久、独占和自动删除: 非持久(non-durable):队列中的消息不会持久化 独占(exclusive):队列为私有队列,只有当前应用程序能够消费队列 自动删除(autodelete ):最后一个消费者取消订阅队列时,队列自动删除 3. 绑定 本文开头,我们创建一个名称为"logs"、类型为"fanout"的交换器。那么,交换器需要分发消息给队列,如果将队列与交换器进行关联呢?我们把交换器和队列之间的关联关系称为绑定(binding)。 绑定的代码如下: channel.queueBind(queueName, "logs", ""); queueName即为前边创建的临时队列,第三个参数为routingKey,这里为空。 4. 完整代码 生产者LogSender: public static final String EXCHANGE_NAME = "logs"; public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); Channel channel = connection.createChannel(); // 创建exchange channel.exchangeDeclare(EXCHANGE_NAME, "fanout"); // 分发消息 String message = "中文日志信息"; channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("utf-8")); channel.close(); connection.close(); } ...

2018-03-22 · 2 min · 216 words · Hank

MindManager2016版本下载,带正版激活码

MindManger是Mindjet公司开发一款思维导图软件,目前已经更新到了2018版本。不过这里介绍的是2016版本,提供激活码。可以直接激活,提供下载的2016版本暂时没有中文版本的,激活码在下载包中。 与之前的版本比较,2016版本增加了多种图表,风格采用win10,您可以访问官网进行详细了解。 官网地址: https://www.mindjet.com/ 下载地址 https://pan.baidu.com/s/1LUqlM5gSMVQESE0suiOhqw 密码 mwtc 软件截图:

2018-03-22 · 1 min · 9 words · Hank

Spring事务管理四:声明式事务

编程式事务虽然可以精确控制事务,但是事务控制代码必须侵入业务逻辑代码中,耦合度高,后期难以维护。一般而言,不需要精确控制事务,所以采用的更多的是Spring的声明式事务。 Spring声明式事务基于AOP实现,有两种事务定义方式:xml配置和注解定义,前者使用tx命名空间,后者使用@Transactional注解。 1. 事务属性 在定义事务之前,需要了解一些事务的参数,正如前边TransactionDefinition类定义的,包括传播机制、隔离级别、是否只读、事务超时等,还包括回滚规则定义等参数。 1.1. 传播机制 传播机制(propagation)定义了客户端与被调用方法之间的事务界限。简单而言,就是一个方法调用其他一个或多个方法来实现业务逻辑时,这些方法间的事务如何进行传播,这就由传播机制来决定。 Spring提供了7中传播机制,如下表所示: Table 1. 事务的7种传播机制 传播行为 说明 REQUIRED 业务方法需要在一个事务中运行。如果方法运行时,已经处在一个事务中,那么加入到该事务,否则为自己创建一个新的事务 NOT_SUPPORTED 声明方法不需要事务。如果方法没有关联到一个事务,容器不会为它开启事务。如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行 REQUIRES_NEW 属性表明不管是否存在事务,业务方法总会为自己发起一个新的事务。如果方法已经运行在一个事务中,则原有事务会被挂起,新的事务会被创建,直到方法执行结束,新事务才算结束,原先的事务才会恢复执行 MANDATORY 该属性指定业务方法只能在一个已经存在的事务中执行,业务方法不能发起自己的事务。如果业务方法在没有事务的环境下调用,容器就会抛出异常。 SUPPORTS 这一事务属性表明,方法可以受事务控制,也可以不。如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行 NEVER 指定业务方法绝对不能在事务范围内执行。如果业务方法在某个事务中执行,容器会抛出异常,只有业务方法没有关联到任何事务,才能正常执行 NESTED 如果一个活动的事务存在,则运行在一个嵌套的事务中. 如果没有活动事务, 则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保存点。内部事务的回滚不会对外部事务造成影响。它只对DataSourceTransactionManager事务管理器起效 注意REQUIRES_NEW和NESTED两者的区别; PROPAGATION_REQUIRES_NEW启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. PROPAGATION_NESTED 开始一个 "嵌套的" 事务, 它是已经存在事务的一个真正的子事务. 潜套事务开始执行时, 它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. ...

2018-03-08 · 2 min · 401 words · Hank

Spring事务管理三:编程式事务

前边提到,编写程序式的事务管理可以清楚的定义事务的边界,可以实现细粒度的事务控制,比如你可以通过程序代码来控制你的事务何时开始,何时结束等,它可以实现细粒度的事务控制。 1. TransactionTemplate Spring提供了TransactionTemplate对象来控制事务,它使用了一种回调机制,通过回调来修改事务状态,继承关系如下: TransactionOperations接口定义了基础的事务的执行操作execute(),该接口便于后续扩展,一般不直接使用。 在使用TransactionTemplate之前,需要声明bean: <!--JDBC事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!--编程式事务配置--> <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> <property name="transactionManager" ref="transactionManager"/> </bean> TransactionTemplate使用PlatformTransactionManager的实现来管理事务,这里注入的是JDBC的事务管理器。事务控制代码如下: public void add(User user) throws Exception{ // Spring编码式事务,回调机制 transactionTemplate.execute(new TransactionCallback<Object>() { @Override public Object doInTransaction(TransactionStatus status) { try { userMapper.insertSelective(user); } catch (Exception e) { // 异常,设置为回滚 status.setRollbackOnly(); throw e; } return null; } }); } 调用TransactionTemplate的execute()方法,传递一个TransactionCallback接口的实现,执行其doInTransaction()方法,该方法会回传事务状态对象TransactionStatus,如果有异常,则调用status.setRollbackOnly()将事务状态标记为回滚,否则doInTransaction方法正常返回,事务则会提交。 如果事务控制的方法不需要返回值,那么可以使用TransactionCallback接口的抽象实现类TransactionCallbackWithoutResult: @Override public void add(User user) throws Exception { // Spring编码式事务,回调机制 transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { try { userMapper.insertSelective(user); } catch (Exception e) { // 异常,设置为回滚 status.setRollbackOnly(); throw e; } } }); } ...

2018-03-08 · 1 min · 103 words · Hank

Spring事务管理二:Spring事务管理器

前边简单介绍了事务的概念和其ACID特性,现在让我们看看Spring是如何实现对事务的支持的。 1. Spring事务管理器结构 Spring并不直接管理事务,而是提供多种事务管理器,将管理事务的责任委托给JTA或相应的持久性机制所提供的某个特定平台的事务实现。 Spring对事务管理器的抽象: Spring提供了顶层接口PlatformTransactionManager,并提供了扩展接口ResourceTransactionManager和抽象实现类AbstractPlatformTransactionManager。PlatformTransactionManager下的所有接口和实现类如图所示: 一般而言,每一种事务实现的类图如下: PlatformTransactionManager是Spring事务管理器的核心,接口定义如下: public interface PlatformTransactionManager { // 返回一个已经激活的事务或创建一个新的事务(根据给定的TransactionDefinition类型参数定义的事务属性), // 返回的是TransactionStatus对象代表了当前事务的状态,其中该方法抛出TransactionException(未检查异常) // 表示事务由于某种原因失败。 TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException; // 提交给定的事务,检查其状态。如果事务被标记为rollback-only,则执行回滚。 // 如果事务不是新的事务,则忽略提交周围适当的事务。如果先前的事务被挂起,则在提交新事务后恢复先前的事务。 void commit(TransactionStatus status) throws TransactionException; // 执行给定事务的回滚。如果事务不是一个新的事务,将其周边适当的事务标记为rollback-only。 // 如果先前的事务被挂起,则在回滚新事务后恢复先前的事务。 void rollback(TransactionStatus status) throws TransactionException; } TransactionDefinition接口定义如下: public interface TransactionDefinition { // 返回事务传播行为 int getPropagationBehavior(); // 返回事务隔离级别 int getIsolationLevel(); // 返回事务超时时间 int getTimeout(); // 返回事务是否只读 boolean isReadOnly(); // 返回事务的名称 String getName(); } ...

2018-03-07 · 1 min · 200 words · Hank

Spring事务管理一:Spring事务简介

1. 定义 在软件开发领域, 全有或全无的操作被称为事务(transaction)。 事物允许将几个操作组合成一个要么全部发生要么全部不发生的工作单元。发生和不发生两者只能选择其一,而不可能两者都选择。事务确保了数据和资源免于处在不一致的状态。 让我们继续使用最简单有效的转账例子来说明事务: A给B转100元,这个过程大概需要两步: step1、从A的账户扣除100元; step2、给B的账户增加100元。 这两步必须要么全部执行成功要么都不成功并且还原账户的金钱为初始值,否则转账失败。如果step1成功而step2失败,那么A账户少了100元,但是B却没收到钱;如果step1失败而step2成功,B账户会多出100元。这两种情况都是不允许发生的。 所以,step1和step2必须处于同一事务中,我们就说事务把这两个操作组合为要么都成功要么都失败的工作单元。整个操作成功,那么转账成功,结果是A账户扣了100元而B账户多了100元;如果整个过程失败,那么A和B的账户的金额恢复到转账前的状态,即未发生任何变化,就像转账操作从未发生过一样。 2. ACID特性 Atomic(原子性):确保所有操作要么都发生,要么都不发生。 Consistent(一致性):事务前后,数据均处于正确状态,数据不应该被破坏。 Isolated(隔离性):事务彼此隔离,用户间操作不相混淆。 Durable(持久性):事务一旦完成,结果应该持久化,例如保存到数据库中。 上边转账的例子中,两步操作必须要么成功要么都失败,确保了原子性;而原子性保证账户中的金额不会处于不一致或者说部分完成,保证了一致性;转账过程与其他转账过程互不影响,遵循隔离性;转账成功后,结果必须持久化保存,防止事务结果丢失。 3. Spring对事务管理的支持 Spring提供两者事务管理方式:编程式事务管理和声明式事务管理。 3.1. 编程式事务管理 编写程序式的事务管理可以清楚的定义事务的边界,可以 实现细粒度的事务控制,比如你可以通过程序代码来控制你的事务何时开始,何时结束等,与后面介绍的声明式事务管理相比,它可以实现细粒度的事务控制。 3.2. 声明式事务管理 如果你并不需要细粒度的事务控制,你可以使用声明式事务,在Spring中,你只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中, 解除了和代码的耦合, 这是 对应用代码影响最小的选择,从这一点再次验证了Spring关于 AOP的概念。当你不需要事务管理的时候,可以直接从Spring配置文件中移除该设置。 Spring通过回调机制将实际的事务实现从事务性代码中抽象出来。如果应用只使用一种持久化资源,那么可以使用持久化机制本身的事务性支持(JDBC、Hibernate、JPA等);如果应用事务跨多个资源,那么Spring会使用第三方的JTA(JAVA事务API)实现来支持分布式(XA)事务。 使用声明式事务还是编程式事务管理,在很大程度上是 细粒度和易用性之间权衡。一般情况下,不需要精确控制事务,采用声明式事务不仅易用性高,而且降低了事务与其他业务代码的耦合性。 4. 总结 在软件开发领域,全有或全无的操作被称为事务。事务必须具备ACID特性,在Spring中,除了编程来精确控制事务,还可以使用AOP来配置低耦合度的声明式事务控制。

2018-03-07 · 1 min · 38 words · Hank

股票是什么

最近看了下股票相关的东西,刚好看到一则比较有意思的说明股票是什么的故事,转载下来! 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 从前,在一片广阔的大草原上,有很多只母鸡,他们,吃着火锅唱着歌,吃着青草下着蛋。但是,母鸡们又不愿意像这样日复一日的吃草,下蛋。于是,一场革命性的变革即将到来…… 1. Chapter 1 股票的产生 有一天,一只有远见的花母鸡突然想到,如果我能吃到更多,更好的虫子,岂不是可以进化成母鸡中的战斗机,下更多、更好的蛋? 可是,问题来了,这只花母鸡手里没有钱,就只能卖身。花母鸡找到了一只聪明的猴子,说,你给我钱,我之后每下两个蛋,都能分你一个。 猴子当然聪明得很,他心想:花母鸡每年能生100个蛋,一个蛋能卖1块钱,于是我每年就能分50块钱,那我给你1000块钱吧,这样我20年就能回本。 花母鸡说,OK,我这有50张 母鸡券,拿着作为凭证吧。 于是,猴子这50张母鸡券就被称为“ 股份”,而他所做的1000块钱投资,就叫“按20倍 市盈率入股”。啥叫市盈率?就是母鸡券的价格除以每年可以领到的鸡蛋,也可以理解为多久“回本”。这个数字,越低越好。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 2. Chapter 2 股东的权益 花母鸡拿着猴子的钱,开开心心的吃了很多虫子,理所当然的也生了更多的蛋。 第一年,花母鸡下了100个蛋,分了猴子50个,猴子很开心。这,就叫做“ 分红”。 第二年,花母鸡还是下了100个蛋,但花母鸡把猴子叫过来开了个股东大会,说我用这100个蛋换个大鸡窝,以后每年我都能多生10个蛋,股东你觉得如何呀?猴子一想,我原来每年收益5%,用50个蛋换以后每年5个蛋,每年收益10%,10年回本,合算!这,就叫做 有效率的再投资。 这时候,聪明猴投资母鸡的事儿在动物圈传开了,猩猩这时候跑过来说,我给你1000块钱,要不你把50个母鸡券卖给我? 聪明猴心想,我特么又不是傻B,就跟猩猩说:我这个母鸡品种好,叫 价值鸡,现在每年生110个蛋,以后还能健康生蛋50年,我按照20年回本卖给你,怎么也得1100!猩猩说,好好好,买买买! 于是,动物圈的第一次股权交易发生了,而单张母鸡券的价格,也就从20元,上浮到了22元。这,就是 股价上浮的原因之一。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 3. Chapter 3 市场形成 这个交易做成以后,在动物圈引发了轰动,各种动物都蠢蠢欲动,尤其是那些安安稳稳下蛋的母鸡们。于是,每个品种的母鸡,都发行了带有自己印记的母鸡券,给小动物们互相买卖。 老虎王二虽然并不知道这些动物在弄啥嘞,那些母鸡券貌似只有“卵”用,但作为大王,底下动物干什么都得管着才行(他们的确啥都不懂),于是他效仿大洋彼岸隔壁森林发达市场的老王颁布了一项规定:母鸡可以在老虎家向所有动物公开拍卖自己的母鸡券,所有的母鸡券都必须在老虎家里交易,这就叫做 股票交易市场。 而母鸡必须在老虎家做体检,登记所有信息,方便动物们深入了解母鸡,另一方面动物们也容易找到自己的交易对手,这样 傻傻的犀牛大象也毫不费力就可以买卖母鸡券了! 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 4. Chapter 4 股价 大家已经可以想到,假设一个母鸡一夜之间变成了胖母鸡,生蛋加倍,那么凭借相应母鸡券就可以领到双倍的鸡蛋,所以母鸡券价格翻倍;反之今夜母鸡得了禽流感,卒,对应母鸡券就一文不值。 那么再许许多多个夜晚,什么都没有发生,还是原来的母鸡,还是生普通的蛋,为什么母鸡券还是涨了跌了?原因很复杂,或许是猴子认为母鸡会变胖,或许是因为猩猩想娶媳妇儿急需用钱,但无论怎样的原因, 母鸡券的价格上涨或下降,母鸡都得不到任何直接的好处或者是坏处,还是原来的母鸡,还是生普通的蛋。 而股价,有时候跟母鸡本身,并没有什么关系。 戈登·盖克曾经是华尔街的一名金融大亨,1987年因为炒股搞内幕交易被抓。2001年戈登出狱后重操旧业,然后碰上了金融风暴……上面说的,是两部《华尔街》电影的剧情。为了让普通观众更好理解故事,编剧借角色之口,打了很多生动形象的比喻。主演迈克尔·道格拉斯还因此拿了个奥斯卡影帝。打比喻,讲故事,是普及金融知识的最佳手段。这也是我们开办“母鸡经济学”专栏的初衷,我们请了一批一线的金融从业者,请他们用这种方式,给大家聊聊金融这点事。本期作者为私募基金公司交易员2.5。 哦对了,开头提到的迈克尔·道格拉斯生活中可不像电影里那样呼风唤雨,他自己炒股赔了据说高达1亿美元。 原文: 作者:邹波 链接:https://www.zhihu.com/question/25215182/answer/54368155[]

2018-02-28 · 1 min · 54 words · Hank

MySQL中批量插入和批量更新

大批量同步数据到MySQL中,涉及到更新和新增数据,如果仅仅循环一次次插入,不仅耗费过多的网络开销,性能低下,而且频繁的获取数据库连接、操作数据库,增加数据库压力,甚至严重影响数据库性能。因此,此时我们需要批量操作,来减轻数据库压力,提升应用性能。 1. 数据准备 首先创建一张测试表,插入一些初始数据: CREATE TABLE `hello` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('1', 'zhangsan', '30'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('2', 'lisi', '18'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('3', 'wangwu', '22'); INSERT INTO `test`.`hello` (`id`, `name`, `age`) VALUES ('4', 'zhaoliu', '25'); ...

2018-02-27 · 2 min · 240 words · Hank

RabbitMQ基础(二)——工作队列Work Queues

上一篇,我们编写了从一个指定的队列发送和接收消息的程序。在本文中,我们将创建一个工作队列,用于将耗时的任务分配给多个工作人员。 Work Queues,称为工作队列(也称Task Queues,任务队列),其主旨在于避免立即执行资源密集型任务,并且必须等待它完成。相反,我们把任务安排在后来执行。对此,我们将任务封装为消息并将其发送到队列中。在后台运行的工作进程会获取任务并最终执行任务。当你运行许多工作进程时,任务将在他们之间共享。 1. 轮询调度 使用任务队列的优点之一是能够轻松地并行工作。如果我们积累了大量的工作,我们就可以增加更多的任务消费者,这样就可以很容易地扩大规模。默认情况下,RabbitMQ将依次将每个消息发送给下一个使用者。平均每个消费者将得到相同数量的消息,这种分发消息的方式称为轮询。 2. 消息确认 完成一项任务可能需要几秒钟。你可能会想,如果其中一个消费者开始了一项很长的任务,并且只完成了部分任务,会发生什么。使用我们当前的代码,一旦RabbitMQ向客户传递消息,它立即标记为删除。在这种情况下,如果你关闭一个工作进程,我们将失去它正在处理的信息。我们也将丢失所有发送给这个特定工作进程并且还未处理消息。 通常,我们希望一个工作进程挂掉后,将其任务交给其他工作进程完成。为了保证消息不丢失,RabbitMQ支持消息确认机制:当消费者处理完消息后,其反馈给RabbitMQ,表明消息已经被接收和处理,RabbitMQ可以自由删除该消息。 如果一个消费者挂掉(其通道关闭,连接关闭,或者TCP连接丢失),而没有发送ack,RabbitMQ将会知道一条消息没有被完全处理,并且将重新排队。如果同时有其他的消费者在线,那么它将很快把消息重新交给另一个消费者处理,这样就保证不会丢失任何信息。 消息不会超时:当消费者进程挂掉时,RabbitMQ将重新传递消息,即使处理消息需要很长时间。 RabbitMQ默认是开启手动消息确认的,我们可以通过autoAck=true标记明确地关闭,即采用系统自动确认消息。如果需要手动确认消息,那么将autoAck设置为false,一旦我们完成了任务,需要向工作进程发出确认消息,代码如下: // 处理完成,手动接收消息时,需要在处理成功后进行反馈,保证消息不丢失 channel.basicAck(envelope.getDeliveryTag(), false); 示例的部分关键代码如下: NewTask.java /** * 发布5条消息,每条消息的一个“.”表示消息执行时间需要1秒。 * * @param args * @throws IOException * @throws TimeoutException */ public static void main(String[] args) throws IOException, TimeoutException { ConnectionFactory factory = new ConnectionFactory(); factory.setHost("localhost"); Connection connection = factory.newConnection(); String[] msgs = new String[]{ "First Message.", // 1s "Second Message..", // 2s "Third Message...", // 3s "Fourth Message....", // 4s "Fifth Message....." // 5s }; Channel channel = connection.createChannel(); channel.queueDeclare(TASK_QUEUE_NAME, false, false, false, null); for (String msg : msgs) { // 发布消息 channel.basicPublish("", TASK_QUEUE_NAME, null, msg.getBytes("utf-8")); System.out.println("[x] Sent '" + msg + "'"); } channel.close(); connection.close(); } ...

2018-01-24 · 3 min · 431 words · Hank

RabbitMQ基础(一)——基本概念和HelloWorld

1. 基本概念 RabbitMQ是一个消息代理,是一个erlang开发的AMQP(Advanced Message Queue )的开源实现。 RabbitMQ是轻量级的,易于部署在premises和云中。它支持多种消息传递协议。RabbitMQ可以部署在分布式和联合配置中,以满足高级别、高可用性需求。 其主要思想非常简单:它接受并转发消息。你可以把它想象成邮局:当你把邮件寄到邮箱时,你很确定邮差先生最终会把邮件寄给你的收件人。使用这个比喻,RabbitMQ是一个邮筒,一个邮局和一个邮差。 RabbitMQ与邮局的主要区别在于,它不处理纸张,而是接受、存储和转发二进制数据。 官网地址: http://www.rabbitmq.com 2. AMQP AMQP,即Advanced Message Queuing Protocol,一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受客户端/中间件不同产品,不同的开发语言等条件的限制。Erlang中的实现有 RabbitMQ等。 目标是实现一种在全行业广泛使用的标准消息中间件技术,以便降低企业和系统集成的开销,并且向大众提供工业级的集成服务。通过AMQP,让消息中间件的能力最终被网络本身所具有,并且通过消息中间件的广泛使用发展出一系列有用的应用程序。 Broker: 中间件。接收和分发消息的应用,RabbitMQ Server就是Message Broker。 Virtual host: 虚拟主机。出于多租户和安全因素设计的,把AMQP的基本组件划分到一个虚拟的分组中,类似于网络中的namespace概念。当多个不同的用户使用同一个RabbitMQ server提供的服务时,可以划分出多个vhost,每个用户在自己的vhost创建exchange/queue等。 Connection: 连接。publisher/consumer和broker之间的TCP连接。断开连接的操作只会在client端进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题。 Channel: 渠道。如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的。Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。 Exchange: 路由。message到达broker的第一站,根据分发规则,匹配查询表中的routing key,分发消息到queue中去。常用的类型有:direct (point-to-point), topic (publish-subscribe) and fanout (multicast)。 Queue: 队列。消息最终被送到这里等待consumer取走。一个message可以被同时拷贝到多个queue中。 Binding: 绑定。exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。 3. RabbitMQ术语 3.1. Producter 即生产者。Producing就是发送,发送消息的程序是生产者(Producter)。用P表示,如下图: 3.2. Exchange 交换器,RabbitMQ中,其实消息不会直接相队列发送,而是发送给交换器,然后交换器在按照一定的规则转发给不同的队列。交换器做的事情非常简单:一方面,它接收来自生产者的消息,另一边则将消息推送到队列中。交换必须知道如何处理它接收到的消息。是否应该附加到特定的队列?是否应该附加到许多队列?或者应该被抛弃。这些规则由交换类型(exchange type)定义。 3.3. Exchange Type 交换器类型,在创建交换器时指定,用于区分交换器的不同作用,实现不同的功能。RabbitMQ定义了四种交换器类型:direct、topic、headers、fanout,每种类型都有特定的应用场景(见后续文章的详细介绍)。 direct:bindingKey和routingKey进行精确匹配,适用于精确将消息发送给指定队列; topic:bindingKey和routingKey可以进行模糊匹配,通过使用通配符"*"和"#"分别来模糊匹配一个单词和多个单词;适用于将消息按照一定的规则发送到匹配的一个或多个队列; fanout:广播,这种交换器可以将消息广播给所有订阅的交换器; header:不常用,有兴趣的话可以自行了解。 3.4. Queue 即队列,队列是邮箱的名称,它处于RabbitMQ内部。尽管消息流通过RabbitMQ和您的应用程序,但它们只能存储在队列中。队列不受任何限制,它可以根据你的需要存储尽可能多的消息----它本质上是一个无限的缓冲区。许多生产者都可以发送消息到一个队列,许多消费者可以尝试从一个队列接收数据。队列上有它的名称,如下图表示: 3.5. Consumer 即消费者,Consuming跟receiving的含义类似。Consumer通常为等待接收消息的应用程序 。注意,生产者、消费者和消息代理不需要处于同一台主机上,事实上,在大多数应用场景都是如此。 ...

2018-01-18 · 2 min · 354 words · Hank

Windows server 2012 R2评估版本激活

原来服务器安装了Windows server 2012 R2评估版本,只能试用180天,最近刚好到期了,系统激活前每隔一个小时会自动关机,所以必须进行激活。 要激活分为两步: 1、将评估版升级为标准版 2、使用激活工具激活 1. 将评估版升级为标准版 1.1. 查看系统当前版本 最好以管理员身份运行cmd,输入命令: DISM /online /Get-CurrentEdition 可以查询当前安装的版本是什么。 如果是评估版,例如Standard,把“ServerStandardEval”中的Eval这四个字母去掉,就是你的当前版本。 我的当前版本就是ServerStandard,如下图所示: 1.2. 升级为标准版 同样在cmd中输入如下命令: DISM /online /Set-Edition:ServerStandard /ProductKey:你的标准版key /AcceptEula ServerStandard为查出来的EDITION ID,ProductKey为标准版key,例如我使用如下命令成功升级为标准版: DISM /online /Set-Edition:ServerStandard /ProductKey:M98WF-NY2PP-73243-PC8R6-V6B4Y /AcceptEula 如果提示key无效或不可用,多找一些key试试。 升级完成后,重启系统,现在桌面的标准评估版本已经不再显示,右键点击我的电脑-属性,可以看到现在为standard版本,说明升级成功,但是仍然没有激活。 2. 使用激活工具激活 我尝试使用KMSTools激活工具激活,结果激活失败。 最终,在网上找到了小马激活工具,成功激活。 工具下载地址: https://pan.baidu.com/s/1qYwqI2w。 注意:博主亲测可以激活,但杀毒软件可能会报病毒,博主是直接绕过杀毒软件的,存在一定的风险,请谨慎考虑。 激活成功后,再次重启系统,可以看到已经成功激活了。 申明 本文章仅供个人学习参考使用,如需商业用途,建议大家支持正版系统。

2017-12-05 · 1 min · 45 words · Hank

redis desktop manager桌面工具

链接 http://pan.baidu.com/s/1i53VLmX 密码 vmm6 比较好用的Redis可视化界面工具,支持Windows 10, Mac OS X 10.12, Ubuntu 16, LinuxMint | Fedora | CentOS | OpenSUSE等平台。 github地址: https://github.com/uglide/RedisDesktopManager/ 官网: http://redisdesktop.com 界面截图: 可支持控制台: 支持按key过滤查找功能:

2017-11-23 · 1 min · 26 words · Hank

Astah professional 7.2版本破解文件

分享Astah professional 7.2下载和破解: 官方下载地址: http://astah.net/download 1、免费的community版本 链接: http://pan.baidu.com/s/1kVch3oj 密码:3o49 2、专业版 链接: http://pan.baidu.com/s/1slzBKip 密码:lwjn 专业版破解文件 链接: http://pan.baidu.com/s/1pLBlgDH 密码:u9x7 破解方法:解压下载的文件,将astah-pro.jar拷贝的软件安装根目录覆盖原文件即可。 上边的连接永久有效,博主自己测试可以正常破解使用。

2017-11-22 · 1 min · 19 words · Hank

WEBP格式的图片导致ImageIO.read方法return null

1. 问题背景 最近开发一个图片服务,主要是维护项目图片,支持JPG、BMP、PNG、JPEG等常规格式。开发的时候没问题,但是上到生产的时候,客户在维护图片的时候,发现有的JPG格式的图片能上传,有的不能。 拿到正常上传和不能上传的图片,对比发现,不能上传的图片,在windows上根本无法打开,图片如下(用chrome打开可以看见,右键另存图片,或者 点击这里下载): 但是奇怪的是,该图片可以在chrome浏览器显示,也可以在安卓APP上显示,但是其他浏览器和iOS无法正常显示。 2. 问题分析 首先debug代码,发现图片都会使用ImageIO来读取,以便获取图片宽高从而进行等比例压缩,代码如下: BufferedImage bi = ImageIO.read(inputStream); Map rtnMap = new HashMap(); if (bi == null) { rtnMap.put("status", "1"); //上传不成功 rtnMap.put("msg", "该图片本身不是图片格式"); return rtnMap; } 非正常图片,上边的bi会返回null,而不会抛出任何异常信息。 首先怀疑图片是损坏的,因为windows上根本无法打开,显示如下: 但是该图片可以在chrome上正常显示,这表明图片不会是损坏了,应该是跟平台有关,那么会不会是图片格式根本不是JPG,仅仅后缀显示为JPG呢? 然后我用记事本打开正常的和非正常的图片文件进行比对,想获取一些有用的信息,结果发现: 非正常JPG图片 正常JPG图片 如上所示,正常JPG里边,显示格式为JFIF,而非正常图片显示为WEBPVP8,明显不同。看来,不能正常上传的图片是平台不支持的图片格式,到底WEBPVP8是什么格式? 3. WEBP格式 通过谷歌搜索,查询半天发现,这种格式名为WEBP,由谷歌开发,官方地址(记得翻墙): https://developers.google.com/speed/webp/?hl=zh-CN,也是图片格式的一种, 百度百科原文如下: WebP格式,谷歌(google)开发的一种旨在加快图片加载速度的图片格式。图片压缩体积大约只有JPEG的2/3,并能节省大量的服务器带宽资源和数据空间。Facebook Ebay等知名网站已经开始测试并使用WebP格式。 但WebP是一种有损压缩。相较编码JPEG文件,编码同样质量的WebP文件需要占用更多的计算资源。桌面版Chrome可打开WebP格式。 恍然大悟,原来是一种新型图片格式,很明显windows和iOS还不支持,但是谷歌旗下产品可以支持,这就解释了为什么chrome、安卓能够打开。 在JAVA中,还不支持这种格式,可以用如下代码检查当前支持的图片格式: String[] ss = ImageIO.getReaderFormatNames(); System.out.println(Arrays.asList(ss)); 返回结果:[JPG, jpg, bmp, BMP, gif, GIF, WBMP, png, PNG, jpeg, wbmp, JPEG] 在查找问题时,我还尝试使用twelvemonkeys扩展包来扩展所支持的图片类型: <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-jpeg</artifact> <version>3.3.2</version> </dependency> <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-tiff</artifact> <version>3.3.2</version> </dependency> <dependency> <group>com.twelvemonkeys.imageio</group> <artifact>imageio-pnm</artifact> <version>3.3.2</version> </dependency> ...

2017-11-03 · 2 min · 375 words · Hank

ubuntu下ssh关闭密码登陆,采用公钥认证登陆

前段时间,公司内网的机器被黑客入侵,最后分析原因,在于我们希望通过外网访问公司的git仓库,然后开放了22端口,而此前git所在机器上被病毒感染过…… 由于git是单独的服务器,所以我们希望只有公钥认证的用户可以授权访问该服务器,否则拒绝访问。因此,我们决定采用以下方案: 禁止用户通过ssh的账号密码登陆,而是用公钥私钥认证登陆。具体做法如下: 1. 修改SSH配置文件sshd_config 注意是sshd_config,修改ssh_config无效 编辑sshd_config文件: $ vi /etc/ssh/sshd_config 禁用密码验证,默认是yes $ vi /etc/ssh/sshd_config 启用密钥验证 $ vi /etc/ssh/sshd_config 指定公钥数据库文件 $ vi /etc/ssh/sshd_config 2. 重启ssh服务 $ vi /etc/ssh/sshd_config 由于服务器有两个用户:root管理员用户和git用户,而所有访问代码的必须通过git用户访问,git本身已经通过公钥私钥登陆,所以对原来访问代码仓库的客户端没有影响。而目前root用户禁止了密码登陆,如果进行授权呢? 很简单,将授权以root用户登录服务器的客户端公钥加入到/root/.ssh/authorized_keys文件中,客户端登陆时,带上客户端的私钥信息,就可以完成认真并成功以root用户登录,我用的mobXterm工具,其他工具类似,链接选择私钥如下: 这样,就完成了只能公钥认证通过的用户才能登陆服务器的功能。 3. 注意事项 3.1. 保留会话窗口 在修改sshd_config文件并重启ssh之前,先保留一个链接到服务器的会话,否则如果ssh配置出现错误,将导致不能链接到服务器。 3.2. ssh配置文件 修改sshd_config而非ssh_config。 3.3. 为何不修改ssh默认的22端口 因为链接git仓库客户端较多,如果修改了22端口,每一个客户端都要重新设置代码仓库的remote地址,处理起来较为麻烦,如果如何处理请自行百度。当然为了更为安全,建议还是修改ssh默认的22端口。

2017-08-14 · 1 min · 41 words · Hank

git从其他git服务器完整迁移到自建git服务器

1). 从原地址克隆一份裸版本库,原本托管于oschina。 git clone --bare git://github.com/username/project.git (1) 1 --bare 创建的克隆版本库都不包含工作区,直接就是版本库的内容,这样的版本库称为裸版本库。 2). 然后到新的 Git 服务器上创建一个新项目,比如commons。 mkdir commons.git cd commons.git git init --bare 上边的命令表示创建名为commons的版本库,并初始化为空版本库(注意--bare参数)。 3). 以镜像推送的方式上传代码到新的git服务器上。 git push --mirror git@<你的serverIp>:commons.git (1) 1 --mirror 克隆出来的裸版本对上游版本库进行了注册,这样可以在裸版本库中使用git fetch命令和上游版本库进行持续同步。 例如: $ git push --mirror git@192.168.0.106:/data/code/commons.git Counting objects: 100, done. Delta compression using up to 4 threads. Compressing objects: 100% (54/54), done. Writing objects: 100% (100/100), 44.88 KiB | 0 bytes/s, done. Total 100 (delta 13), reused 100 (delta 13) To 192.168.0.106:/data/code/commons.git * [new branch] master -> master ...

2017-06-08 · 1 min · 98 words · Hank

Nginx学习——nginx的下载、安装和启动

Nginx作为一款轻量级WEB服务服务器,除了作为http代理和反向代理服务器,还更广泛的运用于负载均衡、高级http服务、邮件代理服务等。接下来,我们开始学习如何下载安装Nginx服务器,包括windows平台和linux平台。 1. Nginx版本 Nginx的官方网址: http://nginx.org Nginx下载地址: http://nginx.org/en/download.html 如上图所示,目前nginx官方分为三个版本: 主线开发版本(Mainline version):即功能较新的处于开发中的版本,可以用于学习,但不太稳定,不适合商用; 稳定版本(Stable version):功能稳定,适合商用; 历史遗留版本(Legacy versions):较早的历史版本。 其他链接 图1中的CHANGES的链接,是对于此版本的更新日志记录; 图1中的nginx-x.x.x的链接,对应的是nginx特定版本的源代码; 图1中的pgp链接,记录的是使用GPG加密软件计算后的签名信息,用于下载文件的验证,防止文件被篡改; 图1中的nginx/Windows-x.x.x对应的是nginx的windows版本的下载链接。 2. Windows 1、下载安装 下载图1中的nginx/Windows-1.12.0,得到window版本的nginx压缩包。 windows版本为绿色版本,解压即用,无须安装。解压下载的压缩包,得到如下的目录结构: 2、启动 双击nginx.exe启动 直接运行图2的nginx.exe即启动了nginx,同时可以在任务管理器中看到有两个nginx.exe的进程。 Windows命令行启动 运行cmd,进入nginx目录,运行命令 $ nginx.exe 这种方式会使命令行一直处于执行中,无法进行后续操作 或者 $ start nginx 推荐的方式。 如果启动未出现异常信息,表明启动成功,任务管理器中会出现两个nginx.exe的进程。 3、停止 杀进程 直接在任务管理器中kill掉nginx进程,不推荐。 命令行 运行cmd,进入nginx目录,输入命令 $ nginx.exe –s quit 推荐,这种方式是平缓停止,完整有序的停止nginx,并保存相关信息 或者 $ nginx.exe –s stop 快速停止nginx,不保存相关信息。 4、重新加载配置文件 运行cmd,进入nginx目录,输入命令 $ nginx –s reload 5、重新打开日志文件 运行cmd,进入nginx目录,输入命令 $ nginx –s reopen 6、Nginx版本查看 $ ./sbin/nginx –v 或者 $ ./sbin/nginx –V ...

2017-05-15 · 2 min · 269 words · Hank

linux常见操作整理

Linux是一套免费使用和自由传播的类Unix操作系统,是一个基于POSIX和UNIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行主要的UNIX工具软件、应用程序和网络协议。它支持32位和64位硬件。Linux继承了Unix以网络为核心的设计思想,是一个性能稳定的多用户网络操作系统。本文记录了Linux常用的一些命令,并不断完善中,以备查阅。 1. 基本操作 1、删除文件命令: rm -rf 目录循环删除目录 2、打开/关闭fpt命令: service sftpd stop/start 3、ftp开启命令: /etc/rc.d/init.d/xinetd stop /etc/rc.d/init.d/vsftpd start /etc/rc.d/init.d/xinetd start 4、svn开启命令: svnserve -d -r /usr/local/svn/ 5、jetty启动: root@ubuntu-server1:/usr/local/jetty-mvn/jetty9/bin# ./jetty.sh start Starting Jetty: . . . OK Tue Sep 10 12:30:45 CST 2013 6、解压文件到指定目录: tar xxx.gz -C /usr/local/ 7、查看权限命令 查看目录的相关权限可以采用命令ls -lD,或者直接用ls -la如: ls -l www.jb51.net 这里表示查看www.jb51.net目录 8、查看linux机器是32位还是64位的方法: file /sbin/init 或者 file /bin/ls 9、找不到网卡解决: vi /etc/udev/rules.d/70-persistent-net.rules 将最新的eth2网卡改为eth0,并拷贝其ATTR(即最新的mac地址)值,然后编辑eth0的配置文件,将HWADDR改为该值,重启网络服务,问题解决。 10、查看磁盘空间: df -hl 11、查看系统用户和组: groups查看当前登录用户的组内成员 groups gliethttp查看gliethttp用户所在的组,以及组内成员 whoami查看当前登录用户名 /etc/group文件包含所有组 /etc/shadow和/etc/passwd系统存在的所有用户名 ...

2017-04-05 · 4 min · 694 words · Hank

高性能Javascript--脚本的无阻塞加载策略

Javascript在浏览器中的性能,可以说是前端开发者所要面对的最重要的可用性问题。 在Yahoo的Yslow23条规则当中,其中一条是将JS放在底部 <wbr>。原因是,事实上,大多数浏览器使用单进程处理UI和更新Javascript运行等多个任务,而同一时间只能有一个任务被执行。Javascript运行了多长时间,那么在浏览器空闲下来响应用户交互之前的等待时间就有多长。 从基本层面说,这意味着<script>标签的出现使整个页面因脚本解析、运行而出现等待。不论实际的 JavaScript 代码是内联的还是包含在一个不相干的外部文件中,页面下载和解析过程必须停下,等待脚本 完成这些处理,然后才能继续。这是页面生命周期必不可少的部分,因为脚本可能在运行过程中修改页面 内容。典型的例子是 document.write()函数,例如: <html> <head> <title>Script Example</title> </head> <body> <p> <script type="text/javascript"> document.write("The date is " + (new Date()).toDateString()); </script> </p> </body> </html> 当浏览器遇到一个<script>标签时,正如上面 HTML 页面中那样,无法预知 JavaScript 是否在标签中 添加内容。因此,浏览器停下来,运行此 JavaScript 代码,然后再继续解析、翻译页面。同样的事情发生 在使用 src 属性加载 JavaScript的过程中。浏览器必须首先下载外部文件的代码,这要占用一些时间,然后 解析并运行此代码。此过程中,页面解析和用户交互是被完全阻塞的。 <wbr> 因为脚本阻塞其他页面资源的下载过程,所以推荐的办法是:将所有<script>标签放在尽可能接近<body> <wbr>标签底部的位置,尽量减少对整个页面下载的影响。例如: <html> <head> <title>Script Example</title> <link rel="stylesheet" type="text/css" href="styles.css"> </head> <body> <p>Hello world!</p> <-- Example of recommended script positioning --> <script type="text/javascript" src="file1.js"></script> <script type="text/javascript" src="file2.js"></script> <script type="text/javascript" src="file3.js"></script> </body> </html> ...

2017-04-05 · 3 min · 440 words · Hank

MySQL慢查询分析

最近开发微信红包活动,并发量大概在100左右,数据库存储数据几十万条。活动上线后,遇到一个问题,服务启动起来后,在几分钟之内,服务变得很慢,通过分析tcp状态(ss -s)发现,处于timewait状态的TCP达到一千多个,然后从应用前台、后台到数据库逐步分析,最后发现,由于某一张表的数据量达到几十万,而某一个逻辑需要连接该表进行数据查询,导致SQL执行非常缓慢。于是整理下SQL慢查询分析的相关方法,以便查阅。 1. explain语句 explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,explain可以用来查看 SQL 语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优化语句。 Explain语法: explain select … from … [where ...] 例如:explain select * from news; 输出: ---- ------------- ------- ------- ------------------- --------- --------- ------- ------ ------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ---- ------------- ------- ------- ------------------- --------- --------- ------- ------ ------- 下面对各个属性进行了解: 1、id:这是SELECT的查询序列号 2、select_type:select_type就是select的类型,可以有以下几种: SIMPLE:简单SELECT(不使用UNION或子查询等) PRIMARY:最外面的SELECT UNION:UNION中的第二个或后面的SELECT语句 DEPENDENT UNION:UNION中的第二个或后面的SELECT语句,取决于外面的查询 UNION RESULT:UNION的结果。 SUBQUERY:子查询中的第一个SELECT DEPENDENT SUBQUERY:子查询中的第一个SELECT,取决于外面的查询 DERIVED:导出表的SELECT(FROM子句的子查询) ...

2017-04-04 · 6 min · 1074 words · Hank

JVM垃圾回收器工作原理及使用实例介绍

本文首先介绍了JVM各类垃圾回收器及其工作原理,接着通过实例演示它们的使用方式及需注意事项,最后总结了垃圾回收器的配置方式及参数意义。 1. 垃圾收集基础 Java语言的一大特点就是可以进行自动垃圾回收处理,而无需开发人员过于关注系统资源,例如内存资源的释放情况。自动垃圾收集虽然大大减轻了开发人员的工作量,但是也增加了软件系统的负担。 拥有垃圾收集器可以说是Java语言与C语言的一项显著区别。在 C语言中,程序员必须小心谨慎地处理每一项内存分配,且内存使用完后必须手工释放曾经占用的内存空间。当内存释放不够完全时,即存在分配但永不释放的内存块,就会引起内存泄漏,严重时甚至导致程序瘫痪。 以下列举了垃圾回收器常用的算法及实验原理: 1.1. 1、引用计数法 (Reference Counting) 引用计数器在微软的 COM 组件技术中、Adobe 的 ActionScript3 种都有使用。 引用计数器的实现很简单,对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1,当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,则对象 A 就不可能再被使用。 引用计数器的实现也非常简单,只需要为每个对象配置一个整形的计数器即可。但是引用计数器有一个严重的问题,即无法处理循环引用的情况。因此,在 Java 的垃圾回收器中没有使用这种算法。 一个简单的循环引用问题描述如下:有对象 A 和对象 B,对象 A 中含有对象 B 的引用,对象 B 中含有对象 A 的引用。此时,对象 A 和对象 B 的引用计数器都不为 0。但是在系统中却不存在任何第 3 个对象引用了 A 或 B。也就是说,A 和 B 是应该被回收的垃圾对象,但由于垃圾对象间相互引用,从而使垃圾回收器无法识别,引起内存泄漏。 1.2. 2、标记-清除算法 (Mark-Sweep) 标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段首先通过根节点,标记所有从根节点开始的较大对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。该算法最大的问题是存在大量的空间碎片,因为回收后的空间是不连续的。在对象的堆空间分配过程中,尤其是大对象的内存分配,不连续的内存空间的工作效率要低于连续的空间。 1.3. 3、复制算法 (Copying) 将现有的内存空间分为两快,每次只使用其中一块,在垃圾回收时将正在使用的内存中的存活对象复制到未被使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。 如果系统中的垃圾对象很多,复制算法需要复制的存活对象数量并不会太大。因此在真正需要垃圾回收的时刻,复制算法的效率是很高的。又由于对象在垃圾回收过程中统一被复制到新的内存空间中,因此,可确保回收后的内存空间是没有碎片的。该算法的缺点是将系统内存折半。 Java 的新生代串行垃圾回收器中使用了复制算法的思想。新生代分为 eden 空间、from 空间、to 空间 3 个部分。其中 from 空间和 to 空间可以视为用于复制的两块大小相同、地位相等,且可进行角色互换的空间块。from 和 to 空间也称为 survivor 空间,即幸存者空间,用于存放未被回收的对象。 ...

2017-04-03 · 7 min · 1413 words · Hank

maven定义profile无法替换property属性值

1. 场景 最近开发一个web项目,用的maven构建,建立多个profile,对应不同环境,分别包含不同的配置,在打包的时候发现,xml和properties配置文件没有被替换为profile下定义的property属性值。 2. 解决方案: WEB工程需要war插件,启用web资源目录的filter功能,而普通jar不需要。步骤如下: 1、定义profile和各个property属性值 2、定义maven-war插件,并确认webresource文件目录 3、开启filter功能 4、执行clean package -P(profile-id) 命令,可以正常替换web资源目录下的形如${}的变量 3. 示例 3.1. 定义profile 定义dev和uat两个profile,分别对应开发环境和uat环境: <profiles> <profile> <id>dev</id> <properties> <!-- 数据库相关配置--> <mysql.jdbc.url>192.168.1.224:3306/cd_pro</mysql.jdbc.url> <mysql.jdbc.username>root</mysql.jdbc.username> <mysql.jdbc.password>xxxxxx</mysql.jdbc.password> <!-- 环境配置 --> <application.context.ip>http://192.168.1.224/</application.context.ip> <site.id>510100</site.id> <!-- 支付相关配置--> <revisa.pay.service.id>123456</revisa.pay.service.id> <!--LOG4J日志级别--> <log4j.log.level>debug</log4j.log.level> <log4j.logfile.path>D:/logs/cd_pro.log</log4j.logfile.path> <memorycache.url>192.168.1.224:11211</memorycache.url> </properties> <activation> <activeByDefault>true</activeByDefault> </activation> </profile> <profile> <id>uat</id> <properties> <!-- 数据库相关配置--> <mysql.jdbc.url>10.150.39.21:3306/cd_pro</mysql.jdbc.url> <mysql.jdbc.username>root</mysql.jdbc.username> <mysql.jdbc.password>xxxxxx</mysql.jdbc.password> <!-- 环境配置 --> <application.context.ip>http://uat.zaichengdu.com/</application.context.ip> <revisa.pay.site.id>510100</revisa.pay.site.id> <!-- 支付相关配置--> <revisa.pay.service.id>123456</revisa.pay.service.id> <!--LOG4J日志级别--> <log4j.log.level>info</log4j.log.level> <log4j.logfile.path>/data/logs/cd_pro.log</log4j.logfile.path> <memorycache.url>10.150.38.106:11211</memorycache.url> </properties> </profile> </profiles> 3.2. war插件配置和web资源目录定义 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-war-plugin</artifactId> <configuration> <webResources> <resource> <!-- this is relative to the pom.xml directory --> <directory>src/main/resources</directory> <targetPath>WEB-INF/classes/</targetPath> <filtering>true</filtering> </resource> </webResources> </configuration> </plugin> ...

2017-04-03 · 1 min · 113 words · Hank

jQuery validator表单验证框架使用说明

jquery.validate.js是jquery旗下的一个验证框架,借助jquery的优势,我们可以迅速验证一些常见的输入,并且可以自己扩充自己的验证方法,并且对国际化也有很好的支持,本文介绍了该框架的基本使用及常见API。 官网地址: http://jqueryvalidation.org/ 1. 导入js库 <script src='../js/jquery.js' type='text/javascript'></script> <script src='../js/jquery.validate.js' type='text/javascript'></script> 2. 默认校验规则 required:true 必输字段 remote:”check.php” 使用ajax方法调用check.php验证输入值 email:true 必须输入正确格式的电子邮件 url:true 必须输入正确格式的网址 date:true 必须输入正确格式的日期 日期校验ie6出错,慎用 dateISO:true 必须输入正确格式的日期(ISO),例如:2009-06-23,1998/01/22 只验证格式,不验证有效性 number:true 必须输入合法的数字(负数,小数) digits:true 必须输入整数 creditcard: 必须输入合法的信用卡号 equalTo:”#field” 输入值必须和#field相同 accept: 输入拥有合法后缀名的字符串(上传文件的后缀) maxlength:5 输入长度最多是5的字符串(汉字算一个字符) minlength:10 输入长度最小是10的字符串(汉字算一个字符) rangelength:[5,10] 输入长度必须介于 5 和 10 之间的字符串”)(汉字算一个字符) range:[5,10] 输入值必须介于 5 和 10 之间 max:5 输入值不能大于5 min:10 输入值不能小于10 3. 默认的提示 messages: { required: "This field is required.", remote: "Please fix this field.", email: "Please enter a valid email address.", url: "Please enter a valid URL.", date: "Please enter a valid date.", dateISO: "Please enter a valid date (ISO).", dateDE: "Bitte geben Sie ein g眉ltiges Datum ein.", number: "Please enter a valid number.", numberDE: "Bitte geben Sie eine Nummer ein.", digits: "Please enter only digits", creditcard: "Please enter a valid credit card number.", equalTo: "Please enter the same value again.", accept: "Please enter a value with a valid extension.", maxlength: $.validator.format("Please enter no more than {0} characters."), minlength: $.validator.format("Please enter at least {0} characters."), rangelength: $.validator.format("Please enter a value between {0} and {1} characters long.") range: $.validator.format("Please enter a value between {0} and {1}."), max: $.validator.format("Please enter a value less than or equal to {0}."), min: $.validator.format("Please enter a value greater than or equal to {0}.") } ...

2017-04-03 · 6 min · 1138 words · Hank