hi,你好!欢迎访问本站!登录
本站由网站地图腾讯云宝塔系统阿里云强势驱动
当前位置:首页 - 教程 - 杂谈 - 正文 君子好学,自强不息!

从零开始完成安排游戏(十二)——完成战役挂机(3)数据字典和缓存革新

2019-11-18杂谈搜奇网28°c
A+ A-

  上一章,我们增加了游戏的主界面和注册登录功用。由于距离上上篇距离较长,能够有些内容想些的后来就忘了。同时,逻辑也不庞杂,所以形貌比较大略。

  如今跟着模块的增添,悉数架构也暴露出一些问题。本章我们将对悉数体系举行大规模重构。

  比方,之前为了疾速开辟,rms模块,我们采纳了直接接见数据库的体式格局,关于rms模块自身来讲,没有什么问题。

  然则,在game模块中,关于频仍接见的、不常常转变的数据或接口,愿望采纳缓存的体式格局,将数据缓存起来,削减后端压力,同时加速响应速度,从而提拔体验。

  之前rms模块中尝试运用了EhCache,作为内存缓存。但如今增添了game模块,内存缓存没法在两个历程中同享。因而,我们引入redis,把缓存数据一致存到redis中。这里我们先运用spring-data-redis来举行缓存。经由过程在Service的要领上标记注解,来将要领返回效果举行缓存。如许一个粗粒度的缓存,现在能满足大部分需求。背面有须要时,我们再手动操纵redis,举行细粒度的缓存。

  除了缓存革新,发明一些罗列值,比方:种族、职业、阵营等,现在以静态类、罗列类的情势,在各个模块定义,如许每当我修正时,须要同时修正几个处所。因而,我增加了数据字典表,将这类数据一致设置到数据库中,同时由于不常修正,各个模块能够直接将其读到缓存中。数据字典的UML类图以下。

  如许,我只须要一个静态类,罗列出父级设置即可,今后只会增添,平常情况下都不会修正。代码以下:

package com.idlewow.datadict.model;

import java.io.Serializable;

public enum DataType implements Serializable {
    Occupy("10100", "疆域归属"),
    Faction("10110", "阵营"),
    Race("10200", "种族"),
    Job("10250", "职业"),
    MobType("10300", "怪物范例"),
    MobClass("10310", "怪物品种");

    private String code;
    private String value;

    DataType(String code, String value) {
        this.code = code;
        this.value = value;
    }

    public String getCode() {
        return code;
    }

    public String getValue() {
        return value;
    }
}
DataType.java

附一、spring缓存

  spring-context包下,有关于缓存的注解类,能够直接运用。在须要缓存的要领上标记注解即可。主要有@Cacheable、@CacheEvict、@CachePut。

例一:下面的注解,代表此要领实行胜利后,将返回效果缓存到redis中, key为 mapMob:#{id},当效果为NULL时,不缓存。

@Cacheable(value = "mapMob", key = "#id", unless = "#result == null")
public CommonResult find(String id) {
    return super.find(id);
}    

例二:下面的注解,代表此要领实行胜利后,将缓存 dataDict: 中的键悉数消灭

@CacheEvict(value = "dataDict", allEntries = true)
public CommonResult update(DataDict dataDict) {
    return super.update(dataDict);
}

例三:下面的注解,代表要领实行胜利后,将key为 levelExp:#{id} 的缓存更新为要领返回的效果

@CachePut(value = "levelExp", key = "#levelExp.getId()")
public CommonResult update(LevelExp levelExp) {
    return super.update(levelExp);
}

一、缓存革新

  由于是在hessian的要领上举行缓存,这里我们在hessian模块的pom.xml中增加依靠以下:

        <!-- 缓存相干 -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.2.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>3.1.0</version>
        </dependency>
pom.xml

  这里,我们须要设置一个叫 cacheManager 的 bean,经由过程援用spring-data-redis的包,举行设置,来把加了注解的缓存存进redis。

  之前我们一向运用xml对各组件举行设置,此 cacheManager 也能够运用xml举行设置。但在现实运用中,我想将redis的key一致设置成 idlewow:xxx:...,研讨了半天未找到xml情势的设置要领,因而这里运用Java代码举行设置。

  在hessian模块增加包 com.idlewow,然后新建  CacheConfig 类,以下:

package com.idlewow.config;

