springcloud微服务_05_redis优化和页面静态化

前言

商品模块博客中的商品类型进行优化。主要是使用Redis进行商品类型后台缓存优化和模板Velocity进行商品类型主页面页面静态化

一、商品类型优化方案

1、为什么要进行优化

商品的品牌和类型,都是从数据库中全部获取出来的,每一次都要去数据库中查询一次,从而增加数据库的访问压力,反应时间就会长,造成用户体验差。

  • 后台管理(Redis缓存解决)
    使用缓存解决对数据库的频繁操作问题,用内存查询替换数据库磁盘查询.
  • 商城主页(页面静态化解决)
    上面的缓存还不够优化,如果高并发,就会访问Redis缓存N多次,那么就会对缓存服务器造成压力。所以以不发请求静态页面代替要发请求静态页面或者动态页面。没有对后台数据获取.

二、商品类型后台缓存优化。Redis

1、常见的缓存实现方案

springcloud微服务_05_redis优化和页面静态化_第1张图片

(1)、二级缓存

JPA、mybatis的二级缓存,默认情况下不支持集群环境使用

(2)、Redis中央缓存

2、缓存中的数据和数据库中的数据之间的交互

缓存中数据是对数据库中数据的一种内存存放,如何保持两者的数据一致?换言之它们之间如何交互的

springcloud微服务_05_redis优化和页面静态化_第2张图片

3、Redis的数据存储

在redis中:序列化和json字符串(采纳)

  • 存:
    就是把数据库的数据存到Redis中(json字符串)
  • 取:
    将Redis(json字符串)的数据转换成对象。

(1)、使用到的数据存储转换技术

  • Springmvc:jackson
  • Google:gson
  • Alibaba:fastjson技术 ===>最牛逼

(2)、以下博客是使用fastjson技术来进行对象和json字符串之间的相互转换、集合和json字符串之间的相互转换

FastJson入门博客地址

4、项目实现redis

将像Redis缓存这样的简单服务封装到一个公共服务模块中去,因为项目不止商品模块要用到缓存,其他的模块也会用到。

(1)、创建公共的服务模块

先创建公共的服务模块的父模块aigou_common_parent,以及它的公共接口子模块aigou_common_interface和服务子模块aigou_common_service_6699
springcloud微服务_05_redis优化和页面静态化_第3张图片

( 2)、在pom.xml中配置模块之间的依赖

1)、 服务子模块aigou_common_service_6699依赖于公共接口子模块aigou_common_interface

springcloud微服务_05_redis优化和页面静态化_第4张图片

2)、公共接口子模块aigou_common_interface依赖于公共基础模块中的公共工具子模块aigou_basic_util

springcloud微服务_05_redis优化和页面静态化_第5张图片

3)、然后经过上面的依赖配置后,服务子模块aigou_common_service_6699也就间接了依赖了公共基础模块中的公共工具子模块aigou_basic_util

springcloud微服务_05_redis优化和页面静态化_第6张图片

(3)、公共接口子模块aigou_common_interface

feign的支持和velocity模板支持(在接下来的页面静态化需要)

负载均衡feign:服务内部之间调用使用负载均衡,可以在服务子模块aigou_common_service_6699中使用feign,但是为了规范,==将feign写在公共的服务模块的公共接口子模块中。==方便其他地方好用
springcloud微服务_05_redis优化和页面静态化_第7张图片

1)、在公共接口子模块aigou_common_interface的pom.xml中引入需要的依赖

    
        cn.lyq
        aigou_basic_util
        1.0-SNAPSHOT
    

    
    
        org.springframework.cloud
        spring-cloud-starter-openfeign
    

    
    
    
        org.apache.velocity
        velocity
        1.7
    

2)、创建feign的客户端接口。RedisClient
/**
 * @author lyq
 * @date 2019/5/12 21:20
 * 对Redis设置和获取的操作接口
 */
//feign客户端注解
@FeignClient(value = "COMMON-PROVIDER",fallback = RedisFall.class)
public interface RedisClient {

    /**
     * post参数接受:因为set存值有很多参数,用post方式提交
     * @RequestParam:请求参数,从请求参数key中拿值设置到参数key中去
     * @param key
     * @param value
     * 设置。将参数以key/value的形式存入Redis中。
     */
    @RequestMapping(value = "/redis/set",method = RequestMethod.POST)
    void setRedis(@RequestParam("key")String key,@RequestParam("value") String value);

    /**
     * @param key
     * @return
     * 获取json字符串。传入个参数key键拿到Redis的json字符串
     */
    @RequestMapping(value = "/redis/get/{key}",method = RequestMethod.GET)
    String getRedis(@PathVariable("key") String key);
}
3)、创建feign的托底数据。RedisFall
/**
 * @author lyq
 * @date 2019/5/12 21:48
 * redis的托底数据。想写啥写啥,暂时没有写
 */
@Component//配成一个组件
public class RedisFall implements FallbackFactory {


    public RedisClient create(Throwable throwable) {
        //要得到一个RedisClient对象,那么就new一个
        return new RedisClient() {
            public void setRedis(String key, String value) {

            }

            public String getRedis(String key) {
                return null;
            }
        };
    }
}

(4)、暴露出去的redis服务子模块aigou_common_service_6699

上面的公共接口子模块aigou_common_interface是不会暴露出去的,而要把服务子模块aigou_common_service_6699暴露出去。因为有接口要被使用

1)、在pom.xml中引入依赖

Redis的客户端jedis的支持、springboot支持、以及注册到注册中心支持

jedis操作Redis,类似于jdbc操作MySQL一个道理


    
        cn.lyq
        aigou_common_interface
        1.0-SNAPSHOT
    

    
    
    
        redis.clients
        jedis
        2.9.0
    

    
    
        org.springframework.boot
        spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-starter-test
    

    
    
        org.springframework.cloud
        spring-cloud-starter-netflix-eureka-client
    

2)、服务的controller类。RedisController

controller类要去实现feign的客户端接口RedisClient。因为要约束controller,要一致。它要对外暴露接口。
调用Redis的工具类RedisUtil,完成Redis的set和get方法

@RestController
@RequestMapping("/redis")
public class RedisController implements RedisClient {

    @Override
    @RequestMapping(value = "/set",method = RequestMethod.POST)
    public void setRedis(@RequestParam("key") String key, @RequestParam("value") String value) {
        //redis的set方法。调用工具类
        RedisUtil.set(key,value);
    }

    @Override
    @RequestMapping(value = "/get/{key}",method = RequestMethod.GET)
    public String getRedis(@PathVariable("key") String key) {
        //Redis的get方法。调用工具类
        return RedisUtil.get(key);
    }
}
3)、YAML配置。application.yml文件
server:
  port: 6699
  max-http-header-size: 4048576
spring:
  application:
    name: COMMON-PROVIDER #Redis的接口类会根据此名字扫描
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
4)、创建启动类

公共服务模块aigou_common_service创建个启动类
在这里插入图片描述

5)、创建个Redis的工具类。RedisUtil

由于只是在Redis的服务端使用得到,所以就将工具类放在自己的服务中aigou_common_service_6699
连接池的配置和API的封装

/**
 * redis的工具类 
 */
public class RedisUtil {
    private static JedisPool jedisPool;
    static {
        //连接池的配置:
        GenericObjectPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxTotal(20);
        poolConfig.setMaxIdle(5);
        poolConfig.setMaxWaitMillis(3000);
        poolConfig.setTestOnBorrow(true);
        //创建pool:
        // (GenericObjectPoolConfig poolConfig, String host, int port, int timeout, String password)
         jedisPool = new JedisPool(poolConfig,"127.0.0.1",6379,2000,"123456");
    }

