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

SpringMVC中使用Interceptor拦截器

$
0
0
http://haohaoxuexi.iteye.com/blog/1750680
SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那样子判断当前时间是否是购票时间。
   一、定义Interceptor实现类

   SpringMVC 中的Interceptor 拦截请求是通过HandlerInterceptor 来实现的。在SpringMVC 中定义一个Interceptor 非常简单,主要有两种方式,
     第一种方式是要定义的Interceptor类要实现了Spring 的HandlerInterceptor 接口,或者是这个类继承实现了HandlerInterceptor 接口的类,比如Spring 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter ;
     第二种方式是实现Spring的WebRequestInterceptor接口,或者是继承实现了WebRequestInterceptor的类。
   (一)实现HandlerInterceptor接口
   HandlerInterceptor 接口中定义了三个方法,我们就是通过这三个方法来对用户的请求进行拦截处理的。
   (1 )preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法,顾名思义,该方法将在请求处理之前进行调用。SpringMVC 中的Interceptor 是链式的调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor 。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor 中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
   (2 )postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法,由preHandle 方法的解释我们知道这个方法包括后面要说到的afterCompletion 方法都只能是在当前所属的Interceptor 的preHandle 方法的返回值为true 时才能被调用。postHandle 方法,顾名思义就是在当前请求进行处理之后,也就是Controller 方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller 处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行,这和Struts2 里面的Interceptor 的执行过程有点类型。Struts2 里面的Interceptor 的执行过程也是链式的,只是在Struts2 里面需要手动调用ActionInvocation 的invoke 方法来触发对下一个Interceptor 或者是Action 的调用,然后每一个Interceptor 中在invoke 方法调用之前的内容都是按照声明顺序执行的,而invoke 方法之后的内容就是反向的。
   (3 )afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle, Exception ex) 方法,该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet 渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。
下面是一个简单的代码说明:
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import org.springframework.web.servlet.HandlerInterceptor;  
import org.springframework.web.servlet.ModelAndView;  
public class SpringMVCInterceptor implements HandlerInterceptor {  
    /** 
     * preHandle方法是进行处理器拦截用的,顾名思义,该方法将在Controller处理之前进行调用,SpringMVC中的Interceptor拦截器是链式的,可以同时存在 
     * 多个Interceptor,然后SpringMVC会根据声明的前后顺序一个接一个的执行,而且所有的Interceptor中的preHandle方法都会在 
     * Controller方法调用之前调用。SpringMVC的这种Interceptor链式结构也是可以进行中断的,这种中断方式是令preHandle的返 
     * 回值为false,当preHandle的返回值为false的时候整个请求就结束了。 
     */  
    @Override  
    public boolean preHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler) throws Exception {  
        // TODO Auto-generated method stub  
        return false;  
    }  
      
    /** 
     * 这个方法只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行。postHandle是进行处理器拦截用的,它的执行时间是在处理器进行处理之 
     * 后,也就是在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行,也就是说在这个方法中你可以对ModelAndView进行操 
     * 作。这个方法的链式结构跟正常访问的方向是相反的,也就是说先声明的Interceptor拦截器该方法反而会后调用,这跟Struts2里面的拦截器的执行过程有点像, 
     * 只是Struts2里面的intercept方法中要手动的调用ActionInvocation的invoke方法,Struts2中调用ActionInvocation的invoke方法就是调用下一个Interceptor 
     * 或者是调用action,然后要在Interceptor之前调用的内容都写在调用invoke之前,要在Interceptor之后调用的内容都写在调用invoke方法之后。 
     */  
    @Override  
    public void postHandle(HttpServletRequest request,  
            HttpServletResponse response, Object handler,  
            ModelAndView modelAndView) throws Exception {  
        // TODO Auto-generated method stub  
          
    }  
  
    /** 
     * 该方法也是需要当前对应的Interceptor的preHandle方法的返回值为true时才会执行。该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图执行, 
     * 这个方法的主要作用是用于清理资源的,当然这个方法也只能在当前这个Interceptor的preHandle方法的返回值为true时才会执行。 
     */  
    @Override  
    public void afterCompletion(HttpServletRequest request,  
            HttpServletResponse response, Object handler, Exception ex)  
    throws Exception {  
        // TODO Auto-generated method stub  
          
    }  
      
}
 

   (二)实现WebRequestInterceptor 接口
  WebRequestInterceptor 中也定义了三个方法,我们也是通过这三个方法来实现拦截的。这三个方法都传递了同一个参数WebRequest ,那么这个WebRequest 是什么呢?这个WebRequest 是Spring 定义的一个接口,它里面的方法定义都基本跟HttpServletRequest 一样,在WebRequestInterceptor 中对WebRequest 进行的所有操作都将同步到HttpServletRequest 中,然后在当前请求中一直传递。
   (1 )preHandle(WebRequest request) 方法。该方法将在请求处理之前进行调用,也就是说会在Controller 方法调用之前被调用。这个方法跟HandlerInterceptor 中的preHandle 是不同的,主要区别在于该方法的返回值是void ,也就是没有返回值,所以我们一般主要用它来进行资源的准备工作,比如我们在使用Hibernate 的时候可以在这个方法中准备一个Hibernate 的Session 对象,然后利用WebRequest 的setAttribute(name, value, scope) 把它放到WebRequest 的属性中。这里可以说说这个setAttribute 方法的第三个参数scope ,该参数是一个Integer 类型的。在WebRequest 的父层接口RequestAttributes 中对它定义了三个常量:
   SCOPE_REQUEST :它的值是0 ,代表只有在request 中可以访问。
   SCOPE_SESSION :它的值是1 ,如果环境允许的话它代表的是一个局部的隔离的session,否则就代表普通的session,并且在该session范围内可以访问。
   SCOPE_GLOBAL_SESSION :它的值是2 ,如果环境允许的话,它代表的是一个全局共享的session,否则就代表普通的session,并且在该session 范围内可以访问。
   (2 )postHandle(WebRequest request, ModelMap model) 方法。该方法将在请求处理之后,也就是在Controller 方法调用之后被调用,但是会在视图返回被渲染之前被调用,所以可以在这个方法里面通过改变数据模型ModelMap 来改变数据的展示。该方法有两个参数,WebRequest 对象是用于传递整个请求数据的,比如在preHandle 中准备的数据都可以通过WebRequest 来传递和访问;ModelMap 就是Controller 处理之后返回的Model 对象,我们可以通过改变它的属性来改变返回的Model 模型。
   (3 )afterCompletion(WebRequest request, Exception ex) 方法。该方法会在整个请求处理完成,也就是在视图返回并被渲染之后执行。所以在该方法中可以进行资源的释放操作。而WebRequest 参数就可以把我们在preHandle 中准备的资源传递到这里进行释放。Exception 参数表示的是当前请求的异常对象,如果在Controller 中抛出的异常已经被Spring 的异常处理器给处理了的话,那么这个异常对象就是是null 。

   下面是一个简单的代码说明:
import org.springframework.ui.ModelMap;  
import org.springframework.web.context.request.WebRequest;  
import org.springframework.web.context.request.WebRequestInterceptor;  
public class AllInterceptor implements WebRequestInterceptor {  
    /** 
     * 在请求处理之前执行,该方法主要是用于准备资源数据的,然后可以把它们当做请求属性放到WebRequest中 
     */  
    @Override  
    public void preHandle(WebRequest request) throws Exception {  
        // TODO Auto-generated method stub  
        System.out.println("AllInterceptor...............................");  
        request.setAttribute("request", "request", WebRequest.SCOPE_REQUEST);//这个是放到request范围内的,所以只能在当前请求中的request中获取到  
        request.setAttribute("session", "session", WebRequest.SCOPE_SESSION);//这个是放到session范围内的,如果环境允许的话它只能在局部的隔离的会话中访问,否则就是在普通的当前会话中可以访问  
        request.setAttribute("globalSession", "globalSession", WebRequest.SCOPE_GLOBAL_SESSION);//如果环境允许的话,它能在全局共享的会话中访问,否则就是在普通的当前会话中访问  
    }  
  
    /** 
     * 该方法将在Controller执行之后,返回视图之前执行,ModelMap表示请求Controller处理之后返回的Model对象,所以可以在 
     * 这个方法中修改ModelMap的属性,从而达到改变返回的模型的效果。 
     */  
    @Override  
    public void postHandle(WebRequest request, ModelMap map) throws Exception {  
        // TODO Auto-generated method stub  
        for (String key:map.keySet())  
            System.out.println(key + "-------------------------");;  
        map.put("name3", "value3");  
        map.put("name1", "name1");  
    }  
    /** 
     * 该方法将在整个请求完成之后,也就是说在视图渲染之后进行调用,主要用于进行一些资源的释放 
     */  
    @Override  
    public void afterCompletion(WebRequest request, Exception exception)  
    throws Exception {  
        // TODO Auto-generated method stub  
        System.out.println(exception + "-=-=--=--=-=-=-=-=-=-=-=-==-=--=-=-=-=");  
    }  
} 



  二、把定义的拦截器类加到SpringMVC的拦截体系中

         1.在SpringMVC的配置文件中加上支持MVC的schema
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation=" http://www.springframework.org/schema/mvc  
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd" 


         下面是我的声明示例:
<beans xmlns="http://www.springframework.org/schema/beans"  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"  
    xmlns:mvc="http://www.springframework.org/schema/mvc"  
    xsi:schemaLocation="http://www.springframework.org/schema/beans  
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
     http://www.springframework.org/schema/context  
     http://www.springframework.org/schema/context/spring-context-3.0.xsd  
     http://www.springframework.org/schema/mvc  
     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd"> 

         这样在SpringMVC的配置文件中就可以使用mvc标签了,mvc标签中有一个mvc:interceptors是用于声明SpringMVC的拦截器的。

        (二)使用mvc:interceptors标签来声明需要加入到SpringMVC拦截器链中的拦截器
<mvc:interceptors>  <!-- 使用bean定义一个Interceptor,直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求 -->  <bean class="com.host.app.web.interceptor.AllInterceptor"/>  <mvc:interceptor>  <mvc:mapping path="/test/number.do"/>  <!-- 定义在mvc:interceptor下面的表示是对特定的请求才进行拦截的 -->  <bean class="com.host.app.web.interceptor.LoginInterceptor"/>  </mvc:interceptor>  </mvc:interceptors> 

          由上面的示例可以看出可以利用mvc:interceptors标签声明一系列的拦截器,然后它们就可以形成一个拦截器链,拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的preHandle方法会先执行,然而它的postHandle方法和afterCompletion方法却会后执行。
          在mvc:interceptors标签下声明interceptor主要有两种方式:
                    (1)直接定义一个Interceptor实现类的bean对象。使用这种方式声明的Interceptor拦截器将会对所有的请求进行拦截。
                    (2)使用mvc:interceptor标签进行声明。使用这种方式进行声明的Interceptor可以通过mvc:mapping子标签来定义需要进行拦截的请求路径。
          经过上述两步之后,定义的拦截器就会发生作用对特定的请求进行拦截了。

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


ITeye推荐




Java垃圾回收机制与引用类型

$
0
0

Java语言的一个重要特性是引入了自动的内存管理机制,使得开发人员不用自己来管理应用中的内存。C/C++开发人员需要通过 malloc/ freenew/ delete等函数来显式的分配和释放内存。这对开发人员提出了比较高的要求,容易造成内存访问错误和内存泄露等问题。一个常见的问题是会产生“悬挂引用(danglingreferences)”,即一个对象引用所指向的内存区块已经被错误的回收并重新分配给新的对象了,程序如果继续使用这个引用的话会造成不可预期的结果。开发人员有可能忘记显式的调用释放内存的函数而造成内存泄露。而自动的内存管理则是把管理内存的任务交给编程语言的运行环境来完成。开发人员并不需要关心内存的分配和回收的底层细节。Java平台通过垃圾回收器来进行自动的内存管理。

Java垃圾回收机制

Java的垃圾回收器要负责完成3件任务:分配内存、确保被引用的对象的内存不被错误回收以及回收不再被引用的对象的内存空间。垃圾回收是一个复杂而且耗时的操作。如果JVM花费过多的时间在垃圾回收上,则势必会影响应用的运行性能。一般情况下,当垃圾回收器在进行回收操作的时候,整个应用的执行是被暂时中止(stop-the-world)的。这是因为垃圾回收器需要更新应用中所有对象引用的实际内存地址。不同的硬件平台所能支持的垃圾回收方式也不同。比如在多CPU的平台上,就可以通过并行的方式来回收垃圾。而单CPU平台则只能串行进行。不同的应用所期望的垃圾回收方式也会有所不同。服务器端应用可能希望在应用的整个运行时间中,花在垃圾回收上的时间总数越小越好。而对于与用户交互的应用来说,则可能希望所垃圾回收所带来的应用停顿的时间间隔越小越好。对于这种情况,JVM中提供了多种垃圾回收方法以及对应的性能调优参数,应用可以根据需要来进行定制。

Java垃圾回收机制最基本的做法是分代回收。内存中的区域被划分成不同的世代,对象根据其存活的时间被保存在对应世代的区域中。一般的实现是划分成3个世代:年轻、年老和永久。内存的分配是发生在年轻世代中的。当一个对象存活时间足够长的时候,它就会被复制到年老世代中。对于不同的世代可以使用不同的垃圾回收算法。进行世代划分的出发点是对应用中对象存活时间进行研究之后得出的统计规律。一般来说,一个应用中的大部分对象的存活时间都很短。比如局部变量的存活时间就只在方法的执行过程中。基于这一点,对于年轻世代的垃圾回收算法就可以很有针对性。

年轻世代的内存区域被进一步划分成伊甸园(Eden)和两个存活区(survivorspace)。伊甸园是进行内存分配的地方,是一块连续的空闲内存区域。在上面进行内存分配速度非常快,因为不需要进行可用内存块的查找。两个存活区中始终有一个是空白的。在进行垃圾回收的时候,伊甸园和其中一个非空存活区中还存活的对象根据其存活时间被复制到当前空白的存活区或年老世代中。经过这一次的复制之后,之前非空的存活区中包含了当前还存活的对象,而伊甸园和另一个存活区中的内容已经不再需要了,只需要简单地把这两个区域清空即可。下一次垃圾回收的时候,这两个存活区的角色就发生了交换。一般来说,年轻世代区域较小,而且大部分对象都已经不再存活,因此在其中查找存活对象的效率较高。

而对于年老和永久世代的内存区域,则采用的是不同的回收算法,称为“ 标记-清除-压缩(Mark-Sweep-Compact)”。标记的过程是找出当前还存活的对象,并进行标记;清除则遍历整个内存区域,找出其中需要进行回收的区域;而压缩则把存活对象的内存移动到整个内存区域的一端,使得另一端是一块连续的空闲区域,方便进行内存分配和复制。

JDK5中提供了4种不同的垃圾回收机制。最常用的是串行回收方式,即使用单个CPU回收年轻和年老世代的内存。在回收的过程中,应用程序被暂时中止。回收方式使用的是上面提到的最基本的分代回收。串行回收方式适合于一般的单CPU桌面平台。如果是多CPU的平台,则适合的是并行回收方式。这种方式在对年轻世代进行回收的时候,会使用多个CPU来并行处理,可以提升回收的性能。并发标记-清除回收方式适合于对应用的响应时间要求比较高的情况,即需要减少垃圾回收所带来的应用暂时中止的时间。这种做法的优点在于可以在应用运行的同时标记存活对象与回收垃圾,而只需要暂时中止应用比较短的时间。

通过JDK中提供的 JConsole可以很容易的查看当前应用的内存使用情况。在JVM启动的时候添加参数 -verbose:gc可以查看垃圾回收器的运行结果。

Java引用类型

如果一个内存中的对象没有任何引用的话,就说明这个对象已经不再被使用了,从而可以成为被垃圾回收的候选。不过由于垃圾回收器的运行时间不确定,可被垃圾回收的对象的实际被回收时间是不确定的。对于一个对象来说,只要有引用的存在,它就会一直存在于内存中。如果这样的对象越来越多,超出了JVM中的内存总数,JVM就会抛出 OutOfMemory错误。虽然垃圾回收的具体运行是由JVM来控制的,但是开发人员仍然可以在一定程度上与垃圾回收器进行交互,其目的在于更好的帮助垃圾回收器管理好应用的内存。这种交互方式就是使用JDK1.2引入的 java.lang.ref包。

强引用

在一般的Java程序中,见到最多的就是强引用(strongreference)。如Datedate=newDate(),date就是一个对象的强引用。对象的强引用可以在程序中到处传递。很多情况下,会同时有多个引用指向同一个对象。强引用的存在限制了对象在内存中的存活时间。假如对象A中包含了一个对象B的强引用,那么一般情况下,对象B的存活时间就不会短于对象A。如果对象A没有显式的把对象B的引用设为null的话,就只有当对象A被垃圾回收之后,对象B才不再有引用指向它,才可能获得被垃圾回收的机会。

除了强引用之外,java.lang.ref包中提供了对一个对象的不同的引用方式。JVM的垃圾回收器对于不同类型的引用有不同的处理方式。

软引用

软引用(softreference)在强度上弱于强引用,通过类 SoftReference来表示。它的作用是告诉垃圾回收器,程序中的哪些对象是不那么重要,当内存不足的时候是可以被暂时回收的。当JVM中的内存不足的时候,垃圾回收器会释放那些只被软引用所指向的对象。如果全部释放完这些对象之后,内存还不足,才会抛出OutOfMemory错误。软引用非常适合于创建缓存。当系统内存不足的时候,缓存中的内容是可以被释放的。比如考虑一个图像编辑器的程序。该程序会把图像文件的全部内容都读取到内存中,以方便进行处理。而用户也可以同时打开多个文件。当同时打开的文件过多的时候,就可能造成内存不足。如果使用软引用来指向图像文件内容的话,垃圾回收器就可以在必要的时候回收掉这些内存。

 

publicclassImageData{
privateStringpath;
privateSoftReference<byte[]>dataRef;
publicImageData(Stringpath){
this.path=path;
dataRef=newSoftReference<byte[]>(newbyte[0]);
}
privatebyte[]readImage(){
returnnewbyte[1024*1024];//省略了读取文件的操作
}
publicbyte[]getData(){
byte[]dataArray=dataRef.get();
if(dataArray==null||dataArray.length==0){
dataArray=readImage();
dataRef=newSoftReference<byte[]>(dataArray);
}
returndataArray;
}
}

 

 

在运行上面程序的时候,可以使用 -Xmx参数来限制JVM可用的内存。由于软引用所指向的对象可能被回收掉,在通过 get方法来获取软引用所实际指向的对象的时候,总是要检查该对象是否还存活。

弱引用

弱引用(weakreference)在强度上弱于软引用,通过类 WeakReference来表示。它的作用是引用一个对象,但是并不阻止该对象被回收。如果使用一个强引用的话,只要该引用存在,那么被引用的对象是不能被回收的。弱引用则没有这个问题。在垃圾回收器运行的时候,如果一个对象的所有引用都是弱引用的话,该对象会被回收。弱引用的作用在于解决强引用所带来的对象之间在存活时间上的耦合关系。弱引用最常见的用处是在集合类中,尤其在哈希表中。哈希表的接口允许使用任何Java对象作为键来使用。当一个键值对被放入到哈希表中之后,哈希表对象本身就有了对这些键和值对象的引用。如果这种引用是强引用的话,那么只要哈希表对象本身还存活,其中所包含的键和值对象是不会被回收的。如果某个存活时间很长的哈希表中包含的键值对很多,最终就有可能消耗掉JVM中全部的内存。

对于这种情况的解决办法就是使用弱引用来引用这些对象,这样哈希表中的键和值对象都能被垃圾回收。Java中提供了 WeakHashMap来满足这一常见需求。

幽灵引用

在介绍幽灵引用之前,要先介绍Java提供的 对象终止化机制(finalization)。在Object类里面有个 finalize方法,其设计的初衷是在一个对象被真正回收之前,可以用来执行一些清理的工作。因为Java并没有提供类似C++的析构函数一样的机制,就通过finalize方法来实现。但是问题在于垃圾回收器的运行时间是不固定的,所以这些清理工作的实际运行时间也是不能预知的。幽灵引用(phantomreference)可以解决这个问题。在创建幽灵引用 PhantomReference的时候必须要指定一个引用队列。当一个对象的finalize方法已经被调用了之后,这个对象的幽灵引用会被加入到队列中。通过检查该队列里面的内容就知道一个对象是不是已经准备要被回收了。

幽灵引用及其队列的使用情况并不多见,主要用来实现比较精细的内存使用控制,这对于移动设备来说是很有意义的。程序可以在确定一个对象要被回收之后,再申请内存创建新的对象。通过这种方式可以使得程序所消耗的内存维持在一个相对较低的数量。比如下面的代码给出了一个缓冲区的实现示例。

 

