Archive for the ‘程序’ category

groovy及其TemplateServlet的编码问题

July 9th, 2009

上一篇文章里快速上手使用groovy来写页面了,如果在页面里使用中文的话,可能会遇到中文问题。在1.6.3版本的groovy里,可以设置两个属性来解决:

groovy.source.encoding=UTF-8
file.encoding=UTF-8

groovy在解释脚本时,默认会使用一个叫做groovy.source.encoding的系统属性的值来作为源码的编码,如果这也没有设置的话,就会使用file.encoding这个系统属性,也就是操作系统的默认字符集了。然而TemplateServlet很傻,它只认file.encoding,所以要两个属性都设置上,才能解决问题。而file.encoding属性是在整个jvm下都有效的,可能会带来其他的问题。下个版本中groovy会修正这个问题。如果你等不及了,你可以像我这样,hack一下TemplateServlet,把getTemplate()方法的一段代码改为:

//
// Template not cached or the source file changed - compile new
// template!
//
if (template == null) {
    if (verbose) {
        log("Creating new template from file " + file + "...");
    }
   
    String fileEncoding = System.getProperty("groovy.source.encoding");
   
    Reader reader = null;
    try {
        reader = fileEncoding == null ?
                new FileReader(file) : new InputStreamReader(new FileInputStream(file), fileEncoding);
        template = engine.createTemplate(reader);
    } catch (Exception e) {
        throw new ServletException("Creation of template failed: " + e,
                e);
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ignore) {
                // e.printStackTrace();
            }
        }
    }
    cache.put(key, new TemplateCacheEntry(file, template, verbose));
    if (verbose) {
        log("Created and added template to cache. [key=" + key + "]");
    }
}

用了如上的代码后就只要设置groovy.source.encoding一个属性就可以了。

  • Share/Bookmark

用groovyServlet和TemplateServlet快速开发网页

July 8th, 2009

Groovy是专门为java设计的脚本语言,可以和java无缝的结合,为java带来了脚本语言的能力。作为一个脚本语言,groovy有丰富的特性,比如动态类型,闭包等。如果能掌握groovy,并用其来开发,可以大大提高java程序员的生产力。Grails是用groovy写的一个类似于rails的web框架,实际上就是spring+hibernate+groovy。我想用groovy来写写页面,但是又不想用grails这种笨重的东西,正好groovy自带的了两个类可以完成这个任务,就是groovy.servlet包下的 GroovyServlet 和 TemplateServlet类。

下载安装好groovy后,在安装目录的embeddabe目录下复制groovy-all-1.6.3.jar到war/WEB-INF/lib/下。然后,在web.xml里添加如下代码:

  <servlet>
    <servlet-name>groovlet</servlet-name>
    <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
  </servlet>
 
  <servlet>
    <servlet-name>gsp</servlet-name>
    <servlet-class>com.liba.link.groovy.MyTemplateServlet</servlet-class>
  </servlet>
 
  <servlet-mapping>
    <servlet-name>groovlet</servlet-name>
    <url-pattern>*.groovy</url-pattern>
  </servlet-mapping>  
 
  <servlet-mapping>
    <servlet-name>gsp</servlet-name>
    <url-pattern>*.gsp</url-pattern>
  </servlet-mapping>

这样,就可以在web目录下用groovy写以.groovy或.gsp结尾的页面代码了。GroovyServlet就是一个普通的servlet,只不过,它会编译执行指定url对应的.groovy文件,来产生html。这和直接用groovy写servlet的区别是,GroovyServlet为你做了一些特殊变量的绑定,在.groovy里可以通过request, out, application, session等访问HttpServletRequest, HttpServletResponse, ServletContext和Session。而System.out被重定向客户端的输出,因而,可以这样写html了:

out.print("<h1>${request.getParameter('username')}</h1>")

GroovyServlet还绑定了一个MarkupBuilder,名称为html,所以可以用MarkupBuilder快速的生成简单的页面,比如:

html.div {
    table {
        tr {
            td {
                span(style:"color:red", "Hello world")    
            }
        }
    }
}

如果页面里有大量的html,只有少量的html代码,那么用groovyServlet的方式来写,还是会有些麻烦,这时候使用TemplateServlet会好一些。像上面的那样的配置,只要在web目录里写上以.gsp结尾的文件就可以了。在html代码里像jsp那样用 <% %>来插入groovy代码,比如:

<table>
  <tr>
    <td>
       <span style="color:red">
       <% print "Hello world" %>
       </span>
    </td>
   </tr>
</table>

TemplateServlet和GroovyServlet一样,也把常用的对象绑定成里脚本里的变量。

也许有人会问,这比直接用java和jsp有什么好的呢?没有多大好处,就是可以使用groovy代替java而已,但是我觉得就这一点就是很大的进步了。

  • Share/Bookmark