import org.springframework.cache.CacheManager;
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.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import redis.clients.jedis.JedisPoolConfig;

import java.time.Duration;

@EnableCaching
@Configuration
public class CacheConfig extends CachingConfigurerSupport {
    @Bean
    public JedisPoolConfig jedisPoolConfig() {
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxTotal(200);
        jedisPoolConfig.setMaxIdle(50);
        jedisPoolConfig.setMinIdle(20);
        jedisPoolConfig.setMaxWaitMillis(5000);
        jedisPoolConfig.setTestOnBorrow(true);
        jedisPoolConfig.setTestOnReturn(false);
        return jedisPoolConfig;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(jedisPoolConfig());
        return jedisConnectionFactory;
    }

    @Bean
    public CacheManager cacheManager() {
        RedisCacheConfiguration configuration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .disableCachingNullValues()
                .computePrefixWith(cacheName -> "idlewow:" + cacheName + ":");

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(jedisConnectionFactory())
                .cacheDefaults(configuration)
                .build();

        return redisCacheManager;
    }
}
CacheConfig

    这里我只简朴的设置了下,缓存的有效期为1小时,当效果为NULL时不缓存,key前缀为 idlewow:。 有兴致的话能够研讨下究竟可否用xml设置key前缀,注重这里用的是spring-data-redis 2.x版本,和 1.x 版本设置区分较大。

    增加好依靠后,我们须要在效劳的要领上打上标记即可。效劳的完成类,在core模块下。

  比方,我们这里以 MapMobServiceImpl 为例,此效劳的要领update、delete、find实行胜利后,我们均须要更新缓存。由于我们不缓存NULL值,因而add实行后,无需更新缓存。这里的要领已在BaseServiceImpl里完成过来,但须要打注解,不能直接在父类里标记,因而各个子类重写一下要领署名,内容直接 super.find(id),即可,也比较轻易。代码以下:

package com.idlewow.mob.service.impl;

import com.idlewow.common.BaseServiceImpl;
import com.idlewow.common.model.CommonResult;
import com.idlewow.mob.manager.MapMobManager;
import com.idlewow.mob.model.MapMob;
import com.idlewow.mob.service.MapMobService;
import com.idlewow.query.model.MapMobQueryParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("mapMobService")
public class MapMobServiceImpl extends BaseServiceImpl<MapMob, MapMobQueryParam> implements MapMobService {
    @Autowired
    MapMobManager mapMobManager;

    /**
     * 更新数据
     *
     * @param mapMob 数据对象
     * @return
     */
    @Override
    @CachePut(value = "mapMob", key = "#mapMob.getId()")
    public CommonResult update(MapMob mapMob) {
        return super.update(mapMob);
    }

    /**
     * 删除数据
     *
     * @param id 主键id
     * @return
     */
    @Override
    @CacheEvict(value = "mapMob", key = "#id")
    public CommonResult delete(String id) {
        return super.delete(id);
    }

    /**
     * 依据ID查询
     *
     * @param id 主键id
     * @return
     */
    @Override
    @Cacheable(value = "mapMob", key = "#id")
    public CommonResult find(String id) {
        return super.find(id);
    }

    /**
     * 依据舆图ID查询列表
     *
     * @param mapId 舆图ID
     * @return
     */
    @Override
    @Cacheable(value = "mapMobList", key = "#mapId", unless = "#reuslt==null")
    public List<MapMob> listByMapId(String mapId) {
        try {
            return mapMobManager.listByMapId(mapId);
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            return null;
        }
    }
}
MapMobServiceImpl

  OK, hessian模块的缓存已革新终了。能够尝试挪用一下,redis里应当已能够写入数据。

  别的:这里我还增加了一个listByMapId要领,背面game模块会挪用。这里没有再一致返回CommonResult范例。由于我在现实写代码过程当中,发明每次调接口都去做推断实在太烦琐了,对内挪用平常无需这么贫苦。平常在跨部门、公司之间的接口对接,或许对容错要求比较高时,能够将非常悉数捕捉处置惩罚。因而,背面对内的即接口都直接返回须要的数据范例。

二、RMS体系对应革新

  hessian既然已改成了redis缓存,假如rms模块体系逻辑稳定,修正了数据,却没有更新redis缓存,那game模块在挪用hessian时,假如读取了缓存,就会形成数据的不一致。

  因而,我们将rms模块革新为经由过程接见hessian效劳来读写数据,如许挪用hessian要领时就可以触发缓存,不再直接接见数据库。

  这里把EhCache、数据库相干的代码、设置、依靠都删掉。并在pom中增加对hessian的援用,并像game模块一样,设置hessian-client.xml并在applicationContext.xml中引入。

  在代码中,我们将CrudController中的BaseManager替换成BaseService,并将其他处所做对应修正。以下图:

package com.idlewow.rms.controller;

import com.idlewow.common.model.BaseModel;
import com.idlewow.common.model.CommonResult;
import com.idlewow.common.model.PageList;
import com.idlewow.common.model.QueryParam;
import com.idlewow.common.service.BaseService;
import com.idlewow.util.validation.ValidateGroup;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;

public abstract class CrudController<T extends BaseModel, Q extends QueryParam> extends BaseController {
    private final String path = this.getClass().getAnnotation(RequestMapping.class).value()[0];

    @Autowired
    BaseService<T, Q> baseService;
    @Autowired
    HttpServletRequest request;

    @RequestMapping("/list")
    public Object list() {
        return this.path + "/list";
    }

    @ResponseBody
    @RequestMapping(value = "/list", method = RequestMethod.POST)
    public Object list(@RequestParam(value = "page", defaultValue = "1") int pageIndex, @RequestParam(value = "limit", defaultValue = "10") int pageSize, Q q) {
        try {
            q.setPage(pageIndex, pageSize);
            CommonResult commonResult = baseService.list(q);
            if (commonResult.isSuccess()) {
                PageList<T> pageList = (PageList<T>) commonResult.getData();
                return this.parseTable(pageList);
            } else {
                request.setAttribute("errorMessage", commonResult.getMessage());
                return "/error";
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            request.setAttribute("errorMessage", ex.getMessage());
            return "/error";
        }
    }

    @RequestMapping("/add")
    public Object add() {
        return this.path + "/add";
    }

    @ResponseBody
    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public Object add(@RequestBody T t) {
        try {
            CommonResult commonResult = this.validate(t, ValidateGroup.Create.class);
            if (!commonResult.isSuccess())
                return commonResult;

            t.setCreateUser(this.currentUserName());
            commonResult = baseService.insert(t);
            return commonResult;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            return CommonResult.fail(ex.getMessage());
        }
    }

    @RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
    public Object edit(@PathVariable String id, Model model) {
        try {
            CommonResult commonResult = baseService.find(id);
            if (commonResult.isSuccess()) {
                T t = (T) commonResult.getData();
                model.addAttribute(t);
                return this.path + "/edit";
            } else {
                request.setAttribute("errorMessage", commonResult.getMessage());
                return "/error";
            }
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            request.setAttribute("errorMessage", ex.getMessage());
            return "/error";
        }
    }

    @ResponseBody
    @RequestMapping(value = "/edit/{id}", method = RequestMethod.POST)
    public Object edit(@PathVariable String id, @RequestBody T t) {
        try {
            if (!id.equals(t.getId())) {
                return CommonResult.fail("id不一致");
            }

            CommonResult commonResult = this.validate(t, ValidateGroup.Update.class);
            if (!commonResult.isSuccess())
                return commonResult;

            t.setUpdateUser(this.currentUserName());
            commonResult = baseService.update(t);
            return commonResult;
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            return CommonResult.fail(ex.getMessage());
        }
    }

    @ResponseBody
    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    public Object delete(@PathVariable String id) {
        try {
            baseService.delete(id);
            return CommonResult.success();
        } catch (Exception ex) {
            logger.error(ex.getMessage(), ex);
            return CommonResult.fail(ex.getMessage());
        }
    }
}
CrudController.java

  别的,由于增加了数据字典。rms模块须要增加对应的contoller和页面。这里不逐一赘述。既然已有了数据字典,之前写死的罗列,EnumUtil都能够废除了。直接从hessian读取数据字典设置到缓存。

  在com.idlewow.rms.support.util包下增加DataDictUtil类,代码以下:

package com.idlewow.rms.support.util;

import com.idlewow.common.model.CommonResult;
import com.idlewow.datadict.model.DataDict;
import com.idlewow.datadict.model.DataType;
import com.idlewow.datadict.service.DataDictService;
import com.idlewow.query.model.DataDictQueryParam;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
public class DataDictUtil implements Serializable {
    private static final Logger logger = LogManager.getLogger(DataDictUtil.class);

