1. 程式人生 > >[Dubbo開發]Dubbo日誌插件實現(未打包)

[Dubbo開發]Dubbo日誌插件實現(未打包)

object aps utf eth -- util getc 設置 服務器

技術分享圖片

本文需要實現的是一個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();
        }
    }
}

  

註:原創博客,轉載請註明。

 

[Dubbo開發]Dubbo日誌插件實現(未打包)