    //设置。set方法  将值设置到Redis中
    public  static void set(String key,String value){
        Jedis jedis =null;
        try {
            jedis= jedisPool.getResource();
            jedis.set(key,value);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(jedis!=null){
                jedis.close();
            }
        }
    }

    //获取json字符串。get方法
    public  static String get(String key){
        Jedis jedis =null;
        try {
            jedis= jedisPool.getResource();
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            if(jedis!=null){
                jedis.close();
            }
        }
    }

    //测试Redis是否能用
    public static void main(String[] args) {
        RedisUtil.set("sex","1");
    }
}
6)、测试Redis工具类

springcloud微服务_05_redis优化和页面静态化_第8张图片

(5)、Redis服务的消费者的业务逻辑

配置好了redis服务子模块aigou_common_service_6699,然后再来处理Redis的消费者。现在项目中的Redis的消费者就是商品分类和商品品牌。

1)、在Redis服务的消费者 aigou_product_service子模块的pom.xml中引入feign的接口依赖,以及fastjson的依赖

使用feign来调用Redis的服务,那么消费者模块aigou_product_service子模块要注入Feign的接口,也就是要使用RedisClient接口。那么就要引入feign接口的依赖
fastjson:操作Redis数据的,能操作对象与json字符串之间的相互转换和list集合和json字符串之间的相互转换



    cn.lyq
    aigou_common_interface
    1.0-SNAPSHOT




    com.alibaba
    fastjson
    1.2.47

2)、aigou_product_service子模块中商品类型的树状图实现Redis

在商品模块的博客中已经完成了商品分类的树状图结构,但是没有将数据放入Redis缓存中去,每次都要去数据库查询,很影响性能,所以此处作为Redis的消费者去调用Redis的服务,实现Redis的缓存效果。

  • 商品类型的service实现类ProductTypeServiceImpl更改代码

做商品类型,从数据库中查询数据之前,先在Redis中查询是否有数据,如果没有那么就要将从数据库中查询出的数据存入到Redis中去,再返回去;如果第一次进来的Redis中就有数据,那么就直接返回。

@Service
public class ProductTypeServiceImpl extends ServiceImpl implements IProductTypeService {

    //注入mapper对象
    @Autowired
    private ProductTypeMapper productTypeMapper;

    //注入Redis接口
    @Autowired
    private RedisClient redisClient;

    //实现树状结构
    @Override
    public List treeData() {
        //递归方法。查看商品类型数据库表得知pid=0为最上一级。返回前台的都是一级菜单
        //return treeDataRecursion(0L);

        //循环方法。
        return treeDataLoop();
    }

    /**
     * 步骤:循环查询
     * 1:先查询出所有的数据
     * 2:再组装父子结构
     * @return
     */
    public List treeDataLoop() {
        //返回的一级菜单
        List result = new ArrayList<>();
        //1.先查询出所有的数据。selectList方法的参数mapper为null即可
        List productTypes=null;
        //List productTypes = productTypeMapper.selectList(null);
        //数据从Redis中获取
        //1.先通过key去Redis中获取json字符串
        String productTypeJson = redisClient.getRedis(AiGouConstants.COMMON_PRODUCT_TYPE);
        //1.1 Redis中没有这个key,那么就要从数据库去获取,并放入Redis中返回
        if(StringUtils.isEmpty(productTypeJson)){
            //Redis中没有数据
            //就从数据库中查询
            productTypes = productTypeMapper.selectList(null);
            //然后将数据存入到Redis中。使用fastJson技术
                //将list对象集合转换成json字符串
            String jsonString = JSON.toJSONString(productTypes);
                //将json字符串存入到Redis中。key/value
            redisClient.setRedis(AiGouConstants.COMMON_PRODUCT_TYPE,jsonString );
            System.out.println("==========from db==============");
        }else{
            //1.2 Redis中有就直接返回
                //返回Redis。将json字符串转换为list集合
            productTypes=JSON.parseArray(productTypeJson, ProductType.class);
            System.out.println("==========from cache==============");
        }
        //定义一个map集合。参数是id和productType类
        Map map=new HashMap<>();
        //再循环查出来的对象,放到map集合中去
        for (ProductType cur : productTypes) {
            //将对象的id和对象放map集合中去
            map.put(cur.getId(), cur);
        }
        //2.组装父子结构。遍历上面查询到的数据,拿到当前对象
        for (ProductType current : productTypes) {
            //找到一级菜单
            if(current.getPid()==0){
                //装到上面定义的result集合中去
                result.add(current);
            }else{
                //否则就不是一级菜单,你是儿子。那么是哪个对象的儿子?
                //先定义一个老子菜单
                ProductType parent=null;

                /*嵌套循环了,更加影响数据库性能,所以不用,用上面定义的map集合方式存放查询出来的数据
                /再循环查出来的数据
                for (ProductType cur : productTypes) {
                    //如果cur对象的id就是current对象的pid,那么cur对象就是老子。多找一下表中的id和pid的关系
                    if(cur.getId()==current.getPid()){
                        //cur对象就是老子
                        parent=cur;
                    }
                }
                */
                //和上面嵌套循环一个意思。如果map集合中的cur对象的id就是current对象的pid,那么cur对象就是老子。多找一下表中的id和pid的关系
                parent = map.get(current.getPid());
                //然后拿到老子的儿子
                List children = parent.getChildren();
                //然后将你自己加进去。你自己是你老子的儿子
                children.add(current);
            }
        }
        //返回装好的一级菜单
        return result;
    }
}    
  • 图示解释service实现类的重点代码部分
    springcloud微服务_05_redis优化和页面静态化_第9张图片
3)、在公共基础代码中aigou_basic_util中增加一个商品类型的常量的工具类AiGouConstants

在第2步service层的实现类中,会作为Redis的key键
在这里插入图片描述

4)、商品服务模块aigou_product_service的启动类需要feign的注解扫描

springcloud微服务_05_redis优化和页面静态化_第10张图片

5)、公共服务模块的YAML配置。

在最上面的操作已经配置好了

6)、融断配置

融断要配在消费者那一边,而aigou_common_service_6699作为Redis的服务者,商品子模块aigou_product_service作为Redis的消费者。所以融断配置在商品子模块aigou_product_service中。

  • 在aigou_product_service子模块中的YAML配置文件中配置
    springcloud微服务_05_redis优化和页面静态化_第11张图片
7)、启动7001注册中心、6699公共服务模块、8002商品模块服务
8)、使用postman测试Redis缓存接口
  • Redis的get接口方法。Redis工具类中测试了一个set方法存值。
    springcloud微服务_05_redis优化和页面静态化_第12张图片
  • Redis的set接口方法。
    springcloud微服务_05_redis优化和页面静态化_第13张图片
  • Redis的请求参数使用postman说明:
    springcloud微服务_05_redis优化和页面静态化_第14张图片
9)、使用postman测试商品分类的接口图示

springcloud微服务_05_redis优化和页面静态化_第15张图片

10)、Feign的使用总结

springcloud微服务_05_redis优化和页面静态化_第16张图片

11)、上述Redis中央缓存问题:如何保证Redis的同步操作

保证Redis增删改数据的时候Redis同步操作。
商品分类的实现类ProductTypeServiceImpl继承了baomidou包的serviceImpl实现类,serviceImpl中有更新数据的update方法。那么就可以直接重写父类的update方法,完成自己的功能即可。

  • 修改ProductTypeServiceImpl商品分类的service实现类代码
/**
 * 

* 商品目录 服务实现类 *

* * @author lyqtest * @since 2019-05-10 */ @Service public class ProductTypeServiceImpl extends ServiceImpl implements IProductTypeService { //注入mapper对象 @Autowired private ProductTypeMapper productTypeMapper; //注入Redis接口 @Autowired private RedisClient redisClient; //实现树状结构 @Override public List treeData() { //递归方法。查看商品类型数据库表得知pid=0为最上一级。返回前台的都是一级菜单 //return treeDataRecursion(0L); //循环方法。 return treeDataLoop(); } //重写继承ServiceImpl中的更新方法。完成Redis的同步操作 @Override public boolean update(ProductType entity, Wrapper wrapper) { //先更新数据库 boolean update = super.update(entity, wrapper); //再查询数据库,然后再调用下面定义的flushRedis()方法同步数据到Redis中去 flushRedis(); return update; } /** * 步骤:循环查询 * 1:先查询出所有的数据 * 2:再组装父子结构 * @return */ public List treeDataLoop() { //返回的一级菜单 List result = new ArrayList<>(); //1.先查询出所有的数据。selectList方法的参数mapper为null即可 List productTypes=null; //List productTypes = productTypeMapper.selectList(null); //数据从Redis中获取 //1.先通过key去Redis中获取json字符串 String productTypeJson = redisClient.getRedis(AiGouConstants.COMMON_PRODUCT_TYPE); //1.1 Redis中没有这个key,那么就要从数据库去获取,并放入Redis中返回 if(StringUtils.isEmpty(productTypeJson)){ //Redis中没有数据 //调用下面定义的flushRedis()方法从数据库中获取 productTypes = flushRedis(); System.out.println("==========from db 从数据库来=============="); }else{ //1.2 Redis中有就直接返回 //返回Redis。将json字符串转换为list集合 productTypes=JSON.parseArray(productTypeJson, ProductType.class); System.out.println("==========from cache 从缓存来=============="); } //定义一个map集合。参数是id和productType类 Map map=new HashMap<>(); //再循环查出来的对象,放到map集合中去 for (ProductType cur : productTypes) { //将对象的id和对象放map集合中去 map.put(cur.getId(), cur); } //2.组装父子结构。遍历上面查询到的数据,拿到当前对象 for (ProductType current : productTypes) { //找到一级菜单 if(current.getPid()==0){ //装到上面定义的result集合中去 result.add(current); }else{ //否则就不是一级菜单,你是儿子。那么是哪个对象的儿子? //先定义一个老子菜单 ProductType parent=null; //如果map集合中的cur对象的id就是current对象的pid,那么cur对象就是老子。多找一下表中的id和pid的关系 parent = map.get(current.getPid()); //然后拿到老子的儿子 List children = parent.getChildren(); //然后将你自己加进去。你自己是你老子的儿子 children.add(current); } } //返回装好的一级菜单 return result; } //从数据库中查询数据 private List flushRedis() { List productTypes;//就从数据库中查询 productTypes = productTypeMapper.selectList(null); //然后将数据存入到Redis中。使用fastJson技术 //将list对象集合转换成json字符串 String jsonString = JSON.toJSONString(productTypes); //将json字符串存入到Redis中。key/value redisClient.setRedis(AiGouConstants.COMMON_PRODUCT_TYPE,jsonString ); return productTypes; } }
  • 完成上述功能的重要代码图示:

大体的重要部分示意图,具体的看上面代码
springcloud微服务_05_redis优化和页面静态化_第17张图片

12)、MySQL的数据和Redis初始化同步时机的两种方式:
  • 常归使用:在第一次使用 的时候,把数据从MySQL写到Redis。第一次很慢
  • 业务多,高并发使用:把需要缓存的数据,在项目启动的时候从数据库查询放入Redis。启动较慢,访问块
  • 根据业务需要选择上述两种

三、商品类型主页面页面静态化。velocity技术

虽然在上面的操作中,做了商品分类的Redis中央缓存。

1、页面静态化的含义

把动态页面使用静态页面替换:就是一个写死数据的html页面。将模板和需要的数据结合,生成一个静态的HTML页面。

2、使用页面静态化的原因

