HRM人力资源系统-Day09

静态化优化方案设计

课程主页的访问人数非常多, 以不发请求静态页面代替要发请求静态页面或者动态页面.没有对后台数据获取.
课程详情页:只要课程信息不改,详情页就不会改变.
官网主页:一定的时间段是不可变
招聘主页:一定的时间段是不可变
职位详情:只要职位信息不改,详情页就不会改变.

有的页面访问人数很多,但是在一定时间段内不会改变(数据没变化).页面静态化.
静态化好处
①降低数据库或缓存压力
②提高响应速度,增强用户体验.
基本分析
静态页面=模板(结构)+数据(内容)
静态页面生成时机:
①当部署并启动,需要在后台管理里面触发一个按钮,初始化静态页面. 初始化
②当数据(类型,广告等)发生改变后(后台做了增删改),要覆盖原来静态页面. 替换
方案:页面静态化,通过模板技术来实现.
模板技术:freemaker,velocity,thymeleaf等

模板(velocity)+数据=静态页面(初始化,数据改变)

Dfs技术-fastdfs

Client

<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form</artifactId>
    <version>2.1.0</version>
</dependency>
<dependency>
    <groupId>io.github.openfeign.form</groupId>
    <artifactId>feign-form-spring</artifactId>
    <version>2.1.0</version>
</dependency>
@FeignClient(value = "HRM-FASTDFS",configuration = FeignMultipartSupportConfig.class,
//@FeignClient(value = "HRM-FASTDFS",configuration = FeignClientsConfiguration.class,
        fallbackFactory = FastDfsClientHystrixFallbackFactory.class
        )
