月度归档:2016年11月

zkclient和curator开源

开源的两个zookeeper的客户端,简化了程序员的开发工具。

开源客户端,原生api的不足

连接的创建是异步的,需要开发人员自行编码实现等待
连接没有自动的超时重连机制
Zk本身没提供序列化机制,需要开发人员自行指定,从而实现数据的序列化和反序列化
Watcher注册一次只会生效一次,需要不断的重复注册
Watcher的使用方式不符合java本身的术语,如果采用监听器方式,更容易理解
不支持递归创建树形节点

开源客户端—ZkClient介绍

Github上一个开源的zk客户端,由datameer的工程师Stefan Groschupf和Peter Voss一起开发
– 解决session会话超时重连
– Watcher反复注册
– 简化开发api
– 还有…..
– https://github.com/sgroschupf/zkclient

开源客户端—Curator介绍
1. 使用CuratorFrameworkFactory工厂的两个静态方法创建客户端
a) static CuratorFramework newClient(String connectString, int sessionTimeoutMs, int connectionTimeoutMs,
RetryPolicy retryPolicy)
b) static CuratorFramework newClient(String connectString, RetryPolicy retryPolicy)
2. Start()方法启动
参数说明
connectString 分开的ip:port对
retryPolicy 重试策略,默认四种:Exponential BackoffRetry,RetryNTimes ,RetryOneTime,
RetryUntilElapsed
sessionTimeoutMs 会话超时时间,单位为毫秒,默认60000ms
connectionTimeoutMs 连接创建超时时间,单位为毫秒,默认是15000ms

CAP定理

2000年7月加州大学伯克利分校Eric Brewer教授首次提出了CAP猜想,并成为分布式计算领域的公认定理。该定理认为一个分布式系统不可能同时满足一致性(C:Consistency),可用性(A:Availability)和分区容错性(P:Partition Tolerance),这三个基本需求,最多只能满足其中两项。

五种一致性

推荐书籍:从PAXOS到ZOOKEEPER分布式一致性原理与实践,
链接:http://pan.baidu.com/s/1bpabUxh 密码:1v91

zookeeper的简单例子

import org.apache.zookeeper.*;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.data.Stat;

import java.util.concurrent.CountDownLatch;

class DataMonitor implements Watcher, AsyncCallback.StatCallback{
    public ZooKeeper zk = null;
    private int i = 0;
    public int iexistcheck = 0;
    public DataMonitor(){

    }

    public void process(WatchedEvent event) {
        String path = event.getPath();
        System.out.println("process: iexistcheck="+iexistcheck+" - "+"i:="+i+",path:"+path+" - state:" + event.getState());
        i++;
    }

    public void processResult(int rc, String path, Object ctx, Stat stat) {
        System.out.println("processResult.1:iexistcheck="+iexistcheck+" - "+"i:="+i+",rc:" + rc + " - path:"+path+" - state:" + stat);
        try {
            System.out.println("processResult.2.getData:iexistcheck="+iexistcheck+" - "+"i:="+i+" - "+new String(zk.getData("/test", false, null)));
        }catch (Exception e){
            System.out.print("processResult.3:exception.....iexistcheck="+iexistcheck+" - "+"i:="+i);
        }finally {
            System.out.println("processResult.4:iexistcheck="+iexistcheck+" - "+"i:="+i+",end......end......end..");
        }
    }
}

public class ZooKeeperTest {

