您的位置:首页 > 其它

淘淘商城系列——网页静态化——业务逻辑及测试

2017-06-12 13:15 423 查看
首先,我们把ActiveMQ的客户端jar包的依赖添加到工程中,即需要在taotao-item-web工程中添加对ActiveMQ的依赖,如下图所示。



下面我们需要把taotao-search-service工程下的applicationContext-activemq.xml文件拷贝到taotao-item-web工程下的spring目录下并且更名为springmvc-activemq.xml,除此之外,我们还得对springmvc-activemq.xml文件的内容做一些修改,修改后的内容如下:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd"> 
<!-- 真正可以产生Connection的ConnectionFactory,由对应的JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.129:61616" />
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>

<!--这个是主题目的地,一对多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="item-add-topic" />
</bean>

<!-- 配置消息监听器 -->
<bean id="htmlGenListener" class="com.taotao.item.listener.HtmlGenListener" />
<!-- 配置消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="topicDestination" />
<property name="messageListener" ref="htmlGenListener" />
</bean>

</beans>


上面配置文件中配置了一个消息监听器,那么我们需要创建这么一个消息监听器,当监听到有添加商品的消息时便去生成静态页面,消息监听器类如下图所示。



为方便大家复制,现将HtmlGenListener消息监听器的代码给出。

/**
* 监听商品添加时事件,然后生成商品静态页面
* <p>Title: HtmlGenListener</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
public class HtmlGenListener implements MessageListener {

@Autowired
private ItemService itemService;

@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;

@Value("${HTML_OUT_PATH}")
private String HTML_OUT_PATH;

@Override
public void onMessage(Message message) {
try {
// 1、创建一个MessageListener接口的实现类
// 2、从message中取商品id
TextMessage textMessage = (TextMessage) message;
String strItemId = textMessage.getText();
Long itemId =  new Long(strItemId);
// 3、根据商品id查询商品基本消息、商品描述。(即数据集已准备完毕)
TbItem tbItem = itemService.getItemById(itemId);
Item item = new Item(tbItem);
TbItemDesc tbItemDesc = itemService.getItemDesc(itemId);
// 创建数据集
Map data = new HashMap();
data.put("item", item);
data.put("itemDesc", tbItemDesc);
// 4、创建商品详情页面的模板
// 5、指定静态文件输出目录
Configuration configuration = freeMarkerConfigurer.getConfiguration();
Template template = configuration.getTemplate("item.htm");
FileWriter out = new FileWriter(new File(HTML_OUT_PATH + itemId + ".html"));
// 6、生成静态文件
template.process(data, out);
// 关闭流
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}

}


代码中输出的文件路径配置在配置文件中,如下图所示。



以上代码写完之后,紧接着我们就要来测试一下了。我们先启动各个服务,zookeeper服务、image服务、Solr服务(我这里用的是集群版)、Redis服务(我这里用的是单机版)、ActiveMQ服务。然后依次启动taotao-manager、taotao-content、taotao-search、taotao-manager-web、taotao-portal-web、 taotao-search-web、taotao-item-web这7个工程。都成功启动后,我们在淘淘商城后台页面添加一个商品,如下图所示。



添加完商品后,我们到静态文件目录下查看生成的静态文件,发现啥都没有,如下图所示,不是说好了能生成静态页面吗?这尼玛是要闹哪一出!!!



除此之外,Eclipse控制台还打印java.lang.NullPointerException异常,如下图所示。



然而当我们再次点击提交按钮还是提交该商品时,到静态文件目录下查看生成的静态文件,发现他妈的就有了,如下图所示。



这到底是一个什么样的问题啊!这究竟是为什么啊?我们可以看看taotao-manager-service工程下的ItemServiceImpl实现类中的添加商品的方法,即addItem方法,如下图所示。



大家仔细阅读一下addItem方法,然后我谈谈这个怪问题出现的原因。我们第一次刚开机,电脑比较慢,我们添加商品时,一旦添加完商品之后就直接发送消息了,这时事务提交了吗?由于addItem这个方法还没执行完,消息就已经发送出去了。我们必须明确一点就是,只有这个方法结束之后才会提交事务,现在在这个方法内部还没有提交事务之前,消息就已经发送出去了,消息发送出去之后,这个消息马上就被订阅方接收了,接收了之后,他会马上根据商品id去查询这个商品,由于他去查询商品的时候,我们这个事务还没提交,那么这就是在不同的事务里面,我们那边事务没提交,这边他在查,能查到吗?很显然查不到,查不到之后,所以就报了一个空指针异常。那么第二次为什么好使了呢?第二次我们由于已经处理过这个数据库了,已经添加过一次商品了,数据库中的有些服务已经被调到内存里面了,处理速度就比较快了,那么下一次我们再提交的时候,一旦发完消息之后,这个方法执行完了,事务马上提交,提交之后,订阅方再查询数据库的时候,他就能查询到这个商品信息了。

问题出现了,那到底该怎么解决呢?这里我给出两种解决方案,任君选择。第一种方案,别把发送消息的代码写在Service层中,我们可以写在表现层里面,即写在taotao-manager-web工程下的ItemController类中的addItem方法里面。在这里面发送消息,百分之百就会没问题。因为我们在这儿发消息,说明Service层中添加商品的方法它已然执行完了,服务已经执行完了,商品就已经有了,这时候我们再发个消息,订阅方百分之百的能查到商品信息。第二种方案,正如我所说的那样,在添加商品的时候是涉及到事务的,事务提交之后才能在数据库中查询到商品信息,假如网络不济,造成事务还没提交,接收消息的一端想去查询商品信息,这时显然是查询不到的,为了等待事务提交,采用三次尝试的机制,所以我们应该把HtmlGenListener消息监听器的代码修改一下,修改后如下图所示。



为方便大家复制,现将修改后的HtmlGenListener消息监听器的代码给出。

/**
* 监听商品添加时事件,然后生成商品静态页面
* <p>Title: HtmlGenListener</p>
* <p>Description: </p>
* <p>Company: www.itcast.cn</p>
* @version 1.0
*/
public class HtmlGenListener implements MessageListener {

@Autowired
private ItemService itemService;

@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;

@Value("${HTML_OUT_PATH}")
private String HTML_OUT_PATH;

@Override
public void onMessage(Message message) {
try {
// 1、创建一个MessageListener接口的实现类
// 2、从message中取商品id
TextMessage textMessage = (TextMessage) message;
String strItemId = textMessage.getText();
Long itemId =  new Long(strItemId);
// 3、根据商品id查询商品基本消息、商品描述。(即数据集已准备完毕)
/*
* 等待事务的提交,采用三次尝试的机会
*
* 根据商品id查询商品基本信息,这里需要注意的是消息发送方法
* 有可能还没有提交事务,因此这里是有可能取不到商品信息的,
* 为了避免这种情况出现,我们最好等待事务提交,这里我采用3次
* 尝试的方法,每尝试一次休眠一秒
*/
TbItem tbItem = null;
for (int i = 0; i < 3; i++) {
Thread.sleep(1000); // 休眠一秒
tbItem = itemService.getItemById(itemId);
// 如果获取到了商品基本信息,那就退出循环
if (tbItem != null) {
break;
}
}

Item item = new Item(tbItem);
TbItemDesc tbItemDesc = itemService.getItemDesc(itemId);
// 创建数据集
Map data = new HashMap();
data.put("item", item);
data.put("itemDesc", tbItemDesc);
// 4、创建商品详情页面的模板
// 5、指定静态文件输出目录
Configuration configuration = freeMarkerConfigurer.getConfiguration();
Template template = configuration.getTemplate("item.htm");
FileWriter out = new FileWriter(new File(HTML_OUT_PATH + itemId + ".html"));
// 6、生成静态文件
template.process(data, out);
// 关闭流
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}

}


这里,我选择第二种解决方案。

接下来我们重新启动taotao-item-web工程,再次添加一个商品,然后我们到静态文件目录下查看生成的静态文件,如下图所示,我们双击生成的静态页面。



可以看到生成的静态页面是没有样式的,这是因为我们没有把样式文件放到相应的目录下。



为了让样式好看,我们把taotao-item-web工程的webapp目录下的css、images、js文件拷贝到”F:\temp\freemarker”目录下也就是要与”item”目录在同一级别,如下图所示。



要想看到页面正常效果,我们可以先使用windows版的nginx,大家可以到nginx官网下载一个windows版本的安装包,我下载的是1.8.0版本,解压后进入它的conf目录下,打开nginx.conf文件,修改location下面的root目录为”F:/temp/freemarker”,如下图所示。



修改完后,回到上一级目录,双击nginx.exe运行nginx,会看到一闪而过,这时nginx便启动了。



下面我们便使用nginx来访问我们的静态网页,在浏览器地址栏中输入
http://localhost/item/149724166800604.html
即可看到如下图所示界面,页面正常展示出了商品详情页面。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: