LINUX上MYSQL优化三板斧

http://www.woqutech.com/?p=1200

讲了如何优化linux mysql服务器,主要讲了操作系统层面的优化

  • CPU方面

    关闭电源保护模式(充分利用cpu资源)

  • 内存:

    vm.swappiness = 0 (尽量少swap)

    关闭numa (在NUMA架构下,本地内存的访问和非本地内存的访问代价是不一样的)

  • 文件系统:

    用noatime(不用更新文件的access time),nobarrier(不用文件系统强制底层设备刷新缓存,由RAID卡或者Flash卡来保证)挂载系统

    IO调度策略修改为deadline。(IO调度策略采用deadline,它是非公平的调度策略,但是能兼顾一个请求不会在队列中等待太久导致饿死)

Java Multi-Threading and Concurrency Interview Questions with Answers

http://www.journaldev.com/1162/java-multi-threading-concurrency-interview-questions-with-answers

虽然是面试题,但是讲了很多java线程中的基础概念

禁用Spotlight后,Alfred找不到app

个人感觉Alfred要比spotlight好用得多,特别是装了某些workflows后,操作效率明显提高.本着不要浪费性能的原则,参考MAC OS X 关闭 spotlight 降温的大法把spotlight服务给全禁用掉了.

用了两天感觉不对,一些常用软件在Alfred中找不到,官方回答了Can Alfred work without Spotlight enabled?,看来还得启用spotlight才行.悲剧的是降温大法里面提到的启用办法老是报错.下面的命令能解决这个问题:

sudo mv Search2.bundle/ Search.bundle/
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.metadata.mds.plist
sudo mdutil -a -i on 

markdown中出现了特殊字符导致blog一直不更新

前几天写了一篇博文,提交到github后,一直主页都刷不出来,以为是github出问题了.专门去twiter看了下,没有发现风吹草动,这下必须得把测试预览环境搭建起来.

sudo gem install jekyll
#安装markdwon解析插件
sudo gem install rdiscount
#切换到项目路径
cd bohrqiu.github.com
#启动jekyll服务并监听文件变动
jekyll serve --watch 启动起来后,居然看到markdown解析失败了

| Maruku tells you:
+-----
| Malformed HTML starting at "<v2,则打包失败)"
| -----
| gin`来规范传递依赖,要求当前依赖的版本和传递依赖版本一样或者比传递依赖版本高(比如A->Cv1 ,A->D->Cv2,如果v1<v2,则打包失败)EOF 没想到`<`符号还不能直接使用~~!

git api 总结

git很久没有用了,加上现在有github的客户端,基本上把命令都忘完了,偶然看到 @heiniuhaha 妹纸总结的git api,太详细了,收了.

打造环境感知的应用

这里说的环境感知的应用是指,应用放在不同的环境,就可以使用不同环境的配置,不需要重新打包.

1.我眼中理想的环境和应用

  • 应用不用管环境配置
  • 参数配置在配置管理系统中的
  • 框架从配置管理中加载配置
  • 只有操作系统或者应用服务器知道环境的概念

理想很丰满,现实很惨…,揉醒了,继续面对现实.要实现这个目标,还有很多需要做的.现在我们尽可能要做的是,开发童鞋自己把环境搞定,不给其他童鞋添堵.

2.适合我们的环境感知的应用

我们的系统大多数用的是tomcat作为应用服务器,最好是让应用服务器来提供环境标识,在应用中结合spring profile机制来实现环境感知

3.如何操作

3.1在操作系统中添加环境变量

比如在10测试环境,在/etc/profile中增加如下东东:

export CATALINA_OPTS=' -Dspring.profiles.active=dev '

然后执行. /etc/profile解析

3.2配置日志

并不是所有系统都会针对不同环境启用不同的日志配置文件,即便有logback可以很方便的来解决这些问题.

3.2.1 在logback配置内区分环境

比如我们在本地测试的时候,把日志输出到console,便于我们查问题. 可以在logback.xml中加入:

<if condition='property("os.name").toUpperCase().contains("WINDOWS")||property("os.name").toUpperCase().contains("MAC")'>
	<then>
		<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
			<encoder>
				<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %logger{0}:%L-%X{ID}- %msg%n</pattern>
			</encoder>
		</appender>
		<root>
			<appender-ref ref="STDOUT" />
		</root>
	</then>
</if>

上面的配置会在windows和mac中启用console日志输出

在logback中引入外部配置文件,也可以区分不同的环境

<property resource="spring/log/log-${spring.profiles.active}.properties" />

上面会根据spring.profiles.active的配置读取不同的配置文件

3.2.2 不同环境区分不同的日志配置文件

如果您有针对不同环境不同的日志文件,com.yjf.common.log.LogbackConfigListener提供了支持.

<context-param>
  		<param-name>logbackConfigLocation</param-name>
  		<param-value>WEB-INF/logback-${spring.profiles.active}.xml</param-value>
  	</context-param>

3.3 配置应用

数据库和外部资源配置,一般会放入到单独的配置文件中,我们可以使用spring提供的能力来实现环境感知

3.3.1 使用properties文件
<bean id="propertyConfigurerForJDBC"
	class="com.yjf.common.dal.EncryptablePropertyPlaceholderConfigurer">
	<property name="order" value="1" />
	<property name="ignoreUnresolvablePlaceholders" value="true" />
	<property name="location"
		value="classpath:jdbc-${spring.profiles.active}.properties" />
</bean>

上面会根据spring.profiles.active的配置读取不同jdbc配置文件

3.3.2 使用spring profile
<beans profile="production">
 	<bean id="xxx" class="xxxxBEAN" />  
</beans>
<beans profile="test">
 	<bean id="xxx" class="xxxxBEAN" />  
</beans>

spring profile通过读取系统或者环境变量spring.profiles.active来启用不同的bean.

3.3.2 硬编码实现

com.yjf.common.env.Env提供了在编写java code时,区分不同的环境

    private void doIt() {
    	if (Env.isOnline()) {
    		//do anything you like.
        }
	}

上面的代码只在生成环境运行,com.yjf.common.env.Env通过读取spring.profiles.active来判断环境

3.4 配置测试

通过上面的一些列配置,环境都由spring.profiles.active控制.在本地测试时,也需要启用此环境变量.

3.4.1 tomcat/jetty 启动类

TomcatBootstrapHelper启动时,默认会在系统变量里增加spring.profiles.active=dev

new TomcatBootstrapHelper(11111).start(); 上面的代码会使用`dev`环境配置.如果您按照3.3.1配置,此时会读取`jdbc-dev.properties`

我没有写jetty的启动帮助类,主要原因是为了和线上保持一致,减少一些不可预知的问题.如果要使用jetty,请增加如下代码:

static{
	System.setProperty("spring.profiles.active", "dev");
}
3.4.1 单元测试

在测试类或者测试父类中增加:

static{
	System.setProperty("spring.profiles.active", "dev");
}

依赖管理

项目依赖问题很让人头痛,也出了很多事故,线上线下都很闹心。本着让大家都能开心的原则😄,在@培根童鞋的要求下,刚好我对maven很熟,刚好我对公司遇到的依赖问题也很了解,写了此文档。

请您带着一个大大的问号?阅读此文档.您可以把您遇到的依赖问题(此文档没有包含的)发给我,丰富案例库.您可以补充依赖检查中的不足之处,毕竟我个人的能力有限.您可以分享一些灰常有用的插件,方便大家.您可以谈谈您对依赖风险控制的想法.Help Me Help You!

一.依赖管理目标

  1. 此规范更新后,使用方不需要修改代码
  2. 规范开源jar包版本
  3. 检查传递依赖
  4. 检查项目classpath中是否有类名相同的依赖jar包
  5. 检查已知不能使用的jar包
  6. 定义常用插件
  7. 优化yjf-common-util依赖,不是每个项目都使用的包定义为provided,由项目自己引入

二.如何实现目标

1.创建公共父pom

  • 定义所有易极付项目都依赖的父pom com.yiji.yiji-parent
  • 此pom为SNAPSHOT
  • 此pom在dependencyManagement中定义常用jar包的版本
  • 此pom显示依赖yjf-common-util guava log
  • 此pom使用maven-enforcer-plugin来规范传递依赖,要求当前依赖的版本和传递依赖版本一样或者比传递依赖版本高(比如A->Cv1 ,A->D->Cv2,如果v1<v2,则打包失败)
  • 此pom使用maven-enforcer-plugin来规范引入会导致已知问题的包(比如我们的项目都使用slf4jlogback,那我们的依赖中不能出现org.slf4j:slf4j-log4j12)
  • 此pom使用com.yiji.maven.yiji-maven-plugin来检查classpath中是否有类名相同的jar包出现.如果有,在console中也会提示警告,在执行打包命令的目录生成dependency-check.log文件,此文件中会记录检查了哪些包.同时,以后我们也可以通过此日志文件来了解我们项目间的依赖情况.
  • 此pom包含常用maven插件maven-compiler-plugin maven-source-plugin maven-eclipse-plugin findbugs-maven-plugin maven-pmd-plugin方便大家日常使用
  • 此pom中的开源依赖,我会定期check是否有更新,是否有bug修复

2.开发com.yiji.maven.yiji-maven-plugin

此插件已开发完毕,代码也很简单,可以发现一些类加载顺序不一致导致的潜在的问题.

目前此插件只检查不同的jar包中是否有相同的类名.还可以增加对资源文件的检查,文件名相同还可以增加对内容的检查.这些需求如有必要,以后在加上.此插件也是SNAPSHOT的,以后我升级了,大家不用改动任何代码.

3.定制settings.xml

  • 此文件定义snapshot依赖为每次打包检查
  • 其他大多人不需要关心的东东

三.如何实施

目前已完成cs项目的改造,使用上面的东东,cs的pom文件还廋身不少.

由于传递依赖,不敢贸然大规模推广,先选择被依赖较少的项目使用.和@培根商量,先选择boss项目\易融通项目\易房保项目使用,使用过程中出现任何问题,请联系我(也可以顺带请我喝茶)

如果这几个项目把雷踩完了,需要找一个统一的时间点,大家一起修改\测试\上线 ## 四.如何搞

1.替换setting.xml

下载svn://192.168.45.206/common/yiji-parent/settings.xml,替换maven安装目录中的setting.xml

2.配置项目父pom

拿cs为例,在cs的父pom中加入

<parent>
    <groupId>com.yiji</groupId>
    <artifactId>yiji-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
</parent> 去掉dependencyManagement中的开源jar依赖(公司内部的依赖不要去掉, `com.yiji.yiji-parent`中没有定义这些东东)。检查项目中的开源依赖是否有版本号,如果有并且IDE提示重复的定义,去掉此版本号;如果没有提示,应该是在 `com.yiji.yiji-parent`里没有加入此依赖,请联系我。

3.测试打包

执行mvnp试试,如果有传递依赖问题,打包会失败,请先联系我。如果打包成功,请检查dependency-check.log文件中有没有警告

4.测试项目

运行单元测试用例,看会不会出现问题,最好找@翼德同学全量回归下。

五.FAQ

1.为什么不提供方便发布的东东

原因有:
  • 容易出错,命令很简单,容易把发往生产的包发到测试环境,除非你很了解
  • setting.xml里不支持定义distributionManagement,只能在pom里面定义。因为我们有多套nexus,需要通过profile来定义不同环境对应不同的nexus,但是profile不能继承(我测试是这样的)
  • 我们大多数项目,只能把facade发布到nexus,即便profile支持继承,我也不敢把这玩意儿加到com.yiji.yiji-parent中。万一某童鞋在项目根目录执行了mvn deploy,@培根会不开心的…😠
如何解决:

可以参考com.yiji.yiji-parent中的profiles部分,请在facade中定义这个玩意儿,然后执行命令

2.有哪些常用的mvn命令,可以方便大家使用

非window用户,请在~/.bash_profile中加入

	alias mvni='mvn -T 1C clean install -Dmaven.test.skip=true'
	alias mvnp='mvn -T 1C clean package -Dmaven.test.skip=true'
	alias mvnv='mvn versions:set -DgenerateBackupPoms=false'
	alias mvnd='mvn -T 1C clean deploy -Dmaven.test.skip=true'
	alias mvndd='mvn -T 1C clean deploy -P dev  -Dmaven.test.skip=true'
	alias mvndo='mvn -T 1C clean deploy -P online -Dmaven.test.skip=true'
	alias mvnc='mvn -T 1C clean eclipse:clean idea:clean'
	alias mvne='mvn -T 1C clean eclipse:clean eclipse:eclipse  -DdownloadSources=true' 不知道各命令啥意思的童鞋请google.

window用户请把svn://192.168.45.206/common/yiji-parent/script中的脚本添加到PATH中,have a try.

3.为毛不加入自动生成doc文档插件

yjf-common-util里面用maven-javadoc-plugin来生成doc文档并在发布时上传到nexus。不过这很费时,而且我们以前使用的codetemplates里面有很多javadoc不认识的东东,警告一大堆,看着惨不忍睹。

4.IDE里面安装maven插件有什么好处

好处很多,它可以检查一些pom编写错误,也可以方便看依赖树。eclipse对maven支持很牛,依赖树看着会很爽,简单的依赖问题,用它就可以搞定。IDEA的智能提示很牛,添加依赖快捷键就可以搞定。

以后有依赖问题的童鞋,先用IDE提供的依赖树功能发现问题。找我也可以,但别让我给你安装maven插件,生命是短暂的啊。 ### 5.maven我不熟怎么办

肯定不是凉拌,可以先看看http://www.infoq.com/cn/minibooks/maven-in-action http://www.juvenxu.com/category/maven/ 后面我会给大家分享maven一些基本的东东。 ### 6.依赖问题除了maven导致的,还有其他导致的,如何解决?

主要是把场景找出来,然后分析这些问题,我们自己来添加些防御手段.比如今天@周洋同学遇到的两个spring bean id配置一样了,导致本地开发测试ok,199启动确不正常.我们可以给spring添加点东东来检查重复id的问题.

7.我依赖的某jar版本就是不一样

有这样的的需求,现在问题比较突出的应该是金融这块.金融依赖很多银行提供的jar包,这些包可能会冲突.比如金融某项目,既依赖httpclient3又依赖httpclient4.

这种情况只能在项目里面指定版本了,使用com.yiji.maven.yiji-maven-plugin里提供的版本不是强制约束.但是建议大家都别这样做,除非没办法.

8.spring4.0都发布了,我们是不是该升级了.

我们现在用的是spring 3.1,可能有项目用的spring3.2.

spring3.2 有很多新特性,比如test framework,此次升级也把spring升级到3.2.6.

spring 4.0改动太大,暂时不考虑

9.在公共父pom中升级一个版本,风险怎么把控

以后大家都继承此父pom,升级一个版本意味着大家都升级了.风险确实很大.

首先,我们会去分析此依赖的release notes,评估升级的必要性和影响面.

然后,我们会找bops这样的大杂烩项目来做测试,测试相关特性是否受影响.

10.为什么不把setting.xml的配置移到pom中

这样做的目的是为了做到环境感知,不同环境的maven setting.xml会不一样,这是信息中心和我需要做的事情.对于大家,只需要使用svn repos中的setting.xml.放到pom中,这个pom需要定义不同的profile,还需要修改我们现有的各个环境的打包脚本.

11.常见踩雷问题

11.1 java.lang.NoClassDefFoundError: org/springframework/ui/velocity/VelocityEngineFactory

这个原因是spring把相关的类放到了spring-context-support里面.如果你用spring的声明式cache,也会遇到找不到类,都加入下面的依赖.

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-support</artifactId>
</dependency>

11.2 velocity报错

java.lang.IllegalStateException: Cannot convert value of type 	[org.springframework.web.servlet.view.velocity.VelocityConfigurer] to required type [org.apache.velocity.app.VelocityEngine] for property 'velocityEngine': no matching editors or conversion strategy found
at org.springframework.beans.TypeConverterDelegate.convertIfNecessary(TypeConverterDelegate.java:267)

报错是因为配置的bean org.springframework.web.servlet.view.velocity.VelocityConfigurer id为velocityEngine.此id覆盖了默认的velocityEngine,把这个id改为velocityConfigurer就ok

11.3 net.sf.ehcache.util.UpdateChecker - Update check failed:

关闭ehcache启动时检查版本,在ehcache配置根元素上添加属性updateCheck="false"

11.4 BeanCopier报错

‘net.sf.cglib.core.TypeUtils.parseType(Ljava/lang/String;)Lorg/objectweb/asm/Type;’ 这是以为spring 3.2对asm有改动Migrating to Spring Framework 3.2(D.3和D.4讲了这些东东),咱也得跟着改动,遇到这个错误,去掉cglib-nodep的依赖就ok

加入下面的依赖:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
</dependency>
<dependency>
    <groupId>org.ow2.asm</groupId>
    <artifactId>asm-util</artifactId>
</dependency>

11.5 Spring MVC controller 处理ajax请求报错 406 Not Acceptable

spring 3.2引入了内容协商的概念,此概念很REST,资源输出格式由客户端来定义.目前支持三种:

  • 请求后缀名 比如getUser.html getUser.xml getUser.json 分别代表请求输出为html/xml/json
  • 参数 比如请求为getUser?type=xml getUser?type=json
  • http header 在http请求中设置Accept header,由客户端编程定义接收什么格式的返回.

这三种方式,我个人觉得第三种最优雅,很适合编程实现对资源的访问.第一种很直观,第二种有点破坏REST的味道了.参考Content Negotiation using Spring MVC

遇到这个错误,很可能是因为我们在Controller中定义@RequestMappingvalue带有html后缀,但是我们在方法上也加上了@ResponseBody,这让spring很困惑,你请求为html,返回输出又要去解析为json.

配置:

1.引入正确的schema 把schemaLocation中spring mvc schema版本去掉

	http://www.springframework.org/schema/mvc
      http://www.springframework.org/schema/mvc/spring-mvc.xsd

2.配置contentNegotiationManager

<bean id="contentNegotiationManager"
      class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorPathExtension" value="false"/>
    <property name="favorParameter" value="false"/>
    <property name="ignoreAcceptHeader" value="true"/>
    <property name="useJaf" value="false"/>
    <property name="defaultContentType" value="application/json"/>
</bean>

3.配置json转换器

<mvc:annotation-driven
         content-negotiation-manager="contentNegotiationManager">
    <mvc:message-converters register-defaults="true">
        <bean id="fastJsonHttpMessageConverter"
              class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
            <property name="supportedMediaTypes">
                <list>
                    <value>application/json;charset=UTF-8</value>
                </list>
            </property>
            <property name="features" value="WriteDateUseDateFormat"/>
        </bean>
    </mvc:message-converters>
</mvc:annotation-driven>

我们很多童鞋spring mvc用得很不地道,建议看看官方demo mvc-showcase-screencast

11.6 使用com.yjf.common.web.CrossScriptingFilter报找不到ESAPI

添加依赖.

<dependency>
	<groupId>org.owasp.esapi</groupId>
	<artifactId>esapi</artifactId>
	<exclusions>
		<exclusion>
        	<artifactId>log4j</artifactId>
        	<groupId>log4j</groupId>
    	</exclusion>
    	<exclusion>
    		<groupId>xerces</groupId>
        	<artifactId>xercesImpl</artifactId>
    	</exclusion>
     </exclusions>
</dependency>
<dependency>
	<groupId>xerces</groupId>
    <artifactId>xercesImpl</artifactId>
</dependency>