OneCoder


  • 首页

  • 归档

  • 标签

  • 关于

  • 搜索

自定义xsd文件及使用

发表于 2014-04-02 | 阅读次数

需要规范用户开发并行计算任务的配置文件的格式,自然考虑定义任务配置的xsd文件。对于xsd的介绍可以参考:
http://www.w3school.com.cn/schema/schema_example.asp
这里,OneCoder给出想要定义的XML文件的格式以及根据该格式定义出的xsd文件。供大家参考。其实整个定义过程还是很简单的。

定义后的xsd文件如下(省略冗余的配置项):

<?xml version="1.0" encoding= "UTF-8"?>
<xsd:schema xmlns= "http://www.coderli.com/shurnim" xmlns:xsd= "http://www.w3.org/2001/XMLSchema"
     targetNamespace="http://www.coderli.com/shurnim" elementFormDefault="qualified" >
     <xsd:simpleType name ="IDType">
           <xsd:restriction base ="xsd:string">
               <xsd:pattern value= "[\w,\-,_]{1,32}"></xsd:pattern >
           </xsd:restriction >
     </xsd:simpleType >
     <!-- 定义类型 -->
     <xsd:complexType name ="jobType">
           <xsd:sequence >
               <xsd:element name ="jobId" type="IDType">
                    <xsd:annotation >
                         <xsd:documentation ><![CDATA[任务ID,唯一区别一个并行计算任务,必须唯一 ]]></xsd:documentation >
                    </xsd:annotation >
               </xsd:element >
               <xsd:element name ="jobName" type= "xsd:string" minOccurs ="0">
                    <xsd:annotation >
                         <xsd:documentation ><![CDATA[任务名称,可选 ]]></xsd:documentation >
                    </xsd:annotation >
               </xsd:element >
             </xsd:sequence >
     </xsd:complexType >
     <!-- 定义类型 -->
     <xsd:complexType name ="jobBundleType">
           <xsd:sequence >
               <xsd:element name ="job" maxOccurs= "unbounded" type="jobType" >
               </xsd:element >
           </xsd:sequence >
           <xsd:attribute name ="bundleName" type= "IDType"></xsd:attribute >
     </xsd:complexType >


     <xsd:element name ="jobBundle" type="jobBundleType">
     </xsd:element >
</xsd:schema>

对应的xml文件即为:

<?xml version="1.0" encoding= "UTF-8"?>
<jobBundle bundleName= "sldfjlaksd" xmlns= "http://www.coderli.com/shurnim"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.coderli.com/shurnim pc-3.0.xsd">
     <job >
           <jobId >df-asd_asdf238-sdf_adf</ jobId>
           <distributorClassName ></distributorClassName >
     </job >
</jobBundle>

通过对比xml和xsd文件其实可以很容易的学会xsd的定义规则。定义元素,区分基本数据类型和"复杂"的可嵌套的数据类型。基本数据类型支持校验,校验又支持正则等等。这些基础的知识在w3cschool上都有。这里稍微绕一点的知识可能就是xml的命名空间了。这个网上介绍的文章也很多,可以参考:http://www.cnblogs.com/martin-chen/archive/2011/02/24/xml-studynote-namespace.html

xml引用xsd的方式有多种,上面的文章里也有介绍。引用本地xsd的时候是配置的路径,相对自己所在的文件夹开始的。

注意一点,就在eclipse里开发xsd的时候,我是一遍开发一遍在xml中进行测试的。这个时候每修改一处xsd,需要重新打开xml才能重新加载。这一个小问题,却让OneCoder吃了不少苦头。最后才“幡然醒悟”。

最后,附上利用xsd校验xml和解析xml头中引用的xsd文件的Java代码。既然定义了,就要合理的利用:

package com.coderli.schema;


import java.io.InputStream;
import java.net.URL;


import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;


import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.dom4j.util.XMLErrorHandler;
import org.xml.sax.SAXException;


/**
* @author lihzh
* @date 2014年4月2日 下午2:34:08
*/
public class JobConfigXMLValidateTest {


     public static void main(String args[]) throws SAXException {
           validateXMLByXSD();
     }


     /**
      *
      * @throws SAXException
      * @author lihzh
      * @date 2014年4月2日 下午4:25:39
      */
     public static void validateXMLByXSD() throws SAXException {
          String xmlFileName = "com/coderli/schema/shurnim.xml" ;
          String xsdFileName = "com/coderli/schema/shurnim.xsd" ;
           // 建立schema工厂
          SchemaFactory schemaFactory = SchemaFactory
                    . newInstance("http://www.w3.org/2001/XMLSchema");
           // 建立验证文档文件对象,利用此文件对象所封装的文件进行schema验证
          URL schemaFile = JobConfigXMLValidateTest. class.getClassLoader()
                   .getResource(xsdFileName);
           // 利用schema工厂,接收验证文档文件对象生成Schema对象
          Schema schema = schemaFactory.newSchema(schemaFile);
           // 通过Schema产生针对于此Schema的验证器,利用schenaFile进行验证
          Validator validator = schema.newValidator();
           // 创建默认的XML错误处理器
          XMLErrorHandler errorHandler = new XMLErrorHandler();
          validator.setErrorHandler(errorHandler);
           // 得到验证的数据源
          InputStream xmlStream = JobConfigXMLValidateTest. class.getClassLoader()
                   .getResourceAsStream(xmlFileName);
          Source source = new StreamSource(xmlStream);
           // 开始验证,成功输出success!!!,失败输出fail
           try {
              validator.validate(source);
              XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
               // 如果错误信息不为空,说明校验失败,打印错误信息
               if (errorHandler.getErrors().hasContent()) {
                   System. out.println( "XML文件通过XSD文件校验失败!" );
                   writer.write(errorHandler.getErrors());
              } else {
                   System. out.println( "Good! XML文件通过XSD文件校验成功!" );
              }
              ;
          } catch (Exception ex) {
              ex.printStackTrace();
          }
     }
}

当不合法的时候,打印信息如下:(例如设置jobId为:asdfa23##4)

XML文件通过XSD文件校验失败!

  cvc-pattern-valid: Value 'asdfa23##4' is not facet-valid with respect to pattern '[\w,\-,_]{1,32}' for type 'IDType'.
  cvc-type.3.1.3: The value 'asdfa23##4' of element 'jobID' is not valid.

读取xml中引用的xsd信息的代码:

SAXReader saxReader = new SAXReader();
               Document document = saxReader.read(configFile);
               Element root = document.getRootElement();
               QName qName = new QName("schemaLocation", new Namespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));
               Attribute xsdAddr = root.attribute(qName);
               System.out.println(xsdAddr.getText());
阅读全文 »

Netty5.x中新增和值得注意的点

发表于 2014-02-26 | 阅读次数
最近事情多,OneCoder折腾了好几天,总算翻译完成了。
翻译自官方文档:http://netty.io/wiki/new-and-noteworthy-in-5.x.html
 
该文档会列出在Netty新版本中值得注意变化和新特性列表。帮助你的应用更好的适应新的版本。
 
不像Netty3.x和4.x之间的变化,5.x没有那么大的变化,不过也取得了其简化设计中的一些突破性进展.。我们力求尽可能平滑的从4.x版本过度到5.x版本,如果你在迁移过程中遇到任何问题,请告知我们。
 
核心变化
 
 
支持Android
 
提供了:
  •  移动设备变成更加强大
  • 通过Ice Cream Sandwich解决了在ADK中最著名的与NIO和SSLEngine相关的问题,且
  • 用户显然想要重用他们应用中的的编解码和处理器代码。
我们决定官方支持Android(4.0及以上版本)
 
简化处理器层次
 
ChannelInboundHandler和ChannelOutboundHandler整合为ChannelHandler。ChannelHandler现在包含输入和输出的处理方法。
 
ChannelInboundHandlerAdapter,ChannelOutboundHandlerAdapter和ChannelDuplexHandlerAdapter已被废弃,由 ChannelHandlerAdapter代替。
 
由于现在无法区分处理器(handler) 是输入还是输出的处理器,CombinedChannelDuplexHandler现在由 ChannelHandlerAppender代替。
 
更多相关变化,可参考https://github.com/netty/netty/pull/1999
 
channelRead0() → messageReceived()
 
我知道。这是一个愚蠢的错误。如果你使用了SimpleChannelInboundHandler,你需要把channelRead0()重命名为messageReceived()。
 
废弃中移除的
 
Channel.deregister()已被移除。不再生效和被使用。取而代之的,我们将允许Channel被充注册到不同的事件循环。
 
ChannelHandlerContext.attr(..) == Channel.attr(..)
 
Channel和ChannelHandlerContext类都实现了AttributeMap接口,使用户可以在其上关联一个或多个属性。有时会让用户感到困惑的是Channel和ChannelHandlerContext都有其自己的存储用户定义属性的容器。例如,即使你通过Channel.attr(KEY_X).set(valueX)给属性'KEY_X’赋值,你却无法通过ChannelHandlerContext.attr(KEY_X).get()方法获取到值。反之亦是如此。这种行为不仅仅令人不解而且还浪费内存。
 
为了解决这个问题,我们决定每个Channel内部仅保留一个map。AttributeMap总是用AttributeKey作为它的key。AttributeKey确保键的唯一性,因此每个Channel中如果存在一个以上的属性容易是多余的。只要用户把他自己的AttributeKey定义成ChannelHandler的private static final变量,就不会有出现重复key的风险。
 
更简单更精确的缓冲区泄漏追踪
 
 
之前,查找缓冲区泄漏是很困难的,并且泄漏的警告信息也不是很有帮助。现在我们有了增强的泄漏报告机制,该机制会在增长超过上限时触发。
 
更多的信息可查看:http://netty.io/wiki/reference-counted-objects.html 。由于该特性十分重要,所以也移植入了4..0.14.Final版中。
 
PooledByteBufAllocator成为默认的allocator
 
在4.x版本中,UnpooledByteBufAllocator是默认的allocator,尽管其存在某些限制。现在PooledByteBufAllocator已经广泛使用一段时间,并且我们有了增强的缓冲区泄漏追踪机制,所以是时候让PooledByteBufAllocator成为默认了。
 
全局唯一的Channel ID
 
 
每个Channel现在有了全局唯一的ID,其生成的依据是:
 
   * MAC地址(EUI-48或是EUI-64),最好是全局唯一的,
   * 当前进程的ID
   * System#currentTimeMillis()
   * System#nanoTime()
   * 随机的32位整数,以及
   * 系列递增的32位整数
 
可通过Channel.id()方法获取Channel的ID。
 
更灵活的线程模型
 
 
增加了新的ChannelHandlerInvoker接口,用于使用户可以选择使用哪个线程调用事件处理方法。替代之前的在向ChannelPipeline添加 ChannelHandler时指定一个EventExecutor的方式,使用该特性需要指定一个用户自定义的 ChannelHandlerInvoker实现。
 
关于该变化更多的信息,可参考:https://github.com/netty/netty/commit/132af3a485015ff912bd567a47881814d2ce1828
 
EmbeddedChannel的易用性
 
EmbeddedChannel中的readInbound()和readOutbound()方法返回专门类型的参数,因此你不必在转换他们的返回值。这可以简化你的测试用例代码。
EmbeddedChannel ch = ...;

// BEFORE:
FullHttpRequest req = (FullHttpRequest) ch.readInbound();

// AFTER:
FullHttpRequest req = ch.readInbound();

 
使用Executor代替ThreadFactory
 
 
有些应用要求用户使用Executor运行他们的任务。4.x版本要求用户在创建事件循环(event loop)时指定ThreadFacotry,现在不再是这样了。
 
关于该变化的更多信息,可参考:https://github.com/netty/netty/pull/1762
 
Class loader友好化
 
一些类型,如AttributeKey对于在容器环境下运行的应用是不友好的,现在不是了。
 
编解码和处理器(handlers)
 
   * XmlFrameDecoder支持流式的XML文档
 
   * 二进制的memcache协议编解码
   * 支持SPDY/3.1 (也移植到了4.x版本)
   * 重构了HTTP多部分的编解码
 
阅读全文 »

JZMQ环境变量配置说明 ZeroMQ Java Binding

发表于 2014-01-24 | 阅读次数

在考虑使用ZeroMQ时,灵活夸平台使用的问题。除了要在编译不同平台的版本,还需要在不同的平台下,进行相应的环境变量的配置。简要说明一下:
首先,对于JNI调用来说,不论什么平台都需要指定本地动态链接库的位置,指向包含动态链接库的文件夹:

-Djava.library.path=XXX

不过光这样是不够的,因为底层C/C++ 库之间也需要知道彼此的位置,这就需要指定库的地址:

Windows,配置path环境变量

set path=%path%;XXX

Linux,配置LD_LIBRARY_PATH环境变量

