微服务[学成在线] day06:页面发布以及课程管理

😎 知识点概览

为了方便后续回顾该项目时能够清晰的知道本章节讲了哪些内容,并且能够从该章节的笔记中得到一些帮助,所以在完成本章节的学习后在此对本章节所涉及到的知识点进行总结概述。

本章节为【学成在线】项目的 day06 的内容

  • 使用 Spring boot 集成 RabbitMQGridFS 实现基于生产者和消费者模型的页面静态化发布的流程。

    在本章节的知识点中,再次复习了基于 GridFSRabbitMQ 的分布式静态页面发布的知识点,深化了记忆。

  • 使用三级菜单实现课程计划的查询和添加

    这里的技术点不是很多,用到了 Mysql 的表内自连接查询,以及在添加课程的时候,需要考虑一些意外情况的发生,例如再添加课程时,如果该课程的根节点(一级菜单)不存在,则需要为该课程添加一个根节点后再进行该二级节点的添加。

目录

内容会比较多,小伙伴们可以根据目录进行按需查阅。

一、页面发布

0x01 技术方案

本项目使用MQ实现页面发布的技术方案如下:

image-20200401211416315

**技术方案说明 **

1、平台包括多个站点,页面归属不同的站点。

2、发布一个页面应将该页面发布到所属站点的服务器上。

3、每个站点服务部署 cms client 程序,并与交换机绑定,绑定时指定站点Id为routingKey。

指定站点id为 routingKey 就可以实现 cms client 只能接收到所属站点的页面发布消息。

4、页面发布程序向MQ发布消息时指定页面所属站点 IdroutingKey,将该页面发布到它所在服务器上的cms client

路由模式分析如下

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

比如 发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布。

所以本项目采用 routing 模式,用站点 id 作为 routingKey,这样就可以匹配页面只发布到所属的站点服务器上。

页面发布流程图如下

image-20200401211714847

1、前端请求 cms 执行页面发布。

2、cms 执行静态化程序生成 html文件。

3、cmshtml 文件存储到 GridFS 中。

4、cmsMQ 发送页面发布消息

5、MQ 将页面发布消息通知给 Cms Client

6、Cms ClientGridFS 中下载 html 文件

7、Cms Clienthtml 保存到所在服务器指定目录

0x02 页面发布消费方

需求分析

功能分析

创建 Cms Client 工程作为页面发布消费方,将 Cms Client 部署在多个服务器上,它负责接收到页面发布 的消息后从 GridFS 中下载文件在本地保存。

需求如下

1、将 cms Client 部署在服务器,配置队列名称和站点 ID

2、cms Client 连接 RabbitMQ 并监听各自的“页面发布队列”

3、cms Client 接收页面发布队列的消息

4、根据消息中的页面 idmongodb 数据库下载页面到本地

创建Cms Client工程

1、POM配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>xc-framework-parent</artifactId>
        <groupId>com.xuecheng</groupId>
        <version>1.0-SNAPSHOT</version>
        <relativePath>../xc-framework-parent/pom.xml</relativePath>
    </parent>

    <artifactId>xc-service-manage-cms-client</artifactId>

    <!--项目依赖-->
    <dependencies>
        <dependency>
            <groupId>com.xuecheng</groupId>
            <artifactId>xc-framework-model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-io</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>
    </dependencies>
</project>
2、配置文件

resources下配置 application.yml

server:
  port: 31000
spring:
  application:
    name: xc-service-manage-cms-client
  data:
    mongodb:
      uri: mongodb://root:123123@localhost:27017
      database: xc_cms
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /
  freemarker:
    cache: false #关闭模板缓存,方便测试
    settings:
      template_update_delay: 0
xuecheng:
  mq:
  #cms客户端监控的队列名称(不同的客户端监控的队列不能重复)
    queue: queue_cms_postpage_01
    routingKey: 5a751fab6abb5044e0d19ea1 #此routingKey为门户站点ID3

说明 在配置文件中配置队列的名称,每个 cms client在部署时注意队列名称不要重复

3、启动类
package com.xuecheng.manage_cms_client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(basePackages={
   "com.xuecheng.framework"})//扫描common下的所有类
@ComponentScan(basePackages={
   "com.xuecheng.manage_cms_client"})
public class ManageCmsClientApplication {
   
    public static void main(String[] args) {
   
        SpringApplication.run(ManageCmsClientApplication.class,args);
    }
}

RabbitmqConfig 配置类

消息队列设置如下

1、创建 ex_cms_postpage 交换机

