作者 朱兆平

多线程及线程锁

@@ -164,6 +164,7 @@ message-bus: @@ -164,6 +164,7 @@ message-bus:
164 consumer-group-id: HYYWGroup 164 consumer-group-id: HYYWGroup
165 kafka: 165 kafka:
166 bootstrap-servers: 192.168.1.73:32771 166 bootstrap-servers: 192.168.1.73:32771
  167 + max-poll-records: 60
167 consumer: 168 consumer:
168 properties: 169 properties:
169 security: 170 security:
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 <packaging>jar</packaging> 6 <packaging>jar</packaging>
7 <groupId>com.tianbo</groupId> 7 <groupId>com.tianbo</groupId>
8 <artifactId>messagebus-trans-message</artifactId> 8 <artifactId>messagebus-trans-message</artifactId>
9 - <version>1.0-feign-kafka</version> 9 + <version>1.4-feign-kafka</version>
10 <description>消息转发服务</description> 10 <description>消息转发服务</description>
11 11
12 <parent> 12 <parent>
@@ -3,6 +3,7 @@ package com.tianbo.messagebus; @@ -3,6 +3,7 @@ package com.tianbo.messagebus;
3 import org.springframework.boot.SpringApplication; 3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
5 import org.springframework.boot.web.client.RestTemplateBuilder; 5 import org.springframework.boot.web.client.RestTemplateBuilder;
  6 +import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
6 import org.springframework.cloud.netflix.eureka.EnableEurekaClient; 7 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
7 import org.springframework.cloud.openfeign.EnableFeignClients; 8 import org.springframework.cloud.openfeign.EnableFeignClients;
8 import org.springframework.context.annotation.Bean; 9 import org.springframework.context.annotation.Bean;
@@ -15,6 +16,7 @@ import org.springframework.web.client.RestTemplate; @@ -15,6 +16,7 @@ import org.springframework.web.client.RestTemplate;
15 @EnableEurekaClient 16 @EnableEurekaClient
16 @EnableFeignClients 17 @EnableFeignClients
17 @EnableScheduling 18 @EnableScheduling
  19 +@EnableCircuitBreaker
18 public class MessageTransApplication { 20 public class MessageTransApplication {
19 21
20 public static void main(String[] args) { 22 public static void main(String[] args) {
@@ -63,8 +63,8 @@ public class KafkaConsumerConfig { @@ -63,8 +63,8 @@ public class KafkaConsumerConfig {
63 propsMap.put(ConsumerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG,60000); 63 propsMap.put(ConsumerConfig.CONNECTIONS_MAX_IDLE_MS_CONFIG,60000);
64 propsMap.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG,50); 64 propsMap.put(ConsumerConfig.FETCH_MIN_BYTES_CONFIG,50);
65 propsMap.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG,400); 65 propsMap.put(ConsumerConfig.FETCH_MAX_WAIT_MS_CONFIG,400);
66 - propsMap.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 20);  
67 - propsMap.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 10*60*1000); 66 + propsMap.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 60);
  67 + propsMap.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, 120*1000);
