学成在线day06,Cms页面发布功能,模块2:课程管理
页面发布的实现流程图:
技术方案说明:
1、平台包括多个站点,页面归属不同的站点。
2、发布一个页面应将该页面发布到所属站点的服务器上。
3、每个站点服务部署cms client程序,并与交换机绑定,绑定时指定站点Id为routingKey。
指定站点id为routingKey就可以实现cms client只能接收到所属站点的页面发布消息。
4、页面发布程序向MQ发布消息时指定页面所属站点Id为routingKey,将该页面发布到它所在服务器上的cms
client。
路由模式分析如下:
发布一个页面,需发布到该页面所属的每个站点服务器,其它站点服务器不发布。
比如:发布一个门户的页面,需要发布到每个门户服务器上,而用户中心服务器则不需要发布。
所以本项目采用routing模式,用站点id作为routingKey,这样就可以匹配页面只发布到所属的站点服务器上。
1.2.2创建Cms Client工程主要功能是吧页面从GridFS下载文件到本地
需要的相关jar包
<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>
数据配置+和日志文件:
server:
port: 31000
spring:
application:
name: xc-service-manage-cms-client
data:
mongodb:
uri: mongodb://root:aaa@qq.comlocalhost:27017
database: xc_cms
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
xuecheng:
mq:
#cms客户端监控的队列名称(不同的客户端监控的队列不能重复)
queue: queue_cms_postpage_01
routingKey: 5a751fab6abb5044e0d19ea1 #此routingKey为门户站点ID
说明:在配置文件中配置队列的名称,每个 cms client在部署时注意队列名称不要重复
日志文件,去前面的文档找。
3、启动类
@SpringBootApplication
@EntityScan("com.xuecheng.framework.domain.cms")//扫描实体类
@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。
//交换机和队列的配置类
@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";
//队列的名称QUEUE_CMS_POSTPAGE,通过配置文件的内容注入具体的队列
@Value("${xuecheng.mq.queue}")
public String queue_cms_postpage_name;
//routingKey 即站点Id
@Value("${xuecheng.mq.routingKey}")
public String routingKey;
//通过常量交换机名,创建新的交换机
@Bean(EX_ROUTING_CMS_POSTPAGE)//注入交换机的名称
public Exchange EXCHANGE_TOPICS_INFORM() {
//通过交换机方法,创建一个名为,配置文件中的交换机名,的交换机,
// 设置为一直都存在。重启也不销毁,创建
return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
}
//通过常量队列名,创建新的队列
@Bean(QUEUE_CMS_POSTPAGE)
public Queue QUEUE_CMS_POSTPAGE() {
//注意bean的队列名,和队列名称不一样。注意看
Queue queue = new Queue(queue_cms_postpage_name);
return queue;
}
//绑定队列到交换机
@Bean
//通过IOC容器中的队列名和交换机名,注入名称。
//再由队列绑定交换机,路由key设置为门户站点,扩展参数,不设置
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();
}
}
写页面发布需要的Dao,站点的信息库:
再写消息消费的监听类ConsumerPostPage:
在cms client工程的mq包下创建ConsumerPostPage类,ConsumerPostPage作为发布页面的消费客户端,监听
页面发布队列的消息,收到消息后从mongodb下载文件,保存在本地
@Component
public class ConsumerPostPage {
private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerPostPage.class);
@Autowired
PageService pageService;
//从配置文件中,注入要监听的队列名称
@RabbitListener(queues = {"${xuecheng.mq.queue}"})
//只要队列中有消息,
public void postPage(String msg){
//就解析消息为map
Map map = JSON.parseObject(msg, Map.class);
//得到消息中的页面id
String pageId = (String) map.get("pageId");
//校验页面是否合法
CmsPage cmsPage = pageService.findCmsPageById(pageId);
if(cmsPage == null){
//不合法,就在日志中打印找不到这个页面的日志
LOGGER.error("receive postpage msg,cmsPage is null,pageId:{}",pageId);
return ;
}
//调用service方法将页面从GridFs中下载到服务器
pageService.savePageToServerPath(pageId);
}
}
页面发布生产方
再到cms服务中的service中,加入静态化页面到GridFS服务器,发送消息,更新cmsPage页面数据的方法:
//页面发布的方法
public ResponseResult post(String pageId){
//先执行页面静态化,调用写好的静态化方法
String pageHtml = this.getPageHtml(pageId);
//再把静态化好的html存入gridFs文件管理器中,得到一个新的cmspage,对象
//传入cmspage页面id,和静态化好的字符串页面
CmsPage cmsPage = this.saveHtml(pageId, pageHtml);
//给Mq发消息,喊他,可以去下载页面到,本地客户端的目录下了
this.sendPostPage(pageId,cmsPage);
//返回消息发送成功
return new ResponseResult(CommonCode.SUCCESS);
}
//向mq 发送消息
private void sendPostPage(String pageId,CmsPage cmsPage){
//消息队列的路由key是站点id,所以需要得到站点id
String siteId = cmsPage.getSiteId();
//发送一个map,转的json
Map<String,String> msg = new HashMap<>();
//存页面id
msg.put("pageId",pageId);
//把map转为json字符串发送
String jsonString = JSON.toJSONString(msg);
//发送消息给交换机,交换机,站点id,消息内容
rabbitTemplate.convertAndSend(RabbitmqConfig.EX_ROUTING_CMS_POSTPAGE,siteId,jsonString);
}
//往gridFs文件管理器中,存静态化好的html方法
private CmsPage saveHtml(String pageId,String htmlContent){
//查询要更新哪个cmsPage页面
CmsPage cmsPage = this.getById(pageId);
if (cmsPage == null) {
//传入的参数非法
ExceptionCast.cast(CommonCode.INVALID_PARAM);
}
//存静态页面,先把字符串页面转换为输入流
ObjectId objectId = null;
try {
InputStream inputStream = IOUtils.toInputStream(htmlContent, "utf-8");
//通过grid模板,存输入流,页面名称存进去,取得自动生成的静态页面的id,类型为object
objectId = gridFsTemplate.store(inputStream, cmsPage.getPageName());
} catch (IOException e) {
e.printStackTrace();
}
//将html文件id更新到cmsPage中,
cmsPage.setHtmlFileId(objectId.toHexString());
cmsPageRepository.save(cmsPage);
//最后在把,已经更新过FileId列id,的对象进行返回
return cmsPage;
}
RabbitMQ配置
1、配置Rabbitmq的连接参数
在application.yml添加如下配置:
注意,这个配置是在Spring之下的:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
virtualHost: /
写交换机的配置类:
//交换机和队列的配置类
@Configuration
public class RabbitmqConfig {
//交换机名称
public static final String EX_ROUTING_CMS_POSTPAGE="ex_routing_cms_postpage";
//通过常量交换机名,创建新的交换机
@Bean(EX_ROUTING_CMS_POSTPAGE)//注入交换机的名称
public Exchange EXCHANGE_TOPICS_INFORM() {
//通过交换机方法,创建一个名为,配置文件中的交换机名,的交换机,
// 设置为一直都存在。重启也不销毁,创建
return ExchangeBuilder.directExchange(EX_ROUTING_CMS_POSTPAGE).durable(true).build();
}
}
在api工程定义页面发布接口:
//页面发布
@ApiOperation("发布页面")
public ResponseResult post(String pageId);
CmsPageController
编写Controller实现api接口,接收页面请求,调用service执行页面发布。
@Override
@PostMapping("/postPage/{pageId}")
public ResponseResult post(@PathVariable("pageId") String pageId) {
return pageService.post(pageId);
}
写页面发布前端:
用户操作流程:
1、用户进入cms页面列表。
2、点击“发布”请求服务端接口,发布页面。
3、提示“发布成功”,或发布失败。
API方法
在 cms前端添加 api方法。
//发布页面
export const page_postPage= (id) => {
return http.requestPost(apiUrl+'/cms/page/postPage/'+id)
}
页面
修改page_list.vue,添加发布按钮
<el-button
size="small"type="text"
@click="postPage(page.row.pageId)">发布
</el-button>
添加页面发布事件:
postPage (pageId) {
this.$confirm('确认发布该页面吗?', '提示', {
}).then(() => {
cmsApi.page_postPage(pageId).then((res) => {
if(res.success){
console.log('发布页面id='+pageId);
this.$message.success('发布成功,请稍后查看结果');
}else{
this.$message.error('发布失败');
}
});
}).catch(() => {
});
},
1.6 思考
1、如果发布到服务器的页面内容不正确怎么办?
2、一个页面需要发布很多服务器,点击“发布”后如何知道详细的发布结果?
3、一个页面发布到多个服务器,其中有一个服务器发布失败时怎么办?
模块2:课程管理
上一篇: 实验3.4:以邻接表为存储结构的图的深度、宽度优先遍历
下一篇: 图的邻接矩阵存储结构