1. 程式人生 > >網站性能優化小結和spring整合redis

網站性能優化小結和spring整合redis

remove utf ota turn tor package process 基本屬性 version

現在越來越多的地方需要非關系型數據庫了,最近網站優化,當然從頁面到服務器做了相應的優化後,通過在線網站測試工具與之前沒優化對比,發現有顯著提升。

服務器優化目前主要優化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 cant 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相關的原理等不是特別了解,需後面多加努力學習

網站性能優化小結和spring整合redis