看源码最好理解的就是从使用开始,本节从客户端连接构建开始,逐步到broker
一、使用案例
PulsarClient build = PulsarClient.builder()
.listenerThreads(1)
.ioThreads(1)
.serviceUrl("pulsar://127.0.0.1:6650")
.build();
这是建造者模式,参数比较多使用建造者设计模式比较直观清晰
二、PulsarClient实现类缩减版
builder实现是ClientBuilderImpl
,.listenerThreads、.ioThreads都是ClientBuilderImpl的API,参数统一放到ClientConfigurationData
中,该对象保存client配置信息。
简化版:
public class ClientBuilderImpl implements ClientBuilder {
ClientConfigurationData conf;
public PulsarClient build(){
...
PulsarClient client = new PulsarClientImpl(this.conf);
return client;
}
public ClientBuilder ioThreads(int numIoThreads) {
this.conf.setNumIoThreads(numIoThreads);
return this;
}
public ClientBuilder listenerThreads(int numListenerThreads) {
this.conf.setNumListenerThreads(numListenerThreads);
return this;
}
...
}
三、PulsarClient的参数详解
下面是client的全部参数,先介绍一下关键的配置参数,然后再看pulsarClient的构造初始化了哪些内容?都是干嘛的?
// broker地址
private String serviceUrl;
private transient ServiceUrlProvider serviceUrlProvider;
// auth开头都是权限相关的
private Authentication authentication;
private String authPluginClassName;
private String authParams;
private Map<String, String> authParamMap;
// 客户端和服务端接口交互的超时时间
private long operationTimeoutMs = 30000;
// 多久打印一下数据,比如消费者的消费速度、字节数、消费者总共接收了多少消息等
private long statsIntervalSeconds = 60;
// 客户端和服务端接口交互用的线程数
private int numIoThreads = 1;
// 消费者的线程数
private int numListenerThreads = 1;
// 连接池用的,相当于最大连接数创建
private int connectionsPerBroker = 1;
// tcp nagle算法
private boolean useTcpNoDelay = true;
// 下面是tls协议相关的
private boolean useTls = false;
private String tlsTrustCertsFilePath = "";
private boolean tlsAllowInsecureConnection = false;
private boolean tlsHostnameVerificationEnable = false;
// 用于创建Semaphore,控制客户端向服务端请求速率 例如查询topic属于哪个broker,查询分区数
private int concurrentLookupRequest = 5000;
private int maxLookupRequest = 50000;
// 重定向次数 查询topic属于哪个broker时随机选个broker看看是否存在,不存在定向到其他broker
private int maxLookupRedirects = 20;
// 当前请求过多,对服务端造成限流,会拒绝客户端请求 场景:还是查询topic属于哪个broker和查询分区数
private int maxNumberOfRejectedRequestPerConnection = 50;
// channel建立后,创建一个定时心跳任务保活channel,该参数是多久发一次心跳请求
private int keepAliveIntervalSeconds = 30;
// 客户端与服务端的连接超时时间
private int connectionTimeoutMs = 10000;
// pulsar有admin web接口,该参数是http请求的超时时间
private int requestTimeoutMs = 60000;
// 退避初始时间 例如:连接服务端失败了,是不是要重试,那过多久重试呢,重试后又失败了,再过多久重试呢,比如初始1秒,再失败通过一套计算方式,得出下次重试3秒后,再失败10秒后 就是干这个用的
private long initialBackoffIntervalNanos = TimeUnit.MILLISECONDS.toNanos(100);
// 接上面:不能一直重试,因为越来越久,总得有个封顶然后再从初识或某个点开始 这个就是封顶值
private long maxBackoffIntervalNanos = TimeUnit.SECONDS.toNanos(60);
// EpollEventLoop调用select时的等待策略
private boolean enableBusyWait = false;
private String listenerName;
private boolean useKeyStoreTls = false;
private String sslProvider = null;
private String tlsTrustStoreType = "JKS";
private String tlsTrustStorePath = null;
private String tlsTrustStorePassword = null;
private Set<String> tlsCiphers = Sets.newTreeSet();
private Set<String> tlsProtocols = Sets.newTreeSet();
// 发送消息后,再没有收到服务端回调数据还在内存中。用于控制同一时刻内存中存在的消息大小限制
private long memoryLimitBytes = 0;
private String proxyServiceUrl;
private ProxyProtocol proxyProtocol;
private boolean enableTransaction = false;
private Clock clock = Clock.systemDefaultZone();
终于介绍完了。。。
四、PulsarClient构造实现
private PulsarClientImpl(ClientConfigurationData conf, EventLoopGroup eventLoopGroup, ConnectionPool cnxPool, Timer timer,
boolean createdEventLoopGroup, boolean createdCnxPool) throws PulsarClientException {
try {
this.createdEventLoopGroup = createdEventLoopGroup;
this.createdCnxPool = createdCnxPool;
// 创建连接用
this.eventLoopGroup = eventLoopGroup;
setAuth(conf);
this.conf = conf;
clientClock = conf.getClock();
conf.getAuthentication().start();
// 连接池,将来创建连接用的上面的eventLoopGroup
this.cnxPool = cnxPool;
// 主要是调用用户接收消息的回调方法时由internalExecutorService切换为externalExecutorProvider
externalExecutorProvider = new ExecutorProvider(conf.getNumListenerThreads(), "pulsar-external-listener");
// 主要是消费者接收服务端端消息用
internalExecutorService = new ExecutorProvider(conf.getNumIoThreads(), "pulsar-client-internal");
// 用户查询topic所在broker地址、分区数、schema信息
if (conf.getServiceUrl().startsWith("http")) {
lookup = new HttpLookupService(conf, eventLoopGroup);
} else {
lookup = new BinaryProtoLookupService(this, conf.getServiceUrl(), conf.getListenerName(), conf.isUseTls(), externalExecutorProvider.getExecutor());
}
// 延迟任务
if (timer == null) {
this.timer = new HashedWheelTimer(getThreadFactory("pulsar-timer"), 1, TimeUnit.MILLISECONDS);
needStopTimer = true;
} else {
this.timer = timer;
}
// 将来添加生产者
producers = Collections.newSetFromMap(new ConcurrentHashMap<>());
// 将来添加消费者
consumers = Collections.newSetFromMap(new ConcurrentHashMap<>());
// 事务
if (conf.isEnableTransaction()) {
tcClient = new TransactionCoordinatorClientImpl(this);
try {
tcClient.start();
} catch (Throwable e) {
log.error("Start transactionCoordinatorClient error.", e);
throw new PulsarClientException(e);
}
}
// 上面参数讲过
memoryLimitController = new MemoryLimitController(conf.getMemoryLimitBytes());
state.set(State.Open);
} catch (Throwable t) {
shutdown();
shutdownEventLoopGroup(eventLoopGroup);
closeCnxPool(cnxPool);
throw t;
}
}
五、总结
总结:创建PulsarClient,主要初始化线程组、连接池、权限模型、查询broker分区 topic所属等用的LookupService、流量控制、将来存放生产消费者的集合
主要的一点:PulsarClient创建完成是没有创建连接的、也没有和Broker的交互,都只是在准备。