SAAS-HRM-页面静态化

为什么要使用页面静态化

课程主页的访问人数非常多, 以不发请求静态页面代替要发请求静态页面或者动态页面.没有对后台数据获取.

课程详情页:只要课程信息不改,详情页就不会改变.

官网主页:一定的时间段是不可变

招聘主页:一定的时间段是不可变

职位详情:只要职位信息不改,详情页就不会改变.

有的页面访问人数很多,但是在一定时间段内不会改变(数据没变化).页面静态化

静态化好处

①降低数据库或缓存压力

②提高响应速度,增强用户体验.

方案分析

页面静态化是这一种方案,而模板技术是实现这种方案的技术。

静态页面=模板(结构)+数据(内容)

静态页面生成时机:

​ ①当部署并启动,需要在后台管理里面触发一个按钮,初始化静态页面. 初始化

​ ②当数据(类型,广告等)发生改变后(后台做了增删改),要覆盖原来静态页面. 替换

方案:页面静态化,通过模板技术来实现.

模板技术:freemaker,velocity,thymeleaf等

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

单体项目方案分析

img

集群项目架构分析

img

页面中心功能

新建项目模块

1)页面结构

​ hrm_page_parent

hrm_page_common

hrm_page_client

hrm_page_service_2030

2)基本结构搭建-拷贝course模块

common Pom

<dependencies>

        <!--不能直接依赖starter,有自动配置,而消费者是不需要额。-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.zhanglin</groupId>
            <artifactId>hrm_basic_util</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--不能全部引入mybatis-plus,这是要做数据库操作,这里是不需要的,只需引入核心包解决错误而已-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>2.2.0</version>
        </dependency>
    </dependencies>

client pom

<dependencies>
        <dependency>
            <groupId>com.zhanglin</groupId>
            <artifactId>hrm_page_common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--客户端feign支持-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>
    </dependencies>

service pom

<dependencies>
    <!--所有provider公共依賴-->
    <dependency>
        <groupId>com.zhanglin</groupId>
        <artifactId>hrm_page_common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- Eureka 客户端依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

    <!--配置中心支持-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>

    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger2</artifactId>
        <version>2.9.2</version>
    </dependency>
    <dependency>
        <groupId>io.springfox</groupId>
        <artifactId>springfox-swagger-ui</artifactId>
        <version>2.9.2</version>
    </dependency>

    <!--mybatis-plus支持-->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>

    <!--数据库支持-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!--        引入feign fastdfs调用-->
    <!--所有provider公共依賴-->
    <dependency>
        <groupId>com.zhanglin</groupId>
        <artifactId>hrm_common_client</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
    <dependency>
        <groupId>org.apache.velocity</groupId>
        <artifactId>velocity</artifactId>
        <version>1.7</version>
    </dependency>
</dependencies>

配置

service

bootstrap.yml

spring:
  profiles:
    active: dev
  cloud:
    config:
      name: application-page #github上面名称
      profile: ${spring.profiles.active} #环境 java -jar -D xxx jar
      label: master #分支
      discovery:
        enabled: true #从eureka上面找配置服务
        service-id: hrm-config-server #指定服务名
      #uri: http://127.0.0.1:1299 #配置服务器 单机配置
eureka: #eureka不能放到远程配置中
  client:
    service-url:
      defaultZone: http://localhost:1010/eureka  #告诉服务提供者要把服务注册到哪儿 #单机环境
  instance:
    prefer-ip-address: true #显示客户端真实ip
feign:
  hystrix:
    enabled: true #开启熔断支持
  client:
    config:
      remote-service:           #服务名,填写default为所有服务
        connectTimeout: 3000
        readTimeout: 3000
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

application.yml

spring:
  application:
    name: hrm-page

入口类

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class PageServiceApplication2030 {
    public static void main(String[] args) {
        SpringApplication.run(PageServiceApplication2030.class,args);
    }
}

路由(配置中心)

img

application-page-dev.yml

server:
  port: 2030
