Quantcast
Channel: IT社区推荐资讯 - ITIndex.net
Viewing all 15843 articles
Browse latest View live

获取数据库30天内各个指标的TOP语句

$
0
0

要查询短时间内的可以从v$sql 或者是v$sqlarea 如果要查询一周或者一个月内 那么有可能在V$SQLAREA里找不到!下面是通过历史DBA_HIST_SQLSTAT里获得, 这个是通过快照方式保留下来的.


--执行时间最长的


WITH  BASTABLE AS 
(  
  SELECT DBMS_LOB.SUBSTR(SQL_TEXT,4000, 1 ) AS SQL_FULL_TEXT,
         DHST.SQL_ID,
         ROUND (X.ELAPSED_TIME / 1000000 / X.EXECUTIONS_DELTA, 3) AVG_ELAPSED_TIME_SEC,
         ROUND (X.CPU_TIME / 1000000 / X.EXECUTIONS_DELTA, 3) AVG_CPU_TIME_SEC, 
         ROUND (X.BUFFER_GETS_DELTA / X.EXECUTIONS_DELTA, 3) AVG_BUFFER_GETS,  
         ROUND (X.PARSE_CALLS_DELTA/X.EXECUTIONS_DELTA*100, 3) EXEC_PARSE_RATE,
         ROUND (X.PHYSICAL_READ_BYTES_DELTA/1024/X.EXECUTIONS_DELTA, 3) AVG_PHYSICAL_READ_KB, 
         ROUND (X.DISK_READS_DELTA / X.EXECUTIONS_DELTA, 3) AVG_DISK_READS,      
         EXECUTIONS_DELTA AS EXEC_TOTAL_NUM,DHST.COMMAND_TYPE,N.COMMAND_NAME        
    FROM DBA_HIST_SQLTEXT DHST, DBA_HIST_SQLCOMMAND_NAME    N,  
         (  
          SELECT DHSS.SQL_ID SQL_ID,
                 SUM (DHSS.CPU_TIME_DELTA) CPU_TIME,   
                 SUM (DHSS.ELAPSED_TIME_DELTA) ELAPSED_TIME,   
                 CASE SUM (DHSS.EXECUTIONS_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.EXECUTIONS_DELTA) END AS EXECUTIONS_DELTA,
                 CASE SUM (DHSS.SORTS_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.SORTS_DELTA) END AS SORTS_DELTA,     
                 CASE SUM (DHSS.FETCHES_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.FETCHES_DELTA) END AS FETCHES_DELTA,
                 CASE SUM (DHSS.PARSE_CALLS_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.PARSE_CALLS_DELTA) END AS PARSE_CALLS_DELTA,
                 CASE SUM (DHSS.DISK_READS_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.DISK_READS_DELTA) END AS DISK_READS_DELTA,
                 CASE SUM (DHSS.BUFFER_GETS_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.BUFFER_GETS_DELTA) END AS BUFFER_GETS_DELTA,
                 CASE SUM (DHSS.IOWAIT_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.IOWAIT_DELTA) END AS IOWAIT_DELTA,
                 CASE SUM (DHSS.PHYSICAL_READ_BYTES_DELTA) WHEN 0 THEN 1 ELSE SUM (DHSS.PHYSICAL_READ_BYTES_DELTA) END AS PHYSICAL_READ_BYTES_DELTA                                                                                                                                                                                         
            FROM DBA_HIST_SQLSTAT DHSS 
           WHERE DHSS.SNAP_ID IN   
                (SELECT SNAP_ID   
                   FROM DBA_HIST_SNAPSHOT   
                  WHERE BEGIN_INTERVAL_TIME >= TRUNC(SYSDATE)-30
                    AND END_INTERVAL_TIME <TRUNC(SYSDATE)-0  
                ) 
           AND DHSS.PARSING_SCHEMA_NAME =UPPER('SHARK')                      
          GROUP BY DHSS.SQL_ID
          ) X 
   WHERE X.SQL_ID = DHST.SQL_ID   
     AND DHST.COMMAND_TYPE = N.COMMAND_TYPE 
)
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_DISK_READS AS VALUE_S, 'AVG_DISK_READS' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%' ORDER BY AVG_DISK_READS DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_ELAPSED_TIME_SEC AS VALUE_S, 'AVG_ELAPSED_TIME_SEC' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY AVG_ELAPSED_TIME_SEC DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_CPU_TIME_SEC AS VALUE_S, 'AVG_CPU_TIME_SEC' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY AVG_CPU_TIME_SEC DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_BUFFER_GETS AS VALUE_S, 'AVG_BUFFER_GETS' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY AVG_BUFFER_GETS DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, EXEC_PARSE_RATE AS VALUE_S, 'EXEC_PARSE_RATE' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY EXEC_PARSE_RATE DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_PHYSICAL_READ_KB AS VALUE_S, 'AVG_PHYSICAL_READ_KB' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47 ORDER BY AVG_PHYSICAL_READ_KB DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, EXEC_TOTAL_NUM AS VALUE_S, 'EXEC_TOTAL_NUM' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE<>47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY EXEC_TOTAL_NUM DESC ) WHERE ROWNUM <=5
UNION ALL 
SELECT * FROM 
(
SELECT SQL_FULL_TEXT,SQL_ID,EXEC_TOTAL_NUM, AVG_ELAPSED_TIME_SEC AS VALUE_S, 'PROCEDURES_EXEC_TIME' AS VALUES_TYPE 
FROM BASTABLE WHERE COMMAND_TYPE=47  AND SQL_FULL_TEXT NOT LIKE '/* SQL A%'  ORDER BY AVG_ELAPSED_TIME_SEC DESC ) WHERE ROWNUM <=5;


作者:ZengMuAnSha 发表于2014-6-20 9:19:13 原文链接
阅读:116 评论:0 查看评论

spring实现数据库读写分离

$
0
0

现在大型的电子商务系统,在数据库层面大都采用读写分离技术,就是一个Master数据库,多个Slave数据库。Master库负责数据更新和 实时数据查询,Slave库当然负责非实时数据查询。因为在实际的应用中,数据库都是读多写少(读取数据的频率高,更新数据的频率相对较少),而读取数据 通常耗时比较长,占用数据库服务器的CPU较多,从而影响用户体验。我们通常的做法就是把查询从主库中抽取出来,采用多个从库,使用负载均衡,减轻每个从 库的查询压力。

  采用读写分离技术的目标:有效减轻Master库的压力,又可以把用户查询数据的请求分发到不同的Slave库,从而保证系统的健壮性。我们看下采用读写分离的背景。

  随着网站的业务不断扩展,数据不断增加,用户越来越多,数据库的压力也就越来越大,采用传统的方式,比如:数据库或者SQL的优化基本已达不到要求,这个时候可以采用读写分离的策 略来改变现状。

  具体到开发中,如何方便的实现读写分离呢?目前常用的有两种方式:

  1 第一种方式是我们最常用的方式,就是定义2个数据库连接,一个是MasterDataSource,另一个是SlaveDataSource。更新数据时我们读取MasterDataSource,查询数据时我们读取SlaveDataSource。这种方式很简单,我就不赘述了。

  2 第二种方式动态数据源切换,就是在程序运行时,把数据源动态织入到程序中,从而选择读取主库还是从库。主要使用的技术是:annotation,Spring AOP ,反射。下面会详细的介绍实现方式。

   在介绍实现方式之前,我们先准备一些必要的知识,spring 的AbstractRoutingDataSource 类

     AbstractRoutingDataSource这个类 是spring2.0以后增加的,我们先来看下AbstractRoutingDataSource的定义:

    public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean  {}


    AbstractRoutingDataSource继承了AbstractDataSource ,而AbstractDataSource 又是DataSource 的子类。DataSource   是javax.sql 的数据源接口,定义如下:

复制代码
public interface DataSource  extends CommonDataSource,Wrapper {

  /**
   * <p>Attempts to establish a connection with the data source that
   * this <code>DataSource</code> object represents.
   *
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   */
  Connection getConnection() throws SQLException;

  /**
   * <p>Attempts to establish a connection with the data source that
   * this <code>DataSource</code> object represents.
   *
   * @param username the database user on whose behalf the connection is
   *  being made
   * @param password the user's password
   * @return  a connection to the data source
   * @exception SQLException if a database access error occurs
   * @since 1.4
   */
  Connection getConnection(String username, String password)
    throws SQLException;

}
复制代码

   DataSource 接口定义了2个方法,都是获取数据库连接。我们在看下AbstractRoutingDataSource 如何实现了DataSource接口:

复制代码
    public Connection getConnection() throws SQLException {
        return determineTargetDataSource().getConnection();
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return determineTargetDataSource().getConnection(username, password);
    }
复制代码

      很显然就是调用自己的determineTargetDataSource()  方法获取到connection。determineTargetDataSource方法定义如下:

复制代码
protected DataSource determineTargetDataSource() {
        Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");
        Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);
        if (dataSource == null && (this.lenientFallback || lookupKey == null)) {
            dataSource = this.resolvedDefaultDataSource;
        }
        if (dataSource == null) {
            throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");
        }
        return dataSource;
    }
复制代码

  我们最关心的还是下面2句话:

    Object lookupKey = determineCurrentLookupKey();
        DataSource dataSource = this.resolvedDataSources.get(lookupKey);

    determineCurrentLookupKey方法返回lookupKey,resolvedDataSources方法就是根据 lookupKey从Map中获得数据源。resolvedDataSources 和determineCurrentLookupKey定义如下:

  private Map<Object, DataSource> resolvedDataSources;

  protected abstract Object determineCurrentLookupKey()

  看到以上定义,我们是不是有点思路了,resolvedDataSources是Map类型,我们可以把MasterDataSource和SlaveDataSource存到Map中,如下:

    key        value

    master             MasterDataSource

    slave                  SlaveDataSource

  我们在写一个类DynamicDataSource  继承AbstractRoutingDataSource,实现其determineCurrentLookupKey() 方法,该方法返回Map的key,master或slave。

 

  好了,说了这么多,有点烦了,下面我们看下怎么实现。

   上面已经提到了我们要使用的技术,我们先看下annotation的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface DataSource {
    String value();
}

     我们还需要实现spring的抽象类AbstractRoutingDataSource,就是实现determineCurrentLookupKey方法:

复制代码
public class DynamicDataSource extends AbstractRoutingDataSource {

    @Override
    protected Object determineCurrentLookupKey() {
        // TODO Auto-generated method stub
        return DynamicDataSourceHolder.getDataSouce();
    }

}


public class DynamicDataSourceHolder {
    public static final ThreadLocal<String> holder = new ThreadLocal<String>();

    public static void putDataSource(String name) {
        holder.set(name);
    }

    public static String getDataSouce() {
        return holder.get();
    }
}
复制代码

  从DynamicDataSource 的定义看出,他返回的是DynamicDataSourceHolder.getDataSouce()值,我们需要在程序运行时调用 DynamicDataSourceHolder.putDataSource()方法,对其赋值。下面是我们实现的核心部分,也就是AOP部 分,DataSourceAspect定义如下:

复制代码
public class DataSourceAspect {

    public void before(JoinPoint point)
    {
        Object target = point.getTarget();
        String method = point.getSignature().getName();

        Class<?>[] classz = target.getClass().getInterfaces();

        Class<?>[] parameterTypes = ((MethodSignature) point.getSignature())
                .getMethod().getParameterTypes();
        try {
            Method m = classz[0].getMethod(method, parameterTypes);
            if (m != null && m.isAnnotationPresent(DataSource.class)) {
                DataSource data = m
                        .getAnnotation(DataSource.class);
                DynamicDataSourceHolder.putDataSource(data.value());
                System.out.println(data.value());
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}
复制代码

  为了方便测试,我定义了2个数据库,shop模拟Master库,test模拟Slave库,shop和test的表结构一致,但数据不同,数据库配置如下:

复制代码
<bean id="masterdataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/shop" /><property name="username" value="root" /><property name="password" value="yangyanping0615" /></bean><bean id="slavedataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.jdbc.Driver" /><property name="url" value="jdbc:mysql://127.0.0.1:3306/test" /><property name="username" value="root" /><property name="password" value="yangyanping0615" /></bean><beans:bean id="dataSource" class="com.air.shop.common.db.DynamicDataSource"><property name="targetDataSources">  <map key-type="java.lang.String">  <!-- write --><entry key="master" value-ref="masterdataSource"/>  <!-- read --><entry key="slave" value-ref="slavedataSource"/>  </map>  </property>  <property name="defaultTargetDataSource" ref="masterdataSource"/>  </beans:bean><bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!-- 配置SqlSessionFactoryBean --><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource" /><property name="configLocation" value="classpath:config/mybatis-config.xml" /></bean>
复制代码

  在spring的配置中增加aop配置

复制代码
<!-- 配置数据库注解aop --><aop:aspectj-autoproxy></aop:aspectj-autoproxy><beans:bean id="manyDataSourceAspect" class="com.air.shop.proxy.DataSourceAspect" /><aop:config><aop:aspect id="c" ref="manyDataSourceAspect"><aop:pointcut id="tx" expression="execution(* com.air.shop.mapper.*.*(..))"/><aop:before pointcut-ref="tx" method="before"/></aop:aspect></aop:config><!-- 配置数据库注解aop -->
复制代码

  下面是MyBatis的UserMapper的定义,为了方便测试,登录读取的是Master库,用户列表读取Slave库:

复制代码
public interface UserMapper {
    @DataSource("master")
    public void add(User user);

    @DataSource("master")
    public void update(User user);

    @DataSource("master")
    public void delete(int id);

    @DataSource("slave")
    public User loadbyid(int id);

    @DataSource("master")
    public User loadbyname(String name);
    @DataSource("slave")
    public List<User> list();
}
复制代码

   好了,运行我们的Eclipse看看效果,输入用户名admin 登录看看效果

  

  

   从图中可以看出,登录的用户和用户列表的数据是不同的,也验证了我们的实现,登录读取Master库,用户列表读取Slave库。

 

  感谢阅读,希望这篇文章能给你带来帮助!



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Android下个版本将默认使用ART运行时

$
0
0

ART 运行时已包含在 Android4.4 KitKat 中,我们都知道它最终将会替代旧的低效的 Dalvik 运行时。据 xda-developers 报道,Google 开发者已经正式从 AOSP 主枝中移除了 Dalvik,将 ART 设为默认,并留下了评语“Dalvik is dead, long live Dalvik!”ART 代表 Android Runtime,其处理应用程序执行的方式不同于 Dalvik,Dalvik 是依靠一个 Just-In-Time (JIT)编译器去解释字节码,编译后的应用代码需要一个解释器在设备上运行。

ART 在应用安装时就预编译字节码到机器语言,这一机制叫 Ahead-Of-Time (AOT)编译。

本文链接

Android下个版本将默认使用ART运行时

$
0
0
ART运行时已包含在Android4.4 KitKat中,我们都知道它最终将会替代旧的低效的Dalvik运行时。据xda-developers报道,Google开发者已经正式从AOSP主枝中移除了Dalvik,将ART设为默认,并留下了评语“Dalvik is dead, long live Dalvik!”ART代表Android Runtime,其处理应用程序执行的方式不同于Dalvik,Dalvik是依靠一个Just-In-Time (JIT)编译器去解释字节码,编译后的应用代码需要一个解释器在设备上运行。ART在应用安装时就预编译字节码到机器语言,这一机制叫Ahead-Of-Time (AOT)编译。






开源爬虫软件汇总

$
0
0

     世界上已经成型的爬虫软件多达上百种,本文对较为知名及常见的开源爬虫软件进行梳理,按开发语言进行汇总,如下表所示。虽然搜索引擎也有爬虫,但本次我汇总的只是爬虫软件,而非大型、复杂的搜索引擎,因为很多兄弟只是想爬取数据,而非运营一个搜索引擎。

开源爬虫汇总表

开发语言

软件名称

软件介绍

许可证

Java

Arachnid

微型爬虫框架,含有一个小型HTML解析器

GPL

crawlzilla

安装简易,拥有中文分词功能

Apache2

Ex-Crawler

由守护进程执行,使用数据库存储网页信息

GPLv3

Heritrix

严格遵照robots文件的排除指示和META robots标签

LGPL

heyDr

轻量级开源多线程垂直检索爬虫框架

GPLv3

ItSucks

提供swing GUI操作界面

不详

jcrawl

轻量、性能优良,可以从网页抓取各种类型的文件

Apache

JSpider

功能强大,容易扩展

LGPL

Leopdo

包括全文和分类垂直搜索,以及分词系统

Apache

MetaSeeker

网页抓取、信息提取、数据抽取工具包,操作简单

不详

Playfish

通过XML配置文件实现高度可定制性与可扩展性

MIT

Spiderman

灵活、扩展性强,微内核+插件式架构,通过简单的配置就可以完成数据抓取,无需编写一句代码

Apache

webmagic

功能覆盖整个爬虫生命周期,使用Xpath和正则表达式进行链接和内容的提取

Apache

Web-Harvest

运用XSLT、XQuery、正则表达式等技术来实现对Text或XML的操作,具有可视化的界面

BSD

WebSPHINX

由两部分组成:爬虫工作平台和WebSPHINX类包

Apache

YaCy

基于P2P的分布式Web搜索引擎

GPL

Python

QuickRecon

具有查找子域名名称、收集电子邮件地址并寻找人际关系等功能

GPLv3

PyRailgun

简洁、轻量、高效的网页抓取框架

MIT

Scrapy

基于Twisted的异步处理框架,文档齐全

BSD

C++

hispider

支持多机分布式下载, 支持网站定向下载

BSD

larbin

高性能的爬虫软件,只负责抓取不负责解析

GPL

Methabot

经过速度优化、可抓取WEB、FTP及本地文件系统

不详

Methanol

模块化、可定制的网页爬虫,速度快

不详

C#

NWebCrawler

统计信息、执行过程可视化

GPLv2

Sinawler

国内第一个针对微博数据的爬虫程序,功能强大

GPLv3

spidernet

以递归树为模型的多线程web爬虫程序,支持以GBK (gb2312)和utf8编码的资源,使用sqlite存储数据

MIT

Web Crawler

多线程,支持抓取PDF/DOC/EXCEL等文档来源

LGPL

网络矿工

功能丰富,毫不逊色于商业软件

BSD

PHP

OpenWebSpider

开源多线程网络爬虫,有许多有趣的功能

不详

PhpDig

适用于专业化强、层次更深的个性化搜索引擎

GPL

Snoopy

具有采集网页内容、提交表单功能

GPL

ThinkUp

采集推特、脸谱等社交网络数据的社会媒体视角引擎,可进行交互分析并将结果以可视化形式展现

GPL

微购

可采集淘宝、京东、当当等300多家电子商务数据

GPL

ErLang

Ebot

可伸缩的分布式网页爬虫

GPLv3

Ruby

Spidr

可将一个或多个网站、某个链接完全抓取到本地

MIT


     五岳之巅原创,转载请注明出处。


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

开源Python网络爬虫框架Scrapy

$
0
0

介绍:

所谓网络爬虫,就是一个在网上到处或定向抓取数据的程序,当然,这种说法不够专业,更专业的描述就是,抓取特定网站网页的HTML数据。不过由于一个网站的网页很多,而我们又不可能事先知道所有网页的URL地址,所以,如何保证我们抓取到了网站的所有HTML页面就是一个有待考究的问题了。

一般的方法是,定义一个入口页面,然后一般一个页面会有其他页面的URL,于是从当前页面获取到这些URL加入到爬虫的抓取队列中,然后进入到新新页面后再递归的进行上述的操作,其实说来就跟深度遍历或广度遍历一样。

上面介绍的只是爬虫的一些概念而非搜索引擎,实际上搜索引擎的话其系统是相当复杂的,爬虫只是搜索引擎的一个子系统而已。下面介绍一个开源的爬虫框架Scrapy。

一、概述

Scrapy是一个用 Python 写的 Crawler Framework ,简单轻巧,并且非常方便,并且官网上说已经在实际生产中在使用了,不过现在还没有 Release 版本,可以直接使用他们的 Mercurial 仓库里抓取源码进行安装。

Scrapy 使用 Twisted 这个异步网络库来处理网络通讯,架构清晰,并且包含了各种中间件接口,可以灵活的完成各种需求。整体架构如下图所示:

image

绿线是数据流向,首先从初始 URL 开始,Scheduler 会将其交给 Downloader 进行下载,下载之后会交给 Spider 进行分析,Spider 分析出来的结果有两种:一种是需要进一步抓取的链接,例如之前分析的“下一页”的链接,这些东西会被传回 Scheduler ;另一种是需要保存的数据,它们则被送到 Item Pipeline 那里,那是对数据进行后期处理(详细分析、过滤、存储等)的地方。另外,在数据流动的通道里还可以安装各种中间件,进行必要的处理。

二、组件

1、Scrapy Engine(Scrapy引擎)

Scrapy引擎是用来控制整个系统的数据处理流程,并进行事务处理的触发。更多的详细内容可以看下面的数据处理流程。

2、Scheduler(调度)

调度程序从Scrapy引擎接受请求并排序列入队列,并在Scrapy引擎发出请求后返还给他们。

3、Downloader(下载器)

下载器的主要职责是抓取网页并将网页内容返还给蜘蛛( Spiders)。

4、Spiders(蜘蛛)

蜘蛛是有Scrapy用户自己定义用来解析网页并抓取制定URL返回的内容的类,每个蜘蛛都能处理一个域名或一组域名。换句话说就是用来定义特定网站的抓取和解析规则。

蜘蛛的整个抓取流程(周期)是这样的:

  1. 首先获取第一个URL的初始请求,当请求返回后调取一个回调函数。第一个请求是通过调用start_requests()方法。该方法默认从start_urls中的Url中生成请求,并执行解析来调用回调函数。
  2. 在回调函数中,你可以解析网页响应并返回项目对象和请求对象或两者的迭代。这些请求也将包含一个回调,然后被Scrapy下载,然后有指定的回调处理。
  3. 在回调函数中,你解析网站的内容,同程使用的是Xpath选择器(但是你也可以使用BeautifuSoup, lxml或其他任何你喜欢的程序),并生成解析的数据项。
  4. 最后,从蜘蛛返回的项目通常会进驻到项目管道。

5、Item Pipeline(项目管道)

项目管道的主要责任是负责处理有蜘蛛从网页中抽取的项目,他的主要任务是清晰、验证和存储数据。当页面被蜘蛛解析后,将被发送到项目管道,并经过几个特定的次序处理数据。每个项目管道的组件都是有一个简单的方法组成的Python类。他们获取了项目并执行他们的方法,同时他们还需要确定的是是否需要在项目管道中继续执行下一步或是直接丢弃掉不处理。

项目管道通常执行的过程有:

  1. 清洗HTML数据
  2. 验证解析到的数据(检查项目是否包含必要的字段)
  3. 检查是否是重复数据(如果重复就删除)
  4. 将解析到的数据存储到数据库中

6、Downloader middlewares(下载器中间件)

下载中间件是位于Scrapy引擎和下载器之间的钩子框架,主要是处理Scrapy引擎与下载器之间的请求及响应。它提供了一个自定义的代码的方式来拓展 Scrapy的功能。下载中间器是一个处理请求和响应的钩子框架。他是轻量级的,对Scrapy尽享全局控制的底层的系统。

7、Spider middlewares(蜘蛛中间件)

蜘蛛中间件是介于Scrapy引擎和蜘蛛之间的钩子框架,主要工作是处理蜘蛛的响应输入和请求输出。它提供一个自定义代码的方式来拓展Scrapy的功能。蛛中间件是一个挂接到Scrapy的蜘蛛处理机制的框架,你可以插入自定义的代码来处理发送给蜘蛛的请求和返回蜘蛛获取的响应内容和项目。

8、Scheduler middlewares(调度中间件)

调度中间件是介于Scrapy引擎和调度之间的中间件,主要工作是处从Scrapy引擎发送到调度的请求和响应。他提供了一个自定义的代码来拓展Scrapy的功能。

三、数据处理流程

Scrapy的整个数据处理流程有Scrapy引擎进行控制,其主要的运行方式为:

  1. 引擎打开一个域名,时蜘蛛处理这个域名,并让蜘蛛获取第一个爬取的URL。
  2. 引擎从蜘蛛那获取第一个需要爬取的URL,然后作为请求在调度中进行调度。
  3. 引擎从调度那获取接下来进行爬取的页面。
  4. 调度将下一个爬取的URL返回给引擎,引擎将他们通过下载中间件发送到下载器。
  5. 当网页被下载器下载完成以后,响应内容通过下载中间件被发送到引擎。
  6. 引擎收到下载器的响应并将它通过蜘蛛中间件发送到蜘蛛进行处理。
  7. 蜘蛛处理响应并返回爬取到的项目,然后给引擎发送新的请求。
  8. 引擎将抓取到的项目项目管道,并向调度发送请求。
  9. 系统重复第二部后面的操作,直到调度中没有请求,然后断开引擎与域之间的联系。

安装:

Scrapy是一个快速,高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便的修改。它也提供了多种类型爬虫的基类,如 BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。

下面介绍Scrapy在windows下的安装:

首先下载windows版: Scrapy-0.15.0.2842.win32.exe,直接安装。

安装之后不能直接运行scrapy提供的test,会提示错误,因为scrapy基于其他一些python库,需要把这些库都安装才行。

Twisted:Twisted Matrix 是一种用来进行网络服务和应用程序编程的纯 Python 框架,虽然 Twisted Matrix 中有大量松散耦合的模块化组件,但该框架的中心概念还是非阻塞异步服务器这一思想。Twisted的安装也非常简单,在这里直接下载windows平台下的相应版本即可: http://pypi.python.org/packages/2.7/T/Twisted/

zope.interface:在这里下载 http://pypi.python.org/pypi/zope.interface/3.8.0#downloads。zope.interface没有提供windows平台下的exe版,只提供了windows平台下的egg包。

ez_setup:下载 http://pypi.python.org/pypi/ez_setup,安装。将egg文件放置在{python安装目录}\Scripts目录下。

打开CMD并切换至scripts目录,easy_install zope.interface-3.8.0-py2.6-win32.egg安装。

w3lib:zope.interface问题解决之后还会提示缺少w3lib,下载 http://pypi.python.org/pypi/w3lib后安装即可

libxml2:使用scrapy的html解析功能时,会提示你缺少libxml2,所以我们先把这个也装上,地址 http://xmlsoft.org/sources/win32/python/,下载相应的版本即可。

至此就可以使用Scrapy玩spider了,大家可以根据文档写一个简单的爬虫试试,实际上使用scrapy做一个简易的爬虫甚至只需要几行代码就可以了,以后有空再详细说说使用方法,本文不做更多描述。

入门:

本文参考Scrapy Tutorial里面的文档,翻译出来加上自己的理解,供大家学习。

在本文中,我们将学会如何使用Scrapy建立一个爬虫程序,并爬取指定网站上的内容,这一切在Scrapy框架内实现将是很简单轻松的事情。

本教程主要内容包括一下四步:

1.创建一个新的Scrapy Project
2.定义你需要从网页中提取的元素Item
3.实现一个Spider类,通过接口完成爬取URL和提取Item的功能
4.实现一个Item PipeLine类,完成Item的存储功能

新建工程

首先,为我们的爬虫新建一个工程,首先进入一个目录(任意一个我们用来保存代码的目录),执行:

[python]view plaincopy

  1. scrapy startproject Domz 

最后的Domz就是项目名称。这个命令会在当前目录下创建一个新目录Domz,结构如下:

[python]view plaincopy

  1. dmoz/ 
  2.    scrapy.cfg    
  3.    dmoz/ 
  4.        __init__.py 
  5.        items.py 
  6.        pipelines.py 
  7.        settings.py 
  8.        spiders/ 
  9.            __init__.py 

scrapy.cfg: 项目配置文件

items.py: 需要提取的数据结构定义文件
pipelines.py: 管道定义,用来对items里面提取的数据做进一步处理,如保存等
settings.py: 爬虫配置文件
spiders: 放置spider的目录

定义Item

在items.py里面定义我们要抓取的数据:

[python]view plaincopy

  1. from scrapy.item import Item, Field 
  2. class DmozItem(Item): 
  3.    title = Field() 
  4.    link = Field() 
  5.    desc = Field() 

这里我们需要获取dmoz页面上的标题,链接,描述,所以定义一个对应的items结构,不像Django里面models的定义有那么多种类的Field,这里只有一种就叫Field(),再复杂就是Field可以接受一个default值。

实现Spider

spider只是一个继承字scrapy.spider.BaseSpider的Python类,有三个必需的定义的成员

name:名字,这个spider的标识
start_urls:一个url列表,spider从这些网页开始抓取
parse(): 一个方法,当start_urls里面的网页抓取下来之后需要调用这个方法解析网页内容,同时需要返回下一个需要抓取的网页,或者返回items列表

所以在spiders目录下新建一个spider,dmoz_spider.py:

[python]view plaincopy

  1. class DmozSpider(BaseSpider): 
  2.    name = "dmoz.org"
  3.    start_urls = [ 
  4. "http://www.dmoz.org/Computers/Programming/Languages/Python/Books/", 
  5. "http://www.dmoz.org/Computers/Programming/Languages/Python/Resources/"
  6.    ] 
  7. def parse(self, response): 
  8.        filename = response.url.split("/")[-2] 
  9.        open(filename, 'wb').write(response.body) 

提取Item

提取数据到Items里面,主要用到XPath提取网页数据:

scrapy有提供两个XPath选择器,HtmlXPathSelector和XmlXPathSelector,一个用于HTML,一个用于XML,XPath选择器有三个方法

select(xpath): 返回一个相对于当前选中节点的选择器列表(一个XPath可能选到多个节点)
extract(): 返回选择器(列表)对应的节点的字符串(列表)
re(regex): 返回正则表达式匹配的字符串(分组匹配)列表
一种很好的方法是在Shell里面对XPath进行测试:

[python]view plaincopy

  1. scrapy shell http://www.dmoz.org/Computers/Programming/Languages/Python/Books/ 

现在修改parse()方法看看如何提取数据到items里面去:

[python]view plaincopy

  1. def parse(self, response): 
  2.       hxs = HtmlXPathSelector(response) 
  3.       sites = hxs.select('//ul/li') 
  4.       items = [] 
  5. for site in sites: 
  6.           item = DmozItem() 
  7.           item['title'] = site.select('a/text()').extract() 
  8.           item['link'] = site.select('a/@href').extract() 
  9.           item['desc'] = site.select('text()').extract() 
  10.           items.append(item) 
  11. return items 

实现PipeLine

PipeLine用来对Spider返回的Item列表进行保存操作,可以写入到文件、或者数据库等。

PipeLine只有一个需要实现的方法:process_item,例如我们将Item保存到一个文件中:

[python]view plaincopy

  1. def __init__(self): 
  2. self.file = open('jingdong.txt', 'wb') 
  3. def process_item(self, item, spider): 
  4. self.file.write(item['title'] + '\t'+ item['link'] + '\t' + item['desc']+'\n') 

到现在,我们就完成了一个基本的爬虫的实现,可以输入下面的命令来启动这个Spider:

[python]view plaincopy

  1. scrapy crawl dmoz.org 

Scrapy之URL解析与递归爬取:

前面介绍了Scrapy如何实现一个最简单的爬虫,但是这个Demo里只是对一个页面进行了抓取。在实际应用中,爬虫一个重要功能是”发现新页面”,然后递归的让爬取操作进行下去。

发现新页面的方法很简单,我们首先定义一个爬虫的入口URL地址,比如Scrapy入门教程中的start_urls,爬虫首先将这个页面的内容抓取之后,解析其内容,将所有的链接地址提取出来。这个提取的过程是很简单的,通过一个html解析库,将这样的节点内容提取出来,href参数的值就是一个新页面的URL。获取这个URL值之后,将其加入到任务队列中,爬虫不断的从队列中取URL即可。这样,只需要为爬虫定义一个入口的URL,那么爬虫就能够自动的爬取到指定网站的绝大多数页面。

当然,在具体的实现中,我们还需要对提取的URL做进一步处理:

1. 判断URL指向网站的域名,如果指向的是外部网站,那么可以将其丢弃
2. URL去重,可以将所有爬取过的URL存入数据库中,然后查询新提取的URL在数据库中是否存在,如果存在的话,当然就无需再去爬取了。

下面介绍一下如何在Scrapy中完成上述这样的功能。

我们只需要改写spider的那个py文件即可,修改parse()方法代码如下:

[python]view plaincopy

  1. from scrapy.selector import HtmlXPathSelector 
  2. def parse(self, response): 
  3.     hxs = HtmlXPathSelector(response) 
  4.     items = [] 
  5.     newurls = hxs.select('//a/@href').extract() 
  6.     validurls = [] 
  7. for url in newurls: 
  8. #判断URL是否合法
  9. if true: 
  10.                         validurls.append(url) 
  11.         items.extend([self.make_requests_from_url(url).replace(callback=self.parse) for url in validurls]) 
  12.         sites = hxs.select('//ul/li') 
  13.         items = [] 
  14. for site in sites: 
  15.                 item = DmozItem() 
  16.                 item['title'] = site.select('a/text()').extract() 
  17.                 item['link'] = site.select('a/@href').extract() 
  18.                 item['desc'] = site.select('text()').extract() 
  19.                 items.append(item) 
  20. return items 

 

全文转载自: http://blog.csdn.net/zbyufei/article/details/7554322


  青春就应该这样绽放   游戏测试:三国时期谁是你最好的兄弟!!   你不得不信的星座秘密

kbengine v0.1.8 发布,开源mmog服务端引擎

$
0
0

KBEngine是一款开源mmog服务端引擎, 使用统一协议能够轻松与前端对接,能轻松使用unity3d、 ogre、 cocos2d、 html5等作为前端表现。

底层框架由c++编写, 逻辑层使用python, 开发者无需重复实现一些通用的底层服务端技术, 使开发者能够真正集中精力到游戏开发上来, 快速打造各种游戏。

(经常被问到承载上限, kbengine底层架构被设计为多进程分布式动态负载均衡方案, 理论上只需要不断扩展硬件就能够不断增加承载上限, 单台机器的承载上限取决于游戏逻辑本身的复杂度。)

官方网站:

http://www.kbengine.org

demo视频

http://i.youku.com/kbengine

Selenium2.41.0—获取动态资源 (转)

$
0
0

一概述

    获取动态资源,可以使用HtmlUnit,但是其对JS的支持还是不够完善。相对与HtmlUnit还有一种驱动浏览器的下载还原工具Selenium。可以打开浏览器,获取网页,下载解析,支持dom,js,解析效果更好,但是打开浏览器速度方面有一定损失。个人实验,禁用CSS,图片下载,速度还尚可。

    Selenium也是自动化测试工具,支持驱动不同的浏览器,Firefox,IE,Chrome等,也包含HtmlUnit提供的驱动实现。

    本文描述Selenium驱动Firefox,请求响应,设置cookies,驱动JS等方法,用他登录某主流weibo还是不错的。

 

二 版本

 

Xml代码 复制代码  收藏代码
  1. <dependency>  
  2.      <groupId>org.seleniumhq.selenium </groupId>  
  3.      <artifactId>selenium-java </artifactId>  
  4.      <version>2.41.0 </version>  
  5. </dependency>  
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>2.41.0</version></dependency>

 

三 典型应用

1)打开Google,搜索baidu

可以看到Firefox从打开,填写表单,到提交打开新页面过程。

 

Java代码 复制代码  收藏代码
  1. /** 
  2.  * 打开google搜索百度 
  3.  *  
  4.  * @param queryStr 
  5.  */  
  6. public  static  void demo() {  
  7.     String url = "http://www.google.com.hk";  
  8.   
  9.     WebDriver webDriver =  new FirefoxDriver();  
  10.     // 打开google  
  11.     webDriver.get(url);  
  12.   
  13.     // 使用Selenium的dom模型获取form  
  14.     WebElement webElement = webDriver.findElement(By.name("q"));  
  15.     webElement.sendKeys("baidu");  
  16.     webElement.submit();  
  17.   
  18.     // 通过判断 title 内容等待搜索页面加载完毕,Timeout 设置10秒  
  19.   
  20.     ( new WebDriverWait(webDriver, 100)).until( new ExpectedCondition<Boolean>() {  
  21.          public Boolean apply(WebDriver d) {  
  22.              return d.getTitle().toLowerCase().indexOf("baidu") != -1;  
  23.         }  
  24.     });  
  25.   
  26.     String responseBody = webDriver.getPageSource();  
  27.     System.out.println("Response : " + responseBody);  
  28.   
  29.     // 关闭浏览器  
  30.     webDriver.close();  
  31. }  
/**
 * 打开google搜索百度
 * 
 * @param queryStr
 */
public static void demo() {
	String url = "http://www.google.com.hk";

	WebDriver webDriver = new FirefoxDriver();
	// 打开google
	webDriver.get(url);

	// 使用Selenium的dom模型获取form
	WebElement webElement = webDriver.findElement(By.name("q"));
	webElement.sendKeys("baidu");
	webElement.submit();

	// 通过判断 title 内容等待搜索页面加载完毕,Timeout 设置10秒

	(new WebDriverWait(webDriver, 100)).until(new ExpectedCondition<Boolean>() {
		public Boolean apply(WebDriver d) {
			return d.getTitle().toLowerCase().indexOf("baidu") != -1;
		}
	});

	String responseBody = webDriver.getPageSource();
	System.out.println("Response : " + responseBody);

	// 关闭浏览器
	webDriver.close();
}

 

2)获取动态网页

与下面的请求响应一样,打开页面等待加载完毕即可,JS填充页面,AJAX都OK。

 

四 样例

1)请求响应

 

Java代码 复制代码  收藏代码
  1. public  static  void main(String[] args)  throws Exception {  
  2.     String url = "http://www.google.com";  
  3.     WebDriver webDriver =  new FirefoxDriver();  
  4.     // 打开google  
  5.     webDriver.get(url);  
  6.         String responseBody = webDriver.getPageSource();    
  7.         System.out.println("Response : " + responseBody);    
  8.     webDriver.quit();  
  9. }  
public static void main(String[] args) throws Exception {
	String url = "http://www.google.com";
	WebDriver webDriver = new FirefoxDriver();
	// 打开google
	webDriver.get(url);
        String responseBody = webDriver.getPageSource();  
        System.out.println("Response : " + responseBody);  
	webDriver.quit();
}

 

(2)配置不加载资源

 

Java代码 复制代码  收藏代码
  1. /** 
  2.  * 获得不加载 css,图片,flash的浏览器 
  3.  * @return 
  4.  */  
  5. public WebDriver getNoResouceWebDriver(){  
  6.     FirefoxProfile firefoxProfile =  new FirefoxProfile();  
  7.     // 去掉css  
  8.     firefoxProfile.setPreference("permissions.default.stylesheet", 2);  
  9.     // 去掉图片  
  10.     firefoxProfile.setPreference("permissions.default.image", 2);  
  11.     // 去掉flash  
  12.     firefoxProfile.setPreference("dom.ipc.plugins.enabled.libflashplayer.so",  false);  
  13.      return  new FirefoxDriver(firefoxProfile);  
  14. }  
/**
 * 获得不加载 css,图片,flash的浏览器
 * @return
 */
public WebDriver getNoResouceWebDriver(){
	FirefoxProfile firefoxProfile = new FirefoxProfile();
	// 去掉css
	firefoxProfile.setPreference("permissions.default.stylesheet", 2);
	// 去掉图片
	firefoxProfile.setPreference("permissions.default.image", 2);
	// 去掉flash
	firefoxProfile.setPreference("dom.ipc.plugins.enabled.libflashplayer.so", false);
	return new FirefoxDriver(firefoxProfile);
}

 

(3)配置Firefox路径

启动报找不到Firefox的时候可以使用:System.setProperty("webdriver.firefox.bin", firefoxPath)

 

(4)Cookies

1)设置Cookies

 

Java代码 复制代码  收藏代码
  1. public  void setCookies(WebDriver,webDriver,Map<String, String> cookies) {  
  2.      if (cookies !=  null && cookies.size() > 0) {  
  3.          for (Entry<String, String> c : cookies.entrySet()) {  
  4.             Cookie cookie =  new Cookie(c.getKey(), c.getValue());  
  5.             webDriver.manage().addCookie(cookie);  
  6.   
  7.             System.out.println("Set Cookies : " + c.getKey() + " | " + c.getValue());  
  8.         }  
  9.     }  
  10. }  
public void setCookies(WebDriver,webDriver,Map<String, String> cookies) {
	if (cookies != null && cookies.size() > 0) {
		for (Entry<String, String> c : cookies.entrySet()) {
			Cookie cookie = new Cookie(c.getKey(), c.getValue());
			webDriver.manage().addCookie(cookie);

			System.out.println("Set Cookies : " + c.getKey() + " | " + c.getValue());
		}
	}
}

 

2)获取响应Cookies

 

Java代码 复制代码  收藏代码
  1. public Map<String,String> getCookies(WebDriver webDriver){  
  2.     Set<Cookie> cookies = webDriver.manage().getCookies();  
  3.     Map<String, String> responseCookies =  new HashMap<String,String>();  
  4.      for (Cookie c : cookies) {  
  5.         responseCookies.put(c.getName(), c.getValue());  
  6.     }  
  7.       
  8.      return responseCookies;  
  9. }  
public Map<String,String> getCookies(WebDriver webDriver){
	Set<Cookie> cookies = webDriver.manage().getCookies();
	Map<String, String> responseCookies = new HashMap<String,String>();
	for (Cookie c : cookies) {
		responseCookies.put(c.getName(), c.getValue());
	}
	return responseCookies;
}

 

3)清理Cookies

 

Java代码 复制代码  收藏代码
  1. /** 
  2.  * 清除所有cookie 
  3.  */  
  4. public  void clearCookies(WebDriver webDriver) {  
  5.     webDriver.manage().deleteAllCookies();  
  6. }  
/**
 * 清除所有cookie
 */
public void clearCookies(WebDriver webDriver) {
	webDriver.manage().deleteAllCookies();
}

 

(5)驱动JS

Selenium 的Dom对不可见的Element(html有但是CSS属性为不显示等)找不到,这时候使用JS操作和提交是个不错的选择:

 

Java代码 复制代码  收藏代码
  1. public  void doWeb(WebDriver webDriver) {  
  2.     StringBuilder js =  new StringBuilder();  
  3.     js.append("document.getElementsByName('username')[1].value='").append(WeiboAccount.USERNAME)  
  4.             .append("';");  
  5.     js.append("document.getElementsByName('password')[1].value='").append(WeiboAccount.PASSWORD)  
  6.             .append("';");  
  7.     js.append("document.getElementsByClassName('W_btn_g')[1].click();");  
  8.     ((JavascriptExecutor) webDriver).executeScript(js.toString());  
  9.   
  10.     ( new WebDriverWait(webDriver, 100)).until( new ExpectedCondition<Boolean>() {  
  11.          public Boolean apply(WebDriver d) {  
  12.              return d.getTitle().indexOf("我的首页") != -1;  
  13.         }  
  14.     });  
  15. }  

 转载至: http://shihlei.iteye.com/blog/2067716



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐




DRUID连接池的使用

$
0
0

                DRUID——为监控而生的DB池

  • DRUID介绍

    DRUID是阿里巴巴开源平台上一个数据库连接池实现,它结合了C3P0、DBCP、PROXOOL等DB池的优点,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况,可以说是针对监控而生的DB连接池(据说是目前最好的连接池,不知道速度有没有BoneCP快)。

 

  • 配置参数

和其它连接池一样DRUID的DataSource类为:com.alibaba.druid.pool.DruidDataSource,基本配置参数如下:

 

配置缺省值说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。
如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this)
jdbcUrl 连接数据库的url,不同数据库不一样。例如:
mysql : jdbc:mysql://10.20.153.104:3306/druid2
oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username 连接数据库的用户名
password 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter
driverClassName根据url自动识别这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis 有两个含义:
1) Destroy线程会检测连接的间隔时间2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun 不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis  
connectionInitSqls 物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别当数据库抛出一些不可恢复的异常时,抛弃连接
filters 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:
监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters 

类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系

 

  • 使用方法

DB数据源的使用方法也就是2种,一种是在代码中写死通过NEW操作符创建DataSSource,然后set一些连接属性,这里不在累述;另外一种是基于SPRING的配置方法,然后让SPRING的Context自动加载配置(以下配置文件默认都在项目根目录下conf文件夹中)

1、属性文件:application.properties(DataSource连接参数)

 

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.username=root
jdbc.password=1qaz!QAZ

 

2、SPRING配置文件:spring-base.xml

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=" http://www.springframework.org/schema/beans"
 xmlns:xsi=" http://www.w3.org/2001/XMLSchema-instance" xmlns:batch=" http://www.springframework.org/schema/batch"
 xsi:schemaLocation=" http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

 

 <bean id="propertyConfigure"
  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
  <property name="locations">
   <list>
    <value>./conf/application.properties</value>
   </list>
  </property>
 </bean>

 

 <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
  init-method="init" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}" />
  <property name="url" value="${jdbc.url}" />
  <property name="username" value="${jdbc.username}" />
  <property name="password" value="${jdbc.password}" />
  <!-- 配置初始化大小、最小、最大 -->
  <property name="initialSize" value="1" />
  <property name="minIdle" value="1" />
  <property name="maxActive" value="10" />

  <!-- 配置获取连接等待超时的时间 -->
  <property name="maxWait" value="10000" />

  <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
  <property name="timeBetweenEvictionRunsMillis" value="60000" />

  <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
  <property name="minEvictableIdleTimeMillis" value="300000" />

  <property name="testWhileIdle" value="true" />

  <!-- 这里建议配置为TRUE,防止取到的连接不可用 -->
  <property name="testOnBorrow" value="true" />
  <property name="testOnReturn" value="false" />

  <!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
  <property name="poolPreparedStatements" value="true" />
  <property name="maxPoolPreparedStatementPerConnectionSize"
   value="20" />

  <!-- 这里配置提交方式,默认就是TRUE,可以不用配置 -->

  <property name="defaultAutoCommit" value="true" />

  <!-- 验证连接有效与否的SQL,不同的数据配置不同 -->
  <property name="validationQuery" value="select 1 " />
  <property name="filters" value="stat" />
  <property name="proxyFilters">
   <list>
    <ref bean="logFilter" />
   </list>
  </property>
 </bean>

 

 <bean id="logFilter" class="com.alibaba.druid.filter.logging.Slf4jLogFilter">
  <property name="statementExecutableSqlLogEnable" value="false" />
 </bean>

</beans>

上面红色标注部分为监控DB池连接执行监控,后面在做详细说明.

 

  • 监控方式

1、WEB方式监控配置

<servlet>
     <servlet-name>DruidStatView</servlet-name>
     <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
 </servlet>
 <servlet-mapping>
     <servlet-name>DruidStatView</servlet-name>
     <url-pattern>/druid/*</url-pattern>
 </servlet-mapping>
 <filter>
  <filter-name>druidWebStatFilter</filter-name>
  <filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
  <init-param>
   <param-name>exclusions</param-name>
   <param-value>/public/*,*.js,*.css,/druid*,*.jsp,*.swf</param-value>
  </init-param>
  <init-param>
   <param-name>principalSessionName</param-name>
   <param-value>sessionInfo</param-value>
  </init-param>
  <init-param>
   <param-name>profileEnable</param-name>
   <param-value>true</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>druidWebStatFilter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

 

把上面servlet配置添加到项目web.xml即可。然后运行Tomcat,浏览器输入 http://IP:PROT/druid

就可以打开Druid的监控页面了.

2、日志文件监控

Druid提供了多种日志文件监控 commons-logging、log4j等,这里我们主要使用slf4j和logback来进行日志监控配置。

首先要引入slf4j和logback相关的jar文件(从Maven公共仓库下载 http://search.maven.org/

<slf4j.version>1.7.7</slf4j.version>
<logback.version>1.1.2</logback.version>

 

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>${slf4j.version}</version>
 </dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-access</artifactId>
    <version>${logback.version}</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>${logback.version}</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>${logback.version}</version>
</dependency> 

 

接下配置logback的配置文件(./conf/logback.xml)

 

<configuration>

 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  <layout class="ch.qos.logback.classic.PatternLayout">
   <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
   </Pattern>
  </layout>
 </appender>

 <appender name="FILE" class="ch.qos.logback.core.FileAppender">
  <file>./logs/druid_info.log</file>
  <layout class="ch.qos.logback.classic.PatternLayout">
   <Pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</Pattern>
  </layout>
  <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
   <level>debug</level>
  </filter>
 </appender>

 <root level="DEBUG">
  <appender-ref ref="FILE" />
 </root>
</configuration>

 

最后就是写一个测试类进行测试

public class TestMain {

 public static void loadLoggerContext() {
  System.getProperties().put("logback.configurationFile", "./conf/logback.xml");
  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
  StatusPrinter.setPrintStream(System.err);
  StatusPrinter.print(lc);
 }

 public static void main(String[] args) {
  try {
   loadLoggerContext();
   FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("./conf/spring-base.xml");

  } catch (Exception e) {
   System.out.println(e);
  }
 }
}

 

 

 

 

 

 

 

 

 

 

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Nginx缓存解决方案:SRCache

$
0
0

前些天帮别人优化PHP程序,搞得灰头土脸,最后黔驴技穷开启了 FastCGI Cache,算是勉强应付过去了吧。不过FastCGI Cache不支持分布式缓存,当服务器很多的时候,冗余的浪费将非常严重,此外还有数据一致性问题,所以它只是一个粗线条的解决方案。

对此类问题而言, SRCache是一个细粒度的解决方案。其工作原理大致如下:

SRCache工作原理

SRCache工作原理

当问题比较简单的时候,通常SRCache和 Memc模块一起搭配使用。网上能搜索到一些相关的 例子,大家可以参考,这里就不赘述了。当问题比较复杂的时候,比如说缓存键的动态计算等,就不得不写一点代码了,此时 Lua模块是最佳选择。

闲言碎语不多讲,表一表Nginx配置文件长啥样:

lua_package_path '/path/to/vendor/?.lua;;';

init_by_lua_file /path/to/config.lua;

server {
    listen 80;
    server_name foo.com;

    root /path;
    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ /index.php$is_args$args;
    }

    location ~ \.php$ {
        set $key "";
        set $ttl 600;
        set $skip 1;

        rewrite_by_lua_file /path/to/guard.lua;

        srcache_fetch_skip $skip;
        srcache_store_skip $skip;
        srcache_fetch GET /memcached key=$key;
        srcache_store PUT /memcached key=$key&ttl=$ttl;

        add_header X-Srcache-Fetch $srcache_fetch_status;
        add_header X-Srcache-Store $srcache_store_status;

        try_files $uri =404;

        include fastcgi.conf;
        fastcgi_pass 127.0.0.1:9000;
    }

    location /memcached {
        internal;
        content_by_lua_file /path/to/data/memcached.lua;
    }
}

Nginx启动后,会载入config.lua中的配置信息。请求到达后,缺省情况下,SRCache为关闭状态,在guard.lua中,会对当前请求进行正则匹配,一旦匹配成功,那么就会计算出缓存键,并且把SRCache设置为开启状态,最后由memcached.lua完成读写。

看看「config.lua」文件的内容,它主要用来记录一些全局的配置信息:

config = {}

config["memcached"] = {
    {host = "127.0.0.1", port = "11211"},
    {host = "127.0.0.1", port = "11212"},
    {host = "127.0.0.1", port = "11213"},
}

config["ruleset"] = {
    {pattern = "/test", fields = {x = "number", y = "string"}},
}

看看「guard.lua」文件的内容,它主要用来计算缓存键,并开启SRCache模块:

local uri = string.match(ngx.var.request_uri, "[^?]+")

for _, rule in ipairs(config["ruleset"]) do
    local pattern = rule["pattern"]
    local option  = rule["option"]

    if ngx.re.match(uri, pattern, option or "") then
        local ttl = rule["ttl"]

        if ttl then
            ngx.var.ttl = ttl
        end

        local args = ngx.req.get_uri_args()

        local fields = rule["fields"]

        if fields then
            for name in pairs(args) do
                if fields[name] then
                    if fields[name] == "number" then
                        if not tonumber(args[name]) then
                            ngx.exit(OK)
                        end
                    end
                else
                    args[name] = nil
                end
            end
        end

        local key = {
            ngx.var.request_method, " ",
            ngx.var.scheme, "://",
            ngx.var.host, uri,
        }

        args = ngx.encode_args(args);

        if args ~= "" then
            key[#key + 1] = "?"
            key[#key + 1] = args
        end

        key = table.concat(key)
        key = ngx.md5(key)

        ngx.var.key = key

        ngx.var.skip = "0"

        break
    end
end

看看「memcached.lua」文件的内容,它主要通过 Resty库来读写Memcached:

local memcached = require "resty.memcached"

local key = ngx.var.arg_key

local index = ngx.crc32_long(key) % #config["memcached"] + 1

local host = config["memcached"][index]["host"]
local port = config["memcached"][index]["port"]

local memc, err = memcached:new()

if not memc then
    ngx.log(ngx.ERR, err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end

memc:set_timeout(100)

local ok, err = memc:connect(host, port)

if not ok then
    ngx.log(ngx.ERR, err)
    ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
end

local method = ngx.req.get_method()

if method == "GET" then
    local res, flags, err = memc:get(key)

    if err then
        ngx.log(ngx.ERR, err)
        ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
    end

    ngx.print(res)
elseif method == "PUT" then
    local value = ngx.req.get_body_data()
    local ttl = ngx.var.arg_ttl

    local ok, err = memc:set(key, value, ttl)

    if not ok then
        ngx.log(ngx.ERR, err)
        ngx.exit(ngx.HTTP_SERVICE_UNAVAILABLE)
    end
else
    ngx.exit(ngx.HTTP_NOT_ALLOWED)
end

memc:set_keepalive(1000, 10)

最后一个问题:如何判断缓存是否生效了?试试下面的命令:

shell> curl -v http://foo.com/test?x=123&y=abc< X-Srcache-Fetch: HIT< X-Srcache-Store: BYPASS

关于激活SRCache前后的性能对比,视环境的不同而不同,大家自行实验吧,不过绝对是数量级的提升,更重要的是这一切对业务层完全透明,别愣着了,快试试吧!

Linux下LVM的配置详解

$
0
0
 LVM是Logical Volume Manager(逻辑卷管理器)的简写,它为主机提供了更高层次的磁盘存储管理能力。LVM可以帮助系统管理员为应用与用户方便地分配存储空间。在LVM管理下的逻辑卷可以按需改变大小或添加移除。另外,LVM可以为所管理的逻辑卷提供定制的命名标识。因此,使用LVM主要是方便了对存储系统的管理,增加了系统的扩展性。
   一、准备lvm环境
  1.硬盘的准备
  添加了一块硬盘/dev/hdb。
  准备了三个分区,方案如下:容量为100M,仅为了实验准备。
  /dev/hdb1
  /dev/hdb2
  /dev/hdb3
  2.转换分区类型为lvm卷
  fdisk /dev/hdb
  t转换为lvm卷类型
  Device Boot      Start    End      Blocks  Id  System
  /dev/hdb1        1         208      98248+  8e  Linux LVM
  /dev/hdb2        209      416      98280   8e  Linux LVM
  /dev/hdb3        417      624      98280   8e  Linux LVM
  然后w保存并且
  #partprobe       /*使用磁盘分区生效*/
   二、lvm创建过程
  1.从硬盘驱动器分区中创建物理卷(physical volumes-PV)。
  2.从物理卷中创建卷组(volume groups-VG)
  3.从卷组中创建逻辑卷(logical volumes-LV),并分派逻辑卷挂载点,其中只有逻辑卷才可以写数据。
  lvm的最大的特点就是可以动态的调整分区的大小,并且可以随着分区容量的增长而增加磁盘空间的容量。
  LVM配置与创建
   三、LVM的物理卷PV
  1.相关命令
  pvcreate  创建PV
  pvscan    扫描PV
  pvdisplay 显示PV
  pvremove  删除PV
  partprobe
  2.创建物理卷
  如果以上容量不够,可以再添加其它分区到物理卷中。
[root@redhat ~]# pvcreate /dev/hdb1 /dev/hdb2
Physical volume “/dev/hdb1″ successfully created
Physical volume “/dev/hdb2″ successfully created
[root@redhat ~]# pvscan
PV /dev/hdb1         lvm2 [95.95 MB]
PV /dev/hdb2         lvm2 [95.98 MB]
Total: 2 [191.92 MB] / in use: 0 [0   ] / in no VG: 2 [191.92 MB]
[root@redhat ~]# pvdisplay
— NEW Physical volume —
PV Name               /dev/hdb1
VG Name
PV Size               95.95 MB
Allocatable           NO
PE Size (KByte)       0
Total PE              0
Free PE               0
Allocated PE          0
PV UUID               2Ni0Tx-oeSy-zGUP-t7KG-Fh22-0BUi-iyPhhQ
— NEW Physical volume —
PV Name               /dev/hdb2
VG Name
PV Size               95.98 MB
Allocatable           NO
PE Size (KByte)       0
Total PE                  0
Free PE                  0
Allocated PE           0
PV UUID               2XLXfY-V3L2-Mtsl-79U4-ovuJ-YaQf-YV9qHs
四、创建LVM的卷组VG
  1.相关命令
  vgcreate   创建VG
  vgscan     扫描VG
  vgdispaly
  vgextend
  vgreduce
  vgchange
  vgremove
  2.创建逻辑卷VG
[root@redhat ~]# vgcreate vg0 /dev/hdb1 /dev/hdb2
Volume group “vg0″ successfully created
[root@redhat ~]# vgscan
Reading all physical volumes.  This may take a while…
Found volume group “vg0″ using metadata type lvm2
[root@redhat ~]# vgdisplay
— Volume group —
VG Name               vg0
System ID
Format                lvm2
Metadata Areas        2
Metadata Sequence No  1
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                0
Open LV               0
Max PV                0
Cur PV                2
Act PV                2
VG Size               184.00 MB
PE Size               4.00 MB   /*分配的块的大小默认为4M*/
Total PE              46
Alloc PE / Size       0 / 0
Free  PE / Size       46 / 184.00 MB
VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH增加VG容量到1TB的方法:
vgcreate -s 16M vg0 /dev/hdb1 /dev/hdb2
  3.删除与添加逻辑卷
  [root@redhat ~]# vgreduce vg0 /dev/hdb2
  Removed “/dev/hdb2″ from volume group “vg0″
  [root@redhat ~]# vgextend vg0 /dev/hdb2
  Volume group “vg0″ successfully extended
   五、创建LVM的逻辑卷LV
  1.相关命令
  lvcreate
  lvscan
  lvdisplay
  lvextend
  lvreduce
  lvremove
  lvresize
  2.创建逻辑卷LV
[root@redhat ~]# lvcreate -L 184M -n data vg0
Logical volume “data” created
[root@redhat ~]# lvscan
ACTIVE     ‘/dev/vg0/data’ [184.00 MB] inherit
[root@redhat ~]# lvdisplay
— Logical volume —
LV Name                /dev/vg0/data
VG Name                vg0
LV UUID                HNKO5d-yRre-qVnP-ZT8D-fXir-XTeM-r6WjDX
LV Write Access        read/write
LV Status              available
# open                 0
LV Size                184.00 MB
Current LE             46
Segments               2
Allocation             inherit
Read ahead sectors     0
Block device           253:0
   六、挂载LVM的逻辑卷LV
  lv的格式化:
  mkfs.ext3 /dev/vg0/data
  mdkir /mnt/lvm
  mount /dev/vg0/data /mnt/lvm
  [root@redhat ~]# ls /mnt/lvm
  lost+found
  [root@redhat ~]# df -T
  文件系统      类型     1K-块        已用     可用 已用% 挂载点
  /dev/hda3     ext3     7625092   2219460   5012040  31% /
  /dev/hda1     ext3      101086     10006     85861  11% /boot
  tmpfs        tmpfs      150108         0    150108   0% /dev/shm
  /dev/mapper/vg0-data
  ext3      182469      5664    167385   4% /mnt/lvm
   七、LVM的容量调整
  LVM的容量调整可以在多个环节进行调整,比如:可以在物理卷上,VG上,以及LV上,都可以进行容量的扩展,这也是LVM它的一个优势所在。
  1.添加物理卷
  首先应卸载在使用过程中的LV,然后必须保证该磁盘的类型是lvm类型,才能添加进来。
[root@redhat ~]# umount /dev/vg0/data
[root@redhat ~]# pvcreate /dev/hdb3
Physical volume “/dev/hdb3″ successfully created
[root@redhat ~]# pvscan
PV /dev/hdb1   VG vg0   lvm2 [92.00 MB / 0    free]
PV /dev/hdb2   VG vg0   lvm2 [92.00 MB / 0    free]
PV /dev/hdb3            lvm2 [95.98 MB]
Total: 3 [279.98 MB] / in use: 2 [184.00 MB] / in no VG: 1 [95.98 MB]
  2.添加VG的容量
  把上面新添加的LVM磁盘加入到vg0卷组中。
[root@redhat ~]# vgextend vg0 /dev/hdb3
Volume group “vg0″ successfully extended
[root@redhat ~]# vgdisplay
— Volume group —
VG Name               vg0
System ID
Format                lvm2
Metadata Areas        3
Metadata Sequence No  5
VG Access             read/write
VG Status             resizable
MAX LV                0
Cur LV                1
Open LV               0
Max PV                0
Cur PV                3
Act PV                3
VG Size               276.00 MB
PE Size               4.00 MB
Total PE              69
Alloc PE / Size       46 / 184.00 MB
Free  PE / Size       23 / 92.00 MB
VG UUID               kL5CGk-5Odk-r3PK-9q0A-s94h-OHv4-BojBnH
  3.添加入LV中VG增珈的容量
  把新加入LVM磁盘的容量加入LV中。
  [root@redhat ~]# lvextend -L +92M /dev/vg0/data
  Extending logical volume data to 276.00 MB
  Logical volume data successfully resized
  [root@redhat ~]# lvscan
  ACTIVE      ‘/dev/vg0/data’ [276.00 MB] inherit
  [root@redhat ~]# resize2fs -f /dev/vg0/data
  resize2fs 1.39 (29-May-2006)
  Resizing the filesystem on /dev/vg0/data to 282624 (1k) blocks.
  The filesystem on /dev/vg0/data is now 282624 blocks long.
  如果不做这一步的话,在实现挂载的时候,发现LV的容量没有真正的加入进LV卷中,因为相关信息写入到了磁盘超级块中。
  4.挂载使用
  [root@redhat ~]# mount /dev/vg0/data /mnt/lvm
  [root@redhat ~]# df
  文件系统               1K-块        已用     可用 已用% 挂载点
  /dev/hda3              7625092   2219468   5012032  31% /
  /dev/hda1               101086     10006     85861  11% /boot
  tmpfs                   150108         0    150108   0% /dev/shm
  /dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
  LVM的卸载
   八、LVM的卸载方法
  如果不想使用LVM的话,可以卸载它, 卸载的方法与分区的删除方法类似,就是最后创建的最先删除。顺序如下:
  先删除LV
  再删除VG
  最后PV
  以前的LVM的分区应用fdisk转换成其它类型的文件系统,当普通分区使用。
   九、LVM的卸载过程
  1.umount取消挂载
[root@redhat ~]# df
文件系统               1K-块        已用     可用 已用% 挂载点
/dev/hda3              7625092   2219468   5012032  31% /
/dev/hda1               101086     10006     85861  11% /boot
tmpfs                   150108         0    150108   0% /dev/shm
/dev/mapper/vg0-data    273569      6168    256097   3% /mnt/lvm
[root@redhat ~]# umount /mnt/lvm
  2.删除LV逻辑卷
  [root@redhat ~]# lvremove /dev/vg0/data
  Do you really want to remove active logical volume “data”? [y/n]: y
  Logical volume “data” successfully removed
  3.删除VG卷组
  [root@redhat ~]# vgchange -a n vg0
  0 logical volume(s) in volume group “vg0″ now active
  说明:把vg0转换成休眠状态,实验中这一步可以不用。
  [root@redhat ~]# vgremove vg0
  Volume group “vg0″ successfully removed
  4.删除PV
[root@redhat ~]# pvscan 查看pv的情况
PV /dev/hdb1         lvm2 [95.95 MB]
PV /dev/hdb2         lvm2 [95.98 MB]
PV /dev/hdb3         lvm2 [95.98 MB]
Total: 3 [287.90 MB] / in use: 0 [0   ] / in no VG: 3 [287.90 MB]
[root@redhat ~]# pvremove /dev/hdb1 /dev/hdb2 /dev/hdb3
Attempt to close device ‘/dev/cdrom’ which is not open.
Labels on physical volume “/dev/hdb1″ successfully wiped
Labels on physical volume “/dev/hdb2″ successfully wiped
Labels on physical volume “/dev/hdb3″ successfully wiped
  5.最后就是用fdisk修改磁盘的类型了。


顺其自然EVO 2014-06-20 13:23 发表评论

Spring整合freemarker发送邮件

$
0
0

一. 背景知识

在上一篇博文:  使用JavaMail发送邮件和接受邮件, 我们学习了原生的JavaApi发送邮件, 我们会发现代码比较多, 特别是当邮件内容很丰富的时候, 我们需要在Java中拼装Html, 是不是觉得非常麻烦. 

下面我们使用一种比较简单的方法: spring + javaMail + freemarker, 使用freemarker模板引擎后, 我们就不用再在Java中拼装html.


二. 环境准备

废话不多说了, 下面我们准备下开发环境:

1. 所需Jar包:

spring.jar(2.5), commons-logging.jar, mail.jar, freemarker.jar, spring-webmvc.jar, activation.jar

2. 安装易邮邮件服务器, 这个我们在上一篇博文中有讲过, 这里就不再赘述.

3. D盘中放一张图片 "welcome.gif" 和一个word文件 "欢迎注册.docx" 以填充邮件内容.


三. 代码实现

1. 代码结构图如下:



2. 实体Bean:

/**
 * 用户对象
 */
public class User {
	private String username;
	private String password;

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}
2. 发邮件业务接口

public interface EmailService {
	public void sendEmail(User user);
}
3. 发邮件实现

public class EmailServiceImpl implements EmailService {

	private JavaMailSender mailSender;
	private FreeMarkerConfigurer freeMarkerConfigurer; 
	private static final String ENCODING = "utf-8";

