`
wenson
  • 浏览: 1052026 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Grails中实现用户即时在线状态的记录与读取

阅读更多

 之前有一需求:网站用户线上即时消费扣点,商户可以在线查看其商品的即时在线消费用户情况。目前网上查找到的资料有两种:
一是通过访问数据库,在数据库用户表比如Users表中添加一个字段 bit 当有人用这个用户登录的时候就把它变成1,但这方法会有以下问题:第一数据库更新问题,需要很大的性能而且如果大量用户这样做,后果不堪设想。第二:服务器无法判断设么时候用户推出登录;
二是通过JS来实现,但这种方法无法解决断网问题,还有就是如果一个用户在同一个计算机上登录两次那么第二次将不能登录,如果一个用户关闭浏览器服务器不知道,这个用户还想登录,后果就是登不上去了。

另外有一种个人觉得比较好的方案:
思想:就是用类似QQ的方法,如果一个用户再次登录,退出第一个登录的用户,保留第二个
存储:利用用户池java.util.Map作为用户池容器,里面放着用户标示和sessionId,这个容器放到application对象中介可以;
原理:当用户登录时直接put进去就可以了,如果这个对象存在就会被更新
其他:编写listener用HttpSessionAttributeListener地实现处理session属性的removed在removed的时候清楚这个用户在对象池中的状态
检查:编写一个用户Filter来检查请求的sessionId和用户池是否匹配就可以了如果不匹配清楚这个session中的用户对象迫使下线
针对项目使用的是Grails架构,把实现此功能的过程总结如下:
1、把消费记录记到application中,消费的controller为ConsumController.groovy,即时消费的action为:

def instantPay = {
        def personService = PersonService.get(params.id)
        def user = User.findByUserName(session.userName)
        if(!user){
            render(text:"請登入會員!",contentType:"text/html",encoding:"UTF-8")
        }
        int leftPoint = consumeRecordService.queryLeftPoint(user)
        if(leftPoint < personService?.unitPrice){
            render "對不起,您的點數不夠,請充值!"
        }
        
        String consumeId = session.getAttribute("consumeId")
        Long consumeRecordId = consumeId?.toLong()
        //把該用戶添加到產品服務即時計費列表,列表保存在application当中
        def pscontext = request.session.servletContext

        Map serviceMap = pscontext.getAttribute("serviceMap")
        if(!serviceMap){
            serviceMap = new HashMap()
        }
        ConsumeRecord cvalue = serviceMap.get(consumeRecordId)

        if(!cvalue || cvalue?.member != user){
            serviceMap = presenceService.putPresence(consumeRecordId,serviceMap)
            pscontext.setAttribute ("serviceMap",serviceMap)
            
        }


        Long cid = new Long(0)

        if(consumeId != null && !consumeId.equals("")){
           cid = consumeId.toLong()
        }
        Long id = consumeRecordService.getFirstConsume(cid, personService.id, user.id, 1, personService.unitPrice)

        session.setAttribute ("consumeId", id)
        
        leftPoint = consumeRecordService.queryLeftPoint(user)
        ConsumeRecord consumeRecord = ConsumeRecord.get(id)
        Date startTime = consumeRecord.startTime
        Date endTime = consumeRecord.endTime
        Long leftsec = ((endTime.getTime() - startTime.getTime())/1000)/60
        response.characterEncoding = "utf-8"
        render(text:"您已在線使用該服務時間為:${leftsec}分鐘,剩余點數為:${leftPoint}",contentType:"text/html")
       // render "您已使用的服務時間為:${leftsec}分鐘,剩余點數為:${leftPoint}"
    }

此Controoler当中有用到presenceService来对保存在application中的即时在线消费用户状态的管理,presenceService内容如下:

/**
 * Created by IntelliJ IDEA.
 * User: Administrator
 * Date: 2008-6-12
 * Time: 16:53:54
 * To change this template use File | Settings | File Templates.
 */
class PresenceService {
    boolean transactional = true
    Map allPresencesMap = new HashMap()
//保存到application中
    def Map putPresence(Long consumeId, Map serviceMap){
        ConsumeRecord consumeRecord = new ConsumeRecord()
        if(consumeId){
            consumeRecord = ConsumeRecord.get(consumeId)
        }

        if(serviceMap){
            allPresencesMap = serviceMap
        }
        allPresencesMap.put(consumeId,consumeRecord)
        return allPresencesMap
    }

//从application中删除
    def void removePresence(Long consumeId, Map serviceMap){
        if(serviceMap){
           serviceMap.remove(consumeId)
        }
    }
//从application中删除所有的的在线用户
    def void removePresenceList(Long personServiceId){
        Map ctx = personServices?.personService
        if ( ctx ) {
            personServices.remove( personServiceId );
            ctx.clear();
        }
    }
//从application中取得所有的在线用户
    def List getPresenceList(User SP, Map serviceMap){
        List result = [];
        if(serviceMap){
            for(m in serviceMap){
                ConsumeRecord consumeRecord = m.value
                if(consumeRecord && consumeRecord?.id ){
                    if(consumeRecord?.serviceProvider.userName == SP.userName){
                        result << consumeRecord
                    }
                }

            }

        }
        return result;
    }
//取得指定的在线用户
    def Presence getPresence( String key, Long personServiceId ) {

        Map ctx = personServices?.personService

        if ( !ctx ) {
            ctx = new HashMap();
            personServices.put( personServiceId, ctx );
        }

        Presence result = ( Presence ) ctx.get( key );

        return result;
    }

}

 
2、要即时地把在线消费的用户记录到application中,这里用到了JOB进行定时作业。在Grails中集合了quartz这个plugin,使用grails create-job OnlineCheck,内容如下:

import org.codehaus.groovy.grails.web.context.ServletContextHolder

class OnlineCheckJob {
    def startDelay = 60000
    def timeout = 1000 * 91 // execute job once in 1.5 seconds
    def group = "OnlineStatus"
    PresenceService presenceService
    def execute() {
        println "Job:refresh online status......."
        def cxt = ServletContextHolder.getServletContext()
        //取得保存在application中的serviceMap
        Map serviceMap = cxt.getAttribute("serviceMap")

        Date now = new Date();
        //如果serviceMap在application中存在,则对其进行遍历,取得即时在线消费的用户
        println("presenceService :${presenceService}")
        if(serviceMap){
            for(m in serviceMap){
                // Map comsumeMap = new HashMap();
                ConsumeRecord consumeRecord = m.value
                if(consumeRecord != null && consumeRecord.id != null){
                    Date endTime = consumeRecord.endTime
                   //如果consumeRecord的最后消费时间大于当前时间65秒,则把当前在线消费的用户踢下线
             if(((now?.getTime() - endTime?.getTime()) / 1000 ) > 65){
                        presenceService.removePresence (consumeRecord.id, serviceMap)
                        println("remove online status:consumeRecordId=${consumeRecord.id}")
                    }
                }

            }
        }
        
    }
}

 

 

3、记录到application中了之后,就可以从application中读取保存在里面的在线用户了,建立一个controller,读取在线状态的action为:

def onlinestatus= {
        User sp = User.findByUserName(session.userName)  //sp为商户
        def pscontext = request.session.servletContext
        Map serviceMap = pscontext.getAttribute("serviceMap")
        List result = presenceService.getPresenceList(sp,serviceMap)

        request.setAttribute ("resultList",result)
        //[resultList:result]

        [resultList:result]  
    }

 如果要即时获取此在线消费用户的列表,则要使用Ajax来定时执行此action了:

<g:javascript>

function timerUpdater() {
              var myAjax = new Ajax.PeriodicalUpdater(
              {success:'onlinelist'},// 成功或失敗更新的页面div元素
                      '/user/onlinestatus', // 请求的URL
              {
                  asynchronous:true,
                  method: 'post', // HTTP请求的方式为GET
                  evalScripts: true, // 是否执行请求页面中的脚本
                  frequency: 60, // 更新的频率
                  decay: 2 // 衰减系数
              }
                      );
              return false;
          }
</g:javascript>

 

 保存所有之后,即可运行测试了。。。。。。。。

 

5
1
分享到:
评论
2 楼 wenson 2008-07-15  
agile_boy  写道
写的很详细, 不知对于cluster这样的方案是否可行?

目前我仅是在单台Server上测试,还没有在cluster上测试过,我想这问题会在下阶段实行。。。。
1 楼 agile_boy 2008-07-14  
写的很详细, 不知对于cluster这样的方案是否可行?

相关推荐

    Grails中文参考手册

    Views 是 Grails 应用程序中的用户界面部分,通常使用 Groovy Server Pages (GSP) 文件编写。GSP 结合了 HTML、CSS 和 JavaScript 以及 Groovy 语法,允许动态生成页面内容。 **Services** Services 是可重用的...

    grails-用户手册

    GORM是Grails的持久化框架,它实现了Hibernate和ActiveRecord的功能,使得与数据库的交互变得简单。通过定义领域类,你可以轻松地完成CRUD(创建、读取、更新、删除)操作。 五、Grails插件系统 Grails的插件系统...

    grails实现分页技术

    在Grails这个基于Groovy的敏捷开发框架中,实现分页功能对于任何Web应用程序都是至关重要的,特别是当处理大量数据时。Grails提供了一些内置的支持,但如果你需要在自定义的控制器和视图中实现分页,那么就需要遵循...

    grails中文参考手册

    - **Hello World示例**:在新创建的应用中,你可以通过创建一个简单的控制器和视图来实现经典的“Hello, World!”程序,体验Grails的快速开发能力。 3. **使用IDE** Grails支持多种集成开发环境(IDE),如...

    grails中文入门简介

    验证是Web开发中不可或缺的一部分,Grails通过声明约束和验证约束来实现。它支持客户端验证,使得可以在用户提交表单之前校验数据。Grails的国际化支持非常好,开发者可以通过简单的配置来为不同的语言环境定制应用...

    Grails 下拉框联动最优实现

    在Grails中,我们可以利用AJAX技术实现这种交互,AJAX即异步JavaScript和XML,它允许页面在不刷新整个页面的情况下与服务器交换数据并局部更新页面内容。这正是实现联动效果的关键,因为它能让用户体验更加流畅,...

    grails中的上传,下载

    在Grails框架中,文件的上传与下载是常见的功能需求,尤其在开发涉及大量文档、图片或媒体文件处理的应用程序时。以下是对Grails中上传和下载操作的深入解析,包括其实现机制、代码示例及注意事项。 ### 文件上传 ...

    Grails-2.4.4-用户手册

    **Grails 2.4.4 用户手册** **一、Grails 框架概述** Grails 是一个基于 Groovy 语言的开源全栈式Web应用框架,它旨在提高开发效率,提供简洁、灵活的代码结构,以及强大的自动化工具。Grails 2.4.4 是该框架的一个...

    Grails入门精通及中文API

    在学习过程中,还可以参考在线教程、视频课程和社区论坛,如Stack Overflow和Grails官方社区,以便获取更多实践经验和帮助。 总之,Grails作为Java平台上的快速Web开发框架,以其高效、灵活和丰富的生态系统,为...

    grails 中文第二版

    ### Grails框架中文第二版知识点概述 #### 一、简介 **Grails 2.0新特性** 1. **面向开发的特性** - 改进了开发者体验,例如通过简化配置来提高开发效率。 - 引入了新的工具和功能,如Groovy模板引擎Thymeleaf...

    Grails1.1中文文档

    《Grails 1.1 中文文档》是一个非常宝贵的资源,尤其对于国内的开发者来说,由于Grails在中文社区中的资料相对较少,这份文档的价值不言而喻。Grails是一个基于Groovy语言的开源Web应用框架,它借鉴了Ruby on Rails...

    grails中文API

    通过定义Groovy类并使用Grails的GORM(Grails Object Relational Mapping)来与数据库交互,可以轻松实现数据持久化。 4. **控制器(Controllers)** 控制器负责处理HTTP请求,调用业务逻辑并生成响应。Grails的...

    grails ajax

    在Grails中,Ajax可以与JavaScript紧密集成,极大地提升了用户体验。 标题"grails ajax"表明我们将探讨如何在Grails应用中有效地使用Ajax技术。Ajax允许后台与服务器进行异步数据交换,而无需重新加载整个网页。这...

    grails 中文文档+grails-fckeditor-0.9.5.zip插件

    FCKEditor是一款著名的在线文本编辑器,grails-fckeditor-0.9.5插件则是将其集成到Grails应用中的解决方案。以下是关键点: 1. 安装与配置:在Grails项目中添加插件依赖,然后通过配置文件调整编辑器的参数,如默认...

    Grails Grails Grails

    1. **模型(Model)**: 在Grails中,模型通常由Domain Classes表示,它们是持久化的实体,与数据库表对应。Domain Classes使用Groovy的元编程特性,如属性验证和动态方法,提供数据验证和业务逻辑。 2. **视图...

    grails 中文手册

    - GORM 是 Grails 提供的持久化框架,它提供了对数据库操作的简便接口,包括CRUD(创建、读取、更新、删除)操作。 - GORM 支持领域驱动设计(Domain-Driven Design),领域模型可以直接映射到数据库表,通过注解...

    使用 Grails 快速开发 Web 应用程序

    2. **View**:Grails使用GSP(Groovy Server Pages)作为视图,类似于JSP,但更灵活,允许在页面中直接嵌入Groovy代码,实现动态内容的生成。 3. **Controller**:控制器负责处理用户请求,管理业务逻辑,接收表单...

    Grails1.1中文文档(CHM)

    Grails1.1最新 中文 文档 当今的Java Web开发技术显得过于复杂,相对于它本身的需要来说。现在主流的Java Web框架也是异常复杂,...与那些框架不同的是,Grails是构建在现有的像Spring、Hibernate这样的Java技术之上。

Global site tag (gtag.js) - Google Analytics