    @Autowired
    DataDictService dataDictService;

    public static Map<String, Map<String, String>> ConfigMap = new HashMap<>();

    public void initialize() {
        DataDictQueryParam dataDictQueryParam = new DataDictQueryParam();
        CommonResult commonResult = dataDictService.list(dataDictQueryParam);
        if (commonResult.isSuccess()) {
            List<DataDict> dataDictList = (List<DataDict>) commonResult.getData();
            for (DataDict dataDict : dataDictList) {
                if (ConfigMap.containsKey(dataDict.getParentCode())) {
                    ConfigMap.get(dataDict.getParentCode()).put(dataDict.getCode(), dataDict.getValue());
                } else {
                    Map map = new HashMap();
                    map.put(dataDict.getCode(), dataDict.getValue());
                    ConfigMap.put(dataDict.getParentCode(), map);
                }
            }
        } else {
            logger.error("缓存加载失利!");
        }
    }

    public static Map<String, String> occupy() {
        return ConfigMap.get(DataType.Occupy.getCode());
    }

    public static Map<String, String> job() {
        return ConfigMap.get(DataType.Job.getCode());
    }

    public static Map<String, String> faction() {
        return ConfigMap.get(DataType.Faction.getCode());
    }

    public static Map<String, String> mobClass() {
        return ConfigMap.get(DataType.MobClass.getCode());
    }

    public static Map<String, String> mobType() {
        return ConfigMap.get(DataType.MobType.getCode());
    }
}
DataDictUtil.java

  在StartUpListener中,初始化缓存:

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        logger.info("缓存初始化。。。");
        dataDictUtil.initialize();
        logger.info("缓存初始化终了。。。");
    }

  后端缓存有了,一样的,前端写死的罗列也不须要了。能够运用localStorage举行缓存。代码以下:

/* 数据字典缓存 */
var _cache = {
    version: new Date().getTime(),
    configmap: null
};

/* 读取缓存 */
function loadCache() {
    if (_cache.configmap == null || (new Date().getTime() - _cache.version) > 1000 * 60 * 30) {
        var localConfigMap = localStorage.getItem("configmap");
        if (localConfigMap) {
            _cache.configmap = JSON.parse(localConfigMap);
        } else {
            /* 读取数据字典缓存 */
            $.ajax({
                url: '/manage/data_dict/configMap',
                type: 'post',
                success: function (data) {
                    _cache.configmap = data;
                    localStorage.setItem("configmap", JSON.stringify(_cache.configmap));
                },
                error: function () {
                    alert('ajax error');
                }
            });
        }
    }
}

/* 数据字典Key */
var DataType = {
    "Occupy": "10100",  // 疆域归属
    "Faction": "10110", // 阵营
    "Race": "10200",    // 种族
    "Job": "10250",     // 职业
    "MobType": "10300", // 怪物范例
    "MobClass": "10310" // 怪物品种
};

DataDict.prototype = {
    occupy: function (value) {
        return _cache.configmap[DataType.Occupy][value];
    },
    job: function (value) {
        return _cache.configmap[DataType.Job][value];
    },
    faction: function (value) {
        return _cache.configmap[DataType.Faction][value];
    },
    mobClass: function (value) {
        return _cache.configmap[DataType.MobClass][value];
    },
    mobType: function (value) {
        return _cache.configmap[DataType.MobType][value];
    }
};

loadCache();
Helper.js

  注重,这里运用了jQuery的ajax要求,必须在援用之前援用jquery。

小结

       内容有些许脱漏,下周再补充些。

  源码下载地点:https://545c.com/file/14960372-405053633

 

  选择打赏方式
微信赞助

打赏

QQ钱包

打赏

支付宝赞助

打赏

  移步手机端
从零开始完成安排游戏(十二)——完成战役挂机(3)数据字典和缓存革新

1、打开你手机的二维码扫描APP
2、扫描左则的二维码
3、点击扫描获得的网址
4、可以在手机端阅读此文章
未定义标签

本文来源:搜奇网

本文地址:https://www.sou7.cn/281986.html

关注我们:微信搜索“搜奇网”添加我为好友

版权声明: 本文仅代表作者个人观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。请记住本站网址https://www.sou7.cn/搜奇网。

发表评论

选填

必填

必填

选填

请拖动滑块解锁
>>