export LD_LIBRARY_PATH=XXX

基本就ok了。还有一点略微奇怪的是,在OneCoder这里,windows下需要两个dll文件。jzmq.dl和libzmq.dll,但是在linux下核心文件虽然还是两个libjzmq.so和libzmq.so和libzmq.so.3文件(这里,我使用的是ZeroMQ3.2.4的源码编译)。否则总会报找不到libzmq.so.3这个文件的错误。不知道具体原因。
 

阅读全文 »

ZeroMQ研究 Majordomo Protocol, Java样例实现

发表于 2014-01-18 | 阅读次数
最近研究利用zeromq实现多对多的双向自由收发。在官方上发现了MDP协议,经过验证貌似可行。正在开发中,将验证代码分享如下。
 
 
交互协议栈:
 
Worker端:
A READY command consists of a multipart message of 4 frames, formatted on the wire as follows:
 
   * Frame 0: Empty frame
   * Frame 1: "MDPW01" (six bytes, representing MDP/Worker v0.1)
   * Frame 2: 0x01 (one byte, representing READY)
   * Frame 3: Service name (printable string)
 
A REQUEST command consists of a multipart message of 6 or more frames, formatted on the wire as follows:
 
   * Frame 0: Empty frame
   * Frame 1: "MDPW01" (six bytes, representing MDP/Worker v0.1)
   * Frame 2: 0x02 (one byte, representing REQUEST)
   * Frame 3: Client address (envelope stack)
   * Frame 4: Empty (zero bytes, envelope delimiter)
   * Frames 5+: Request body (opaque binary)
 
A REPLY command consists of a multipart message of 6 or more frames, formatted on the wire as follows:
 
   * Frame 0: Empty frame
   * Frame 1: "MDPW01" (six bytes, representing MDP/Worker v0.1)
   * Frame 2: 0x03 (one byte, representing REPLY)
   * Frame 3: Client address (envelope stack)
   * Frame 4: Empty (zero bytes, envelope delimiter)
   * Frames 5+: Reply body (opaque binary)
 
A HEARTBEAT command consists of a multipart message of 3 frames, formatted on the wire as follows:
 
   * Frame 0: Empty frame
   * Frame 1: "MDPW01" (six bytes, representing MDP/Worker v0.1)
   * Frame 2: 0x04 (one byte, representing HEARTBEAT)
 
A DISCONNECT command consists of a multipart message of 3 frames, formatted on the wire as follows:
 
   * Frame 0: Empty frame
   * Frame 1: "MDPW01" (six bytes, representing MDP/Worker v0.1)
   * Frame 2: 0x05 (one byte, representing DISCONNECT)
 
Client端:
 
A REQUEST command consists of a multipart message of 4 or more frames, formatted on the wire as follows:
 
   * Frame 0: Empty (zero bytes, invisible to REQ application)
   * Frame 1: "MDPC01" (six bytes, representing MDP/Client v0.1)
   * Frame 2: Service name (printable string)
   * Frames 3+: Request body (opaque binary)
 
A REPLY command consists of a multipart message of 4 or more frames, formatted on the wire as follows:
 
   * Frame 0: Empty (zero bytes, invisible to REQ application)
   * Frame 1: "MDPC01" (six bytes, representing MDP/Client v0.1)
   * Frame 2: Service name (printable string)
   * Frames 3+: Reply body (opaque binary)
 
下面是示例代码,基于官方的代码精简改造,去掉了heartbeat机制。便于理解功能。
 
Broker:
package com.coderli.zeromq.majordomoprotocol;


import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;


import org.zeromq.ZContext;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为核心Broker模块
 *
 */
public class Broker extends JZMQBase {


     private static class Service {
           // 服务名
           public final String name;
           // 请求信息队列
          Deque<ZMsg> requests;
           // 待用worker队列
          Deque<Worker> waiting; // List of waiting workers


           public Service(String name) {
               this. name = name;
               this. requests = new ArrayDeque<ZMsg>();
               this. waiting = new ArrayDeque<Worker>();
          }
     }


     private static class Worker {
           // worker的唯一标识
           @SuppressWarnings( "unused")
          String identity; // Identity of worker
           // 目标worker地址
          ZFrame address; // Address frame to route to
           // 包含的service名称,如果存在
          Service service;


           public Worker(String identity, ZFrame address) {
               this. address = address;
               this. identity = identity;
          }
     }


     private ZContext ctx;
     private ZMQ.Socket socket;
     private Map<String, Service> services;
     private Map<String, Worker> workers;
     private Deque<Worker> waiting;


     public static void main(String[] args) {
          Broker broker = new Broker();
          broker.bind( BROKER_FRONT_END);
          broker.mediate();
     }


     public Broker() {
           this. services = new HashMap<String, Service>();
           this. workers = new HashMap<String, Worker>();
           this. waiting = new ArrayDeque<Worker>();
           this. ctx = new ZContext();
           this. socket = ctx.createSocket(ZMQ. ROUTER);
     }


     public void mediate() {
           while (!Thread. currentThread().isInterrupted()) {
              ZMQ.Poller items = new ZMQ.Poller(1);
              items.register( socket, ZMQ.Poller. POLLIN);
              items.poll();
               if (items.pollin(0)) {
                   ZMsg msg = ZMsg. recvMsg(socket);
                    if (msg == null) {
                        System. out.println( "接收到的消息为null。" );
                         break; // Interrupted
                   }
                   System. out.println( "I: received message:\n");
                   msg.dump(System. out);
                    // 根据协议栈规则读取数据,此处需要注意broker接受到的协议栈格式
                   ZFrame sender = msg.pop();
                   ZFrame empty = msg.pop();
                   ZFrame header = msg.pop();
                    if (MDP. C_CLIENT.frameEquals(header)) {
                        processClient(sender, msg);
                   } else if (MDP.W_WORKER.frameEquals(header))
                        processWorker(sender, msg);
                    else {
                        System. out.println( "E: invalid message:\n");
                        msg.dump(System. out);
                        msg.destroy();
                   }
                   sender.destroy();
                   empty.destroy();
                   header.destroy();
              }
          }
          destroy();
     }


     private void destroy() {
          Worker[] deleteList = workers.entrySet().toArray( new Worker[0]);
           for (Worker worker : deleteList) {
              deleteWorker(worker, true);
          }
           ctx.destroy();
     }


