redis依赖包_淘淘商城项目学习日记-06-淘淘商城-redis缓存、搜索系统搭建

淘淘商城-redis缓存、搜索系统搭建

内容复习

轮播图的展示

  1. 内容管理的后台:CMS系统
    1. 内容分类的管理
    2. 内容管理,需要指定内容的分类
  1. 前台展示内容
    1. 需要服务层发布服务。
    2. Portal调用服务获得数据。
    3. HttpClient调用服务获得数据。
    4. 获得数据后,把数据转换成需要的数据格式传递给jsp。

课程计划

  1. 在业务逻辑中添加缓存
    1. 缓存可以使用redis作为缓存。
    2. Redis集群
    3. 在java代码中使用redis单机版、集群版
    4. 在业务逻辑中添加缓存
  1. 搜索系统的实现
    1. 创建一个搜索的工程
    2. 使用solr实现搜索
    3. Solr集群搭建

缓存的添加

Redis 的单机版

安装步骤:

第一步:安装gcc编译环境

yum install gcc-c++

第二步:把redis的源码上传到linux服务器(cd /root)

第三步:解压缩(tar -zxvf redis-3.0.0.tar.gz)

88f4e23b9b489cce7c5d0c32227bec67.png

第四步:进入到解压后的redis文件夹中执行make指令

b824fca31c709ded3a1ada50b40673d9.png

(如果执行失败则可能是gcc环境没有弄好,删除相关文件夹之后重新安装配置)

第五步:指定安装目录:make install PREFIX=/usr/local/redis

1d01275abf1f7718838d67ae45070617.png

(如果执行目录是/usr/local,则默认安装到其对应bin目录下,进入/usr/local/bin目录查看信息。此处指定安装目录为/usr/local/redis)完成安装,(如果换路径,通过移动相关文件夹即可:“mv bin/ redis”)

2ad03a1a12a1640a682a13c76b1e7d95.png

67f54785bf331fe4987957e23928cd18.png

启动redis

两种启动方式,前端启动、后台启动。

前端启动:./redis-server(进入到redis安装目录,执行指令)

1b9292d133fa393a496183b1dca31019.png

后台启动:

  1. 复制redis.conf到redis的安装目录

进入redis解压包将redis.conf文件复制到redis安装目录

b17e9cf31c61c1880377a6f7dbf7842a.png

cp redis.conf /usr/local/redis/

  1. 修改redis.conf。修改daemonize yes

d4c9c4b407d531b375929d5e090a2b3f.png
  1. [root@bogon redis]# ./redis-server redis.conf

07971a9d787093ee308c84e477765af3.png

客户端

redis-cli -p 端口 -h ip地址 <-c>连接集群时使用此参数

默认端口:6379 Ip:localhost(127.0.0.1)

32b574fe9e99a2562ec837a295aa709d.png

RedisDesktopManager:

虚拟机配置,虚拟ip为192.168.187.128,如果连接失败,则需要查看出错原因,例如防火墙是否关闭?redis.conf配置文件“#bind 127.0.0.1是否已注释”、“是否配置requirepass密码”等,一一进行排除

dc18020eff7e9e575781de7707cfeeb1.png

a7c97addc87bbadcf6c62fa6a75da136.png

但此工具只能在单机版环境使用,不支持redis集群。

Redis集群

redis-cluster架构图

5562ca8daeac335c955f8d2bc8061efc.png

架构细节:

