需求:一个功能,接受url形式如:http://localhost:8080/save_log/SaveLog?dir1=dir1&dir2=dir2&dir3=dir3&filename=filename35&content=1
dir就是文件路径,filename是文件名,content是文件内容,把这个请求的内容到对应的路径的文件里,根据url中的参数,将url里的信息保存到文件里,要求可以支持100次/s的存储效率。
由于servlet本身就是并发处理的,因此我们只需要处理好对应的调用函数就可以了。
考虑到对效率有要求,因此使用一个StringBuffer做缓冲区,将数据保存在内存中,然后定时将数据写入文件。基本思路是先将文件名存储在内存中,将url的filename与内存中的filename相比较,如果匹配,则向StringBuffer中写入content内容,如果不匹配,则直接创建文件,写入content内容。使用StringBuffer的length作为判断写入文件的条件。
以下是实现代码
servlet,简单的实现了一个对url的控制,便于测试
package savelog.packagename.com;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/** * Servlet implementation class SaveLog */
@WebServlet("/SaveLog")
public class SaveLog extends HttpServlet {
private static final long serialVersionUID = 1L;
/** * @see HttpServlet#HttpServlet() */
public SaveLog() {
super();
// TODO Auto-generated constructor stub
}
/** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
request.setCharacterEncoding("UTF-8");
PrintWriter writer = response.getWriter();
writer.println("DIR1:"+request.getParameter("dir1"));
writer.println("DIR2:"+request.getParameter("dir2"));
writer.println("DIR3:"+request.getParameter("dir3"));
writer.println("filename:"+request.getParameter("filename"));
writer.println("content:"+request.getParameter("content"));
writer.println(request.getCharacterEncoding());
if(request.getParameter("dir1")==null){
writer.println("dir1 muxt exist");
return;
}
//String path2=request.getParameter("dir1")+"\\"+request.getParameter("dir2")+"\\"+request.getParameter("dir3");
StringBuilder path=new StringBuilder();
path.append(request.getParameter("dir1"));
if(request.getParameter("dir2")!=null&&!request.getParameter("dir2").equals(""))
path.append("\\"+request.getParameter("dir2"));
if(request.getParameter("dir3")!=null&&!request.getParameter("dir3").equals(""))
path.append("\\"+request.getParameter("dir3"));
String filename=request.getParameter("filename");
String temp=request.getParameter("content");
String content=new String(temp.getBytes("ISO-8859-1"),"UTF-8");
writer.println("Path:"+path);
writer.println("savePath:"+Data.saveLog(path.toString(), filename, content));
}
/** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response) */
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO Auto-generated method stub
doGet(request, response);
}
}
Global类,定义全局变量,保存在内存中。
package savelog.packagename.com;
public class Global {
/*写日志缓冲区*/
public static StringBuffer stringBuffer=new StringBuffer();
/*filename标志*/
public static String filename=new String("");
/*根路径*/
public static final String rootPath = "e:\\logroot";
}
Date类,业务逻辑类,实现了存储的业务逻辑
package savelog.packagename.com;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class Data {
/** * 访问方法(同步) * @param path * @param filename * @param content * @return */
public static synchronized String saveLog(String path, String filename, String content){
if(Global.filename.equals("")){
//第一次访问,创建文件
Global.filename=filename;
String savePath=createFile(path,filename,content);
Global.stringBuffer.setLength(0);
return savePath;
}
if(Global.filename.equals(filename)){
//追加入已有文件
Global.stringBuffer.append(getTime()+"\r\n"+content+"\r\n");
if (Global.stringBuffer.length()>10000) {
writelog(path, Global.filename, Global.stringBuffer.toString());
Global.stringBuffer.setLength(0);
}
return Global.stringBuffer.toString();
}else {
//更换文件名
if(Global.stringBuffer.length()>0){
writelog(path, Global.filename, Global.stringBuffer.toString());
Global.stringBuffer.setLength(0);
}
String savePath=createFile(path,filename,content);
Global.filename=filename;
return savePath;
}
}
/** * 创建新的文件路径,并写入当前 content * @param path * @throws Throwable */
private static String createFile(String path,String filename,String content){
String saveDirPath=Global.rootPath+"\\"+path+"\\"+getDate();
File Dirs = new File(saveDirPath);
Dirs.mkdirs();
String saveFilePath=saveDirPath+"\\"+filename+".log";
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(saveFilePath, true),"UTF-8"));
writer.write(getTime()+"\r\n"+content+"\r\n");
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
return saveFilePath;
}
/** * 将StringBuffer中的内容写入到file * @param path * @param content */
private static void writelog(String path,String filename,String content){
BufferedWriter writer = null;
String saveFilePath=Global.rootPath+"\\"+path+"\\"+getDate()+"\\"+filename+".log";
// 处理运行中文件被删除的情况
// if(!new File(saveFilePath).exists())
// new File(Global.rootPath+"\\"+path+"\\"+getDate()).mkdirs();
try {
writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(saveFilePath, true),"UTF-8"));
writer.write(content);
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/** * 获得当前日期 * @return 例:2015-10-11 */
private static String getDate(){
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
String dateStr = "" + year + "-" + month + "-" + day;
return dateStr;
}
/** * 获得当前时间 例:2015-10-11 15:00:01 * @return */
private static String getTime(){
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(new Date().getTime());
}
}
并发的处理,简单的处理了并发,使用synchronized关键字处理了saveLog方法,确保每次只有一个线程可以访问该方法,这样就保证了该方法的安全性,避免在多线程的状态下出现不可预期的结果。
性能测试:
使用apache ab进行压力测试,10000次访问的时间是3.2s左右,完全可以满足性能要求。
不足:每次只能处理一个url,如果并发多个url,分布在多个文件中,性能太低,不能满足需求。