`
LoveLZY
  • 浏览: 57341 次
  • 性别: Icon_minigender_1
博客专栏
Group-logo
从零编写RPC框架
浏览量:0
社区版块
存档分类
最新评论

浅谈分布式项目日志监控

阅读更多
   目前公司项目采用dubbo服务化升级之后,原先大而全的几个主要应用,拆散重构成多个分布式服务。这个公司业务架构和系统架构实现一次升级,并发和业务开发效率得到提升。但是事情是两面的,引入dubbo服务化之后,导致业务链路过长,日志分散。不能在使用原来的日志处理方式了。
   分布式情况下,每个日志分散到各自服务所在机器,日志的收集和分析使用原来古老的模式,肯定是过时了,集群和服务规模小还好,数量一大,我想不管是运维人员还是开发人员都会头疼。
   目前处理这个需求最为火热的中间套件,自然首选是ELK,ELK是java技术栈的。也符合目前公司需求。ELK的安装就不讲述了,感兴趣的可以查看官网或者自行百度,资料还是挺多的。
   确定了日志收集和分析的中间件,剩下一个就是日志埋点和怎么把日志串起来了。以前单个应用的时代,系统级别的日志可以通过aop解决。在分布式情况下对每一个独立服务而言,自身的日志系统还是通过aop解决,唯一需要的就是怎么把分散到各自不同应用的日志串起来。这个有个高大上的说法叫做业务链监控。
   目前国内开源的产品有大众点评的cat,是整套业务链监控解决方案。对于我公司目前来说太重了,我这边日志已经有elk,就没必要在额外引入cat。那如何自己实现呢。
   既然是链路,那自然有入口有出口。我们需要做的就是在入口出生成一个全局唯一的traceId,然后把这个traceId按照业务链路传递到各个服务中去。traceId就是一根线,把各个服务的日志串起来。注意一点,服务的时间要同步,因为是根据来时间排序的。
   traceId的生成,简单方案可以采用uuid,其次推荐使用twiiter的snowflake算法。
   traceId的传递,需要根据rpc框架来实现了。dubbo框架采用dubbo的fiter来实现,参考代码如下:
      // 调用过程拦截
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        //异步获取serviceId,没获取到不进行采样
        String serviceId = tracer.getServiceId(RpcContext.getContext().getUrl().getServiceInterface());
        if (serviceId == null) {
            Tracer.startTraceWork();
            return invoker.invoke(invocation);
        }

        long start = System.currentTimeMillis();
        RpcContext context = RpcContext.getContext();
        boolean isConsumerSide = context.isConsumerSide();
        Span span = null;
        Endpoint endpoint = null;
        try {
            endpoint = tracer.newEndPoint();
//            endpoint.setServiceName(serviceId);
            endpoint.setIp(context.getLocalAddressString());
            endpoint.setPort(context.getLocalPort());
            if (context.isConsumerSide()) { //是否是消费者
                Span span1 = tracer.getParentSpan();
                if (span1 == null) { //为rootSpan
                    span = tracer.newSpan(context.getMethodName(), endpoint, serviceId);//生成root Span
                } else {
                    span = tracer.genSpan(span1.getTraceId(), span1.getId(), tracer.genSpanId(), context.getMethodName(), span1.isSample(), null);
                }
            } else if (context.isProviderSide()) {
                Long traceId, parentId, spanId;
                traceId = TracerUtils.getAttachmentLong(invocation.getAttachment(TracerUtils.TID));
                parentId = TracerUtils.getAttachmentLong(invocation.getAttachment(TracerUtils.PID));
                spanId = TracerUtils.getAttachmentLong(invocation.getAttachment(TracerUtils.SID));
                boolean isSample = (traceId != null);
                span = tracer.genSpan(traceId, parentId, spanId, context.getMethodName(), isSample, serviceId);
            }
            invokerBefore(invocation, span, endpoint, start);//记录annotation
            RpcInvocation invocation1 = (RpcInvocation) invocation;
            setAttachment(span, invocation1);//设置需要向下游传递的参数
            Result result = invoker.invoke(invocation);
            if (result.getException() != null){
                catchException(result.getException(), endpoint);
            }
            return result;
        }catch (RpcException e) {
            if (e.getCause() != null && e.getCause() instanceof TimeoutException){
                catchTimeoutException(e, endpoint);
            }else {
                catchException(e, endpoint);
            }
            throw e;
        }finally {
            if (span != null) {
                long end = System.currentTimeMillis();
                invokerAfter(invocation, endpoint, span, end, isConsumerSide);//调用后记录annotation
            }
        }
    }



  dubbo通过invocation.setAttachmen来在消费者和调用者之间传递traceId。
  如果是http接口调用实现的rpc建议采用在request的head里面传递traceId。
  在本地服务里面通过threadlocal变量来传递traceId。
  如果想打印sql语句,通过orm框架的拦截器机制实现,以下是mybatis的参考代码
  @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
				RowBounds.class, ResultHandler.class }) })
public class MidaiLogMybatisPlugn implements Interceptor {
	@Override
	public Object intercept(Invocation invocation) throws Throwable {

		Object result = null;
		//从当前线程获取trace
		MidaiLogTrace trace = MidaiLogTraceService.getMidaiLogTrace();
		if(trace !=null){
		  Object[] arguments = invocation.getArgs();
		  MidaiLogTraceService.traceSqlLog(trace.getTraceId(), getSqlStatement(arguments));
		}
		try {
			result = invocation.proceed();		
		} catch (Exception e) {
			throw e;
		}
		return result;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this); // mybatis提供的包装工具类
	}

	@Override
	public void setProperties(Properties properties) {
	}

	private String getSqlStatement(Object[] arguments) {
		MappedStatement mappedStatement = (MappedStatement) arguments[0];
		Object parameter = null;
		if (arguments.length > 1) {
			parameter = arguments[1];
		}
		String sqlId = mappedStatement.getId();
		BoundSql boundSql = mappedStatement.getBoundSql(parameter);
		Configuration configuration = mappedStatement.getConfiguration();
		String sql = showSql(configuration, boundSql);
		StringBuilder str = new StringBuilder(100);
		str.append(sqlId);
		str.append(":");
		str.append(sql);
		str.append(":");
		return str.toString();
	}

	public String showSql(Configuration configuration, BoundSql boundSql) {
		Object parameterObject = boundSql.getParameterObject();
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
		if (parameterMappings.size() > 0 && parameterObject != null) {
			TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
			if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
				sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));

			} else {
				MetaObject metaObject = configuration.newMetaObject(parameterObject);
				for (ParameterMapping parameterMapping : parameterMappings) {
					String propertyName = parameterMapping.getProperty();
					if (metaObject.hasGetter(propertyName)) {
						Object obj = metaObject.getValue(propertyName);
						sql = sql.replaceFirst("\\?", getParameterValue(obj));
					} else if (boundSql.hasAdditionalParameter(propertyName)) {
						Object obj = boundSql.getAdditionalParameter(propertyName);
						sql = sql.replaceFirst("\\?", getParameterValue(obj));
					}
				}
			}
		}
		return sql;
	}

	private static String getParameterValue(Object obj) {
		String value = null;
		if (obj instanceof String) {
			value = "‘" + obj.toString() + "‘";
		} else if (obj instanceof Date) {
			DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
			value = "‘" + formatter.format(new Date()) + "‘";
		} else {
			if (obj != null) {
				value = obj.toString();
			} else {
				value = "";
			}

		}
		return value;
	}
}



    当系统并发达到一定数量级,log4j日志打印本身会成为瓶颈,这个时候需要mq来解耦了,不在打印日志,而是发送mq消息,由mq消费端处理。因为目前公司项目并发数量还不足以导致该问题,因此尚未采用。
    elk收集日志之后,通过kibana可以提供搜索。
   

    剩下最后的工作量就是提供一个web界面来更好的分析和展示数据。



 



  • 大小: 129.3 KB
0
2
分享到:
评论
1 楼 javatozhang 2016-11-04  
非常不错!!!

相关推荐

    浅谈交通管理大数据分布式管理应用平台的架构设计方案.pdf

    综上所述,交通管理大数据分布式管理应用平台的架构设计方案涉及到的关键技术点包括大数据存储技术、数据仓库技术、分布式计算模型、数据管理和监控技术等。这些技术的应用不仅解决了传统交通管理系统的局限性,还为...

    浅谈自动化监控系统在水电厂中的应用研究.pdf

    首先,水电厂自动化监控系统的基本结构通常采用开放分布式计算机结构,这种结构允许多台电子计算机协同工作,完成数据的采集与处理、监控以及故障诊断等功能。根据水电厂控制对象的多样性和控制层级的复杂性,该系统...

    浅谈基于linux环境的网络监控技术.pdf

    它还具备一个基于Web的界面,方便管理员查看网络状态和日志。 Nagios采用了分布式-集中式的管理模式。主要程序安装在中心服务器上,而代理程序则部署在需要监控的各个主机上。监控有主动和被动两种模式。主动监控...

    浅谈SQL Server数据库安全监控系统结构和工作原理.pdf

    整个系统采用分布式结构设计,集成了检测、分析、控制三大子系统,每个子系统都采用层次化设计和业务逻辑与通讯管理分层实现,形成集中控制与多层管理的架构。 2. 实时状态查询模块 系统补充了实时状态查询模块,...

    UES ELK浅谈

    UES ELK 浅谈 ELK(Elasticsearch、Logstash、Kibana)是一套完整的集中式日志管理系统,它提供了一整套的解决方案,能够满足大规模的日志数据处理和分析需求。 ELK 简介 ELK 技术是由 Elasticsearch、Logstash ...

    浅谈微服务的发展以及可观测性.docx

    - 集中式日志收集:通过ELK栈(Elasticsearch、Logstash、Kibana)或Fluentd等工具收集分散的日志数据,便于统一管理和查询。 - 日志格式标准化:使用JSON等结构化格式存储日志,便于解析和分析。 2. **指标监控*...

    浅谈入侵检测技术浅谈入侵检测技术

    当前,基于网络的网络入侵检测系统(NIDS)成为主流,能够实时监控网络流量,检测异常行为。 2. 入侵检测技术原理 入侵检测技术主要包括信息收集和信号分析两个步骤。信息收集涉及网络入侵检测模块和主机入侵检测...

    浅谈基于互联网的新能源区域集控运行管理模式.pdf

    在【系统建设】过程中,采用先进的软件技术、开发模式和管理方式,如B/S架构,结合国际规范和标准,运用分布式数据库技术,实现容错和备份恢复,确保系统的稳定和可靠性。 【集控中心】扮演着监控和遥控的角色,...

    浅谈大数据环境下的信息系统审计.zip

    审计应涵盖权限管理、访问控制、日志监控等方面,以防止未授权访问和恶意攻击。 在审计过程中,审计师的角色也在转变。他们不再仅仅是检查员,而是成为业务伙伴,帮助企业提升数据治理能力,优化数据驱动的决策过程...

    Oracle_RAC原理浅谈

    DBWR(Database Writer)和SMON(System Monitor)等后台进程负责数据的写入和系统的监控。同时,用户进程和服务器进程通过PGA(Program Global Area)进行交互。控制文件、数据文件、重做日志文件以及参数文件等都...

    浅谈工厂智能化门禁系统.pdf

    系统采用模块化和分布式结构,便于功能的快速扩展和系统的稳定运行。 该智能化门禁系统实现了多项关键功能: 1. **入场规范管理**:通过线上审批流程,预先录入并审核入场人员信息,确保人员管理规范化,同时所有...

    浅谈Linux下SSH远程访问技术的应用.pdf

    - 日志监控:定期检查`/var/log/auth.log`等日志文件,发现异常登录尝试。 - 安全策略:限制root用户SSH登录,启用防火墙规则限制SSH端口访问,使用密钥认证而非口令。 总结来说,SSH在Linux环境中的应用是系统...

    浅谈大数据与云计算.pdf

    以视频监控为例,监控的视频中可能只有几秒钟的数据是有价值的。 4. 处理速度快(Velocity):大数据的这一特征与传统数据挖掘区别明显。随着数据量的急剧增加,快速处理和分析数据的能力成为了企业的核心竞争力。 ...

    浅谈基于云平台的智能家居系统应用.pdf

    信息融合与处理系统模型包括了日志管理单元、设备管理单元和数据格式转换模块,这些都是为了高效处理传感信息而设计的。 3. 分布式处理系统设计:分布式处理系统对大量、复杂结构的传感信息进行处理。为了确保系统...

    浅谈对机器人的认识.pdf

    这些实时状态信息对于监控机器人运行状况和执行精确动作至关重要。 2. **环境感知**:机器人通过各种传感器收集环境信息,如图像、声音、距离等。这些数据会被存储在数据库中,以便后续分析和处理。例如,自动驾驶...

    浅谈大数据环境下的云计算信息安全问题.pdf

    3. 审计和监控:通过日志管理和实时监控来追踪数据访问行为,及时发现并响应潜在的安全威胁。 4. 备份和灾难恢复计划:制定数据备份策略和灾难恢复计划,保证数据的高可用性和在灾难发生时能够快速恢复。 5. 安全...

    浅谈电网调度自动化系统的安全运行管理.pdf

    - **日常管理**:建立详细的运行日志和停运记录,记录设备运行状况,定期统计各项性能指标,制定缺陷管理制度,确保问题得到及时解决。 - **定期检测**:对主站和厂站系统进行定期检查,包括双机热备用切换、网络...

    浅谈Java消息队列总结篇(ActiveMQ、RabbitMQ、ZeroMQ、Kafka)

    4. 日志处理:使用消息队列解决大量日志传输的问题,实现日志数据的接收、存储和转发。 5. 消息通讯:使用消息队列实现点对点消息队列或聊天室等功能。 消息队列中间件示例: 1. 电商系统:使用高可用、可持久化的...

    Python示例-从基础到高手PDF

    第 9 章 python+Django 实现 Nagios 自动化添加监控项目 第 10 章 通过 python 和 websocket 构建实时通信系统[扩展 saltstack 监控] 第 11 章 关于 B+tree (附 python 模拟代码) 第 12 章 Python 编写的 socket ...

Global site tag (gtag.js) - Google Analytics