虽然加载商品分类时使用到了Redis中央缓存,但是如果主页的访问人数很多,那么也会给Redis带来很大的压力,那么以不发请求的静态页面代替要发请求静态页面或者动态页面。没有从后台获取数据:对后台的压力就减小了,同时页面不用再动态的渲染,用户体验更好。

3、页面静态化的好处

(1)降低数据库或缓存压力

(2)高响应速度,增强用户体验

4、如何选择什么页面需要静态化

页面布局和数据不会经常变的,比如说首页。或者页面变化较小,比如详情页等等

5、常见的模板技术

(1)freemark

(2)velocity

6、什么时候需要使用静态页面

(1)、前台项目第一次启动的时候

(2)、模板关联的数据,发生变化的时候,就需要重新生成静态页面(首页就是商品分类数据)

7、velocity模板技术运用

完成今天的第二个功能,页面的静态化。由于页面静态化功能以后会在很多地方都需要调用,所以和Redis一样,将这个功能封装成一个服务,在之前的公共服务中增加静态化功能

(1)在公共服务模块aigou_common_parent中的公共接口子模块aigou_common_interface中创建静态化功能的接口和托底数据

1)、静态化接口。PageClient

springcloud微服务_05_redis优化和页面静态化_第18张图片

2)、静态化功能的托底数据。PageFall

springcloud微服务_05_redis优化和页面静态化_第19张图片

(2)提供velocity模板技术的工具类。VelocityUtil

velocity的工具类放在公共服务模块中的aigou_common_interface接口子模块中。

