RabbitMQ应用一:分散业务处理

  对于需要处理很多工作的业务接口中,对各种接口的调用往往造成这个接口耗时过长,而各种接口的频繁调用,也对服务器造成了很大压力。用线程来解决前面的问题,在线程的新建和销毁都需要耗费时间,即使用线程池来实现,服务器照样也有压力。而提升服务器性能来解决后一个问题,性价比不够高。如果这时能用上MQ,不为是一个比较好的解决方法。


  如上图所示,在一个博彩类APP中,用户在界面下注比赛,会调用服务器的下注接口。下注接口是一个业务处理量非常大的接口。对用户的体验也非常主要,谁也不想下注一场比赛要等好几秒才下单成功。但这个接口不仅要把下注信息写入到数据库中,还要统计很多信息,比如用户的喜好,用户花费金额的排行榜,下注额在一场比赛的某一方的总额,总额过高,就要调低这一方的赔率,防止黑天鹅事件导致公司亏损过大等。

  在普通应用处理场景,一条流水线下来,每个业务处理都要花费时间,同步进行的处理,累积出来的时间就延长了。如果访问量过大,还会造成服务器崩溃。所以我们想到,最重要的主业务还是主服务器处理,而那些统计及其其他不重要、不需要及时反馈的业务处理通过MQ来发送。MQ再推送到其他副业务处理服务器上处理。减轻了服务器压力,提高了响应能力。

详细代码参考我的git项目:https://github.com/888xin/rabbitmq

里面的主服务器(也就是MQ的发送端)是项目:rabbit-pruducer , 副服务器(也就是MQ的消费端)是项目:rabbit-consumer


主服务器端(MQ发送端)先实现下注逻辑PlayBall.java

package com.lhx.contest;

import com.lhx.bean.Bet;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * Created by lhx on 2016/9/18 16:36
 *
 * @Description
 */
@Service
public class PlayBall {

    private Logger logger = LoggerFactory.getLogger(PlayBall.class);

    @Resource
    private AmqpTemplate fanoutTemplate;

    public void bet(long userId, long contestId, double money, int support) throws IOException {

        System.out.println(String.format("下注比赛了,用户为%d,赛事为%d,下注金额为%f,支持方为%d",userId, contestId, money, support));
        System.out.println("下注写入数据库");
        System.out.println("=============================");
        System.out.println("现在开始统计信息,发rabbitMq");
        Bet bet = new Bet();
        bet.setUserId(userId);
        bet.setContestId(contestId);
        bet.setMoney(money);
        bet.setSupport(support);
        ObjectMapper mapper = new ObjectMapper();
        String jsonStr = mapper.writeValueAsString(bet);
        fanoutTemplate.convertAndSend(jsonStr);
        System.out.println("+++++++++发送出去++++++");
    }

}

rabbitMq.xml配置:使用fanout交换器

<?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:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/rabbit
     http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd">
    <!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="connectionFactory"
                               username="guest" password="guest" host="localhost" port="5672" />


    <!--定义rabbit template用于数据的接收和发送 -->
    <rabbit:template id="amqpTemplate"  connection-factory="connectionFactory"
                     exchange="exchangeTest" />

    <!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
    <rabbit:admin connection-factory="connectionFactory" />



    <!--fanout 把一条消息通过多条队列传输出去-->
    <rabbit:template id="fanoutTemplate"  connection-factory="connectionFactory"
                     exchange="fanoutExchange"/>

    <!--定义queue -->
    <rabbit:queue name="fanoutQueue" durable="true" auto-delete="false" exclusive="false" />
    <rabbit:queue name="fanoutQueue2" durable="true" auto-delete="false" exclusive="false" />

    <!--fanout交换器-->
    <rabbit:fanout-exchange name="fanoutExchange">
        <rabbit:bindings>
            <rabbit:binding queue="fanoutQueue"></rabbit:binding>
            <rabbit:binding queue="fanoutQueue2"></rabbit:binding>
        </rabbit:bindings>
    </rabbit:fanout-exchange>