spring:
  application:
    name: hrm-page
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/hrm-page
    username: root
    password: 123456
mybatis-plus:
  mapper-locations: classpath:com/zhanglin/mapper/*Mapper.xml
  type-aliases-package: com.zhanglin.domain,com.zhanglin.query

Swagger

img

img

测试

img
项目domain表设计

img

t_site:前端站点 主站 课程 职位 要做页面静态化的前端网站

t_page 页面, 在站点下面有多个页面要做静态化。 定义了模板

t_page_config 每做一次页面静态化,都要添加一个对象。一个页面一次持久化所需要数据保存。

微服务实现-代码生成并测试

img

img

前端实现

模板技术-velocity

Velocity是一个基于java的模板引擎(template engine),它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。作为一个比较完善的模板引擎,Velocity的功能是比较强大的,但强大的同时也增加了应用复杂性。

课程主页静态化实现

pom

<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity -->
<dependency>
    <groupId>org.apache.velocity</groupId>
    <artifactId>velocity</artifactId>
    <version>1.7</version>
</dependency>

VelocityUtils

package com.zhanglin.utils;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;

import java.io.*;
import java.util.Properties;

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();
		}
	}

}

生成课程页面

测试方法初始化

数据改变重新生成

技术方案说明:

  1. 平台包括多个站点,页面归属不同的站点。
  2. 发布一个页面应将该页面发布到所属站点的服务器上。
  3. 每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey。 指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息。
  4. 页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cms client。

路由模式分析如下:

发布一个页面,需发布到该页面所属的每个站点服务器,其它站点服务器不发布。

比如:发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布。 所以本项目采用routing模式,用站点id作为routingKey,这样就可以匹配页面只发布到所属的站点服务器上。

页面发布流程图如下:

img

feign文件上传与下载

下载

Feigin

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

Service

@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);

调用:

Response response =
        fastDfsClient.download(templateUrl); //通过fastdfs下载压缩包
String tmpdir=System.getProperty("java.io.tmpdir");
System.out.println(tmpdir);
String zipName = tmpdir+"/temp.zip";

os = new FileOutputStream(zipName);
is = response.body().asInputStream();
IOUtils.copy(is , os); //保存到本地

上传:

Feign

<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>

配置类 config

import feign.codec.Encoder;
import feign.form.spring.SpringFormEncoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Scope;

@Configuration
public class FeignMultipartSupportConfig {

    @Bean
    @Primary
    @Scope("prototype")
    public Encoder multipartFormEncoder() {
        return new SpringFormEncoder();
    }

    @Bean
    public feign.Logger.Level multipartLoggerLevel() {
        return feign.Logger.Level.FULL;
    }
}

fastDfsClient

//fallbackFactory = CourseTypeClientHystrixFallbackFactory.class
@FeignClient(value = "HRM-FASTDFS",configuration = FeignMultipartSupportConfig.class,
//@FeignClient(value = "HRM-FASTDFS",configuration = FeignClientsConfiguration.class,
        fallbackFactory = FastDfsClientHystrixFallbackFactory.class
        )
@RequestMapping("/fastdfs")
public interface FastDfsClient {
    @PostMapping(value= "/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
            , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public String upload(@RequestPart(value = "file") MultipartFile file);

Service:

@PostMapping(value = "/upload", produces = {MediaType.APPLICATION_JSON_UTF8_VALUE}
        , consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public String upload(@RequestPart(value = "file") MultipartFile file) {
@Autowired
private FastDfsClient fastDfsClient;
@Test
public void test1() throws Exception{
    FileItem fileItem = createFileItem(new File("C:\\Users\\Administrator\\Desktop\\问题.txt"));
    MultipartFile mfile = new CommonsMultipartFile(fileItem);
    fastDfsClient.upload(mfile);
}
/*
创建FileItem
 */
private FileItem createFileItem(File file) {
    FileItemFactory factory = new DiskFileItemFactory(16, null);
    String textFieldName = "textField";
    FileItem item = factory.createItem("file", "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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值