概述
云技术:透过网络将庞大的计算处理程序自动分拆成无数个较小的子程序,再交由多部服务器所组成的庞大系统经搜寻、计算分析之后将处理结果回传给用户。透过这项技术,网络服务提供者可以在数秒之内,达成处理数以千万计甚至亿计的信息,达到和“超级计算机”同样强大效能的网络服务。
分布式多文件自平衡云传输系统: 当用户通过网络下载资源时,系统将筛选多个发送端向接收端发送不同的资源片段;接收端成功接收资源后,也将成为系统中的一个发送结点;因此,当系统刚开启时,只有资源根服务器拥有所有资源,但随着时间的推移,不同用户下载了不同的资源,资源发送端会逐渐增加,资源根服务器的压力也会逐渐减小。
基本思想
- 资源管理中心负责维护一张Map(key为资源唯一标识,value为对应的资源提供者列表),资源管理中心还通过心跳检测来剔除已经宕机的资源提供者;资源管理中心支持资源与资源提供者的注册和注销;当资源请求者申请某资源时,资源管理中心将拥有该资源的所有资源提供者信息发送给对应的资源请求者;
- 资源提供者上线后先扫描本地资源,然后将拥有的资源注册到资源管理中心,资源根服务器(由app开发者部署的服务器,并非用户端)也作为普通资源提供者向资源管理中心注册信息;当资源请求者向资源提供者请求资源时,资源提供者连接资源请求者临时建立的服务器并发送请求的资源;
- 资源请求者上线后先从资源管理中心获取全部的资源信息列表,并扫描对比本地拥有的资源,由用户决定下载哪个资源,再向资源管理中心申请资源提供者列表,通过资源请求端的结点选择策略和资源分配策略后,向选择的发送结点分配任务,并开启临时服务器接收资源;当资源接收完毕后,再向资源管理中心注册本端拥有了该资源,本端成为一个拥有该资源的资源提供者;
从上述来看,除了资源根服务器和资源管理中心以外,别的网络结点都包含资源提供者和资源请求者,这些用户结点不仅可以作为资源提供者发送资源,也可以作为资源请求者下载资源;
本系统基于博主自己开发的RPC通信框架和服务发现框架,若有兴趣可看RPC与RMI框架、手写微服务框架之服务发现。
网络模块
Transmission
package com.dl.multi_file.netWork;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import com.dl.multi_file.resource.SectionInfo;
/**
* 传输层<br>
* 1、提供数据片段的接收与发送;<br>
* 2、以分段形式发送,默认为8kb/次;<br>
* 3、数据发送前,先发送描述对象;
* @author dl
*
*/
public class Transmission {
public static final int DEFAULT_BUFFFER_LENGTH = 1 << 13;
private int sendLength;
public Transmission() {
sendLength = DEFAULT_BUFFFER_LENGTH;
}
public void setSendLength(int sendLength) {
this.sendLength = sendLength;
}
/**
* 接收服务器发送的字节流数据;<br>
* 在数据发送前会先发送描述对象,当对象为null表示发送完;
* @param dis
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public SectionInfo recvfrom(DataInputStream dis) throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(dis);
SectionInfo sectionInfo = (SectionInfo) ois.readObject();
return sectionInfo;
}
/**
* 读取字节流,读取长度由参数决定;<br>
* 由于发送端发送的数据可能过长,因此采用分段读取的方式;
* @param dis
* @param size
* @return
* @throws IOException
* @throws ClassNotFoundException
*/
public byte[] recvfrom(DataInputStream dis, int size) throws IOException, ClassNotFoundException {
byte[] data = new byte[size];
int restLen = size;
int readLen;
int len;
int offset = 0;
while (restLen > 0) {
len = restLen > sendLength ? sendLength : restLen;
readLen = dis.read(data, offset, len);
restLen -= readLen;
offset += readLen;
}
return data;
}
/**
* 发送描述对象
* @param dos
* @param sectionInfo
* @throws IOException
*/
public void sendObject(DataOutputStream dos, SectionInfo sectionInfo) throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(dos);
oos.writeObject(sectionInfo);
}
/**
* 发送字节数据
* @param dos
* @param data
* @throws IOException
*/
public void sendData(DataOutputStream dos, byte[] datas) throws IOException {
dos.write(datas);
}
}
NetNode
package com.dl.sd.netWork;
/**
* 结点信息类<br>
* 1、保存网络结点的ip和port;<br>
* 2、保存结点的发送次数和正在发送的个数,为策略做准备;<br>
* @author dl
*
*/
public class NetNode implements INetNode {
private String ip;
private int port;
private int sendCount;
private int sendingCount;
public NetNode() {
}
public NetNode(String ip, int port) {
this.ip = ip;
this.port = port;
}
public String getIp() {
return ip;
}
public void setIp(String ip) {
this.ip = ip;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public int getSendCount() {
return sendCount;
}
public void setSendCount(int sendCount) {
this.sendCount = sendCount;
}
public int getSendingCount() {
return sendingCount;
}
public void setSendingCount(int sendingCount) {
this.sendingCount = sendingCount;
}
public void accomplishTask() {
this.sendCount++;
this.sendingCount--;
}
public void startNewTask() {
this.sendingCount++;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((ip == null) ? 0 : ip.hashCode());
result = prime * result + port;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
NetNode other = (NetNode) obj;
if (ip == null) {
if (other.ip != null)
return false;
} else if (!ip.equals(other.ip))
return false;
if (port != other.port)
return false;
return true;
}
@Override
public String toString() {
return "ip: " + ip + "port: " + port;
}
}
资源模块
LocalResources
package com.dl.multi_file.resource;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.parser_reflect.util.PropertiesParser;
/**
* 扫描文件,读写文件的基本类<br>
* 1、给定默认根路径,也可通过properties文件配置;<br>
* 2、根据根路径读取路径下的文件,将扫描到的资源保存在map中;<br>
* 3、提供RandomAccessFile的随机文件读写;
* @author dl
*
*/
public class LocalResources {
/**
* 默认根路径
*/
public static final String DEFAULT_PATH = "N:\\LocalResources";
// 以文件名、起始地址与文件长字符串为键,以文件描述对象为值
private static final Map<String, SectionInfo> systemMap = new HashMap<>();
/**
* 根路径
*/
private static String rootPath;
public LocalResources() {
rootPath = DEFAULT_PATH;
}
public static void setRootPath(String rootPath) {
LocalResources.rootPath = rootPath;
}
public void readConfig(String path) {
PropertiesParser.load(path);
String rootPath = PropertiesParser.findElement("rootPath");
if (rootPath != null && !rootPath.equals("")) {
LocalResources.rootPath = rootPath;
}
}
/**
* 根据根目录读取本地资源;
*/
public Map<String, SectionInfo> scanLocalResource() {
File rootFile = new File(rootPath);
scanLocalResource(rootFile);
return new HashMap<>(systemMap);
}
/**
* 带有递归的文件扫描;
* @param rootFile
*/
private void scanLocalResource(File rootFile) {
File[] files = rootFile.listFiles();
for (File file : files) {
if (file.isFile()) {
String filePath = file.getAbsolutePath();
int len = rootPath.length();
String path = filePath.substring(len);
int length = (int) file.length();
SectionInfo info = new SectionInfo(path, 0, length);
systemMap.put(info.toString(), info);
} else {
scanLocalResource(file);
}
}
}
/**
* 返回map的键集,以便上层使用
* @return
*/
public List<String> resourceList() {
return new ArrayList<>(systemMap.keySet());
}
public SectionInfo getSection(String resourceHandle) {
return systemMap.get(resourceHandle);
}
/**
* 根据文件名来获取文件完整信息的字符串;
* @param path
* @return
*/
public String getIntactHandle(String path) {
String[] str = path.split(":");
Collection<String> handles = systemMap.keySet();
for (String string : handles) {
if (string.split(":")[0].equals(str[0])) {
return string;
}
}
return null;
}
/**
* 根据文件名读取指定偏移量和长度的字节数据;
* @param name
* @param offset
* @param size
*/
public byte[] readFromLocal(SectionInfo sectionInfo) {
String path = sectionInfo.getFileName();
int offset = sectionInfo.getOffset();
int size = sectionInfo.getSize();
String fileName = rootPath + "\\" + path;
File file = new File(fileName);
int length = (int) file.length();
byte[] datas = new byte[length];
RandomAccessFile randomAccessFile = null;
try {
randomAccessFile = new RandomAccessFile(file, "r");
randomAccessFile.read(datas, offset, size);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (randomAccessFile != null) {
try {
randomAccessFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return datas;
}
/**
* 将字节数据写入文件,由读写对象、偏移地址和长度决定;
* @param randomAccessFile
* @param datas
* @param offset
* @param size
*/
public void writeInLocal(RandomAccessFile randomAccessFile
, byte[] datas, int offset, int size) {
try {
randomAccessFile.write(datas, offset, size);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 提供带有根路径的文件读写对象;
* @param name
* @return
*/
public RandomAccessFile getRandomAccessFile(String path) {
String fileName = rootPath + "\\" + path;
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "rw");
return randomAccessFile;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 提供带有根路径的文件只读对象;
* @param path
* @return
*/
public RandomAccessFile getRandomAccessFileOnlyRead(String path) {
String fileName = rootPath + "\\" + path;
try {
RandomAccessFile randomAccessFile = new RandomAccessFile(fileName, "r");
return randomAccessFile;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 提供增加本地资源的方法
* @param sectionInfo
*/
public void addResource(SectionInfo sectionInfo) {
systemMap.put(sectionInfo.toString(), sectionInfo);
}
}
SectionInfo
package com.dl.multi_file.resource;
import java.io.Serializable;
/**
* 文件描述类<br>
* 1、可描述整个文件,也可描述片段信息;<br>
* 2、可序列化和反序列化,为对象传输奠定基础;
* @author dl
*
*/
public class SectionInfo implements Serializable {
private static final long serialVersionUID = 3417961581440292164L;
private String fileName;
private int offset;
private int size;
public SectionInfo() {
}
/**
* 将符合协议的字符串解析为SectionInfo对象
* @param sectionInfoStr
*/
public SectionInfo(String sectionInfoStr) {
String[] info = sectionInfoStr.split(":");
if (info.length == 3) {
this.fileName = info[0];
this.offset = Integer.valueOf(info[1]);
this.size = Integer.valueOf(info[2]);
}
}
public SectionInfo(String fileName, int offset, int size) {
this.fileName = fileName;
this.offset = offset;
this.size = size;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public int getOffset() {
return offset;
}
public void setOffset(int offset) {
this.offset = offset;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public boolean isRight(int offset, int size) {
return this.offset <= offset && (this.offset + this.size) >= (offset + size);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((fileName == null) ? 0 : fileName.hashCode());
result = prime * result + offset;
result = prime * result + size;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SectionInfo other = (SectionInfo) obj;
if (fileName == null) {
if (other.fileName != null)
return false;
} else if (!fileName.equals(other.fileName))
return false;
if (offset != other.offset)
return false;
if (size != other.size)
return false;
return true;
}
/**
* 此方法产生的字符串将用作资源管理中心的map中的键;<br>
* 通过此字符串进行资源请求;在一套资源体系中一定是唯一的;
*/
@Override
public String toString() {
if (fileName