论坛首页 Java企业应用论坛

深入介绍guzz服务,回答为何说能提高xx%倍开发效率

浏览 2685 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-02-03  
服务介绍

服务(Service)是guzz最重要的特性之一,用于帮助开发团队积累和重用代码。这种重用不仅仅是一些api的重用(类似apache commons包),而是更高层的功能模块的重用,类似于SOA。通过服务,团队或公司可以很快沉淀出通用的功能模块,并将其直接在新项目中使用,避免重复开发和重复维护。

服务的主要设计目标是帮助缩短开发周期、提高项目代码质量、降低维护成本;而实现办法就是功能复用。

服务实现

服务通过实现 org.guzz.Service 接口进行声明。在实现中,一般通过继承:
org.guzz.service.AbstractService

来实现。对于需要进行远程调用的服务(指RMI/hessian类似的远程调用),可以通过继承:
org.guzz.service.AbstractRemoteService

来实现。(服务接口计划增加服务间相互依赖的支持,以后可能会增加一些方法,实现时最好继承抽象父类)

下面通过一个具体的例子说明服务如何编写和使用。

反刷屏服务

假设我们正在创建一个大型社区系统,为了防止用户刷屏需要设计反刷屏模块。这个模块可能用于:限定一个IP 1分钟只能发10个论坛帖子,一个用户10分钟只能投2票,一个人的博客同一个IP 10秒钟内的访问只记录1次,新闻评论1个用户10分钟只能发8个评论……

根据分析,虽然业务不同,但我们只需要设计一个接口检测一个标记是否超过了允许操作的次数就可以了。不同的系统可以以自己的要求去生成标记,如论坛以用户ID做标记防止刷屏,以帖子散列为标记去除重复的帖子,博客以IP为标记防止刷访问量等等。

设计接口

基于此,我们设计如下接口:
public interface FixedLifeCountService {
	
	/**
	 * 如果增加计数后的值小于或等于最大允许值,增加计数,并返回true;否则不进行计数操作,并返回false。
	 * 
	 * @param key 计数对象的唯一标识。
	 * @param addCount 增加计数值。正数增加计数,负数减少计数值。如果为0,不更改计数值。
	 * @param maxCountAllowed 最大允许计数到的值
	 * @param maxLifeInSeconds 计数有效时间
	 * @return 返回增加当前计数后,计数值是否超过最大允许值( > @param maxCountAllowed)。
	 */
	public boolean incCountIfLess(String key, int addCount, int maxCountAllowed, int maxLifeInSeconds) ;
}

实现服务

接口定义好了,我们开始将接口做成服务。我们对准确性要求比较高,所以做成服务器端计算的,用一个memcached保存计数。

public class MemcachedCountServiceClientImpl extends AbstractService implements FixedLifeCountService {
	
	private String appName ;	

	public boolean incCountIfLess(String key, int addCount, int maxCountAllowed, int maxLifeInSeconds) {
		String m_key = appName + key ;
		//连接memcached,根据m_key查询已经记录了多少次,返回结果
		//如果没有刷屏嫌疑,向memcached中增加计数,用于下次使用。
		//具体代码略去
		return true ;
	}

	public boolean configure(ServiceConfig[] scs) {
		if(scs == null || scs.length == 0){
			log.warn("FixedLifeCountService is not started. no configuration found.") ;
			return false ;
		}
				
		this.appName = scs[0].getProps().getProperty("appName") ;

		return true ;
	}

	public boolean isAvailable() {
		return appName != null ;
	}

	public void shutdown() {
	}

	public void startup() {
	}

}

在本实现中,incCountIfLess是继承反刷屏接口的实现,通过连接memcached计算key的值是否超出要求。

public boolean configure(ServiceConfig[] scs) 方法为配置方法,guzz允许对服务进行配置,并且一个服务可以配置多组(如slave数据库可以通过3个配置组配置3台)。在系统启动时,guzz调用此方法将配置信息注入服务实现中。本例中,我们将appName(应用系统的名称)就是服务的参数,在每个项目如blog、bbs使用时配置不同的值,防止key在memcached中冲突。configure方法返回true表示配置成功,可以继续调用startup()启动服务;否则表示启动失败。

public boolean isAvailable() 返回服务是否可用。由具体服务自己设计用途自己使用。

public void shutdown() 在应用关闭时调用,释放资源。

public void startup() 在configure(ServiceConfig[] scs) 返回true后调用,用于服务初始化自身某些资源。如可以在本例中初始化memcached连接池等。

配置服务

服务实现完成以后,需要将他配置到各个应用系统中去。首先将服务的代码打成jar包,放到各个系统的lib中。随后,以论坛为例,将服务暴露给论坛开发人员使用。

服务的配置在guzz.xml中通过标签<service>进行,可以配置无限多个。如我们的反刷屏服务配置为:
<guzz-configs>
…
<config-server>
		<server class="org.guzz.config.LocalFileConfigServer">
			<param name="resource" value="bbs.properties" />
		</server>
</config-server>
…
<service name="fixedLifeCountService" configName="fundFixedLifeCountServiceClient" class="org.guzz.service.impl.MemcachedCountServiceClientImpl " />
….
</guzz-configs>

<service>标签中需定义以下属性:
name:服务名称,应用系统通过名称获取服务。
configName:配置文件(.properties)中的配置组名称
class:服务实现完整类名

每个服务都可以有他自己的配置属性(记得上面说的appName?),guzz通过ConfigManager获取这些配置信息。如以上配置文件中的org.guzz.config.LocalFileConfigServer,通过读取本地文件bbs.properties获取服务配置信息。(ConfigManager可以自己实现,从统一的配置管理服务器读取配置。)

bbs.properties的格式类似于Mysql的配置文件,以[组名]进行分组。对于我们的服务,需要在bbs.properties中添加如下配置:
[fundFixedLifeCountServiceClient]
appName=bbs

与Mysql配置文件不同的时,配置组可以重复,以实现对一个服务配置多组,如:
[fundFixedLifeCountServiceClient]
appName=bbs

[fundFixedLifeCountServiceClient]
appName=bbs1

[fundFixedLifeCountServiceClient]
appName=bbs2

对于这种配置,public boolean configure(ServiceConfig[] scs) 在执行时将传入长度为3的配置数组。如果不配置,传入长度为0的数组。

获取与使用服务

获取服务的方式有2种,通用的一种是通过GuzzContext获取。首先获取到GuzzContext对象,然后调用:
FixedLifeCountService fixedLifeCountService = (FixedLifeCountService) this.getGuzzContext().getService("fixedLifeCountService") ;

另外一种模式是在spring中将服务设置为1个bean:
    <!-- guzzframework -->
    <bean id="guzzContext" class="org.guzz.web.context.spring.GuzzContextBeanFactory" factory-method="createGuzzContext">
    	<constructor-arg><value>/WEB-INF/guzz.xml</value></constructor-arg>
    </bean>
    
    <bean id=" fixedLifeCountService" class="org.guzz.web.context.spring.GuzzServiceFactoryBean">
    	<property name="serviceName" value=" fixedLifeCountService" />
    </bean>

然后通过IOC的ref引用注入给需要的类。

获取到服务后,便可以按照应用需要调用服务方法,如:
If(!incCountIfLess(userId, 1,10, 600)){
	throw new Exception(“别刷屏!”) ;
}

有多少功能可以做成服务?

Guzz内部的主数据库,从数据库组,延迟更新(g:inc标签),调试(可以用于分析sql查询)都是按照服务的方式编写的,因此这些内容的配置方法与服务也相同。

对于企业或者团队,可以做成服务的模块很多,如检测身份证是否正确,计算手机号归属地,上面的反刷屏,缓存管理和使用,用户登录退出以及session管理,SSO,后台登录与权限管理,应用日志记录,内容过滤,应用数据监控,数据分发等等。一个应用系统可以说很大一部分的开发工作都可以做成服务来重用。

服务是guzz的4大核心功能之一(ORM + 多组数据库分散与主从 + 服务 + 配置管理),通过服务可以构建起企业技术基础平台,为每个项目服务。
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics