Flume-ng TaildirSource 填坑

Flume-ng 1.7.0中增加了TaildirSource,可以监控目录中文件的变化自动读取文件内容。

不过实际应用时发现几个问题:

1, 不支持windows系统。

2, 如果文件改名了,文件内容会被重复读取。

3, 文件编码,只支持UTF-8格式。 

针对上面问题作了如下改造:

1.不支持windows系统是因为 ReliableTaildirEventReader.getInode(File file) 方法中的代码依赖linux系统。

  private long getInode(File file) throws IOException {
    long inode = (long) Files.getAttribute(file.toPath(), "unix:ino");
    return inode;
  }

window系统下的JDK不支持:Files.getAttribute(file.toPath(), "unix:ino")。不支持咋办?当然是想办法实现一个啊。Linux系统下可以使用inode跟踪一个文件,文件被重命名inode不会改变。Windows的NTFS系统有类似的东东,具体实现方式如下:

JNA依赖:
<dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna</artifactId>
           <version>4.2.2</version>
       </dependency>
       <dependency>
           <groupId>net.java.dev.jna</groupId>
           <artifactId>jna-platform</artifactId>
           <version>4.2.2</version>
       </dependency> 
       
package my.test.flumesource.util;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinBase.FILETIME;
import com.sun.jna.platform.win32.WinDef.DWORD;
import com.sun.jna.platform.win32.WinNT.HANDLE;
import com.sun.jna.win32.StdCallLibrary;
import com.sun.jna.win32.W32APIFunctionMapper;
import com.sun.jna.win32.W32APITypeMapper;

public interface Kernel32 extends StdCallLibrary {
	final static Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
		private static final long serialVersionUID = 1L;
		{
			put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
			put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
		}
	};

	public Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("Kernel32",
			Kernel32.class, WIN32API_OPTIONS);

	public int GetLastError();

	public class BY_HANDLE_FILE_INFORMATION extends Structure {
		public DWORD dwFileAttributes;
		public FILETIME ftCreationTime;
		public FILETIME ftLastAccessTime;
		public FILETIME ftLastWriteTime;
		public DWORD dwVolumeSerialNumber;
		public DWORD nFileSizeHigh;
		public DWORD nFileSizeLow;
		public DWORD nNumberOfLinks;
		public DWORD nFileIndexHigh;
		public DWORD nFileIndexLow;

		public static class ByReference extends BY_HANDLE_FILE_INFORMATION
				implements Structure.ByReference {

		};

		public static class ByValue extends BY_HANDLE_FILE_INFORMATION
				implements Structure.ByValue {

		}

		@Override
		protected List getFieldOrder() {
			List<String> fields = new ArrayList<String>();
			fields.addAll(Arrays.asList(new String[] { "dwFileAttributes",
					"ftCreationTime", "ftLastAccessTime", "ftLastWriteTime",
					"dwVolumeSerialNumber", "nFileSizeHigh", "nFileSizeLow",
					"nNumberOfLinks", "nFileIndexHigh", "nFileIndexLow" }));
			return fields;

		};
	};

	boolean GetFileInformationByHandle(HANDLE hFile,
			BY_HANDLE_FILE_INFORMATION lpFileInformation);
}

package my.test.flumesource.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinBase;
import com.sun.jna.platform.win32.WinNT.HANDLE;

public class WinFileUtil {
	private static Logger logger = LoggerFactory.getLogger(WinFileUtil.class);

	public static String getFileId(String filepath) {

		final int FILE_SHARE_READ = (0x00000001);
		final int OPEN_EXISTING = (3);
		final int GENERIC_READ = (0x80000000);
		final int FILE_ATTRIBUTE_ARCHIVE = (0x20);

		WinBase.SECURITY_ATTRIBUTES attr = null;
		my.test.flumesource.util.Kernel32.BY_HANDLE_FILE_INFORMATION lpFileInformation = new my.test.flumesource.util.Kernel32.BY_HANDLE_FILE_INFORMATION();
		HANDLE hFile = null;

		hFile = Kernel32.INSTANCE.CreateFile(filepath, 0,
				FILE_SHARE_READ, attr, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE,
				null);
		String ret = "0";
		if (Kernel32.INSTANCE.GetLastError() == 0) {

			my.test.flumesource.util.Kernel32.INSTANCE
					.GetFileInformationByHandle(hFile, lpFileInformation);

			ret = lpFileInformation.dwVolumeSerialNumber.toString()
					+ lpFileInformation.nFileIndexLow.toString();

			Kernel32.INSTANCE.CloseHandle(hFile);

			if (Kernel32.INSTANCE.GetLastError() == 0) {
				logger.debug("inode:" + ret);
				return ret;
			} else {
				logger.error("关闭文件发生错误:{}", filepath);
				throw new RuntimeException("关闭文件发生错误:" + filepath);
			}
		} else {
			if (hFile != null) {
				Kernel32.INSTANCE.CloseHandle(hFile);
			}
			logger.error("打开文件发生错误:{}", filepath);
			throw new RuntimeException("打开文件发生错误:" + filepath);
		}

	}
}

问题2,3不详细写了。 源码有空了传到git上。 



你可能感兴趣的:(Flume-ng TaildirSource 填坑)