redis被大量用在分佈式的環境中,天然而然分佈式環境下的鎖如何解決,立馬成爲一個問題。例如咱們當前的手遊項目,服務器端是按業務模塊劃分服務器的,有應用服,戰鬥服等,可是這兩個vm都有可能同時改變玩家的屬性,這若是在同一個vm下面,就很容易加鎖,但若是在分佈式環境下就沒那麼容易了,固然利用redis現有的功能也有解決辦法,好比redis的腳本。java
redis在2.6之後的版本中增長了Lua腳本的功能,能夠經過eval命令,直接在RedisServer環境中執行Lua腳本,而且能夠在Lua腳本中調用Redis命令。
使用腳本的好處:
1.減小網絡開銷:能夠把一些要批量處理的功能,發在一個腳本里面執行,減小客戶端和redis的交互次數
2.原子操做:這主要就是咱們在這邊主要利用的功能,在分佈式環境下保證數據的原子性。
3.複用:客戶端發送的腳本會永久的存儲在redis中,這就意味着其餘客戶端能夠複用這一腳本而不須要使用代碼完成一樣的邏輯。
下面先看一段lua腳本:redis
local food=redis.call('hget',KEYS[1],'food'); food=food+ARGV[1]; redis.call('hset',KEYS[1],'food',food); local diamond=redis.call('hget',KEYS[1],'diamond'); diamond=diamond+ARGV[2]; redis.call('hset',KEYS[1],'diamond',diamond);
注:redis.call是咱們在腳本中調用redis命令,KEYS和ARGV2個數組,分別是鍵和參數,下標都是從1開始的,不是0。
這段腳本的功能是取出 KEYS指定的玩家food(糧草)和diamond(玉石),而後就行修改,最後保存在redis中,腳本的執行,保證了整個操做的原子性。
下面咱們用java代碼來看看具體的實現過程 數組
Jedis jedis = new Jedis("192.168.128.128", 6379); // 1.初始玩家數據到redis中 GamePlayer player = new GamePlayer(); player.setId(1001); player.setName("ksfzhaohui"); player.setFood(100); player.setDiamond(100); Map<String, String> beanMap = BeanUtil.warp(player);// 將對象轉換成map String beanKey = getRedisBeanKey(player.getClass(), player.getId()); System.out.println("key:" + beanKey); jedis.hmset(beanKey, beanMap);// 將玩家數據保存到redis中
首先模擬了一個玩家將玩家信息保存在redis中,這邊的Id隨便寫了一個,正常的狀況下都是經過redis的命令incr生成一個id
結果: 服務器
String script = "local food=redis.call('hget',KEYS[1],'food');" + "food=food+ARGV[1];" + "redis.call('hset',KEYS[1],'food',food);" + "local diamond=redis.call('hget',KEYS[1],'diamond');" + "diamond=diamond+ARGV[2];" + "redis.call('hset',KEYS[1],'diamond',diamond);"; List<String> keys = new ArrayList<String>(); keys.add(beanKey); List<String> args = new ArrayList<String>(); args.add("100"); args.add("100"); // 3.執行腳本 jedis.eval(script, keys, args);
public class BeanUtil { private static Logger logger = Logger.getLogger(BeanUtil.class); private static final String CLASS = "class"; /** * 將指定的對象數據封裝成map * * @param bean * 對象數據 * @return */ @SuppressWarnings("all") public static Map<String, String> warp(Object bean) { Map<String, String> propertyMap = new HashMap<String, String>(); try { PropertyDescriptor[] ps = Introspector.getBeanInfo(bean.getClass()) .getPropertyDescriptors(); for (PropertyDescriptor propertyDescriptor : ps) { String propertyName = propertyDescriptor.getName(); if (propertyName != null && !propertyName.equals(CLASS)) { Method getter = propertyDescriptor.getReadMethod(); if (getter != null) { propertyMap.put(propertyName, String.valueOf(getter.invoke(bean, null))); } } } } catch (Exception e) { logger.error(e); } return propertyMap; } }
固然網上還有一些其餘的方法,如: 用SETNX實現分佈式鎖分佈式