public class VelocityUtils {
	private static Properties p = new Properties();
	static {
		p.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, "");
		p.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
		p.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
		p.setProperty(Velocity.OUTPUT_ENCODING, "UTF-8");
	}
	
	/**
	 * 返回通过模板,将model中的数据替换后的内容
	 * @param model
	 * @param templateFilePathAndName
	 * @return
	 */
	public static String getContentByTemplate(Object model, String templateFilePathAndName){
		try {
			Velocity.init(p);
			Template template = Velocity.getTemplate(templateFilePathAndName);
			VelocityContext context = new VelocityContext();
			context.put("model", model);
			StringWriter writer = new StringWriter();
			template.merge(context, writer);
			String retContent = writer.toString();
			writer.close();
			return retContent;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * 根据模板,静态化model到指定的文件 模板文件中通过访问model来访问设置的内容
	 * 
	 * @param model
	 *            数据对象
	 * @param templateFilePathAndName
	 *            模板文件的物理路径
	 * @param targetFilePathAndName
	 *            目标输出文件的物理路径
	 */
	public static void staticByTemplate(Object model, String templateFilePathAndName, String targetFilePathAndName) {
		try {
			Velocity.init(p);
			Template template = Velocity.getTemplate(templateFilePathAndName);
			
			VelocityContext context = new VelocityContext();
			context.put("model", model);
			FileOutputStream fos = new FileOutputStream(targetFilePathAndName);
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, "UTF-8"));// 设置写入的文件编码,解决中文问题
			template.merge(context, writer);
			writer.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 静态化内容content到指定的文件
	 * 
	 * @param content
	 * @param targetFilePathAndName
	 */
	public static void staticBySimple(Object content, String targetFilePathAndName) {
		VelocityEngine ve = new VelocityEngine();
		ve.init(p);
		String template = "${content}";
		VelocityContext context = new VelocityContext();
		context.put("content", content);
		StringWriter writer = new StringWriter();
		ve.evaluate(context, writer, "", template);
		try {
			FileWriter fileWriter = new FileWriter(new File(targetFilePathAndName));
			fileWriter.write(writer.toString());
			fileWriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

(3)将模板工具类中需要的三个参数设定为常量

在公共基础模块aigou_basic_parent的工具类子模块aigou_basic_util中

springcloud微服务_05_redis优化和页面静态化_第20张图片

(4)在公共服务模块aigou_common_parent模块的商品服务子模块aigou_common_service_6699的controller类中

@RestController
@RequestMapping("/common")
public class PageController implements PageClient {

    @RequestMapping(value = "/page",method = RequestMethod.POST)
    @Override
    public void createPage(@RequestBody Map map) {
        //获取参数:
        //调用封装的工具类:
        Object model=map.get(AiGouConstants.PAGE_MODEL);
        String templateFilePathAndName=(String)map.get(AiGouConstants.PAGE_TEMPLATEFILEPATHANDNAME);
        String targetFilePathAndName=(String)map.get(AiGouConstants.PAGE_TARGETFILEPATHANDNAME);
        System.out.println("model:"+model);
        System.out.println("templateFilePathAndName:"+templateFilePathAndName);
        System.out.println("targetFilePathAndName:"+targetFilePathAndName);
        VelocityUtils.staticByTemplate(model,templateFilePathAndName,targetFilePathAndName);
    }
}

(5)公共服务模块中的aigou_common_interface接口子模块的pom.xml中引入velocity的依赖

上面的操作中已经引入了

springcloud微服务_05_redis优化和页面静态化_第21张图片

(6)在公共服务接口模块aigou_common_interface中的resource资源文件中拷贝进去页面的模板

模板很多的代码
springcloud微服务_05_redis优化和页面静态化_第22张图片

(7)再次对service层实现类ProductServiceImpl进行修改。商品分类模块使用静态化功能最重要的部分代码。

由于在上面的Redis中央缓存操作,在商品主页面访问数量非常大的时候,会更Redis造成压力,所以再结合使用静态化页面功能。

/**
 * 

* 商品目录 服务实现类 *

* * @author lyqtest * @since 2019-05-10 */ @Service public class ProductTypeServiceImpl extends ServiceImpl implements IProductTypeService { //注入mapper对象 @Autowired private ProductTypeMapper productTypeMapper; //注入Redis接口 @Autowired private RedisClient redisClient; //注入静态化接口 @Autowired private PageClient pageClient; //同样是重写父类ServiceImpl的方法。这里解决静态化功能 @Override public boolean updateById(ProductType entity) { //根据数据库 boolean update = super.updateById(entity); //同步redis: List productTypes = flushRedis(); //根据模板,使用数据,生成一个静态页面: List result=new ArrayList<>(); makeStructure(result,productTypes); //先生成productType.html Map map = new HashMap<>(); //设置三个参数: //需要的数据:就是List map.put(AiGouConstants.PAGE_MODEL,result); map.put(AiGouConstants.PAGE_TEMPLATEFILEPATHANDNAME,"H:\\IDEA\\idea_workspace\\aigou_parent\\aigou_common_parent\\aigou_common_interface\\src\\main\\resources\\template\\product.type.vm"); map.put(AiGouConstants.PAGE_TARGETFILEPATHANDNAME,"H:\\IDEA\\idea_workspace\\aigou_parent\\aigou_common_parent\\aigou_common_interface\\src\\main\\resources\\template\\product.type.vm.html"); pageClient.createPage(map); //再生成home.html Map homeMap = new HashMap<>(); //设置三个参数: Map staticRootMap=new HashMap<>(); //根路径: staticRootMap.put("staticRoot","H:\\IDEA\\idea_workspace\\aigou_parent\\aigou_common_parent\\aigou_common_interface\\src\\main\\resources\\"); homeMap.put(AiGouConstants.PAGE_MODEL,staticRootMap); homeMap.put(AiGouConstants.PAGE_TEMPLATEFILEPATHANDNAME,"H:\\IDEA\\idea_workspace\\aigou_parent\\aigou_common_parent\\aigou_common_interface\\src\\main\\resources\\template\\home.vm"); homeMap.put(AiGouConstants.PAGE_TARGETFILEPATHANDNAME,"H:\\IDEA\\idea_workspace\\aigou_parent\\aigou_common_parent\\aigou_common_interface\\src\\main\\resources\\template\\home.html"); pageClient.createPage(homeMap); return update; } //实现树状结构 @Override public List treeData() { //递归方法。查看商品类型数据库表得知pid=0为最上一级。返回前台的都是一级菜单 //return treeDataRecursion(0L); //循环方法。 return treeDataLoop(); } //重写继承ServiceImpl中的更新方法。完成Redis的同步操作 @Override public boolean update(ProductType entity, Wrapper wrapper) { //先更新数据库 boolean update = super.update(entity, wrapper); //再查询数据库,然后再调用下面定义的flushRedis()方法同步数据到Redis中去 flushRedis(); return update; } /** * 步骤:循环查询 * 1:先查询出所有的数据 * 2:再组装父子结构 * @return */ public List treeDataLoop() { //返回的一级菜单 List result = new ArrayList<>(); //1.先查询出所有的数据。selectList方法的参数mapper为null即可 List productTypes=null; //List productTypes = productTypeMapper.selectList(null); //数据从Redis中获取 //1.先通过key去Redis中获取json字符串 String productTypeJson = redisClient.getRedis(AiGouConstants.COMMON_PRODUCT_TYPE); //1.1 Redis中没有这个key,那么就要从数据库去获取,并放入Redis中返回 if(StringUtils.isEmpty(productTypeJson)){ //Redis中没有数据 //调用下面定义的flushRedis()方法从数据库中获取 productTypes = flushRedis(); System.out.println("==========from db 从数据库来=============="); }else{ //1.2 Redis中有就直接返回 //返回Redis。将json字符串转换为list集合 productTypes=JSON.parseArray(productTypeJson, ProductType.class); System.out.println("==========from cache 从缓存来=============="); } //调用下面的makeStructure()方法,装入对应的菜单中 makeStructure(result, productTypes); //返回装好的一级菜单 return result; } private void makeStructure(List result, List productTypes) { //定义一个map集合。参数是id和productType类 Map map=new HashMap<>(); //再循环查出来的对象,放到map集合中去 for (ProductType cur : productTypes) { //将对象的id和对象放map集合中去 map.put(cur.getId(), cur); } //2.组装父子结构。遍历上面查询到的数据,拿到当前对象 for (ProductType current : productTypes) { //找到一级菜单 if(current.getPid()==0){ //装到上面定义的result集合中去 result.add(current); }else{ //否则就不是一级菜单,你是儿子。那么是哪个对象的儿子? //先定义一个老子菜单 ProductType parent=null; /*嵌套循环了,更加影响数据库性能,所以不用,用上面定义的map集合方式存放查询出来的数据 /再循环查出来的数据 for (ProductType cur : productTypes) { //如果cur对象的id就是current对象的pid,那么cur对象就是老子。多找一下表中的id和pid的关系 if(cur.getId()==current.getPid()){ //cur对象就是老子 parent=cur; } } */ //和上面嵌套循环一个意思。如果map集合中的cur对象的id就是current对象的pid,那么cur对象就是老子。多找一下表中的id和pid的关系 parent = map.get(current.getPid()); //然后拿到老子的儿子 List children = parent.getChildren(); //然后将你自己加进去。你自己是你老子的儿子 children.add(current); } } } //从数据库中查询数据 private List flushRedis() { List productTypes;//就从数据库中查询 productTypes = productTypeMapper.selectList(null); //然后将数据存入到Redis中。使用fastJson技术 //将list对象集合转换成json字符串 String jsonString = JSON.toJSONString(productTypes); //将json字符串存入到Redis中。key/value redisClient.setRedis(AiGouConstants.COMMON_PRODUCT_TYPE,jsonString ); return productTypes; } }
  • 重要的代码图示

springcloud微服务_05_redis优化和页面静态化_第23张图片

(8)使用postman接口测试,测试能否生成静态化HTML页面

springcloud微服务_05_redis优化和页面静态化_第24张图片

你可能感兴趣的:(springcloud微服务)