package org.jeecg.common.util;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
/**
* redis锁
*
* @author zzy
*/
@Slf4j
@Component
public class RedisLockUtils {
@Resource
private RedisTemplate redisTemplate;
public static final String UNLOCK_LUA;
/**
* 释放锁脚本,原子操作
*/
static {
StringBuilder sb = new StringBuilder();
sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");
sb.append("then ");
sb.append(" return redis.call(\"del\",KEYS[1]) ");
sb.append("else ");
sb.append(" return 0 ");
sb.append("end ");
UNLOCK_LUA = sb.toString();
}
/**
* 获取分布式锁
*
* @param lockKey key
* @param requestId 唯一id
* @param expire 时间值
* @param timeUnit 时间单位
* @return true:加锁成功,false:已有人获取锁
*/
public boolean tryLock(String lockKey, String requestId, long expire, TimeUnit timeUnit) {
try {
RedisCallback<Boolean> callback = (connection) -> connection.set(lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")), Expiration.seconds(timeUnit.toSeconds(expire)), RedisStringCommands.SetOption.SET_IF_ABSENT);
return (Boolean) redisTemplate.execute(callback);
} catch (Exception e) {
log.error("redis lock error.", e);
}
return false;
}
/**
* 释放锁
*
* @param lockKey key
* @param requestId 唯一id
* @return true:释放成功
*/
public boolean releaseLock(String lockKey, String requestId) {
RedisCallback<Boolean> callback = (connection) -> connection.eval(UNLOCK_LUA.getBytes(), ReturnType.BOOLEAN, 1, lockKey.getBytes(Charset.forName("UTF-8")), requestId.getBytes(Charset.forName("UTF-8")));
return (Boolean) redisTemplate.execute(callback);
}
/**
* 获取Redis锁的value值
*
* @param lockKey key
* @return value
*/
public String get(String lockKey) {
try {
RedisCallback<String> callback = (connection) -> new String(connection.get(lockKey.getBytes()), Charset.forName("UTF-8"));
return (String) redisTemplate.execute(callback);
} catch (Exception e) {
log.error("获取redis锁发生异常", e);
}
return null;
}
}
业务:一个服务器上有2个相同的项目。会造成2个定时任务会在一个时间段同时获取数据, 造成数据交叉。为了避免此现象发生,采用redis分布式锁的形式,当一个项目的定时任务开始工作是,另一个项目的定时任务处于等待状态,直到前面的定时任务释放锁后。另一个项目在去执行定时任务。避免数据交叉。
锁对象是 IP地址+端口号.
@Scheduled(cron = "0 */3 * * * ?")
public void synchInoutRecord() throws IOException {
setCookie();
//要竞争的资源。锁ip+端口号 过期时间5分钟
StringBuilder sb = new StringBuilder();
String ipAddress = sb.append(IPUtils.getIpAddress()).append(":").append(port).toString();
log.info("===============获取到的IP地址:{}==================", ipAddress);
boolean lock = redisLockUtils.tryLock("dsrwIpPort", ipAddress, 60 * 5, TimeUnit.SECONDS);
log.info("============本次定时任务开始==============");
if (lock) {
log.info("============获得分布式锁成功==============");
int page = 1;
int pagesize = 300;
String start = "", end = sdf.format(new Date());
JmjkUnvInoutRecord record = this.GetLastRecord();
if (record != null) {
start = sdf.format(record.getPassTime());
}
ExecInoutRecordSync(page, pagesize, start, end);
String ipPort = redisLockUtils.get("dsrwIpPort");
log.info("============分布式锁:{}=======================",ipPort);
//释放锁
boolean dsrwIpPort = redisLockUtils.releaseLock("dsrwIpPort", ipAddress);
log.info("============释放分布式锁成功=======================");
} else {
log.info("============获得分布式锁失败=======================");
//获取key
String ipPort = redisLockUtils.get("dsrwIpPort");
log.info("============{}机器上占用分布式锁,聚类任务正在执行=======================", ipPort);
}
log.info("============本次定时任务结束==============");
}
package org.jeecg.common.util;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import javax.servlet.http.HttpServletRequest;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
/**
* IP地址
*
* @Author scott
* @email [email protected]
* @Date 2019年01月14日
*/
public class IPUtils {
private static Logger logger = LoggerFactory.getLogger(IPUtils.class);
/**
* 获取IP地址
*
* 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
* 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
*/
public static String getIpAddr(HttpServletRequest request) {
String ip = null;
try {
ip = request.getHeader("x-forwarded-for");
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("WL-Proxy-Client-IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_CLIENT_IP");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("HTTP_X_FORWARDED_FOR");
}
if (StringUtils.isEmpty(ip) || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
} catch (Exception e) {
logger.error("IPUtils ERROR ", e);
}
// //使用代理,则获取第一个IP地址
// if(StringUtils.isEmpty(ip) && ip.length() > 15) {
// if(ip.indexOf(",") > 0) {
// ip = ip.substring(0, ip.indexOf(","));
// }
// }
return ip;
}
/**
* 获取本项目所在服务器的IP地址和端口号
*
* @param request
* @return
*/
public static String ipAddress(HttpServletRequest request) {
String host = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
return host;
}
public static String getIpAddress(){
String localIP = "127.0.0.1";
try {
OK:
for (Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); interfaces.hasMoreElements(); ) {
NetworkInterface networkInterface = interfaces.nextElement();
if (networkInterface.isLoopback() || networkInterface.isVirtual() || !networkInterface.isUp()) {
continue;
}
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (address instanceof Inet4Address) {
localIP = address.getHostAddress();
break OK;
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return localIP;
}
public static void main(String[] args) {
String ipAddress = IPUtils.getIpAddress();
System.out.println(ipAddress);
}
}