IO和性能

July 3rd, 2009

最近几天看了几家互联网公司的和架构有关的PPT,有支付宝,linkedin,douban等。这些公司所使用的具体的技术有差别,但是为了能做到支持大规模访问,他们都在与缓慢的IO做斗争! 比如说豆瓣网的历次架构变迁,不是因为磁盘IO成为瓶颈,就是因为网络IO成为瓶颈。如果能把IO的问题解决掉,那么对于互联网企业来说,性能问题就解决掉一半了,我想。

与飞速的CPU和内存相比,磁盘和网络的IO慢如蜗牛,这是问题的根源。如果没有大规模廉价而又足够快的存储方案出现的话,这个问题还将继续下去。我们只能期待这一革命性的技术出现来从根本上解决这一问题吧!

那对于现在的我们该怎么办呢?大家的想法可能都是一样的,一是加快IO速度,二是避免不必要的IO。豆瓣曾经通过购买更高性能的硬盘来解决这个问题,如果想要更好的效果,需要投入更多的资金来购买整套的高性能存储方案,但是这成本太高了,很多公司后来都会放弃。于是大部分公司都会极力采用后者,就是避免不必要的IO,缓存就是用来做这样的事的。

将低速介质上的数据放到高速介质上,访问者先访问高速介质,在找不到需要数据时才访问低速介质,以此来提升访问数据的速度,这样的技术就是缓存了。设在一段时间内有m次没有命中缓存,n次命中,而访问一次缓存的时间是t1, 访问慢速介质的时间是t2,那么为了是缓存有效就得满足不等式: m * (t1 + t2) + n * t1 < (m + n) * t2, 于是有 m / n < t2 / t1 -1 。由此可见,在介质的速度比一定的情况下,命中比率要高于一定的值,缓存才有效果。提高命中比率,就是要把那些最常用的数据放到缓存里。另一方面,缓存里数据如果被修改了,就要同步到低速存储介质中,同样的,如果低速存储介质的数据被修改了,也要同步到缓存里。如果加上同步的开销,就需要更高的命中率。提高命中率和保证数据的同步,是使用缓存的两个艰巨的任务。

豆瓣网使用memcache,还使用专门的nigix服务器来提供图片的服务,其实也是cache。linkedin则使用了数十台大内存的服务器作为缓存服务器,运行着用c++写的专门的缓存程序。可见这些网站在使用缓存方面都是不遗余力呀。如果能把缓存用好,那么就把一大半的IO问题解决掉了。

IO往往会成为程序的瓶颈,因此,程序员应当时刻的注意,你写的代码是否在进行IO操作,是否可以减少IO操作,等等。

  • Share/Bookmark

DRY, Don’t Repeat Yourself!

July 1st, 2009

DRY,就是不要写重复的代码,这也是程序设计的一个黄金准则之一。

1 引申自关注点分离

其实这一条准则,也是从关注点分离引申而来的。如果完成相同或相似功能的代码片段,在一个程序的很多地方出现,那么无疑会使包含它的代码多了一个关注点。把相同或相似的代码抽象出来,放在一个地方,这样就可以使调用者减少一个关注点,同时将所有的对同类问题的关注合并到一个地方。比如说,判断一个用户是否登录,这一功能被很多地方使用,如果把这一功能包装成函数放到一个地方,需要使用的地方只要引用一下函数就可以了,这样引用者就不用关心如何判断一个用户是否登录了。

2 好处

2.1 提高效率

同样的功能,完成一次就可以了,如果每次要使用轮子的时候,都重复发明轮子,那是对人类智慧的浪费。如果发明一个万用轮子不容易,那么发明一个够用的可以反复使用的轮子就可以了。

2.2 更易读

如过你打开一个程序的源代码,发现有大量类似又稍有差别的代码,你还有多少心情读下去吗?要读下去的话,你就得像diff工具一样去比较类似代码间的细微差别,还要揣摩它的含义。这样的代码读起来太痛苦了。因此,如果没有重复的代码,就没有这份痛苦。

2.3 更易维护

如果你用类似的算法,重复写了很多的程序,结果却发现这个算法有个错误,那你不得不小心翼翼的找到所有的这样的程序,并一个一个又一个的修正。那是一个多么无聊的工作呀。如果漏掉了,或者将其中的某些一不小心修改错了,还得回头再重新修改,那台痛苦了!所以充斥着copy&paste的代码,是维护者的噩梦。

3 怎么做

程序语言和编程方法的进步,也就是人们在实践DRY的过程。要做到DRY,就是要不断的通过抽象,把重复的功能抽象并包装起来。我们先是有了函数和子程序,后来有了类和方法,等等。我知道,为了DRY,还会有新的工具和方法论被发明。

  • Share/Bookmark