publicclassPhantomBuffer{
privatebyte[]data=newbyte[0];
privateReferenceQueue<byte[]>queue=newReferenceQueue<byte[]>();
privatePhantomReference<byte[]>ref=newPhantomReference<byte[]>(data,queue);
publicbyte[]get(intsize){
if(size<=0){
thrownewIllegalArgumentException("Wrongbuffersize");
}
if(data.length<size){
data=null;
System.gc();//强制运行垃圾回收器
try{
queue.remove();//该方法会阻塞直到队列非空
ref.clear();//幽灵引用不会自动清空,要手动运行
ref=null;
data=newbyte[size];
ref=newPhantomReference<byte[]>(data,queue);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
returndata;
}
}

 

 

在上面的代码中,每次申请新的缓冲区的时候,都首先确保之前的缓冲区的字节数组已经被成功回收。引用队列的 remove方法会阻塞直到新的幽灵引用被加入到队列中。不过需要注意的是,这种做法会导致垃圾回收器被运行的次数过多,可能会造成程序的吞吐量过低。

 

引用队列

 

在有些情况下,程序会需要在一个对象的可达到性发生变化的时候得到通知。比如某个对象的强引用都已经不存在了,只剩下软引用或是弱引用。但是还需要对引用本身做一些的处理。典型的情景是在哈希表中。引用对象是作为WeakHashMap中的键对象的,当其引用的实际对象被垃圾回收之后,就需要把该键值对从哈希表中删除。有了引用队列( ReferenceQueue),就可以方便的获取到这些弱引用对象,将它们从表中删除。在软引用和弱引用对象被添加到队列之前,其对实际对象的引用会被自动清空。通过引用队列的 poll/ remove方法就可以分别以非阻塞和阻塞的方式获取队列中的引用对象。



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


ITeye推荐



PV、UV这些指标过时了么?也许阅读时长是更好的衡量标准

$
0
0



媒体需要一些数字指标去衡量内容的价值,一直沿用至今的是很粗暴的PV或者UV,社交时代许多人也开始关注分享或者互动数,还有些更进一步用读者花在内容阅读上的时间作为衡量指标。

聚合信息类网站Upworthy今天在博客上表达了这样一个看法,它认为更好的指标是attention minutes,就是读者点击某个链接到分享这个链接之间所花的时间,也就是读者真正阅读内容所花的时间。如果你打开一个链接,放在那去看别的页面,这段时间是会通过技术手段排除在外的。

attention minutes这个指标的确会更好的反映读者对内容的态度,但也有弊端,比如本来就很长的文章,在其他条件一致时,肯定会比短文章的attention minutes更长,也许可以再除以文章的字数?

Upworthy还列出了一些数据,称Twitter若以UV来算是个不错的平台,但是若以attention minutes算就不咋地了。相较之下,Youtube、Medium以及the Financial Times会更看重用户对内容的沉浸度。

折腾来折腾去,其实大家最想知道的,可能还是读者在看完这篇文章后,心里的满意度是多少,这才是最终的衡量标准吧。

除非注明,本站文章均为原创或编译,转载请注明: 文章来自 36氪

36氪官方iOS应用正式上线,支持『一键下载36氪报道的移动App』和『离线阅读』立即下载!

将开源进行到底:Facebook引爆下一轮开源浪潮

$
0
0

据国外媒体的报道,Facebook如今已经无可争议地成为全球最大的开源公司之一,继为自己的网络服务和移动应用研发和部署服务器、数据存储设备以及供电设备之后,Facebook在前不久发布了采用开放设计理念的Wedge网络交换机,再加上此前所推出的开放定制化操作系统FBOSS以及对“开放计算项目(Open Compute Project)”的大力推动,Facebook显然已经对全球的开源事业做出了巨大的贡献。

那么,Facebook的这些举措是不是已经敲响了其他开源公司的“丧钟”了呢?毕竟包括Facebook、谷歌和LinkedIn在内的多家互联网科技企业都已经走上了开源之路,那些以销售开源软件为主要业务的公司是否还有生存的余地呢?

全新的软件产业

在很长的一段时间中,大部分软件是为了满足企业需求而开发的,而不是为了销售,但随着Oracle、IBM、SAP和微软等软件巨头的出现,我们也开始看到越来越多的商业销售软件,尽管上述软件公司如今已经达到数十亿甚至更大的规模,但实际上开源软件产业的价值已经达到数万亿美元的规模,这是传统的软件销售行业所难以比拟的。

软 件的发展曾经一度受制于“授权模式”,但这种情况目前正在发生改变。Facebook和其他互联网巨头企业已经认识到开源的重要价值。开发商自然希望在开 源环境下利用最优质的代码来开发软件,这也正是流媒体视频公司Netflix定期在其总部举行开放源码活动的原因所在,因为这不仅能够吸引更多的开发商, 同时还能促进自身软件生态系统的健康发展。

所以就目前情况而言,越开源就意味着能够开发出质量越高的软件,这种全新的软件开发模式已经开始被越来越多的主流软件企业所接受。

Facebook的大胆选择

以 开源分布式数据库管理系统Apache Cassandra为例,Facebook曾在2008年采用该系统为其邮件系统提供支持,但是在2010年Facebook在对邮件系统进行升级时放弃 了该系统,转而采用Hbase,针对此次转变,Facebook曾做过如下说明:

“2008年我们采用了开源的Cassandra,用以满 足用户邮件系统中收件箱搜索对数据库的要求,但是我们的运营和数据库团队在MySQL方面的知识更为广泛一些,这一转换对于他们来说需要付出许多时间成 本,所以我们不得不停止对Cassandra的资金投入,转而选择更大的新系统。在对MySQL、Cassandra和HBase等多个数据库系统进行测 试和评估之后,我们最终选择了HBase来为Facebook全新邮件系统提供数据库支持。”

从此不难看出,Facebook并未局限在原 有的技术环境之中,即便自己曾在该项技术上投入了许多资源,“Facebook一直以来在工具的选择上面都非常务实,”Facebook的前任工程师史蒂 芬•格瑞姆(Steven Grimm)介绍说,“尽管最初我们采用的是Cassandra来进行内部开发,但是随后发现它难以解决新的网站功能的数据存储问题,所以最终我们还是选 择用HBase来替换Cassandra。”

Facebook的这一选择自然是非常了不起,但是这种模式对于其他也采用Cassandra数据库的公司(如Netflix和思科)来说可能并不适用,他们更希望内部的数据库架构更加稳定,这应该是Facebook的模式所并不具备,毕竟Facebook只是通过改变来满足了自身的需求,而并未涉足开源软件业务。

开源黄金时代

尽管开源的概念已经出现了多年,但实际上“开源黄金时代”才刚刚开始,Facebook及其同行所推出的开源项目已经表明未来将会有更多的公司来提供与开源代码相关的维护和支持服务。

随着互联网和软件技术的不断发展,“每家公司都必须是一家软件公司”早已成为老生常谈,但许多企业距离这个目标还有很远,即便的是那些 以软件开发为核心竞争力的公司,他们在开源项目中也难以占据优势地位,所以“开源时代”对于每家公司都是平等的,只有保持不断创新,才有可能在这个竞争激 烈的市场上继续生存下去。

在这个“开源黄金时代”中,许多有趣的、伟大的计算趋势均由开源所引领,包括智能手机设备、云计算基础设置和大数 据基础设施等等,开源让我们得以对数据有更为深入的认识。相信在以Facebook和谷歌等科技巨头的引领下,会出现更多更优质的开源软件来进一步改变我 们的生活和所处的社会。

译者:璞玉

Stom概念

$
0
0

实时计算系统

S4 storm puma

 

自己实现一个实时计算系统要考虑哪些问题

1.低延迟、高性能、分布式(单机已无法满足要求)、可扩展、容错

2.容易在上面开发应用程序,消息不丢失败、消息严格有序

 

Storm优势

1.简单的编程模型 类似于MapReduce的Spout/Bolt

2.是一个服务框架,支持热部署,及时上线下线App

3.可以使用多种编程语言(Clojure,java,Ruby,Python)

4.容错性,Storm会管理工作进程与节点的故障

5.水平扩展,计算是在多个线程、进程、服务器间进行的

6.可靠的消息处理

7.快速  (ZeroMQ作为底层消息队列)

8.本地模式

 

Storm架构

Nimbus 主节点,分配代码、布置任务、故障检测

Supervisor 工作节点,监听工作,开始与停止属于自己管理的worker进程

Worker 进行具体处理组件逻辑的进程

Task 在worker中每一个Spout/Bolt的线程称为task.

Zookeeper  Nimbus与Supervisor的调度

 

Storm基本概念

Topology 拓卜,一个job

Spout 在一个topology中产生源数据流的组件

Bolt 在一个topology中接受数据然后执行处理的组件。可以做业务逻辑。

Tuple 一次消息传递的基本单元

Stream 源源不断传递的tuple组成了stream

 

Storm使用场景

1.流聚合,把多个数据流聚合成一个数据流(基于一些共同的tuple字段)

2.批处理,把一组tuple一起处理,而不是一个个单独处理

3.BasicBolt

4.内存缓存+Fields grouping组合

5.计算top N

 

Storm的分组机制

Storm Grouping定义了一个流在Bolt任务间该如何被切分。提供了6种:

1.随机分组 Shuffle grouping

2.字段分组 Fields grouping

3.全部分组 (慎用)

4.全局分组 (解决top N)

5.无分组      (等效于随机分组)

6.直接分组

 

还可以实现CustomStreamGrouping接口来定制自己需要的分组



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


ITeye推荐



CloudFoundry 中的GoRouter性能测试

$
0
0

      之前一直感觉CloudFoundry的GoRouter的性能不靠谱,或者我们的CloudFoundry 部署架构存在问题,想着进行一些压力测试,但是一直苦于没有压力测试的工具。上一周,部门需要出一个测试报告,刚好借此机会。进行一个比较好的测试。

      测试的时候,是使用的两个gorouter+nginx,测试使用的应用是一个比较简单的应用,使用LoadRunner进行压力测试,使用LoadRunner的1000个用户进行,测试效果非常差。和QQ群里的同学交流,他们也出现了类似的问题,使用F5或者HaProxy 都很正常,但是使用nginx 出现很多问题,表现出来的性能非常差。所以也尝试改用haproxy做负载均衡。

    haproxy的部署方式就不描述了,使用haproxy的默认配置,性能也是很差,开始进行调优。一开始以为是应用、gorouter的问题,但是定位了很久,发现这些目前看来都没有什么问题。最后,对haproxy的配置文件进行了优化,目前我的haproxy配置文件:

      global
    log 127.0.0.1   syslog info
    daemon
    maxconn 300000
    spread-checks 4
    nbproc 8

defaults
    log global
    timeout connect 30000ms
    timeout client 300000ms
    timeout server 300000ms
    # maxconn 320000
   # option http-pretend-keepalive
    option dontlognull
    option forwardfor
    option redispatch
    option abortonclose
listen admin_stats
       bind 0.0.0.0:1080   
        mode http            
        option httplog
        maxconn 10
        stats refresh 30s
        stats uri /stats
        stats realm XingCloud\ Haproxy
        stats auth admin:admin        
        stats hide-version             
frontend http-in
    mode tcp
    bind *:80
    reqadd X-Forwarded-Proto:\ http
    default_backend tcp-routers

backend tcp-routers
    mode tcp
    balance source
         server node1 10.106.1.46:80  weight 10  inter 2000 rise 2 fall 5 maxconn 10000
        server node2 10.106.1.57:80  weight 10 inter 2000 rise 2 fall 5 maxconn 10000

还需要修改 操作系统的配置 /etc/sysctl.conf

net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.ipv4.tcp_max_tw_buckets = 6000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096        87380   4194304
net.ipv4.tcp_wmem = 4096        16384   4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.netdev_max_backlog = 262144
net.core.somaxconn = 262144
net.ipv4.tcp_max_orphans = 3276800
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_syn_retries = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_fin_timeout = 1
net.ipv4.tcp_keepalive_time = 30
net.ipv4.ip_local_port_range = 1024    65000
net.nf_conntrack_max = 1024000

    修改该配置文件,主要是为了增加机器可以打开的TCP连接数,还需要设置ulimit,设置的应该比较大一些。我使用的操作系统是Ubuntu10.04 ,需要加载模块 modprobe ip_conntrack

最终的测试结果:

 

 

之前使用nginx的时候,才300TPS。。。哎。。现在终于好多了。目前使用的机器都是虚拟机(4core /10G/ )如果使用更好的机器,不知道结果会不会好一些。

作者:wangdk789 发表于2014-6-24 19:22:30 原文链接
阅读:97 评论:0 查看评论

Web API 设计摘要

$
0
0

最近读了一本微电子书 Brian Mulloy 所著《Web API Design》感觉颇多收获,特对其内容做了个整理摘要以便回顾其观点精华以指导日常工作中的设计思路。


本文主要讲述 Web API 设计,追求一种更务实的 REST 风格。 正如作者所说 REST 是一种架构风格,而非严格的标准,没必要在形式定义上去做过多真论,到底什么才是真正的 REST? 设计的目的是为了表达某样东西是如何使用的,那么 API 设计的成功与否是由开发人员是否能够快速上手并用的愉快。

下面讲述了 Web API 设计的 13 个要点。


本条是关于 URL 设计的,使用名词而非动词,让 URL 保持简单和符合直觉。
这条是针对资源型 URL 设计而言,为什么?请看下一条。



用名词表达来领域概念,可以极大的减少 URL(API)的需求量。
使用 HTTP 协议方法动作来操作领域概念集。
通过分离概念和行为极大简化了 API 设计,让 URL 中只体现概念而非行为。



/elements/elementId 的二级 URL 形式,第一级表达元素集合,第二级表达集合中某个具体元素 id,因此使用名词复数是作者推荐的更符合认知直觉的方法。 同时对于元素集合使用更具体的领域名词含义更清晰,若使用抽象的概念名词则表达不清。



为了表达对象间的关联性,有一种方法是体现在 URL 的层级中,但 URL 层级过深并不便于记忆和认知。 这里推荐用 HTTP '?' 后可选参数来表达关联条件,简化 URL 复杂性。




如果可能使用 HTTP 的错误码来映射 API 错误。 HTTP 本身已经定义了广为认知的错误码区间,按类型将错误映射到对应区间对开发人员的学习和认知更友好。 提供尽可能详尽的错误信息。



绝不发布一个不带版本号的 API。关于这点做过软件维护的都明白,有一点细节就是版本号的选择,请使用 v1, v2 整数版本号而非 v1.1 v1.2 这种带小数点版本号机制。这里有个心理认知原因,带小数点的版本通常给人的感觉会频繁变化,而对于一个开放的 API 而言频繁变更绝对是应该避免的,因此不要使用带小数点版本号机制。



当我请求某个对象时不需要其全部属性或需要分页时怎么办?上图中例子已经很好的回答了。



该条针对非资源型 URL 设计而言,因为有些情况就是请求做一个计算,如上图中所示请求金额按币种进行转换。 对于此类 API,使用动词就是合适的,但最好在你的 API 文档中将此类 API 独立分类说明。



开发人员对文件系统的后缀名命名方式都很熟悉了,因此使用后缀名表达响应格式是自然的。 那么默认格式应该选择什么呢?毫无疑问是 JSON,这一点与 javascript 是 Web 端的通用语言有关。



如上,既然我们要照顾 javascript 语言,那么属性命名自然也要采用 javascript 语言传统的驼峰命名法。



简单的搜索可以用资源型 API 来模式化,但全局的搜索 Google 模式大家都很熟悉。



为 API 申请独立的子域名,有且仅有一个是最好的,而且最好是这个域名模式  api.youdomain.com



有了 API 还不够,辅助以 SDK 工具包可以进一步减轻 API 使用者的负担,最重要的是还能避免 API 的误用和低效使用。 其实这里还有一个好处:

Eating your own dog food


作者:mindfloating 发表于2014-6-24 19:20:04 原文链接
阅读:92 评论:0 查看评论

Mac技巧之苹果电脑上把视频转换成 GIF 动态图片的免费软件:Gifrocket

$
0
0
昨天在 Twitter 上看到 @Beryl_snw 分享了个苹果电脑 Mac OS X 系统上的小软件:Gifrocket,拖拽即可把视频转换成 GIF 动态图片。而且图片尺寸、质量、视频起止时间等参数都可以设定。

苹果电脑上把视频转换成 GIF 的免费软件:Gifrocket

极简风格 + 扁平化 的界面,把要转换的视频拖进去就行了。转换好的 GIF 图片会存储在和视频同一文件夹里。

点击右上角的齿轮按钮,可以调出设置界面,如下图所示:

苹果电脑上把视频转换成 GIF 的免费软件:Gifrocket

由于还是 Beta 版,有时候会出血问题,比如我遇到过修改分辨率以后出马赛克的情况。以后有更新了会改善吧。

Gifrocket 下载地址 http://www.gifrocket.com/


Tags - ,

Cloudera Impala 初体验

$
0
0

Impala是Cloudera公司主导开发的新型查询系统,它提供SQL语义,能查询存储在Hadoop的HDFS和HBase中的PB级大数据。已有的Hive系统虽然也提供了SQL语义,但由于Hive底层执行使用的是MapReduce引擎,仍然是一个批处理过程,难以满足查询的交互性。相比之下,Impala的最大特点也是最大卖点就是它的快速。Impala 为存储在 HDFS 和 HBase 中的数据提供了一个实时 SQL 查询接口。

Impala优点

下图来自 zdnet,描述了Impala的一些优点:

Impala优点

从上图中看出主要的优点:SQL友好,比Hive快,支持多种存储引擎文件格式,接口丰富(ODBC,JDBC,Client),开源,部署容易。


Impala架构

Impala解决方案包含下面几大部分:Clients:包括 Hue, ODBC clients, JDBC clients, and the Impala Shell 
Hive Metastore:存放结构定义的元数据,当你创建、删除、修改表结构,或者加载数据到表中时,会自动的通知Impala节点。
Cloudera Impala:运行在数据节点上,分析、调度、执行查询任务,每个Impala实例都可以接收、调度来自客户端的查询,这些查询分发到Impala节点进行查询,Impala节点相当于工作进程,执行查询,并将结果返回。
HBase and HDFS:存储供Impala查询的数据。

下图描述了Impala的架构:

Impala架构

上图中,黄色部分为Impala组件。Impala使用了Hive的SQL接口(包括SELECT、 INSERT、Join等操作),但目前只实现了Hive的SQL语义的子集(例如尚未对UDF提供支持),表的元数据信息存储在Hive的 Metastore中。StateStore是Impala的一个子服务,用来监控集群中各个节点的健康状况,提供节点注册、错误检测等功能。 Impala在每个节点运行了一个后台服务Impalad,Impalad用来响应外部请求,并完成实际的查询处理。Impalad主要包含Query Planner、Query Coordinator和Query Exec Engine三个模块。QueryPalnner接收来自SQL APP和ODBC的查询,然后将查询转换为许多子查询,Query Coordinator将这些子查询分发到各个节点上,由各个节点上的Query Exec Engine负责子查询的执行,最后返回子查询的结果,这些中间结果经过聚集之后最终返回给用户。

Impala进程

从进程的角度看分为如下的三类进程:

The Impala Daemon
是Impala的核心进程,进程名叫做:impalad,运行在所有的数据节点上,可以读写数据,并接收客户端的查询请求,并行执行来自集群中其他节点的查询请求,将中间结果返回给调度节点。调用节点将结果返回给客户端。
The Impala Statestore
状态管理进程,定时检查The Impala Daemon的健康状况,协调各个运行impalad的实例之间的信息关系,Impala正是通过这些信息去定位查询请求所要的数据,进程名叫做 statestored,在集群中只需要启动一个这样的进程,如果Impala节点由于物理原因、网络原因、软件原因或者其他原因而下线,Statestore会通知其他节点,避免查询任务分发到不可用的节点上。
The Impala Catalog Service
元数据管理服务,进程名叫做 catalogd,将数据表变化的信息分发给各个进程。

搭建的CDH5环境上找到了这些进程:

Impala进程分布
hostname进程名称
h1.worker.comstatestored、catalogd
h2.worker.comimpalad
h3.worker.comimpalad
h4.worker.comimpalad

[root@h1 ~]# hostname<span style="color:#3333ff;">h1.worker.com</span>
[root@h1 ~]# ps -ef | grep impala
impala   14048  7910  0 04:13 ?        00:00:30 /opt/cloudera/parcels/CDH-5.0.2-1.cdh5.0.2.p0.13/lib/impala/sbin-retail/catalogd --flagfile=/var/run/cloudera-scm-agent/process/57-impala-CATALOGSERVER/impala-conf/catalogserver_flags
impala   14070  7910  0 04:13 ?        00:03:01 /opt/cloudera/parcels/CDH-5.0.2-1.cdh5.0.2.p0.13/lib/impala/sbin-retail/statestored --flagfile=/var/run/cloudera-scm-agent/process/61-impala-STATESTORE/impala-conf/state_store_flags
root     48029 31543  0 10:13 pts/0    00:00:00 grep impala
[root@h1 ~]# 

[root@h2 ~]# hostname<span style="color:#3333ff;">h2.worker.com</span>
[root@h2 ~]# ps -ef | grep impala
impala   13919  4405  0 04:13 ?        00:01:12 /opt/cloudera/parcels/CDH-5.0.2-1.cdh5.0.2.p0.13/lib/impala/sbin-retail/impalad --flagfile=/var/run/cloudera-scm-agent/process/58-impala-IMPALAD/impala-conf/impalad_flags
root     24212 18173  0 10:16 pts/0    00:00:00 grep impala

Impala快的原因

从网上找了一段Impala快的原因,主要有以下几方面的原因。
Impala不需要把中间结果写入磁盘,省掉了大量的I/O开销。
省掉了MapReduce作业启动的开销。MapReduce启动task的速度很慢(默认每个心跳间隔是3秒钟),Impala直接通过相应的服务进程来进行作业调度,速度快了很多。
Impala完全抛弃了MapReduce这个不太适合做SQL查询的范式,而是像Dremel一样借鉴了MPP并行数据库的思想另起炉灶,因此可做更多的查询优化,从而省掉不必要的shuffle、sort等开销。
通过使用LLVM来统一编译运行时代码,避免了为支持通用编译而带来的不必要开销。
用C++实现,做了很多有针对性的硬件优化,例如使用SSE指令。
使用了支持Data locality的I/O调度机制,尽可能地将数据和计算分配在同一台机器上进行,减少了网络开销。

Impala源代码

https://github.com/cloudera/impala

后面重点分析下Impala的源代码。个人感觉和分布式数据库查询引擎的架构比较类型。


参考文档

Cloudera Impala User Guide

Cloudera aims to bring real-time queries to Hadoop, big data

Cloudera Impala:基于Hadoop的实时查询开源项目

Impala:新一代开源大数据分析引擎


原创作品,转载请注明出处  http://blog.csdn.net/yangzhaohui168/article/details/34185579



作者:yangzhaohui168 发表于2014-6-25 1:24:24 原文链接
阅读:5 评论:0 查看评论

迁移Zabbix数据库到TokuDB

$
0
0

背景介绍

线上的Zabbix数据库有几个大表数据量疯狂增长,单表已经超过500G,而且在早期也没做成分区表,后期维护非常麻烦。比如,想删除过期的历史数据,在原先的模式下,history、history_uint等几个大表是用 (itemid, clock) 两个字段做的联合主键,只用 clock 字段检索效率非常差。

TokuDB 是一个高性能、支持事务处理的 MySQL 和 MariaDB 的存储引擎。TokuDB 的主要特点是高压缩比,高 INSERT 性能,支持大多数在线修改索引、添加字段,特别适合像 Zabbix 这种高 INSERT,少 UPDATE 的应用场景。

迁移准备

欲使用 TokuDB 引擎,服务层可以选择和 MariaDB ,也可以选择 Percona ,鉴于我以往使用 Percona 的较多,因此本次也选择使用 Percona 版本集成 TokuDB 引擎。

当前最新版下载地址: http://www.percona.com/redir/downloads/Percona-Server-5.6/LATEST/binary/tarball/Percona-Server-5.6.17-rel66.0-608.TokuDB.Linux.x86_64.tar.gz

按照正常方式安装即可,配置文件中增加3行:

malloc-lib= /usr/local/mysql/lib/mysql/libjemalloc.so
plugin-dir = /usr/local/mysql/lib/mysql/plugin/
plugin-load=ha_tokudb.so

如果不加载jemalloc,启动时就会有类似下面的报错:

[ERROR] TokuDB not initialized because jemalloc is not loaded
[ERROR] Plugin 'TokuDB' init function returned error.
[ERROR] Plugin 'TokuDB' registration as a STORAGE ENGINE failed.

并且,修改内核配置,禁用transparent_hugepage,不关闭的话可能会导致TokuDB内存泄露(建议写到 /etc/rc.local 中,重启后仍可生效):

echo never > /sys/kernel/mm/redhat_transparent_hugepage/defrag
echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag

如果不修改内核设置,启动时就会有类似下面的报错:

Transparent huge pages are enabled, according to /sys/kernel/mm/redhat_transparent_hugepage/enabled
Transparent huge pages are enabled, according to /sys/kernel/mm/transparent_hugepage/enabled
[ERROR] TokuDB will not run with transparent huge pages enabled.
[ERROR] Please disable them to continue.
[ERROR] (echo never > /sys/kernel/mm/transparent_hugepage/enabled)
[ERROR]
[ERROR] ************************************************************
[ERROR] Plugin 'TokuDB' init function returned error.
[ERROR] Plugin 'TokuDB' registration as a STORAGE ENGINE failed.

然后,初始化数据库,启动即可。

我的服务器配置:E5-2620 * 2,64G内存,1T可用磁盘空间(建议datadir所在分区设置为xfs文件系统),下面是我使用的相关选项,仅供参考:

#
#my.cnf
# 
# Percona-5.6.17, TokuDB-7.1.6,用于Zabbix数据库参考配置
# 我的服务器配置:E5-2620 * 2,64G内存,1T可用磁盘空间(建议datadir所在分区设置为xfs文件系统)
# TokuDB版本:Percona-5.6.17, TokuDB-7.1.6(插件加载模式)
# 
# created by yejr(http://imysql.com), 2014/06/24
# 
[client]
port            = 3306
socket          = mysql.sock
#default-character-set=utf8
 
[mysql]
prompt="\\u@\\h \\D \\R:\\m:\\s [\\d]>
#pager="less -i -n -S"
tee=/home/mysql/query.log
no-auto-rehash
 
[mysqld]
open_files_limit = 8192
max_connect_errors = 100000
 
#buffer & cache
table_open_cache = 2048
table_definition_cache = 2048
max_heap_table_size = 96M
sort_buffer_size = 2M
join_buffer_size = 2M
tmp_table_size = 96M
key_buffer_size = 8M
read_buffer_size = 2M
read_rnd_buffer_size = 16M
bulk_insert_buffer_size = 32M
 
#innodb
#只有部分小表保留InnoDB引擎,因此InnoDB Buffer Pool设置为1G基本上够了
innodb_buffer_pool_size = 1G
innodb_buffer_pool_instances = 1
innodb_data_file_path = ibdata1:1G:autoextend
innodb_flush_log_at_trx_commit = 1
innodb_log_buffer_size = 64M
innodb_log_file_size = 256M
innodb_log_files_in_group = 2
innodb_file_per_table = 1
innodb_status_file = 1
transaction_isolation = READ-COMMITTED
innodb_flush_method = O_DIRECT

#tokudb
malloc-lib= /usr/local/mysql/lib/mysql/libjemalloc.so
plugin-dir = /usr/local/mysql/lib/mysql/plugin/
plugin-load=ha_tokudb.so
 
#把TokuDB datadir以及logdir和MySQL的datadir分开,美观点,也可以不分开,注释掉本行以及下面2行即可
tokudb-data-dir = /data/mysql/zabbix_3306/tokudbData
tokudb-log-dir = /data/mysql/zabbix_3306/tokudbLog
 
#TokuDB的行模式,建议用 FAST 就足够了,如果磁盘空间很紧张,建议用 SMALL
#tokudb_row_format = tokudb_small
tokudb_row_format = tokudb_fast
tokudb_cache_size = 44G
 
#其他大部分配置其实可以不用修改的,只需要几个关键配置即可
tokudb_commit_sync = 0
tokudb_directio = 1
tokudb_read_block_size = 128K
tokudb_read_buf_size = 128K

迁移过程

建议在一台全新的服务器上启动Percona(TokuDB)实例进程,初始化新的Zabbix数据库,直接将大表转成TokuDB引擎,并且开启分区模式。这样相比直接在线ALTER TABLE或者INSERT…SELECT导入数据都要来的快一些(我简单测试了下,差不多能快2-3倍,甚至更高)。

在做数据迁移时,建议在目标服务器上做库表结构初始化,在源服务器上采用分段方式导出,一个表导出多个备份文件,方便在恢复时可以并发导入。在导入时,并且记得临时关闭 binlog,最起码设置 sync_binlog = 0 以及 tokudb_commit_sync = 0,以提高导入速度。采用 mysqldump 增加 -w 参数即可实现根据条件分段导出,具体可参考上一次的文章: [MySQL FAQ]系列— mysqldump加-w参数备份,或者是用 MySQLDumper

需要用到外键的表继续保留InnoDB引擎,其他表都可以转成TokuDB, history_str、trends、trends_uint、history、history_uint等几个大表是一定要转成TokuDB的, events由于需要用到外键,所以继续保留InnoDB引擎。

我将表结构初始化SQL脚本提供下载了,一份是没有采用分区表的,一份是采用分区表的,大家可自行选择。一般如果记录数超过1亿,就建议使用分区表,根据时间字段( clock)分区,方便后期维护,例如删除过期历史数据什么的。

收尾

剩下的基本没啥可做的了,就是观察下运行状态,是否还有个别慢查询堵塞。在我的环境中,一开始把items表也转成TokuDB了,结果有个画图的SQL执行计划不准确,非常慢。后来发现 items表也需要用到外键,于是又转回InnoDB表,这个SQL也恢复正常了。

数据库初始化脚本我整理后提供下载了,大家可以直接使用。

附件1:不使用分区表附件2:使用分区表

适用版本:

Zabbix版本:Zabbix 2.2.0
TokuDB版本:Percona-5.6.17, TokuDB-7.1.6(插件加载模式)

如果还有什么问题,欢迎加入我的QQ群( 272675472)讨论。

一家在线医生预约平台值16亿美元?现实就是如此

$
0
0

文/瑞雪

据VC Experts获得的一份公司文件显示,在线医生服务预约平台ZocDoc已经筹集了1.52亿美元的新资金,其估值达到了16亿美元。这份文件的日期标注为5月30日。

总部位于纽约的ZocDoc此前已经筹集了9790万美元资金,当时的C系列融资回合是由高盛集团和DST Global牵头的,对该公司的估值为7亿美元。DST Global是俄罗斯企业家尤里·米尔纳(Yuri Milner)运营下的一只基金,该基金也参与了最新的这个融资回合。

ZocDoc成立于2007年,目前其员工总数已达550人,在2000多个城市中提供自身服务,计划在年底以前将其服务拓展至遍及全美各地。这家公司已经吸引了一些传统医疗保健公司的注意力,如安泰集团(Aetna)等。此前有报道称,安泰集团曾在2013年出价3亿美元收购ZocDoc。

ZocDoc拒绝就此置评。今年早些时候,ZocDoc首席执行官塞鲁斯·马苏米(Cyrus Massoumi)对此前的市场传闻作出了回应,称该公司不会急于IPO(首次公开招股)上市。

截至目前为止,已有500多万名病人使用ZocDoc的系统来预订医疗服务,但该公司从未披露已有多少名医生向其支付包月费用以使用相关服务,原因是ZocDoc不希望透露公司营收。

在今年2月份,ZocDoc将其第五名员工、时任首席财务官的Netta Samroengraja重新任命为首席人力官,并引入了曾担任婚庆服务科技公司WeddingWire首席财务官的约瑟夫·霍兰德(Joseph Holland)接替Samroengraja的原有职务。马苏米在当时接受《财富》杂志采访时表示,这一职务变动是Samroengraja本人的决定。

本月早些时候,ZocDoc发布了一种面向企业雇员的最新订阅服务,这种名为ZocDoc for Business的服务可提供更加个性化的搜索结果和服务。

来源:腾讯科技

责编:杜航

微软安卓新机 Nokia X2:售价约 830 元 将于 7 月上市

$
0
0

微软可能从未想过放弃 Android 市场。在今年的世界移动大会上,公司发布了其首款 Android 智能手机 Nokia X。4 月收购诺基亚手机部门后,如今微软又推出了其升级版——Nokia X2。

新机外形与 Nokia X 相似,但尺寸和配置都有提升,拥有 4.3 寸显示屏、1GB RAM 和 1.2GHz 双核处理器。其中 1GB RAM 将会大幅提升应用切换时的流畅性,而这一点也是 Nokia X 的短板。此前的 Nokia X 只有一颗返回键,引发无数用户吐槽,如今微软终于回归智能机主流,在 X2 正面放置了返回和 Home 两颗触控键。

Nokia X2 继续使用类似 Window Phone 的定制版 Android 操作界面,作为一款售价只有 99 欧元(约 830 元)的低端机型,它虽然不具备过硬的配置,但却拥有诺基亚出色的品质和做工。微软希望未来可以占领低端手机市场,通过自家云服务和相关应用吸引更多用户。诺基亚 X2 将于今年 7 月上市,目前有绿、橙、黑三种颜色可选,未来或将开发其他配色。

来源: The Verge








famo.us是什么?

$
0
0

 

初次访问 famo.us,被他的动画和交互效果所震撼,研究过其代码后,发现用到三种技术:matrix3d变换,requestAnimationFrame实现动画,以及物理运动引擎,单从技术上看三者都不难实现,但事物表面背后往往隐藏深意,famo.us的由来和目标却是野心勃勃。

famo.us2

创始人为web apps性能低下困扰已久,后受到 Twitter Bootstrap的启发,试图提供一种用于开发本地级运行效率的web app框架,什么Sencha touch, jQuery Mobile等都是低效的代名词,这些为website而生的东西不适合复杂的web apps应用,应绕开浏览器固有的一些缺陷(document 渲染机制,CSS3导致的性能问题,浏览器之间的差异等等),实现一种针对web app的渲染引擎。

对于famo.us能否真正带来一波web app开发的热潮,尚不可知,从各种资料来看,已经有了一些app示例,但我未曾亲眼所见,目前公开的只有官网的元素周期表的例子,平板上运行也算流畅,但耗电厉害,实用性有待考证。

对技术感兴趣的朋友,可以看看这篇演示文稿: http://www.slideshare.net/befamous/html5-devconf-oct-2012-tech-talk ,作者详细介绍了famo.us 技术上的由来,作为演示文稿,循序渐进,简洁明了的思路也是值得借鉴的

famo.us

下面是2012年九月,famo.us的第一封简报,原文: http://us5.campaign-archive1.com/?u=4656ba2b0a364690c8530bc1e&id=675bbf010f

为什么我们如此特别

尝试使用HTML5构建一个web应用,实现可靠的本地程序级的运行效率,通过诸如Sencha Touch,jQuery Mobile和Appcelerator Titanium这样的框架是不可能实现的,实际上,很多公司,譬如Facebook,已经宣布放弃一些在HTML5上的努力,因为浏览器的一些固有问题需要很长时间来解决。Famo.us之所以特别,是因为我们实现了一种渲染引擎,绕开了浏览器的这些固有问题,从而实现可靠的本地级别高性能的复杂web应用,兼容多种设备,简单的说,经过多年的开发,我们解决了困扰复杂web应用的性能问题,应用的布局,动画和设计这些原本只存在于本地应用的东西,现在也可以用于web开发中,我们希望能带来一波从未有过的web app开发浪潮

一旦我们有了突破,下一步我们将寻求一种方式来分享这些成果,以社区驱动,检验,接受和合理分析

famo.us是什么?

如果你喜欢Twitter Bootstrap,那么你将爱上famo.us,很多方面我们都视Bootstrap为灵感来源
Bootstrap 引导web开发者构建跨设备访问的网站,给开发者提供视图图层,网站布局,框架,UI(用户界面)和UX(用户体验)组件,主题,字体和JavaScript插件
Famo.us 引导web开发者构建跨设备访问的应用程序,给开发者提供视图图层,应用布局,框架,UI和UX组件,主题,字体和JavaScript插件

路线图

我们将参照Bootstrap类似的展示方式,当我们正式启动,我们将公开我们GitHub上的工程,这样你可以分享和连接,我们还会创建Google组,你可以提问,Mark和我会直接回复,我们还会通过简报,博客(当它发布的时候),twitter @befamous 进行更新,我们将花费大量的时间保证文档完整和示例打包,就时机而言,我们希望第一个完整版本能在今年晚些时候发布(主要取决于文档)

使用 famo.us

famo.us的开发与Bootstrap类似,使用famo.us你可以先从应用布局开始,然后混合和匹配相应的famo.us 的UI和UX组件和部件

在我们的”hello world”应用中,你将使用famo.us 的卡片布局(Spotify, Twitter, Facebook和很多其他应用都使用这样的布局)构建一个Twitter iPad 应用的复制品,并采用了famo.us的UI和UX组件,并填充了一些演示数据,一旦你掌握了famo.us的窍门,你可以选择替代的应用布局,并按你所喜欢的修改,实际上你可以创建自己的应用布局,组件和主题,并将他们分享到famo.us市场,我们的目标是所有本地应用的主流布局,另外我们希望能和社区合作创造出更多东西,每个应用布局将能用于iOS和Android,同时作为Web app或者包装进一个本地app,实现手机,平板和PC上布局的灵活转换。



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


ITeye推荐



eMarketer:数据解读美国电子商务的发展现状及未来前景

$
0
0

本文从五个方面透析美国电子商务的发展现状及未来前景,除非特别说明数据均来源于eMarketer。

美国有多少网购人群

数据显示目前美国的互联网用户约为2.4亿,渗透率高达74.9%。其中已经有约3/4的互联网用户属于网购人群,网购渗透率达到71.6%。

如此高的互联网用户渗透率和网购渗透率意味着美国电子商务的增长将更多的来自于老用户线上销售额的提高,而不是吸引新用户的参与,这点与中国的情况不太一样。

美国的线上交易额有多大

2013年,eMarketer预计美国电子商务销售额将达到2,590万美金(约为1.6万亿人民币),相比2012年的2,255万美金(约为1.4万亿人民币)增长14.8%。

尽管电子商务的前景依然十分光明,但是2012年美国的线上交易占社会消费品零售总额的比例也才不过6%。很多研究都认为,这一比例将最终达到20%或是更多。如果这样的话,那么将有更多品类的销售额转移到线上。

移动商务的情况怎么样

美国移动互联网发展迅速,来自移动端的交易额也是逐年上涨,并且这一趋势在未来相当长的一段时间内都将持续。未来,移动互联网将不仅带来电子商务交易额的增加,也将使得电子商务的交易方式发生一些改变。eMarketer 的数字显示2013年移动端的成交额将达390亿美金(约合2,418亿人民币),相较2012年的250亿美金(约合1,550亿人民币)增长56.5%。2012年来自移动端的交易额占比达到11%,预计2013年将达15%,2017年提升到25%。

平板电脑是驱动移动商务的关键。数字显示,目前来自移动设备的交易额平板占到了65%,手机则为35%。预计到2017年,这一比例将是71.5%, vs. 27% 。

移动商务交易额的迅速增长得益于智能手机和平板电脑等移动设备的普及。2013年,预计将有7,940万美国消费者使用移动设备购物,占有整体网购人群的51%。预计2017年,这一比例将提升至77.1%。

平板电脑对消费者购物的影响更大,拥有一个平板电脑基本确保了消费者一定会使用他购物。到2017年,预计78%的平板电脑用户将会通过他购物。69.6%的网购用户将会使用平板电脑,49.9%的会使用智能手机。

什么品类卖的最好

电脑、消费电子等3C产品以及服装、服饰占有美国线上交易额的最大部分,二者加起来预计2013年将占有42.9%的交易额,预计2016年将达到45.6%。然而大宗产品例如汽车配件,以及食物和饮料的线上销售额并不令人十分满意,但是这也预示着电商巨大的成长空间。目前,这些商品的销售大部分还是来自于线下。

未来的增长率如何

eMarketer 预计美国的电子商务交易额在2012-2017年间的复合增长率仍将达到14.0%,这是同类机构中最为激进的。

就品类来看,很多品类 2012年至2017年间的复合增长率依然很高。例如服装、服饰和图书、音像制品的年均复合增长率分别达到了17.2%和16.3%。与此同时,食物和饮料等线上销售额较低的品类预计也将获得比较高的增速,预计年均复合增长率将达到17%。

 


© 推荐 for 互联网的那点事. | 猛击下载: iPhone客户端猛击下载: Android客户端

谷歌眼镜重大升级 内存翻倍至2GB推出语音拍照功能

$
0
0

QQ图片20140625171513

谷歌眼镜重大升级 内存翻倍至2GB推出语音拍照功能

【TechWeb报道】6月25日消息,据国外媒体报道,紧接着谷歌眼镜第一次走出美国市场,谷歌再次宣布对谷歌眼镜进行一次重大的软件和硬件升级。新版谷歌眼镜内存容量翻倍,电池续航能力增加20%。

The Verge网站称,谷歌相关团队通过官方博文宣布了升级的消息。新版谷歌眼镜的内存将由现在的1GB升级至2GB。扩大的内存,将支持同步运行多个软件,另外软件的启动速度也会加快。

需要指出的是,目前的谷歌眼镜仍然处在测试版,目前尚未推出眼镜的正式版产品。而在软件和硬件上,谷歌将一直根据用户意见逐渐完善产品设计。

鉴于之前一千多位使用者的反馈,电池续航能力成为反馈热点。谷歌称,得益于固件的改进,新版谷歌眼镜搭载了更大容量电池,续航能力较去年的设备增加了20%。

在软件方面,谷歌新增了一项语音操控的摄像头取景器功能。这样用户可以更好的拍摄照片。谷歌表示这也是许多用户提出的要求。另外,在本月初,谷歌眼镜还增加了可以显示停车地点以及追踪快件等功能。

谷歌表示,今年三月份交付的谷歌眼镜,电池续航能力已经有所加强。从今以后,消费者将拿到最新版的谷歌眼镜。

但是,谷歌眼镜在英国的售价高达1700美元,超1万元人民币,并高于美国本土1500美元售价。这几乎是市面上最昂贵的智能穿戴设备。(露天)


Nokia X2 上手:软硬一体,更胜一筹

$
0
0

R3JvdXBfMDNfRFNJTU1XdHFhSE5r1-1000x1000

今年 2 月推出首款运行 Android 系统的 Nokia X 后,诺基亚在不到半年的时间内带来了第二代设备——Nokia X2。新机调整了屏幕尺寸,同时升级了芯片、内存和镜头像素。

只从外观判断,很难看出 Nokia X2 是一款定位低端的设备。诺基亚将 Asha 500 系列备受好评的双注塑背壳带给了 Nokia X2,机器外壳包裹了一层类似水晶的透明“糖衣”,观感出色。

不过 Asha 500 背壳容易磨花的缺点,想必 Nokia X2 也一并继承。好在 Nokia X2 的背壳可以拆卸,磨花后可以再买一块背壳替换。

Nokia X2 的四角和机身边缘经过打磨。和前代相比,它们过渡地更为顺滑,整机的握持体验更胜一筹。

QnJpZ2h0LU9yYW5nZS1Gcm9udC1E1-1000x1000

Nokia X2 的另一项重要变化是加入了 Home 键。微软物流经理 蒂莫·萨里说,Home 键可以帮助用户更方便地浏览手机内容。

事实也确实如此。Android 同时允许用户通过返回键和 Home 键回到主屏,但一次点按 Home 键显然比多次点按返回键更加高效。Home 键的加入也让返回键被剥离出来赋予更多功能——长按 Nokia X2 的返回键,系统会弹出类似 Windows Phone 8.1 的多任务列表,拖拽应用卡片或点击关闭按钮都可以退出后台程序。

Nokia-X2_multitasking

主屏幕的滑动手势同样被分化出更多功能,过去左右滑动都是进入 Fastlane 界面,现在左滑手势被调整为进入应用列表——已装应用会像 Windows Phone 一样按字母顺序纵向排列,点击首字母就可以快速查找某款应用。

右滑手势则依旧交给 Fastlane。待办事项和闹钟等重要事项可以固定在界面顶部,其它任务按使用时间排列。长按任务卡片,就可以删除它在 Fastlane 中的记录。

其它的系统调整还包括拍照界面和通知中心的优化。拍照界面中,过去位于底栏的模式、拍摄和相册按钮,现在被统一为拍摄键,应用界面更多地用于显示视野内的景物;通知中心加入了双 SIM 切换入口,点击后可以快速查看不同 SIM 卡的来电通知和短信等内容。

“我们做了很多调整。”参与 Nokia X2 开发的设计师亚尼斯·潘尼亚拉斯 概括道,“有些调整很明显,有些则比较微妙。不过它们的结果都是为了实现更加丰富、快捷、简单的用户体验。”

R3JvdXBfMDJfRFNJTU1XdHFhSE5r1-1000x1000

潘尼亚拉斯的努力获得了不错的反馈,Engadget 在上手后 评价道:“在简单的上手过程中,(Nokia X2 的)软件运行顺畅,500 万像素的自动对焦镜头同样响应快速。总的来说,是个不错的组合。”

不过双核芯片、大尺寸屏幕和高像素镜头的加入让 Nokia X2 的成本大幅增加,它的售价达到 99 英镑,约合人民币 1050 元。Nokia X 不到 600 块的售价恐怕很快就要成为历史,未来诺基亚拥趸们需要支付更多资金才能体验到 Nokia X2。

Nokia X 系列的另一个问题在于,用户最终能否接受它的系统风格,而不是用刷机和第三方桌面来替代它——目前来看,大多数 Android 应用的图标风格和 Nokia X 的系统 UI 冲突明显,两种风格带来的割裂感让不少人对此排斥。

今年 2 月的 MWC 上,埃洛普对到场的数千名参会者表示,运行改版 Android 系统的 Nokia X 是用户通往微软服务的一扇大门。但现在来看,想通过 Nokia X 吸引到更多用户,微软还有很多工作要做。

优酷

 

题图来自 Nokia Conversation

阅读、思考、自我反省,相信坚持可以改变人生。

#欢迎关注爱范儿认证微信公众号:AppSolution(微信号:appsolution),发现新酷精华应用。



爱范儿 · Beats of Bits |原文链接· 查看评论· 新浪微博· 微信订阅· 加入爱范社区!


微信CRM的六大功能与微信开放接口

$
0
0

小编按:最近身边关于微信电商的讨论愈加热烈,甚至不少朋友都对微信CRM进行了析构,作为“移动互联网上最大的CRM入口”,微信不单纯只是帮助企业和消费者之间的沟通,甚至可以做企业内部协调。小编特找了一篇对于微信CRM全面解析的好文与大家分享。

CRM1
摘要 : 微信CRM,首先要从业务架构上进行设计清楚。微信CRM的本质,是在微信渠道上利用微信的特点和接口而扩展的CRM系统。而CRM系统,一般可以分为五大模块:客户、销售、营销、服务、会员,另外还有一些附属模块:产品、知识库、活动、交易、统计报表等。在确定微信CRM的业务架构后就可以进一步设计具体的数据模型、功能模块和接口对接了。

业内一直都在传说微信是天生的CRM,可是没有人看到过微信CRM的真容。随着微信最新公众平台的改版和开放接口的微信认证开放,微信CRM离企业越来越近。汉拓科技第一时间申请认证并对接了微信开放接口,在此就详细展开一下,与大家分享如何基于微信开放接口来开发企业的微信CRM?

企业要开发微信CRM,首先要从业务架构上进行设计清楚。微信CRM的本质,是在微信渠道上利用微信的特点和接口而扩展的CRM系统。而CRM系统,一般可以分为五大模块:

客户、销售、营销、服务、会员,另外还有一些附属模块:产品、知识库、活动、交易、统计报表等。在确定微信CRM的业务架构后就可以进一步设计具体的数据模型、功能模块和接口对接了。让我们跟随汉拓科技的SocialCRM来一一体会一下微信CRM的功能模块设计。

微信SCRM框架图

CRM2

客户管理模块

客户管理主要是微信CRM进行客户信息管理、客户分级分类、客户沟通关怀和客户生命周期管理的基础模块。

客户信息管理可以通过获取用户基本信息接口建立基本的客户信息档案,通过获取用户地理位置接口获取客户的位置信息,只是需要建立位置轨迹管理,而不是简单的坐标位置储存。通过关注订阅事件和取消关注事件,记录客户的关注时间和取消关注时间。

针对公众号已有的关注者,可以通过获取关注者信息接口批量获取公众号粉丝,该接口每次获取10000条,几个循环就可以把品牌公众号上的粉丝导入到微信CRM进行管理。

客户分类可以基于用户分组接口,实现公众号的分组与微信CRM的分组同步,客户分类后形成目标列表,是营销活动的目标对象,也是客户服务差异化的基础。

客户生命周期管理,基于客户的关注时间、取消关注时间以及当前的微信互动频率、微信互动信息方向等,建立一个粉丝的生命周期管理。

CRM3

营销管理模块

微信团队的“微信不是营销工具”,更多是指不是轰炸式的营销,不是天天推送消息,而是对于通道而言,对于个性化和CRM而言,精准营销和许可营销依旧是优化的方向之一。因此,微信CRM中,营销管理是需要用心设计的一个模块。

微信CRM的营销管理的核心是,建立拉式营销而不是推式营销。因此,建议品牌尽量放弃那种一天一推的轰炸模式,而是设计吸引客户主动触发的拉式营销。从这个角度上讲,基于消息模板单发消息接口应该限制,或者限制批量群发营销,鼓励一对一的客户事件触发的下行模板消息。

营销管理需要用到的是事件推送接口、发送位置消息接口、自定义菜单接口和扫一扫功能,通过客户触发的时间、发送的消息、点击自定义菜单以及扫码,让客户进行具体的营销活动。比如关注订阅事件触发一个最新优惠活动的下行消息、发送位置消息签到下行一个特定活动,或者点击相关活动菜单进入到营销活动的页面或者链接。

微信CRM可以让客户扫描二维码进入到营销活动页面。还可以通过带参数的二维码,标识这个市场活动的营销代码,客户扫码可以识别来源,这样在微信CRM中实现市场活动—线索的响应反馈闭环。

拉式营销的主要形态基本上就是O2O,只是需要与业务运营进行融合,实现常态化。但要看企业是不是能够真正做到重构?比如日常的营销活动,必须从微信运营部门会签,看每一个营销活动是否可以增加微信元素?

微信呼叫中心

从客户服务模块延伸出来一个呼叫中心模块,为什么把微信呼叫中心单独拎出来专门讲?是因为呼叫中心是大CRM的一部分,但是又因为业态特殊所以独成一体。针对呼叫中心的设计,比较特殊,而且可以用在客户服务上,也可以用在销售外呼上,甚至来进行营销活动的预热或者邀约等。

由于一对一的主动单发是受限的,因此微信的销售模块就不单独设计了,部分功能而是融合在微信呼叫中心里面。

微信CRM的呼叫中心模块,可以基于自定义菜单接口,实现呼叫中心的IVR交互菜单,同时再单独开发模块实现多用户和智能分配,实现不同菜单分配到不同用户或用户组,这是呼叫中心最基本的IVR和ACD。

由于呼叫中心多语音的特点,以及座席客服都是声音甜美,所以需要通过语音识别接口实现对语音微信呼叫的识别和转换,同时通过多媒体文件接口实现微信语音呼叫的备案(专业术语叫录音系统)和质检功能,并可以通过多媒体文件接口回复实时的或者预录制好的语音。

微信CRM的呼叫中心模块偏重于微信客服坐席的运营管理,最难的也是业务设计而不是系统。比如微信座席是与传统座席混排还是独立?这直接涉及到呼叫中心模块的设计。

微信智能交互引擎

CRM6

会员门户模块

说到会员,最好的案例就是陈坤的公众号,他基本上实现的是一个会员微信门户,用到的功能主要是Oauth2.0授权接口,实现网页上的获取信息等动作。

微信CRM的会员模块可以通过设计HTML5的会员掌上门户网页,同时对接微信CRM的客户管理等模块,形成微信H5会员门户+微信CRM的完整管理体系。

以此类推,你可以利用Oauth2.0授权接口+HTML5实现企业的其他微信门户,比如掌上网站、企业门户、内部OA等等。这块不属于微信CRM,叶开就不赘述了。

统计报表管理

微信CRM的统计报表管理,这块功能是不可或缺的,但又是最难伺候的,因为是要给老板看的。微信的开放接口并没有跟数据相关的,因此可能很多东西要你自己来想办法了。

首先,基于前面讲的关注、取消关注等数据,统计用户的新增、流失对比分析;

其次,基于前面讲的微信交互的时间、方向等数据,设计微信交互的统计分析;

然后,再高深点儿的是在市场活动中对O2O的跟踪、管理和效果,形成市场活动的效果响应统计,也就是ROI分析;

其实吧,如果腾讯移动分析的API能够开放,那你的微信内容的到达、阅读、分享转发等数据就可以成为微信CRM的统计报表中很重量级的报表了。

仪表盘/统计报表管理

CRM7

最后,聊一下微信大数据的构建。虽然微信的获取用户信息比较少,只有四五项,但这才是真正“逼迫”你在交互中设计碎片化数据的采集。通过微信CRM设置交互索引很关键,对话中的语义分析、点击不同菜单的事件等都可以对应标签,每一次的时间、地理位置等都是时间空间数据,这些组合起来就是微信CRM的大数据,不要小看喔,看看你能够从这里面分析出什么宝贝来?

基于微信的开放接口设计企业的微信CRM,基本上讲完了。建议尽快去申请微信认证,这样就可以对接进来,早日实现你的微信CRM,当然微信认证拥有普通认证的全部权利,比如搜索可见、认证标识等等。悄悄的透露一下,微信认证的企业有可能向微信团队的市场经理单独申请微支付接口,好处你晓得啦。

微信CRM对于企业而言,还是一项基础服务,汉拓科技已经有成熟的系统,企业可以自己开发,也可以来合作,这方面是开放的。说基础服务,主要是在与企业更重要的是增值服务,因为企业的运营价值往往体现在微信CRM上的增值应用,比如O2O、联合促销、口碑传播、促销引擎等,这方面汉拓科技邀请有诚意、有创新意识的中型企业进行合作试点,共同打造微信CRM上的创新应用。

好吧,严肃的设计话题讲完了,我们来猜测一下:微信会不会自己开发CRM?怎么,这个很难猜测呢,那你来猜测一下:你会不会自己开发微信CRM?

来源:yepoint (公众号)

 

 


(关注更多人人都是产品经理观点,参与微信互动(微信搜索“人人都是产品经理”或“woshipm”)

iOS 7.1.X 系统完美越狱工具发布

$
0
0

6月24日凌晨,国内的黑客团队“盘古”终于改变了iOS越狱的历史,其自制的iOS 7.1.X 完美越狱工具正式发布。盘古黑客团队将这款越狱工具直接命名为“Pangu”,它兼容于所有可以运行 iOS 7.1-iOS 7.1.x 的苹果设备。盘古团队提醒越狱用户,虽然这款工具在大多数机型上都已经测试成功,但还是希望用户在越狱前进行数据备份,以免造成数据丢失。

jailbreak

这里也提醒 iOS 7.0.4已越狱用户,iOS 7.1.1只是在流畅度上有所提升,如果你已越狱并且运行正常,完全没必要升级到 iOS 7.1.1。

另外,由于之前太极助手风波,大家对国内的越狱工具总是有点将信将疑,Pangu 完美越狱工具确实也捆绑了某助手( 但是可选的),已有很多小白鼠测试过该越狱工具,很完美,一些常用插件也可以在 iOS 7.1.1上运行。

至于这款完美越狱工具发布为什么突然发布,而不等 iOS 7.1.2及 iOS 8发布后再行推出,可能是因为该完美越狱工具同样支持即将发布的iOS 7.1.2,而并不支持 iOS 8 beta,所以没有保留这个越狱漏洞的必要了。

官网下载地址: Pangu越狱工具

用搜索神器Everything定位Webshell木马后门

$
0
0

Everything是速度最快的文件名搜索软件。其速度之快令人震惊,百G硬盘几十万个文件,可以在几秒钟之内完成索引;文件名搜索瞬间呈现结果。它小巧免费,支持中文,支持正则表达式,可以通过HTTP或FTP分享搜索结果。

Everything搜索工具的最大优点是速度。其速度不是快,是极快;用户不是满意,而是震惊。

因为Everything的索引无需逐一扫描硬盘文件,而是直接读取NTFS文件系统的USN日志。所以速度已经快到令人震惊,甚至是愤怒了:凭什么可以这么快!

“善用佳软”上有Everything的详细介绍: http://xbeta.info/everything-search-tool.htm

—–分割线—–

小菜最近闲着无事,搭建了一个blog,但因为是第一次,比较紧张,害怕被黑客入侵,所以狂补安全方面的知识,但无奈自身水平不够,而且在了解了一些安全知识之后认为如果网站被盯上了,被入侵是迟早的是,所以,我只好做被入侵之后的打算了:尽可能的查找被嵌入的webshell……

然后最近也在总结一些软件的使用经验,刚好到了Everything这款搜索神器,学着学着就想试试用Everything来辅助webshell的查找,也就有了下面的内容(这里查找的思路是通过文件的修改时间来进行判断的,因为对于一个相对稳定的网站来说,网页代码不会经常性的发生变化,所以可以通过文件的修改时间进行辅助判断):

1.通过文件的修改日期

dateaccessed:<date>    #搜索在指定日期被访问的文件或文件夹
datecreated:<date>     #搜索在指定日期被创建的文件或文件夹
datemodified:<date>    #搜索在指定日期被修改的文件或文件夹
da:<date>           #dateaccessed:<date>的简写
dc:<date>           #datecreated:<date>的简写
dm:<date>           #datemodified:<date>的简写

其中的date的语法格式和一些常量如下:

date格式:

year
month/year or year/month depending on locale settings
day/month/year, month/day/year or year/month/day depending on locale settings

一些date的常量:

today
yesterday
tomorrow<last|past|prev|current|this|coming|next><year|month|week><last|past|prev|coming|next><x><years|months|weeks|hours|minutes|mins|seconds|secs>
january|february|march|april|may|june|july|august|september|october|november|december
jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec
sunday|monday|tuesday|wednesday|thursday|friday|saturday
sun|mon|tue|wed|thu|fri|sat
unknown

实际使用举例如下:

C:\Users\userName\Desktop datecreated:yesterday    #查找桌面上昨天修改过的文件or文件夹
C:\Users\userName\Desktop datemodified:today    #查找桌面上今天修改过的文件or文件夹
C:\Users\userName\Desktop datemodified:2014/6/15    #查找桌面上在2014/6/15这天修改过的文件or文件夹{2014/6/15这个格式和你本地的设定有关,你可以打开Everything看"Date Modified"那一列的显示格式}
C:\Users\userName\Desktop datemodified:lastweek    #查找桌面上上个星期修改过的文件or文件夹
C:\Users\userName\Desktop datemodified:january    #查找桌面上在一月份修改过的文件or文件夹

提示:最好指定一个路径来进行搜索(例如在进行webshell检测时,指定网站目录),否则速度很慢{当然了,这与个人电脑配置有关,在我的瓜机上面很慢就是了}

2.通过文件大小

使用语法介绍:

size:<size>Search for files with the specified size in bytes.
Size Syntax:
size[kb|mb|gb]
Size Constants:
empty
tiny0 KB < size <= 10 KB
small10 KB < size <= 100 KB
medium100 KB < size <= 1 MB
large1 MB < size <= 16 MB
huge16 MB < size <= 128 MB
giganticsize > 128 MB
unknown

实际举例如下:

一般的webshell文件也不大{当然了,区分大小马},所以可以试试查找大于0KB小于10KB的文件( size:tiny

查找文件大小小于50KB的文件方法( size:<=50kb

当然也可以通过查找PHP文件,然后再按文件大小排序的方式来进行。

3.简洁实用的手动方式

C:\Apache\htdocs *.php#显示了结果之后,再按照文件修改时间/大小/文件名排序,快速而且直接
C:\Apache\htdocs *.php | *.jpg

一般是通过指定搜索路径的方式来加快速度,可以通过多种方式的结合来达到自己的目的,这个需要根据自己的情况来定,这里就不细说了。

上面提供的只是一种思路,在Windows上也可以通过批处理脚本或PHP/Python脚本编写功能更强大的webshell查找工具(之前在Freebuf上也有过几篇很好的文章,比如: http://www.freebuf.com/tools/8341.html),不过就效率,速度和直观性而言,Everything这款工具确实还是非常值得推荐的!Everything还有很多的功能值得我们去发现、去挖掘,多组合、多尝试就可以找到适合自己的方法,祝好运!

编程高手也可以自行编写脚本调用Everything的命令行来进行周期性的扫描、报告,如果写好了能给大家分享一下那就更好了(☆_☆)/~~

————————–

在Linux上因为原生集成了很多命令行工具,速度也是非常快,所以也不用其他多余的工具了,写个shell脚本,然后放在crontab中周期性运行并把结果发送给自己,效果还是很不错的。

—–下面是从网上搜集的一些使用find/xargs/grep的命令组合查找webshell的方法—–

查找"/path/to/webroot"目录里面在10天内进行过修改的php文件(可根据需要进行微调):

find /path/to/webroot -name "*.php" -mtime -10

如果文件更新时间不确定,我们可以通过查找关键字的方法来确定。要想查的准确需要熟悉webshell常用的关键字,我这里列出一些常用的,其他的大家可以从网收集一些webshell,总结自己的关键字,括号里面我总结的一些关键字(eval,shell_exec,passthru,popen,system)查找方法如下:

find /path/to/webroot -name "*.php" |xargs grep "eval" |less
find /path/to/webroot -name "*.php" |xargs grep "shell_exec" |less
find /path/to/webroot -name "*.php" |xargs grep "passthru" |less

当然你还可以导出到文件,下载下来慢慢分析:

find /home -name "*.php"|xargs grep "fsockopen"|tee webshell_scan.log

这里我就不一一罗列了,如果有自己总结的关键字直接替换就可以。当然并不是所有的找出的文件都是webshell,这个需要自己做一下判断,判断的方法也简单,直接从浏览器访问一下这个文件或者和自己找的一些webshell比较一下,看得多了,基本上一眼就可以判断是不是webshell文件。

因为Linux上的这个的查找方法在各种网站上都有类似的内容,没法找到原文出处,如有请告知,谢谢。

最后来点猛料:

在查找webshell的时候,集中注意力是非常必要的,可如果你昨天看了些小电影而导致精力不足无法集中精神排查的话可是非常要命的,所以,你需要:

打开Everything,在搜索框中输入: *.rm | *.rmvb |*.avi | *.wmv | *.mkv | *.mpeg | *.3gp,按大小排序,选择非系统视频文件,深呼吸,闭眼,手不要抖,按住Shift+Del键(是的,shift+del,剥夺它们进回收站的权利),睁眼,露出灿烂的笑容,生活原来如此美好~~

作者 _zero

Spring MVC 入门实例

$
0
0

概述:

springmvc 框架围绕DispatcherServlet这个核心展开,DispatcherServlet是Spring MVC的总控制,它负责截获请求并将其分派给相应的处理器处理。SpringMVC框架包括注解驱动控制器、请求及响应的信息处理、视图解析、本地化解析、上传文件解析、异常处理以及表单标签绑定等内容。
SpringMVC是主要基于MODEL2实现的技术框架,Model2是经典的MVC(model、view、control)模型在web应用中的变体,这个改变主要源于HTTP协议的无状态性。Model2的目的和MVC一样,也是利用处理器分离模型、视图和控制,达到不同技术层级间松散层耦合的效果,提高系统的灵活性,复用性和可维护性。在多数情况下可以将Model2与MVC等同起来。SpringMVC结构如下如图所示。


从接受请求道返回响应,Spring MVC框架的众多组建通力配合、各司其职,有条不紊的完成分内的工作。在整个框架中,DispatcherServlet处于核心位置,它负责协调和
组织不同组件完成请求并返回相应的的工作。和大多数MVC框架一样Spring MVC通过一个前端servlet接收所有的请求,并将具体工作委托给其他组件进行处理,DispatcherServlet就是SpringMVC的前端Servlet。下面对整个过程做一下介绍。

1)整个过程始于客户端发出的一个HTTP请求,Web应用服务器接收到这个请求,如果匹配DispatcherServlet的请求映射路径(web.xml指定),Web容器将该请求转交给DispatcherServlet处理
2)DispatcherServlet接收到这个请求后,将根据请求的信息(包括URL、HTTP方法、请求报文头、请求参数、Cookie等)及HandlerMapping的配置找到处理请求的处理器Handler。可将HandlerMapping看成路由控制器,将Handler看成目标主机。值得注意的是SpringMVC并没有定义一个Handler接口,实际上任何一个Object都可以成为请求处理器。
3)当DispatcherServlet根据HandlerMapping得到对应当前请求的Handler后,通过HandlerAdapter对Handler进行封装,再以统一的适配器借口调用Handler。HandlerAdapter是SpringMVC的架构级接口,顾名思义,HandlerAdapter是一个适配器,它用统一的接口对各种Handler方法进行调用。
4)处理器完成业务逻辑的处理后将返回一个ModelAndView给DispatcherServlet,ModelAndView包含了视图逻辑名和模型数据信息。
5)ModelAndView中包含的是“逻辑视图名”而非真正的视图对象,DispatcherServlet借由ViewResolver完成逻辑视图名到真实视图对象的解析工作。
6)当得到真实的视图对象View后,DispatcherServlet就使用这个View对象对ModelAndView中的模型数据进行视图渲染。
7)最终客户端得到的响应信息,可能是一个普通的HTML页面,也可能是一个XML活JSON串,甚至是一张图片或一个PDF文档等不同的媒体形式。