@RequestMapping("/fastdfs")
public interface FastDfsClient {
//    @RequestMapping(value="/upload",method= RequestMethod.POST)
     @PostMapping(value="/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
             , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    String upload(@RequestPart("file") MultipartFile file);

    /**
     * 删除对象信息
     * @return
     */
    @RequestMapping(value="/delete",method=RequestMethod.DELETE)
    AjaxResult delete(@RequestParam("path") String path);

    //获取用户
    @RequestMapping(value = "/download",method = RequestMethod.GET,consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    Response download(@RequestParam("path")String path); //直接把流写到response

}

Service

package org.hhw.hrm.controller;

import org.hhw.hrm.client.FastDfsClient;
import org.hhw.hrm.util.AjaxResult;
import org.hhw.hrm.util.FastDfsApiOpr;
import com.sun.xml.internal.messaging.saaj.util.ByteInputStream;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

@RestController
@RequestMapping("/fastdfs")
public class FastDfsController {
    Logger logger = LoggerFactory.getLogger(FastDfsController.class);
//    @PostMapping("/upload")
//    public String upload(@RequestParam("file") MultipartFile file) {

    @PostMapping(value="/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
            , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String upload(@RequestPart("file") MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename(); // 1.png
            String extName = fileName.substring(fileName.lastIndexOf(".")+1);
            System.out.println(extName);
            return FastDfsApiOpr.upload(file.getBytes(),extName);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("error...."+e.getMessage());
        }
        return null;
    }

    @DeleteMapping("/delete")
    public AjaxResult delete(@RequestParam("path") String path) {
        ///group1/xxxx
        try {
            String pathTmp = path.substring(1); // goup1/xxxxx/yyyy
            String groupName =  pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
            String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// /xxxxx/yyyy
            System.out.println(groupName);
            System.out.println(remotePath);
            FastDfsApiOpr.delete(groupName,remotePath);
            return AjaxResult.me();
        }catch (Exception e){
            e.printStackTrace();
            logger.error("error...."+e.getMessage());
            return AjaxResult.me().setSuccess(false).setMessage(e.getMessage());
        }
    }

    @GetMapping(value = "/download",consumes = MediaType.APPLICATION_JSON_UTF8_VALUE)
    public void download(@RequestParam("path")String path, HttpServletResponse response) {
        String pathTmp = path.substring(1); // goup1/xxxxx/yyyy
        String groupName =  pathTmp.substring(0, pathTmp.indexOf("/")); //goup1
        String remotePath = pathTmp.substring(pathTmp.indexOf("/")+1);// xxxxx/yyyy
        System.out.println(groupName);
        System.out.println(remotePath);
        OutputStream os = null;
        InputStream is = null;
        try {
            byte[] datas = FastDfsApiOpr.download(groupName, remotePath);
            os = response.getOutputStream(); //直接给以流方式进行返回
            is = new ByteInputStream(datas,datas.length);
            IOUtils.copy(is,os);
        }catch (Exception e){
            e.printStackTrace();
            logger.error("error...."+e.getMessage());
        }
        finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

课程主页静态化实现

测试方法初始化
数据改变重新生成
技术方案说明:
1、平台包括多个站点,页面归属不同的站点。
2、发布一个页面应将该页面发布到所属站点的服务器上。
3、每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey。 指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息。
4、页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cms client。
路由模式分析如下:
发布一个页面,需发布到该页面所属的每个站点服务器,其它站点服务器不发布。
比如:发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布。 所以本项目采用routing模式,用站点id作为routingKey,这样就可以匹配页面只发布到所属的站点服务器上。
在这里插入图片描述
课程模块

//初始化课程管理首页
    @Test
    public void testInitCourseSiteIndex()throws Exception{
        courseTypeService.InitCourseSiteIndex();
}



@Autowired
    private PageConfigClient pageConfigClient;
    @Override
    public void InitCourseSiteIndex() {
        //1 准备模板,并且上传fastdfs
        //2存放数据到redis
        List<CourseType> courseTypes = queryTypeTree(0L);
        String dataKey = "courseTypes";
        redisClient.set(dataKey, JSONArray.toJSONString(courseTypes));
        //3调用静态化接口产生静态页面,并且放入fastdfs
        String pageName = "CourseIndex";
        //本来应该通过PageName获取page后设置pageconfig传递,由于数据在查询端,还不如直接传入pageName到那边查询.
        Map<String,String> map = new HashMap<>();
        map.put("dataKey",dataKey);
        map.put("pageName",pageName);
        pageConfigClient.staticPage(map);
        //4往消息队列放一个消息,让pageAgent来下载静态页面
    }


 @Override
    public boolean insert(CourseType entity) {
        courseTypeMapper.insert(entity);
        List<CourseType> courseTypes = queryTypeTree(0L);
        courseTypeCache.setCourseTypes(courseTypes);

        //同步页面静态化
        this.InitCourseSiteIndex();
        return true;
    }

    @Override
    public boolean deleteById(Serializable id) {
        courseTypeMapper.deleteById(id);
        List<CourseType> courseTypes = queryTypeTree(0L);
        courseTypeCache.setCourseTypes(courseTypes);

        //同步页面静态化
        this.InitCourseSiteIndex();
        return true;
    }

    @Override
    public boolean updateById(CourseType entity) {
        courseTypeMapper.updateById(entity);
        List<CourseType> courseTypes = queryTypeTree(0L);
        courseTypeCache.setCourseTypes(courseTypes);

        //同步页面静态化
        this.InitCourseSiteIndex();
        return true;
    }

页面生成者-page

PageConfigController

//页面静态化接口
    @PostMapping("/staticPage")
    AjaxResult staticPage(@RequestBody Map<String, String> map){
        String dataKey = map.get("dataKey");
        String pageName = map.get("pageName");
        try {
            pageConfigService.staticPage(dataKey,pageName);
            return AjaxResult.me();
        } catch (Exception e) {
            e.printStackTrace();
            return AjaxResult.me().setSuccess(false).setMessage("静态化失败!"+e.getMessage());
        }

    }
@Override
    public void staticPage(String dataKey, String pageName) {
        FileOutputStream os = null;
        InputStream is = null;
        try {
            //一 页面静态化
            Pager pager = pagerMapper
                    .selectList(new EntityWrapper<Pager>().eq("name", pageName)).get(0);
            String templateUrl = pager.getTemplateUrl(); //fastdfs地址 zip包的
            String templateName = pager.getTemplateName(); //要执行模板文件

            //1.1 下载fastdfs上面压缩包
            Response response =
                    fastDfsClient.download(templateUrl); //通过fastdfs下载压缩包
            is = response.body().asInputStream();

            //1.2 所有静态化中间数据都写入临时目录
            //1)跨操作系统
            //2 操作系统会自动维护,不用删除
            String tmpdir=System.getProperty("java.io.tmpdir");
            System.out.println(tmpdir+"jjjjj.........");
            String zipPath = tmpdir+"/temp.zip"; //要下载zip包路径
            String unZipPath = tmpdir + "/temp/"; //解压到路径
            os = new FileOutputStream(zipPath);
            IOUtils.copy(is , os); //保存到本地

            ZipUtil.unzip(zipPath,unZipPath); // 解压缩

            //1.3 获取到模板
            String templatePath = unZipPath+"/"+templateName; //模板路径 temp/home.vm
            System.out.println(templatePath+"zz.........");
            //2 生成静态页面的路劲
            String templatePagePath = templatePath+".html"; //本地静态页面地址
            System.out.println(templatePagePath+"xxx.........");



            //3 生成数据
            String courseTypes =redisClient.get("courseTypes"); //redis数据
            List<CourseTypeDto> courseTypeDtos = JSONArray.parseArray(courseTypes,CourseTypeDto.class);
            Map<String, Object> modelMap = new HashMap<>(); //封装两个参数作为一个对象传入进去
            modelMap.put("staticRoot", unZipPath);
            modelMap.put("courseTypes", courseTypeDtos);


            //4 做页面静态化
            VelocityUtils.staticByTemplate(modelMap,templatePath,templatePagePath); //进行页面静态化

            // 5 传递到fastdfs
            String pageUrl = fastDfsClient.upload(
                    new CommonsMultipartFile(createFileItem(new File(templatePagePath),"file")));
            //二 PageConfig 并且进行保存
            PageConfig config = new PageConfig();
            config.setTemplateUrl(templateUrl);
            config.setTemplateName(templateName);
            config.setDataKey(dataKey);
            config.setPhysicalPath(pager.getPhysicalPath());
            config.setDfsType(0L); //0表示fastDfs
            config.setPageUrl(pageUrl);
            config.setPageId(pager.getId());
            pageConfigMapper.insert(config);
            //三 往消息队列放入消息
            String routingKey = siteMapper
                    .selectList(new EntityWrapper<Site>().eq("id", pager.getSiteId())).get(0).getSn();
            System.out.println(routingKey+"ddh......");
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("fileSysType",0);
            jsonObject.put("staticPageUrl",pageUrl);
            jsonObject.put("physicalPath",pager.getPhysicalPath());
            System.out.println(jsonObject.toJSONString()+"dbl.....");
            rabbitTemplate.convertAndSend(
                    RabbitmqConstants.EXCHANGE_DIRECT_INFORM,routingKey,jsonObject.toJSONString());
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /*
创建FileItem
 */
    private FileItem createFileItem(File file,String filedName) {
        FileItemFactory factory = new DiskFileItemFactory(16, null);
        FileItem item = factory.createItem(filedName, "text/plain", true, file.getName());
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        try {
            FileInputStream fis = new FileInputStream(file);
            OutputStream os = item.getOutputStream();
            while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.close();
            fis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return item;
    }

页面发布消费方 -PageAgent

package org.hhw.hrm.config;


import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 主站
 * 课程管理
 * 职位管理
 */
@Configuration
public class RabbitmqConfig {

    @Value("${rabbitmq.queues.routingKey}")
    public  String routingKey;
    public static final String EXCHANGE_DIRECT_INFORM = RabbitmqConstants.EXCHANGE_DIRECT_INFORM;
    public static final String QUEUE_INFORM_PAGESTATIC = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC;

    /**
     * 交换机配置
     * ExchangeBuilder提供了fanout、direct、topic、header交换机类型的配置
     *
     * @return the exchange
     */
    @Bean(EXCHANGE_DIRECT_INFORM)
    public Exchange exchange_direct_inform() {
    //durable(true)持久化,消息队列重启后交换机仍然存在
        return ExchangeBuilder.directExchange(EXCHANGE_DIRECT_INFORM).durable(true).build();
    }

    //声明队列
    @Bean(QUEUE_INFORM_PAGESTATIC) //交给spring管理的bean的名字可以随便的
    public Queue pageStaticQueue() {
        Queue queue = new Queue(QUEUE_INFORM_PAGESTATIC); //队列名
        return queue;
    }

    //    Qualifier//获取特定名称bean
    @Bean
    public Binding BINDING_QUEUE_INFORM_HRMJOBSITE(@Qualifier(QUEUE_INFORM_PAGESTATIC) Queue queue,
                                              @Qualifier(EXCHANGE_DIRECT_INFORM) Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
    }


    public String getRoutingKey() {
        return routingKey;
    }

    public void setRoutingKey(String routingKey) {
        this.routingKey = routingKey;
    }
}

@Component
public class StaticPageDownloadHandler {

    @Autowired
    private FastDfsClient fastDfsClient;

    @RabbitListener(queues = RabbitmqConstants.QUEUE_INFORM_PAGESTATIC)
    public void receiveHomeSite(String msg, Message message, Channel channel) {
        //msg -fileSysType,staticPageUrl,physicalPath
        JSONObject jsonObject = JSONObject.parseObject(msg);
        Integer fileSysType = jsonObject.getInteger("fileSysType");
        String staticPageUrl = jsonObject.getString("staticPageUrl");
        String physicalPath = jsonObject.getString("physicalPath");
        System.out.println(staticPageUrl);
        System.out.println(physicalPath);

        switch (fileSysType) {
            case 0: //fastdfs
                fastDfsDownloadAndCopy(staticPageUrl, physicalPath);
                break;
            case 1: //hdfs
                hdfsDownloadAndCopy(staticPageUrl, physicalPath);
                break;
            default:
                break;
        }
    }
    /**
     * 通过fastdfs下载文件,并且拷贝到特定的目录
     * @param staticPageUrl
     * @param physicalPath
     */
    private void hdfsDownloadAndCopy(String staticPageUrl, String physicalPath) {

        //@TODO
    }

    /**
     * 通过fastdfs下载文件,并且拷贝到特定的目录
     * @param staticPageUrl
     * @param physicalPath
     */
    private void fastDfsDownloadAndCopy(String staticPageUrl, String physicalPath) {
        Response response = fastDfsClient.download(staticPageUrl);
        InputStream is = null;
        OutputStream os = null;
        try {
            is  = response.body().asInputStream();
            os = new FileOutputStream(physicalPath);
            IOUtils.copy(is,os);
        } catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值