在Spring Web应用程序上跟踪用户活动

时间:2019-12-03 13:55:57

标签: java spring

我有一个使用Spring框架用Java编写的Web应用程序。 我想存储用户的活动,例如页面访问,操作,交互等。 我读到,通常这是通过为每个跟踪方面创建一个表来完成的。我想知道使用Spring框架是否有更好的方法,例如拦截所有请求并触发某些动作的方法。 您建议使用哪种技术来存储所有这些信息?知道我正在使用通过JPA与之交互的MySql数据库。但是,由于我对这些事情真的很陌生,因此我不知道应该使用NoSql数据库还是应该使用已有的MySql数据库。这个奇迹来自这样的想法:这种数据流将比来自诸如登录,创建,删除等更传统的动作的普通数据流大得多。 希望能向自己解释一下……如果不只是告诉我,我会尝试添加更多详细信息。

[编辑1]

该网络应用是电子商务。到目前为止,它并没有那么多用户,但是它将(成千上万)。

用户跟踪它的目的仅仅是为了描述他们,以便为他们提供更好,更自定义的服务。例如,如果看到用户正在查看许多精确类别的商品,我可以向他展示更多此类商品。

我不太在乎性能,我的意思是,它不必这么快。

知道我只有一个数据库,所有内容都存储在其中。我不知道用这种数据流收费是否会降低其性能。

该应用程序在AWS ElasticBeanstalk上运行,而数据库在AWS RDS上。

2 个答案:

答案 0 :(得分:2)

通常,它是一个非常广泛的主题。

我想到以下注意事项:

  1. 每一段时间内有多少个请求传递给微服务?如果用户数量很少(转化为数据库记录的数量),那么可以使用MySQL方法,那么该表就不会很大。但是请注意,有时还是应该对其进行清洁

  2. 延迟重要吗?有时必须非常迅速地处理请求,将跳数添加到数据库以保存用户首选项可能会出现问题

  3. 您如何使用此类信息?您是否打算使用仪表板(在这种情况下,千分尺+ Prometheus / InfluxDB和Grafana可能是一个很好的解决方案)。您是否打算实际按请求数向用户收费,是否能够将月度账单发送到PDF格式的电子邮件中,或提供对此类信息的Web访问(例如AWS)?

  4. 速率限制器如何?您是否打算拒绝一些频繁且来自同一用户的请求?

  5. 将“添加”此类信息的实例有多少?如果您现在有成千上万的微服务必须写入MySQL,怎么办?它可能无法承受这样的负载(除了为其设置的常规负载外)?

解决方案的范围可能会有所不同。 您可以批处理内存中每个用户的请求,并在向Kafka发送消息时一次发送一次,然后使用kafka流在其上提供聚合。使用这种方法,您可以最大程度地减少添加的处理对现有解决方案的影响,并将部署一些其他服务,这些服务将能够处理大量数据。

另一个选项:也许您可以创建一个异步填充的日志文件,并将信息存储在该文件中。然后,您可能想添加一些“代理” /侧面车容器,例如logstash,并将数据流式传输到某些存储中。与该领域相关的另一个项目是Apache Flume,它将允许您构建管道。

对于计费,您可以使用专用系统,AFAIK spring没有像这样的东西,通常这些现成的产品可以与之集成。

对于速率限制,您可以考虑:Resilience4j或使用redis

解决

答案 1 :(得分:0)

是的,这是可能的,下面是带有示例片段的三种方法,这些示例将有助于您的实现。此外,这还取决于您在记录活动时以及何时将活动数据视为过时的存储数据。有很多因素可以决定您的数据存储。

方法1:您可以使用Spring-Security跟踪登录用户

您可以编写HTTPSessionBindingListener并跟踪类似这样的操作

@Component
public class LoggedUser implements HttpSessionBindingListener {

    private String username; 
    private ActiveUserStore activeUserStore;

    public LoggedUser(String username, ActiveUserStore activeUserStore) {
        this.username = username;
        this.activeUserStore = activeUserStore;
    }

    public LoggedUser() {}

    @Override
    public void valueBound(HttpSessionBindingEvent event) {
        List<String> users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (!users.contains(user.getUsername())) {
            users.add(user.getUsername());
        }
    }

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) {
        List<String> users = activeUserStore.getUsers();
        LoggedUser user = (LoggedUser) event.getValue();
        if (users.contains(user.getUsername())) {
            users.remove(user.getUsername());
        }
    }

    // standard getter and setter
}

对于登录和注销,您可以使用AuthenticationSuccessHandler进行跟踪

@Component("myAuthenticationSuccessHandler")
public class MySimpleUrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    ActiveUserStore activeUserStore;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, 
      HttpServletResponse response, Authentication authentication) 
      throws IOException {
        HttpSession session = request.getSession(false);
        if (session != null) {
            LoggedUser user = new LoggedUser(authentication.getName(), activeUserStore);
            session.setAttribute("user", user);
        }
    }
}

方法2:如果要使其非常简单,另一种方法是可以编写一次OncePerRequestFilter

@Component
@Ordered(Ordered.LOWEST_PRECEDENCE)
public class LogFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {
        // Log the info you need
        // ...
        filterChain.doFilter(request, response);
    }
}

方法3:使用Spring AOP实施。

@Aspect
@Component
public class WebMethodAuditor {

protected final Log logger = LogFactory.getLog(getClass());

public static final String DATE_FORMAT_NOW = "yyyy-MM-dd HH:mm:ss";

@Autowired
AuditRecordDAO auditRecordDAO; 

@Before("execution(* com.mycontrollers.*.*(..))")
public void beforeWebMethodExecution(JoinPoint joinPoint) {
    Object[] args = joinPoint.getArgs();
    String methodName = joinPoint.getSignature().getName();
    User principal = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    Timestamp timestamp = new Timestamp(new java.util.Date().getTime());
    // only log those methods called by an end user
    if(principal.getUsername() != null) {
        for(Object o : args) {
            Boolean doInspect = true;
            if(o instanceof ServletRequestDataBinder) doInspect = false;
            if(o instanceof ExtendedModelMap) doInspect = false;
            if(doInspect) {
                if(o instanceof BaseForm ) {
                    // only show form objects
                    AuditRecord ar = new AuditRecord();
                    ar.setUsername(principal.getUsername());
                    ar.setClazz(o.getClass().getCanonicalName());
                    ar.setMethod(methodName);
                    ar.setAsString(o.toString());
                    ar.setAudit_timestamp(timestamp);
                    auditRecordDAO.save(ar);
                }
            }
        }
    }
}

}

来源和更多详细信息:

https://www.baeldung.com/spring-security-track-logged-in-users

Spring / AOP: Best way to implement an activities log in the database

What is the best way to log Activity in Spring Boot with Thymeleaf?