生产工厂类
import org.apache.pulsar.client.api.MessageId;
import org.apache.pulsar.client.api.Producer;
import org.apache.pulsar.client.api.PulsarClient;
import org.apache.pulsar.client.api.Schema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Component
public class ProducerFactory {
// 日志管理
private static final Logger logger = LoggerFactory.getLogger(ProducerFactory.class);
// Pulsar客户端对象
@Autowired
private PulsarClient client;
// 消息生产者集合
private ConcurrentHashMap<String, Producer<byte[]>> producerMap = new ConcurrentHashMap<>();
/**
* @param topic: 主题名称
* @desc: 记录消息生产者
* @creater: Rock.Zha
* @createDate 2020/8/19 10:17
*/
private Producer<byte[]> getTheProducer(String topic) {
Producer<byte[]> producer = producerMap.get(topic);
if (producer == null) {
synchronized (topic.intern()) {
producer = producerMap.get(topic);
if (producer == null) {
try {
// topic 生产者消息写到哪个主题中
// producerName 生产者的名字,不唯一即可,如果不设置,会有默认值
// sendTimeout 超时时间
// 默认情况下,当队列已满时,所有对Send和SendAsync方法的调用都将失败,除非您将BlockIfQueueFull设置为true
producer = client.newProducer(Schema.BYTES)
.topic(topic)/*.producerName(Thread.currentThread().getName() + "_" + topic)*/
.batchingMaxPublishDelay(10, TimeUnit.MILLISECONDS)
.sendTimeout(10, TimeUnit.SECONDS)
.blockIfQueueFull(true).create();
producerMap.putIfAbsent(topic, producer);
return producer;
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return producer;
}
/**
* @param topic: 生产者消息写到哪个主题中
* @param message: 生产者发送的内容
* @desc: 向Pulsar发送Message
* @creater: Rock.Zha
* @createDate 2020/8/18 14:22
*/
public void sendAsync(String topic, String message) {
Producer<byte[]> producer = this.getTheProducer(topic);
producer.sendAsync(message.getBytes()).thenAccept(msgId -> {
logger.info("Message with ID %s successfully sent---发送成功", msgId);
});
}
/**
* 同步发送消息
*
* @param topic
* @param message
* @return
*/
public String send(String topic, String message) {
String info = null;
try {
Producer<byte[]> producer = this.getTheProducer(topic);
MessageId messageId = producer.send(message.getBytes());
if (messageId != null) {
info = messageId.toString();
}
logger.info("pulsar无返值", info);
} catch (Exception e) {
logger.error("pulsar发送消息异常:", e);
}
return info;
}
/**
* 同步发送消息
*
* @param topic
* @param message
* @return
*/
public String sendDeliver(String topic, String message,Long delayTime) {
String info = null;
try {
Producer<byte[]> producer = this.getTheProducer(topic);
MessageId messageId =producer.newMessage()
.value(message.getBytes())
.deliverAfter(delayTime, TimeUnit.MINUTES) //单位可以自由选择
.send();
if (messageId != null) {
info = messageId.toString();
}
logger.info("pulsar无返值", info);
} catch (Exception e) {
logger.error("pulsar发送消息异常:", e);
}
return info;
}
}
2.配置类
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Scope
@Component
@Data
public class PulsarPathConfig {
@Value("${pulsar.env.path}")
private String pulsarPath;
}
import org.apache.pulsar.client.api.AuthenticationFactory;
import org.apache.pulsar.client.api.PulsarClient;
import org.apache.pulsar.client.api.PulsarClientException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
public class PulsarClientConfiguration {
@Value(value = "${pulsar.server.url}")
private String pulsarServerUrl; // PurSar路径,从配置文件获取
@Value(value = "${pulsar.env}")
private String pulsarEnv; // pulsar的连接环境、qa测试环境无需token、prd环境需要token
@Value(value = "${pulsar.token}") // pulsar的token加密方式
private String token;
static final String ENV_QA = "qa";
static final String ENV_PRD = "prd";
// @Bean注解注册bean,同时可以指定初始化和销毁方法
@Bean
@Scope("prototype")
public PulsarClient pulsarClient() throws PulsarClientException {
if (pulsarEnv.equals(ENV_QA)) {
return PulsarClient.builder().serviceUrl(pulsarServerUrl).build(); // 测试环境无需token
} else {
return PulsarClient.builder().serviceUrl(pulsarServerUrl).authentication(AuthenticationFactory.token(token)).build();// 生产环境需要token
}
}
}
3.枚举类
**
* 带参数的枚举常量(订阅者)
*/
public enum Subscription {
DEMO("demo-subscription"), // 测试
NULL_SUBSCRIPTION("");
private String name;
Subscription(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
/**
* 带参数的枚举常量(主题)
*/
public enum Topic {
DEMO("demo-topic", "demo-topic");
private String code;
private String name;
Topic(String code, String name) {
this.code = code;
this.name = name;
}
public static <T extends Topic> T getByCode(String code) {
//通过反射取出Enum所有常量的属性值
for (Topic topic : Topic.class.getEnumConstants()) {
if (code.equals(topic.getCode())) {
return (T) topic;
}
}
return (T) Topic.NULL_TOPIC;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
}
4.测试实例
发送代码
String messageId = producerFactory.send(pulsarPathConfig.getPulsarPath() + Topic.XX.getName(), JSON.toJSONString("111"));
if(EmptyUtils.isEmpty(messageId)){
return false;
}
return true;
发送或处理业务逻辑失败后,重新发送延迟消息
try {
//消费本条消息
//发送延迟消息
String messageId = producerFactory.sendDeliver(pulsarPathConfig.getPulsarPath() + Topic.XX.getName(),new String(message.getData(), "UTF-8"),5L );
consumer.acknowledge(message);
} catch (Exception e1) {
e1.printStackTrace();
log.error("延时业务异常:{}", e1.getMessage(), e1);
}
消费代码
@Slf4j
@Component
public class CreateTaskPaInfoListener extends Thread {
@Autowired
private PulsarClient client;
@Value("${pulsar.env.path}")
private String pulsarPath;
// 用于开启、停用监听
private volatile boolean started = false;
@PostConstruct
public void doReceive() {
this.started = true;
this.start();
}
@PreDestroy
public void shutdown() {
this.started = false;
}
@Override
public void run() {
Consumer<byte[]> consumer = null;
try {
consumer = client.newConsumer(Schema.BYTES)
.topic(pulsarPath + Topic.XX.getName())
.subscriptionName(Subscription.XX.getName())
.subscriptionType(SubscriptionType.Shared)
.batchReceivePolicy(BatchReceivePolicy.builder()
.maxNumMessages(100)
.maxNumBytes(1024 * 1024)
.timeout(5000, TimeUnit.MILLISECONDS)
.build())
.subscribe();
} catch (PulsarClientException e) {
e.printStackTrace();
}
while (started) {
Messages<byte[]> messages = null;
try {
messages = consumer.batchReceive();
} catch (PulsarClientException e) {
log.error("PulsarClientException接受消息异常:{}",e);
// 消息批量接收失败,全部重置。
consumer.negativeAcknowledge(messages);
//return;
}
for (Message<byte[]> message : messages) {
// 执行业务逻辑
this.execute(consumer, message);
}
}
try {
consumer.close();
} catch (PulsarClientException e) {
e.printStackTrace();
}
}
/**
* 执行业务逻辑
*
* @param consumer
* @param message
*/
private void execute(Consumer<byte[]> consumer, Message<byte[]> message) {
try {
String msg = new String(message.getData(), "UTF-8");
Test conditionBean = JSONObject.parseObject(msg, Test.class);
consumer.acknowledge(message);
} catch (Exception e) {
log.error("处理异常:{}", e.getMessage(), e);
consumer.negativeAcknowledge(message);
}
}
}