顯示具有 Spring 標籤的文章。 顯示所有文章
顯示具有 Spring 標籤的文章。 顯示所有文章

2015年12月30日 星期三

Message Queue Intro & Spring RabbitMQ Example

http://villebez.logdown.com/posts/2015/12/21/message-queue-intro-spring-rabbitmq-example
進入這次主題之前,先讓大家認識
什麼是 Message Queue, Wikipedia.
在電腦科學中,訊息佇列(英語:Message queue)是一種行程間通信或同一行程的不同執行緒間的通信方式,軟體的貯列用來處理一系列的輸入,通常是來自使用者。訊息佇列提供了非同步的通信協定,也就是說:訊息的傳送者和接收者不需要同時與訊息佇列互交。訊息會儲存在佇列中,直到接收者取回它。
訊息佇列(Message Queue,簡稱MQ),從字面意思上看,本質是個佇列,FIFO先入先出,只不過佇列中存放的內容是message而已。其主要用途:不同行程Process/執行緒Thread之間通信。
最初起源于金融系統,用於在分散式系統中存儲轉發消息,在易用性、擴展性、高可用性等方面表現不俗。
訊息中介軟體主要用於元件之間的解耦,訊息的發送者無需知道訊息使用者的存在,反之亦然。
接著要準備這次的環境,下載安裝server,RabbitMQ
RabbitMQ Server Commands,詳見手冊
基本上windows上安裝方式就是下一步、下一步就好了,
安裝完可以連看看RabbitMQ Management Page,輸入default user
Management Page就不詳述了,自己玩玩看吧。
http://localhost:15672/
default username/password:guest/guest
server安裝完後就可以進入這次的主題
其實Spring AMQP管網已經有兩個簡易範例了,一個是純Java,一個是使用Spring的方式
我是使用Spring的方式,做些微調整,並記錄下須注意的地方
首先New Gradle Project,設定dependencies
dependencies {
compile 'org.springframework.amqp:spring-rabbit:1.5.3.RELEASE'
}
Spring Context Config (applicationContext.xml)

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">

    <rabbit:connection-factory id="connectionFactory"
        host="127.0.0.1" port="5672" username="guest" password="guest" />

    <rabbit:template id="amqpTemplate" connection-factory="connectionFactory"
        exchange="myExchange" />
 
    <rabbit:admin connection-factory="connectionFactory" />

  <rabbit:queue name="myQueue" />

    <rabbit:fanout-exchange name="myExchange">
        <rabbit:bindings>
            <rabbit:binding queue="myQueue" />
        </rabbit:bindings>
    </rabbit:fanout-exchange>

    <rabbit:listener-container id="rabbitMQContainer"
        connection-factory="connectionFactory" auto-startup="false">
        <rabbit:listener ref="messageHandler" queue-names="myQueue" />
    </rabbit:listener-container>

    <bean id="messageHandler" class="MessageHandler"/>
</beans>


注意設定xmlns:rabbit以及xsi:schemaLocation (http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd)
auto-startup預設為true,也就是spring載入後consumer listener就會啟動並且keep alive
與官網做法不同rabbit:listener沒有設定method屬性,後面說明

Message Handler
public class MessageHandler implements MessageListener {
    public void onMessage(Message message) {
        System.out.println(message);
    }
}
這裡跟官網用法不同,是去實作org.springframework.amqp.core.MessageListener
所以Spring Content Config並沒有設定rabbit:listener的method屬性,
好處是可以拿到更多Message的資訊,不是只有Body的字串,
要用哪種方式沒有對錯,單看需求而定!!
Output Message Object
(Body:'Test'; ID:null; Content:text/plain; Headers:{}; Exchange:myExchange; RoutingKey:; Reply:null; DeliveryMode:PERSISTENT; DeliveryTag:1)
Main
public static void main(String[] args) {
        ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        AmqpTemplate amqpTemplate = ctx.getBean("amqpTemplate", AmqpTemplate.class);
        amqpTemplate.convertAndSend("Test");
}

2015年5月22日 星期五

Spring AOP Around Advice 實作 Exception 處理

緣由:專案內需要將Exception獨立抽出來處理,為了不影響原有的商業邏輯,並且讓code reuse
所以第一時間就想到要用AOP實作,順便回去練一下很久沒碰的Spring AOP,並複習一下觀念
問題:Exception處理
解決方式:Spring AOP Around Advice
好處:
1. 統一錯誤訊息,讓每個工程師各寫各的錯誤處理風格可以得到統一。
2. 讓其他人可以專注在商務邏輯
3. 程式看起來比較簡潔乾淨
基本上我不解釋觀念,因為網路上已經有很多很棒的解說了
我也還在學習,很多東西其實第一次看完全都不懂,很抽象,
但隨著時間、經驗的累積,又回去看了第二次第三次,就會越來越有感覺
spring-aop-diagram.jpgspring-aop-diagram.jpg
In Spring AOP, 4 type of advices are supported :
  • Before advice – Run before the method execution
  • After returning advice – Run after the method returns a result
  • After throwing advice – Run after the method throws an exception
  • Around advice – Run around the method execution, combine all three advices above.
============================= 分隔線 =================================
我是使用 Around advice ,其實也就是把程式裡散落在各處的try catch集中至ExceptionAdvice統一處理。
build.gradle
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'maven'

compileJava.options.encoding = 'UTF-8'
def springVersion = "3.2.6.RELEASE"

dependencies {
    compile 'org.aspectj:aspectjweaver:1.7.1'
    compile 'org.slf4j:jcl-over-slf4j:1.7.1'
    compile 'org.slf4j:slf4j-log4j12:1.7.1'
    compile "org.springframework:spring-aop:$springVersion"
    compile "org.springframework:spring-context:$springVersion"
}
applicationContext.xml


    

    
    
    
    
        
            
            
        
     

com.test.aop.advice.ExceptionAdvice
public class ExceptionAdvice {
    private static Logger log = LoggerFactory.getLogger(ExceptionAdvice.class);

    public Object exceptionHandler(ProceedingJoinPoint proceedingJoinPoint) {
        Object value = null;

        try {
            value = proceedingJoinPoint.proceed();
        } catch (Throwable e) {
            log.info(proceedingJoinPoint.getSignature().getName() + " error");
            
            String errorMsg = "內部錯誤:" + e.getMessage();
            String errorCode = "500";
            
            Map resultJsonMap = new HashMap();
            resultJsonMap.put("successful", false);
            resultJsonMap.put("errorCode", errorCode);
            resultJsonMap.put("errorMsg", errorMsg );

            value = resultJsonMap;
            log.info(value.toString());
        }
        return value;
    }
com.test.service.TestService
public class TestService {
    private static ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    public String runTest() {
        throw new IllegalArgumentException("這是測試");
    }

    public static void main(String[] args) {
        TestService ts = ac.getBean("testService", TestService.class);
        ts.runTest();
    }
}
參考資料