     /**
      * 处理客户端请求的,用于分发给指定的worker.
      */
     private void processClient(ZFrame sender, ZMsg msg) {
           if (msg.size() < 2) {
              System. out.println( "消息栈不完整,不能发送" );
               return;
          }
          ZFrame serviceFrame = msg.pop();
          msg.wrap(sender);
          dispatch(requireService(serviceFrame), msg);
          serviceFrame.destroy();
     }


     private void processWorker(ZFrame sender, ZMsg msg) {
           if (msg.size() < 1) {
              System. out.println( "回复给客户端的消息不完整,不能发送。" );
          }
          ZFrame command = msg.pop();
           boolean workerReady = workers.containsKey(sender.strhex());
          Worker worker = requireWorker(sender);
           if (MDP. W_READY.frameEquals(command)) {
               if (workerReady) {
                   System. out.println( "删除worker:" + sender.strhex());
                   deleteWorker(worker, true);
              } else {
                   ZFrame serviceFrame = msg.pop();
                   worker. service = requireService(serviceFrame);
                   workerWaiting(worker);
                   serviceFrame.destroy();
              }
          } else if (MDP. W_REPLY.frameEquals(command)) {
               if (workerReady) {
                   System. out.println( "开始给客户端相应" );
                   ZFrame client = msg.unwrap();
                   msg.addFirst(worker. service. name);
                   msg.addFirst(MDP. C_CLIENT.newFrame());
                   msg.wrap(client);
                   msg.send( socket);
                   workerWaiting(worker);
              } else {
                   deleteWorker(worker, true);
              }
          } else {
              System. out.print( "不合法的消息结构" );
              msg.dump(System. out);
          }
          msg.destroy();
     }


     private void deleteWorker(Worker worker, boolean disconnect) {
          System. out.println( "删除worker");
           if (disconnect) {
              sendToWorker(worker, MDP. W_DISCONNECT, null, null);
          }
           if (worker. service != null)
              worker. service. waiting.remove(worker);
           workers.remove(worker);
          worker. address.destroy();
     }


     private Worker requireWorker(ZFrame address) {
           assert (address != null);
          String identity = address.strhex();
          Worker worker = workers.get(identity);
           if (worker == null) {
              worker = new Worker(identity, address.duplicate());
               workers.put(identity, worker);
              System. out.println( "注册了新的worker:" + identity);
          }
           return worker;
     }


     private Service requireService(ZFrame serviceFrame) {
           assert (serviceFrame != null);
          String name = serviceFrame.toString();
          Service service = services.get(name);
           if (service == null) {
              service = new Service(name);
               services.put(name, service);
          }
           return service;
     }


     private void bind(String endpoint) {
           socket.bind(endpoint);
          System. out.println( "Broker版定在端口: " + endpoint);
     }


     public synchronized void workerWaiting(Worker worker) {
           waiting.addLast(worker);
          worker. service. waiting.addLast(worker);
          dispatch(worker. service, null);
     }


     private void dispatch(Service service, ZMsg msg) {
           assert (service != null);
           if (msg != null) {
              service. requests.offerLast(msg);
          }
           while (!service. waiting.isEmpty() &amp;&amp; !service.requests.isEmpty()) {
              msg = service. requests.pop();
              Worker worker = service. waiting.pop();
               waiting.remove(worker);
              sendToWorker(worker, MDP. W_REQUEST, null, msg);
              msg.destroy();
          }
     }


     public void sendToWorker(Worker worker, MDP command, String option,
              ZMsg msgp) {
          ZMsg msg = msgp == null ? new ZMsg() : msgp.duplicate();
           if (option != null)
              msg.addFirst( new ZFrame(option));
          msg.addFirst(command.newFrame());
          msg.addFirst(MDP. W_WORKER.newFrame());
          msg.wrap(worker. address.duplicate());
          System. out.println( "给worker发送命令: [" + command + "]。");
          msg.dump(System. out);
          msg.send( socket);
     }
}
ClientAPI:
package com.coderli.zeromq.majordomoprotocol;


import org.zeromq.ZContext;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为Client端依赖的API。
 *
 */
public class ClientAPI {


     private String broker;
     private ZContext ctx;
     private ZMQ.Socket client;
     private long timeout = 2500;
     private int retries = 3;


     public long getTimeout() {
           return timeout;
     }


     public void setTimeout( long timeout) {
           this. timeout = timeout;
     }


     public int getRetries() {
           return retries;
     }


     public void setRetries( int retries) {
           this. retries = retries;
     }


     public ClientAPI(String broker) {
           this. broker = broker;
           ctx = new ZContext();
          reconnectToBroker();
     }


     void reconnectToBroker() {
           if ( client != null) {
               ctx.destroySocket( client);
          }
           client = ctx.createSocket(ZMQ. REQ);
           client.connect( broker);
          System. out.println( "连接到Broker:" + broker );
     }


     /**
      * 给broker发送消息
      *
      * @param service
      * @param request
      * @return
      */
     public ZMsg send(String service, ZMsg request) {


          request.push( new ZFrame(service));
          request.push(MDP. C_CLIENT.newFrame());
          System. out.println( "发送消息给worker:" + service);
          request.dump(System. out);
          ZMsg reply = null;


           int retriesLeft = retries;
           while (retriesLeft > 0 &amp;&amp; !Thread.currentThread().isInterrupted()) {
              request.duplicate().send( client);
              ZMQ.Poller items = new ZMQ.Poller(1);
              items.register( client, ZMQ.Poller. POLLIN);
               if (items.poll( timeout) == -1)
                    break; // 超时退出
               if (items.pollin(0)) {
                   ZMsg msg = ZMsg. recvMsg(client);
                   System. out.println( "接收到消息。" );
                   msg.dump(System. out);
                   ZFrame header = msg.pop();
                   header.destroy();
                   ZFrame replyService = msg.pop();
                   replyService.destroy();
                   reply = msg;
                    break;
              } else {
                   items.unregister( client);
                    if (--retriesLeft == 0) {
                        System. out.println( "超过重试次数,错误。退出。" );
                         break;
                   }
                   System. out.println( "没有收到回应,重试。" );
                   reconnectToBroker();
              }
          }
          request.destroy();
           return reply;
     }


     public void destroy() {
           ctx.destroy();
     }
}
ClientOne:
package com.coderli.zeromq.majordomoprotocol;


import org.zeromq.ZMsg;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为Client端一号,定向发给1、2号worker
 *
 */
public class ClientOne extends JZMQBase {


     public static void main(String[] args) throws InterruptedException {
          ClientAPI clientSession = new ClientAPI(BROKER_FRONT_END);


           int count;
           for (count = 0; count < 1; count++) {
              ZMsg request = new ZMsg();
              ZMsg reply = null;
               long start = System. nanoTime();
              request.addString(String. valueOf(start));
               if (count % 2 == 1) {
                   reply = clientSession.send( "one", request);
              } else {
                   reply = clientSession.send( "two", request);
              }
               if (reply != null)
                   reply.destroy();
               else
                    break; // Interrupt or failure
              Thread. sleep(1000000L);
          }


          System. out.printf( "%d requests/replies processed\n", count);
          clientSession.destroy();
     }
}
 
WorkerAPI:
package com.coderli.zeromq.majordomoprotocol;


import org.zeromq.ZContext;
import org.zeromq.ZFrame;
import org.zeromq.ZMQ;
import org.zeromq.ZMsg;


/**
 * ZeroMQ验证 workerAPI封装
 *
 * @author lihzh
 * @date 2014年1月15日 下午2:23:14
 */
public class WorkerAPI {


     private String broker;
     private ZContext ctx;
     private String service;
     private ZMQ.Socket worker;


     private long timeout = 2500;


     private ZFrame replyTo;


     public WorkerAPI(String broker, String service) {
           assert (broker != null);
           assert (service != null);
           this. broker = broker;
           this. service = service;
           ctx = new ZContext();
          reconnectToBroker();
     }


     /**
      * 给Broker发送消息
      *
      * @param command
      * @param option
      * @param msg
      */
     void sendToBroker(MDP command, String option, ZMsg msg) {
          msg = msg != null ? msg.duplicate() : new ZMsg();


           if (option != null)
              msg.addFirst( new ZFrame(option));
          msg.addFirst(command.newFrame());
          msg.addFirst(MDP. W_WORKER.newFrame());
          msg.addFirst( new ZFrame( new byte[0]));
          msg.send( worker);
     }


     void reconnectToBroker() {
           if ( worker != null) {
               ctx.destroySocket( worker);
          }
           worker = ctx.createSocket(ZMQ. DEALER);
           worker.connect( broker);
          sendToBroker(MDP. W_READY, service, null);
     }


     /**
      * 接收数据
      *
      * @param reply
      * @return
      * @author lihzh
      * @date 2014年1月15日 下午2:24:23
      */
     public ZMsg receive(ZMsg reply) {
           if (reply != null) {
              reply.wrap( replyTo);
              sendToBroker(MDP. W_REPLY, null, reply);
              reply.destroy();
          }
           while (!Thread. currentThread().isInterrupted()) {
              ZMQ.Poller items = new ZMQ.Poller(1);
              items.register( worker, ZMQ.Poller. POLLIN);
               if (items.poll( timeout) == -1)
                    break; // Interrupted
               if (items.pollin(0)) {
                   ZMsg msg = ZMsg. recvMsg(worker);
                    if (msg == null)
                         break; // Interrupted
                   System. out.print( "接收到数据:" );
                    long time = System. nanoTime();
                    long endTime = Long
                             . valueOf(new String(msg.getLast().getData()));
                   System. out.println( "消耗时间:" + (time - endTime));
                   msg.dump(System. out);
                   ZFrame empty = msg.pop();
                   empty.destroy();
                   ZFrame header = msg.pop();
                   header.destroy();
                   ZFrame command = msg.pop();
                    if (MDP.W_REQUEST.frameEquals(command)) {
                         replyTo = msg.unwrap();
                        command.destroy();
                         return msg;
                   } else {
                        System. out.println( "不合法的消息结构。" );
                        msg.dump(System. out);
                   }
                   command.destroy();
                   msg.destroy();
              }


          }
           return null;
     }


     public void destroy() {
           ctx.destroy();
     }
}
 
WorkerOne:
package com.coderli.zeromq.majordomoprotocol;


import org.zeromq.ZMsg;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为Worker端,定向回复给调用的client
 *
 */
public class WorkerOne extends JZMQBase {


     /**
      * @param args
      */
     public static void main(String[] args) {
          WorkerAPI workerSession = new WorkerAPI(BROKER_FRONT_END, "one" );


          ZMsg reply = null;
           while (!Thread. currentThread().isInterrupted()) {
              ZMsg request = workerSession.receive(reply);
               if (request == null)
                    break;
              reply = request;
          }
          workerSession.destroy();
     }
}
 
WorkerTwo:
package com.coderli.zeromq.majordomoprotocol;


import org.zeromq.ZMsg;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为Worker端,定向回复给调用的client
 *
 */
public class WorkerTwo extends JZMQBase {


     /**
      * @param args
      */
     public static void main(String[] args) {
          WorkerAPI workerSession = new WorkerAPI(BROKER_FRONT_END, "two" );


          ZMsg reply = null;
           while (!Thread. currentThread().isInterrupted()) {
              ZMsg request = workerSession.receive(reply);
               if (request == null)
                    break;
              reply = request;
          }
          workerSession.destroy();
     }
}
 
MDP常量类:
package com.coderli.zeromq.majordomoprotocol;


import java.util.Arrays;


import org.zeromq.ZFrame;


/**
 * ZeroMQ Majordomo Protocol协议验证<br>
 * 用于实现多client、多worker实现双向指定目标数据收发 <br>
 * 此为常量类
 *
 */
public enum MDP {


     C_CLIENT("MDPC01"), W_WORKER("MDPW01"),


     W_READY(1), W_REQUEST(2), W_REPLY(3), W_HEARTBEAT(4), W_DISCONNECT (5);


     private final byte[] data;


     MDP(String value) {
           this. data = value.getBytes();
     }


     MDP(int value) { // watch for ints>255, will be truncated
           byte b = ( byte) (value &amp; 0xFF);
           this. data = new byte[] { b };
     }


     public ZFrame newFrame() {
           return new ZFrame( data);
     }


     public boolean frameEquals(ZFrame frame) {
           return Arrays. equals(data, frame.getData());
     }
}
 
附,基类代码
/**
 * @author lihzh
 * @date 2014年1月14日 上午9:32:01
 */
public abstract class JZMQBase {


     protected static String LOCAL_ADDRESS = "tcp://127.0.0.1:1234";
     protected static String LOCAL_ADDRESS_PUSHER = "tcp://127.0.0.1:2345";
     protected static String LOCAL_ADDRESS_ROUTER = "tcp://127.0.0.1:3456";
     protected static String LOCAL_ADDRESS_DECLARER = "tcp://127.0.0.1:4567";


     protected static String BROKER_FRONT_END = "tcp://127.0.0.1:4000";
     protected static String BROKER_BACK_END = "tcp://127.0.0.1:4001";
}

代码介绍:

其实原理很简单,主要利用ZeroMQ底层封装好的发送接受协议,来事先给指定的客户端发送消息。由于zeromq是基于socket的,所以本质上只能点对点通信。所以要事先多对多中心,就需要中间的一个转发器。即Broker。在Broker中记录了目标地址,这个地址ZeroMQ底层提供的,必须使用保存起来,用于下次发送时使用。

阅读全文 »

ZeroMQ 初学 Java Binding验证代码

发表于 2014-01-15 | 阅读次数
学习ZeroMQ使用,根据官方文档介绍,写了如下Java验证代码。仅供参考。需要依赖jzmq的jar包和本地库。
 
1、请求-响应模式
package com.coderli.zeromq.requestreplay;


import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ 请求响应模式验证代码 <br>
 * 此为服务端
 *
 * @author OneCoder
 * @date 2014年1月13日 下午11:28:47
 * @website http://www.coderli.com
 */
public class ReplayServer extends JZMQBase {


     public static void main(String[] args) {
           // 参数代表使用多少线程,大多数情况下,1个线程已经足够。
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为响应模式
          ZMQ.Socket socket = context.socket(ZMQ. REP);
          socket.bind( LOCAL_ADDRESS); // 绑定服务地址及端口
           for (;;) {
              System. out.println( "Server start.");
              socket.recv();
              String str = "Ok, I'm server";
              socket.send(str);
          }
     }
}
package com.coderli.zeromq.requestreplay;


import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ 请求响应模式验证代码 <br>
 * 此为客户端
 *
 * @author OneCoder
 * @date 2014年1月11日 上午10:34:18
 * @website http://www.coderli.com
 */
public class RequestClient extends JZMQBase {


     /**
      * @param args
      * @author OneCoder
      * @date 2014年1月11日 上午10:34:18
      */
     public static void main(String[] args) {


          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为请求模式
          ZMQ.Socket socket = context.socket(ZMQ. REQ);
           // 创建链接
          socket.connect( LOCAL_ADDRESS);
           int count = 1;
           for (;;) {
               try {
                    long time = System. nanoTime();
                   socket.send( "Hello, currentTime: " + count);
                    byte[] recs = socket.recv();
                    long end = System. nanoTime();
                   System. out.println( new String(recs) + " Cost time: "
                             + (end - time));
                   count++;
                   Thread. sleep(1000);
              } catch (Exception e) {
                   e.printStackTrace();
              }
          }
     }
}
测试结果,单线程请求-相应一次的耗时大概在450us。
2、Publish-subscribe
package com.coderli.zeromq.pubsub;


import java.util.Random;


import org.zeromq.ZMQ;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ 发布订阅模式Java验证代码 <br>
 * 此为发布者
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class Publisher extends JZMQBase {


     public static void main(String[] args) throws InterruptedException {
           // 参数代表使用多少线程,大多数情况下,1个线程已经足够。
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为发布者
          ZMQ.Socket socket = context.socket(ZMQ. PUB);
          socket.bind( LOCAL_ADDRESS); // 绑定服务地址及端口
           for (;;) {
               int i = ( int) ( new Random().nextDouble() * 2 + 1);
              String s = String. valueOf(i);
               long time = System. nanoTime();
              socket.send(s + String. valueOf(time));
              System. out.println( "发布了新消息,时间:" + time + " 类型:" + s);
              Thread. sleep(2000);
          }
     }
}
package com.coderli.zeromq.pubsub;


import org.zeromq.ZMQ;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ 发布订阅模式Java验证代码 <br>
 * 此为订阅者1号
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class SubscriberOne extends JZMQBase {


     public static void main(String[] args) throws InterruptedException {
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为请求模式
          ZMQ.Socket socket = context.socket(ZMQ. SUB);
           // 创建订阅者,必须要过主题过滤器
           byte[] filter = "1".getBytes();
          socket.subscribe(filter);
          socket.connect( LOCAL_ADDRESS);
           for (;;) {
               byte[] recs = socket.recv();
               long receiveTime = System. nanoTime();
              String oriMsg = new String(recs);
              String msg = new String(recs,1,recs.length-1);
               long pubTime = Long. valueOf(msg);
               long costTime = receiveTime - pubTime;
              System. out.println( "Receive: " + oriMsg + " Cost time: " + costTime);
          }
     }
}
package com.coderli.zeromq.pubsub;


import org.zeromq.ZMQ;


import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ 发布订阅模式Java验证代码 <br>
 * 此为订阅者2号
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class SubscriberTwo extends JZMQBase{


     public static void main(String[] args) throws InterruptedException {
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为请求模式
          ZMQ.Socket socket = context.socket(ZMQ. SUB);
           // 创建订阅者,必须要过主题过滤器
           byte[] filter = "2".getBytes();
          socket.subscribe(filter);
          socket.connect( LOCAL_ADDRESS);
           for (;;) {
               byte[] recs = socket.recv();
               long receiveTime = System. nanoTime();
              String oriMsg = new String(recs);
              String msg = new String(recs,1,recs.length-1);
               long pubTime = Long. valueOf(msg);
               long costTime = receiveTime - pubTime;
              System. out.println( "Receive: " + oriMsg + " Cost time: " + costTime);
          }
     }
}
   * 发布者中随机发布开头为1或者2的消息。
   * 订阅者中必须有过滤器,从前向后匹配发布者发送的消息,完全匹配则接收消息。这里1/2号订阅者分别过滤消息开头是1/2的数据。
   * 如果没有发布者,则订阅者阻塞,直到有发布者发送消息。如果订阅者掉线,消息会丢失。
   * 如果要订阅多个filter只需多次调用subscribe方法即可。
   * 从发布到订阅收到消息,大约耗时300us。
 
3、PipeLine模式
 
想象一下这样的场景,如果需要统计各个机器的日志,我们需要将统计任务分发到各个节点机器上,最后收集统计结果,做一个汇总。PipeLine比较适合于这种场景,他的结构图,如图3所示。
package com.coderli.zeromq.pipeline;

import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Pipeline模式Java验证代码 <br>
 * 此为主Pusher
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class MainPusher extends JZMQBase {


     public static void main(String[] args) throws InterruptedException {
           // 参数代表使用多少线程,大多数情况下,1个线程已经足够。
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为Pusher
          ZMQ.Socket socket = context.socket(ZMQ. PUSH);
          socket.bind( LOCAL_ADDRESS); // 绑定服务地址及端口
           for (;;) {
               long time = System. nanoTime();
              socket.send(String. valueOf(time));
              System. out.println( "发布了新消息,时间:" + time);
              Thread. sleep(2000);
          }
     }


}
package com.coderli.zeromq.pipeline;

import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Pipeline模式Java验证代码 <br>
 * 此为中转worker
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class WorkerOne extends JZMQBase {


     public static void main(String[] args) {
           // 指定模式为pull模式
          ZMQ.Socket receiver = ZMQ.context(1).socket(ZMQ.PULL);
          receiver.connect( LOCAL_ADDRESS);
           // 指定模式为push模式
          ZMQ.Socket sender = ZMQ.context(1).socket(ZMQ.PUSH);
          sender.connect( LOCAL_ADDRESS_PUSHER);
           for (;;) {
               byte[] recs = receiver.recv();
               long receiveTime = System. nanoTime();
              String oriMsg = new String(recs);
               long pubTime = Long. valueOf(oriMsg);
               long costTime = receiveTime - pubTime;
              System. out.println( "Receive: " + oriMsg + " Cost time: " + costTime);
              sender.send( "1" + oriMsg);
              System. out.println( "Send to sinker.");
          }
     }
}
package com.coderli.zeromq.pipeline;

import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Pipeline模式Java验证代码 <br>
 * 此为中转worker
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class WorkerTwo extends JZMQBase {


     public static void main(String[] args) {
           // 指定模式为pull模式
          ZMQ.Socket receiver = ZMQ.context(1).socket(ZMQ.PULL);
          receiver. connect(LOCAL_ADDRESS);
           // 指定模式为push模式
          ZMQ.Socket sender = ZMQ.context(1).socket(ZMQ.PUSH);
          sender. connect(LOCAL_ADDRESS_PUSHER);
           for (;;) {
               byte[] recs = receiver.recv();
               long receiveTime = System. nanoTime();
              String oriMsg = new String(recs);
               long pubTime = Long. valueOf(oriMsg);
               long costTime = receiveTime - pubTime;
              System. out
                        .println( "Receive: " + oriMsg + " Cost time: " + costTime);
              sender.send( "2" + oriMsg);
              System. out.println( "Send to sinker.");
          }
     }
}
package com.coderli.zeromq.pipeline;

import org.zeromq.ZMQ;
import com.coderli.zeromq.JZMQBase;


/**
 * ZeroMQ Pipeline模式Java验证代码 <br>
 * 此为最终sinker
 *
 * @author OneCoder
 * @date 2014年1月14日 上午11:16:03
 * @blog http://www.coderli.com
 */
public class Sinker extends JZMQBase {


     public static void main(String[] args) {
          ZMQ.Context context = ZMQ. context(1);
           // 指定模式为pull模式
          ZMQ.Socket receiver = context.socket(ZMQ. PULL);
          receiver. bind(LOCAL_ADDRESS_PUSHER);
           for (;;) {
               byte[] recs = receiver.recv();
               long receiveTime = System. nanoTime();
              String oriMsg = new String(recs);
              String msg = new String(recs,1,recs.length-1);
               long pubTime = Long. valueOf(msg);
               long costTime = receiveTime - pubTime;
              System. out.println( "Receive: " + oriMsg + " Cost time: " + costTime);
          }
     }
}
以上只是一些初级结构的初步使用,对于我来说重点还是研究router模式,实现N对M集群的定向通信。随后会公布研究代码
阅读全文 »

Windows7 x64下编译ZeroMQ和JZMQ Java Binding

发表于 2014-01-13 | 阅读次数

首先,先说明的是,OneCoder采用的是使用vs2010的编译方式,zeromq的版本是3.2.4。

先编译zeromq3.2.4的源码。双击builds/msvc/ 目录下的msvc.sln导入到VS2010。选择x64位编译器,生成解决方案。

默认的生成目录是在zeromq-3.2.4\builds\msvc\Release\ 下。

然后编译Jzmq,从github上下载源码。
git clone https://github.com/zeromq/jzmq.git

默认master分支的代码在windows好像目录结构不对,所以这里选择的是切换到tag2.2.2的分支。
同样,打开jzmq目录下,builds\msvs\msvc.sln文件,导入到VS2010中。
编辑工程属性。选择x64编译环境。编辑VC++工程目录

在包含目录中,添加JDK的include目录,JDK include目录中的win32目录以及刚才zeromq目录中的include目录。

在库目录中,添加刚才编译zeromq生成的release目录。

然后生成解决方案即可。

与4.0.3版本编译,会出现异常:

error C2371: “int8_t”: 重定义;不同的基类型

可能兼容性还有问题。

运行期,只需要依赖编译生成的jzmq.dll和libzmq.dll文件即可。

阅读全文 »

MacOS10.9 下 ZeroMQ4.0.3和Java Binding安装部署

发表于 2014-01-10 | 阅读次数

ZeroMQ是什么可以自己去官网了解。
http://zeromq.org/

Mac下,对于安装了brew的朋友,很简单了。
首先安装zeromq

brew install zeromq

如果报错,很可能是因为没有安装命令行编译工具。可以通过xcode命令安装

xcode-select --install

安装成功后,即可正常编译zeromq了。

对于自己手动编译的朋友,也不麻烦。首先还是要保证安装了命令行编译工具,同上通过xcode-select安装。然后解压zeromq。然后进入目录,执行:

./configure
make
make install

然后安装jzmq,java binding
通过github下载源码
git clone https://github.com/zeromq/jzmq.git
然后依次执行

./autogen.sh
./configure
make
make install

即可在/usr/local/share/java 目录下编译出zmq.jar。

注:
如果报错:
checking whether the C compiler works… no
可以执行:

export CC=/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
CPP='/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -E'

 

阅读全文 »

Restlet2.1.6发布,修正ObjectRepresentation的构造函数问题

发表于 2013-12-16 | 阅读次数

OneCoder在Restlet 2.1.4中 匪夷所思的ObjectRepresentation的构造函数中,提到过在使用2.1.4的时候遇到的异常

Exception in thread "main" java.lang.IllegalArgumentException : The serialized representation must have this media type: application/x-java-serialized-object or this one: application/x-java-serialized-object+xml
          at org.restlet.representation.ObjectRepresentation.<init>(ObjectRepresentation.java:203)
     at org.restlet.representation.ObjectRepresentation.<init>(ObjectRepresentation.java:114)

