zoukankan      html  css  js  c++  java
  • [Dubbo开发]Dubbo日志插件实现(未打包)

    本文需要实现的是一个Dubbo的日志插件,日志插件的原理如上图所示。

    一、原理

    简单的Dubbo生产者和消费者实现服务调用的原理为:

    ①生产者在注册中心上注册服务;

    ②消费者在注册中心上订阅服务;

    ③一旦建立了订阅,消费者和生产者将进行点对点的通信;

    此时会产生一个问题:如果作为第三方需要对服务的调用过程进行日志记录(有实际生产需求),那么将失去对调用服务的控制。

    于是,在Dubbo简单生产者和消费者的基础上,增加一个日志服务器(本质上也是一个Dubbo生产者),并使用Dubbo拦截器实现日志的记录功能,实现的原理为:

    ④消费者向生产者发送request请求之前,先由拦截器(filter)向日志服务器发送一条日志,将请求的信息,诸如接口、方法名、参数等信息记录到日志服务器上;

    ⑤消费者对生产者发送request请求;

    ⑥生产者对消费者的请求进行响应(Response),当然前提是网络连接畅通,生产者服务可以正常被调用;

    ⑦在消费者收到响应之后,由拦截器(filter)再向日志服务器发送一条日志,将返回的信息,诸如返回值等信息记录到日志服务器上。

    以上就是以Dubbo拦截器方式实现的日志插件的原理。

    二、实现

    接下来是具体的实现过程:

    消费者端的文件目录结构

    FilterDesc.java

    package com.mohrss.service;
    /*
     * Class: FilterDesc
     * Function:拦截对象
     */
    public class FilterDesc {
    	private String interfaceName ;//接口名
        private String methodName ;//方法名
        private Object[] args ;//参数
        
        public FilterDesc(){
        	
        }
    
        public String getInterfaceName() {
            return interfaceName;
        }
    
        public void setInterfaceName(String interfaceName) {
            this.interfaceName = interfaceName;
        }
    
        public String getMethodName() {
            return methodName;
        }
    
        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }
    
        public Object[] getArgs() {
            return args;
        }
    
        public void setArgs(Object[] args) {
            this.args = args;
        }
    }
    

    LogFilter.java

    package com.mohrss.service;
    
    import com.alibaba.dubbo.rpc.*;
    import com.alibaba.dubbo.rpc.service.GenericService;
    import com.alibaba.fastjson.JSON;
    /*
     * Class:LogFilter
     * Function:日志拦截器
     */
    public class LogFilter implements Filter {
    	private static final FilterDesc filterReq = new FilterDesc();
    	private static final FilterDesc filterRsp = new FilterDesc();
    	private LogService ls = null;
    	public LogFilter(){
    		ls = (LogService)SpringContextUtil.getApplicationContext().getBean("testLogService");
    	}
    
    	public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    		try{
    			//request部分
    			filterReq.setInterfaceName(invocation.getInvoker().getInterface().getName());
    			filterReq.setMethodName(invocation.getMethodName());
    			filterReq.setArgs(invocation.getArguments());
    			ls.printLog("Dubbo请求数据" + JSON.toJSONString(filterReq));
    			
    			
    			Result result = invoker.invoke(invocation);
    			
    			if(GenericService.class != invoker.getInterface() && result.hasException()){
    				ls.printLog("Dubbo执行异常"+result.getException().toString());
    				
    			}else{
    				ls.printLog("Dubbo执行成功");
    				
    				filterRsp.setInterfaceName(invocation.getMethodName());
    				filterRsp.setMethodName(invocation.getMethodName());
    				filterRsp.setArgs(new Object[]{result.getValue()});
    				ls.printLog("Dubbo返回数据" + JSON.toJSONString(filterRsp));
    			}
    			
    			return result;
    		}
    		catch(RuntimeException e){
    			ls.printLog("Dubbo未知异常" + RpcContext.getContext().getRemoteHost()
    					+ ".service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
    					+ ", exception: " + e.getClass().getName() + ": " + e.getMessage());
    			throw e;
    		}
    	}
    }

    LogService.java(由日志服务器提供的接口文件,实际中应该是打包成jar包)

    package com.mohrss.service;
    
    public interface LogService {
    	public void printLog(String log);
    }
    

    ProviderService.java(由生产者提供的接口文件,实际中应该是打包成jar包)

    package com.mohrss.service;
    
    public interface ProviderService {
    	public void sayHello();
    	public int calc(int a, int b);
    }
    

    SpringContextUtil.java

    package com.mohrss.service;
    
    import org.springframework.beans.BeansException;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextAware;
    
    /*
     * Class: SpringContextUtil
     * Function:用于获得当前Spring上下文环境的工具类
     */
    public class SpringContextUtil implements ApplicationContextAware {
    
    	// Spring应用上下文环境  
        private static ApplicationContext context;  
      
        /** 
         * 实现ApplicationContextAware接口的回调方法。设置上下文环境 
         *  
         * @param applicationContext 
         */  
        public void setApplicationContext(ApplicationContext applicationContext) {  
            SpringContextUtil.context = applicationContext;  
        }  
      
        /** 
         * @return ApplicationContext 
         */  
        public static ApplicationContext getApplicationContext() {  
            return context;  
        }  
      
        public static Object getBean(String name) throws BeansException {  
            return context.getBean(name);  
        }  
    
    }
    

    com.alibaba.dubbo.rpc.filter(拦截器配置文件)

    logFilter=com.mohrss.service.LogFilter

    dubbo-consumer.xml(消费者自己的dubbo配置文件)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans.xsd  
            http://code.alibabatech.com/schema/dubbo  
            http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">   
             
        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->  
        <!-- <dubbo:application name="consumer" />   --> 
        
        <!-- 使用multicast广播注册中心暴露发现服务地址 -->  
        <!-- <dubbo:registry  protocol="zookeeper" address="127.0.0.1:2181" />  -->      
        
        <!-- 生成远程服务代理,可以和本地bean一样使用testProviderService -->  
        <!-- 对谁拦截,就给谁加filter -->
        <dubbo:reference id="testProviderService" interface="com.mohrss.service.ProviderService" filter="logFilter" async="true"/>
         
        <!-- 引入外部插件的配置文件 -->
    	<import resource="classpath:filter.xml" />
    </beans>
    

    dubbo.properties(更加规范的配置文件,可以避免经常修改dubbo-consumer.xml文件,文件名必须叫dubbo.properties,不然会加载不到)

    # Consumer application name
    dubbo.application.name=consumer
    # Zookeeper registry address
    dubbo.registry.protocol=zookeeper
    dubbo.registry.address=127.0.0.1:2181

    filter.xml(插件的配置文件,因为是作为插件使用,所以单独放在filter.xml中,在dubbo-consumer.xml里用import resource引用)

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans.xsd  
            http://code.alibabatech.com/schema/dubbo  
            http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">   
        <bean id="springContextUtil" class="com.mohrss.service.SpringContextUtil" />
        
        <dubbo:reference id="testLogService" interface="com.mohrss.service.LogService" /> 
    </beans>
    

    TestConsumerService.java 

    package com.mohrss.consumer;
    
    import java.io.IOException;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    import com.mohrss.service.ProviderService;
    /*
     * Class: TestConsumerService
     * Function: 程序入口,访问生产者,被拦截的对象
     */
    public class TestConsumerService {
    
        public static void main(String[] args) {
        	//读取xml配置文件
            ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"dubbo-consumer.xml"});
            context.start();
    
            ProviderService testService = (ProviderService) context.getBean("testProviderService");
            testService.calc(1, 2);
            testService.calc(5, 6);
            testService.sayHello();
            testService.calc(7, 8);
            
            try {
                System.in.read();
            } catch (IOException e) {       
                e.printStackTrace();
            }  
        }
    }
    

     生产者代码实现参考之前的博客:https://www.cnblogs.com/Vivianwang/p/9408493.html

     日志服务器文件目录:

     

    LogService.java

    package com.mohrss.service;
    
    public interface LogService {
    	public void printLog(String log);
    }
    

    LogServiceImpl.java

    package com.mohrss.service.impl;
    
    import org.springframework.stereotype.Service;
    
    import com.mohrss.service.LogService;
    
    @Service("logService")
    public class LogServiceImpl implements LogService {
    	public void printLog(String log){
    		System.out.println(log);
    	}
    }
    

    dubbo-log.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"  
    	xmlns:context="http://www.springframework.org/schema/context"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"  
        xsi:schemaLocation="http://www.springframework.org/schema/beans  
            http://www.springframework.org/schema/beans/spring-beans.xsd  
            http://code.alibabatech.com/schema/dubbo  
            http://code.alibabatech.com/schema/dubbo/dubbo.xsd
            http://www.springframework.org/schema/context  
            http://www.springframework.org/schema/context/spring-context-3.0.xsd ">   
            
    	<!--用注解方式实现bean-->
    	<context:component-scan base-package="com.mohrss.service">  
    		<context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/>   
    	</context:component-scan>
        
        <!-- 提供方应用信息,用于计算依赖关系 -->  
        <dubbo:application name="logservice"  />    
        
        <!-- 使用zookeeper注册中心暴露服务地址  配置后spring管理-->  
        <dubbo:registry address="zookeeper://127.0.0.1:2181" />     
        
        <!-- 用dubbo协议在20881端口暴露日志服务 -->  
        <dubbo:protocol name="dubbo" port="20881" />  
        
        <!-- 声明需要暴露的服务接口 -->  
        <dubbo:service interface="com.mohrss.service.LogService" ref="logService" />  
    </beans>
    

    TestLogService.java

    package com.mohrss.service;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestLogService {
    	public static void main(String[] args) {
            ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext(new String[]{"dubbo-log.xml"});
            context.start();
            System.out.println("日志服务已经注册成功!");
            try {
                System.in.read();//让此程序一直跑,表示一直提供服务
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    

      

     注:原创博客,转载请注明。

     

  • 相关阅读:
    Web开发常用知识点
    我的PHP之旅:开篇,走入开源的世界
    WPF Knowledge Points ContentControl和ContentPresenter的区别
    WPF Knowledge Points 控件状态利器:VisualStateManager详解
    WPF Control Hints ComboBox : 如何去掉ComboBox的DropDownButton
    WPF Control Hints ContextMenu : 怎么通过MenuItem的Click事件取得ContextMenuItem绑定的类实例?
    WPF Knowledge Points Binding.StringFormat不起作用的原理和解决
    AJAX请求 $.ajaxSetup方法的使用
    Html标签输出到前台并导出到Excel
    XML序列化和反序列化
  • 原文地址:https://www.cnblogs.com/Vivianwang/p/9451006.html
Copyright © 2011-2022 走看看