	public void setMailSender(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

	public void setFreeMarkerConfigurer(FreeMarkerConfigurer freeMarkerConfigurer) {
		this.freeMarkerConfigurer = freeMarkerConfigurer;
	}
	

	/**
	 * 发送带附件的html格式邮件
	 */
	public void sendEmail(User user) {
		MimeMessage msg = null;
		try {
			msg = mailSender.createMimeMessage();
			MimeMessageHelper helper = new MimeMessageHelper(msg, true, ENCODING);
			helper.setFrom("service@estore.com");
			helper.setTo("zhangsan@estore.com");
			helper.setSubject(MimeUtility.encodeText("estore注册成功提示邮件", ENCODING, "B"));
			helper.setText(getMailText(user), true); // true表示text的内容为html
			
			// 添加内嵌文件,第1个参数为cid标识这个文件,第2个参数为资源
			helper.addInline("welcomePic", new File("d:/welcome.gif")); // 附件内容
			
			// 这里的方法调用和插入图片是不同的,解决附件名称的中文问题
			File file = new File("d:/欢迎注册.docx");
			helper.addAttachment(MimeUtility.encodeWord(file.getName()), file);
		} catch (Exception e) {
			throw new RuntimeException("error happens", e);
		} 
		mailSender.send(msg);
		System.out.println("邮件发送成功...");
	}

	/**
	 * 通过模板构造邮件内容,参数content将替换模板文件中的${content}标签。
	 */
	private String getMailText(User user) throws Exception {
		// 通过指定模板名获取FreeMarker模板实例
		Template template = freeMarkerConfigurer.getConfiguration().getTemplate("registe.html"); 
		// FreeMarker通过Map传递动态数据
		Map<String, String> map = new HashMap<String, String>(); 
		map.put("username", user.getUsername()); // 注意动态数据的key和模板标签中指定的属性相匹配
		map.put("password", user.getPassword());
		// 解析模板并替换动态数据,最终content将替换模板文件中的${content}标签。
		String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(template, map);
		return htmlText;
	}
}
4. spring核心配置

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/aop 
	http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
	http://www.springframework.org/schema/tx 
	http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"><bean id="freeMarker" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"><property name="templateLoaderPath" value="classpath:" /> <!-- 指定模板文件目录  --><property name="freemarkerSettings"><!-- 设置FreeMarker环境属性 --><props><prop key="template_update_delay">1800</prop> <!--刷新模板的周期,单位为秒 --><prop key="default_encoding">UTF-8</prop> <!--模板的编码格式 --><prop key="locale">zh_CN</prop> <!--本地化设置--></props></property></bean><!-- 注意:这里的参数(如用户名、密码)都是针对邮件发送者的 --><bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"><property name="host">  <value>localhost</value>  </property>  <property name="javaMailProperties">  <props>  <prop key="mail.smtp.auth">true</prop>  <prop key="mail.smtp.timeout">25000</prop>  </props>  </property>  <property name="username">  <value>service</value> <!-- 发送者用户名 --></property>  <property name="password">  <value>123</value> <!-- 发送者密码 --></property> </bean><bean id="emailService" class="com.zdp.service.impl.EmailServiceImpl"><property name="mailSender" ref="mailSender"></property><property name="freeMarkerConfigurer" ref="freeMarker"></property></bean></beans>    
5. 模板文件:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>  <meta http-equiv="content-type" content="text/html;charset=utf8">  </head>  <body>  恭喜您成功注册estore!<br/>您的用户名为:<font color='red' size='20'>${username}</font>,  
        	您的密码为:<font color='red' size='20'>${password}</font>  <img src='cid:welcomePic'/></body>  </html> 
6. 单元测试:

public class EmailServiceImplTest {
	@Test
	public void testSendEmail() {
		ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
		EmailService emailService = (EmailService) context.getBean("emailService");
		User user = new User();
		user.setUsername("zhangsan");
		user.setPassword("123");
		emailService.sendEmail(user);
	}
}
7. 效果图如下:




作者:zdp072 发表于2014-6-20 23:39:13 原文链接
阅读:7 评论:0 查看评论

JVM日志和参数的理解

$
0
0

转自:http://hot66hot.iteye.com/blog/2075819

写这篇wiki的目的:最近在调整Hbase的JVM,翻了些文档和wiki,想写点东西,给自己和想了解jvm日志和参数的同 学提供些帮助.


 一:理解GC日志格式,读GC日志的方法

1:开启日志

-verbose:gc 

-XX:+PrintGCDetails 

-XX:+PrintGCDateStamps

-Xloggc:/path/gc.log

-XX:+UseGCLogFileRotation  启用GC日志文件的自动转储 (Since Java)

-XX:NumberOfGClogFiles=1  GC日志文件的循环数目 (Since Java)

-XX:GCLogFileSize=1M  控制GC日志文件的大小 (Since Java)

-XX:+PrintGC包含-verbose:gc

-XX:+PrintGCDetails //包含-XX:+PrintGC

只要设置-XX:+PrintGCDetails 就会自动带上-verbose:gc和-XX:+PrintGC

-XX:+PrintGCDateStamps/-XX:+PrintGCTimeStamps 输出gc的触发时间

 


 2:新生代(Young GC)gc日志分析

Java代码    收藏代码
  1. 2014-02-28T11:59:00.638+0800: 766.537:[GC2014-02-28T11:59:00.638+0800: 766.537:  
  2. [ParNew: 1770882K->212916K(1835008K), 0.0834220 secs]  
  3. 5240418K->3814487K(24903680K), 0.0837310 secs] [Times: user=1.12 sys=0.02, real=0.08 secs]  

  2014-02-28T11:59:00 ...(时间戳):[GC(Young GC)(时间戳):[ParNew(使用ParNew作为年轻代的垃圾回收期):

1770882K(年轻代垃圾回收前的大小)->212916K(年轻代垃圾回收以后的大小)(1835008K)(年轻代的capacity), 0.0834220 secs(回收时间)]
5240418K(整个heap垃圾回收前的大小)->3814487K(整个heap垃圾回收后的大小)(24903680K)(heap的capacity), 0.0837310secs(回收时间)]
[Times: user=1.12(Young GC用户耗时) sys=0.02(Young GC系统耗时), real=0.08 secs(Young GC实际耗时)]

   其中 Young GC回收了1770882-212916=1557966K内存
Heap通过这次回收总共减少了 5240418-3814487=1425931 K的内存。1557966-1425931=132035K说明这次Young GC有约128M的内存被移动到了Old Gen,
 提示:进代量(Young->Old)需要重点观察,预防promotion failed.

 

 3:老年代(CMS old gc)分析

Java代码    收藏代码
  1. 2014-02-28T23:58:42.314+0800: 25789.661: [GC [1 CMS-initial-mark: 17303356K(23068672K)] 18642315K(24903680K), 1.0400410 secs] [Times: user=1.04 sys=0.00, real=1.04 secs]  
  2. 2014-02-28T23:58:43.354+0800: 25790.701: [CMS-concurrent-mark-start]  
  3. 2014-02-28T23:58:43.717+0800: 25791.064: [CMS-concurrent-mark: 0.315/0.363 secs] [Times: user=1.64 sys=0.02, real=0.37 secs]  
  4. 2014-02-28T23:58:43.717+0800: 25791.064: [CMS-concurrent-preclean-start]  
  5. 2014-02-28T23:58:43.907+0800: 25791.254: [CMS-concurrent-preclean: 0.181/0.190 secs] [Times: user=0.20 sys=0.01, real=0.19 secs]  
  6. 2014-02-28T23:58:43.907+0800: 25791.254: [CMS-concurrent-abortable-preclean-start]  
  7.  CMS: abort preclean due to time 2014-02-28T23:58:49.082+0800: 25796.429: [CMS-concurrent-abortable-preclean: 5.165/5.174 secs] [Times: user=5.40 sys=0.04, real=5.17 secs]  
  8. 2014-02-28T23:58:49.083+0800: 25796.430: [GC[YG occupancy: 1365142 K (1835008 K)]2014-02-28T23:58:49.083+0800: 25796.430: [Rescan (parallel) , 0.9690640 secs]2014-02-28T23:58:50.052+0800: 25797.399: [weak refs processing, 0.0006190 secs]2014-02-28T23:58:50.053+0800: 25797.400: [scrub string table, 0.0006290 secs] [1 CMS-remark: 17355150K(23068672K)] 18720292K(24903680K), 0.9706650 secs] [Times: user=16.49 sys=0.06, real=0.97 secs]  
  9. 2014-02-28T23:58:50.054+0800: 25797.401: [CMS-concurrent-sweep-start]  
  10. 2014-02-28T23:58:51.940+0800: 25799.287: [CMS-concurrent-sweep: 1.875/1.887 secs] [Times: user=2.03 sys=0.03, real=1.89 secs]  
  11. 2014-02-28T23:58:51.941+0800: 25799.288: [CMS-concurrent-reset-start]  
  12. 2014-02-28T23:58:52.067+0800: 25799.414: [CMS-concurrent-reset: 0.127/0.127 secs] [Times: user=0.13 sys=0.00, real=0.13 secs]  
  13. 2014-03-01T00:00:36.293+0800: 25903.640: [GC2014-03-01T00:00:36.293+0800: 25903.640: [ParNew: 1805234K->226801K(1835008K), 0.1020510 secs] 10902912K->9434796K(24903680K), 0.1023150 secs] [Times: user=1.35 sys=0.02, real=0.10 secs]  
  14. 2014-03-01T00:07:13.559+0800: 26300.906: [GC2014-03-01T00:07:13.559+0800: 26300.906: [ParNew: 1799665K->248991K(1835008K), 0.0876870 secs] 14086673K->12612462K(24903680K), 0.0879620 secs] [Times: user=1.24 sys=0.01, real=0.09 secs]  

 CMS的gc日志分为一下几个步骤,重点关注initial-mark和remark这两个阶段,因为这两个阶段会stop进程。

初始标记(init mark):收集根引用,这是一个stop-the-world阶段。

并发标记(concurrent mark):这个阶段可以和用户应用并发进行。遍历老年代的对象图,标记出活着的对象。

并发预清理(concurrent preclean):这同样是一个并发的阶段。主要的用途也是用来标记,用来标记那些在前面标记之后,发生变化的引用。主要是为了缩短remark阶段的stop-the-world的时间。

重新标记(remark):这是一个stop-the-world的操作。暂停各个应用,统计那些在发生变化的标记。

并发清理(concurrent sweep):并发扫描整个老年代,回收一些在对象图中不可达对象所占用的空间。

并发重置(concurrent reset):重置某些数据结果,以备下一个回收周期

提示:红色为全部暂停阶段重点关注.

 

Java代码    收藏代码
  1. [GC [1 CMS-initial-mark: 17303356K(23068672K)] 18642315K(24903680K), 1.0400410 secs] [Times: user=1.04 sys=0.00, real=1.04 secs]  

 其中数字依表示标记前后old区的所有对象占内存大小和old的capacity,整个JavaHeap(不包括perm)所有对象占内存总的大小和JavaHeap的capacity。

 

Java代码    收藏代码
  1. [GC[YG occupancy: 1365142 K (1835008 K)]2014-02-28T23:58:49.083+0800: 25796.430:   
  2. [Rescan (parallel) , 0.9690640 secs]2014-02-28T23:58:50.052+0800: 25797.399:   
  3. [weak refs processing, 0.0006190 secs]2014-02-28T23:58:50.053+0800: 25797.400: [scrub string table, 0.0006290 secs]   
  4. [1 CMS-remark: 17355150K(23068672K)] 18720292K(24903680K), 0.9706650 secs] [Times: user=16.49 sys=0.06, real=0.97 secs]  

 Rescan (parallel)表示的是多线程处理young区和多线程扫描old+perm的总时间, parallel 表示多GC线程并行。

weak refs processing 处理old区的弱引用的总时间,用于回收native memory.等等


参考资料:

  https://blogs.oracle.com/jonthecollector/entry/the_unspoken_cms_and_printgcdetails

  https://blogs.oracle.com/poonam/entry/understanding_cms_gc_logs

 

4:老年代(CMS old GC ) concurrent mode failure日志

Java代码    收藏代码
  1. 2014-03-03T09:38:26.457+0800: 233373.804: [GC [1 CMS-initial-mark: 17319615K(23068672K)] 17351070K(24903680K), 0.0419440 secs]   
  2. [Times: user=0.04 sys=0.00, real=0.04 secs]  
  3. 2014-03-03T09:38:26.499+0800: 233373.846: [CMS-concurrent-mark-start]  
  4. 2014-03-03T09:38:28.175+0800: 233375.522: [GC2014-03-03T09:38:28.175+0800: 233375.522: [CMS2014-03-03T09:38:28.887+0800: 233376.234:   
  5. [CMS-concurrent-mark: 1.989/2.388 secs] [Times: user=14.37 sys=0.24, real=2.39 secs]  
  6.  (concurrent mode failure): 17473174K->8394653K(23068672K), 19.3309170 secs] 18319691K->8394653K(24903680K),   
  7.  [CMS Perm : 23157K->23154K(98304K)], 19.3311700 secs] [Times: user=22.18 sys=0.00, real=19.33 secs]  

 concurrent mode failure一般发生在CMS GC 运行过程中,老年代空间不足,引发MSC(Full GC)

上面的这条发日志说明CMS运行到CMS-concurrent-mark过程中就出现空间不足,产生并发失败(17319615K(23068672K)占77%),


解决思路:降低YGC频率,降低CMS GC触发时机,适当降低CMSInitiatingOccupancyFraction.

 

5:新生代(ParNew YGC)promotion failed日志 

Java代码    收藏代码
  1. 2014-02-27T21:19:42.460+0800: 210095.040: [GC 210095.040: [ParNew (promotion failed): 1887487K->1887488K(1887488K), 0.4818790 secs]210095.522: [CMS: 13706434K->7942818K(23068672K), 9.7152990 secs] 15358303K->7942818K(24956160K), [CMS Perm : 27424K->27373K(98304K)], 10.1974110 secs] [Times: user=12.06 sys=0.01, real=10.20 secs]  

 promotion failed一般发生在新生代晋升老年代时,老年代空间不够或连续空间不够却还没达到old区的触发值,引发Full Gc.

 

解决 思路:由于heap碎片,YGC晋升对象过大,过长.(mid/long Time Object),调整-XX:PretenureSizeThreshold=65535,-XX:MaxTenuringThreshold=6

 

6:system.gc()产生的Full GC日志

Java代码    收藏代码
  1. <strong>2014-01-21T17:44:01.554+0800: 50212.568: [Full GC (System) 50212.568:   
  2. [CMS: 943772K220K(2596864K), 2.3424070 secs] 1477000K->220K(4061184K), [CMS Perm : 3361K->3361K(98304K)], 2.3425410 secs] [Times: user=2.33 sys=0.01, real=2.34 secs]</strong>  

 

Full GC (System)意味着这是个system.gc调用产生的MSC。
“943772K->220K(2596864K), 2.3424070 secs”表示:这次MSC前后old区内总对象大小,old的capacity及这次MSC耗时。
“1477000K->220K(4061184K)”表示:这次MSC前后JavaHeap内总对象大小,JavaHeap的capacity。
“3361K->3361K(98304K)], 2.3425410 secs”表示:这次MSC前后Perm区内总对象大小,Perm区的capacity。


解决:
使用-XX:+DisableExplicitGC参数,System.gc()会变成空调用.
如果应用有地方大量使用direct memory 或 rmi,那么使用-XX:+DisableExplicitGC要小心。
可以使用-XX:+ExplicitGCInvokesConcurrent替换把 System.gc()从Full GC换成CMS GC.

 

原因:
DirectByteBuffer没有finalizer,native memory的清理工作是通过sun.misc.Cleaner自动完成
sun.misc.Cleaner是基于PhantomReference的清理工具,Full GC/Old GC会对old gen做reference processing,同时触发Cleaner对已死的DirectByteBuffer对象做清理。
如果长时间没有GC或者只做了young GC的话,不会触发old区Cleaner的工作,容易产生DirectMemory OOM.
参考: https://gist.github.com/rednaxelafx/1614952

RMI会做的是分布式GC。Sun JDK的分布式GC是用纯Java实现的,为RMI服务。 
参考: http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html

 

7:特殊的Full GC日志,根据动态计算直接进行MSC

Java代码    收藏代码
  1. 2014-02-13T13:48:06.349+0800: 7.092: [GC 7.092: [ParNew: 471872K->471872K(471872K), 0.0000420 secs]7.092: [CMS: 366666K->524287K(524288K), 27.0023450 secs] 838538K->829914K(996160K), [CMS Perm : 3196K->3195K(131072K)], 27.0025170 secs]  

 ParNew的时间特别短,jvm在minor gc前会首先确认old是不是足够大,如果不够大,这次young gc直接返回,进行MSC(Full GC)。


二:参数配置和理解

1:参数分类和说明

jvm参数分固定参数和非固定参数

1):固定参数

如:-Xmx,-Xms,-Xmn,-Xss.

2):非固定参数

如:

-XX:+<option> 启用选项

-XX:-<option> 不启用选项

-XX:<option>=<number> 给选项设置一个数字类型值,可跟单位,例如 128k, 2g