(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.

(2)节点的fail是通过集群中超过半数的节点检测失效时才生效.

(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可

(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->value

Redis 集群中内置了 16384 个哈希槽,当需要在 Redis 集群中放置一个 key-value 时,redis 先对 key 使用 crc16 算法算出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,redis 会根据节点数量大致均等的将哈希槽映射到不同的节点

集群搭建

集群中应该至少有三个节点,每个节点有一备份节点。需要6台服务器。

搭建伪分布式,需要6个redis实例。

搭建集群的步骤:

第一步:创建6个redis实例指定端口从7001到7006

4278872607bbfcf34bd1c72c2673d80d.png

修改redis名称,进入redis01文件夹下,如果存在dump.rdb文件(快照文件),删除该文件。

98faf4e976db3d5bcdc17fa3998aea53.png

第二步:修改redis.conf 配置文件,修改端口号,打开Cluster-enable yes前面的注释。

修改redis01的端口号为7001,设置cluster-enable为yes

以此类推,复制多个redis

6644612e12ca60df8c150c8f5db95f16.png

进入到每个redis文件夹中,修改相应的端口号:依次对应

redis01-7001、redis02-7002、redis03-7003、redis04-7004、redis05-7005、redis06-7006

6bce2a1fed018efb75cc8fefc7e141ef.png

第三步:需要一个ruby脚本。在redis源码文件夹下的src目录下。redis-trib.rb

第四步:把redis-trib.rb文件复制到到redis-cluster目录下。

c92551f3a715ef9b6fa659f7a8867bcb.png

第五步:执行ruby脚本之前,需要安装ruby环境。

1、yum install ruby

2、yum install rubygems

3、安装redis-trib.rb运行依赖的ruby的包(将相关包上传到服务器)

76d1145a344b5a458be27db7a7f85a24.png

[root@bogon ~]# gem install redis-3.0.0.gem

eed3f09c6e3b8617ff6b117557dd24ad.png

第六步:启动所有的redis实例

1ac6fe7bdecc434ca9e85eb472855464.png

d8686e45ec657949eab21c36ac75bcc8.png

第七步:使用redis-trib.rb创建集群(将对应ip修改为指定的服务器ip)

./redis-trib.rb create --replicas 1 192.168.187.128:7001 192.168.187.128:7002 192.168.187.128:7003 192.168.187.128:7004 192.168.187.128:7005 192.168.187.128:7006

1fe190b398bf67aa45301e32ab3fdede.png

ba3051d427e50e99581cfb5ffd7e4cfa.png

使用客户端连接集群: redis01/redis-cli -p 7001 -c

c76e49f7753cb6a501748721f96f03c2.png

如果没有-c后缀则无法跳转

如何使用redis的java客户端

需要使用Jedis连接redis服务器。

连接单机版

先把jedis依赖的jar包添加到工程中。在taotao-rest工程下的pom.xml中添加jedis依赖

5679a7e26201304dc817c1a7ecde4485.png
//单机版测试
	@Test
	public void testJedisSingle() throws Exception {
		//创建一个Jedis对象
		Jedis jedis = new Jedis("192.168.25.153", 6379);
		jedis.set("test", "hello jedis");
		String string = jedis.get("test");
		System.out.println(string);
		jedis.close();
	}

1bc17d4103284c7ec5636b5d3602385c.png

查看测试结果:

c01b1e8ba5e0a265a70634021f7bbf83.png

使用连接池:

//使用连接池
	@Test
	public void testJedisPool() throws Exception {
		//创建一个连接池对象
		//系统中应该是单例的。
		JedisPool jedisPool = new JedisPool("192.168.25.153", 6379);
		//从连接池中获得一个连接
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get("test");
		System.out.println(result);
		//jedis必须关闭
		jedis.close();
		
		//系统关闭时关闭连接池
		jedisPool.close();
		
	}

测试结果:

b3c9649b0248c1e0f1a6f60833851770.png

集群版使用Jedis

//连接redis集群
	@Test
 public void testJedisCluster() throws Exception {
		//创建一个JedisCluster对象
		Set<HostAndPort> nodes = new HashSet<>();
		nodes.add(new HostAndPort("192.168.25.153", 7001));
		nodes.add(new HostAndPort("192.168.25.153", 7002));
		nodes.add(new HostAndPort("192.168.25.153", 7003));
		nodes.add(new HostAndPort("192.168.25.153", 7004));
		nodes.add(new HostAndPort("192.168.25.153", 7005));
		nodes.add(new HostAndPort("192.168.25.153", 7006));
		//在nodes中指定每个节点的地址
		//jedisCluster在系统中是单例的。
		JedisCluster jedisCluster = new JedisCluster(nodes);
		jedisCluster.set("name", "zhangsan");
		jedisCluster.set("value", "100");
		String name = jedisCluster.get("name");
		String value = jedisCluster.get("value");
		System.out.println(name);
		System.out.println(value);
 
 
		//系统关闭时关闭jedisCluster
		jedisCluster.close();
	}

测试结果:

ebc91736874a4e2562a50d7d4068da57.png

项目中使用jedis

思路:创建一个redis操作的接口。分别创建两个实现类对应redis 的单机版和集群版。当使用单机版redis时,配置单机版的实现类,当使用集群版本的时候,配置集群版的实现类

接口:

package com.taotao.rest.component.impl;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
public class JedisClientSingle implements JedisClient{	
	@Autowired
	private JedisPool jedisPool;
	@Override
	public String set(String key, String value) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.set(key, value);
		jedis.close();
		return result;
	}
	@Override
	public String get(String key) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.get(key);
		jedis.close();
		return result;
	}
	@Override
	public Long hset(String key, String item, String value) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hset(key, item, value);
		jedis.close();
		return result;
	}
	@Override
	public String hget(String key, String item) {
		Jedis jedis = jedisPool.getResource();
		String result = jedis.hget(key, item);
		jedis.close();
		return result;
	}
	@Override
	public Long incr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.incr(key);
		jedis.close();
		return result;
	}
	@Override
	public Long decr(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.decr(key);
		jedis.close();
		return result;
	}
	@Override
	public Long expire(String key, int second) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.expire(key, second);
		jedis.close();
		return result;
	}
	@Override
	public Long ttl(String key) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.ttl(key);
		jedis.close();
		return result;
	}
@Override
	public Long hdel(String key, String item) {
		Jedis jedis = jedisPool.getResource();
		Long result = jedis.hdel(key, item);
		jedis.close();
		return result;
	}
}