68 //} 68 //}
69 return propsMap; 69 return propsMap;
70 } 70 }
@@ -13,6 +13,7 @@ import java.util.Map; @@ -13,6 +13,7 @@ import java.util.Map;
13 @Slf4j 13 @Slf4j
14 public class ConsumersCache { 14 public class ConsumersCache {
15 public static Map<String, KafkaConsumer<String, String>> consumerMap; 15 public static Map<String, KafkaConsumer<String, String>> consumerMap;
  16 + public static Map<String, Boolean> consumerLock;
16 17
17 public static Map<String, KafkaConsumer<String, String>> getConsumerMap() { 18 public static Map<String, KafkaConsumer<String, String>> getConsumerMap() {
18 if (consumerMap !=null){ 19 if (consumerMap !=null){
@@ -22,4 +23,30 @@ public class ConsumersCache { @@ -22,4 +23,30 @@ public class ConsumersCache {
22 consumerMap = new HashMap<String, KafkaConsumer<String, String>>(); 23 consumerMap = new HashMap<String, KafkaConsumer<String, String>>();
23 return consumerMap; 24 return consumerMap;
24 } 25 }
  26 +
  27 + public static Map<String, Boolean> getConsumerLock() {
  28 + if (consumerMap !=null){
  29 + return consumerLock;
  30 + }
  31 + log.trace("初始化消费者锁缓存");
  32 + consumerLock = new HashMap<String, Boolean>();
  33 + return consumerLock;
  34 + }
  35 +
  36 + public static void lock(String key){
  37 + getConsumerLock();
  38 + consumerLock.put(key,true);
  39 + }
  40 +
  41 + public static void unlock(String key){
  42 + getConsumerLock();
  43 + consumerLock.put(key,false);
  44 + }
  45 +
  46 + public static Boolean getLockState(String key){
  47 + getConsumerLock();
  48 + return consumerLock.get(key);
  49 + }
  50 +
  51 +
25 } 52 }
  1 +package com.tianbo.messagebus.model;
  2 +
  3 +import java.util.List;
  4 +
  5 +public class Cache {
  6 +
  7 + public static List<MSGS> SEND_CACHE;
  8 +}
1 package com.tianbo.messagebus.model; 1 package com.tianbo.messagebus.model;
2 2
  3 +import com.alibaba.fastjson.JSONObject;
  4 +
3 public class MSG { 5 public class MSG {
4 /** 6 /**
5 * 具体消息头部信息 7 * 具体消息头部信息
@@ -8,7 +10,7 @@ public class MSG { @@ -8,7 +10,7 @@ public class MSG {
8 /** 10 /**
9 * 具体消息支持JSON字符串或者XML 11 * 具体消息支持JSON字符串或者XML
10 */ 12 */
11 - private String BODY; 13 + private Object BODY;
12 14
13 public HEADER getHEADER() { 15 public HEADER getHEADER() {
14 return HEADER; 16 return HEADER;
@@ -18,16 +20,16 @@ public class MSG { @@ -18,16 +20,16 @@ public class MSG {
18 this.HEADER = HEADER; 20 this.HEADER = HEADER;
19 } 21 }
20 22
21 - public String getBODY() { 23 + public Object getBODY() {
22 return BODY; 24 return BODY;
23 } 25 }
24 26
25 - public void setBODY(String BODY) { 27 + public void setBODY(Object BODY) {
26 this.BODY = BODY; 28 this.BODY = BODY;
27 } 29 }
28 30
29 @Override 31 @Override
30 public String toString() { 32 public String toString() {
31 - return this.BODY; 33 + return JSONObject.toJSONString(this.BODY);
32 } 34 }
33 } 35 }
1 package com.tianbo.messagebus.service; 1 package com.tianbo.messagebus.service;
2 2
3 import com.alibaba.fastjson.JSON; 3 import com.alibaba.fastjson.JSON;
4 -import com.alibaba.fastjson.JSONObject;  
5 import com.tianbo.messagebus.config.KafkaConsumerConfig; 4 import com.tianbo.messagebus.config.KafkaConsumerConfig;
6 import com.tianbo.messagebus.controller.response.ResultJson; 5 import com.tianbo.messagebus.controller.response.ResultJson;
7 import com.tianbo.messagebus.kafka.ConsumersCache; 6 import com.tianbo.messagebus.kafka.ConsumersCache;
8 -import com.tianbo.messagebus.model.HEADER;  
9 -import com.tianbo.messagebus.model.MSG;  
10 import com.tianbo.messagebus.model.MSGS; 7 import com.tianbo.messagebus.model.MSGS;
11 import com.tianbo.messagebus.myinterface.KafkaSendApi; 8 import com.tianbo.messagebus.myinterface.KafkaSendApi;
  9 +import feign.FeignException;
12 import lombok.extern.slf4j.Slf4j; 10 import lombok.extern.slf4j.Slf4j;
13 import org.apache.commons.lang.StringUtils; 11 import org.apache.commons.lang.StringUtils;
14 import org.apache.kafka.clients.consumer.ConsumerConfig; 12 import org.apache.kafka.clients.consumer.ConsumerConfig;
@@ -17,6 +15,8 @@ import org.apache.kafka.clients.consumer.ConsumerRecords; @@ -17,6 +15,8 @@ import org.apache.kafka.clients.consumer.ConsumerRecords;
17 import org.apache.kafka.clients.consumer.KafkaConsumer; 15 import org.apache.kafka.clients.consumer.KafkaConsumer;
18 import org.springframework.beans.factory.annotation.Autowired; 16 import org.springframework.beans.factory.annotation.Autowired;
19 import org.springframework.beans.factory.annotation.Value; 17 import org.springframework.beans.factory.annotation.Value;
  18 +import org.springframework.scheduling.annotation.Async;
  19 +import org.springframework.scheduling.annotation.EnableAsync;
20 import org.springframework.scheduling.annotation.Scheduled; 20 import org.springframework.scheduling.annotation.Scheduled;
21 import org.springframework.stereotype.Service; 21 import org.springframework.stereotype.Service;
22 22
@@ -26,6 +26,7 @@ import java.util.Map; @@ -26,6 +26,7 @@ import java.util.Map;
26 26
27 @Service 27 @Service
28 @Slf4j 28 @Slf4j
  29 +@EnableAsync
29 public class KafkaReadProcessor { 30 public class KafkaReadProcessor {
30 31
31 /** 32 /**
@@ -42,12 +43,20 @@ public class KafkaReadProcessor { @@ -42,12 +43,20 @@ public class KafkaReadProcessor {
42 43
43 @Value("${kafka.bootstrap-servers}") 44 @Value("${kafka.bootstrap-servers}")
44 private String servers; 45 private String servers;
  46 +
  47 + @Value("${kafka.max-poll-records}")
  48 + private String maxPollRecords;
45 /** 49 /**
46 * 失败重发请求次数 50 * 失败重发请求次数
47 */ 51 */
48 private static final int RETRY_TIMES= 10; 52 private static final int RETRY_TIMES= 10;
49 53
50 - @Scheduled(fixedRate = 6000) 54 + /**
  55 + * 多线程中consumer锁
  56 + */
  57 +
  58 + @Async
  59 + @Scheduled(fixedRate = 3000)
51 public void msgProcess(){ 60 public void msgProcess(){
52 try{ 61 try{
53 if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(groupName)){ 62 if (StringUtils.isNotEmpty(userName) && StringUtils.isNotEmpty(groupName)){
@@ -55,12 +64,25 @@ public class KafkaReadProcessor { @@ -55,12 +64,25 @@ public class KafkaReadProcessor {
55 Map<String, Object> map=new KafkaConsumerConfig().consumerConfigs(); 64 Map<String, Object> map=new KafkaConsumerConfig().consumerConfigs();
56 map.put(ConsumerConfig.GROUP_ID_CONFIG, groupName); 65 map.put(ConsumerConfig.GROUP_ID_CONFIG, groupName);
57 map.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,servers); 66 map.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,servers);
  67 + map.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG,maxPollRecords);
58 log.info("----2.消费者组为:{}----",groupName); 68 log.info("----2.消费者组为:{}----",groupName);
59 69
60 //针对三个partition创建三个消费者,并缓存 70 //针对三个partition创建三个消费者,并缓存
61 for (int i = 1; i <=3 ; i++) { 71 for (int i = 1; i <=3 ; i++) {
62 KafkaConsumer<String, String> consumer; 72 KafkaConsumer<String, String> consumer;
63 String consumerName = userName+"-"+i; 73 String consumerName = userName+"-"+i;
  74 +
  75 + //消费锁判定
  76 + Map map_lock = ConsumersCache.getConsumerLock();
  77 + if (map_lock.containsKey(consumerName) && ConsumersCache.getLockState(consumerName)){
  78 + log.info("[CONSUMER-LOCK-{}] 消费状态为锁定,正在消费",consumerName);
  79 + return;
  80 + }else {
  81 + log.info("[CONSUMER-LOCK-{}] 消费状态为正常,可以消费",consumerName);
  82 + ConsumersCache.lock(consumerName);
  83 + }
  84 +
  85 +
64 if (ConsumersCache.getConsumerMap().containsKey(consumerName)){ 86 if (ConsumersCache.getConsumerMap().containsKey(consumerName)){
65 consumer = ConsumersCache.consumerMap.get(consumerName); 87 consumer = ConsumersCache.consumerMap.get(consumerName);
66 log.info("[loop-start]3.从缓存中获取到消费者:{}的消费者信息[{}]。",consumerName,consumer); 88 log.info("[loop-start]3.从缓存中获取到消费者:{}的消费者信息[{}]。",consumerName,consumer);
@@ -68,12 +90,12 @@ public class KafkaReadProcessor { @@ -68,12 +90,12 @@ public class KafkaReadProcessor {
68 map.put(ConsumerConfig.CLIENT_ID_CONFIG,consumerName); 90 map.put(ConsumerConfig.CLIENT_ID_CONFIG,consumerName);
69 consumer =new KafkaConsumer<String, String>(map); 91 consumer =new KafkaConsumer<String, String>(map);
70 ConsumersCache.consumerMap.put(consumerName,consumer); 92 ConsumersCache.consumerMap.put(consumerName,consumer);
71 - log.info("3.缓存中没有消费者{}的信息,创建新的消费者信息",consumerName); 93 + log.info("[CONSUMER] 3.缓存中没有消费者{}的信息,创建新的消费者信息",consumerName);
72 } 94 }
73 95
74 consumer.subscribe(Arrays.asList(userName)); 96 consumer.subscribe(Arrays.asList(userName));
75 ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(3)); 97 ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(3));
76 - log.info("----4.消费者:{}此次成功消费数据{}条----",consumerName,records.count()); 98 + log.info("----[CONSUMER] 4.消费者:{}此次成功消费数据{}条----",consumerName,records.count());
77 99
78 if(!records.isEmpty()){ 100 if(!records.isEmpty()){
79 for (ConsumerRecord<String, String> record : records) { 101 for (ConsumerRecord<String, String> record : records) {
@@ -87,65 +109,80 @@ public class KafkaReadProcessor { @@ -87,65 +109,80 @@ public class KafkaReadProcessor {
87 //todo:消息备份或者重发? 109 //todo:消息备份或者重发?
88 reTrySend(msgs); 110 reTrySend(msgs);
89 } 111 }
90 -  
91 } 112 }
92 consumer.commitSync(); 113 consumer.commitSync();
93 - log.info("5.消费者{}-{}消费提交成功",consumerName,groupName); 114 + log.info("[CONSUMER] 消费者{}-{}消费提交成功",consumerName,groupName);
94 115
95 }else { 116 }else {
96 log.info("----[END]5.消费者的TOPIC没有新的消费数据即将返回----"); 117 log.info("----[END]5.消费者的TOPIC没有新的消费数据即将返回----");
97 } 118 }
  119 + ConsumersCache.unlock(consumerName);
98 } 120 }
99 121
100 } 122 }
101 123
102 }catch (Exception e){ 124 }catch (Exception e){
103 e.printStackTrace(); 125 e.printStackTrace();
  126 + log.info("转发出错,{}",e.toString());
  127 + log.error("转发出错,{}",e.toString());
  128 + ConsumersCache.unlock(userName+"-"+1);
  129 + ConsumersCache.unlock(userName+"-"+2);
  130 + ConsumersCache.unlock(userName+"-"+3);
104 } 131 }
105 132
106 } 133 }
107 134
108 public MSGS transMsg(String msg){ 135 public MSGS transMsg(String msg){
109 - JSONObject rootJson = JSON.parseObject(msg);  
110 - JSONObject msgJson = rootJson.getJSONObject("MSG");  
111 - JSONObject body = msgJson.getJSONObject("BODY");  
112 -  
113 - HEADER msgHeader = msgJson.getObject("HEADER",HEADER.class);  
114 - msgHeader.setSNDR(userName);  
115 -  
116 - MSG transMsg= new MSG();  
117 - String transBody = body.toJSONString();  
118 - transMsg.setHEADER(msgHeader);  
119 - transMsg.setBODY(transBody);  
120 -  
121 - MSGS msgs = new MSGS();  
122 - msgs.setMSG(transMsg);  
123 - return msgs; 136 + MSGS rootJson = JSON.parseObject(msg,MSGS.class);
  137 + rootJson.getMSG().getHEADER().setSNDR(userName);
  138 +// JSONObject msgJson = rootJson.getJSONObject("MSG");
  139 +// JSONObject body = msgJson.getJSONObject("BODY");
  140 +//
  141 +// HEADER msgHeader = msgJson.getObject("HEADER",HEADER.class);
  142 +// msgHeader.setSNDR(userName);
  143 +//
  144 +// MSG transMsg= new MSG();
  145 +// transMsg.setHEADER(msgHeader);
  146 +// transMsg.setBODY(body);
  147 +//
  148 +// MSGS msgs = new MSGS();
  149 +// msgs.setMSG(transMsg);
  150 + return rootJson;
124 } 151 }
125 152
126 public boolean sendmsg(MSGS msgs){ 153 public boolean sendmsg(MSGS msgs){
127 - ResultJson response = kafkaSendApi.send(msgs); 154 + try {
  155 + ResultJson response = kafkaSendApi.send(msgs);
128 156
129 - if ("200".equals(response.getCode())){  
130 - log.info("………………6-消息发送成功{}………………",response.toString());  
131 - return true; 157 + if ("200".equals(response.getCode())){
  158 + log.info("[SEND-PRODUCT]………………消息发送成功{}………………",response.toString());
  159 + return true;
  160 + }
  161 + log.info("[SEND-PRODUCT]400-消息发送失败->{}",response.toString());
  162 + }catch (FeignException ex){
  163 + log.error("[SEND-PRODUCT] 发送服务调用失败-->>{}",ex.toString());
132 } 164 }
133 - log.info("400-消息发送失败->{}",response.toString());  
134 return false; 165 return false;
135 } 166 }
136 167
137 /** 168 /**
138 * feign重发消息 169 * feign重发消息
139 */ 170 */
140 - public void reTrySend(MSGS msgs){ 171 + public boolean reTrySend(MSGS msgs) throws InterruptedException {
141 log.error("***进入重发***"); 172 log.error("***进入重发***");
142 - for (int i = 0; i < RETRY_TIMES; i++) { 173 + boolean flag = false;
  174 + int i = 0;
  175 + while (true){
  176 + Thread.sleep(1000);
  177 + i++;
143 boolean sendResult = sendmsg(msgs); 178 boolean sendResult = sendmsg(msgs);
144 if (sendResult){ 179 if (sendResult){
145 - log.error("***重发成功***"); 180 + log.error("[RESEND-PRODUCT]***重发成功,重发次数({})***",i);
  181 + log.info("[RESEND-PRODUCT]***重发成功,重发次数({})***",i);
  182 + flag = true;
146 break; 183 break;
147 } 184 }
148 } 185 }
149 - log.error("***已尝试重发>>>{}<<<次,重发失败***",RETRY_TIMES); 186 + return flag;
150 } 187 }
151 } 188 }
  1 +package com.tianbo.messagebus.service;
  2 +
  3 +
  4 +import com.tianbo.messagebus.controller.response.ResultJson;
  5 +import com.tianbo.messagebus.model.Cache;
  6 +import com.tianbo.messagebus.model.MSGS;
  7 +import com.tianbo.messagebus.myinterface.KafkaSendApi;
  8 +import lombok.extern.slf4j.Slf4j;
  9 +import org.springframework.beans.factory.annotation.Autowired;
  10 +import org.springframework.scheduling.annotation.Scheduled;
  11 +import org.springframework.stereotype.Service;
  12 +
  13 +/**
  14 + * 重发服务
  15 + * 从内存数组队列读取报文,并发送直到发送成功为止,
  16 + * 代表这个程序不能随便重启了,重启会丢失需要重复发的数据
  17 + */
  18 +
  19 +@Service
  20 +@Slf4j
  21 +public class ResendProcessor {
  22 +
  23 + private static int TIMES = 20;
  24 +
  25 + @Autowired
  26 + KafkaSendApi kafkaSendApi;
  27 +
  28 + public boolean sendmsg(MSGS msgs){
  29 + ResultJson response = kafkaSendApi.send(msgs);
  30 +
  31 + if ("200".equals(response.getCode())){
  32 + log.info("………………重发消息发送成功{}………………",response.toString());
  33 + return true;
  34 + }
  35 + log.info("400-重发消息发送失败->{}",response.toString());
  36 + return false;
  37 + }
  38 +
  39 + public void resend(){
  40 + if (!Cache.SEND_CACHE.isEmpty()){
  41 + log.info("开始从内存读取报文,发送失败的报文,待重发数量->{}",Cache.SEND_CACHE.size());
  42 + Cache.SEND_CACHE.removeIf(this::send);
  43 + }
  44 + }
  45 +
  46 + private boolean send(MSGS msgs){
  47 + for (int j = 0; j < TIMES; j++) {
  48 + if(sendmsg(msgs)) {
  49 + return true;
  50 + }
  51 + }
  52 + return false;
  53 + }
  54 +}