网站性能优化小结和spring整合redis
现在越来越多的地方需要非关系型数据库了,最近网站优化,当然从页面到服务器做了相应的优化后,通过在线网站测试工具与之前没优化对比,发现有显著提升。
服务器优化目前主要优化tomcat,在tomcat目录下的server.xml文件配置如下内容:
<Connector port="1818" protocol="HTTP/1.1" maxHttpHeaderSize="8192" maxThreads="1000" minSpareThreads="100" maxSpareThreads="1000" minProcessors="100" maxProcessors="1000" enableLookups="false" compression="on" compressionMinSize="2048" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain" connectionTimeout="20000" URIEncoding="utf-8" acceptCount="1000" redirectPort="8443" disableUploadTimeout="true"/> 参数说明: Protocol 采用的协议//可将HTTP/1.1改为org.apache.coyote.http11.Http11NioProtocol 启动NIO模式 maxHttpHeaderSize 代表请求和响应的HTTP首部的最大长度,单位是字节。如果不指定,该属性将被设为4096(4K)。 maxThreads 客户请求最大线程数 minSpareThreads Tomcat初始化时创建的 socket 线程数 maxSpareThreads Tomcat连接器的最大空闲 socket 线程数 enableLookups 若设为true, 则支持域名解析,可把 ip 地址解析为主机名 redirectPort 在需要基于安全通道的场合,把客户请求转发到基于SSL 的 redirectPort 端口 acceptAccount 监听端口队列最大数,满了之后客户请求会被拒绝(不能小于maxSpareThreads ) connectionTimeout 连接超时 minProcessors 服务器创建时的最小处理线程数 maxProcessors 服务器同时最大处理线程数 URIEncoding URL统一编码 compression 打开压缩功能 compressionMinSize 启用压缩的输出内容大小,这里面默认为2KB compressableMimeType 压缩类型 connectionTimeout 定义建立客户连接超时的时间. 如果为 -1, 表示不限制建立客户连接的时间
网站性能优化,参照了《高性能网站建设指南》这本书和部分知识博客
就我们项目而言,我参照这本书,按照这么几个规范进行,书上提出了,优化十四个建议,不过,并不是十四建议通通采纳,网站性能一定能上升的非常好,要结合项目的实际情况。
这是我们采取的前端性能优化措施:
1.减少http请求 比如外部的css,js和图片等组件,访问一个网站时,这些组件都会被加载,组件过多,加载时间长,特别是图片等,所以减少http请求,可有效提高网站性能
2.头部引用外部css和底部引用js 初次点击进入网站,网站的背景图和其他非js效果的css效果会最先加载,j如果不放在头部的话,首先看到的就是空白,然后就有相应的css渲染效果,底部引用js,在视觉上让用户觉得加载快了,而且外部的css和js方便管理,内联的js和css过度使用,会导致页面代码重构和后续其他人开发,会比较吃力。同时这样做也是一种很好的规范。js放在尾部也就是</body>标签前,它会被最后加载,如果统统放在<head></head>下,并行加载,会导致阻塞后面文件的下载和会导致后面的css渲染变慢。因此放在尾部是比较好的选择。
3.压缩组件。目前通过tomcat中的上述配置实行gzip压缩
4.合并css和js文件 大家要知道加载一个js和加载两个js文件的速度完全是不一样的,尽快前者js文件的容量大于后者两个。总之一个请求的速度总会大于两个请求的速度。
从http请求的角度解析,客户端发出请求给服务器,服务器响应数据返回给客户端。一个请求到响应的速度始终大于两个请求。还是回到之前的减少http请求。另外合并不代表一个无关的js和另外好几个无关js合在一起,这样不利于后面管理,合并应该是相关js函数合在一起,不相关js文件如果内容很多,可不必合并,如果只有单独的一两个函数,可与另外一两个函数合并,切记要写注释,同时合并js,不可合并过多
后台采取的措施:
1.sql优化 查询尽量查出符合需要的字段,严禁用*,同时in和not in尽可能用exists和not exists替换等
2.Java代码复用,减少冗余,特别是后台很多重复的service,将其公共通用部分写成一个函数,以供用到的Controller进行复用(当然这对于优化网站性能方面,可能帮助不大,但有利于后续开发的进行)
下面进行正式的spring整合redis:
为什么要用redis?
就目前我们项目而言,打开pms后台加载过慢,当然原因包括没用的js过多引用进来加载时间长,自然速度慢,频繁的http请求,布局不合理(js全部放在头部),sql没有优化等。
上述问题都可以解决。
回到上述问题,为什么使用redis。使用redis做缓存,R可以将所有的数据先保存到缓存中,然后再存入mysql中,减小数据库压力,提高效率 。
redis为什么访问数据的速度大于mysql?
因为前者访问的是内存,后者是磁盘
因为cpu是直接与内存进行数据交互的
演示实例:
注意ssm框架,jdk8,tomcat8服务器
一、pom依赖
<!-- redis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.1.0</version> </dependency> <!-- spring-data-redis --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>1.0.2.RELEASE</version> </dependency> <!-- mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.0.3</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.24</version> </dependency>
二、对应的application-config.xml配置
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxIdle" value="1000" /> <property name="testOnBorrow" value="true"/> </bean> <!-- 连接池配置,类似数据库连接池 --> <bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" > <property name="hostName" value="192.168.126.128"></property> <property name="port" value="6379"></property> <property name="password" value="123456"></property> <property name="poolConfig" ref="poolConfig"></property> </bean> <!-- 调用连接池工厂配置 --> <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"> <property name="connectionFactory" ref="connectionFactory"></property> <!-- 如果不配置Serializer,那么存储的时候智能使用String,如果用User类型存储,那么会提示错误User can't cast to String!!! --> <property name="keySerializer"> <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" /> </property> <property name="valueSerializer"> <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" /> </property> </bean> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <!-- 基本属性 url、user、password --> <property name="driverClassName" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/test" /> <property name="username" value="root" /> <property name="password" value="1234" /> <property name="filters" value="stat,config" /> <!-- 配置初始化大小、最小、最大 --> <property name="initialSize" value="1" /> <property name="minIdle" value="1" /> <property name="maxActive" value="40" /> <!-- 配置获取连接等待超时的时间 --> <property name="maxWait" value="60000" /> <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --> <property name="timeBetweenEvictionRunsMillis" value="60000" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --> <property name="minEvictableIdleTimeMillis" value="300000" /> <property name="validationQuery" value="SELECT 'x' FROM DUAL" /> <property name="testWhileIdle" value="true" /> <property name="testOnBorrow" value="false" /> <property name="testOnReturn" value="false" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 --> <property name="poolPreparedStatements" value="true" /> <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> <!-- 超过时间限制是否回收 --> <property name="removeAbandoned" value="true" /> <!-- 超时时间;单位为秒。180秒=3分钟 --> <property name="removeAbandonedTimeout" value="180" /> <!-- 关闭abanded连接时输出错误日志 --> <property name="logAbandoned" value="true" /> <!-- 配置监控统计拦截的filters,去掉后监控界面sql无法统计 --> <!-- property name="filters" value="stat" /--> </bean>
三、JavaBean
记得一定要实现序列化,否则会报错
package com.tp.soft.entity; import java.io.Serializable; public class User implements Serializable{ /** * */ private static final long serialVersionUID = -1695973853274402680L; private int userid; private String login_name; private String login_pwd; public User() { } public User(int userid, String login_name, String login_pwd) { super(); this.userid = userid; this.login_name = login_name; this.login_pwd = login_pwd; } public int getUserid() { return userid; } public void setUserid(int userid) { this.userid = userid; } public String getLogin_name() { return login_name; } public void setLogin_name(String login_name) { this.login_name = login_name; } public String getLogin_pwd() { return login_pwd; } public void setLogin_pwd(String login_pwd) { this.login_pwd = login_pwd; } }
四、接口类
package com.tp.soft.dao; import com.tp.soft.entity.User; public interface UserMapper { public User getUserById(int id); }
五、接口对应的xml文件
<?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.tp.soft.dao.UserMapper"> <!-- 缓存类配置 --> <cache type="com.tp.soft.redis.RedisCache" /> <select id="getUserById" parameterType="int" resultType="user" useCache="true"> select * from AU_USER where userid = #{id} </select> </mapper>
六、mybatis-config.xm配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<!-- 二级缓存开启 -->
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="false"/>
<setting name="aggressiveLazyLoading" value="true"/>
</settings>
<!-- 配置映射类的别名 -->
<typeAliases>
<!-- 配置entity下的所有别名 别名首字母小写 -->
<package name="com.tp.soft.entity" />
</typeAliases>
</configuration>
七、service和service实现类
package com.tp.soft.service; import com.tp.soft.entity.User; public interface UserSvc { public User getUser(int id); }
package com.tp.soft.service.impl; import javax.annotation.Resource; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import com.tp.soft.dao.UserMapper; import com.tp.soft.entity.User; import com.tp.soft.service.UserSvc; @Service("userService") public class UserSvcImpl implements UserSvc{ @Resource private UserMapper userMapper; public User getUser(int id) { User user = null; try{ user = userMapper.getUserById(id); }catch (DataAccessException e) { System.out.println(e.getLocalizedMessage()); } return user; } }
八、Controller
package com.tp.soft.controller; import javax.annotation.Resource; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import com.tp.soft.entity.User; import com.tp.soft.service.UserSvc; @Controller public class UserController { @Resource private UserSvc userSvc; @RequestMapping(value="/QueryUser") public String toQueryUser(int id,Model model){ User user = userSvc.getUser(id); System.out.println(user.getLogin_name()); model.addAttribute("user", user); return "/pc/userTest"; } }
九、需用到的util
package com.tp.soft.redis; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; import redis.clients.jedis.JedisPoolConfig; public class JedisUtil { private static String ADDR = "192.168.126.128"; private static int PORT = 6379; private static String AUTH = "123456"; private static int MAX_ACTIVE = 1024; private static int MAX_IDLE = 200; private static int MAX_WAIT = 10000; private static int TIMEOUT = 10000; private static boolean TEST_ON_BORROW = true; private static JedisPool jedisPool = null; static { try{ JedisPoolConfig config = new JedisPoolConfig(); config.setMaxIdle(MAX_IDLE); config.setTestOnBorrow(TEST_ON_BORROW); jedisPool = new JedisPool(config,ADDR,PORT,TIMEOUT,AUTH); }catch (Exception e) { e.printStackTrace(); } } public synchronized static Jedis getJedis(){ try{ if(jedisPool != null){ Jedis jedis = jedisPool.getResource(); return jedis; }else{ return null; } }catch (Exception e) { e.printStackTrace(); return null; } } public static void returnResource(final Jedis jedis){ if(jedis != null){ jedisPool.returnResource(jedis); } } }
package com.tp.soft.redis; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.ibatis.cache.Cache; /* * 使用第三方缓存服务器,处理二级缓存 */ public class RedisCache implements Cache { private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); private String id; public RedisCache(final String id) { if (id == null) { throw new IllegalArgumentException("Cache instances require an ID"); } this.id = id; } public String getId() { return this.id; } public void putObject(Object key, Object value) { JedisUtil.getJedis().set(SerializeUtil.serialize(key.toString()), SerializeUtil.serialize(value)); } public Object getObject(Object key) { Object value = SerializeUtil.unserialize(JedisUtil.getJedis().get( SerializeUtil.serialize(key.toString()))); return value; } public Object removeObject(Object key) { return JedisUtil.getJedis().expire( SerializeUtil.serialize(key.toString()), 0); } public void clear() { JedisUtil.getJedis().flushDB(); } public int getSize() { return Integer.valueOf(JedisUtil.getJedis().dbSize().toString()); } public ReadWriteLock getReadWriteLock() { return readWriteLock; } }
package com.tp.soft.redis; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class SerializeUtil { public static byte[] serialize(Object object) { ObjectOutputStream oos = null; ByteArrayOutputStream baos = null; try { // 序列化 baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(object); byte[] bytes = baos.toByteArray(); return bytes; } catch (Exception e) { e.printStackTrace(); } return null; } public static Object unserialize(byte[] bytes) { if (bytes == null) return null; ByteArrayInputStream bais = null; try { // 反序列化 bais = new ByteArrayInputStream(bytes); ObjectInputStream ois = new ObjectInputStream(bais); return ois.readObject(); } catch (Exception e) { e.printStackTrace(); } return null; } }
十、演示效果
目前本人也是刚刚用到没多久,如果那里有问题,欢迎大家指教
其实性能的瓶颈和mysql有关系,目前对于mysql相关的原理等不是特别了解,需后面多加努力学习