消息队列Pulsar的部署和使用

原文地址

原文链接

前言

这里简单介绍下Pulsar作为消息的的使用,不涉及原理和高可用集群的部署,原理以及比较重要的服务端/客户端参数,会在后续《消息队列Pulsar详解》中聊到,这里只是作为一个Pulsar入门的快速开始。关于不使用网络上其他热门消息队列KafkaRabbitmq的原因,一个是理由是平常用吐了,第二个是相对于老式消息队列,Pulsar在设计的时候解决了他们的一些痛点,并且在一定程度上集成了他们都优点,博采众长,但是由于是一个相对较新的中间件,有些功能不完善也是不可避免的,看大家自己选择。如果是生产用,那肯定是稳定性会更重要一些

服务端部署

详细的部署文档参考Pulsar Get started,这里我们仅介绍Docker容器的单例部署,基本的docker-compose.yml如下

version: "3"
services:
  pulsar:
    image: 'apachepulsar/pulsar:3.1.1'
    restart: always
    container_name: 'pulsar'
    hostname: 'pulsar'
    ports:
      - '9527:6650'
      - '9528:8080'
    privileged: true
    volumes:
      - pulsardata:/pulsar/data
      - pulsarconf:/pulsar/conf
    command: bin/pulsar standalone
volumes:
  pulsardata:
  pulsarconf:

由于是单机本地调用,这里不配置数据加密,身份证验证以及功能授权相关功能,如果有需要的小伙伴可以参考Pulsar security overview

客户端使用

我们这里仍然使用的是Java17+Spring3+Cloud4架构,同时引用Pulsar的方式是使用Spring的封装包,但是替换Pulsar核心为最高版本。之后可能会有一个大的更新,参考1.0.0-SNAPSHOT,但是目前还是以0.2.0为可用,可用随时关心最新版本以便升级

       <dependency>
            <groupId>org.springframework.pulsar</groupId>
            <artifactId>spring-pulsar-spring-boot-starter</artifactId>
            <version>0.2.0</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.pulsar</groupId>
                    <artifactId>pulsar-client-all</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.pulsar</groupId>
            <artifactId>pulsar-client-all</artifactId>
            <version>3.1.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>*</artifactId>
                    <groupId>org.apache.logging.log4j</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>javax.activation</artifactId>
                    <groupId>com.sun.activation</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>validation-api</artifactId>
                    <groupId>javax.validation</groupId>
                </exclusion>
            </exclusions>
        </dependency>

使用高版本的Java会有一个问题,PulsarDnsResolverUtil需要深度反射sun.net.InetAddressCachePolicy (in module java.base)的时候会没有权限,导致报错。在低版本,比如JDK8的的使用中则没有这个限制,所以高版本的在使用Pulsar时,在目前版本3.1.X还是需要增加运行参数--add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/sun.net=ALL-UNNAMED,有关于--add-opens更详细的解释参考–add-opens,关于Pulsar的解释同时可以参考DnsResolverUtil “Cannot get DNS TTL settings from sun.net.InetAddressCachePolicy class” in Java 17

首先在启动类增加启用注解

@someelse
@EnablePulsar
public class YuiApplication {
	public static void main(String[] args) {
		SpringApplication.run(YuiApplication.class, args);
	}
}

然后添加配置文件

spring:
  pulsar:
    client:
      service-url: pulsar://your_ip:9527

这里我们简单聊下Pulsar的几个核心概念,基本在其他消息中间件都能找到差不多对应的处理

Tenant

Pulsar中租户的概念和我们一般Saas场景中的租户一样,可以拥有自己的鉴权和命名空间等功能,用于完成一整个消息中间件的逻辑。目的是为了大型分布式系统对于不同应用场景的分割,更多内容参考Multi Tenancy

Namespaces

租户可以创建多个命名空间,用于区隔自己的业务范围,层级化管理,避免由于不同业务Topic重复,操作失误导致的消息界面混乱问题

Topic

消息传递的通道,链接的生产者和消费者需要拥有相同的Topic

