安装
Redis是c语言开发的。
安装redis需要c语言的编译环境。如果没有gcc需要在线安装。yum install gcc-c++
安装步骤:
第一步:redis的源码包上传到linux系统。
第二步:解压缩redis。
第三步:编译。进入redis源码目录。make
第四步:安装。make install PREFIX=/usr/local/redis
PREFIX参数指定redis的安装目录。一般软件安装到/usr目录下
启动
前端启动:在redis的安装目录下直接启动redis-server
[root@localhost bin]# ./redis-server
后台启动:
把解压目录中的/root/redis-3.0.0/redis.conf复制到安装目录中/usr/local/redis/bin目录下
[root@localhost redis-3.0.0]# cp redis.conf /usr/local/redis/bin/
修改配置文件:
[root@localhost bin]# ./redis-server redis.conf
daemon yes #守护进程改为是
查看redis进程:
[root@localhost bin]# ps aux|grep redis
root 5190 0.1 0.3 33936 1712 ? Ssl 18:23 0:00 ./redis-server *:6379
root 5196 0.0 0.1 4356 728 pts/0 S+ 18:24 0:00 grep redis
关闭
[root@localhost bin]# kill 5190 #正常关闭
[root@localhost bin]# kill -9 5190 #强制关闭
[root@localhost bin]# ./redis-cli shutdown
连接
首先redis.conf文件中设置指定的ip和端口,更改后服务要重启
redis-cli -h 主机 -p 端口
[root@localhost bin]# ./redis-cli -h 192.168.25.3 -p 6379
常用命令
String:key-value(做缓存)
Redis中所有的数据都是字符串。命令不区分大小写,key是区分大小写的。Redis是单线程的。Redis中不适合保存内容大的数据。
get、set、incr:加一(生成id)、Decr:减一
设置键值对: set key value
获取指定键的值: get key
查看所有key: keys *
删除键:del key
指定key的value加一: incr key
指定key的value减一: decr key
Hash :key-fields-values(做缓存)
相当于一个key对于一个map,map中还有key-value。使用hash对key进行归类。
Hset:向hash中添加内容
Hget:从hash中取内容
设置key的fields和values: hset key field value
获取指定key的field的值: hget key field
获取指定key的所有filed字段: hkeys key
获取指定key的所有值: hvals key
删除指定key的指定field字段: hdel key field
获取key的所有fiel和value信息: hgetall key
List:有顺序可重复
从左边添加:lpush key value [value..]
从右边添加:rpush key value [value..]
获取所有list元素: lrange key start end
从左边取出(取出list集合中就不存在了):lpop key
从右边取出(取出list集合中依然不存在): rpop key
Set:元素无顺序,不能重复
在集合中添加元素(自动过滤重复元素): sadd key value[value..]
移除集合中的元素: srem key value[value..]
查看Set中的值:smembers key
第一个set和第二个set做比较: sdiff key1 key2
交集: sinter key1 key2
并集: sunion key1 key2
SortedSet(zset):有顺序,不能重复(消耗性能最高,能用list替代就用list)
按顺序给Set添加元素: zadd zset1 2 a 5 b 1 c 6 d
查看指定key中的所有元素: zrange zset1 0 -1
移除指定key中的指定元素: zrem zset1 a
查询的排序反转: zrange zset1 0 -1
连同排序号一起查出来: zrange zset1 0 -1 withscores
设置key的过期时间
expire key second:设置key的过期时间,一旦过期就清除
ttl key:查看key的有效期
persist key:清除key的过期时间。Key持久化。
持久化方案
Redis的所有数据都是保存到内存中的。
Rdb:快照形式,定期把内存中当前时刻的数据保存到磁盘。Redis默认支持的持久化方案。
aof形式:append only file。把所有对redis数据库操作的命令,增删改操作的命令。保存到文件中。数据库恢复时把所有的命令执行一遍即可。
Redis分布式存储的常见方案
主从
从服务器连接主服务器,发送SYNC命令; 主服务器接收到SYNC命名后,开始执行BGSAVE命令生成RDB文件并使用缓冲区记录此后执行的所有写命令; 主服务器BGSAVE执行完后,向所有从服务器发送快照文件,并在发送期间继续记录被执行的写命令; 从服务器收到快照文件后丢弃所有旧数据,载入收到的快照; 主服务器快照发送完毕后开始向从服务器发送缓冲区中的写命令; 从服务器完成对快照的载入,开始接收命令请求,并执行来自主服务器缓冲区的写命令;(从服务器初始化完成)主服务器每执行一个写命令就会向从服务器发送相同的写命令,从服务器接收并执行收到的写命令(从服务器初始化完成后的操作)
优点:
支持主从复制,主机会自动将数据同步到从机,可以进行读写分离为了分载Master的读操作压力,Slave服务器可以为客户端提供只读操作的服务,写服务仍然必须由Master来完成Slave同样可以接受其它Slaves的连接和同步请求,这样可以有效的分载Master的同步压力。
缺点:
Redis不具备自动容错和恢复功能,主机从机的宕机都会导致前端部分读写请求失败,需要等待机器重启或者手动切换前端的IP才能恢复。主机宕机,宕机前有部分数据未能及时同步到从机,切换IP后还会引入数据不一致的问题,降低了系统的可用性。Redis较难支持在线扩容,在集群容量达到上限时在线扩容会变得很复杂。
Cluster(集群)
redis3.0上加入了cluster模式,实现的redis的分布式存储,每台redis节点上存储不同的内容。
所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽。
节点的fail是通过集群中超过半数的节点检测失效时才生效。
客户端与redis节点直连,不需要中间代理层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
工作方式:
在redis的每一个节点上,都有这么两个东西,一个是插槽(slot),它的的取值范围是:0-16383。还有一个就是cluster,可以理解为是一个集群管理的插件。当我们的存取的key到达的时候,redis会根据crc16的算法得出一个结果,然后把结果对 16384 求余数,这样每个 key 都会对应一个编号在 0-16383 之间的哈希槽,通过这个值,去找到对应的插槽所对应的节点,然后直接自动跳转到这个对应的节点上进行存取操作。
为了保证高可用,redis-cluster集群引入了主从模式,一个主节点对应一个或者多个从节点,当主节点宕机的时候,就会启用从节点。当其它主节点ping一个主节点A时,如果半数以上的主节点与A通信超时,那么认为主节点A宕机了。如果主节点A和它的从节点A1都宕机了,那么该集群就无法再提供服务了。
集群
当redis中的数据是存放在内存中的。当内存存满时会存放在虚拟内存中(虚拟内存效率很低),为了解决这个问题要使用集群,redis每个节点存放的数据是不一样的。
架构细节:
(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 会根据节点数量大致均等的将哈希槽映射到不同的节点
Redis集群的搭建
Redis集群中至少应该有三个节点。要保证集群的高可用,需要每个节点有一个备份机。
Redis集群至少需要6台服务器。
搭建伪分布式。可以使用一台虚拟机运行6个redis实例。需要修改redis的端口号7001-7006
集群搭建环境
1、使用ruby脚本搭建集群。需要ruby的运行环境。
安装ruby
yum install ruby
yum install rubygems
2、将redis-3.0.0.gem包上传到服务器,安装ruby脚本运行使用的包。
[root@localhost ~]# gem install redis-3.0.0.gem
Successfully installed redis-3.0.0
1 gem installed
Installing ri documentation for redis-3.0.0...
Installing RDoc documentation for redis-3.0.0...
3、从解压包中src下的redis-trib.rb复制到集群目录下
[root@localhost ~]# cd redis-3.0.0/src
[root@localhost src]# ll *.rb
-rwxrwxr-x. 1 root root 48141 Apr 1 2015 redis-trib.rb
[root@localhost src]# cp redis-trib.rb ../../java/redis-cluster/
搭建步骤
需要6台redis服务器。搭建伪分布式。
需要6个redis实例。
需要运行在不同的端口7001-7006
第一步:创建6个redis实例,每个实例运行在不同的端口。需要修改redis.conf配置文件。配置文件中还需要把cluster-enabled yes前的注释去掉。
第二步:启动每个redis实例。
启动redis-cluster文件夹下的六个redis [root@localhost redis-cluster]# vim start-cluster.sh cd redis0 ./redis-server redis.conf cd .. cd redis1 ./redis-server redis.conf cd .. cd redis2 ./redis-server redis.conf cd .. cd redis3 ./redis-server redis.conf cd .. cd redis4 ./redis-server redis.conf cd .. cd redis5 ./redis-server redis.conf cd .. [root@localhost redis-cluster]# chmod u+x start-cluster.sh 创建关闭集群的脚本: [root@localhost redis-cluster]# vi shutdow-all.sh redis0/redis-cli -p 7000 shutdown redis1/redis-cli -p 7001 shutdown redis2/redis-cli -p 7002 shutdown redis3/redis-cli -p 7003 shutdown redis4/redis-cli -p 7004 shutdown redis5/redis-cli -p 7005 shutdown [root@localhost redis-cluster]# chmod u+x shutdow-all.sh
第三步:使用ruby脚本搭建集群。
./redis-trib.rb create --replicas 1 192.168.25.3:7000 192.168.25.3:7001 192.168.25.3:7002 192.168.25.3:7003 192.168.25.3:7004 192.168.25.3:7005Jedis
需要把jedis依赖的jar包添加到工程中。Maven工程中需要把jedis的坐标添加到依赖。
连接单机版
第一步:创建一个Jedis对象。需要指定服务端的ip及端口。
第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。
第三步:打印结果。
第四步:关闭Jedis。
@Test public void testJedis() throws Exception { // 第一步:创建一个Jedis对象。需要指定服务端的ip及端口。 Jedis jedis = new Jedis("192.168.25.3", 6379); // 第二步:使用Jedis对象操作数据库,每个redis命令对应一个方法。 String result = jedis.get("hello"); // 第三步:打印结果。 System.out.println(result); // 第四步:关闭Jedis jedis.close(); }
连接单机版使用连接池
第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。
第二步:从JedisPool中获得Jedis对象。
第三步:使用Jedis操作redis服务器。
第四步:操作完毕后关闭jedis对象,连接池回收资源。
第五步:关闭JedisPool对象。
@Test public void testJedisPool() throws Exception { // 第一步:创建一个JedisPool对象。需要指定服务端的ip及端口。 JedisPool jedisPool = new JedisPool("192.168.25.153", 6379); // 第二步:从JedisPool中获得Jedis对象。 Jedis jedis = jedisPool.getResource(); // 第三步:使用Jedis操作redis服务器。 jedis.set("jedis", "test"); String result = jedis.get("jedis"); System.out.println(result); // 第四步:操作完毕后关闭jedis对象,连接池回收资源。 jedis.close(); // 第五步:关闭JedisPool对象。 jedisPool.close(); }
连接集群
第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。
第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。
第三步:打印结果
第四步:系统关闭前,关闭JedisCluster对象。
@Test public void testJedisCluster() throws Exception { // 第一步:使用JedisCluster对象。需要一个Set<HostAndPort>参数。Redis节点的列表。 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)); JedisCluster jedisCluster = new JedisCluster(nodes); // 第二步:直接使用JedisCluster对象操作redis。在系统中单例存在。 jedisCluster.set("hello", "100"); String result = jedisCluster.get("hello"); // 第三步:打印结果 System.out.println(result); // 第四步:系统关闭前,关闭JedisCluster对象。 jedisCluster.close(); }向业务逻辑中添加缓存
接口封装
常用的操作redis的方法提取出一个接口,分别对应单机版和集群版创建两个实现类。
/*接口定义*/ public interface JedisClient { String set(String key, String value); String get(String key); Boolean exists(String key); Long expire(String key, int seconds); Long ttl(String key); Long incr(String key); Long hset(String key, String field, String value); String hget(String key, String field); Long hdel(String key, String... field); } /*单机版实现类*/ public class JedisClientPool 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 Boolean exists(String key) { Jedis jedis = jedisPool.getResource(); Boolean result = jedis.exists(key); jedis.close(); return result; } @Override public Long expire(String key, int seconds) { Jedis jedis = jedisPool.getResource(); Long result = jedis.expire(key, seconds); 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 incr(String key) { Jedis jedis = jedisPool.getResource(); Long result = jedis.incr(key); jedis.close(); return result; } @Override public Long hset(String key, String field, String value) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hset(key, field, value); jedis.close(); return result; } @Override public String hget(String key, String field) { Jedis jedis = jedisPool.getResource(); String result = jedis.hget(key, field); jedis.close(); return result; } @Override public Long hdel(String key, String... field) { Jedis jedis = jedisPool.getResource(); Long result = jedis.hdel(key, field); jedis.close(); return result; } } <!-- 配置单机版的连接 --> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="6379"></constructor-arg> </bean> <bean class="com.e3mall.common.jedis.JedisClientPool"> <property name="jedisPool" ref="jedisPool"></property> </bean> /*集群版实现类*/ package cn.e3mall.jedis; import org.springframework.beans.factory.annotation.Autowired; 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 Boolean exists(String key) { return jedisCluster.exists(key); } @Override public Long expire(String key, int seconds) { return jedisCluster.expire(key, seconds); } @Override public Long ttl(String key) { return jedisCluster.ttl(key); } @Override public Long incr(String key) { return jedisCluster.incr(key); } @Override public Long hset(String key, String field, String value) { return jedisCluster.hset(key, field, value); } @Override public String hget(String key, String field) { return jedisCluster.hget(key, field); } @Override public Long hdel(String key, String... field) { return jedisCluster.hdel(key, field); } } <!-- 集群版的配置 --> <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.3"></constructor-arg> <constructor-arg name="port" value="7001"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="7002"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="7003"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="7004"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="7005"></constructor-arg> </bean> <bean class="redis.clients.jedis.HostAndPort"> <constructor-arg name="host" value="192.168.25.3"></constructor-arg> <constructor-arg name="port" value="7006"></constructor-arg> </bean> </set> </constructor-arg> </bean> <bean id="jedisClientCluster" class="com.e3mall.common.jedis.JedisClientCluster"> <property name="jedisCluster" ref="jedisCluster"></property> </bean>
注意:单机版和集群版不能共存,使用单机版时注释集群版的配置。使用集群版,把单机版注释。
封装代码测试
@Test public void testJedisClient() throws Exception { //初始化Spring容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring/applicationContext-redis.xml"); //从容器中获得JedisClient对象 JedisClient jedisClient = applicationContext.getBean(JedisClient.class); jedisClient.set("first", "100"); String result = jedisClient.get("first"); System.out.println(result); }
添加缓存
功能分析
查询内容列表时添加缓存。
1、查询数据库之前先查询缓存。
2、查询到结果,直接响应结果。
3、查询不到,缓存中没有需要查询数据库。
4、把查询结果添加到缓存中。
5、返回结果。
向redis中添加缓存:
Key:cid
Value:内容列表。需要把java对象转换成json。
使用hash对key进行归类。
HASH_KEY:HASH
|--KEY:VALUE
|--KEY:VALUE
|--KEY:VALUE
|--KEY:VALUE
注意:添加缓存不能影响正常业务逻辑。
String key = this.getClass().getName() +"."+ Thread.currentThread().getStackTrace()[1].getMethodName(); //包名.类名.方法名 String field = "sys_information_type:"+sys_information_type+",id:"+id; //方法参数
if (jedisClient.hget(key, field)!=null) { //判断redis中是否存在 System.out.println("从redis中取"); //向客户端输出 resp.reset(); resp.setContentType("text/plain; charset=UTF-8"); resp.setCharacterEncoding("utf-8"); resp.getOutputStream().write(jedisClient.hget(key, field).getBytes("utf-8")); resp.getOutputStream().flush(); return null; }
//将数据库中查找的数据转换为json格式存储到redis中 JSONObject jsonObject=new JSONObject(); 从数据库中查找数据... //将查询到的数据存储在redis中 jedisClient.hset(key, field, jsonObject.toString()); jedisClient.expire(key, 60); //经常更新的数据可以设置过期时间,单位秒
返回数据...
缓存同步
对内容信息做增删改操作后只需要把对应缓存删除即可。不用把整个hash删掉,删掉hash指定的field即可。
jedisClient.expire(key, 0); //将指定key设过期,string,hash都可以
--------------------------------20180725分割线--------------------
spring+jackson环境
封装类RedisTool.java类
public class RedisTool { @Autowired JedisClient jedisClient; public JedisClient getJedisClient() { return jedisClient; } public void setJedisClient(JedisClient jedisClient) { this.jedisClient = jedisClient; } public RedisTool() { } /** * 查询是否存在 * @Pa * @param paramString 参数拼接成的字符串 * @return 存在返回json字符串,不存在返回none * @throws UnsupportedEncodingException */ public String getReids(String key,String paramString){ //String key = this.getClass().getName() +"."+ Thread.currentThread().getStackTrace()[1].getMethodName(); //包名.类名.方法名 String field = paramString; //方法参数 if (jedisClient.hget(key, field)!=null) { //判断redis中是否存在 String result = jedisClient.hget(key, field); System.out.println("从redis中取"+result); //向客户端输出 return result; } return "none"; } /** * 向redis中添加 * @param paramString 参数拼接成的字符串 * @param jsonObject 结果- 缓存到redis中的字符串 * @throws UnsupportedEncodingException */ public void setReids(String key,String paramString,String jsonObject) throws UnsupportedEncodingException { String field = paramString; //String key = this.getClass().getName() +"."+ Thread.currentThread().getStackTrace()[1].getMethodName(); //包名.类名.方法名 jedisClient.hset(key, field, jsonObject); jedisClient.expire(key, 60); //经常更新的数据可以设置过期时间,单位秒 } }
Controller层
@RequestMapping("/getAllBook") @ResponseBody public Page getAllBook(String pageNo,String pageSize,String cateCode_search,String money_search_start,String money_search_end,String time_search_start,String time_search_end,String remark_search,HttpServletRequest request,HttpServletResponse response) throws IOException { pageNo=pageNo==null?"1":pageNo; //当前页码 pageSize=pageSize==null?"5":pageSize; //页面大小 /*从redis中读取*/ //key参数 String redis_key = this.getClass().getName() +"."+ Thread.currentThread().getStackTrace()[1].getMethodName(); //包名.类名.方法名 String userId = ((Users)request.getSession().getAttribute("user")).getId().toString(); //field参数 String paramString = "userId"+userId+"pageNo:"+pageNo+"pageSize"+pageSize+"cateCode_search"+cateCode_search+"money_search_start"+money_search_start+"money_search_end"+money_search_end+"time_search_start"+time_search_start+"time_search_end"+time_search_end+"remark_search"+remark_search; if(redisTool.getReids(redis_key,paramString)!="none"){ //读取 Page page = null; try { page = new ObjectMapper().readValue(redisTool.getReids(redis_key,paramString),Page.class); } catch (JsonProcessingException e) { e.printStackTrace(); } return page; } // //获取当前页数据 List<AccountExt> list = bookService.getAllBookByPage(userId,cateCode_search,money_search_start,money_search_end,time_search_start,time_search_end,remark_search,pageNo,pageSize); //获取总数据大小 int totals = bookService.getAllBookCount(userId,cateCode_search,money_search_start,money_search_end,time_search_start,time_search_end,remark_search); //封装返回结果 Page page = new Page(); page.setTotal(totals+""); page.setRows(list); /*存入redis中*/ String jsonResult = new ObjectMapper().writeValueAsString(page); redisTool.setReids(redis_key,paramString,jsonResult); return page; }
applicationContext-redis.xml
<?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.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"> <bean class="org.apache.commons.pool2.impl.GenericObjectPoolConfig" id="poolConfig"> </bean> <!--redis配置--> <bean id="jedisPool" class="redis.clients.jedis.JedisPool"> <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg> <constructor-arg name="host" value="www.52zt.online"></constructor-arg> <constructor-arg name="port" value="6379"></constructor-arg> <constructor-arg name="timeout" value="5000"></constructor-arg> <constructor-arg name="password" value="19950926"></constructor-arg> </bean> <bean class="com.autumn.redis.JedisClientPool" id="jedisClientPool"> <property name="jedisPool" ref="jedisPool"></property> </bean> <bean class="com.autumn.redis.RedisTool"> <property name="jedisClient" ref="jedisClientPool"></property> </bean> </beans>
web.xml
<!-- 加载spring容器 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/applicationContext-*.xml</param-value> </context-param>
参考:
https://www.cnblogs.com/zoro-zero/p/13474733.html