下面是一个用户登录的实例。

登录的实例

1环境准备

在进入实际开发之前需要做一些环境准备工作,其中包括数据库表的建立。工程创建,数据库服务器的配置等。这里使用的tomcat7.0 mysql5.0,Eclipse IDE
先建立数据库脚本sampledb.sql
DROP DATABASE IF EXISTS sampledb;
CREATE DATABASE sampledb DEFAULT CHARACTER SET utf8;
USE sampledb;

##创建用户表
CREATE TABLE t_user (
   user_id   INT AUTO_INCREMENT PRIMARY KEY,
   user_name VARCHAR(30),
   credits INT,
   password  VARCHAR(32),
   last_visit datetime,
   last_ip  VARCHAR(23)
)ENGINE=InnoDB; 

##创建用户登录日志表
CREATE TABLE t_login_log (
   login_log_id  INT AUTO_INCREMENT PRIMARY KEY,
   user_id   INT,
   ip  VARCHAR(23),
   login_datetime datetime
)ENGINE=InnoDB; 

##插入初始化数据
INSERT INTO t_user (user_name,password) 
             VALUES('admin','123456');
COMMIT;

用到的jar文件(Spring3.0以上的jar基本都可以通过,不一定是如下的jar包):


2持久层

持久层负责数据的访问操作,DAO类被上层的业务类调用,Spring本身支持多种流行的ORM映射框架。这里使用SpringJDBC来作为持久层实现技术。
第一步建立domain Object也被称为实体类。
User.java
package com.baobaotao.domain;
import java.io.Serializable;
import java.util.Date;
public class User implements Serializable{
	private int userId;
	private String userName;
	private String password;
	private int credits;
	private String lastIp;
	private Date lastVisit;
	public String getLastIp() {
		return lastIp;
	}
	public void setLastIp(String lastIp) {
		this.lastIp = lastIp;
	}
	public Date getLastVisit() {
		return lastVisit;
	}
	public void setLastVisit(Date lastVisit) {
		this.lastVisit = lastVisit;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	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;
	}
	public int getCredits() {
		return credits;
	}
	public void setCredits(int credits) {
		this.credits = credits;
	}
}
LoginLog.java
package com.baobaotao.domain;
import java.io.Serializable;
import java.util.Date;
public class LoginLog implements Serializable{
	private int loginLogId;
	private int userId;
	private String ip;
	private Date loginDate;
	public String getIp() {
		return ip;
	}
	public void setIp(String ip) {
		this.ip = ip;
	}
	public Date getLoginDate() {
		return loginDate;
	}
	public void setLoginDate(Date loginDate) {
		this.loginDate = loginDate;
	}
	public int getLoginLogId() {
		return loginLogId;
	}
	public void setLoginLogId(int loginLogId) {
		this.loginLogId = loginLogId;
	}
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
}