Subscription

消费者获取生产者消息的流程

Subscription types

目前支持的订阅的类型有:

  • Exclusive:相同订阅名只有一个消费者可用获得消息
  • Failover:容灾处理,相同订阅名只有一个获得消息,但是会根据可用性选择消费者
  • Shared:即使是相同订阅名,所有消费者可以轮询获得消息
  • Key_Shared:根据消息Key进行Hash处理,从而选择消费者保证相同的Key的消息不会被传到不同消费者

Schema

用于对象的序列化,我们可以指定/自定义序列化方式,用于简化数据传递步骤,增加消息通知的兼容性。具体参考schema-get-started

示例

一般情况下,我们主要的消息中间件有两个功能,一个是希望所有订阅的对象都可以收到消息,此时我们只需要给予每个消费者不同的订阅名称,并且设置Exclusive订阅类型即可

public class MessageServiceImpl implements IMessageService {

    @Resource
    private PulsarTemplate<UserSendMsgDto> messagePulsarTemplate;

    private static final String PULSAR_TOPIC_MSG_DATA = "msg-data";

    @Override
    public void sendMsg(String userId, UserSendMsgParam param) {
        try {
            UserSendMsgDto sendObj = ConversionUtils.convertObj(param, UserSendMsgDto.class);
            sendObj.setSender(userId);
            messagePulsarTemplate.send(PULSAR_TOPIC_MSG_DATA, sendObj);
        } catch (Exception ex) {
            log.error("[op:sendMsg] sendMsg fail");
            ex.printStackTrace();
        }
    }

    @PulsarListener(
            subscriptionName = "msg-data-subscription-1",
            topics = PULSAR_TOPIC_MSG_DATA,
            subscriptionType = SubscriptionType.Exclusive,
            schemaType = SchemaType.JSON
    )
    public void topicListener1(UserSendMsgDto msg) {
        log.info("Received 1 String message: {}", JSON.toJSONString(msg));
    }

    @PulsarListener(
            subscriptionName = "msg-data-subscription-2",
            topics = PULSAR_TOPIC_MSG_DATA,
            subscriptionType = SubscriptionType.Exclusive,
            schemaType = SchemaType.JSON
    )
    public void topicListener2(UserSendMsgDto msg) {
        log.info("Received 2 String message: {}", JSON.toJSONString(msg));
    }

}

另一种是希望任意消费者收到消息处理即可,用于平衡负载,此时可以给予所有消费者相同的订阅名,并且设置Shared订阅类型即可

public class MessageServiceImpl implements IMessageService {

    @Resource
    private PulsarTemplate<UserSendMsgDto> messagePulsarTemplate;

    private static final String PULSAR_TOPIC_MSG_DATA = "msg-data";

    @Override
    public void sendMsg(String userId, UserSendMsgParam param) {
        try {
            UserSendMsgDto sendObj = ConversionUtils.convertObj(param, UserSendMsgDto.class);
            sendObj.setSender(userId);
            messagePulsarTemplate.send(PULSAR_TOPIC_MSG_DATA, sendObj);
        } catch (Exception ex) {
            log.error("[op:sendMsg] sendMsg fail");
            ex.printStackTrace();
        }
    }

    @PulsarListener(
            subscriptionName = "msg-data-subscription-1",
            topics = PULSAR_TOPIC_MSG_DATA,
            subscriptionType = SubscriptionType.Shared,
            schemaType = SchemaType.JSON
    )
    public void topicListener1(UserSendMsgDto msg) {
        log.info("Received 1 String message: {}", JSON.toJSONString(msg));
    }

    @PulsarListener(
            subscriptionName = "msg-data-subscription-1",
            topics = PULSAR_TOPIC_MSG_DATA,
            subscriptionType = SubscriptionType.Shared,
            schemaType = SchemaType.JSON
    )
    public void topicListener2(UserSendMsgDto msg) {
        log.info("Received 2 String message: {}", JSON.toJSONString(msg));
    }

}

原文地址

原文链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值