第一步:安装部署rocketMq,这一步网上教程很多,这里就不多说了,直接上代码
第二步:在pom文件中引入rocketMq的jar包
<!--rocketmq-->
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.2.0</version>
</dependency>
第三步:为了项目看起来更赶紧整洁一些,可以把一些基本的配置创建一个类
package xxx.xxx.xxx.rocketMQ.config;
/**
* @Description:rocketMq基本配置
* @Date: Created in 9:16 2021/3/18
*/
public class MqConfig {
//mq的默认端口
public static final String NAME_SERVER = "127.0.0.1:9876";
// 生产者组名-推送
public static final String PRODUCER_GROUP_PUSH_MSG = "producer_group_push_msg";
// 消费者组名-推送
public static final String CONSUMER_GROUP_PUSH_MSG = "consumer_group_push_msg";
/*********** 我们的项目商城模块推送使用了mq,拿在这里做例子 *************/
// topic- 商城(key和value都可以自定义)
public static final String MALL = "mall";
// 延时消息:关闭订单tag(topic MALL 的tag)
public static final String CLOSE_MALL_ORDER = "close_mall_order";
// 延时消息:自动收货tag(topic MALL 的tag)
public static final String RECEIPT_MALL_ORDER = "receipt_mall_order";
// 购物车tag(topic MALL 的tag)
public static final String DEL_CART = "del_cart";
// topic- 微信公众号
public static final String TOPIC_GZH = "topic_gzh";
// topic- 极光app推送
public static final String TOPIC_JG_APP = "topic_jg_app";
}
第四步:创建生产者
package xxx.xxx.xxx.rocketMQ.producer;
import xxx.xxx.xxx.rocketMQ.config.MqConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Slf4j
@Configuration
public class MqProducer {
@Bean
public DefaultMQProducer getRocketMQProducer() {
DefaultMQProducer producer;
producer = new DefaultMQProducer(MqConfig.PRODUCER_GROUP_PUSH_MSG);
producer.setNamesrvAddr(MqConfig.NAME_SERVER);
//Rocket默认开启了VIP通道,VIP通道端口为10911-2=10909。若Rocket服务器未启动端口10909,则报connect to <> failed,所以要关闭一下
producer.setVipChannelEnabled(false);
try {
producer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
return producer;
}
}
第五步:创建消费者
package xxx.xxx.xxx.rocketMQ.consumer;
import xxx.xxx.xxx.rocketMQ.config.MqConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.consumer.ConsumeFromWhere;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* @Description: 消费者
* @Date: Created in 9:18 2021/3/18
*/
@Slf4j
@Configuration
public class RocketMQConsumer {
//消费者需要引入监听
@Resource
private RocketMsgListener msgListener;
@Bean
public DefaultMQPushConsumer getRocketMQConsumer(){
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(MqConfig.CONSUMER_GROUP_PUSH_MSG);
consumer.setNamesrvAddr(MqConfig.NAME_SERVER);
//将监听引入消费者
consumer.registerMessageListener(msgListener);
//Rocket默认开启了VIP通道,VIP通道端口为10911-2=10909。若Rocket服务器未启动端口10909,则报connect to <> failed,关闭默认开启的vip通道
consumer.setVipChannelEnabled(false);
consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_LAST_OFFSET);
try {
//将要使用的TOPIC引入(可订阅多个tag,但是一个消息只能有一个tag)
//另一种写法:consumer.subscribe("TopicA", "tagA||tagB"); ‘*’代表MALL的所有tag
consumer.subscribe(MqConfig.MALL, "*");
consumer.subscribe(MqConfig.TOPIC_GZH, "*");
consumer.start();
}catch (MQClientException e){
e.printStackTrace();
}
return consumer;
}
}
第六步:创建消费者调用的监听 (方法中有微信公众号消息推送和商城模块的个别操作,仅供参考)
package xxx.xxx.xxx.rocketMQ.consumer;
import xxx.xxx.xxx.pay.utils.WxUtil;
import xxx.xxx.xxx.rocketMQ.config.MqConfig;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* 消息消费监听
*/
@Component
@Slf4j
public class RocketMsgListener implements MessageListenerConcurrently {
//微信的工具类
@Autowired
private WxUtil wxUtil;
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext context) {
if (CollectionUtils.isEmpty(list)) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
for (MessageExt messageExt : list) {
log.info("接受到的消息为:" + new String(messageExt.getBody()));
int reConsume = messageExt.getReconsumeTimes();
// 消息已经重试了3次,如果不需要再次消费,则返回成功
if (reConsume == 3) {
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
//查看消费的是否为微信公众号的Topic
if (messageExt.getTopic().equals(MqConfig.TOPIC_GZH)) {
String tags = messageExt.getTags();
switch (tags) {
case "push_gzh_template":
log.info("公众号推送 tag == >>" + tags);
JSONObject msgBody = JSONObject.fromObject(new String(messageExt.getBody()));
//targetOpenidStr or targetMemberType or orderType 为使用mq消息队列请求时自定义的
String targetOpenidStr = msgBody.getString("targetOpenidStr");
int targetMemberType = msgBody.getInt("targetMemberType");
int orderType = msgBody.getInt("orderType");
//Message()方法中的keys值
String orderId = messageExt.getKeys();
// 获取具体推送内容(根据自己的逻辑去获取值)
String resContent = "";
if (StringUtils.isNotBlank(targetOpenidStr)) {
//获取微信的token
String wxAccessToken = wxUtil.getAccess_token("微信的appId", "微信的ApiKey");
if (StringUtils.isBlank(wxAccessToken)) {
log.error("公众号推送客服消息失败: 未获取到wxAccessToken");
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
String[] openidArr = targetOpenidStr.split(",");
for (int i = 0; i < openidArr.length; i++) {
String s = openidArr[i];
JSONObject jsonObject = new JSONObject();
jsonObject.put("content", resContent);
//调用微信 推送客服消息
wxUtil.sxyPushCustomerMsg(s, wxAccessToken, jsonObject);
}
}
break;
default:
log.info("未匹配到Tag == >>" + tags);
break;
}
} else if (messageExt.getTopic().equals(MqConfig.MALL)) {//商城Topic
String tags = messageExt.getTags();
switch (tags) {
case MqConfig.CLOSE_MALL_ORDER: //关闭订单tag
// 要处理的订单 orderId为调用mq队列时将订单id录入body字段
String orderId = new String(messageExt.getBody());
// todo 自己的业务流程
xxxService.xxxxx(orderId);
break;
case MqConfig.RECEIPT_MALL_ORDER: //自动收货tag
// 要自动收货订单
String orderId2 = new String(messageExt.getBody());
xxxService.xxxxx(orderId);
break;
default:
log.info("未匹配到Tag == >>" + tags);
break;
}
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
第七步:微信的工具类WxUtil(用于例子中的微信公众号推送,不需要可以忽略)
package xxx.xxx.xxx.pay.utils;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @Description:
* @Date: Created in 10:29 2019/8/9
*/
@Slf4j
@Component
public class WxUtil {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* @Description: 获取access_token
* @date: 2019/7/12 11:14
* @param: [appid, appsecret]
* @return: java.lang.String
*/
public String getAccess_token(String appid, String appsecret) {
// 先判断redis中是否存在
String token = redisTemplate.opsForValue().get("自定义存入redis中的key");
if (StringUtils.isBlank(token) == false) {
// token还未过期,获取后直接返回,无需重新获取
log.info("======从Redis中获取的微信公众平台access_token:" + token);
return token;
}
// token已过期或不存在,需重新获取
redisTemplate.delete("自定义存入redis中的key");
String access_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" + "&appid=" + appid + "&secret=" + appsecret;
String message = "";
try {
URL url = new URL(access_url);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
connection.setDoInput(true);
connection.connect();
//获取返回的字符
InputStream inputStream = connection.getInputStream();
int size = inputStream.available();
byte[] bs = new byte[size];
inputStream.read(bs);
message = new String(bs, "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
//获取access_token
JSONObject jsonObject = JSONObject.fromObject(message);
log.info("======从微信api获取的微信公众平台access_token:" + jsonObject.toString());
String accessToken = jsonObject.getString("access_token");
String expires_in = jsonObject.getString("expires_in");
// 防止代码运行超时,提前1分钟让微信token失效
redisTemplate.opsForValue().set("自定义存入redis中的key", accessToken, Integer.valueOf(expires_in) - 60, TimeUnit.SECONDS);
return accessToken;
}
/**
* @Description: 尚泽商学院推送客服消息
* @date: 2019/7/12 11:25
* @param: [prepay_id, data]
* @return: void
*/
public void sxyPushCustomerMsg(String touser, String accesstoken, Object data) {
StringBuilder requestUrl = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=");
requestUrl.append(accesstoken);
JSONObject json = new JSONObject();
json.put("touser", touser);//设置openid
json.put("msgtype", "text");//设置模板消息内容
json.put("text", data);//设置模板消息内容
log.info("推送消息:" + json.toString());
Map<String, Object> map = null;
try {
HttpClient client = HttpClientBuilder.create().build();//构建一个Client
HttpPost post = new HttpPost(requestUrl.toString());//构建一个POST请求
StringEntity s = new StringEntity(json.toString(), "UTF-8");
s.setContentEncoding("UTF-8");
s.setContentType("application/json; charset=UTF-8");
post.setEntity(s);//设置编码,不然模板内容会乱码
HttpResponse response = client.execute(post);//提交POST请求
HttpEntity result = response.getEntity();//拿到返回的HttpResponse的"实体"
String content = EntityUtils.toString(result);
System.out.println(content);//打印返回的消息
JSONObject res = JSONObject.fromObject(content);//转为json格式
if (res != null && "ok".equals(res.get("errmsg"))) {
log.info("客服消息推送成功");
} else {
//封装一个异常
StringBuilder sb = new StringBuilder("客服消息推送失败\n");
sb.append(res.toString());
throw new Exception(sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description: 推送模板消息消息
* @date: 2019/7/12 11:25
* @param: [prepay_id, data]
* @return: void
*/
public void wxProPushTelMsg(String touser, String accesstoken, String prepay_id, Object data) {
// StringBuilder requestUrl = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/wxopen/template/send?access_token=");
StringBuilder requestUrl = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=");
// StringBuilder requestUrl = new StringBuilder("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=");
requestUrl.append(accesstoken);
JSONObject json = new JSONObject();
json.put("touser", touser);//设置openid
// json.put("template_id", admin_template_id);//设置模板id
// json.put("form_id", prepay_id);//设置formid
// json.put("data", data);//设置模板消息内容
json.put("msgtype", "text");//设置模板消息内容
json.put("text", data);//设置模板消息内容
log.info("推送消息:" + json.toString());
// json.put("page", "pages/index/main");//跳转微信小程序页面路径(非必须)
Map<String, Object> map = null;
try {
HttpClient client = HttpClientBuilder.create().build();//构建一个Client
HttpPost post = new HttpPost(requestUrl.toString());//构建一个POST请求
StringEntity s = new StringEntity(json.toString(), "UTF-8");
s.setContentEncoding("UTF-8");
s.setContentType("application/json; charset=UTF-8");
post.setEntity(s);//设置编码,不然模板内容会乱码
HttpResponse response = client.execute(post);//提交POST请求
HttpEntity result = response.getEntity();//拿到返回的HttpResponse的"实体"
String content = EntityUtils.toString(result);
System.out.println(content);//打印返回的消息
JSONObject res = JSONObject.fromObject(content);//转为json格式
//把信息封装到map
// map = MdzwUtils.parseJSON2Map(res);
if (res != null && "ok".equals(res.get("errmsg"))) {
System.out.println("模版消息发送成功");
} else {
//封装一个异常
StringBuilder sb = new StringBuilder("模版消息发送失败\n");
sb.append(map.toString());
throw new Exception(sb.toString());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
第八步:rocketMq的工具类
package com.shangze.third.rocketMQ;
import com.alibaba.fastjson.JSONObject;
import xxx.xxx.xxx.utils.FastJsonUtil;
import xxx.xxx.xxx.rocketMQ.config.MqConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* @Description:
* @Date: Created in 13:41 2020/4/7
*/
@Component
@Slf4j
public class PushMsgRocketUtil {
@Autowired
private DefaultMQProducer mqProducer;
/**
* @Description: 发消息
* @date: 17:46 2021/01/30
* @param: [topic, tags, keys, object,level(自定义的,延时的时间)]
* @return: void
*/
public void pushMsg(String topic, String tags, String keys, byte[] body, int level) {
String s = "发送消息:tag=" + tags ;
try {
//mq发送
Message sendMsg = new Message(topic, tags, keys, body);
if (level > 0){
s =s+",延时消息处理level="+ level;
sendMsg.setDelayTimeLevel(level);
}
SendResult sendResult = mqProducer.send(sendMsg);
log.info(s+ ",发送状态:" + sendResult.getSendStatus());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* @Description:
* @date: 2020/4/8 13:58
* @param: targetMemberId,
* @param: orderType,
* @param: orderId]
* @return: void
*/
public void pushToGzhByType(int targetMemberType, int orderType, String orderId) {
// 最终的openid字符串
StringBuilder sbuilder = new StringBuilder();
// todo 自己的逻辑......
String targetOpenidStr = sbuilder.toString();
if(StringUtils.isBlank(targetOpenidStr)){
return;
}
if (targetOpenidStr.substring(targetOpenidStr.length() - 1, targetOpenidStr.length()).equals(",")) {
targetOpenidStr = targetOpenidStr.substring(0, targetOpenidStr.length() - 1);
}
try {
JSONObject object = new JSONObject();
//与监听中的messageExt.getBody()中的key值对应
object.put("targetOpenidStr", targetOpenidStr);
object.put("targetMemberType", targetMemberType);
object.put("orderType", orderType);
//mq发送
Message sendMsg = new Message(MqConfig.TOPIC_GZH, "push_gzh_template", orderId, FastJsonUtil.beanToJson(object).getBytes("utf-8"));
mqProducer.send(sendMsg);
} catch (Exception e) {
e.printStackTrace();
}
}
}
第九步:测试推送
//入参均为自定义,根据自己的业务逻辑来写
public void test(int targetMemberType, int orderType, String orderId) {
pushMsgRocketUtil.pushToGzhByType(targetMemberType, orderType, orderId);
}
//入参均为自定义,根据自己的业务逻辑来写
public void pushMsgRocketUtil(String orderParentId) throws UnsupportedEncodingException {
// 清空购物车
JSONObject object = new JSONObject();
List<String> strings = new ArrayList<>();
strings.add("365320011394322432");
object.put("goodsIdList", strings);
object.put("memberId", "235657896754321");
pushMsgRocketUtil.pushMsg(MqConfig.MALL, MqConfig.DEL_CART, "", FastJsonUtil.beanToJson(object).getBytes("utf-8"), 0);
pushMsgRocketUtil.pushMsg(MqConfig.MALL, MqConfig.CLOSE_MALL_ORDER, orderParentId, orderParentId.getBytes("utf-8"),"24(需要延时的时间)");
pushMsgRocketUtil.pushMsg(MqConfig.MALL, MqConfig.RECEIPT_MALL_ORDER, orderParentId, orderParentId.getBytes("utf-8"), 12(需要延时的时间));
}