初始化

master
DongZeLiang 2024-04-18 10:58:11 +08:00
commit de22a25f44
7 changed files with 609 additions and 0 deletions

35
.gitignore vendored 100644
View File

@ -0,0 +1,35 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

60
pom.xml 100644
View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.muyu</groupId>
<artifactId>load-center</artifactId>
<version>1.0.0</version>
<description>
车辆网关负载中心
</description>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring-boot.version>2.7.13</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringBoot 依赖配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.47</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,50 @@
package com.muyu.common.redis.configure;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* Redis使FastJson
*
* @author muyu
*/
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> {
public static final String[] JSON_WHITELIST_STR = {"org.springframework", "com.muyu"};
public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;
static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(JSON_WHITELIST_STR);
private Class<T> clazz;
public FastJson2JsonRedisSerializer (Class<T> clazz) {
super();
this.clazz = clazz;
}
@Override
public byte[] serialize (T t) throws SerializationException {
if (t == null) {
return new byte[0];
}
return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
}
@Override
public T deserialize (byte[] bytes) throws SerializationException {
if (bytes == null || bytes.length <= 0) {
return null;
}
String str = new String(bytes, DEFAULT_CHARSET);
return JSON.parseObject(str, clazz, AUTO_TYPE_FILTER);
}
}

View File

@ -0,0 +1,41 @@
package com.muyu.common.redis.configure;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis
*
* @author muyu
*/
@Configuration
@EnableCaching
@AutoConfigureBefore(RedisAutoConfiguration.class)
public class RedisConfig extends CachingConfigurerSupport {
@Bean
@SuppressWarnings(value = {"unchecked", "rawtypes"})
public RedisTemplate<Object, Object> redisTemplate (RedisConnectionFactory connectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}

View File

@ -0,0 +1,289 @@
package com.muyu.common.redis.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
/**
* spring redis
*
* @author muyu
**/
@SuppressWarnings(value = {"unchecked", "rawtypes"})
@Component
public class RedisService {
@Autowired
public RedisTemplate redisTemplate;
/**
* IntegerString
*
* @param key
* @param value
*/
public <T> void setCacheObject (final String key, final T value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* IntegerString
*
* @param key
* @param value
* @param timeout
* @param timeUnit
*/
public <T> void setCacheObject (final String key, final T value, final Long timeout, final TimeUnit timeUnit) {
redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
}
/**
*
*
* @param key Redis
* @param timeout
*
* @return true=false=
*/
public boolean expire (final String key, final long timeout) {
return expire(key, timeout, TimeUnit.SECONDS);
}
/**
*
*
* @param key Redis
* @param timeout
* @param unit
*
* @return true=false=
*/
public boolean expire (final String key, final long timeout, final TimeUnit unit) {
return redisTemplate.expire(key, timeout, unit);
}
/**
*
*
* @param key Redis
*
* @return
*/
public long getExpire (final String key) {
return redisTemplate.getExpire(key);
}
/**
* key
*
* @param key
*
* @return true false
*/
public Boolean hasKey (String key) {
return redisTemplate.hasKey(key);
}
/**
*
*
* @param key
*
* @return
*/
public <T> T getCacheObject (final String key) {
ValueOperations<String, T> operation = redisTemplate.opsForValue();
return operation.get(key);
}
/**
*
*
* @param key
*/
public boolean deleteObject (final String key) {
return redisTemplate.delete(key);
}
/**
*
*
* @param collection
*
* @return
*/
public boolean deleteObject (final Collection collection) {
return redisTemplate.delete(collection) > 0;
}
/**
* List
*
* @param key
* @param dataList List
*
* @return
*/
public <T> long setCacheList (final String key, final List<T> dataList) {
Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
return count == null ? 0 : count;
}
/**
* list
*
* @param key
*
* @return
*/
public <T> List<T> getCacheList (final String key) {
return redisTemplate.opsForList().range(key, 0, -1);
}
public <T> T getCacheListValue (final String key, long index) {
return (T) redisTemplate.opsForList().index(key, index);
}
/**
* Set
*
* @param key
* @param dataSet
*
* @return
*/
public <T> BoundSetOperations<String, T> setCacheSet (final String key, final Set<T> dataSet) {
BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
Iterator<T> it = dataSet.iterator();
while (it.hasNext()) {
setOperation.add(it.next());
}
return setOperation;
}
/**
* set
*
* @param key
*
* @return
*/
public <T> Set<T> getCacheSet (final String key) {
return redisTemplate.opsForSet().members(key);
}
/**
* Map
*
* @param key
* @param dataMap
*/
public <T> void setCacheMap (final String key, final Map<String, T> dataMap) {
if (dataMap != null) {
redisTemplate.opsForHash().putAll(key, dataMap);
}
}
/**
* Map
*
* @param key
*
* @return
*/
public <T> Map<String, T> getCacheMap (final String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
* @param value
*/
public <T> void setCacheMapValue (final String key, final String hKey, final T value) {
redisTemplate.opsForHash().put(key, hKey, value);
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
*
* @return Hash
*/
public <T> T getCacheMapValue (final String key, final String hKey) {
HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
return opsForHash.get(key, hKey);
}
/**
* Hash
*
* @param key Redis
* @param hKeys Hash
*
* @return Hash
*/
public <T> List<T> getMultiCacheMapValue (final String key, final Collection<?> hKeys) {
return redisTemplate.opsForHash().multiGet(key, hKeys);
}
/**
* redishashKey
* @param key redis
* @param hashKey hash
*/
public boolean hashKey(final String key, final String hashKey){
return this.redisTemplate.opsForHash().hasKey(key, hashKey);
}
/**
* Hash
*
* @param key Redis
* @param hKey Hash
*
* @return
*/
public boolean deleteCacheMapValue (final String key, final String hKey) {
return redisTemplate.opsForHash().delete(key, hKey) > 0;
}
/**
*
*
* @param pattern
*
* @return
*/
public Collection<String> keys (final String pattern) {
return redisTemplate.keys(pattern);
}
/**
*
* @param key key
* @param number
* @return
*/
public Long decrement (final String key, Long number) {
return redisTemplate.opsForValue().decrement(key,number);
}
/**
*
* @param key key
* @param number
* @return
*/
public Long increment (final String key, Long number) {
return redisTemplate.opsForValue().increment(key,number);
}
}

View File

@ -0,0 +1,3 @@
spring:
redis:
host: 127.0.0.1

View File

@ -0,0 +1,131 @@
package com.muyu;
import com.muyu.common.redis.service.RedisService;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
/**
* @author DongZl
* @description:
* @Date 2024/4/12 5:13
*/
@SpringBootTest(classes = LoadCenterApplication.class)
public class LoadTest {
@Autowired
private RedisService redisService;
@Test
public void load(){
// 初始化序列
redisService.setCacheObject("cursor", 0);
List<WorkGatewayNode> nodeIdList = new ArrayList<>(){{
add(new WorkGatewayNode("work-gateway-node-A", 8));
add(new WorkGatewayNode("work-gateway-node-B", 12));
add(new WorkGatewayNode("work-gateway-node-C", 2));
add(new WorkGatewayNode("work-gateway-node-D", 39));
add(new WorkGatewayNode("work-gateway-node-E", 39));
}};
// 100
List<String> loadNodeList = new ArrayList<>();
long count = nodeIdList.stream().mapToInt(WorkGatewayNode::getWeight).sum();
if (count < 100){
List<WorkGatewayNode> list = nodeIdList.stream()
.sorted((o1, o2) -> o2.getWeight() - o1.getWeight())
.toList();
int countWeight = 0;
for (long i = count ; i < 100 ; i++) {
WorkGatewayNode workGatewayNode = list.get(countWeight++ % list.size());
workGatewayNode.setWeight(workGatewayNode.getWeight() + 1);
}
}
whFor:while (true){
for (WorkGatewayNode workGatewayNode : nodeIdList) {
int weight = workGatewayNode.getWeight();
if (weight > 0){
loadNodeList.add(
workGatewayNode.getNodeId()
);
workGatewayNode.setWeight(weight - 1);
}
}
int sum = nodeIdList.stream()
.mapToInt(WorkGatewayNode::getWeight)
.sum();
if (sum <= 0){
break whFor;
}
}
redisService.deleteObject("work:node:gateway");
redisService.setCacheList("work:node:gateway", loadNodeList);
CountDownLatch countDownLatch = new CountDownLatch(3000);
new Thread(() -> {
for (int i = 0 ; i < 1000 ; i++) {
Long cursor = redisService.increment("cursor", 1L);
String nodeId = redisService.getCacheListValue("work:node:gateway", cursor % 100);
System.out.println(Thread.currentThread().getName() + "" + cursor + "-----" + nodeId);
stiNode.sti(nodeId);
countDownLatch.countDown();
}
}).start();
new Thread(() -> {
for (int i = 0 ; i < 1000 ; i++) {
Long cursor = redisService.increment("cursor", 1L);
String nodeId = redisService.getCacheListValue("work:node:gateway", cursor % 100);
System.out.println(Thread.currentThread().getName() + "" + cursor + "-----" + nodeId);
stiNode.sti(nodeId);
countDownLatch.countDown();
}
}).start();
new Thread(() -> {
for (int i = 0 ; i < 1000 ; i++) {
Long cursor = redisService.increment("cursor", 1L);
String nodeId = redisService.getCacheListValue("work:node:gateway", cursor % 100);
System.out.println(Thread.currentThread().getName() + "" + cursor + "-----" + nodeId);
stiNode.sti(nodeId);
countDownLatch.countDown();
}
}).start();
try {
countDownLatch.await();
stiNode.show();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
class stiNode{
private static Map<String, Integer> stiNodeMap = new HashMap<>();
public synchronized static void sti(String nodeId){
Integer stiCount = stiNodeMap.getOrDefault(nodeId, 0);
stiNodeMap.put(nodeId, stiCount + 1);
}
public static void show(){
stiNodeMap.forEach((key,val) -> {
System.out.println(key + "-----" + val);
});
}
}
@Data
@AllArgsConstructor
class WorkGatewayNode {
private String nodeId;
private int weight;
}