通过SpringJDBC实现DAO类。UserDao.java
package com.baobaotao.dao;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.stereotype.Repository;
import com.baobaotao.domain.User;
@Repository
public class UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	public int getMatchCount(String userName, String password) {
		String sqlStr = " SELECT count(*) FROM t_user "
				+ " WHERE user_name =? and password=? ";
		return jdbcTemplate.queryForInt(sqlStr, new Object[] { userName, password });
	}
	public User findUserByUserName(final String userName) {
		String sqlStr = " SELECT user_id,user_name,credits "
				+ " FROM t_user WHERE user_name =? ";
		final User user = new User();
		jdbcTemplate.query(sqlStr, new Object[] { userName },
				new RowCallbackHandler() {
					@Override
					public void processRow(ResultSet rs) throws SQLException {
						user.setUserId(rs.getInt("user_id"));
						user.setUserName(userName);
						user.setCredits(rs.getInt("credits"));
					}
				});
		return user;
	}
	public void updateLoginInfo(User user) {
		String sqlStr = " UPDATE t_user SET last_visit=?,last_ip=?,credits=? "
				+ " WHERE user_id =?";
		jdbcTemplate.update(sqlStr, new Object[] { user.getLastVisit(),
				user.getLastIp(),user.getCredits(),user.getUserId()});
	}
}

LoginLogDao.java
package com.baobaotao.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.baobaotao.domain.LoginLog;
@Repository
public class LoginLogDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	public void insertLoginLog(LoginLog loginLog) {
		String sqlStr = "INSERT INTO t_login_log(user_id,ip,login_datetime) "
				+ "VALUES(?,?,?)";
		Object[] args = { loginLog.getUserId(), loginLog.getIp(),
				          loginLog.getLoginDate() };
		jdbcTemplate.update(sqlStr, args);
	}
}

然后在Spring中装配DAO。这部分和Service的装配在同一份配置文件中将在后面列出。

3业务层

业务层仅有一个业务类,即UserService,UserService负责将持久化层的UserDao和LogLoginDao组织起来完成用户名/密码认证、登录日志记录等操作。
UserSerice.java
package com.baobaotao.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.baobaotao.dao.LoginLogDao;
import com.baobaotao.dao.UserDao;
import com.baobaotao.domain.LoginLog;
import com.baobaotao.domain.User;
@Service
public class UserService {
	@Autowired
	private UserDao userDao;
	@Autowired
	private LoginLogDao loginLogDao;
	public boolean hasMatchUser(String userName, String password) {
		int matchCount =userDao.getMatchCount(userName, password);
		return matchCount > 0;
	}
	public User findUserByUserName(String userName) {
		return userDao.findUserByUserName(userName);
	}
	public void loginSuccess(User user) {
		user.setCredits( 5 + user.getCredits());
		LoginLog loginLog = new LoginLog();
		loginLog.setUserId(user.getUserId());
		loginLog.setIp(user.getLastIp());
		loginLog.setLoginDate(user.getLastVisit());
        userDao.updateLoginInfo(user);
        loginLogDao.insertLoginLog(loginLog);
	}	
}

然后在Spring中装配Service。即在applicationContext.xml文件,进行如下的更改。这里同时列出了对Dao的装配。
<?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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	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-3.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/tx 
       http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"><!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 --><context:component-scan base-package="com.baobaotao.dao"/><context:component-scan base-package="com.baobaotao.service"/><!-- 配置数据源 --><bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close" 
		p:driverClassName="com.mysql.jdbc.Driver"
		p:url="jdbc:mysql://localhost:3306/sampledb" 
		p:username="root"
		p:password="526156" /><!-- 配置Jdbc模板  --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
		p:dataSource-ref="dataSource" /><!-- 配置事务管理器 --><bean id="transactionManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		p:dataSource-ref="dataSource" /><!-- 通过AOP配置提供事务增强,让service包下所有Bean的所有方法拥有事务 --><aop:config proxy-target-class="true"><aop:pointcut id="serviceMethod"
			expression=" execution(* com.baobaotao.service..*(..))" /><aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice" /></aop:config><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="*" /></tx:attributes></tx:advice></beans>

4展现层

业务层和持久层的开发任务已经完成,Spring2.5新增了基于注解的MVC,Spring3.0开始提供了REST风格的MVC,Springmvc在spring3.0时变的很轻便、强大、易用。
业务层和持久层开发完成,就是为程序提供界面的时候了(实际开发界面也有先开发的或协同开发)。首先对web.xml进行配置,以便Web容器启动时能够自动启动Spring容器
web.xml
<?xml version="1.0" encoding="UTF-8"?><web-app version="2.5" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:applicationContext.xml</param-value></context-param><listener><listener-class>
			org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>baobaotao</servlet-name><servlet-class>
			org.springframework.web.servlet.DispatcherServlet</servlet-class><load-on-startup>3</load-on-startup></servlet><servlet-mapping><servlet-name>baobaotao</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping><welcome-file-list><welcome-file>index.jsp</welcome-file></welcome-file-list></web-app>