    private static CountDownLatch downLatch = new CountDownLatch(1);
    private static final int TIME_OUT = 300000;
    private static final String HOST = "localhost:2181";
    public static void main(String[] args) throws Exception{
        DataMonitor dm = new DataMonitor();
        ZooKeeper zookeeper = new ZooKeeper(HOST, TIME_OUT, dm);
        dm.zk = zookeeper;
        zookeeper.exists("/test", true, dm, dm);
        Stat stat =  null;
        for(int i = 0; i < 30; i++){
            String buf = "value="+i;
            System.out.println("loop i="+i);
            if(zookeeper.exists("/test", false) == null)
            {
                //PERSISTENT_SEQUENTIAL模式情况下,真正路径是从返回值获取。其格式为/test0000000028。
                //EPHEMERAL模式下,该节点是临时节点,当超时或session断开时,它也跟随着消失。适合检验机器岩机与否等状态。防单点或执备切换等。
                String node_real = zookeeper.create("/test", "znode1".getBytes(), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
                System.out.println("nodename:"+node_real);
            }
            //第三个参数为版本号,当前输入的版本号与目标版本的版本号是相同的情况,才能更新到数据。否则会更新失败。
            if(stat == null){
                stat = zookeeper.setData(node_real, buf.getBytes() , -1);
            }else{
                stat = zookeeper.setData(node_real, buf.getBytes() , stat.getVersion());
            }
            System.out.println("Czxid:" + stat.getCzxid() + " - Mzxid:" + stat.getMzxid() + " - Version:" + stat.getVersion());
            if(i > 5){
                if(i % 3 == 0){
                    dm.iexistcheck = i;
                    zookeeper.exists("/test", true, dm, dm);
                }
            }
            Thread.sleep(1000);
        }
        zookeeper.delete("/test", -1);
        Thread.sleep(5000);
        zookeeper.close();
    }
}

			org.apache.zookeeper
        		zookeeper
        		3.4.8
        		
            			
                			com.sun.jmx
                			jmxri
            			
            			
					com.sun.jdmk
                			jmxtools
            			
            			
                			javax.jms
                			jms
            			
        		
        	
loop i=0
process: iexistcheck=0 - i:=0,path:null - state:SyncConnected
processResult.1:iexistcheck=0 - i:=1,rc:-101 - path:/test - state:null
processResult.3:exception.....iexistcheck=0 - i:=1processResult.4:iexistcheck=0 - i:=1,end......end......end..
process: iexistcheck=0 - i:=1,path:/test - state:SyncConnected
Czxid:1055 - Mzxid:1056 - Version:1
loop i=1
Czxid:1055 - Mzxid:1057 - Version:2
loop i=2
Czxid:1055 - Mzxid:1058 - Version:3
loop i=3
Czxid:1055 - Mzxid:1059 - Version:4
loop i=4
Czxid:1055 - Mzxid:1060 - Version:5
loop i=5
Czxid:1055 - Mzxid:1061 - Version:6
loop i=6
Czxid:1055 - Mzxid:1062 - Version:7
processResult.1:iexistcheck=6 - i:=1,rc:0 - path:/test0000000029 - state:1055,1062,1480343483194,1480343489294,7,0,0,0,7,0,1055

processResult.2.getData:iexistcheck=6 - i:=1 - value=6
processResult.4:iexistcheck=6 - i:=1,end......end......end..
loop i=7
process: iexistcheck=6 - i:=1,path:/test0000000029 - state:SyncConnected
Czxid:1055 - Mzxid:1063 - Version:8
loop i=8
Czxid:1055 - Mzxid:1064 - Version:9
loop i=9
Czxid:1055 - Mzxid:1065 - Version:10
processResult.1:iexistcheck=9 - i:=2,rc:0 - path:/test0000000029 - state:1055,1065,1480343483194,1480343492344,10,0,0,0,7,0,1055

processResult.2.getData:iexistcheck=9 - i:=2 - value=9
processResult.4:iexistcheck=9 - i:=2,end......end......end..

Log4j比较全面的配置

附:Log4j比较全面的配置

Log4j配置文件实现了输出到控制台、文件、回滚文件、发送日志邮件、输出到数据库日志表、自定义标签等全套功能。
log4j.rootLogger=DEBUG,console,dailyFile,im
log4j.additivity.org.apache=true
# 控制台(console)
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.ImmediateFlush=true
log4j.appender.console.Target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

# 日志文件(logFile)
log4j.appender.logFile=org.apache.log4j.FileAppender
log4j.appender.logFile.Threshold=DEBUG
log4j.appender.logFile.ImmediateFlush=true
log4j.appender.logFile.Append=true
log4j.appender.logFile.File=D:/logs/log.log4j
log4j.appender.logFile.layout=org.apache.log4j.PatternLayout
log4j.appender.logFile.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# 回滚文件(rollingFile)
log4j.appender.rollingFile=org.apache.log4j.RollingFileAppender
log4j.appender.rollingFile.Threshold=DEBUG
log4j.appender.rollingFile.ImmediateFlush=true
log4j.appender.rollingFile.Append=true
log4j.appender.rollingFile.File=D:/logs/log.log4j
log4j.appender.rollingFile.MaxFileSize=200KB
log4j.appender.rollingFile.MaxBackupIndex=50
log4j.appender.rollingFile.layout=org.apache.log4j.PatternLayout
log4j.appender.rollingFile.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# 定期回滚日志文件(dailyFile)
log4j.appender.dailyFile=org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyFile.Threshold=DEBUG
log4j.appender.dailyFile.ImmediateFlush=true
log4j.appender.dailyFile.Append=true
log4j.appender.dailyFile.File=D:/logs/log.log4j
log4j.appender.dailyFile.DatePattern='.'yyyy-MM-dd
log4j.appender.dailyFile.layout=org.apache.log4j.PatternLayout
log4j.appender.dailyFile.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# 应用于socket
log4j.appender.socket=org.apache.log4j.RollingFileAppender
log4j.appender.socket.RemoteHost=localhost
log4j.appender.socket.Port=5001
log4j.appender.socket.LocationInfo=true
# Set up for Log Factor 5
log4j.appender.socket.layout=org.apache.log4j.PatternLayout
log4j.appender.socket.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# Log Factor 5 Appender
log4j.appender.LF5_APPENDER=org.apache.log4j.lf5.LF5Appender
log4j.appender.LF5_APPENDER.MaxNumberOfRecords=2000
# 发送日志到指定邮件
log4j.appender.mail=org.apache.log4j.net.SMTPAppender
log4j.appender.mail.Threshold=FATAL
log4j.appender.mail.BufferSize=10
log4j.appender.mail.From = xxx@mail.com
log4j.appender.mail.SMTPHost=mail.com
log4j.appender.mail.Subject=Log4J Message
log4j.appender.mail.To= xxx@mail.com
log4j.appender.mail.layout=org.apache.log4j.PatternLayout
log4j.appender.mail.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n
# 应用于数据库
log4j.appender.database=org.apache.log4j.jdbc.JDBCAppender
log4j.appender.database.URL=jdbc:mysql://localhost:3306/test
log4j.appender.database.driver=com.mysql.jdbc.Driver
log4j.appender.database.user=root
log4j.appender.database.password=
log4j.appender.database.sql=INSERT INTO LOG4J (Message) VALUES('=[%-5p] %d(%r) --> [%t] %l: %m %x %n')
log4j.appender.database.layout=org.apache.log4j.PatternLayout
log4j.appender.database.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

# 自定义Appender
log4j.appender.im = net.cybercorlin.util.logger.appender.IMAppender
log4j.appender.im.host = mail.cybercorlin.net
log4j.appender.im.username = username
log4j.appender.im.password = password
log4j.appender.im.recipient = corlin@cybercorlin.net
log4j.appender.im.layout=org.apache.log4j.PatternLayout
log4j.appender.im.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n

xml配置文件读取

