ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 10, 100, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000)); threadPoolExecutor.execute(() -> System.out.println("print in thread"));
"Thread" -> "BlockingQueue": pool task "Thread" -> "BlockingQueue": get global ReentrantLock alt get global ReentrantLock success alt BlockingQueue size = 0 "Thread" -> "Condition": await keepAliveTime "BlockingQueue" -> "Thread": non task,execute processWorkerExit method else "BlockingQueue" -> "Thread": first task in queue "Thread" -> "Thread": keep execute task end else "Thread" -> "BlockingQueue": keep acquire ReentrantLock end @enduml
@startuml Database Database as DB entity Cache as Cache
query -> repository: select data
repository -> cache: get data repository -> DB: get data DB -> repository: return data repository -> cache: update data repository -> query: return data
@enduml
wirte though
1 2 3 4 5 6 7 8 9 10 11 12
@startuml Database Database as DB entity Cache as Cache
transcation -> repository: update data
repository -> cache: update data repository -> DB: update data DB -> repository: return result repository -> transcation: return result
@enduml
可能出现的数据不一致
程序没有优雅关闭,更新请求先更新了缓存,但还没更新数据库,数据丢失
更新缓存成功,更新数据库失败导致的数据不一致
适用场景
更新数据库极低概率失败
程序有优雅关闭功能
改进方式
暂无
Write Behind
1 2 3 4 5 6 7 8 9 10 11 12 13 14
@startuml Database Database as DB entity Cache as Cache
query -> repository: query data
repository -> cache: query data repository -> DB: query data DB -> repository: return data repository -> cache: update data
repository -> query: return data
@enduml
1 2 3 4 5 6 7 8 9 10 11
@startuml Database Database as DB entity Cache as Cache
@Override public T getData(K key){ //get data from cache TdataFromCache= cacheUpdate.getData(key); //if cache haven't, get from database and put to cache if(Objects.nonNull(dataFromCache)){ return dataFromCache; } TdataFromDatabase= databaseOperation.getData(key); cacheUpdate.addData(key, dataFromDatabase); return dataFromDatabase; }
@Override publicbooleanupdateData(K key, T data){ //update data to database booleanupdateToDatabaseRes= databaseOperation.updateData(key, data); if(updateToDatabaseRes){ //invalid cache data return cacheUpdate.removeData(key); } returnfalse; }
@Override publicbooleanaddData(K key, T data){ //add data to database return databaseOperation.addData(key, data); }
@Override publicbooleanremoveData(K key){ //remove from database booleanremoveFromDatabaseRes= databaseOperation.removeData(key); if(removeFromDatabaseRes){ //invalid cache data return cacheUpdate.removeData(key); } returnfalse; }
@Override public T getData(K key) { //if cache has data, return if(map.containsKey(key)){ return map.get(key); } //get data from database and write to cache Tdata= databaseOperation.getData(key); map.put(key, data); return data; }
publicConsistentHashingImpl(int virtualNodeNum, String... nodes) { //1. intercept virtual num smaller than 0 if(virtualNodeNum < 0){ log.error("virtual num is not allow smaller than 0"); thrownewIllegalArgumentException(); } //2. initialize loop member attributes this.virtualNodeNum = virtualNodeNum; realNodeToVirtualNode = newTreeMap<>(); hashToNodes = newTreeMap<>(); nodeToData = newHashMap<>(); for(String server : nodes){ hashToNodes.put(getHash(server), server); nodeToData.put(server, newLinkedList<>()); } //3. if virtual node number bigger than 0, add virtual node if(virtualNodeNum > 0){ for(String server : nodes){ addVirtualNode(server); } } }
@Override publicbooleanputData(List<String> data) { //1. circulate call put data method to add data to loop for(String incomingData : data){ if(!putData(incomingData)){ returnfalse; } } returntrue; }
@Override publicbooleanputData(String data) { if(hashToNodes.isEmpty()){ log.error("put data, usable server is empty"); returnfalse; } //1. calculate data's hash value intcurrentHash= getHash(data); //2. get usual node(node's hash value is bigger than data's hash value), if usual node list is empty, get first node in loop SortedMap<Integer, String> usableNodes = hashToNodes.tailMap(currentHash); Stringnode= usableNodes.isEmpty() ? hashToNodes.get(hashToNodes.firstKey()) : usableNodes.get(usableNodes.firstKey()); //3. add data to node List<String> dataList = nodeToData.get(node); dataList.add(data); log.info("put data, data {} is placed to server {}, hash: {}", data, node, currentHash); returntrue; }
@Override publicbooleanremoveNode(String node) { //1. calculate hash value of removing node intremoveServerHash= getHash(node); if(!hashToNodes.containsKey(removeServerHash)){ log.error("remove server, current server is not in server list, please check server ip"); returnfalse; } //2. get data from removing node List<String> removeServerData = nodeToData.get(node); //3. get removing node's virtual node data, remove all virtual node with removing node if(virtualNodeNum != 0){ for(String virtualNode : realNodeToVirtualNode.get(node)){ removeServerData.addAll(nodeToData.get(virtualNode)); hashToNodes.remove(getHash(virtualNode)); nodeToData.remove(virtualNode); } } //4. remove node from hash loop hashToNodes.remove(removeServerHash); nodeToData.remove(node); if(hashToNodes.size() == 0){ log.info("remove server, after remove, server list is empty"); returntrue; } //5. put data to loop by call put data method putData(removeServerData); log.info("remove server, remove server {} success", node); returntrue; }
@Override publicbooleanaddNode(String node) { //1, calculate adding node's hash value intaddServerHash= getHash(node); //2. add node and migrate data if(hashToNodes.isEmpty()){ //2.1 add node and its virtual node to loop directly when current loop is empty hashToNodes.put(addServerHash, node); nodeToData.put(node, newLinkedList<>()); if(virtualNodeNum > 0){ addVirtualNode(node); } } else{ //2.2.1 get data to be migrated from loop SortedMap<Integer, String> greatServers = hashToNodes.tailMap(addServerHash); StringgreatServer= greatServers.isEmpty() ? hashToNodes.get(hashToNodes.firstKey()) : greatServers.get(greatServers.firstKey()); List<String> firstGreatServerData = newLinkedList<>(nodeToData.get(greatServer)); //2.2.2 add node and its virtual node to loop hashToNodes.put(addServerHash, node); nodeToData.put(greatServer, newLinkedList<>()); nodeToData.put(node, newLinkedList<>()); if(virtualNodeNum != 0){ addVirtualNode(node); } //2.2.3 migrate 2.2.1 data to loop by call put data method putData(firstGreatServerData); } log.info("add server, server {} has been added", node); returntrue; }
[main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 000 is placed to server 000, hash: 144 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 111 is placed to server 111, hash: 147 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 222 is placed to server 222, hash: 150 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 333, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 555 is placed to server 555, hash: 159 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 555, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - remove server, remove server 333 success [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 555 is placed to server 555, hash: 159 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 444, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - add server, server 444 has been added [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 444 is placed to server 444, hash: 156 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 000 contains data [000] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 111 contains data [111] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 222 contains data [222] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 444 contains data [333, 444] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 555 contains data [555]
[main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 000 is placed to server 000, hash: 144 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 111 is placed to server 111, hash: 147 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 222 is placed to server 222, hash: 150 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 333, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 555 is placed to server 555, hash: 159 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 555, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - remove server, remove server 333 success [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 555 is placed to server 555, hash: 159 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 333 is placed to server 444, hash: 153 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - add server, server 444 has been added [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 444 is placed to server 444, hash: 156 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - put data, data 555&&0 is placed to server 555&&0, hash: 207 [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 000&&2 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 000&&1 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 000&&0 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 111&&1 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 111&&2 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 555&&1 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 555&&2 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 222&&2 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 444&&0 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 444&&1 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 444&&2 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 555&&0 contains data [555&&0] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 000 contains data [000] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 111 contains data [111] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 222 contains data [222] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 222&&0 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 444 contains data [333, 444] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 555 contains data [555] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 222&&1 contains data [] [main] INFO org.CommonAlgorithms.ConsistentHash.ConsistentHashingImpl - server 111&&0 contains data []
@DisplayName("multipart get config") @SpringBootTest public class MultipartGetConfigTest {
private static final Logger log = LoggerFactory.getLogger(MultipartGetConfigTest.class); @Autowired private ConfigByConfigurationProperties configByConfigurationProperties; @Autowired @Qualifier("configByValueAnnotationV2") private Properties properties; @Test public void getByConfigurationProperties(){ log.info("get by @ConfigurationProperties, value: {}", configByConfigurationProperties.getPort()); log.info("get by @ConfigurationProperties and manual create bean, value: {}", properties.getProperty("port")); }
}
测试结果
1 2
org.spring.demo.MultipartGetConfigTest : get by @ConfigurationProperties, value: 7100 org.spring.demo.MultipartGetConfigTest : get by @ConfigurationProperties and manual create bean, value: 7100