消息队列的原理探究及基于Fourinone的实现


消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。目前使用较多的消息队列有ActiveMQ,RabbitMQ,ZeroMQ,Kafka,MetaMQ,RocketMQ。如要详细了解可参考消息队列总结

一、MQ的两种模式

JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。而MQ则是消息队列服务,是面向消息中间件(MOM)的最终实现,是真正的服务提供者;MQ的实现可以基于JMS,也可以基于其他规范或标准。目前选择的最多的是ActiveMQ。
MQ在JMS规划下的两种经典模式。

第一种:点对点(point-to-point,简称PTP)模式
在点对点模式下,一个生产者向一个特定的队列发布消息,一个消费者从该队列中读取消息。这里,生产者知道消费者的队列,并直接将消息发送到消费者的队列。这种模式概括为:只有一个消费者将获得消息。生产者不需要在接收者消费该消息期间处于运行状态,接收者也同样不需要在消息发送时处于运行状态。每一个成功处理的消息都由接收者签收。

第二种:发布/订阅(publish/subscribe,简称pub/sub)模式
发布者/订阅者模式支持向一个特定的消息主题发布消息。0或多个订阅者可能对接收来自特定消息主题的消息感兴趣。在这种模式下,发布者和订阅者彼此不知道对方。这种模式好比是匿名公告板。这种模式概括:多个消费者可以获得消息。在发布者和订阅者之前存在时间依懒性。发布者需要建立一个订阅(subscription),以便客户能够订购。订阅者必须保持持续的活动状态以接收消息,除非订阅者建立了持久的订阅。在那种情况下,在订阅者未连接时发布的消息将在订阅者重新连接时重新发布。

FourInOne(中文名字“四不像”)是一个四合一分布式计算框架 。将Hadoop,Zookeeper,MQ,分布式缓存四大主要的分布式计算功能合为一个框架内,对复杂的分布式计算应用进行了大量简化和归纳。本文将通过写一个类ForuInOne的框架来实现这两种模式。


二、发送接收的队列模式(PTP)的实现

我们可以将Domain视为MQ队列,每个node为一个队列消息,检查Domain的变化来获取队列消息。

Sender:是一个队列发送者,它发送消息的实现是在queue上创建一个匿名节点来存放消息
p1.create(queue,(Serializable)obj);

Receiver:是一个队列接收者,他轮循queue上有没有最新消息,有就取出,并删除该节点,注意它是每次获取第一个消息,这样保证消息读取的顺序。如图2-1所示。

图 2-1 PTP模式实现

运行步骤:
1)启动ParkServerDemo(它的IP端口如果与当前已有端口冲突可进行修改),IDEA中运行结果如图2-2所示:

图2-2 ParkServerDemo

2)运行Sender,结果如图2-3所示

图2-3 Sender

3)Receiver,结果如2-4所示

图2-4 Receiver

可以看到,按照发送的先后顺序,依次收到“hello”、“world”、“mq”三个消息。 Receiver实际上轮循检查队列是否有消息,有的话每次取出所有队列的消息list,然后再取出第一个消息并删除,或许这样不是很高效,因为如果消息队列的消息太多,一次性取出队列所有消息会影响性能,但是这里旨在演示清楚MQ接收消息的实现,开发者明白了实现机制可以发挥自己的创造性,根据自己需求的消息特点针对性的设计MQ发送接收的实现。比如模仿商业MQ对每个消息队列的容量做一个限制等等。 完整demo源码如下:
//ParkServerDemo
package rmi.rmi;  
 
import rmi.fourinone.LogUtil;  
import rmi.fourinone.ParkService;  
import rmi.fourinone.ParkServiceImpl;  
 
import java.net.MalformedURLException;  
import java.rmi.AlreadyBoundException;  
import java.rmi.Naming;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
 
public class ParkServerDemo {  
   public static void main(String[] args) {  
       try {  
           ParkService parkService = new ParkServiceImpl();  
           LocateRegistry.createRegistry(9985);  
 
           try {  
               Naming.bind("rmi://localhost:9985/ParkService", parkService);  
               LogUtil.info("[ParkServerDemo]", "[main]", "Start server!");  
           } catch (MalformedURLException e) {  
               e.printStackTrace();  
           } catch (AlreadyBoundException e) {  
               e.printStackTrace();  
           }  
       } catch (RemoteException e) {  
           e.printStackTrace();  
       }  
   }  
}

//Sender

package rmi.rmi;  
  
import rmi.fourinone.ParkService;  
  
import java.io.*;  
import java.rmi.RemoteException;  
  
import static rmi.rmi.Utils.getNumber;  
import static rmi.rmi.Utils.getPark;  
import static rmi.rmi.Utils.toBytes;  
  
public class Sender {  
    public static ParkService parkService = getPark();  
  
    public static void send(String queue, Object object) {  
        try{  
            parkService.create(queue, getNumber(), toBytes((Serializable) object));  
        }catch (RemoteException e){  
            e.printStackTrace();  
        }  
        return;  
    }  
  
    public static void main(String[] args) {  
        send("queue1", "hello");  
        send("queue1", "world");  
        send("queue2", "mq");  
    }  
}  

//Recevier

package rmi.rmi;  
  
import rmi.fourinone.ObjValue;  
import rmi.fourinone.ParkService;  
  
import java.rmi.RemoteException;  
import java.util.ArrayList;  
  
import static rmi.rmi.Utils.getPark;  
import static rmi.rmi.Utils.toObject;  
  
public class Receiver {  
    public static ParkService parkService = getPark();  
    public static Object receive(String queue) throws RemoteException {  
        Object obj=null;  
        ObjValue objValue = parkService.get(queue,null);  
  
        ObjValue nodes = objValue.getWidely(queue + "..*");  
        ArrayList<String> nodeNames = nodes.getObjNames();  
        for(String nodeName:nodeNames) {  
            if (objValue != null && !nodeName.contains("version")) {  
                obj = getMsg(objValue, nodeName);  
                parkService.delete(queue, nodeName.split("\\.")[1]);  
                break;  
            }  
        }  
        return obj;  
    }  
  
    public static Object getMsg(ObjValue ov, String Domainnodekey) {  
        return toObject((byte[]) ov.getObj(Domainnodekey));  
    }  
    public static void main(String[] args) {  
        try{  
            System.out.println(receive("queue1"));  
            System.out.println(receive("queue1"));  
            System.out.println(receive("queue2"));  
        }catch (RemoteException e){  
            e.printStackTrace();  
        }  
    }  
}

三、主题订阅模式的实现

我们可以将Domain视为订阅主题,将每个订阅者(Subscriber)注册到Domain的节点Node上,发布者(Publisher)将消息逐一更新每个节点,订阅者监控每个属于自己的节点的变化事件获取订阅消息,收到后清空内容等待下一个消息,多个消息用一个arraylist存放。

图3-1 发送接收模式实现

Publiser:是一个主题发布者,它通过p1.get(topic)获取topic(主题)的所有订阅者节点,并将消息更新到每个节点上,如果有多个追加到arraylist存放。

Subscriber:是一个消息订阅者,它通过subscrib(String topic, String subscribeName, LastestListener lister)实现消息订阅,其中3个参数分别是主题名、订阅者名称、事件处理实现,这个接口会传入更新的节点内容对象,然后Subscriber用一个空的arraylist监控内容,等待下一次接收消息。HappenLastest有个boolean返回值,如果返回false,它会一直监控变化,继续有新的变化时还会进行事件调用;如果返回true,它完成本次事件调用后就终止。
1)启动ParkServerDemo(它的IP端口如果与当前已有端口冲突可进行修改),IDEA中运行结果如图3-2所示:

图3-2 ParkServerDemo

2)运行Subscriber,因为Subscriber可以有多个,传入不同的subscribeName参数代表不同的Subscriber,具体Subscriber的个数及其传入参数可通过./idea/workspace.xml文件来设置,本案例中,只设置了3个Subscriber,且其传入值分别为aaa,bbb,ccc,注意,启动Subscriber的个数越多,耗费计算资源将越大,运行结果如图3-3所示。

图3-3 Subscriber_a

3)运行Publisher,Publisher将向Subscriber发布2个主题,第一个topic1消息为Hello World,第二个topic1消息为Coder。第二个主题topic2消息为Day Day Up。注意Publisher发送n个主题,Subscriber中就应该有n个subscrib函数进行接收。 结果如图3-4所示

图3-4 Publisher

运行Publisher开始投递消息,投递完成后Publisher退出,aaa订阅者的窗口显示如图3-5所示。

图3-5 Subscriber_a

完整demo源码如下:
//ParkServerDemo

package rmi.rmi;  
  
import rmi.fourinone.LogUtil;  
import rmi.fourinone.ParkService;  
import rmi.fourinone.ParkServiceImpl;  
  
import java.net.MalformedURLException;  
import java.rmi.AlreadyBoundException;  
import java.rmi.Naming;  
import java.rmi.RemoteException;  
import java.rmi.registry.LocateRegistry;  
  
public class ParkServerDemo {  
    public static void main(String[] args) {  
        try {  
            ParkService parkService = new ParkServiceImpl();  
            LocateRegistry.createRegistry(9985);  
  
            try {  
                Naming.bind("rmi://localhost:9985/ParkService", parkService);  
                LogUtil.info("[ParkServerDemo]", "[main]", "Start server!");  
            } catch (MalformedURLException e) {  
                e.printStackTrace();  
            } catch (AlreadyBoundException e) {  
                e.printStackTrace();  
            }  
        } catch (RemoteException e) {  
            e.printStackTrace();  
        }  
    }  
}  

//Subscriber

package rmi.rmi;  
  
import rmi.fourinone.*;  
  
import java.io.Serializable;  
import java.rmi.RemoteException;  
import java.util.ArrayList;  
import java.util.Iterator;  
  
import static rmi.rmi.Receiver.getMsg;  
import static rmi.rmi.Utils.getPark;  
import static rmi.rmi.Utils.toBytes;  
import static rmi.fourinone.ParkObjValue.getDomainnodekey;  
  
public class Subscriber implements LastestListener {  
    public static ParkService parkService = getPark();  
    @Override  
    public boolean happenLastest(LastestEvent le) {  
        ObjValue ov = (ObjValue)le.getSource();  
  
        try{  
     //       System.out.println(ov.keySet().size());  
            for(Iterator iter = ov.keySet().iterator(); iter.hasNext();){  
//                System.out.println(ov.keySet().size());  
                String curkey = (String)iter.next();  
                Object objArr = getMsg(ov, curkey);  
                ArrayList arr = (ArrayList) objArr;  
                System.out.println("published message:"+arr);  
                ObjValue newob = parkService.update(curkey.split("\\.")[0], curkey.split("\\.")[1],  
                        toBytes((Serializable) new ArrayList()));  
                le.setSource(newob);  
            }  
        }catch (RemoteException e){  
            e.printStackTrace();  
        }  
  
        return false;  
    }  
    public static void subscrib(String topic, String subscribeName, LastestListener lister)  
    {  
        ArrayList arr = new ArrayList();  
        try {  
            ObjValue objValue = parkService.create(topic, subscribeName, toBytes((Serializable) arr));  
            addLastestListener(topic, subscribeName, objValue, lister);  
        }catch (RemoteException e){  
            e.printStackTrace();  
        }  
    }  
  
    public static void addLastestListener(String domain, String node, ObjValue ov, LastestListener liser) {  
        final String dm = domain;  
        final String nd = node;  
        final ObjValue oov = ov;  
        final LastestListener lis = liser;  
        new AsyncExector(){  
            public void task(){  
                try{  
                    ObjValue oldov = oov;  
                    while(true){  
                        String sVersion = oldov.get(getDomainnodekey(domain, node) + "__version").toString();  
                        long vid = oldov!=null?Long.parseLong(sVersion):0l;  
                        ObjValue newov = parkService.getLastest(dm, nd, vid);  
                        if(newov != null){  
                            LogUtil.fine("[Park]","[Trim LastestEvent]","[obj]");  
                            LastestEvent le = new LastestEvent(newov);  
                            if(lis.happenLastest(le))  
                                break;  
                            oldov = (ObjValue) le.getSource();  
                        }  
                    }  
                }catch(Exception e){  
                    LogUtil.info("[Park]","[addLastestListener]",e);  
                }  
            }  
        }.run();  
    }  
    public static void main(String[] args)  
    {  
        subscrib("topic1", args[0], new Subscriber());  
        subscrib("topic2", args[0], new Subscriber());  
    }  
}  

//Publisher

package rmi.rmi;  
  
import rmi.fourinone.ObjValue;  
import rmi.fourinone.ParkService;  
  
import java.io.Serializable;  
import java.rmi.RemoteException;  
import java.util.ArrayList;  
  
import static rmi.rmi.Receiver.getMsg;  
import static rmi.rmi.Utils.getPark;  
import static rmi.rmi.Utils.toBytes;  
  
public class Publisher {  
    public static ParkService parkService = getPark();  
  
    public static void publish(String topic, Object obj)  
    {  
        try {  
            ObjValue objValue = parkService.get(topic, null);  
            ObjValue nodes = objValue.getWidely(topic + "..*");  
            ArrayList<String> nodeNames = nodes.getObjNames();  
            if (objValue != null) {  
                for (String nodeName : nodeNames) {  
                    if (nodeName.contains("version"))  
                        continue;  
                    Object objArr = getMsg(objValue, nodeName);  
                    ArrayList arr = (ArrayList) objArr;  
                    arr.add(obj);  
                    parkService.update(nodeName.split("\\.")[0], nodeName.split("\\.")[1], toBytes((Serializable) arr));  
                }  
            }  
        }catch (RemoteException e){  
            e.printStackTrace();  
        }catch (NullPointerException e){  
            e.printStackTrace();  
        }  
  
    }  
    public static void main(String[] args)  
    {  
        //发布消息  
        publish("topic1", "Hello World");  
        publish("topic1", "Coder");  
        publish("topic2", "Day Day Up");  
    }  
  
}  

四、MQ的使用场景自我总结

MQ有以下几点特性:

  • 解耦
    在项目启动之初来预测将来项目会碰到什么需求,是极其困难的。消息系统在处理过程中间插入了一个隐含的、基于数据的接口层,两边的处理过程都要实现这一接口。这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束。
  • 冗余
    有些情况下,处理数据的过程会失败。除非数据被持久化,否则将造成丢失。消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险。许多消息队列所采用的”插入-获取-删除”范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
  • 扩展性
    因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。不需要改变代码、不需要调节参数。扩展就像调大电力按钮一样简单。
  • 灵活性
    在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常见;如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  • 可恢复性
    系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
  • 顺序保证
    在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。Kafka保证一个Partition内的消息的有序性。
  • 缓冲
    在任何重要的系统中,都会有需要不同的处理时间的元素。例如,加载一张图片比应用过滤器花费更少的时间。消息队列通过一个缓冲层来帮助任务最高效率的执行———写入队列的处理会尽可能的快速。该缓冲有助于控制和优化数据流经过系统的速度。
  • 异步通信
    很多时候,用户不想也不需要立即处理消息。消息队列提供了异步处理机制,允许用户把一个消息放入队列,但并不立即处理它。想向队列中放入多少消息就放多少,然后在需要的时候再去处理它们。

    根据以上MQ的优势,可以总结出MQ主要在以下场景使用
    1. 进程间通讯和系统间的消息通知,比如在分布式系统中。
    2. 解耦,比如像公司有许多开发团队,每个团队负责业务的不同模块,各个开发团队可以使用MQ来通信。
    3. 在一些高并发场景下,使用MQ的异步特性。

参考资料

[1]独立网页(狗蛋儿_312)“JAVA消息(第一篇)”(2018.5.30 ), [Online]Available:https://blog.csdn.net/weixin_37352094/article/details/80500202
[2]大规模分布式系统架构与设计实战.彭渊.北京:机械工业出版社,2014.1
[3]独立网页(HD243608836)“消息队列mq总结(2018.5.6 ), [Online]Available: https://blog.csdn.net/HD243608836/article/details/80217591
[4]独立网页(过眼浮云~~)Java常用消息队列原理介绍及性能对比”(2017.11.27)
[Online]Available: https://blog.csdn.net/songfeihu0810232/article/details/78648706
[5]独立网页(高尔夫golf)“关于消息队列的使用”(2016.8.15)[Online]Available: https://blog.csdn.net/konglongaa/article/details/52208273
[6]独立网页(shero_zsmj)阿里四不像——分布式核心技术框架 Fourinone”(2016.8.22)[Online]Available:https://blog.csdn.net/songfeihu0810232/article/details/78648706

项目链接

github项目链接
CSDN积分下载链接

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
  Fourinone(中文名字“四不像”)是一个四合一分布式计算框架,在写这个框架之前,我对分布式计算进行了长时间的思考,也看了老外写的其他开源框架,当我们把复杂的hadoop当作一门学科学习时,似乎忘记了我们想解决问题的初衷:我们仅仅是想写个程序把几台甚至更多的机器一起用起来计算,把更多的cpu和内存利用上,来解决我们数量大和计算复杂的问题,当然这个过程中要考虑到分布式的协同和故障处理。如果仅仅是为了实现这个简单的初衷,为什么一切会那么复杂,我觉的自己可以写一个更简单的东西,它不需要过度设计,只需要看上去更酷一点,更小巧一点,功能更强一点。于是我将自己对分布式的理解融入到这个框架中,考虑到底层实现技术的相似性,我将Hadoop,Zookeeper,MQ,分布式缓存四大主要的分布式计算功能合为一个框架内,对复杂的分布式计算应用进行了大量简化和归纳。   首先,对分布式协同方面,它实现了Zookeeper所有的功能,并且做了很多改进,包括简化Zookeeper的树型结构,用domain/node两层结构取代,简化Watch回调多线程等待编程模型,用更直观的容易保证业务逻辑完整性的内容变化事件以及状态轮循取代,Zookeeper只能存储信息不大于1M的内容,Fourinone超过1M的内容会以内存隐射文件存储,增强了它的存储功能,简化了Zookeeper的ACL权限功能,用更为程序员熟悉rw风格取代,简化了Zookeeper的临时节点和序列节点等类型,取代为在创建节点时是否指定保持心跳,心跳断掉时节点会自动删除。Fourinone是高可用的,没有单点问题,可以有任意多个复本,它的复制不是定时而是基于内容变更复制,有更高的性能,Fourinone实现了领导者选举算法(但不是Paxos),在领导者服务器宕机情况下,会自动不延时的将请求切换到备份服务器上,选举出新的领导者进行服务,这个过程中,心跳节点仍然能保持健壮的稳定性,迅速跟新的领导者保持心跳连接。基于Fourinone可以轻松实现分布式配置信息,集群管理,故障节点检测,分布式锁,以及淘宝configserver等等协同功能。   其次, Fourinone可以提供完整的分布式缓存功能。如果对一个中小型的互联网或者企业应用,仅仅利用domain/node进行k/v的存储即可,因为domain/node都是内存操作而且读写锁分离,同时拥有复制备份,完全满足缓存的高性能与可靠性。对于大型互联网应用,高峰访问量上百万的并发读写吞吐量,会超出单台服务器的承受力,Fourinone提供了fa?ade的解决方案去解决大集群的分布式缓存,利用硬件负载均衡路由到一组fa?ade服务器上,fa?ade可以自动为缓存内容生成key,并根据key准确找到散落在背后的缓存集群的具体哪台服务器,当缓存服务器的容量到达限制时,可以自由扩容,不需要成倍扩容,因为fa?ade的算法会登记服务器扩容时间版本,并将key智能的跟这个时间匹配,这样在扩容后还能准确找到之前分配到的服务器。另外,基于Fourinone可以轻松实现web应用的session功能,只需要将生成的key写入客户端cookie即可。   Fourinone对于分布式大数据量并行计算的解决方案不同于复杂的hadoop,它不像hadoop的中间计算结果依赖于hdfs,它使用不同于map/reduce的全新设计模式解决问题。Fourinone有“包工头”,“农民工”,“手工仓库”的几个核心概念。“农民工”为一个计算节点,可以部署在多个机器,它由开发者自由实现,计算时,“农民工”到“手工仓库”获取输入资源,再将计算结果放回“手工仓库”返回给“包工头”。“包工头”负责承包一个复杂项目的一部分,可以理解为一个分配任务和调度程序,它由开发者自己实现,开发者可以自由控制调度过程,比如按照“农民工”的数量将源数据切分成多少份,然后远程分配给“农民工”节点进行计算处理,它处理完的中间结果数据不限制保存在hdfs里,而可以自由控制保存在分布式缓存、数据库、分布式文件里。如果需要结果数据的合并,可以新建立一个“包工头”的任务分配进行完成。多个“包工头”之间进行责任链式处理。总的来说,是将大数据的复杂分布式计算,设计为一个链式的多“包工头”环节去处理,每个环节包括利用多台“农民工”机器进行并行计算,无论是拆分计算任务还是合并结果,都可以设计为一个单独的“包工头”环节。这样做的好处是,开发者有更大能力去深入控制并行计算的过程,去保持使用并行计算实现业务逻辑的完整性,而且对各种不同类型的并行计算场景也能灵活处理,不会因为某些特殊场景被map/reduce的框架限制住思维,并且链式的每个环节也方便进行监控过程。   Fourinone也可以当成简单的mq来使用,将domain视为mq队列,每个node为一个队列消息,监控domain的变化事件来获取队列消息。也可以将domain视为订阅主题,将每个订阅者注册到domain的node上,发布者将消息逐一更新每个node,订阅者监控每个属于自己的node的变化事件获取订阅消息,收到后删除内容等待下一个消息。但是Fourinone实现JMS的规范,不提供JMS的消息确认和消息过滤等特殊功能,不过开发者可以基于Fourinone自己去扩充这些功能,包括mq集群,利用一个独立的domain/node建立队列或者主题的key隐射,再仿照上面分布式缓存的智能根据key定位服务器的做法实现集群管理。   Fourinone整体代码短小精悍,跟Hadoop, Zookeeper, Memcache, ActiveMq等开源产品代码上没有任何相似性,不需要任何依赖,引用一个jar包就可以嵌入式使用,良好支持window环境,可以在一台机器上模拟分布式环境,更方便开发。   开发包里自带了一系列傻瓜上手demo,包括分布式计算、统一配置管理、集群管理、分布式锁、分布式缓存、MQ等方面, 每个demo均控制在少许行代码内,但是涵盖了Fourinone主要的功能,方便大家快速理解并掌握。   Fourinone 2.0新增功能:   Fourinone2.0提供了一个4合1分布式框架和简单易用的编程api,实现对多台计算机cpu,内存,硬盘的统一利用,从而获取到强大计算能力去解决复杂问题。Fourinone框架提供了一系列并行计算模式(农民工/包工头/职介绍/手工仓库)用于利用多机多核cpu的计算能力;提供完整的分布式缓存和小型缓存用于利用多机内存能力;提供像操作本地文件一样操作远程文件(访问,并行读写,拆分,排它,复制,解析,事务等)用于利用多机硬盘存储能力;由于多计算机物理上独立,Fourinone框架也提供完整的分布式协同和锁以及简化MQ功能,用于实现多机的协作和通讯。   一、提供了对分布式文件的便利操作, 将集群中所有机器的硬盘资源利用起来,通过统一的fttp文件路径访问,如:   windows:fttp://v020138.sqa.cm4/d:/data/a.log   linux:fttp://v020138.sqa.cm4/home/user/a.log   比如以这样的方式读取远程文件:   FttpAdapter fa = FttpAdapter("fttp://v020138.sqa.cm4/home/log/a.log");   fa.getFttpReader().readAll();   提供对集群文件的操作支持,包括:   1、元数据访问,添加删除,按块拆分, 高性能并行读写,排他读写(按文件部分内容锁定),随机读写,集群复制等   2、对集群文件的解析支持(包括按行,按分割符,按最后标识读取)   3、对整形数据的高性能读写支持(ArrayInt比ArrayList存的更多更快)   4、两阶段提交和事务补偿处理   5、自带一个集群文件浏览器,可以查看集群所有硬盘上的文件(不同于hadoop的namenode,没有单点问题和容量限制)   总的来说, 将集群看做一个操作系统,像操作本地文件一样操作远程文件。   但是fourinone并不提供一个分布式存储系统,比如文件数据的导入导出、拆分存储、负载均衡,备份容灾等存储功能,不过开发人员可以利用这些api去设计和实现这些功能,用来满足自己的特定需求。   二、自动化class和jar包部署   class和jar包只需放在工头机器上, 各工人机器会自动获取并执行,兼容操作系统,不需要进行安全密钥复杂配置   三、网络波动状况下的策略处理,设置抢救期,抢救期内网络稳定下来不判定结点死亡   本软件由www.gg265.net www.fmrpg.com站长提供
淘宝Fourinone2.0提供了一个4合1分布式框架和简单易用的编程API,实现对多台计算机CPU,内存,硬盘的统一利用,从而获取到强大计算能力去解决复杂问题。Fourinone框架提供了一系列并行计算模式(农民工/包工头/职介绍/手工仓库)用于利用多机多核CPU的计算能力;提供完整的分布式缓存和小型缓存用于利用多机内存能力;提供像操作本地文件一样操作远程文件(访问,并行读写,拆分,排它,复制,解析,事务等)用于利用多机硬盘存储能力;由于多计算机物理上独立,Fourinone框架也提供完整的分布式协同和锁以及简化MQ功能,用于实现多机的协作和通讯。 fourinone-2.05.28\fourinone2.0 .................\............\config.xml .................\............\fourinone-2.05.28-src.jar .................\............\fourinone-2.05.28.jar .................\............\指南和demo .................\............\..........\MQ demo .................\............\..........\.......\MQ demo.txt .................\............\..........\.......\ParkServerDemo.java .................\............\..........\.......\Publisher.java .................\............\..........\.......\Receiver.java .................\............\..........\.......\Sender.java .................\............\..........\.......\Subscriber.java .................\............\..........\WordCount .................\............\..........\.........\inputdata.txt .................\............\..........\.........\ParkServerDemo.java .................\............\..........\.........\WordCount.txt .................\............\..........\.........\WordcountCT.java .................\............\..........\.........\WordcountWK.java .................\............\..........\分布式文件访问和操作demo .................\............\..........\........................\FttpBatchWriteReadDemo.java .................\............\..........\........................\FttpCopyDemo.java .................\............\..........\........................\FttpMulCopyDemo.java .................\............\..........\........................\FttpMulWriteReadDemo.java .................\............\..........\........................\FttpOperateDemo.java .................\............\..........\........................\FttpParseDemo.java .................\............\..........\........................\FttpRo

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值