分布式实战 | 第一篇 :ELK在开源全栈项目【有来商城】的应用,不仅仅是分布式日志收集(一)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
日志服务 SLS,月写入数据量 50GB 1个月
简介: 分布式实战 | 第一篇 :ELK在开源全栈项目【有来商城】的应用,不仅仅是分布式日志收集(一)

一. 前言

其实早前就想计划出这篇文章,但是最近主要精力在完善微服务、系统权限设计、微信小程序和管理前端的功能,不过好在有群里小伙伴的一起帮忙反馈问题,基础版的功能已经差不多,也在此谢过,希望今后大家还是能够相互学习,一起进步~


OK,回正题,ELK是Elasticsearch、Logstash、Kibana三个开源软件的组合,相信很多童鞋使用ELK有去做过分布式日志收集。流程概括为:微服务应用把Logback输出的日志通过HTTP传输至LogStash,然后经过分析过滤,转发至ES,再由Kibana提供检索和统计可视化界面。


在本实战案例中,使用Spring AOP、Logback横切认证接口来记录用户登录日志,收集到ELK,通过SpringBoot整合RestHighLevelClient实现对ElasticSearch数据检索和统计。从日志搜集到数据统计,一次性的走个完整,快速入门ElasticSearch。


本篇涉及的前后端全部源码已上传gitee和github,熟悉有来项目的童鞋快速过一下步骤即可。


项目名称 Github 码云

后台 youlai-mall youlai-mall

前端 youlai-mall-admin youlai-mall-admin

二. 需求

基于ELK的日志搜集的功能,本篇实现的需求如下:


记录系统用户登录日志,信息包括用户IP、登录耗时、认证令牌JWT

统计十天内用户登录次数、今日访问IP和总访问IP

充分利用记录的JWT信息,通过黑名单的方式让JWT失效实现强制下线

实现效果:


访问地址:http://www.youlai.store


Kibana日志可视化统计

微信图片_20230709232551.png


登录次数统计、今日访问IP统计、总访问IP统计

微信图片_20230709232554.png


登录信息,强制用户下线,演示的是自己强制自己下线的效果

微信图片_20230709232616.gif


三. Docker快速搭建ELK环境

1. 拉取镜像

docker pull elasticsearch:7.10.1

docker pull kibana:7.10.1

docker pull logstash:7.10.1


2. elasticsearch部署

1. 环境准备


# 创建文件

mkdir -p /opt/elasticsearch/{plugins,data}  /etc/elasticsearch

touch /etc/elasticsearch/elasticsearch.yml

chmod -R 777 /opt/elasticsearch/data/  

vim /etc/elasticsearch/elasticsearch.yml

# 写入

cluster.name: elasticsearch

http.cors.enabled: true                              

http.cors.allow-origin: "*"                    

http.host: 0.0.0.0

node.max_local_storage_nodes: 100


2. 启动容器


docker run -d --name=elasticsearch --restart=always \

-e discovery.type=single-node \

-e ES_JAVA_OPTS="-Xms256m -Xmx256m" \

-p 9200:9200 \

-p 9300:9300 \

-v /etc/elasticsearch/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \

-v /opt/elasticsearch/data:/usr/share/elasticsearch/data \

-v /opt/elasticsearch/plugins:/usr/share/elasticsearch/plugins \

elasticsearch:7.10.1


3. 验证和查看ElasticSearch版本


curl -XGET localhost:9200

1


微信图片_20230709232628.png

2. kibana部署

1. 环境准备


# 创建文件

mkdir -p /etc/kibana

vim /etc/kibana/kibana.yml


# 写入

server.name: kibana

server.host: "0"

elasticsearch.hosts: [ "http://elasticsearch:9200" ]

i18n.locale: "zh-CN"


2. 启动容器


docker run -d --restart always -p 5601:5601 --link elasticsearch \

-e ELASTICSEARCH_URL=http://elasticsearch:9200 \

-v /etc/kibana/kibana.yml:/usr/share/kibana/config/kibana.yml \

kibana:7.10.1

3. logstash部署

1. 环境准备


配置 logstash.yml

# 创建文件

mkdir -p /etc/logstash/config

vim /etc/logstash/config/logstash.yml


# 写入

http.host: "0.0.0.0"

xpack.monitoring.elasticsearch.hosts: [ "http://elasticsearch:9200" ]

xpack.management.pipeline.id: ["main"]


配置pipeline.yml

# 创建文件

vim  /etc/logstash/config/pipeline.yml


# 写入(注意空格)

- pipeline.id: main

 path.config: "/usr/share/logstash/pipeline/logstash.config"


配置logstash.conf

# 创建文件

mkdir -p /etc/logstash/pipeline

vim /etc/logstash/pipeline/logstash.conf


# 写入

input {

   tcp {

     port => 5044

     mode => "server"

     host => "0.0.0.0"

     codec => json_lines

   }

}

filter{


}

output {

   elasticsearch {

       hosts => ["elasticsearch:9200"]

       # 索引名称,没有会自动创建

       index => "%{[project]}-%{[action]}-%{+YYYY-MM-dd}"

   }

}


2. 启动容器


docker run -d --restart always -p 5044:5044 -p 9600:9600 --name logstash --link elasticsearch \

-v /etc/logstash/config/logstash.yml:/usr/share/logstash/config/logstash.yml \

-v /etc/logstash/config/pipeline.yml:/usr/share/logstash/config/pipeline.yml \

-v /etc/logstash/pipeline/logstash.conf:/usr/share/logstash/pipeline/logstash.conf \

logstash:7.10.1


4. 测试

访问: http://localhost:5601/


微信图片_20230709232644.png


四. Spring AOP + Logback 横切打印登录日志

1. Spring AOP横切认证接口添加日志

代码坐标: common-web#LoginLogAspect


@Aspect

@Component

@AllArgsConstructor

@Slf4j

@ConditionalOnProperty(value = "spring.application.name", havingValue = "youlai-auth")

public class LoginLogAspect {


   @Pointcut("execution(public * com.youlai.auth.controller.AuthController.postAccessToken(..))")

   public void Log() {

   }


   @Around("Log()")

   public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {


       LocalDateTime startTime = LocalDateTime.now();

       Object result = joinPoint.proceed();


       // 获取请求信息

       ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

       HttpServletRequest request = attributes.getRequest();


       // 刷新token不记录

       String grantType=request.getParameter(AuthConstants.GRANT_TYPE_KEY);

       if(grantType.equals(AuthConstants.REFRESH_TOKEN)){

           return result;

       }


       // 时间统计

       LocalDateTime endTime = LocalDateTime.now();

       long elapsedTime = Duration.between(startTime, endTime).toMillis(); // 请求耗时(毫秒)


       // 获取接口描述信息

       MethodSignature signature = (MethodSignature) joinPoint.getSignature();

       String description = signature.getMethod().getAnnotation(ApiOperation.class).value();// 方法描述


       String username = request.getParameter(AuthConstants.USER_NAME_KEY); // 登录用户名

       String date = startTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // 索引名需要,因为默认生成索引的date时区不一致


       // 获取token

       String token = Strings.EMPTY;

       if (request != null) {

           JSONObject jsonObject = JSONUtil.parseObj(result);

           token = jsonObject.getStr("value");

       }


       String clientIP = IPUtils.getIpAddr(request);  // 客户端请求IP(注意:如果使用Nginx代理需配置)

       String region = IPUtils.getCityInfo(clientIP); // IP对应的城市信息


       // MDC 扩展logback字段,具体请看logback-spring.xml的自定义日志输出格式

       MDC.put("elapsedTime", StrUtil.toString(elapsedTime));

       MDC.put("description", description);

       MDC.put("region", region);

       MDC.put("username", username);

       MDC.put("date", date);

       MDC.put("token", token);

       MDC.put("clientIP", clientIP);


       log.info("{} 登录,耗费时间 {} 毫秒", username, elapsedTime); // 收集日志这里必须打印一条日志,内容随便吧,记录在message字段,具体看logback-spring.xml文件

       return result;

   }

}


2. Logback日志上传至LogStash

代码坐标:common-web#logback-spring.xml


   localhost:5044

   

       

           

               Asia/Shanghai

           

           

           

               

                   {

                   "project": "${APP_NAME}",

                   "date": "%X{date}",

                   "action":"login",

                   "pid": "${PID:-}",

                   "thread": "%thread",

                   "message": "%message",

                   "elapsedTime": "%X{elapsedTime}",

                   "username":"%X{username}",

                   "clientIP": "%X{clientIP}",

                   "region":"%X{region}",

                   "token":"%X{token}",

                   "loginTime": "%date{\"yyyy-MM-dd HH:mm:ss\"}",

                   "description":"%X{description}"

                   }

               

           

       

   

   5 minutes



   