2、每个 Cms Client 创建一个队列与交换机绑定

3、每个 Cms Client 程序配置队列名称和 routingKey,将站点ID作为routingKey

package com.xuecheng.manage_cms_client.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 {
   
    //队列bean的名称
    public static final String QUEUE_CMS_POSTPAGE = "queue_cms_postpage";
    //交换机的名称
    public static final String EX_ROUTING_CMS_POSTPAGE = "ex_routing_cms_postpage";

    //队列的名称
    @Value("${xuecheng.mq.queue}")
    public String queue_cms_postpage_name;
    //站点id作为routing key
    @Value("${xuecheng.mq.routingKey}")
    public String routingKey;

    /**
     * 配置direct交换机
     * @return
     */
    @Bean(EX_ROUTING_CMS_POSTPAGE)
    public Exchange EXCHANGE_DIRECT_INFORM(){
   
        return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
    }

    /**
     * 声明队列
     */
    @Bean(QUEUE_CMS_POSTPAGE)
    public Queue QUEUE_CMS_POSTPAGE(){
   
        Queue queue = new Queue(queue_cms_postpage_name);
        return queue;
    }

    /**
     * 绑定队列到交换机
     * @param queue
     * @param exchange
     * @return
     */
    @Bean
    public Binding BINDING_QUEUE_INFORM_SMS(@Qualifier(QUEUE_CMS_POSTPAGE) Queue queue,
                                            @Qualifier(EX_ROUTING_CMS_POSTPAGE) Exchange exchange){
   
        return BindingBuilder.bind(queue).to(exchange).with(routingKey).noargs();
    }

    
}

配置 MongoConfig

后续的代码中需要将GridFSBucket注入成bean

package com.xuecheng.manage_cms_client.config;


import com.mongodb.MongoClient;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.gridfs.GridFSBucket;
import com.mongodb.client.gridfs.GridFSBuckets;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MongoConfig {
   
    @Value("${spring.data.mongodb.database}")
    String db;

    @Bean
    public GridFSBucket getGridFSBucket(MongoClient mongoClient){
   
        MongoDatabase database = mongoClient.getDatabase(db);
        GridFSBucket gridFSBucket = GridFSBuckets.create(database);
        return gridFSBucket;
    }
}

定义消息格式

消息内容采用 json 格式存储数据,如下

页面id 发布页面的id

{
    "pageId":""
}

PageDao

1、使用 CmsPageRepository 查询页面信息

public interface CmsPageRepository extends MongoRepository<CmsPage,String> {
   }

2、使用 CmsSiteRepository 查询站点信息,主要获取站点物理路径

public interface CmsSiteRepository extends MongoRepository<CmsSite,String> {}

PageService

package com.xuecheng.manage_cms_client.service;

import com.mongodb.client.gridfs.GridFSBucket;

import com.mongodb.client.gridfs.GridFSDownloadStream;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.xuecheng.framework.domain.cms.CmsPage;
import com.xuecheng.framework.domain.cms.CmsSite;
import com.xuecheng.framework.exception.ExceptionCast;
import com.xuecheng.framework.model.response.CmsCode;
import com.xuecheng.manage_cms_client.dao.CmsPageRepository;
import com.xuecheng.manage_cms_client.dao.CmsSiteRepository;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsResource;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;

@Service
public class PageService {
   
    @Autowired
    CmsPageRepository cmsPageRepository;

    @Autowired
    CmsSiteRepository cmsSiteRepository;

    @Autowired
    GridFsTemplate gridFsTemplate;

    @Autowired
    GridFSBucket gridFSBucket;

    /**
     * 将页面html保存到页面物理路径
     * @param pageId
     */
    public void savePageToServerPath(String pageId){
   
        Optional<CmsPage> optional = cmsPageRepository.findById(pageId);
        //页面不存在则抛出异常
        if(!optional.isPresent()){
   
            ExceptionCast.cast(CmsCode.CMS_PAGE_NOT_EXISTS);
        }

        CmsPage cmsPage = optional.get();
        CmsSite cmsSite = this.getCmsSiteById(cmsPage.getSiteId());
        if(cmsSite == null){
   
            ExceptionCast.cast(CmsCode.CMS_SIZE_NOT_EXISTS);
        }

        //原讲义和视频中使用的是sizePathysicaiPath,但是SizePage中没有这个字段,使用PageCms中的PathysicaiPath代替
        String pagePath = cmsPage.getPagePhysicalPath() + cmsPage.getPageWebPath(
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值