当时结果阅读代码,认为是Restlet的一个bug,并提交给Restlet。得到回复确认,称将在2.1.6版本中修复:

Hello,

thansk a lot for reporting this issue which is clearly a regression. I've added a ticket for that point: https://github.com/restlet/restlet-framework-java/issues/809
The fix will be part of the 2.1.6 release, available in a few minutes.

Best regards,
Thierry Boileau
 

今天想起,登录了一下Restlet的官方,发现最新版已经是2.1.6了。查看了一些change log,发现该问题确实已经解决。

===========
Changes log  
===========

- 2.1.6 (12/05/2013)
    - Bug fixed
       - Fixed issue #809 regression introduced when handling issues #774 and #778.

不过OneCoder还没有升级测试。大家可以测试一下。

 

阅读全文 »

Restlet 客户端连接超时问题解决

发表于 2013-12-16 | 阅读次数

使用Restlet进行同步请求,有时可能处理的时间会很长所以需要客户端进行较长时间的等待。从API中查得客户端的设置方式如下:

阅读全文 »

Spring Framework 4.0 迁移指南 (官方文档翻译)

发表于 2013-12-14 | 阅读次数

看到Spring Framework4.0发布的消息,看了下new future,OneCoder很喜欢spring这种追“时髦”的风格,groovy脚本配置和Java8都支持了。顺便就翻译了一下官方的迁移指南。对一般使用来说,迁移没什么难度。替换依赖基本就可以了。

如果想要了解Sping Framework4.0.0的新特性,可以参考官方文档中的:New Features and Enhancements in Spring Framework 4.0

环境依赖要求:

Spring Framework4.0 需要Java SE 6 或以上的版本。(特别强调,最低版本实际为2008年发布的JDK6 update 10)。如果你从老版本的Java环境中迁移,你至少需要升级到最近的JDK6版本。推荐使用Java7和8,Java8 的稳定开发者预览状态会一致持续到2014年3月,OpenJDK8 进入最终版为止。

如果你在 Java EE 服务器部署Spring应用,你需要确认你的应用支持Java EE 6及以上的版本。这其中,特别需要注意的是满足JPA2.0和Servlet3.0规范。这个意思其实是说,你仍然可以把你的Spring Framework4.0的应用部署在只支持Servlet2.5规范的容器中。(如。Google App Engine, WebSphere 7, WebLogic 10.3),只是Spring4中一些基于Servlet3.0的特性将会无效。

依赖升级

Spring Framework4.0 声明了下列(可选)依赖的最低版本:

规范

  • Servlet 3.0 (2.5 支持部署)
  • JPA 2.0
  • Bean Validation 1.0
  • JSF 2.0
  • JCache 1.0 PFD
  • JDO 3.0

容器

  • Tomcat 6.0.30
  • Jetty 7.3
  • JBoss AS 6.0
  • GlassFish 3.1
  • Oracle WebLogic 10.3.4 (with JPA 2.0 patch applied)
  • IBM WebSphere 7.0.0.9 (with JPA 2.0 feature pack installed)

库

  • Hibernate Validator 4.3
  • Hibernate 3.6 (推荐4.2 )
  • EhCache 2.1 (推荐2.5)
  • <Quartz 1.8 (推荐2.2 )
  • Jackson 1.8 (推荐2.2 )
  • Groovy 1.8 (推荐2.2)
  • Joda-Time 2.0 (推荐2.3)</span>
  • Hessian 4.0
  • XStream 1.4
  • Apache POI 3.5

废弃的代码

下列的类和方法在Spring Framework4.0中被废弃。这些代码未来将会被移除,所以请检查javadoc并迁移至推荐的写法:

Jackson v1

所有Jackson v1支持的被废弃,以支持Jacksonv2:

  • MappingJacksonMessageConverter
  • JacksonObjectMapperFactoryBean
  • MappingJacksonHttpMessageConverter

泛型:

GenericTypeResolver中的许多方法都被废弃了。新的ResolvableType类提供了对GeneriTypeResolver和GenericCollectionTypeResolver类中废弃方法的替换:

  • GenericTypeResolver.getTargetType(MethodParameter methodParam)
  • GenericTypeResolver.resolveType(Type genericType, Map<TypeVariable, Type> map)
  • GenericTypeResolver.getTypeVariableMap(Class<?> clazz)

Burlap

Burlap不再在开发包下,并且将在以后完全不再提供支持。

  • BurlapClientInterceptor
  • BurlapExporter
  • BurlapProxyFactoryBean
  • BurlapServiceExporter
  • SimpleBurlapServiceExporter

过时的JBoss类

下面的类由于不在当前JBoss释放版中而被废弃:</span></p>

  • JBossWorkManagerTaskExecutor
  • JBossWorkManagerUtils

其他废弃

  • AbstractJaxWsServiceExporter.setWebServiceFeatures(Object[] webServiceFeatures)
  • JaxWsPortClientInterceptor.setWebServiceFeatures(Object[] webServiceFeatures)</span>
  • DefaultKeyGenerator

默认的缓存key生成器</span>

Spring使用的默认的KeyGenerator实现,由原来的DefaultKeyGenerator变为SimpleKeyGenerator。新的生成器不会再有key冲突并且基本不太可能使一个缓存的方法返回错误的结果。如果仍想使用之前的key策略,你需要配置使用废弃的DefaultKeyGenerator或者创建一个自定义的KeyGenerator实现。

MVC 命名空间

Spring MVC的命名空间XSD已经升级,以正确使用一对属性。当升级到spring-mvc-4.0.xsd后,你应该分别用 enable-matrix-variables 和ignore-default-model-on-redirect respectively 来替换原来的enableMatrixVariables 和ignoreDefaultModelOnRedirect 属性。

阅读全文 »
1 … 17 18 19 … 33
LiHongZhe

LiHongZhe

onecoder's blog.

326 日志
8 分类
RSS
Creative Commons
Links
  • 酷壳
  • 酸菜鱼
  • 私塾在线学习网
  • 煮酒品茶
  • 点滴技术博客
  • 思考者日记网·束洋洋
  • 开源视窗
  • 小弟子的网络之路
  • 寄存心–移动开发
  • TicmyBlog
  • 中国程序员人才网
  • 图表秀-在线图表制作
  • IT热血青年
© 2012 - 2022 LiHongZhe
由 Jekyll 强力驱动
主题 - NexT.Muse