        String filePath = Thread.currentThread().getContextClassLoader().getResource("MemCachedConfig.xml").getPath().substring(1);
        File file = new File(filePath.replaceAll("%20"," "));
        try{
            if(file.exists()){ //如果可以成功加载配置文件
                SAXReader sr = new SAXReader();
                Document doc = sr.read(file);
                Element Root = doc.getRootElement(); //获得根节点
                Element Enabled = (Element)Root.selectSingleNode("Enabled"); //获得是否启用memcached节点
                Element Servers = (Element)Root.selectSingleNode("Servers"); //获得可用的服务器列表父节点
                Element Config = (Element)Root.selectSingleNode("Config"); //获得运行环境参数列表父节点                
            }
        }catch(Exception e){
            System.out.println(e.toString());
        }

   dom4j
   dom4j
   1.6

转:Spring MVC getServletConfigClasses和getRootConfigClasses

在不使用Web.XML中配置搭建spring MVC框架时,一般需要创建一个类来继承AbstractAnnotationConfigDispatcherServletInitializer,如下代码所示:

public class SpittrWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[] { RootConfig.class };
    }
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[] { WebConfig.class };
    }
    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

其中有两个方法,getRootConfigClasses()getServletConfigClasses()一直让我很疑惑,两个方法的说明分别是:

  • getRootConfigClasses: Specify @Configuration and/or @Component classes to be provided to the root application context.(将有@Component注解或者@Component注解的配置类提供给根应用上下文)
  • getServletConfigClasses:Specify @Configuration and/or @Component classes to be provided to the dispatcher servlet application context.(将有@Component注解或者@Component注解的配置类提供给根应用上下文)

