前兩天寫過 springMVC+memcached 的整合,我從這個基礎上改造一下,把redis和springmvc整合到一起。
和memcached一樣,redis也有java專用的客戶端,官網推薦使用的是:jedis。
看了一部分資料,大家推薦使用 spring-data-redis (spring在jedis的基礎上又包裝了一層),但是實際中感覺寫起來有點麻煩,不如原生態的jedis好用。
所以我利用spring的構造注入做了一個springmvc整合jedis的例子。
先了解下redis吧,這些資料袋都是從網上看到的:
Redis使用c語言編寫,面向“鍵/值”對類型數據的分佈式NoSql數據庫系統。
目前提供五中數據類型
string(字符串)
list(鏈表)
Hash(哈希)
set(集合)
zset(sorted set 有序集合),有2中編碼類型:ziplist,skiplist,當zset中數據較多時,將會被重構爲skiplist。
默認端口6379
redis-server.exe:服務端
redis-check-dump.exe:本地數據庫檢查
redis-check-aof.exe:更新日誌檢查
redis-benchmark.exe:性能測試,用以模擬同時由N個客戶端發送M個 SETs/GETs 查詢.
redis-cli.exe: 這個是客戶端,服務端開啓後,客戶端就可以輸入各種命令測試了
先寫一個Test類,測一下redis的基本數據類型和jedis的一些常用方法。以下的測試方法也都是從網上看到的,只不過爲了驗證是否準確以及jar包版本的問題,我自己親自敲了一遍。
注意jedis是redis的一個客戶端,是個jar包,不要搞混了……
public class Test {
public static void main(String[] args) {
// Jedis js = new Jedis("127.0.0.1", 6379);
// js.set("key001", "redis001");
// String val = js.get("key001");
// System.out.println(val);
// js.del("key001");
/**************************測試Redis的數據類型**************************/
/**
* list
*/
// js.rpush("list1", "aaaaaaaaaaaaaaaaaaaaaa");
// js.rpush("list1", "bbbbbbbbbbbbbbbbbbbbbb");
// js.rpush("list1", "ccccccccccccccccccccc");
// js.rpush("list1", "dddddddddddddd");
// List<String> vals = js.lrange("list1", 0, -1);
// for (int i = 0; i < vals.size(); i++) {
// System.out.println(vals.get(i));
// }
/**
* set 無須唯一
*/
// js.sadd("s1", "順序3");
// js.sadd("s1", "a");
// js.sadd("s1", "b");
// js.sadd("s1", "1");
// js.sadd("s1", "蛤蛤蛤");
// js.sadd("s1", "2");
// js.sadd("s1", "so waht?");
// js.sadd("s1", "%^");
// js.sadd("s1", "順序1");
// js.sadd("s1", "亂碼嗎?");
// js.sadd("s1", "順序2");
// Set<String> s = js.smembers("s1");
// for (String string : s) {
// System.out.println(s);
// }
// js.srem("s1", "蛤蛤蛤");
/**
* zset(sorted set 有序集合)
* 有2中編碼類型:ziplist,skiplist,當zset中數據較多時,將會被重構爲skiplist
*/
// js.zadd("zs", 92, "張三1");
// js.zadd("zs", 93, "張三7");
// js.zadd("zs", 94, "張三5");
// js.zadd("zs", 87, "張三9");
// js.zadd("zs", 66, "張三");
// js.zadd("zs", 19, "張三0");
// Set<String> sets = js.zrange("zs", 0, -1);
// for (String string : sets) {
// System.out.println(sets);
// }
/**
* Hash
*/
// Map m = new HashMap();
// m.put("1", "t");
// m.put("2", "ttt");
// m.put("username", "老王");
// m.put("password", "123456");
// m.put("age", "79");
// m.put("sex", "man");
// js.hmset("m", m);
// List<String> v = js.hmget("m", new String[]{"username","age"});
// List<String> v1 = js.hmget("m", "sex");
// System.out.println(v);
// System.out.println(v1);
// js.hdel("m", "username");//刪除map中的某一個鍵的鍵值對
/**************************事務控制**************************/
/**
* 事務方式(Transactions)
* 他主要目的是保障,一個client發起的事務中的命令可以連續的執行,而中間不會插入其他client的命令。
*
* 我們調用jedis.watch(…)方法來監控key,如果調用後key值發生變化,則整個事務會執行失敗。
* 另外,事務中某個操作失敗,並不會回滾其他操作。這一點需要注意。
* 還有,我們可以使用discard()方法來取消事務。
*/
// Jedis js1 = new Jedis("127.0.0.1", 6379);
// long s = System.currentTimeMillis();
// Transaction tx = js1.multi();
// for (int i = 0; i < 99999; i++) {
// tx.set("keyttt"+i, "valttt"+i);
// }
// List<Object> res= tx.exec();
// long e = System.currentTimeMillis();
// System.out.println((e-s)/1000.0+"秒");
//System.out.println(res);
// js1.disconnect();
/**************************管道**************************/
/**
* 管道(Pipelining)
* 有時,我們需要採用異步方式,一次發送多個指令,不同步等待其返回結果。
* 這樣可以取得非常好的執行效率。這就是管道
*/
// Jedis js2 = new Jedis("127.0.0.1", 6379);
// long s = System.currentTimeMillis();
// Pipeline pe = js2.pipelined();
// for (int i = 0; i < 9999; i++) {
// pe.set("keya"+i, "valuea"+i);
// }
// List<Object> l = pe.syncAndReturnAll();
// long e = System.currentTimeMillis();
// System.out.println((e-s)/1000.0+"秒");
// js2.disconnect();
/**************************管道中調用事務**************************/
/**
* 管道中調用事務
* 在用法上看,管道中包含了事務
*/
// Jedis js3 = new Jedis("127.0.0.1", 6379);
// long s = System.currentTimeMillis();
// Pipeline pe = js3.pipelined();
// pe.multi();
// for (int i = 0; i < 9999; i++) {
// pe.set("keybb"+i, "valuebb"+i);
// }
// pe.exec();
// List<Object> l = pe.syncAndReturnAll();
// long e = System.currentTimeMillis();
// System.out.println((e-s)/1000.0+"秒");
// js3.disconnect();
/**************************分佈式直連同步調用**************************/
/**
* 分佈式直連同步調用
* 線程不安全的,不建議在線程池中使用直連
*/
// List<JedisShardInfo> shards = Arrays.asList(
// new JedisShardInfo("localhost",6379),
// new JedisShardInfo("localhost",6380));
// ShardedJedis sharding = new ShardedJedis(shards);
// long start = System.currentTimeMillis();
// for (int i = 0; i < 100000; i++) {
// String result = sharding.set("sn" + i, "n" + i);
// }
// long end = System.currentTimeMillis();
// System.out.println("Simple@Sharing SET: " + ((end - start)/1000.0) + " seconds");
// sharding.disconnect();
/**************************分佈式直連同步調用**************************/
/**
* 分佈式直連異步調用
* 線程不安全的,不建議在線程池中使用直連
*/
// List<JedisShardInfo> shards = Arrays.asList(
// new JedisShardInfo("localhost",6379),
// new JedisShardInfo("localhost",6380));
// ShardedJedis sharding = new ShardedJedis(shards);
// ShardedJedisPipeline pipeline = sharding.pipelined();
// long start = System.currentTimeMillis();
// for (int i = 0; i < 100000; i++) {
// pipeline.set("sp" + i, "p" + i);
// }
// List<Object> results = pipeline.syncAndReturnAll();
// long end = System.currentTimeMillis();
// System.out.println("Pipelined@Sharing SET: " + ((end - start)/1000.0) + " seconds");
// sharding.disconnect();
/**************************分佈式連接池同步調用**************************/
/**
* 同步方式
*/
// List<JedisShardInfo> shards = Arrays.asList(
// new JedisShardInfo("localhost",6379),
// new JedisShardInfo("localhost",6380));
//
// ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
//
// ShardedJedis one = pool.getResource();
//
// long start = System.currentTimeMillis();
// for (int i = 0; i < 100000; i++) {
// String result = one.set("spn" + i, "n" + i);
// }
// long end = System.currentTimeMillis();
// pool.returnResource(one);
// System.out.println("Simple@Pool SET: " + ((end - start)/1000.0) + " seconds");
//
// pool.destroy();
//
/**************************分佈式連接池異步調用**************************/
/**
* 異步方式
*/
// List<JedisShardInfo> shards = Arrays.asList(
// new JedisShardInfo("localhost",6379),
// new JedisShardInfo("localhost",6380));
//
// ShardedJedisPool pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
//
// ShardedJedis one = pool.getResource();
//
// ShardedJedisPipeline pipeline = one.pipelined();
//
// long start = System.currentTimeMillis();
// for (int i = 0; i < 100000; i++) {
// pipeline.set("sppn" + i, "n" + i);
// }
// List<Object> results = pipeline.syncAndReturnAll();
// long end = System.currentTimeMillis();
// pool.returnResource(one);
// System.out.println("Pipelined@Pool SET: " + ((end - start)/1000.0) + " seconds");
// pool.destroy();
/**************************其他**************************/
/**
* 清空所有
*/
// js.flushAll();
/**
* 銷燬鏈接
*/
// js.disconnect();
}
開始貼代碼了,springMVC整合jedis
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>SpringMVC-Redis</display-name>
<!-- 引入 spring -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/applicationContext*.xml</param-value>
</context-param>
<!-- 引入 springMVC -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-servlet-config.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 編碼 UTF-8 -->
<filter>
<filter-name>SpringMVC-Redis-Encoding</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SpringMVC-Redis-Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script type="text/javascript" src="${pageContext.request.contextPath }/js/jquery/jquery-1.8.0.min.js"></script>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>index 2</title>
</head>
<body>
<div><font color="red" size="10px">${returnMsg}</font></div>
<form action="${pageContext.request.contextPath }/TestRequest/test" method="post" name="loginForm" id="loginForm">
<div>
用戶名:<input class="username" type="text" id="username" name="username" value=‘‘/>
</div>
<div >
密碼:<input class="password" type="password" id="password" name="password" value=""/>
</div>
<div><input type="button" value="submit" onclick="login()" /></div>
</form>
<script type="text/javascript">
function login(){
var username = $("#username").val();
var password = $("#password").val();
$("#loginForm").submit();
}
document.onkeydown=function(event){
e = event ? event :(window.event ? window.event : null);
if(e.keyCode==13){
login();
}
}
</script>
</body>
</html>
spring-servlet-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd">
<!-- 使用@Controllers前配置 -->
<mvc:annotation-driven />
<!-- 容器加載時 自動掃描所有註解 -->
<context:component-scan base-package="com.test" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Repository" />
<context:include-filter type="annotation" expression="org.springframework.stereotype.Component" />
</context:component-scan>
<!-- 配置靜態資源 -->
<mvc:resources mapping="/js/**" location="/js/" />
<mvc:resources mapping="/image/**" location="/image/" />
<mvc:resources mapping="/css/**" location="/css/" />
<!-- 使用jsp作爲視圖 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<!-- 目標路徑返回到pages下 使用jsp作爲視圖 -->
<property name="prefix" value="/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 異常處理 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.apache.shiro.authz.UnauthorizedException">error/403</prop>
</props>
</property>
</bean>
</beans>
先把這些貼上來是因爲這些文件內容都和上篇博文”springMVC+memcached “的一模一樣
applicationContext.xml
利用spring的構造注入,把集羣參數傳入RedisInitBean中,並且在項目啓動的時候加載RedisInitBean的有參構造方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
<bean id="RedisInitBean" class="com.test.test.RedisInitBean" >
<!-- IP:Port -->
<constructor-arg index="0" type="List">
<list>
<value>127.0.0.1:6379</value>
<value>192.168.3.27:6380</value>
</list>
</constructor-arg>
<!-- maxWaitMillis -->
<constructor-arg index="1" type="long">
<value>1000</value>
</constructor-arg>
<!-- MaxIdle -->
<constructor-arg index="2" type="int">
<value>200</value>
</constructor-arg>
<!-- testOnBorrow -->
<constructor-arg index="3" type="Boolean">
<value>true</value>
</constructor-arg>
</bean>
</beans>
RedisInitBean.java
這裏面要說一下,使用的是 分佈式連接池 異步調用!
package com.test.test;
import java.util.Arrays;
import java.util.List;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
public class RedisInitBean {
private List Host;
private long maxWaitMillis;
private int MaxIdle;
private Boolean testOnBorrow;
private static List<JedisShardInfo> shards ;
private static ShardedJedisPool pool;
private static ShardedJedis jedis;
public RedisInitBean(List host, long maxWaitMillis, int maxIdle,
Boolean testOnBorrow) {
super();
Host = host;
this.maxWaitMillis = maxWaitMillis;
MaxIdle = maxIdle;
this.testOnBorrow = testOnBorrow;
if(host.size()!=0){
for (int i = 0; i < host.size(); i++) {
String h[] = ((String) host.get(i)).split(":");
shards = Arrays.asList(new JedisShardInfo(h[0].trim(),Integer.parseInt(h[1].trim())));
System.out.println(shards);
}
}else{
System.out.println("請檢查Redis配置,host項爲必填項!格式[IP:PORT]");
}
pool = new ShardedJedisPool(new JedisPoolConfig(), shards);
jedis = pool.getResource();
}
public synchronized ShardedJedis getSingletonInstance(){
return jedis;
}
public synchronized static void returnResource(){
pool.returnResource(jedis);
}
public synchronized static void destroy(){
pool.destroy();
}
}
TestRequest.java
剛纔我們寫的index.jsp中,提交了表單後瀏覽器會發起請求,spring攔截請求後會找到註解匹配的類中的方法,TestRequest就是了。
package com.test.web;
import java.util.List;
import javax.servlet.http.HttpSession;
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.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import com.test.test.RedisInitBean;
@Controller
@RequestMapping("/TestRequest")
public class TestRequest {
@Autowired
private RedisInitBean rib;
@RequestMapping("/test")
public ModelAndView test(@RequestParam(value = "username") final String userid,
@RequestParam(value = "password") final String passwd, HttpSession session){
ModelAndView m = new ModelAndView();
m.setViewName("../index");
ShardedJedis jedis = rib.getSingletonInstance();
ShardedJedisPipeline pipeline = jedis.pipelined();
long start = System.currentTimeMillis();
for (int i = 0; i < 99999; i++) {
pipeline.set("zhenbn" + i, "n" + i);
}
List<Object> results = pipeline.syncAndReturnAll();
long end = System.currentTimeMillis();
rib.returnResource();
rib.destroy();
System.out.println("分佈式連接池異步調用耗時: " + ((end - start)/1000.0) + " 秒");
try {
Thread.sleep(5000);//睡5秒,然後打印jedis返回的結果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("返回結果:"+results);
m.addObject("returnMsg","麼麼噠!");
return m;
}
}
存完之後,我們可以取一下試試,看看到底有沒有存進去。
看,取到了吧~
使用jedis的時候要注意配合commons-pool2.jar使用,否則會報錯的。
原因是 JedisPoolConfig extends GenericObjectPoolConfig,
而GenericObjectPoolConfig則是:
BaseObjectPoolConfig則是: