发表于: 2020-07-05 23:31:59
1 1507
今天完成的事情:
昨天问了师兄,我线程数设太多了。。只用设20个循环50次就行。今天重新测一下:
不开memcached,tomcat:
平均139毫秒,吞吐量131
/usr/bin/memcached -d -m 10 -u root -p 11211 -c 256
打开memcached,tomcat:
平均15毫秒,吞吐量321
不开memcached,resin:
平均110毫秒,吞吐量154
打开memcached,resin:
平均6毫秒,吞吐量858
使用nginx负载均衡:
平均25毫秒,吞吐量471
了解了一下memcached的内存回收机制。
memcache不会释放内存,而是重新利用。
在缓存的清除方面,memcache是不释放已分配内存。当已分配的内存所在的记录失效后,这段以往的内存空间,memcache只会重复利用。
memcached的内存回收机制不是说你设置的key到了生命周期就自动从内存中清除的,这个时候必须有一个新的对象入驻请求这个大小的chunk或者 这个过期的对象被get的时候才会清除。
那当所有给memcache的内存都被占用了,这个时候,memcache有两个设置,要么报错,要么,就是用 LRU方法,把last recently used的数据清除出去,也就是删除近段时间最少使用的同规格chunk。
这个时候就会引发应外一个问题,就是当你chunk大小设置不合理的时候,比如slab20 chunk大小非常大,一开始占用了很多内存,但是之后不论是否过期,不被再次利用到的时候就一直处于内存中,这样,当比较小的slab1中的chunk 满了,也没有内存新建slab并分割和slab1同样规格的chunk的时候,memcached就要启动LRU,来清理这个slab下的数据。那这种情 况就会造成在内存的极大程度浪费和cache命中率下降,是某些关键性的数据总是在内存中进进出出,得不到持久的保存。
这个时候我们可以调整factor参数,让chunk的大小达到我们的需要,这个需要根据业务来做。
那slab数量,growth factor大小,LRU频率以及空间浪费情况可以估计出来如下结果:
growth factor↑ slab数量 ↓ LRU频率↓ 空降浪费↑
growth factor↓ slab数量 ↑ LRU频率↑ 空降浪费↓
今天测试的时候碰到一个问题:
使用nginx负载均衡之后登录后的页面无法跳转,
由于同时使用了tomcat和resin,在nginx做负载均衡的时候会将请求随机发到tomcat和resin上,当属于tomcat的登录后会话被转发到resin上后,就会报错。
解决方法:
这里我是直接在配置文件里加上ip_hash:
不过这好像有点治标不治本
ip_hash可以依据请求来源IP对真实服务器的分配。取ipv4类型Ip的前三位字节 或者ipv6的整个ip作为hashing key, 这个方法可以确保访问一个被同一台服务器处理, 除非分配的server宕机。如果宕机 则请求被发往其他的机器。好像是说根据ip来分配服务器,同个ip一直分配同一个服务器。
要想从根本上解决似乎要通过memcached或redis来同步session,我这里没有尝试这种方法。
将memcached替换成Redis:
redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。
安装Redis:
Window 下安装
下载地址:https://github.com/MSOpenTech/redis/releases
Redis) 支持 32 位和 64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C 盘,解压后,将文件夹重新命名为 redis。
运行
开启服务器
打开一个 cmd 窗口 使用cd命令切换目录到 C:\redis 运行redis-server.exe redis.windows.conf
连接数据库
这时候另启一个cmd窗口,原来的不要关闭,不然就无法访问服务端了
切换到redis目录下运行 redis-cli.exe -h 127.0.0.1 -p 6379
Linux 下安装
下载地址:http://redis.io/download,下载最新稳定版本。
安装运行
源码安装
wegt http://download.redis.io/releases/redis-4.0.9.tar.gz
tar -zvxf redis-4.0.9.tar.gz
cd redis/
#编译
make
#编译完成文件生成到当前目录下的src下
cd src
启动redis
./redis-server注意这种方式启动redis 使用的是默认配置。也可以通过启动参数告诉redis使用指定配置文件使用下面命令启动。
./redis-server redis.confredis.conf是一个默认的配置文件。我们可以根据需要使用自己的配置文件。
运行
启动redis服务进程后,就可以使用测试客户端程序redis-cli和redis服务交互了。 比如:
./redis-cli
redis> set foo bar
OK
redis> get foo
"bar"
Redis 配置
Redis 的配置文件位于 Redis 安装目录下,文件名为redis.conf.
你可以通过CONFIG命令查看或设置配置项
redis-server.exe redis.windows.conf启动之后:
redis-cli.exe -h 127.0.0.1 -p 6379
使用set get 写入和读取数据:
在java中使用redis:
首先导入依赖:
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version></dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
对Redis的写入性能做一下测试:
package com.jnshu.util;
import org.junit.Test;
import redis.clients.jedis.Jedis;
public class RedisTest {
@Test
public void test(){
Jedis jedis = new Jedis("localhost", 6379, 100000);
int i = 0;
try {
long start = System.currentTimeMillis();// 开始毫秒数
while (true) {
long end = System.currentTimeMillis();
if (end - start >= 1000) {// 当大于等于1000毫秒(相当于1秒)时,结束操作
break;
}
i++;
jedis.set("test" + i, i + "");
}
} finally {// 关闭连接
jedis.close();
}
// 打印1秒内对Redis的操作次数
System.out.println("redis每秒操作:" + i + "次");
}
}
结果:
据说Redis性能可以达到十万级别。
使用 Redis 连接池
跟数据库连接池相同,Java Redis也同样提供了类redis.clients.jedis.JedisPool来管理我们的Reids连接池对象,并且我们可以使用redis.clients.jedis.JedisPoolConfig来对连接池进行配置:
JedisPoolConfig poolConfig = new JedisPoolConfig();
// 最大空闲数
poolConfig.setMaxIdle(50);
// 最大连接数
poolConfig.setMaxTotal(100);
// 最大等待毫秒数
poolConfig.setMaxWaitMillis(20000);
// 使用配置创建连接池
JedisPool pool = new JedisPool(poolConfig, "localhost");
// 从连接池中获取单个连接
Jedis jedis = pool.getResource();
// 如果需要密码
//jedis.auth("password");
Redis 只能支持六种数据类型(string/hash/list/set/zset/hyperloglog)的操作,但在 Java 中我们却通常以类对象为主,所以在需要 Redis 存储的五中数据类型与 Java 对象之间进行转换,如果自己编写一些工具类,比如一个角色对象的转换,还是比较容易的,但是涉及到许多对象的时候,这其中无论工作量还是工作难度都是很大的,所以总体来说,就操作对象而言,使用 Redis 还是挺难的,好在 Spring 对这些进行了封装和支持。
在 Spring 中,这些问题都可以通过使用RedisTemplate得到解决。
1,使用Spring配置JedisPoolConfig对象
在spring配置文件中加入:
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<!--最大空闲数-->
<property name="maxIdle" value="50"/>
<!--最大连接数-->
<property name="maxTotal" value="100"/>
<!--最大等待时间-->
<property name="maxWaitMillis" value="20000"/>
</bean>
2,为连接池配置工厂模型
Spring Data Redis中有四种工厂模型,它们分别是:
JredisConnectionFactory
JedisConnectionFactory
LettuceConnectionFactory
SrpConnectionFactory
加上:
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<!--Redis服务地址-->
<property name="hostName" value="localhost"/>
<!--端口号-->
<property name="port" value="6379"/>
<!--如果有密码则需要配置密码-->
<!--<property name="password" value="password"/>-->
<!--连接池配置-->
<property name="poolConfig" ref="poolConfig"/>
</bean>
3,配置RedisTemplate
普通的连接根本不能直接将对象直接存入 Redis 内存中,需要将对象序列化(可以简单的理解为继承Serializable接口)。可以把对象序列化之后存入Redis缓存中,然后在取出的时候又通过转换器,将序列化之后的对象反序列化回对象。
RedisTemplate可以找到对应的序列化器去转换Redis的键值:
<bean id="redisTemplate"
class="org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref="connectionFactory"/>
编写测试类:
@Test
public void test1(){
Student student = new Student();
student.setName("东方曜");
student.setImg("星辰之子");
redisTemplate.opsForValue().set("student_1", student);
Student student1 = (Student) redisTemplate.opsForValue().get("student_1");
System.out.println(student1.getImg() + ":" + student1.getName());
}
}
运行结果:
这里想要用Redis替换Memcached的时候发现由于之前直接在service实现类里写的,程序耦合性过高,全都要改,很不方便,似乎可以使用AOP。
收获:
完成了使用nginx负载均衡,简单了解了一下redis的安装及使用
明天计划完成的事情:
将Memcached改为AOP实现,完成redis整合进spring,压力测试。
评论