两者之间有什么区别呢?后来在Spring的官方文档中找到答案。在Spring的官方文档中有这样一段话提到:

ApplicationContext instances in Spring can be scoped. In the Web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. The root WebApplicationContext should contain all the infrastructure beans that should be shared between your other contexts and Servlet instances. These inherited beans can be overridden in the servlet-specific scope, and you can define new scope-specific beans local to a given Servlet instance.

文档连接
粗略翻译一下:

在Spring中,应用上下文(ApplicationContext)的实例是可以范围化(Scoped)的,在Web MVC框架中,每个DispatcherServlet都可以有自己的Web应用上下文(WebApplicationContext),每个DispatcherServlet自己的WebApplicationContext继承所有的在根应用上下文中的bean。根应用上下文应该包含在其他DispatcherServlet中会用到的bean,比如DataSource等,这些在根应用上下文中的bean可以在DispatcherServlet自己的WebApplicationContext中被重载。

在Spring MVC中我们其实是可以创建出多个DispatcherServlet的(只要创建多个继承自AbstractAnnotationConfigDispatcherServletInitializer的类即可)。而每个DispatcherServlet有自己的应用上下文(WebApplicationContext),这个应用上下文只针对这个DispatcherServlet有用。这也就是getServletConfigClasses的作用,获取这个DispatcherServlet的应用上下文的配置类。

而除了每个DispatcherServlet配置类的应用上下文之外,还有一个根应用上下文,这个应用上下文的作用是为了在多个DispatcherServlet之间共享Bean,比如数据源Bean,这就是getRootConfigClasses的作用,用于返回根应用上下文的配置类。Spring框架的机制会保证如果在当前DispatcherServlet的应用上下文中没有找到想要的bean时,会去根应用上下文中去找。

上两张官方文档中的图:这两张图片也阐述了两种用法。

图片1

这是其中的一种用法,当应用中有DispatcherServlet时,每个DispatcherServlet的bean如Controller,ViewResolver,HandlerMapping等,均在getServletConfigClasses返回的类中配置而一些公共的bean,如Services,Repository均在getRootConfigClasses返回的类中配置

图片2

这是另外一种用法,但应用中只有一个DispatcherServlet中时,可以将所有的bean配置均写在根应用上下文中。DispatcherServlet获取想要的bean时,如果没有在自己的应用上下文中找到,则会自动到根应用上下文中去找。

这也就是Spring MVC的ApplicationContext继承机制。

java相关库

http://commons.apache.org/
commons库,包括了各种常用的库如:io,lang,codex等。
——————————————–
Apache Commons IO 包绝对是好东西,地址在http://commons.apache.org/proper/commons-io/,下面用例子分别介绍:
1) 工具类
2) 输入
3) 输出
4) filters过滤
5) Comparators
6) 文件监控

http://hc.apache.org/
HttpComponents库,主要包括了HttpClient的组件,包括异步和同步方式。

——————–
smart-framework,有助于了解一个框架的实现原理。
http://git.oschina.net/huangyong/smart-framework
http://git.oschina.net/huangyong/smart-sample

Servlet创建的三种方式

源代码路径:https://git.oschina.net/kxtry/servletwaycreate
以下是基于servlet3.0API而创建的。
0.pom.xml的准备


        
            javax.servlet
            javax.servlet-api
            3.0.1
        
    

1.ServletContainerInitializer的方式。

在resources/META-INF/services/javax.servlet.ServletContainerInitializer文件中增加以下内容。
HandlerInitializer,如果是有包名,则需要添加上包名如hello.HandlerInitializer
public class ServletManualReg extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().print("ServletManualReg.hello");
    }
}
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.annotation.HandlesTypes;
import java.util.Set;

@HandlesTypes({HandlerInitializer.class})
public class HandlerInitializer implements ServletContainerInitializer {
    public void onStartup(Set> c, ServletContext servletContext)
            throws ServletException {
        System.out.println("onStartup");
        ServletRegistration registration = servletContext.addServlet("ServletManualReg", "hello.ServletManualReg");
        registration.addMapping("/manual_reg");
        System.out.println("leaving onStartup");
    }
}

2.使用ServletContextListener注册

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class DynRegServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().print("DynRegServlet.hello");
    }
}
import javax.servlet.*;
import javax.servlet.annotation.WebListener;

@WebListener
public class DynRegListener implements ServletContextListener {

    public void contextDestroyed(ServletContextEvent arg0) {
    }

    public void contextInitialized(ServletContextEvent sce) {
        ServletContext servletContext = sce.getServletContext() ;
        Servlet dynRegServlet = null ;
        try {
            dynRegServlet = servletContext.createServlet(DynRegServlet.class) ;
        } catch (ServletException e) {
            e.printStackTrace();
        }
        ServletRegistration.Dynamic dynamic = servletContext.addServlet("DynRegServlet", dynRegServlet) ;
        dynamic.addMapping("/dynamic_reg") ;
    }
}

3.使用WebServlet注解注册


import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 使用@WebServlet实现了,自动注册。
 */
@WebServlet(name = "ServletNotationReg", urlPatterns = "/notation_reg")
public class ServletNotationReg extends HttpServlet {
    public ServletNotationReg(){
        int i = 0;
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().print("ServletNotationReg.hello");
    }
}

BeanNameUrlHandlerMapping遍历Controller注解过程

1.关键运行栈细节

2.关注AbstractDetectingUrlHandlerMapping类的detectHandlers函数。

protected void detectHandlers() throws BeansException {
        if(this.logger.isDebugEnabled()) {
            this.logger.debug("Looking for URL mappings in application context: " + this.getApplicationContext());
        }
        /*获取所有bean的名字,然后遍历测试*/
        String[] beanNames = this.detectHandlersInAncestorContexts?BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.getApplicationContext(), Object.class):this.getApplicationContext().getBeanNamesForType(Object.class);
        String[] var2 = beanNames;
        int var3 = beanNames.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String beanName = var2[var4];
            /*将检验beanName的名字,当名字是以反斜扛开头的,均当作是一个URL处理器*/
            String[] urls = this.determineUrlsForHandler(beanName);
            if(!ObjectUtils.isEmpty(urls)) {
                this.registerHandler(urls, beanName);
            } else if(this.logger.isDebugEnabled()) {
                this.logger.debug("Rejected bean name \'" + beanName + "\': no URL paths identified");
            }
        }
    }

3.上图局部变量截图

4.附上determineUrlsForHandler的相关代码。

protected String[] determineUrlsForHandler(String beanName) {
        ArrayList urls = new ArrayList();
        if(beanName.startsWith("/")) {
            urls.add(beanName);
        }

        String[] aliases = this.getApplicationContext().getAliases(beanName);
        String[] var4 = aliases;
        int var5 = aliases.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String alias = var4[var6];
            if(alias.startsWith("/")) {
                urls.add(alias);
            }
        }

        return StringUtils.toStringArray(urls);
    }

5.附相关代码

@Controller("/demo_servlet2")
public class Demo2Servlet extends HttpServlet {	
	@PostConstruct  //init-method="init"
	@Override
	public void init() throws ServletException {
		// TODO Auto-generated method stub
		logger.info("Demo2Servlet init start");		
		
		logger.info("Demo2Servlet init end");
	}

	@RequestMapping("/findall")
	public String handleList(Model model) {
		logger.info("demo02  handleList");
		return "demo";
	}

	@Override
	protected void service(HttpServletRequest req, HttpServletResponse response)
			throws ServletException, IOException {
		// TODO Auto-generated method stub

		logger.info("Demo2Servlet service start");

		response.setContentType(CONTENT_TYPE);
		response.setHeader("Pragma", "No-cache");
		response.setHeader("Cache-Control", "no-cache");
		response.setDateHeader("Expires", 0);
		String result = demoService.getById(1l).getName();
		//Integer.valueOf(result); //测试异常显示页面
		PrintWriter out = response.getWriter();
		out.println("");
		out.println("\n");
		out.println("" + result + "\n");
		out.println("\n");

		out.flush();
		out.close();

		logger.info("Demo2Servlet service end");
	}

	@PreDestroy //destroy-method="destroy"
	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		logger.info("Demo2Servlet destroy start");
		
		logger.info("Demo2Servlet destroy end");
	}
	
}