-XX:<option>=<string> 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.log

 

2:JVM可设置参数和默认值

1):-XX:+PrintCommandLineFlags

打印出JVM初始化完毕后所有跟最初的默认值不同的参数及它们的值,jdk1.5后支持.

线上建议打开,可以看到自己改了哪些值.

2):-XX:+PrintFlagsFinal

显示所有可设置的参数及"参数处理"后的默认值。参数本身只从JDK6 U21后支持

可是查看不同版本默认值,以及是否设置成功.输出的信息中"="表示使用的是初始默认值,而":="表示使用的不是初始默认值

如:jdk6/7 -XX:+MaxTenuringThreshold 默认值都是15,但是在使用CMS收集器后,jdk6默认4 , jdk7默认6.

 

Java代码    收藏代码
  1. [hbase96 logs]# java -version  
  2. java version "1.6.0_27-ea"  
  3. [hbase96 logs]# java -XX:+PrintFlagsInitial | grep MaxTenuringThreshold  
  4. intx MaxTenuringThreshold = 15 {product}  
  5. [hbase96 logs]# java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | grep MaxTenuringThreshold   
  6. intx MaxTenuringThreshold := 4 {product}   
  7.   
  8. [zw-34-71 logs]# java -version  
  9. java version "1.7.0_45"  
  10. [zw-34-71 logs]# java -XX:+PrintFlagsInitial | grep MaxTenuringThreshold  
  11. intx MaxTenuringThreshold = 15 {product}  
  12. [zw-34-71 logs]# java -XX:+PrintFlagsFinal -XX:+UseConcMarkSweepGC | grep MaxTenuringThreshold  
  13. intx MaxTenuringThreshold := 6 {product}  

 

3):-XX:+PrintFlagsInitial

 在"参数处理"之前所有可设置的参数及它们的值,然后直接退出程序.

这里的"参数处理"指: 检查参数之间是否有冲突,通过ergonomics调整某些参数的值等.

 

Java代码    收藏代码
  1. [hbase96 logs]# java -version  
  2. java version "1.6.0_27-ea"  
  3. [hbase96 logs]# java -XX:+PrintFlagsInitial | grep UseCompressedOops  
  4. bool UseCompressedOops = false {lp64_product}   
  5. [hbase96 logs]# java -XX:+PrintFlagsFinal | grep UseCompressedOops  
  6. bool UseCompressedOops := true {lp64_product}  

 

4)CMSInitiatingOccupancyFraction 默认值是多少 

jdk6/7:

Java代码    收藏代码
  1. #java -server -XX:+UseConcMarkSweepGC -XX:+PrintFlagsFinal | grep -P "CMSInitiatingOccupancyFraction|CMSTriggerRatio|MinHeapFreeRatio"  
  2. intx CMSInitiatingOccupancyFraction            = -1              {product}             
  3. intx CMSTriggerRatio                           = 80              {product}             
  4. uintx MinHeapFreeRatio                          = 40              {product}  

 

计算公式:

CMSInitiatingOccupancyFraction = (100 - MinHeapFreeRatio) + (CMSTriggerRatio * MinHeapFreeRatio / 100)

最终结果:  在jdk6/7中 CMSInitiatingOccupancyFraction默认值是92% .不是网上传的68%;  都这么传,是因为 "深入理解Java虚拟机"一书中是68%,但它用的是jdk5 , jdk5的CMSTriggerRatio默认值是20,坑爹...

 

三:JVM内存区域理解和相关参数

一图胜千言,直接上图

1):物理分代图.

 

物理分代是除G1之外的JVM 内存分配方式,jvm 通过-Xmx,-Xmn/newRatio等参数将jvm heap划分成物理固定大小,对于不同场景比例应该设置成多少很考验经验.

一篇JVM CMS优化讲解的非常好的文章:  how-tame-java-gc-pauses

 

2) 逻辑分代图(G1)

逻辑分代是以后的趋势(PS:jkd8连perm都不区分了。), 不需要使用者在纠结Xms/Xmn,SurvivorRatio等比例问题,采用动态算法调整分代大小。

 



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



冷门JS技巧 - think_fish

$
0
0

前端已经被玩儿坏了!像 console.log()可以向控制台输出图片等炫酷的玩意已经不是什么新闻了,像用||操作符给变量赋默认值也是人尽皆知的旧闻了,今天看到Quora上一个 帖子,瞬间又GET了好多前端技能,一些属于技巧,一些则是闻所未闻的冷知识,一时间还消化不过来。现分类整理出来分享给大家,也补充了一些平时的积累和扩展了一些内容。

HTML篇

浏览器地址栏运行JavaScript代码

这个很多人应该还是知道的,在浏览器地址栏可以直接运行JavaScript代码,做法是以javascript:开头后跟要执行的语句。比如:

javascript:alert('hello from address bar :)');

将以上代码贴到浏览器地址栏回车后alert正常执行,一个弹窗神现。

需要注意的是如果是通过copy paste代码到浏览器地址栏的话,IE及Chrome会自动去掉代码开头的javascript:,所以需要手动添加起来才能正确执行,而Firefox中虽然不会自动去掉,但它根本就不支持在地址栏运行JS代码,sigh~

这一技术在我的另一篇博文《 让Chrome 接管邮件连接,收发邮件更方便了》中有使用到,利用在浏览器地址栏中执行JavaScript代码将Gmail设置为系统的邮件接管程序。

浏览器地址栏运行HTML代码

如果说上面那条小秘密知道的人还算多的话,这条秘笈知道的人就要少一些了,在非IE内核的浏览器地址栏可以直接运行HTML代码!

比如在地址栏输入以下代码然后回车运行,会出现指定的页面内容。

data:text/html,<h1>Hello, world!</h1>

 

你造么,可以把浏览器当编辑器

还是浏览器地址栏上做文章,将以下代码贴到地址栏运行后浏览器变成了一个原始而简单的编辑器,与Windows自带的notepad一样,吼吼。

data:text/html, <html contenteditable>

 

归根结底多亏了HTML5中新加的contenteditable属性,当元素指定了该属性后,元素的内容成为可编辑状态。

推而广之,将以下代码放到console执行后,整个页面将变得可编辑,随意践踏吧~

document.body.contentEditable='true';

 

 

利用a标签自动解析URL

很多时候我们有从一个URL中提取域名,查询关键字,变量参数值等的需要,而万万没想到可以让浏览器方便地帮我们完成这一任务而不用我们写正则去抓取。方法就在JS代码里先创建一个a标签然后将需要解析的URL赋值给a的href属性,然后就得到了一切我们想要的了。

var a = document.createElement('a');
a.href = 'http://www.cnblogs.com/wayou/p/';
console.log(a.host);

利用这一原理,稍微扩展一下,就得到了一个更加健壮的解析URL各部分的通用方法了。下面代码来自 James的博客

function parseURL(url) {
var a = document.createElement('a');
a.href = url;
return {
source: url,
protocol: a.protocol.replace(':',''),
host: a.hostname,
port: a.port,
query: a.search,
params: (function(){
var ret = {},
seg = a.search.replace(/^\?/,'').split('&'),
len = seg.length, i = 0, s;
for (;i<len;i++) {
if (!seg[i]) { continue; }
s = seg[i].split('=');
ret[s[0]] = s[1];
}
return ret;
})(),
file: (a.pathname.match(/\/([^\/?#]+)$/i) || [,''])[1],
hash: a.hash.replace('#',''),
path: a.pathname.replace(/^([^\/])/,'/$1'),
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
segments: a.pathname.replace(/^\//,'').split('/')
};
}

页面拥有ID的元素会创建全局变量

在一张HTML页面中,所有设置了ID属性的元素会在JavaScript的执行环境中创建对应的全局变量,这意味着document.getElementById像人的阑尾一样显得多余了。但实际项目中最好老老实实该怎么写就怎么写,毕竟常规代码出乱子的机会要小得多。

<div id="sample"></div>
<script type="text/javascript">
console.log(sample);
</script>

加载CDN文件时,可以省掉HTTP标识

现在很流行的CDN即从专门的服务器加载一些通用的JS和CSS文件,出于安全考虑有的CDN服务器使用HTTPS方式连接,而有的是传统的HTTP,其实我们在使用时可以忽略掉这个,将它从URL中省去。

<script src="//domain.com/path/to/script.js"></script>

 这一点在之前一篇译文博客《 jQuery编程最佳实践》中也有提到。

利用script标签保存任意信息

将script标签设置为type='text'然后可以在里面保存任意信息,之后可以在JavaScript代码中很方便地获取。

<script type="text" id="template">
<h1>This won't display</h1>
</script>

 

var text = document.getElementById('template').innerHTML

CSS篇

关于CSS的恶作剧

相信你看完以下代码后能够预料到会出现什么效果。

*{
cursor: none!important;
}

简单的文字模糊效果

以下两行简单的CSS3代码可达到将文字模糊化处理的目的,出来的效果有点像使用PS的滤镜,so cool!

p {
color: transparent;
text-shadow: #111 0 0 5px;
}

 

 

垂直居中

有好多次博主都有这样的需求,垂直居中显示某个DIV,我们知道CSS中天然有水平居中的样式text-align:center。唯独这个垂直居中无解。

当然你可以将容器设置为display:table,然后将子元素也就是要垂直居中显示的元素设置为display:table-cell,然后加上vertical-align:middle来实现,但此种实现往往会因为display:table而破坏整体布局,那还不如直接用table标签了呢。

下面这个样式利用了translate来巧妙实现了垂直居中样式,需IE9+。

.center-vertical {
position: relative;
top: 50%;
transform: translateY(-50%);
}

相比而言,水平居中要简单得多,像上面提到的text-align:center,经常用到的技巧还有margin:0 auto。但对于margin大法也只在子元素宽度小于容器宽度时管用,当子元素宽度大于容器宽度时此法失效。

如法炮制,利用left和transform同样可实现水平居中,不过意义不大,毕竟text-align和margin差不多满足需求了。

.center-horizontal {
position: relative;
left: 50%;
transform: translateX(-50%);
}

 

多重边框

利用重复指定box-shadow来达到多个边框的效果

在线演示    

/*CSS Border with Box-Shadow Example*/
div {
box-shadow: 0 0 0 6px rgba(0, 0, 0, 0.2), 0 0 0 12px rgba(0, 0, 0, 0.2), 0 0 0 18px rgba(0, 0, 0, 0.2), 0 0 0 24px rgba(0, 0, 0, 0.2);
height: 200px;
margin: 50px auto;
width: 400px
}

 

实时编辑CSS

通过设置style标签的display:block样式可以让页面的style标签显示出来,并且加上contentEditable属性后可以让样式成为可编辑状态,更改后的样式效果也是实时更新呈现的。此技巧在IE下无效。拥有此技能者,逆天也!

<!DOCTYPE html>
<html>
<body>
<style style="display:block" contentEditable>
body { color: blue }
</style>
</body>
</html>

 

创建长宽比固定的元素

通过设置父级窗口的padding-bottom可以达到让容器保持一定的长度比的目的,这在响应式页面设计中比较有用,能够保持元素不变形。

<div style="width: 100%; position: relative; padding-bottom: 20%;">
<div style="position: absolute; left: 0; top: 0; right: 0; bottom: 0;">
this content will have a constant aspect ratio that varies based on the width.
</div>
</div>

 

CSS中也可以做简单运算

通过CSS中的calc方法可以进行一些简单的运算,从而达到动态指定元素样式的目的。

.container{
background-position: calc(100% - 50px) calc(100% - 20px);
}

 

JavaScript篇

生成随机字符串

利用Math.random和toString生成随机字符串,来自前一阵子看到的一篇 博文。这里的技巧是利用了toString方法可以接收一个基数作为参数的原理,这个基数从2到36封顶。如果不指定,默认基数是10进制。略屌!    

function generateRandomAlphaNum(len) {
var rdmString = "";
for (; rdmString.length < len; rdmString += Math.random().toString(36).substr(2));
return rdmString.substr(0, len);
}

 

整数的操作

JavaScript中是没有整型概念的,但利用好位操作符可以轻松处理,同时获得效率上的提升。

|0和~~是很好的一个例子,使用这两者可以将浮点转成整型且效率方面要比同类的parseInt,Math.round 要快。在处理像素及动画位移等效果的时候会很有用。性能比较 见此

var foo = (12.4 / 4.13) | 0;//结果为3
var bar = ~~(12.4 / 4.13);//结果为3

 顺便说句,!!将一个值方便快速转化为布尔值 !!window===true 。

 

重写原生浏览器方法以实现新功能

下载的代码通过重写浏览器的alert让它可以记录弹窗的次数。

(function() {
var oldAlert = window.alert,
count = 0;
window.alert = function(a) {
count++;
oldAlert(a + "\n You've called alert " + count + " times now. Stop, it's evil!");
};
})();
alert("Hello World");

 

关于console的恶作剧

关于重写原生方法,这里有个恶作剧大家可以拿去寻开心。Chrome的console.log是支持对文字添加样式的,甚至log图片都可以。于是可以重写掉默认的log方法,把将要log的文字应用到CSS的模糊效果,这样当有人试图调用console.log()的时候,出来的是模糊不清的文字。好冷,我表示没有笑。

是从 这篇G+帖子的评论里看到的。使用之后的效果是再次调用log会输出字迹模糊不清的文字。

var _log = console.log;
console.log = function() {
_log.call(console, '%c' + [].slice.call(arguments).join(' '), 'color:transparent;text-shadow:0 0 2px rgba(0,0,0,.5);');
};

 

不声明第三个变量的值交换

我们都知道交换两个变量值的常规做法,那就是声明一个中间变量来暂存。但鲜有人去挑战不声明中间变量的情况,下面的代码给出了这种实现。蛮有创意 的。

var a=1,b=2;a=[b,b=a][0];

 

万物皆对象

在JavaScript的世界,万物皆对象。除了null和undefined,其他基本类型数字,字符串和布尔值都有对应有包装对象。对象的一个特征是你可以在它身上直接调用方法。对于数字基本类型,当试图在其身上调用toString方法会失败,但用括号括起来后再调用就不会失败了,内部实现是用相应的包装对象将基本类型转为对象。所以(1).toString()相当于new Number(1).toString()。因此,你的确可以把基本类型数字,字符串,布尔等当对象使用的,只是注意语法要得体。

同时我们注意到,JavaScript中数字是不分浮点和整形的,所有数字其实均是浮点类型,只是把小数点省略了而以,比如你看到的1可以写成1.,这也就是为什么当你试图1.toString()时会报错,所以正确的写法应该是这样:1..toString(),或者如上面所述加上括号,这里括号的作用是纠正JS解析器,不要把1后面的点当成小数点。内部实现如上面所述,是将1.用包装对象转成对象再调用方法。

If语句的变形

当你需要写一个if语句的时候,不妨尝试另一种更简便的方法,用JavaScript中的逻辑操作符来代替。

var day=(new Date).getDay()===0;
//传统if语句
if (day) {
alert('Today is Sunday!');
};
//运用逻辑与代替if
day&&alert('Today is Sunday!');

比如上面的代码,首先得到今天的日期,如果是星期天,则弹窗,否则什么也不做。我们知道逻辑操作存在短路的情况,对于逻辑与表达式,只有两者都真才结果才为真,如果前面的day变量被判断为假了,那么对于整个与表达式来说结果就是假,所以就不会继续去执行后面的alert了,如果前面day为真,则还要继续执行后面的代码来确定整个表达式的真假。利用这点达到了if的效果。

对于传统的if语句,如果执行体代码超过了1 条语句,则需要加花括号,而利用逗号表达式,可以执行任意条代码而不用加花括号。

if(conditoin) alert(1),alert(2),console.log(3);

上面if语句中,如果条件成立则执行三个操作,但我们不需要用花括号将这三句代码括起来。当然,这是不推荐的,这里是冷知识课堂:)

 

禁止别人以iframe加载你的页面

下面的代码已经不言自明了,没什么好多说的。

if (window.location != window.parent.location) window.parent.location = window.location;

 

console.table

Chrome专属,IE绕道的console方法。可以将JavaScript关联数组以表格形式输出到浏览器console,效果很惊赞,界面很美观。

//采购情况
var data = [{'品名': '杜雷斯', '数量': 4}, {'品名': '冈本', '数量': 3}];
console.table(data);

 


本文链接: 冷门JS技巧,转载请注明。

IE、Chrome、Firefox浏览器收藏夹同步方法

$
0
0

个人认为在浏览器的收藏夹同步方面做得最好的还是google chrome,不久前IE自从系统升级到windows 8.1以后必须使用microsoft账户才能同步。这里收集了IE、Chrome、Firefox浏览器收藏夹同步方法,如果你有更好的方法,请一起分享讨论哦。

一、Google Bookmarks (适合 Chrome、Firefox ,提供 Web 版,支持多操作系统平台)

将书签托管在 Google 的云端,是比较稳定可靠的方案(当然这句话放在国内得打个对折)。在线登录 Google Bookmarks 页面后,从左边的侧边栏找到 “Import bookmarks”,将之前导出的 .html 文件导入即可。

Google Bookmarks

如果您使用的是Chrome 浏览器,那么恭喜您,Chrome 6 以上版本本身已经自带了完美强大的同步功能(请从右上角工具图标->选项->同步->自定义中开启书签同步),可自动将您保存的书签同 步保存至个人 Google 账号的 bookmarks 下。

如果您习惯了 Firefox,同样可以通过安装 GMarks这个插件来自动同步 Google Bookmarks。

Google 账号的 bookmarks

对于其他浏览器用户,或许直接访问 Google Bookmarks 的网页版是最省时省力的吧。

二、Firefox Sync (适合Firefox 用户,支持多操作系统平台)

再为 Firefox 用户介绍一个强大的插件 — Firefox Sync。它不仅能同步书签,还能同步密码、设置、访问历史、当前打开书签等,非常强悍,颇有迎头赶上Xmarks 的架势。

Firefox Sync

Firefox 4 Beta 本身已经内置了此工具,用户可直接享受。老版本用户则需下载安装此插件。使用前需通过 Email 注册一个帐号。

并且它还有适合移动设备访问的APP,例如 iPhone 客户端、N900 Maemo 客户端、其他兼容手机客户端。

三、Sync2It(适合 Safari / IE 用户,支持 Mac / Windows 操作系统平台)

Sync2It是又一款跨平台支持的小工具。如果您的系统是 Mac,那么它会自动同步 Safari 的书签;如果系统是 Windows,则会自动同步 IE 书签。它还允许您订阅官网上他人共享的书签列表。

Sync2It

四、Delicious 和 Diigo(适合 Firefox 和 Chrome,支持多操作系统平台)

与 Google Bookmarks 类似的书签云端存储服务有不少,Delicious和 Diigo算是比较知名的两家。二者均有丰富的官方和第三方插件,用于强化浏览器的工具栏、书签栏、扩展等。

其中,Diigo 也提供了导入功能,可导入从 Xmarks 中导出的 .html 文件。

Diigo

对于Delicious的忠实用户,推荐 Firefox和 Chrome的两个专用插件。

Delicious

五、使用第三方同步工具(Dropbox、DBank 等)

当然,也不能忘记这个屡试不爽万年通用技巧——第三方同步工具。借助它们,将应用程序的配置文件自动同步于多台电脑,就可实现一处改动,多处自动调整的效果。当然也适用于浏览器的数据资料同步,比如收藏夹、书签等。

这里首推的自然是 Dropbox,性能卓越,支持共享。国内较为稳定且实用的同步工具有 DBank,同样支持共享,并可将外链批量打包成列表。对于多人团队或是针对不同使用环境有不同书签管理方案的高级用户而言,不失为一种选择。

via:activedirectory

This article addresses: http://www.iefans.net/ie-chrome-firefox-shoucangjia-tongbu/

Here is no comments yet by the time your rss reader get this, Do you want to be the first commentor? Hurry up

公司团队管理大敌:营造内部帝国的“小帝王”

$
0
0

  对权力、声望、财富的追求没完没了,把部门越做越大,直到整个公司失控。

  文 | Joe Robinson

  你的公司里是否有一种人,像是被“集邮”进来的?他们在公司不起眼的地方混吃等死,没什么贡献。这些人的存在拜某些部门领导所赐——他们在公司里建造了一个属于自己的小国家。

  在管理学中,这种现象被称为“营造帝国”,员工数量和财政预算急速增长,人人自我膨胀——这会毁了你的团队、你的企业生存线。

  “这种事能把公司杀掉。”Mark Faust说。他是研究企业增长与周转的专家,供职于辛辛那提Echelon国际管理公司,也是Growth or Bust一书的作者。“如果只是降低公司20%〜30%的增长潜力还算幸运的,然而它的破坏力可能远不止这些。这是制约美国企业发展最大的因素之一。”

  小帝王们可不管这些。他们的目标只是通过不断扩大部门人员,掌握更多信息,来加强个人权力和声望。“就像孔雀要开屏展现自己五颜六色的羽毛来吸引雌性注意一样。”Art Markman说,他是徳克萨斯州立大学心理学家,正在进行人力组织维度项目研究。“当然在这个案例中,这不代表要交尾的信号,而是向组织要权的信号——他在强调他的身份,要你注意他。”

  30年前,这些小帝王的放肆举动或许还能赢得喝彩,但现在时代不同了。近年来针对这些忙着建造小帝国的危险分子的研究越来越多。 管理专家已经意识到,这种行为不易察觉,但会连根破坏企业体系,导致成本超支,增长乏力,部门争斗不断。

  在今天的价值观里,企业对暗中培养心腹的刚愎自用者的容忍度越来越低。且在领导学中,以往严格地强调指挥和控制的管理风格正进化为强调协作精神。对权力饥渴的大男子主义者被视作破坏员工团结的始作俑者。

  密苏里州哥伦比亚市StorageMart公司CMO Tron Jordheim将营造帝国视为企业中层经理人为了获取地位,在身边建立小组织的新趋势。其风险在于“你的成本忽然就上去了”。他说:“工资表上的数字和行政成本开始上升,但你看不到收入。同时,正在做的项目还卡住了。”

  小帝王的花招

  小帝王们的腾挪手腕可谓非常纯熟——A项目要启动了,预算却加到了B项目中。“这种情况在哪儿都有可能发生。”密歇根州立大学罗斯商学院组织行为学中心系主任Robert Quinn说,“他们通常在某一方面非常有能力,然后就会充分利用这种能力做利己的事。这会在企业内掀起是非,但因为他们此时已经具备了相当的力量,给人造成一种感觉,没人搬得动他们。”

  “他们势力太大,不可能倒台”,这是全体员工的感觉。典型的例子是,IT部门的头头掌管着程序命脉,或者是一个销售老大,业绩强到无人敢挑战。你不难发现小帝王的身影:顺着公司的阻力点和冲突梳理,就能找到他们。

   私底下另搞一套事情,暗渡陈仓挪走资源,在公司内部设立屏障——营造个人帝国会扼杀很多业绩表现,破坏和谐氛围,摧毁信任。此外,这些行为还让公司内部滋生不满和厌恶,助长人际间的冲突与隔阂。“公司就是个人际关系网络。”Quinn说,“如果团队精神和组织有序已经不复存在,人人都会感到受到折磨。”企业文化也随之被破坏。人人忙着内斗,牵扯精力,顾不上和市场上真正的竞争者战斗。

  “营造帝国是企业内部的大敌。”Dana Ardi说,她是纽约企业人类学顾问机构的创始人,也是《冠军的倒塌》一书的作者。“他们的目的不是为了让企业取得更伟大的成就。”Ardi声称这种行为在当今社交年代已经远远背离合作精神。“我们要竞争精神,但我们不希望统一战线的人们互相斗法。你希望能在一个互相合作能得到回馈的文化中工作,超额完成公司的目标。”

  在过去几十年中对人力资源激励的研究显示,指挥+控制型的领导力有很多缺点。罗切斯特大学心理学教授Edward L.Deci在《习惯的力量:我们为何这样做》一书和大量代表性的研究中指出,金钱激励和严格僵化的制度是激励不了员工的。想让你的团队发挥出最高的水平,你得让他们有强烈的参与感,并有更强的自发性,你还要确保他们明白怎么做对公司完成目标才是对的。

  但个人帝国的营造却站在这些理论的对立面上。“问题核心不是我能得到多少,而是我能贡献什么。”加州Claremont市德鲁克研究院执行总监Rich Wartzman说,“这是领导的特点之一。公司目标是什么?你的个人目标应该和公司的目标一致。”

  “划地盘”的欲望根源

  胜者为王的权力游戏已经玩了数千年,驱动着人们不断扩大自己的控制领域。你得到的资源和地盘越多越大,当然权力也就越大。能掌控局势的人自然可以获得更多途径和更有价值的资源。

  职场的权力斗争不涉及人类生存斗争,它更像一种社交和心理上的生存斗争游戏。“今天我们的斗争是为了获得信息、关系、地位、视野,所以人们都渴望成功。”总部在洛杉矶的Group Process顾问集团创始人Annette Simmons说,她也是《地盘斗争游戏》一书的作者。事实上Simmons认为,营造个人帝国只是在以另一种形式保护自己的领地。这两种欲望都来自大脑中同一片原始的边缘区域,这意味着营造个人帝国这种花招既可能挑拨其他部门的某一个员工,也可能挑拨本部门的人。但实际上一个典型的造小帝国的人并没意识到他做了这些。

   小帝王中的另外一些人则可能被自大驱使,而这种自大来自外因,如金钱、地位、办公室面积、财政预算、手下人数等。这是个无趣的游戏。外在的肯定并不能让人快乐,因为这些快乐取决于别人是怎么想的。而且,这些东西是无常的。它们还可以使一个人沉迷于此,不断惦记自己其实得不到的东西。

  “今天得到了一个办公室隔间,明天可能就想要个套房。”Markman说,“手下已经有5个人,现在又想要10个,没完没了。”

  大多数发动了帝国营造攻势的人最后得到的结果都是对公司造成了破坏。他们自吹自擂的骄傲表象背后是需要持续安抚的焦虑感和越来越低的自我认同感。“如果你夸奖他们能力出众,他们会很焦虑,因为事实不是那样。”Markman说,“他们搞出的这些做法是为了把焦虑感锁在一个小小的避风港里。‘我一定举足轻重,我手下有这么多人。’”

  当一个人的个人价值建立在外因之上时,这个人总是会恐惧这些东西有一天会消失。“当你看到一些人出于控制欲而采取的一些行为时——不诚实、背后捅刀、收集小道消息、为了营造帝国而营造帝国——这通常都是此人感到威胁时的反应。”费城商业心理学家、《领导为什么晚上睡不着觉》一书作者Nicole Lipkin说。“无论是真实的威胁还是想象出来的威胁,都是出于自我的感觉。”

  一个人有自恋倾向也会助长他营造小帝国的心理。心态不健康的自恋者沉迷于此,为了得到荣誉与赞美,他们需要不断的肯定来得到“我很有权力”的自我感觉。因其自我心理建设能力很差,因而他们憎恨批评,也不能忍受他人得到赞誉,见不得别人身上发生好事。他们缺少领导力中最重要的一个东西:同理心。他们可以在任何时候煽动任何人,言之过甚;会隐瞒糟糕的业绩数据;还会不顾预算不断要求加人,并做成一副十分合理的样子——一切都是为了完成目标。

  一旦这些挡路的自大狂被解除了权力,其他同事开香槟庆祝都不奇怪。罗斯商学院的Quinn回忆,在他工作过的一个公司,首席秘书曾用手头资源不断营造自己的地盘,直至被解雇。“她离职那天走过大厅时,人人都伸出头来看。她走后大家唱起《绿野仙踪》:‘坏女巫挂了。’”

  瓦解小帝国

  当然,不仅仅是权力欲和不安全感导致营造帝国的行为,公司自身也可能引发这场战争,有些公司喜欢不断暗示累积资源才是获得尊重与权力的途径。小帝国之所以能在组织内部成长,“原因要么是你的公司对目标设定得不够明确,要么就是你雇用的人不适合担任领导职务”,德鲁克研究院的Wartzman说。想解决问题,不妨打开天窗说亮话,大家经常围绕目标交流,找出问题所在;多和员工聊聊,问问他们遇到什么麻烦和冲突;哪些因素正在妨碍任务的完成。

  找到了动力和资源消失的黑洞,就可以抓到始作俑者,拿证据给他看:过剩的员工数量和员工福利、吹气球般的地盘扩张、和其他人的不断争斗。当然,这些人一贯的回应是:“我只是在做自己的本职工作。”

  “但真相恰是他们没在做自己的本职工作。”Quinn说,“你的本职工作也包括推动大家前进,当你破坏了大家的动力,你还能说自己在做本职工作吗?这对企业可是数以万计的金钱损失。”

  这些营造小帝国的人必须明白,他们的行为对公司造成破坏,这是一种很差劲的工作表现,只会让不安全感更快地增长。Wartzman认为,关键点是“你要为顾客创造价值,而不是为自己创造价值。否则企业就会倒退”。

  StroageMart的Jordheim这些年来一直在与各种职业经理人进行关于帝国营造的对话。他试图找出这种行为背后的动机。“你真需要雇这个人吗?还是你只是从营造帝国中获得了乐趣?”他这样问某些不断招人的经理人。当他判定某人权力欲过剩时,会告诉他:“你做的太多了,必须悬崖勒马。”业绩表现可以很好地判断以一个领导者现有的资源来看,他做的工作是否真的高效。

  Markman建议,不妨与喜欢营造帝国的人组织一次旅游,让他们感受一下退休后的生活。很多人在退休后都希望自己因“年轻时的进取、创新、推动了企业进程而被人尊重,没几个人回顾自己的职业生涯时,说自己当时想要的就是一间大办公室”。( 编译: Yelena Xu)

Apache Stratos 4.0.0 发布,PaaS 框架

$
0
0

Apache Stratos 是一个支持多语言的 PaaS 框架,提供一个云端的开发、测试和运行可伸缩应用程序的环境。提供高利用率、自动化资源管理和平台的监控以及收费管理。

Apache Stratos 可运行 Tomcat、PHP 和 MySQL 应用作为服务,带来自服务管理、弹性扩展、多租户的部署、使用监控等等。

该系统目前还在 Apache 基金会中进行孵化。

Apache Stratos 4.0.0 发布,此版本是 Apache Stratos 成为高级项目以来的第一个版本发布。现已提供 下载,相关文档请看 这里。主要更新内容如下:

Bug 修复

  • [ STRATOS-202] - nohup: appending output to `nohup.out' message comes when starting up autoscaler, loadbalancer, cloud controller

  • [ STRATOS-356] - Service Aware LB subscription Fails

  • [ STRATOS-364] - CLI does not show error message even though it failed to unsubscribe

  • [ STRATOS-373] - CLI should specifically say if alias is already exists..(Not a general error)

  • [ STRATOS-434] - Add an Operation to List Deployed Multitenant Service Cluster Details

  • [ STRATOS-456] - Exceptions does not propagate when communication via stubs

  • [ STRATOS-476] - Cartridge Agent throws an NPE, if there're no payload params in launch-params file

  • [ STRATOS-478] - UI Exception throws when idle for sometime

  • [ STRATOS-485] - opt is hardcoded in stratos.sh in CA

  • [ STRATOS-486] - Propagating back-end exceptions to the front-end (CLI and UI)

  • [ STRATOS-491] - Exception shown in the CLI instead of the proper message

  • [ STRATOS-492] - Stratos configuration wizard's samples need to be revisited and corrected

  • [ STRATOS-494] - Instances are spawned again after Un-Subscription

  • [ STRATOS-495] - LB cartridge sample configuration does not include load.balancer property

  • [ STRATOS-498] - Cartridge Agent reads launch-params file for each parameter

  • [ STRATOS-502] - Error in SM after sometime

  • [ STRATOS-525] - Ability to assign Openstack availability zones using jclouds Openstack-nova API

  • [ STRATOS-527] - Disable registry versioning

  • [ STRATOS-531] - Some improvements to the UI

  • [ STRATOS-532] - UI allows different passwords password and repeat password fields when creating tenants

  • [ STRATOS-533] - UI not showing correct subscription details of subscribed cartridges

  • [ STRATOS-534] - current instances stopped and re spinned when AS shutdown for a moment and restart

  • [ STRATOS-540] - Stratos GUI goes to an error page if LB cartridge definitions json format is not correct.

  • [ STRATOS-542] - Cartridge Agent should not listen only to localhost

  • [ STRATOS-543] - Topology needs to be sync when the member gets terminated

  • [ STRATOS-544] - private ip/public ip in the NodeMetaData is null when we use multiple networks

  • [ STRATOS-545] - Topology synchronization period needs to be configurable

  • [ STRATOS-557] - Error while starting mysql instances

  • [ STRATOS-564] - Cluster monitor creation should be re-tried few times, if failed once

  • [ STRATOS-572] - Support cartridge undeploy in stratos UI

  • [ STRATOS-576] - wrong theme in stratos manager

  • [ STRATOS-577] - Exception when trying to login as a deactivated tenant

  • [ STRATOS-579] - Issue with check availability button in adding a new tenant

  • [ STRATOS-584] - copy broker client lib step is missing in the documentation

  • [ STRATOS-588] - URL File Path not being included in Location Header

  • [ STRATOS-600] - A new member to replace a faulty member should be spawned via min-check rule

  • [ STRATOS-602] - vCloud: Stratos Manager Unable To Push Payload to Cartridge VM

  • [ STRATOS-609] - Decrement In-Flight Request Count On Fault Requests

  • [ STRATOS-614] - Scale down logic does not execute as intended

  • [ STRATOS-625] - Error in scale down logic

  • [ STRATOS-627] - Event Publishers and Subscribers do not reconnect to message broker if connection drops

改进

  • [ STRATOS-97] - Load Balancer Architectural Changes for Stratos 4.0.0

  • [ STRATOS-348] - Improve Multi-tenant cartridge subscription mechanism

  • [ STRATOS-447] - Add categorization to the cartridge listing page

  • [ STRATOS-448] - Error handling from the backend in the UI.

  • [ STRATOS-461] - Provide validation for the add new teanat page

  • [ STRATOS-505] - Make Stratos Cartridge Agent Truly Extensible

  • [ STRATOS-523] - Run stratos in a single JVM instance

  • [ STRATOS-562] - Single JVM installation script

  • [ STRATOS-591] - Need an error page for Stratos Console

  • [ STRATOS-612] - Fixing few usability issues with setting up Stratos

新特性

  • [ STRATOS-153] - Port cartridge management APIs to REST

  • [ STRATOS-203] - [stratos-manager-frontend] boilerplate MVC structure for the application

从熟人那里购买二手更放心?那试试“有闲”吧

$
0
0

2ndHandStore

海外代购的衣服不合身,搬家了剩余家具周边没有跳蚤市场,二手数码产品不想贱卖给中关村贩子,也许这些,都是人们面对身边闲置物品的困扰,对于流动人口比例极高的一线城市来说,二手市场交易的需求是惊人的,可是二手交易往往琐碎而难以标准化,这为希望搓成交易的中间上提出了不少难题。

二手品闲置目前而言已经有了不少产品,淘宝二手栏目、豆瓣分组、58同城、赶集网、百姓网等网站都承载着主流的交易量,“有闲”则是一个手机端的产品,所以产品逻辑由此展开,既然是信息服务平台,那么首要的是机制的设置,“有闲”加入了自己的不少思考:

通讯录是第一个需要利用的关系,“有闲”集合了类似于LinkdeIn的一二度熟人推荐,创始人王润对此的解释是,熟人关系能更好地帮助信息传播及解决信任度问题,在充分利用好SNS平台工具的前提下,“有闲”还引用了猎聘网站招人的转发分成机制,最终促使成交的人能获得10%的分成。

第二部分是利用基于手机的GPS功能,打开产品主界面用户能看到类似陌陌这样的地理围栏信息,这相当于围绕每个用户所住的地方开了一个周末集市,可以作为日常补给生活用品的选项,同时,产品发布页面手机提供的语音服务也同样适用于缩短发布流程。据王润给出的调研数据,有52%的二手交易发生在熟人之间,通过LBS方式实现的比例则有22%。

used

由于线下已经有多个平台提供二手出售服务,因此“有闲”的线下信息服务部分并不打算收费,无论是熟人关系还是基于同城的陌生人,最终成交的环节“有闲”不会加入任何费用。而针对线上的交易部分,王润同样想到了一些创新,由于加入支付宝等消费者保障平台成本高昂,因此有闲自己创立了一套机制,产品发布时系统会自动提醒价格包含运费以及有闲会抽取10%的佣金,当用户下单后,无需联系快递公司,便会有快递员来电预约取件时间,从买方成交起三日内是保障时间,如果遭遇不诚信行为,可以在此期间退货给卖方,买家资金保证了安全。

有闲的第三条业务线是实体店。据王润透露,2014年下半年将会尝试在高校园区开辟线下实体店,相对与线上的交易方式,实体店将会才去完全寄售的方式,有闲只提供闲置率高的类别代卖,如数码产品、母婴用品、图书音像。买卖双方在寄售前会签订一份协议,商定理想价与底价。7天内通过线上线下的渠道发布,如未能出售则降至底价,如果依然未能成交,那么卖方必须在7天内取回货品,否则有闲有权处理,从而减轻库存的压力。通过这类模式代售的商品,有闲会采取与寄售方三七分成的方式。

有闲的发展路数,可以有不少想象空间,比如定期发布类似闪购这样的拍卖活动,甚至让用户上传闲置的”技能“或“时间”,只要工具好用,那么用的人也会越来越多。

作为一款二手交易的平台产品,本质依然是“方便”,无论是卖方以最快的速度、最短的距离达成交易,还是买方以更便捷的方式获取生活所需,他日平台有了规模,甚至可以收取优先排名、代卖等增值服务,但就机制而言,我依然有两点疑惑:

作为二手货品第一特性是价格,倘若是低单价的生活用品,是否经得起平台或转发分成10%的分成?最近淘宝的二手频道已经有了大改版,作为最大的C2C平台,淘宝了除了已经建立起的一套个人信誉机制,它甚至提供了原链接一键转卖功能,要知道,二手交易品里有不少比例是由于网络购物导致的冲动形消费及信息错配的交易,“有闲”佣金制度的认授性,取决与平台在交易达成的环节上,可以带来多大的便捷。

IT界需求最旺的16项技能

$
0
0

[导读] 目前,IT市场上最缺的是哪方面的技术人才,有哪些技能在市场上最吃香?本文从Foote Partners最新发布的季度IT技能报告、工资指数以及业界人士中探究出了市场上最热的16项IT技能以及相应的薪资。1 需求工程和分析

目前,IT市场上最缺的是哪方面的技术人才,有哪些技能在市场上最吃香?本文从Foote Partners最新发布的季度IT技能报告、工资指数以及业界人士中探究出了市场上最热的16项IT技能以及相应的薪资。

1.需求工程和分析技能

这些技能决定了软件产品的创建、维护、服务等,同时还要考虑许多其它因素以及利益相关者的要求。这对软件开发来说是非常重要的,也是IT演进过程中亘古不变的东西。

虽然需求工程和分析技能在多年以前就已出现,但在过去的一年中,云的快速发展,使这两个技能的需求得到了快速的增长。此外,分析、安全或移动云等也是不可或缺的助推手。

平均年薪:$65,000-$104,000

2.SAP供应链管理技能

这一技能需求来源于德国公司SAP的一个软件模块,它也是世界上最大的ERP供应商,有许多公司在使用它,尤其是物流行业,SAP供应链管理技能可以让公司运营更加敏捷,能够与合作伙伴更加有效地展开工作。

在过去的半年里,这一技能需求增长了20%。平均工资溢价在10-14%。

平均年薪:$ 88,000

3.HBase技能

HBase是Google Bigtable的开源实现,是一个非关系型数据库,采用Java编程语言实现。目前,越来越多的公司使用它把大数据并入到公司的决策过程中,HBase用来实时读/写访问大数据集。

在过去的6个月里,这以技能需求增长了18.2%,平均工资溢价是13%。

平均年薪:$111,000

4.定量分析和回归分析技能

人们在日常工作流中使用该技能进行公司目标、开发分析模块、评估和解决方案的分析,他们可以工作在多个领域,从Web开发人员到会计再到数据库团队等等。拥有这一技能的人一般都具备高学历。

大数据的采用和数据科学家的发展都推动了这一技能的市场需求。以前,许多公司会聘请一些博士来做定量和回归分析,如今越来越多的公司亲睞数据科学家。

平均年薪:$74,000-$117,000

5.IT管理技能

该技能也叫IT治理技能,该技能能够帮助企业和组织在符合行业法规范围内有效和高效地实现他们的业务目标。不同规模的企业,会有不一样的需求。通常,越是大型和正规的公司或行业,就越应该具备IT治理架构功能。

根据Foote Partners的研究报告,在过去的一年里,该技能的市场需求增长了15.5%,工资平均溢价在15%左右。

平均年薪:$97,000-$122,000

6.C#技能

C、C#、C++和Java早已是推动技术发展的基础语言,而根据Foote Partners的研究报告,在过去的半年中,市场对C#技能的需求增长了14.3%,平均工资溢价在6-10%左右。

平均年薪:$61,000-$251,000

7.ITIL技能

ITIL即IT基础架构库(Information Technology Infrastructure ?),为企业的IT服务管理实践提供了一个客观、严谨、可量化的标准和规范。

ITIL管理方法可以帮助企业缩减成本,提供IT服务,并为客户提供更好的客户服务水平。而ITIL v3技能需求在过去一年大幅上升,在过去一年里上涨20%。拥有这些技能的人平均年薪溢价在10—14%。

平均年薪:$61,000

8.企业架构技能

随着企业的发展,企业架构的作用也日益凸显。以前,有许多公司抛弃企业架构,而如今,如果你没有大量的数据清理、数据质量分析、数据管理和数据架构,你很难去做一些高级分析,很难给企业做一些正确的决策依据。这一技能需求在过去的一年里增长了31%。

平均年薪:$91,000-$134,000

9.基础架构技能

靠维护服务和电子邮件驱动公司业务的日子已经一去不复返了,再加上科学技术以闪电般的速度在发展,越来越多的企业意识到基础架构对公司组织架构及业务发展的重要性,并且用来调整长期业务战略和技术。

公司确实需要一些人来搭建一个像样的架构,并且保证所有这些系统之间很好的兼容与运行。而这一技能需求在过去一年里上涨了7.1%,年平均工资溢价在12-17%之间。

平均年薪:$109,000-$141,000

10.安全架构技能

安全性是每个CIO关心的头等问题,有许多新闻都详细介绍了一些大公司被黑客入侵的具体过程,为了抵御和预防网络恐怖分子和脚本黑客的入侵,每个公司都应该构建一个坚实的安全架构。

构建一个坚固的安全架构可能会花费一定的成本,但聪明的CIO肯定会意识到,一旦网站遭遇入侵,那损失可能是无法用金钱衡量的。而在过去一年,市场需求也上涨了23.1%,拥有这项技能的安全工程师的工资溢价在14-18%。

11.商业智能技术

商业智能即BI,话说,大数据即大业务,许多企业都在密切关注如何解读他们已经拥有的数据,这也是BI技能的由来。对许多公司而言,该领域还未成熟,但企业已经明白,利用拥有的大数据帮他们做调研与市场分析,并且对销售和市场推广做出预测。

在过去的六个月中,该职位的市场需求增长了9.1%,而工资溢价大概在12%左右。

平均年薪:$62,000-$164,000

12.商业分析技能

在许多地方都可以发现商业分析师,他们的工作主要是发现、记录、评估风险,并且对不段变化的业务需求作出分析和交付,支持整个业务的实施。

在过去一年中,该技能的市场需求增长了9.1%,工资溢价在12%左右。

平均年薪:$61,000-$105,000

13.统一通信和消息传递技能

IT和业务都是复杂变化着的,IT触及到业务的每个地方,甚至会给业务带来全球性的影响。当涉及到创建一个高效、生产、协作、创新并且以无缝的方式来进行远程办公、BYOD等因素时,统一沟通技巧在里面发挥着非常重要的作用。

该技术涉及到协作工具、电话、短信、社交媒体等等。目前有许多统一通信的活动并且非常受客户欢迎。这一技能在过去的6个月里增长了9.1%,平均工资溢价在12%左右。

平均年薪:$45,000-$113,000

14.风险评估与分析技能

据Foote报告描述,许多公司正将风险评估和分析合并到他们的员工和IT决策过程,来刺激内部高新技术产业的发展。基本上每家公司都会做风险评估,所以,它已不是新的技能。许多公司都把它看作是一项核心技能,好比过去的项目管理。

在过去一年里,该技能的市场需求增长了18.2%,基本工资溢价在13%左右。

平均年薪:$91,000-$104,000

15.移动应用开发技能

移动开发技能在短期内仍处于供不应求的状态,随着越来越多的公司关注、投身到移动事业上,这项技能的需求会日益增加。

这里有许多与移动开发相关的技能,并伴随着其它领域的发展。下面列一些与移动应用开发相关的技术,比如Java、jQuery、JavaScript等其它脚本语言,C/C++/C#、.Net、HTML 5、CSS以及移动OS(iOS、Android)等等。

平均年薪:$76,000-$139,000

16.云计算技能

随着越来越多的公司使用云来降低成本,像PaaS、SaaS和虚拟化技术将会供不应求。但据Foote报告称,目前云计算市场还未成熟。业界许多分析人士认为,在云计算技能与IT技能之间还存在一些差距。

目前,许多公司会对员工提成一些不切实际的要求,因为他们无法招到云计算方面的人才。随着云计算市场的成熟,云计算技能的市场需求将会更加旺盛。

平均年薪:$30,000-$128,000



已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Jackson简单小结

$
0
0
   本文简单小结下 jackson的常用操作 ,使用的是1.9.13的版本;

1) 简单pojo转换json:
   
public class Employee
{
   private Integer id;
   private String firstName;
   private String lastName;
   public Employee(){
   }
   public Employee(Integer id, String firstName, String lastName, Date birthDate){
      this.id = id;
      this.firstName = firstName;
      this.lastName = lastName;
   }
   public Integer getId()
   {
      return id;
   }
   public void setId(Integer id)
   {
      this.id = id;
   }
   public String getFirstName()
   {
      return firstName;
   }
   public void setFirstName(String firstName)
   {
      this.firstName = firstName;
   }
   public String getLastName()
   {
      return lastName;
   }
   public void setLastName(String lastName)
   {
      this.lastName = lastName;
   }
   @Override
   public String toString()
   {
      return "Employee [id=" + id + ", firstName=" + firstName + ", " +"lastName=" + lastName + "]";
   }
}


   转换:
  
public class JavaToJSONExample
{
   public static void main(String[] args)
   {
      @SuppressWarnings("deprecation")
      Employee employee = new Employee(1, "Lokesh", "Gupta", new Date(1981,8,18));
      ObjectMapper mapper = new ObjectMapper();
      try
      {
         mapper.writeValue(new File("c://temp/employee.json"), employee);
      } catch (JsonGenerationException e)
      {
         e.printStackTrace();
      } catch (JsonMappingException e)
      {
         e.printStackTrace();
      } catch (IOException e)
      {
         e.printStackTrace();
      }
   }
}
Output:
//In employee.json file below content will be written
{"id":1,"firstName":"Lokesh","lastName":"Gupta"}


   如果要转换为良好的格式,则:
  
public class JavaToPrettyJSONExample
{
   public static void main(String[] args)
   {
      @SuppressWarnings("deprecation")
      Employee employee = new Employee(1, "Lokesh", "Gupta", new Date(1981,8,18));
      ObjectMapper mapper = new ObjectMapper();
      try
      {
         mapper.defaultPrettyPrintingWriter().writeValue(new File("c://temp/employee.json"), employee);
      } catch (JsonGenerationException e)
      {
         e.printStackTrace();
      } catch (JsonMappingException e)
      {
         e.printStackTrace();
      } catch (IOException e)
      {
         e.printStackTrace();
      }
   }
}
Output:
{"id" : 1,"firstName" : "Lokesh","lastName" : "Gupta"
}



json转换为pojo:
    
public class JSONToJavaExample
{
   public static void main(String[] args)
   {
      Employee employee = null;
      ObjectMapper mapper = new ObjectMapper();
      try
      {
         employee =  mapper.readValue(new File("c://temp/employee.json"), Employee.class);
      } catch (JsonGenerationException e)
      {
         e.printStackTrace();
      } catch (JsonMappingException e)
      {
         e.printStackTrace();
      } catch (IOException e)
      {
         e.printStackTrace();
      }
      System.out.println(employee);
   }
}
Output:
Employee [id=1, firstName=Lokesh, lastName=Gupta]



2  接下来看如何把hashmap转换为json:
  
public class JavaToHashMapExample
{
   public static void main(String[] args)
   {
      HashMap<string, object=""> hashmap = new HashMap<string, object="">();
      hashmap.put("id", 11);
      hashmap.put("firstName", "Lokesh");
      hashmap.put("lastName", "Gupta");
      ObjectMapper mapper = new ObjectMapper();
      try
      {
         mapper.writeValue(new File("c://temp/data.json"), hashmap);
      } catch (JsonGenerationException e)
      {
         e.printStackTrace();
      } catch (JsonMappingException e)
      {
         e.printStackTrace();
      } catch (IOException e)
      {
         e.printStackTrace();
      }
   }
}
File content:
{"id":11,"lastName":"Gupta","firstName":"Lokesh"}


再把hashmap反转为json
   
public class HashMapToJSONExample
{
   public static void main(String[] args)
   {
      HashMap<string, object=""> hashmap = new HashMap<string, object="">();
      ObjectMapper mapper = new ObjectMapper();
      try
      {
         hashmap = mapper.readValue(new File("c://temp/data.json"), 
               new TypeReference<map<string, object="">>(){
                                                       });
      } catch (JsonGenerationException e)
      {
         e.printStackTrace();
      } catch (JsonMappingException e)
      {
         e.printStackTrace();
      } catch (IOException e)
      {
         e.printStackTrace();
      }
      System.out.println(hashmap);
   }
}
Output:
{id=11, lastName=Gupta, firstName=Lokesh}


已有 0人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



Viewing all 15843 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>