</beans>
最后建一个main函数来执行:
package com.lhx.run;

import com.lhx.contest.PlayBall;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by lhx on 2016/9/19 10:45
 *
 * @Description
 */
public class RunMain {

    public static void main(final String... args) throws Exception {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
        PlayBall  playBall = (PlayBall) ctx.getBean("playBall");
        playBall.bet(8L, 101L, 100.00D, 1);
        Thread.sleep(1000);
        ctx.destroy();
    }
}

副服务器端(MQ接收端)编写两个类(可以扩展任意多个类)来处理接收,需要先对服务器代码进行maven构建,从而可以直接引用服务器里面的bean对象。

package com.lhx.fanout;

import com.lhx.bean.Bet;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;

/**
 * Created by lhx on 2016/9/5 17:52
 *
 * @Description
 */
public class FanoutConsumer {

    public void getInfo(String foo) throws IOException {
        System.out.println("处理端1已经收到信息:" + foo);
        System.out.println("============处理端1开始处理===========");
        ObjectMapper mapper = new ObjectMapper();
        Bet bet = mapper.readValue(foo, Bet.class);
        System.out.println("============处理端1统计开始===========");
        System.out.println("下注人数+1,userId为:" + bet.getUserId());

    }


}

package com.lhx.fanout;

import com.lhx.bean.Bet;
import org.codehaus.jackson.map.ObjectMapper;

import java.io.IOException;

/**
 * Created by lhx on 2016/9/5 17:52
 *
 * @Description
 */
public class FanoutConsumer2{

    public void getInfo(String str) throws IOException {
        System.out.println("处理端2已经收到信息:" + str);
        System.out.println("============处理端2开始处理===========");
        ObjectMapper mapper = new ObjectMapper();
        Bet bet = mapper.readValue(str, Bet.class);
        System.out.println("============处理端2统计开始===========");
        System.out.println("下注金额累加,金额为:" + bet.getMoney());
    }

}

rabbitMq.xml设置这两个类来进行接收

<?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:rabbit="http://www.springframework.org/schema/rabbit"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     http://www.springframework.org/schema/rabbit
     http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd">
    <!--配置connection-factory,指定连接rabbit server参数 -->
    <rabbit:connection-factory id="connectionFactory"
                               username="guest" password="guest" host="localhost" port="5672" />


    <!--定义rabbit template用于数据的接收和发送 -->
    <rabbit:template id="amqpTemplate"  connection-factory="connectionFactory"
                     exchange="exchangeTest" />

    <!--通过指定下面的admin信息,当前producer中的exchange和queue会在rabbitmq服务器上自动生成 -->
    <rabbit:admin connection-factory="connectionFactory" />





    <!--fanout 接收-->
    <rabbit:queue name="fanoutQueue" durable="true" auto-delete="false" exclusive="false" />
    <rabbit:queue name="fanoutQueue2" durable="true" auto-delete="false" exclusive="false" />

    <rabbit:listener-container connection-factory="connectionFactory">
        <rabbit:listener ref="fanoutConsumer" method="getInfo" queues="fanoutQueue"/>
        <rabbit:listener ref="fanoutConsumer2" method="getInfo" queues="fanoutQueue2"/>
    </rabbit:listener-container>

    <bean id="fanoutConsumer" class="com.lhx.fanout.FanoutConsumer"/>
    <bean id="fanoutConsumer2" class="com.lhx.fanout.FanoutConsumer2"/>

</beans>

再新建一个main1函数来运行

package com.lhx.run;

import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * Created by lhx on 2016/9/19 10:49
 *
 * @Description
 */
public class RunMain {

    public static void main(String[] args) {
        AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("application.xml");
    }
}
先启动接收端的main函数,再启动发送端的main函数。
正在等待队列信息的推送,接收端的窗口这时显示的效果如下:

运行发送端的main函数,会把消息发送出去,效果图:



这个时候,马上切换到消费端窗口,会看到处理后的效果:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章