mirror of https://github.com/elunez/eladmin
Fix cache by future (#652)
* 利用future回调避免并发调用时有多个线程同时去数据库查询,导致消耗额外资源。经过测试在8个线程并发查询时性能提升 * 修复缓存测试时懒加载报错问题 * 调整格式pull/702/head
parent
7b91c4405c
commit
931ecb3ba7
|
@ -30,7 +30,8 @@ import org.springframework.stereotype.Service;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* @author Zheng Jie
|
||||
|
@ -53,21 +54,15 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
|||
*
|
||||
* @see {@link UserCacheClean}
|
||||
*/
|
||||
static Map<String, JwtUserDto> userDtoCache = new ConcurrentHashMap<>();
|
||||
|
||||
final static Map<String, Future<JwtUserDto>> userDtoCache = new ConcurrentHashMap<>();
|
||||
public static ExecutorService executor = newThreadPool();
|
||||
|
||||
@Override
|
||||
public JwtUserDto loadUserByUsername(String username) {
|
||||
boolean searchDb = true;
|
||||
JwtUserDto jwtUserDto = null;
|
||||
if (loginProperties.isCacheEnable() && userDtoCache.containsKey(username)) {
|
||||
jwtUserDto = userDtoCache.get(username);
|
||||
// 检查dataScope是否修改
|
||||
List<Long> dataScopes = jwtUserDto.getDataScopes();
|
||||
dataScopes.clear();
|
||||
dataScopes.addAll(dataService.getDeptIds(jwtUserDto.getUser()));
|
||||
searchDb = false;
|
||||
}
|
||||
if (searchDb) {
|
||||
Future<JwtUserDto> future = userDtoCache.get(username);
|
||||
if (!loginProperties.isCacheEnable()) {
|
||||
UserDto user;
|
||||
try {
|
||||
user = userService.findByName(username);
|
||||
|
@ -86,9 +81,86 @@ public class UserDetailsServiceImpl implements UserDetailsService {
|
|||
dataService.getDeptIds(user),
|
||||
roleService.mapToGrantedAuthorities(user)
|
||||
);
|
||||
userDtoCache.put(username, jwtUserDto);
|
||||
}
|
||||
return jwtUserDto;
|
||||
}
|
||||
|
||||
if (future==null) {
|
||||
Callable<JwtUserDto> call=()->getJwtBySearchDB(username);
|
||||
FutureTask<JwtUserDto> ft=new FutureTask<>(call);
|
||||
future=userDtoCache.putIfAbsent(username,ft);
|
||||
if(future==null){
|
||||
future=ft;
|
||||
executor.submit(ft);
|
||||
}
|
||||
try{
|
||||
return future.get();
|
||||
}catch(CancellationException e){
|
||||
userDtoCache.remove(username);
|
||||
}catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
}else{
|
||||
try {
|
||||
jwtUserDto=future.get();
|
||||
}catch (InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e.getMessage());
|
||||
}
|
||||
// 检查dataScope是否修改
|
||||
List<Long> dataScopes = jwtUserDto.getDataScopes();
|
||||
dataScopes.clear();
|
||||
dataScopes.addAll(dataService.getDeptIds(jwtUserDto.getUser()));
|
||||
|
||||
}
|
||||
return jwtUserDto;
|
||||
|
||||
}
|
||||
|
||||
|
||||
private JwtUserDto getJwtBySearchDB(String username) {
|
||||
UserDto user;
|
||||
try {
|
||||
user = userService.findByName(username);
|
||||
} catch (EntityNotFoundException e) {
|
||||
// SpringSecurity会自动转换UsernameNotFoundException为BadCredentialsException
|
||||
throw new UsernameNotFoundException("", e);
|
||||
}
|
||||
if (user == null) {
|
||||
throw new UsernameNotFoundException("");
|
||||
} else {
|
||||
if (!user.getEnabled()) {
|
||||
throw new BadRequestException("账号未激活!");
|
||||
}
|
||||
JwtUserDto jwtUserDto = new JwtUserDto(
|
||||
user,
|
||||
dataService.getDeptIds(user),
|
||||
roleService.mapToGrantedAuthorities(user)
|
||||
);
|
||||
return jwtUserDto;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static ExecutorService newThreadPool() {
|
||||
ThreadFactory namedThreadFactory = new ThreadFactory() {
|
||||
final AtomicInteger sequence = new AtomicInteger(1);
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread thread = new Thread(r);
|
||||
int seq = this.sequence.getAndIncrement();
|
||||
thread.setName("future-task-thread" + (seq > 1 ? "-" + seq : ""));
|
||||
if (!thread.isDaemon()) {
|
||||
thread.setDaemon(true);
|
||||
}
|
||||
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
return new ThreadPoolExecutor(10, 200,
|
||||
0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(1024),
|
||||
namedThreadFactory,
|
||||
new ThreadPoolExecutor.AbortPolicy());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,14 +45,14 @@ public class User extends BaseEntity implements Serializable {
|
|||
@ApiModelProperty(value = "ID", hidden = true)
|
||||
private Long id;
|
||||
|
||||
@ManyToMany
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@ApiModelProperty(value = "用户角色")
|
||||
@JoinTable(name = "sys_users_roles",
|
||||
joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")},
|
||||
inverseJoinColumns = {@JoinColumn(name = "role_id",referencedColumnName = "role_id")})
|
||||
private Set<Role> roles;
|
||||
|
||||
@ManyToMany
|
||||
@ManyToMany(fetch = FetchType.EAGER)
|
||||
@ApiModelProperty(value = "用户岗位")
|
||||
@JoinTable(name = "sys_users_jobs",
|
||||
joinColumns = {@JoinColumn(name = "user_id",referencedColumnName = "user_id")},
|
||||
|
|
|
@ -5,7 +5,11 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
|
@ -13,14 +17,19 @@ public class LoginCacheTest {
|
|||
|
||||
@Resource(name = "userDetailsService")
|
||||
private UserDetailsServiceImpl userDetailsService;
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
|
||||
@Test
|
||||
public void testCache() {
|
||||
public void testCache() throws InterruptedException {
|
||||
long start1 = System.currentTimeMillis();
|
||||
int size = 10000;
|
||||
int size = 1000;
|
||||
CountDownLatch latch = new CountDownLatch(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
userDetailsService.loadUserByUsername("admin");
|
||||
executor.submit(() -> userDetailsService.loadUserByUsername("admin"));
|
||||
latch.countDown();
|
||||
}
|
||||
latch.await();
|
||||
|
||||
long end1 = System.currentTimeMillis();
|
||||
//关闭缓存
|
||||
userDetailsService.setEnableCache(false);
|
||||
|
@ -31,4 +40,5 @@ public class LoginCacheTest {
|
|||
long end2 = System.currentTimeMillis();
|
||||
System.out.print("使用缓存:" + (end1 - start1) + "毫秒\n 不使用缓存:" + (end2 - start2) + "毫秒");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue