接到領導的一個需求,希望封裝一下kafka的消費者,可以從配置讀取topic進行消費;一開始首先想到的是用java kafka的高階api手工根據topic創建消費者,一個topic創建一個消費者,依賴zookeeper完成kafka內部的balance和其他管理。後來領導又提出不要依賴zookeeper,之前老師rebalance失敗。
調研了一下,手工實現類似spring kafka的kafkaListener功能又成本太高。KafkaListener註解的topics屬性又是常量,不能變量引入。之前看了一篇文章,在@PostConstruct或者繼承InitializingBean的重寫afterPropertiesSet方法的時候,System.setProperty手工添加topic到系統變量裏,然後通過:
@org.springframework.kafka.annotation.KafkaListener(topics = "${topicName}")
這樣的方式可以把配置中的topic讀到註解的參數中,但是尼瑪這個方法只能添加單個topic,多個topic怎麼辦!!!
因爲太生氣,原諒我說了那麼多廢話。最後選擇用反射的方式手工設置這個屬性,代碼如下:
@Service
public class KafkaHandlerService {
@Autowired
private Environment env;
@PostConstruct
public void initKafkaHandler() {
//比如topics是 topic1,topic2
String topics = env.getProperty("xxx.xxx");
String[] topicArray = topics.split(",");
//反射,listen是方法名,ConsumerRecord.class是參數的類,找到這個監聽方法修改topics的值
Method listen = KafkaHandlerService.class.getDeclaredMethod("listen", ConsumerRecord.class);
KafkaListener kafkaListener = listen.getAnnotation(KafkaListener.class);
InvocationHandler invocationHandler = Proxy.getInvocationHandler(kafkaListener);
Field hField = invocationHandler.getClass().getDeclaredField("memberValues");
//默認的訪問權限是不行的,要修改成true才能修改屬性的值
hField.setAccessible(true);
Map memberValues = (Map) hField.get(invocationHandler);
memberValues.put("topics", topicArray);
}
//topics屬性都不用設置了,因爲已經通過反射設置好了
@KafkaListener
public void listen(ConsumerRecord<?, ?> record) {
//爲所欲爲
}
}