localhost:5044 Logstash配置的input收集数据的监听

%X{username} 输出MDC添加的username的值


相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
2月前
|
NoSQL Java Redis
面试官:项目中如何实现分布式锁?
面试官:项目中如何实现分布式锁?
90 6
面试官:项目中如何实现分布式锁?
|
2月前
|
存储 NoSQL Java
分布式session-SpringSession的应用
Spring Session 提供了一种创建和管理 Servlet HttpSession 的方案,默认使用外置 Redis 存储 Session 数据,解决了 Session 共享问题。其特性包括:API 及实现用于管理用户会话、以应用容器中性方式替换 HttpSession、简化集群会话支持、管理单个浏览器实例中的多个用户会话以及通过 headers 提供会话 ID 以使用 RESTful API。Spring Session 通过 SessionRepositoryFilter 实现,拦截请求并转换 request 和 response 对象,从而实现 Session 的创建与管理。
分布式session-SpringSession的应用
|
2月前
|
存储 NoSQL Java
分布式session-SpringSession的应用
Spring Session 提供了一种创建和管理 Servlet HttpSession 的方案,默认使用外置 Redis 存储 Session 数据,解决 Session 共享问题。其主要特性包括:提供 API 和实现来管理用户会话,以中立方式替换应用程序容器中的 HttpSession,简化集群会话支持,并在单个浏览器实例中管理多个用户会话。此外,Spring Session 允许通过 headers 提供会话 ID 以使用 RESTful API。结合 Spring Boot 使用时,可通过配置 Redis 依赖和支持缓存的依赖实现 Session 共享。
分布式session-SpringSession的应用
|
1月前
|
缓存 网络协议 API
分布式系统应用之服务发现!
分布式系统应用之服务发现!
|
2月前
|
存储 运维 应用服务中间件
阿里云分布式存储应用示例
通过阿里云EDAS,您可以轻松部署与管理微服务应用。创建应用时,使用`CreateApplication`接口基于模板生成新应用,并获得包含应用ID在内的成功响应。随后,利用`DeployApplication`接口将应用部署至云端,返回"Success"确认部署成功。当业务调整需下线应用时,调用`ReleaseApplication`接口释放资源。阿里云EDAS简化了应用全生命周期管理,提升了运维效率与可靠性。[相关链接]提供了详细的操作与返回参数说明。
|
2月前
|
Dubbo Java 应用服务中间件
分布式(基础)-RMI简单的应用
分布式(基础)-RMI简单的应用
|
1月前
|
NoSQL Java Redis
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
Redis分布式锁在高并发场景下是重要的技术手段,但其实现过程中常遇到五大深坑:**原子性问题**、**连接耗尽问题**、**锁过期问题**、**锁失效问题**以及**锁分段问题**。这些问题不仅影响系统的稳定性和性能,还可能导致数据不一致。尼恩在实际项目中总结了这些坑,并提供了详细的解决方案,包括使用Lua脚本保证原子性、设置合理的锁过期时间和使用看门狗机制、以及通过锁分段提升性能。这些经验和技巧对面试和实际开发都有很大帮助,值得深入学习和实践。
太惨痛: Redis 分布式锁 5个大坑,又大又深, 如何才能 避开 ?
|
3月前
|
NoSQL Redis
基于Redis的高可用分布式锁——RedLock
这篇文章介绍了基于Redis的高可用分布式锁RedLock的概念、工作流程、获取和释放锁的方法,以及RedLock相比单机锁在高可用性上的优势,同时指出了其在某些特殊场景下的不足,并提到了ZooKeeper作为另一种实现分布式锁的方案。
110 2
基于Redis的高可用分布式锁——RedLock
|
7天前
|
NoSQL Redis
Redis分布式锁如何实现 ?
Redis分布式锁通过SETNX指令实现,确保仅在键不存在时设置值。此机制用于控制多个线程对共享资源的访问,避免并发冲突。然而,实际应用中需解决死锁、锁超时、归一化、可重入及阻塞等问题,以确保系统的稳定性和可靠性。解决方案包括设置锁超时、引入Watch Dog机制、使用ThreadLocal绑定加解锁操作、实现计数器支持可重入锁以及采用自旋锁思想处理阻塞请求。
41 16