`
hypercube1024
  • 浏览: 85477 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

造了个轮子^_^,firefly应用程序框架,性能大幅超越spring3MVC和Struts2,含测试报告

    博客分类:
  • java
阅读更多

现在的开源框架都是大而全,动辄xxMB的jar包,感觉用不了那么多的功能,而且性能有些不尽如人意。所以还是自己造个轮子吧。

 

firefly的特性

  • 高性能,性能大幅超越spring3、struts2等,详情请看性能测试报告
  • 精简、轻量、无侵入,firefly.jar只有44k
  • core包含IOC、MVC、Interceptor等常用功能
  • 使用简单,基于约定优于配置的思想,默认使用Annotation配置方式

 

firefly的使用风格类似Spring3,具体可以看

http://code.google.com/p/firefly/wiki/guide

 

代码已经发布到 google code ,有兴趣的同学可以一起来开发

http://code.google.com/p/firefly/

 

 

测试环境

  • windows7 jdk1.6.0_22
  • JDK参数:-Xms128m -Xmx512m -XX:+DisableExplicitGC
  • 服务器:jetty 6.1.25
  • CPU: T6600 内存:3G
  • 测试工具:JMeter

用例1测试报告

往JSP打印“你好 firefly!”

500线程,循环2次

取跑10次之后的成绩

框架 版本 吞吐量 错误率
fireflyMVC 1.0-SNAPSHOT 38910.506/分钟 0%
Servlet 2.5 38022.814/分钟 0%
Spring3MVC 3.0.5.RELEASE 13106.16/分钟 0%
Struts2 2.2.1.1 1256.571/分钟 0%

 

用例1

往JSP打印“你好 firefly!”

JSP代码

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Strict//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
        <title>firefly</title>
</head>
<body>
        ${hello}
</body>
</html>

 

firefly测试代码

        @RequestMapping(value = "/hello")
        public String index(HttpServletRequest request) {
                request.setAttribute("hello", "你好 firefly!");
                return "/index.jsp";
        }

 

Servlet测试代码

        @Override
        public void doGet(HttpServletRequest request, HttpServletResponse response)
                        throws ServletException, IOException {
                request.setAttribute("hello", "你好 firefly!");
                request.getRequestDispatcher("/WEB-INF/page/index.jsp").forward(
                                request, response);
        }

 

Spring3MVC测试代码

        @RequestMapping(value="/spring", method=RequestMethod.GET)
        public String index(HttpServletRequest request) {
                request.setAttribute("hello", "你好 firefly!");
                return "/index";
        }

 

Struts2测试代码

    public String execute() throws Exception {
        setMessage("你好 firefly!");
        return SUCCESS;
    }

 

分享到:
评论
57 楼 hypercube1024 2011-02-15  
iminto 写道
不会用,LZ敢不敢给个完整的hello world例子?既然是firefly使用指南,连个hello world 都没有,晕倒


svn主干里面有个 firefly-hello 里面有很多例子
56 楼 hypercube1024 2011-02-15  
genius_45 写道
楼主的代码我大概阅读了
表层发现了几个问题
1.AnnotationBeanreader的scan方法初始化classes改成
if (classes == null) {
   classes = new LinkedHashSet<Class<?>>();
         }
否则配置文件中配置包只能扫描最后一个
2.@Inject如果加在bean的其他方法上,初始化时也会调用该方法
我是个在校学生,没有多少实践机会,看楼主代码我受益匪浅,非常感谢楼主开源共同学习的精神,希望能贡献更多优秀代码大家共同学习



非常感谢,1.确实是个Bug,现在就修复
2. @Inject在方法上面初始化确实会调用该方法
55 楼 iminto 2011-02-15  
不会用,LZ敢不敢给个完整的hello world例子?既然是firefly使用指南,连个hello world 都没有,晕倒
54 楼 genius_45 2011-02-14  
楼主的代码我大概阅读了
表层发现了几个问题
1.AnnotationBeanreader的scan方法初始化classes改成
if (classes == null) {
   classes = new LinkedHashSet<Class<?>>();
         }
否则配置文件中配置包只能扫描最后一个
2.@Inject如果加在bean的其他方法上,初始化时也会调用该方法
我是个在校学生,没有多少实践机会,看楼主代码我受益匪浅,非常感谢楼主开源共同学习的精神,希望能贡献更多优秀代码大家共同学习
53 楼 hypercube1024 2011-02-14  
gzenzen 写道
请问楼主,上传文件表单的文件怎么处理?

@HttpParam 目前还不能注入文件上传,只能支持普通的表单参数注入,文件上传注入正想加进去,目前还是只能用HttpRequest 处理文件上传 ~~
52 楼 gzenzen 2011-02-14  
请问楼主,上传文件表单的文件怎么处理?
51 楼 vb2005xu 2011-01-06  
<p>嗯 不错 我拿你这个改了下 你这个很好用啊</p>
<p> </p>
<pre name="code" class="java">public static String replace(String s, Map&lt;String, String&gt; map) {
StringBuilder ret = new StringBuilder(s.length());
int cursor = 0;
for (int start, end; (start = s.indexOf("${", cursor)) != -1
&amp;&amp; (end = s.indexOf("}", start)) != -1;) {
ret.append(s.substring(cursor, start)).append(
map.get(s.substring(start + 2, end)));
cursor = end + 1;
}
ret.append(s.substring(cursor, s.length()));
return ret.toString();
}</pre>
 
50 楼 hypercube1024 2011-01-06  
<div class="quote_title">vb2005xu 写道</div>
<div class="quote_div">
<div class="quote_title">oojdon 写道</div>
<div class="quote_div">和我的JdonMVC有点相似,楼主加油,共同进步。<br><br>我的返回视图不是用注解,而是显式编程,实现一个渲染接口<br>这样就new Html,new Text,new Json,new JavaScript,new Atom,new Xml.<br>我的开发坐标是RESTful + DCI + DDD + CQRS<br><br>JdonMVC默认是用Jdon作为领域管理层,所以没有IOC。<br>
</div>
<br><br>靠 你咋和我的思路一样呢?? 你看看我这写的<br><pre name="code" class="java">
private static String getTemplate(String url,int delay){
return <span style="color: #ff0000;">String.format</span>("&lt;html&gt;&lt;head&gt;&lt;meta http-equiv=\"refresh\" content=\"%d;URL=%s\" /&gt;&lt;/head&gt;&lt;/html&gt;", delay,url);
// return "&lt;html&gt;&lt;head&gt;&lt;meta http-equiv=\"refresh\" content=\"" + delay + ";URL=" + url + "\" /&gt;&lt;/head&gt;&lt;/html&gt;";
}

</pre>
</div>
<p><span style="color: #ff0000;">String.format 这个性能很差</span>,所以我自己实现了一个Template函数,比format快好几倍</p>
<p> </p>
<p> </p>
<p> </p>
<p><span style="white-space: pre;">
</span></p>
<pre name="code" class="java"> private static final String START_FLAG = "${";
private static final String END_FLAG = "}";
/**
* 将字符串中特定模式的字符转换成map中对应的值
*
* @param s
*            需要转换的字符串
* @param map
*            转换所需的键值对集合
* @return 转换后的字符串
*/
public static String convert(String s, Map&lt;String, String&gt; map) {
StringBuilder ret = new StringBuilder(s.length());
int cursor = 0;
for (int start, end; (start = s.indexOf(START_FLAG, cursor)) != -1
&amp;&amp; (end = s.indexOf(END_FLAG, start)) != -1;) {
ret.append(s.substring(cursor, start)).append(
map.get(s.substring(start + START_FLAG.length(), end)));
cursor = end + END_FLAG.length();
}
ret.append(s.substring(cursor, s.length()));
return ret.toString();
}</pre>
 
<p>在这个帖子里面 <a href="http://hypercube1024.iteye.com/blog/833807">http://hypercube1024.iteye.com/blog/833807</a> 有和正则替换的性能测试报告,format我也测过,性能也很差</p>
<p> </p>
<p> </p>
49 楼 hypercube1024 2011-01-05  
yangguo 写道
据我观察,你的controller是单实例模式,存在与struts1一样的线程不安全,不能脱离容器测试等问题。
缺少根据表单属性生成对象这个常用功能。


这个功能现在的版本是有的,只不过例子里面贴不下了。。。
48 楼 yangguo 2011-01-05  
据我观察,你的controller是单实例模式,存在与struts1一样的线程不安全,不能脱离容器测试等问题。
缺少根据表单属性生成对象这个常用功能。

47 楼 oojdon 2011-01-05  
oojdon 写道
你咋和我的思路一样呢?? 你看看我这写的

呵呵,都想到一块去了,web框架我现在更倾向于事件模型,然后到达后台的DCI场景,再到领域模型。
46 楼 vb2005xu 2011-01-05  
oojdon 写道
和我的JdonMVC有点相似,楼主加油,共同进步。

我的返回视图不是用注解,而是显式编程,实现一个渲染接口
这样就new Html,new Text,new Json,new JavaScript,new Atom,new Xml.
我的开发坐标是RESTful + DCI + DDD + CQRS

JdonMVC默认是用Jdon作为领域管理层,所以没有IOC。

代码示例:
@Path("/user/{userId}")
@GET
public Represent user(Long userId) {
       User user = userQueryService.getUser(userId);
       return new Html("/editUser.ctl", "user", user);
}


文件上传
@POST
@Path("/singleupload")
public Represent upload(FormFile file) {
      return new Text("你上传了单个文件,文件名是:"+file.getFileName());
}
        
@POST
@Path("/mutiupload")
public Represent upload(List<FormFile> files) {
         return new Text("你上传了多个文件,文件数量是:"+files.size());
}



靠 你咋和我的思路一样呢?? 你看看我这写的
package info.moogens.mvc.web;

import info.moogens.mvc.web.exception.ViewNotFound;
import info.moogens.mvc.web.exception.ViewRenderFailed;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class View {

	public static final String contentType_JSON = "application/json";
//	public static final String contentType_XML = "text/xml";
	public static final String contentType_XML = "application/xml";
	public static final String contentType_HTML = "text/html";

	protected String contentType;

	public String getContentType() {
		return contentType;
	}

	protected void sendResponseContentType(HttpServletRequest request,
			HttpServletResponse response) {
				
		if (info.moogens.mvc.tools.Toolkit.notEmpty(contentType)) {
			response.setContentType(contentType + ";charset="
					+ request.getAttribute("currentCharset"));
			
//			response.setCharacterEncoding((String)request.getAttribute("currentCharset"));
		}
	}

	/**
	 * 渲染视图,此方法由HttpRequestMaster调用,开发者不应手动调用
	 * 
	 * 成功渲染必须返回null,否则继续返回视图对象
	 * 
	 * @param master
	 * @return View
	 * @throws ViewNotFound
	 * @throws ViewRenderFailed
	 */
	public abstract View make(WebResourceMaster master)
			throws ViewNotFound, ViewRenderFailed;

}
package info.moogens.mvc.web.view;

import info.moogens.mvc.tools.Toolkit;
import info.moogens.mvc.web.View;
import info.moogens.mvc.web.exception.ViewRenderFailed;

public final class JsonView extends RawView {
		
	public JsonView(Object data) {
		this.data = data ;
		this.contentType = View.contentType_JSON ;
	}
	
	/**
	 * 在此方法中调用 JSON解析器 来解析 this.data
	 */
	protected void renderData() throws ViewRenderFailed {
		// TODO 调用代码
		this.data = Toolkit.toJson(this.data);
	}
	
}
package info.moogens.mvc.web.view;

import info.moogens.mvc.web.View;

public abstract class TemplateView extends View{
	
	protected String template = null ;
	
	public String getTemplate() {
		return template;
	}
}
package info.moogens.mvc.web.view;

import java.io.IOException;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;

import info.moogens.mvc.web.View;
import info.moogens.mvc.web.WebResourceMaster;
import info.moogens.mvc.web.exception.ViewNotFound;
import info.moogens.mvc.web.exception.ViewRenderFailed;

public class JspHtmlView extends TemplateView {
	
	protected JspHtmlView(){
		this.contentType = View.contentType_HTML ;
	}
	
	/**
	 * template 文件名称 如果以 / 开始 则直接取 /下的JSP文件,否则调用 master.getViewBaseDir() 下的模版文件
	 * 
	 * @param template
	 */
	public JspHtmlView(String template) {
		this.contentType = View.contentType_HTML ;
		this.template = info.moogens.mvc.tools.Toolkit.notEmpty(template) ? template.trim() : null ;
	}

	@Override
	public View make(WebResourceMaster master) throws ViewNotFound, ViewRenderFailed {
				
		if (template != null){
			
			// 响应信息未发出时才能渲染 JSP视图
			if (master.getResponse().isCommitted()){ 
				return new RawView("response had committed before render view: " + template);
			}
			
			String templatePath = template.startsWith("/") ? template :	master.getRequestMaster().getViewBaseDir() + template ;			
			RequestDispatcher rd = master.getContext().getRequestDispatcher(templatePath);
			
			if (rd == null) throw new ViewNotFound(template);
			
			try {
				rd.forward(master.getRequest(), master.getResponse());	
			} catch (ServletException e) {
				throw new ViewRenderFailed(template,e.getMessage());
			} catch (IOException e) {
				throw new ViewNotFound(template);
			} finally {
				templatePath = null ;
				rd = null ;
			}
		}
		return null;
	}
	
}
package info.moogens.mvc.web.view;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import info.moogens.mvc.web.View;
import info.moogens.mvc.web.WebResourceMaster;
import info.moogens.mvc.web.exception.ViewNotFound;
import info.moogens.mvc.web.exception.ViewRenderFailed;

public final class RedirectView extends View {
	
	private String url ;
	private int delay ;
	
	/**
	 * 使用HTML重定向,站外地址必须使用 [http://域名/资源id] 的形式,支持自定义重定向时间
	 * @param url
	 * @param timeout
	 */
	public RedirectView(String url,int delay){
		this.url = info.moogens.mvc.tools.Toolkit.notEmpty(url) ? url.trim() : null ;
		this.delay = delay < 0 ? 0 : delay ;
	}
	
	/**
	 * 使用内部重定向
	 * @param url
	 */
	public RedirectView(String url){
		this.url = info.moogens.mvc.tools.Toolkit.notEmpty(url) ? url.trim() : null ;
		this.delay = -1 ;
	}
	
	private static String getTemplate(String url,int delay){
		return String.format("<html><head><meta http-equiv=\"refresh\" content=\"%d;URL=%s\" /></head></html>", delay,url);
//		return "<html><head><meta http-equiv=\"refresh\" content=\"" + delay + ";URL=" + url + "\" /></head></html>";
	}
	
	@Override
	public View make(WebResourceMaster master) throws ViewNotFound, ViewRenderFailed {
		
		if (this.url != null){
			HttpServletResponse response = master.getResponse();
			if (this.delay < 0 && !response.isCommitted()){				
				try {
					response.sendRedirect(this.url);
					return null ;
				} catch (IOException e) {					
					this.delay = 0 ;// 使用HTML重定向
				}				
			}else
				this.delay = 0 ;// 使用HTML重定向
			
			return new RawView(RedirectView.getTemplate(url, this.delay));
		}		
		return null;
	}

}

45 楼 oojdon 2011-01-05  
和我的JdonMVC有点相似,楼主加油,共同进步。

我的返回视图不是用注解,而是显式编程,实现一个渲染接口
这样就new Html,new Text,new Json,new JavaScript,new Atom,new Xml.
我的开发坐标是RESTful + DCI + DDD + CQRS

JdonMVC默认是用Jdon作为领域管理层,所以没有IOC。

代码示例:
@Path("/user/{userId}")
@GET
public Represent user(Long userId) {
       User user = userQueryService.getUser(userId);
       return new Html("/editUser.ctl", "user", user);
}


文件上传
@POST
@Path("/singleupload")
public Represent upload(FormFile file) {
      return new Text("你上传了单个文件,文件名是:"+file.getFileName());
}
        
@POST
@Path("/mutiupload")
public Represent upload(List<FormFile> files) {
         return new Text("你上传了多个文件,文件数量是:"+files.size());
}
44 楼 oznyang 2011-01-05  
呵呵,lz有洁癖,不过别看人家个头大,那是经过积累沉淀和考验的,再说现在动辄上t的硬盘,无所谓啦,也不会影响启动速度,加上用maven来管理jar的话,还算能接受,离题了,不过还是赞lz,写出来也算是对自己的提高,所谓开源不是为了让多少人用,关键是有这种精神
43 楼 vb2005xu 2011-01-05  
一个jar包好几MB 这个还是好的 你没见 十几MB的么
42 楼 hypercube1024 2011-01-05  
oznyang 写道
软件都是从简单到复杂发展起来的,那么多的层次结构,也是为了可扩展性考虑

  是的,主要还是要有个度,太复杂了就 过犹不及 了,一个jar包好几MB,大部分功能都用不上

41 楼 oznyang 2011-01-05  
软件都是从简单到复杂发展起来的,那么多的层次结构,也是为了可扩展性考虑
40 楼 hypercube1024 2011-01-05  
Norther 写道
hypercube1024 写道
Norther 写道
请问童鞋你里面有什么特殊的算法,神奇的Magic处理让你的这个库变的飞快,让大家开开眼。


到没有什么神奇的魔法了,就是类的层次结构比较简单,然后缓存了一些反射之后的对象


那就是因为功能简单,模块少,所以速度快了,如果spring功能也这么简单,未必不如你的快呢?对吧。


^_^ 那就不知道了,看看Spring的类的层次结构就够喝一壶的了,我这个最多两到三层,其实Spring的话80%的情况也就用到20%的功能,我觉得把这20%的功能做好就行了。
39 楼 Norther 2011-01-05  
hypercube1024 写道
Norther 写道
请问童鞋你里面有什么特殊的算法,神奇的Magic处理让你的这个库变的飞快,让大家开开眼。


到没有什么神奇的魔法了,就是类的层次结构比较简单,然后缓存了一些反射之后的对象


那就是因为功能简单,模块少,所以速度快了,如果spring功能也这么简单,未必不如你的快呢?对吧。
38 楼 hypercube1024 2011-01-05  
Norther 写道
请问童鞋你里面有什么特殊的算法,神奇的Magic处理让你的这个库变的飞快,让大家开开眼。


到没有什么神奇的魔法了,就是类的层次结构比较简单,然后缓存了一些反射之后的对象

相关推荐

Global site tag (gtag.js) - Google Analytics