集群版

package com.taotao.rest.component.impl;

import org.springframework.beans.factory.annotation.Autowired;
import com.taotao.rest.component.JedisClient;
import redis.clients.jedis.JedisCluster;

public class JedisClientCluster implements JedisClient {
	@Autowired
	private JedisCluster jedisCluster;
	@Override
	public String set(String key, String value) {
		return jedisCluster.set(key, value);
	}
	@Override
	public String get(String key) {
		return jedisCluster.get(key);
	}
	@Override
	public Long hset(String key, String item, String value) {
		return jedisCluster.hset(key, item, value);
	}
	@Override
	public String hget(String key, String item) {
		return jedisCluster.hget(key, item);
	}
	@Override
	public Long incr(String key) {
		return jedisCluster.incr(key);
	}
	@Override
	public Long decr(String key) {
		return jedisCluster.decr(key);
	}
	@Override
	public Long expire(String key, int second) {
		return jedisCluster.expire(key, second);
	}
	@Override
	public Long ttl(String key) {
		return jedisCluster.ttl(key);
	}
@Override
	public Long hdel(String key, String item) {
		return jedisCluster.hdel(key, item);
	}
}

Spring的配置(ip:192.168.187.128)

<?xml version="1.0" encoding="UTF-8"?>
<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.0.xsd
	http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
	http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">

	<!-- 包扫描器,扫描带@Service注解的类 -->
	<context:component-scan base-package="com.taotao.rest.service"></context:component-scan>
	<!-- 配置redis客户端单机版 -->
	<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
		<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
		<constructor-arg name="port" value="6379"></constructor-arg>
	</bean>
	<!-- 配置redis客户端实现类 -->
	<bean id="jedisClientSingle" class="com.taotao.rest.component.impl.JedisClientSingle"/>
 
	<!-- 配置redis客户端集群版 -->
	<!-- <bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
		<constructor-arg>
			<set>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7001"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7002"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7003"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7004"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7005"/>
				</bean>
				<bean class="redis.clients.jedis.HostAndPort">
					<constructor-arg name="host" value="192.168.25.153"/>
					<constructor-arg name="port" value="7006"/>
				</bean>
			</set>
		</constructor-arg>
	</bean>
	<bean id="jedisClientCluster" class="com.taotao.rest.component.impl.JedisClientCluster"/> -->
</beans>

测试:

@Test
 public void testJedisClientSpring() throws Exception {
		//创建一个spring容器
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-*.xml");
		//从容器中获得JedisClient对象
		JedisClient jedisClient = applicationContext.getBean(JedisClient.class);
		//jedisClient操作redis
		jedisClient.set("cliet1", "1000");
		String string = jedisClient.get("cliet1");
		System.out.println(string);
	}

测试结果:

a34861e3206a3a84cd815e32c89725be.png

在可视化工具中查看数据:

a22c1330733fc288ef1d470a57862559.png

集群版切换,修改applicationContext-service.xml文件,将单机版配置注释,打开集群注释,再次测试即可!

业务逻辑中添加缓存

注意:添加缓存时不要影响正常的业务逻辑。

ae1c6e8ca0deeddcd3d5eb2294e116e0.png