上述配置文件中的servlet-name为baobaotao,则在/WEB-INF下必须提供一个以baobaotao-servlet.xml的配置文件,这是spring mvc框架的契约。这个配置文件无需通过web.xml的contextConfigLocation上下文参数进行声明,因为Spring MVC的Servlet会自动将baobaotao-servlet.xml和Spring其它的配置文件进行拼装。

接下来就是POJO控制器类的编写。
package com.baobaotao.web;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import com.baobaotao.domain.User;
import com.baobaotao.service.UserService;
@Controller
public class LoginController{
	@Autowired
	private UserService userService;
	@RequestMapping(value = "/index.html")
	public String loginPage(){
		return "login";
	}
	@RequestMapping(value = "/loginCheck.html")
	public ModelAndView loginCheck(HttpServletRequest request,LoginCommand loginCommand){
		boolean isValidUser = 
			   userService.hasMatchUser(loginCommand.getUserName(),
					                    loginCommand.getPassword());
		if (!isValidUser) {
			return new ModelAndView("login", "error", "用户名或密码错误。");
		} else {
			User user = userService.findUserByUserName(loginCommand
					.getUserName());
			user.setLastIp(request.getLocalAddr());
			user.setLastVisit(new Date());
			userService.loginSuccess(user);
			request.getSession().setAttribute("user", user);
			return new ModelAndView("main");
		}
	}
}

这里利用了一个默认契约绑定的类LoginCommand.java
package com.baobaotao.web;
public class LoginCommand {
	private String userName;
	private String password;
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
}

ModelAndView的解析配置
SpringMVC将视图逻辑名解析为具体的视图页面,需要在baobaotao-servlet.xml中提供一个定义解析规则的Bean。SpringMVC为视图名到具体视图的映射提供了很多可供选择方法。这里使用InternalResourceViewResolver,它通过为视图逻辑名添加前后缀的方式进行解析。
baobaotao-servlet.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:p="http://www.springframework.org/schema/p"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context 
       http://www.springframework.org/schema/context/spring-context-3.0.xsd"><!-- 扫描web包,应用Spring的注解 --><context:component-scan base-package="com.baobaotao.web"/><!-- 配置视图解析器,将ModelAndView及字符串解析为具体的页面 --><bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver"
		p:viewClass="org.springframework.web.servlet.view.JstlView" 
		p:prefix="/WEB-INF/jsp/"
		p:suffix=".jsp" /></beans>

JSP视图页面:
login.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%><html><head><title>宝宝淘论坛登录</title></head><body><c:if test="${!empty error}"><font color="red"><c:out value="${error}" /></font></c:if>        <form action="<c:url value="loginCheck.html"/>" method="post">用户名:
			<input type="text" name="userName"><br>密 码:
			<input type="password" name="password"><br><input type="submit" value="登录" /><input type="reset" value="重置" /></form></body></html>
main.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%><%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"><title>宝宝淘论坛</title></head><body>
    ${user.userName},欢迎您进入宝宝淘论坛,您当前积分为${user.credits};
</body></html>

最后部署运行即可。


参考书目:1. http://www.mkyong.com/tutorials/spring-mvc-tutorials/
    2. Spring3.X应用开发实战

作者:LGCSSX 发表于2014-6-25 23:10:55 原文链接
阅读:0 评论:0 查看评论
Viewing all 15843 articles
Browse latest View live


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