e48eb33bd34f738f13019742f8ba5f08.png
package com.taotao.rest.service.impl;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.taotao.common.utils.JsonUtils;
import com.taotao.mapper.TbContentMapper;
import com.taotao.pojo.TbContent;
import com.taotao.pojo.TbContentExample;
import com.taotao.pojo.TbContentExample.Criteria;
import com.taotao.rest.component.JedisClient;
import com.taotao.rest.service.ContentService;
@Service
public class ContentServiceImpl implements ContentService {
	@Autowired
	private JedisClient jedisClient;
	@Autowired
	private TbContentMapper contentMapper;	
	@Value("${REDIS_CONTENT_KEY}")
	private String REDIS_CONTENT_KEY;
	@Override
	public List<TbContent> getContentList(Long cid) {
		// 根据cid查询内容列表
		//查询数据库之前先查询缓存,如果有直接返回
		try {
			//从redis中取缓存数据
			String json = jedisClient.hget(REDIS_CONTENT_KEY, cid+"");
			if (!StringUtils.isBlank(json)) {
				//把json转换成List
				List<TbContent> list = JsonUtils.jsonToList(json, TbContent.class);
				return list;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		TbContentExample example = new TbContentExample();
		Criteria criteria = example.createCriteria();
		criteria.andCategoryIdEqualTo(cid);
		//执行查询
		List<TbContent> list = contentMapper.selectByExampleWithBLOBs(example);
		//返回结果之前,向缓存中添加数据
		try {
			//为了规范key可以使用hash
			//定义一个保存内容的key,hash中每个项就是cid
			//value是list,需要把list转换成json数据。
			jedisClient.hset(REDIS_CONTENT_KEY, cid+"", JsonUtils.objectToJson(list));
		} catch (Exception e) {
			e.printStackTrace();
		}
		return list;
	}
}

缓存同步

当后台修改内容信息后,只需要把redis中缓存的数据删除即可。

后台系统不直接操作redis数据库。

可以在taotao-rest中发布一个服务,当后台对内容信息修改后,调用服务即可。

服务的功能就是根据cid删除redis中缓存数据。

发布服务

Dao层

使用JedisClient。

Service层

接收cid,根据cid调用JedisClient删除redis中缓存的数据。返回结果TaotaoResult。

@Override
	public TaotaoResult syncContent(Long cid) {
		jedisClient.hdel(REDIS_CONTENT_KEY, cid + "");
		return TaotaoResult.ok();
	}

Controller层

发布服务,接收参数cid,返回结果TaotaoResult。

@RequestMapping("/sync/content/{cid}")
	@ResponseBody
	public TaotaoResult sysncContent(@PathVariable Long cid) {
		try {
			TaotaoResult result = contentService.syncContent(cid);
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
		}
	}

测试:http://localhost:8081/rest/sync/content/89

54cb341c4f3738c0a4a81658faf3461d.png

d5394c78a17b2630b7887ce5d4468afb.png

调用缓存同步

taotao-manager-web后台系统中,只要内容发生变化(增删改操作)均需要调用缓存同步的服务。

a2e09924264fc30a29a4d12a92c0fa58.png

80e3fa8e8e4f65dbb17786a305673747.png

此处是在controller获取配置文件的相关属性,因此可以在springmvc中扫描相关配置文件:(或者是在service层提供相应的get方法)

3217ce2f848c7d1215d9f6adc1f42712.png

f12af9cc690a13df93d08618ae1fa06f.png

启动taotao-rest、taotao-manager、taotao-portal进行测试(taotao-rest没开则导致redis数据访问失败)

测试:如果redis数据没有及时更新,则首页访问获取的数据是脏数据!

先将REDIS_CONTENT_KEY数据删除,随后访问taotao商城首页刷新数据,可以查看到REDIS_CONTENT_KEY数据添加到redis缓存中,随后在taotao后台管理系统中添加大广告图片,之后再查看redis的GUI工具可以看到REDIS_CONTENT_KEY被清楚,重新访问taotao首页则可看到REDIS_CONTENT_KEY又重新被加载到redis缓存!

商品类目展示添加缓存-待开发

结合上述内容,仿写代码。

taotao-rest修改

结合需求修改JedisClient接口,封装方法

6837a09156d43424ca6785776686d970.png

resource.properties:

837a155a30a173928a4263ac1f956bc4.png

商品类目Service接口:

public interface ItemCatService {
	// 获取分类列表
	public ItemCatResult getItemCatList();
	// 同步内容信息
	public TaotaoResult syncItemCat();
}

商品类目Service实现类:

package com.taotao.rest.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.taotao.common.result.TaotaoResult;
import com.taotao.common.utils.JsonUtils;
import com.taotao.mapper.TbItemCatMapper;
import com.taotao.pojo.TbItemCat;
import com.taotao.pojo.TbItemCatExample;
import com.taotao.pojo.TbItemCatExample.Criteria;
import com.taotao.rest.component.JedisClient;
import com.taotao.rest.pojo.CatNode;
import com.taotao.rest.pojo.ItemCatResult;
import com.taotao.rest.service.ItemCatService;

@Service
public class ItemCatServiceImpl implements ItemCatService {
	@Autowired
	private TbItemCatMapper itemCatMapper;
	
	@Autowired
	private JedisClient jedisClient;
	
	@Value("${REDIS_ITEM_CAT_KEY}")
	private String REDIS_ITEM_CAT_KEY;
	
	@Override
	public ItemCatResult getItemCatList() {
		//调用递归方法查询商品分类列表
		//查询数据库之前先查询缓存,如果有直接返回
		try {
			//从redis中取商品分类缓存数据
			String json = jedisClient.get(REDIS_ITEM_CAT_KEY);
			if (!StringUtils.isBlank(json)) {
				//把json转换成List
				List list = JsonUtils.jsonToList(json, CatNode.class);
				ItemCatResult result = new ItemCatResult();
				result.setData(list);
				return result;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		List catList = getItemCatList(0l);
		//返回结果之前,向缓存中添加数据
		try {
			//为了规范key可以使用hash
			//定义一个保存内容的key,hash中每个项就是cid
			//value是list,需要把list转换成json数据。
			jedisClient.set(REDIS_ITEM_CAT_KEY, JsonUtils.objectToJson(catList));
		} catch (Exception e) {
			e.printStackTrace();
		}
		ItemCatResult result = new ItemCatResult();
		result.setData(catList);
		return result;
	}
	
	private List getItemCatList(Long parentId) {
		//根据parentId查询列表
		TbItemCatExample example = new TbItemCatExample();
		Criteria criteria = example.createCriteria();
		criteria.andParentIdEqualTo(parentId);
		//执行查询
		List<TbItemCat> list = itemCatMapper.selectByExample(example);
		List resultList = new ArrayList<>();
		// 定义计数器
		int index = 0;
		for (TbItemCat tbItemCat : list) {
			// 判断index是否到达临界值
			if(index>=14) {
				break;
			}
			//如果是父节点
			if (tbItemCat.getIsParent()) {
				CatNode node = new CatNode();
				node.setUrl("/products/"+tbItemCat.getId()+".html");
				//如果当前节点为第一级节点
				if (tbItemCat.getParentId() == 0) {
					node.setName("<a href='/products/"+tbItemCat.getId()+".html'>"+tbItemCat.getName()+"</a>");
					// 第一级节点设置不能超过14
					index++;
				} else {
					node.setName(tbItemCat.getName());
				}
				node.setItems(getItemCatList(tbItemCat.getId()));
				//把node添加到列表
				resultList.add(node);
			} else {
				//如果是叶子节点
				String item = "/products/"+tbItemCat.getId()+".html|" + tbItemCat.getName();
				resultList.add(item);
			}
		}
		return resultList;
	}

	@Override
	public TaotaoResult syncItemCat() {
		jedisClient.del(REDIS_ITEM_CAT_KEY);
		return TaotaoResult.ok();
	}
}

Controller层修改:

package com.taotao.rest.controller;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.taotao.common.result.TaotaoResult;
import com.taotao.common.utils.ExceptionUtil;
import com.taotao.rest.pojo.ItemCatResult;
import com.taotao.rest.service.ItemCatService;


@Controller
@RequestMapping("/item/cat")
public class ItemCatController {
	
	@Autowired
	private ItemCatService itemCatService;

//	@RequestMapping(value="/list")
//	@ResponseBody
//	public ItemCatResult testGetItemCatList() {
//		return itemCatService.getItemCatList();
//	}
	
	// 方法1
//	@RequestMapping(value="/list",produces=MediaType.APPLICATION_JSON_VALUE+";charset=utf-8")
//	@ResponseBody
//	public String getItemCatList(String callback) {
//		ItemCatResult result = itemCatService.getItemCatList();
//		if (StringUtils.isBlank(callback)) {
//			//需要把result转换成字符串
//			String json = JsonUtils.objectToJson(result);
//			return json;
//		}
//		//如果字符串不为空,需要支持jsonp调用
//		//需要把result转换成字符串
//		String json = JsonUtils.objectToJson(result);
//		return callback + "(" + json + ");";
//	}
	
	// 方法2
	@RequestMapping(value="/list")
	@ResponseBody
	public Object getItemCatList(String callback) {
		ItemCatResult result = itemCatService.getItemCatList();
		if (StringUtils.isBlank(callback)) {
			//需要把result转换成字符串
			return result;
		}
		//如果字符串不为空,需要支持jsonp调用
		MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(result);
		mappingJacksonValue.setJsonpFunction(callback);
		return mappingJacksonValue;
	}
	
	@RequestMapping(value="/sync")
	@ResponseBody
	public TaotaoResult sysncItemCat() {
		try {
			TaotaoResult result = itemCatService.syncItemCat();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
		}
	}
}

测试同步:http://localhost:8081/rest/item/cat/sync

eaca47787e5cdb49f180b2b38f8a2948.png

Taotao-manager-web修改

此处还没有设计商品分类内容的调整,此处待定!

搜索系统搭建

搜索功能需要发布服务共pc端、移动端使用。根据关键词搜索,得到json格式的搜索结果。创建一个搜索系统,发布搜索服务。

系统架构:

1d317f8a38fb64d31538654df7813413.png
  • 创建一个搜索工程:taotao-search
  • 搭建solr服务

创建搜索系统

可以参考taotao-rest创建

0e73cca1c546133753d05551247da853.png

7825c5962cddef602c56725dab915859.png

使用的技术:

  • Mybatis
  • Spring
  • Springmvc(发布服务)
  • SolrJ(solr服务的客户端)

Pom文件参考

<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>
		<groupId>com.taotao</groupId>
		<artifactId>taotao-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<groupId>com.taotao</groupId>
	<artifactId>taotao-search</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<dependency>
			<groupId>com.taotao</groupId>
			<artifactId>taotao-manager-dao</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context-support</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<!-- solr客户端 -->
		<dependency>
			<groupId>org.apache.solr</groupId>
			<artifactId>solr-solrj</artifactId>
		</dependency>
	</dependencies>
	<!-- 添加tomcat插件 -->
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.tomcat.maven</groupId>
				<artifactId>tomcat7-maven-plugin</artifactId>
				<configuration>
					<port>8083</port>
					<path>/</path>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

POM文件:

<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>
		<groupId>com.taotao</groupId>
		<artifactId>taotao-parent</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>
	<groupId>com.taotao</groupId>
	<artifactId>taotao-search</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<dependency>
			<groupId>com.taotao</groupId>
			<artifactId>taotao-manager-mapper</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>
		<!-- Spring -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-beans</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aspects</artifactId>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>servlet-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<scope>provided</scope>
		</dependency>
		<!-- solr客户端 -->
		<dependency>
			<groupId>org.apache.solr</groupId>
			<artifactId>solr-solrj</artifactId>
		</dependency>
	</dependencies>
	<!-- 添加tomcat插件 -->
	<build>
	 	<pluginManagement>
			<plugins>
				<plugin>
					<groupId>org.apache.tomcat.maven</groupId>
					<artifactId>tomcat7-maven-plugin</artifactId>
					<configuration>
						<port>8083</port>
						<path>/</path>
					</configuration>
				</plugin>
				<plugin>  
	                <groupId>org.apache.maven.plugins</groupId>  
	                <artifactId>maven-war-plugin</artifactId>  
	                <configuration>  
	                    <failOnMissingWebXml>false</failOnMissingWebXml>  
	                </configuration>  
	            </plugin>
			</plugins>
		</pluginManagement>
	</build>
</project>  

框架整合

ssm整合:

f861d784a29afdb03210a1faebdf3d13.png

32424f126b0ca6403b7bb10f16dabc7f.png

9188e86ecd52cc239aff0ba5de4d7b9f.png

web.xml配置:

taotao-rest相关修改为相应的taotao-search对应的内容

问题分析:

fdd7be573757e6fd95d80f55684f5d8b.png

解决方法1:如果工程不是web项目则可在pom.xml中添加配置(配置failOnMissingWebXml)

<build>
  <plugins>
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.6</version>
    <configuration>
     <failOnMissingWebXml>false</failOnMissingWebXml>
    </configuration>
   </plugin>
  </plugins>
 </build>

f81f114a2fbe7dfce1472d1e1235cd11.png

解决方法2:针对web项目解决:在视图Project Explorer中操作,右击项目——>Java EE Tools——>Generate Deployment Descriptor Stub.然后系统会在src/main/webapp/WEB_INF文件加下创建web.xml文件。

e2f547027b4552167b5cd58195990bdc.png

Solr服务的搭建

需要在linux系统下搭建solr服务。

  • 需要安装tomcat
  • 安装jdk

CentOS单机版安装:

第一步:安装jdk、安装tomcat

在/root/soft下导入相关的压缩包,安装jdk、tomcat

Jdk安装

tar -zxvf jdk-7u55-linux-i586.tar.gz

43de8e38216d3b567aa45981954bed7a.png

将jdk复制到/usr/local/software目录下(手动创建)

977e69aaf52f93c3ef455b90cdb62ef8.png

通过vim /etc/profile指令修改配置(jdk配置:配置环境变量)

a0c9c8d90a5245820deb2a0ee04a01e5.png

保存配置,通过source /etc/profile指令使配置文件生效,通过java –version查看当前配置的jdk版本

e6b08d6f68b41aaa5f12dbacacecb8ed.png

配置完成,发现使用的是默认的jdk版本,而不是配置的jdk1.7,因此此处需要通过“which java”查看当前使用的是哪个路径的java,参考相关教程修正

6b300d2ac81e70ab813957994175c02b.png

(此处存在问题,待解决!)

6d2d71f732cf6d799839ca7a7ef6710d.png

参考链接:https://blog.csdn.net/meilin_ya/article/details/80650945

错误描述:安装好jdk之后,通过java -version,javac,java等命令测试是否安装成功时出现错误

-bash: /usr/java/jdk1.7.0_71/bin/java: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

错误原因:没有那个文件或目录,需要安装glibc

解决办法:终端输入sudo yum install glibc.i686命令,安装好glibc,问题解决

cce4b3f21bbee71116fd4650b923b056.png

Tomcat安装

tar -zxf apache-tomcat-7.0.47.tar.gz

在/usr/local/目录下创建solr目录,将tomcat复制到该目录

8338778bcf3e36f85a38570225c3ffef.png

第二步:解压solr压缩包

838cac375de7f5e4414fd009770dcb88.png

第三步:把dist/solr-4.10.3.war部署到tomcat下

将解压的安装目录下的dist/solr-4.10.3.war服务器部署到tomcat下

fcc1b90d6ae743ddeb1565e983c96551.png

f02b413fe2363c4d8c085ce73df1fc93.png

第四步:解压缩war包,启动tomcat解压

进入到/usr/local/solr/tomcat/bin目录执行“./startup.sh”指令

6397bc2d1d573c72fb96f9a451ec6a04.png

解压完成,可以将原来的solr.war包删除

7abae3496ca18bc206841e0e4340d433.png

第五步:需要把/root/soft/solr-4.10.3/example/lib/ext目录下的所有的jar包添加到solr工程中

e07d952c5e3a6af59ed88bdee33efb3c.png

第六步:创建solrhome。把/root/solr-4.10.3/example/solr文件夹复制一份作为solrhome。复制到:/usr/local/solr/solrhome

9790bc7d53141413dc1e0e7c1d47abb5.png

第七步:告诉solr服务solrhome的位置。需要修改web.xml

333c105101e63e0af9d762eb08f1e7bd.png

修改/usr/local/solr/tomcat/webapps/solr/WEB-INF下的web.xml文件

ff0b1737b16a43b1e9c8b38f308f2b3a.png

e9efc9a4fbe327482c268d2afc7daee4.png

第八步:启动tomcat

启动tomcat进行测试

f46ebe4775af11ad779e1893c7afcd8d.png

出现外网无法访问的情况,尝试关闭防火墙进行测试

systemctl status firewalld

systemctl stop firewalld

1b5491b44801de35ebf5f55e2d69201c.png

http://192.168.187.129:8080

b6bcd4d459886ff23e6248dbef457ba0.png

http://192.168.187.129:8080/solr/#/

bff2f96a9fdf405d8aab93922bfa8a49.png

配置中文分析器、自定义业务域

分析器使用IKAnalyzer。

使用方法:

第一步:把IKAnalyzer依赖的jar包添加到solr工程中。把分析器使用的扩展词典添加到classpath中

把IKAnalyzer依赖的jar包添加到solr工程中

11703f5a445776fefccf49fd3b3f9a27.png

把分析器使用的扩展词典添加到classpath中,如果对应路径没有classes文件夹则需要手动创建,随后再将相关数据复制到该文件夹

(/usr/local/solr/tomcat/webapps/solr/WEB-INF/classes)

c383ac15c304792adbd1c7725f741e43.png

6e83a6254901f0eb00c36dce0ea8a949.png

查看复制的数据信息

7c059c9b61e32790b5c95089775f161d.png

第二步:需要自定义一个FieldType。Schema.xml中定义。可以在FieldType中指定中文分析器。

修改指定路径的文件,在末尾添加如下信息

e3604e8ad0d5b35300720e978c32aee4.png

<fieldType name="text_ik" class="solr.TextField">

<analyzer class="org.wltea.analyzer.lucene.IKAnalyzer"/>

</fieldType>

6124fd63e02b11c4e9ffffdc124da5ff.png

第三步:自定义域,指定域的类型为自定义的FieldType。

Sql语句:指定关键字查询之类的

SELECT
	a.id,
	a.title,
	a.sell_point,
	a.price,
	a.image,
	b.name category_name,
	c.item_desc
FROM
	tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
WHERE
	a.status = 1

将下面的数据添加到schema.xml文件下方

<field name="item_title" type="text_ik" indexed="true" stored="true"/>
<field name="item_sell_point" type="text_ik" indexed="true" stored="true"/>
<field name="item_price"  type="long" indexed="true" stored="true"/>
<field name="item_image" type="string" indexed="false" stored="true" />
<field name="item_category_name" type="string" indexed="true" stored="true" />
<field name="item_desc" type="text_ik" indexed="true" stored="false" />

<field name="item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true"/>
<copyField source="item_title" dest="item_keywords"/>
<copyField source="item_sell_point" dest="item_keywords"/>
<copyField source="item_category_name" dest="item_keywords"/>
<copyField source="item_desc" dest="item_keywords"/>

ad015fcfae9f18b7da71c1f2dcfd8e96.png

第四步:重新启动tomcat进行测试

如果发现数据没有加载成功,需要确认配置文件schema.xml(如果是直接通过word复制的数据可能会出现和linux系统中空格字符格式不匹配的情况,此处需要注意),其次先关闭tomcat再次重启测试

d94642e5e3515977f9cb0e77dd025a89.png

索引库中导入数据

Solrj的使用

public class SolrJTest {

	@Test
	public void testSolrJ() throws Exception {
		//创建连接
		SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr");
		//创建一个文档对象
		SolrInputDocument document = new SolrInputDocument();
		//添加域
		document.addField("id", "solrtest01");
		document.addField("item_title", "测试商品");
		document.addField("item_sell_point", "卖点");
		//添加到索引库
		solrServer.add(document);
		//提交
		solrServer.commit();
	}
	
	@Test
	public void testQuery() throws Exception {
		//创建连接
		SolrServer solrServer = new HttpSolrServer("http://192.168.25.154:8080/solr");
		//创建一个查询对象
		SolrQuery query = new SolrQuery();
		query.setQuery("*:*");
		//执行查询
		QueryResponse response = solrServer.query(query);
		//取查询结果
		SolrDocumentList solrDocumentList = response.getResults();
		for (SolrDocument solrDocument : solrDocumentList) {
			System.out.println(solrDocument.get("id"));
			System.out.println(solrDocument.get("item_title"));
			System.out.println(solrDocument.get("item_sell_point"));
		}
	}
}

测试分析:

执行testSolrJ方法,随后在网页中访问solr主工程测试是否成功

d893a6c041d52ad7424feae7cd6a1ea2.png

f579ac0a726f9b27274110ba7375ace7.png

执行testQuery方法,显示查找的数据信息

a31dd580141727e2a905704aefe1091e.png

使用后台管理系统删除数据库:

72019501535bcae3a77e853217deb284.png

再次点击查找,数据被清空

06086226ca8b6dce0b16c1cae0e1d954.png

导入数据

分析

从数据库中根据sql语句查询数据,遍历数据创建文档对象,把文档对象写入索引库。

Dao层

Sql语句:

SELECT
	a.id,
	a.title,
	a.sell_point,
	a.price,
	a.image,
	b.`name` category_name,
	c.item_desc
FROM
	tb_item a
LEFT JOIN tb_item_cat b ON a.cid = b.id
LEFT JOIN tb_item_desc c ON a.id = c.item_id
WHERE
	a.`status` = 1

需要创建一个mapper文件。

在taotao-search中创建相关的pojo、mapper、service、controller包

创建SearchItem接收查找结果:

6826d8627a37831717251d6c81d1a5eb.png

Dao层接口:

package com.taotao.search.mapper;
import java.util.List;
import com.taotao.search.pojo.SearchItem;
public interface ItemMapper {
	List<SearchItem> getItemList();
}

Mapper文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.taotao.search.mapper.ItemMapper" >
	<select id="getItemList" resultType="com.taotao.search.pojo.SearchItem">
		SELECT
			a.id,
			a.title,
			a.sell_point,
			a.price,
			a.image,
			b.`name` category_name,
			c.item_desc
		FROM
			tb_item a
		LEFT JOIN tb_item_cat b ON a.cid = b.id
		LEFT JOIN tb_item_desc c ON a.id = c.item_id
		WHERE
			a.`status` = 1
	</select>
</mapper>

a93eb72c96a582b935286c50adc53637.png

1c9bada9a4f593ef526a150753a1f6b0.png

Service层

取商品列表,遍历列表,创建文档对象,把文档对象写入索引库。

要操作索引库需要SolrServer对象,可以把SolrServer放到spring容器中,注入到Service。

485a0ff8289dcc9805954cad480a038d.png
public interface ItemService {
	public TaotaoResult importItems() throws Exception;
}
ServiceImpl:
package com.taotao.search.service.impl;
import java.util.List;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.common.SolrInputDocument;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.taotao.common.result.TaotaoResult;
import com.taotao.search.mapper.ItemMapper;
import com.taotao.search.pojo.SearchItem;
import com.taotao.search.service.ItemService;
@Service
public class ItemServiceImpl implements ItemService {
	@Autowired
	private SolrServer solrServer;
	@Autowired
	private ItemMapper itemMapper;
	@Override
	public TaotaoResult importItems() throws Exception {
		//查询数据库获得商品列表
		List<SearchItem> itemList = itemMapper.getItemList();
		//遍历列表
		for (SearchItem item : itemList) {
			//创建文档对象
			SolrInputDocument document = new SolrInputDocument();
			//添加域
			document.addField("id", item.getId());
			document.addField("item_title", item.getTitle());
			document.addField("item_sell_point", item.getSell_point());
			document.addField("item_price", item.getPrice());
			document.addField("item_image", item.getImage());
			document.addField("item_category_name", item.getCategory_name());
			document.addField("item_desc", item.getItem_desc());
			//写入索引库
			solrServer.add(document);
		}
		//提交
		solrServer.commit();
		return TaotaoResult.ok();
	}
}

Controller层

请求一个url,返回TaotaoResult。

package com.taotao.search.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import com.taotao.common.result.TaotaoResult;
import com.taotao.common.utils.ExceptionUtil;
import com.taotao.search.service.ItemService;
@Controller
public class ItemController {
	@Autowired
	private ItemService itemService;
	
	@RequestMapping("/importall")
	@ResponseBody
	public TaotaoResult importAll() {
		try {
			TaotaoResult result = itemService.importItems();
			return result;
		} catch (Exception e) {
			e.printStackTrace();
			return TaotaoResult.build(500, ExceptionUtil.getStackTrace(e));
		}
	}
}

可能出现的问题:

5666f253dfa7b722de339a7aecdff57b.png

原因分析:相关的资源文件没有找到,导致数据绑定失败,需要在pom.xml中添加相关的配置。Pom.xml文件修改如下:

<resources>
			<resource>
				<directory>src/main/java</directory>
				<includes>
					<include>**/*.properties</include>
					<include>**/*.xml</include>
				</includes>
				<filtering>false</filtering>
			</resource>
			<resource>
				<directory>src/main/resources</directory>
				<includes>
					<include>**/*.properties</include>
					<include>**/*.xml</include>
				</includes>
				<filtering>false</filtering>
			</resource>
		</resources>

7f4912d64eef77de45d119acba3ab0ec.png

测试,访问数据:http://localhost:8083/search/importall

623e6f40903094acc174babe7a7ddb47.png

在solr后台查看数据:

63cec3814680b16ab0ac443e9d4f9b9d.png
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值