开发日常小结(4):如何将Redis技术运用到java项目工程呢?--分析Jedis的使用和实例详解

2018年4月7日

背景:

大型商业项目,无论是java还是php,或是python,都会对redis数据库进行开发,redis数据库相比于其他的key-value数据库还是优势明显的。redis的叙述,在前文《Redis (REmote DIctionary Server):一个高性能的key-value数据库的学习(1)》有提到,这里不多介绍。

运用:

在实际工作中,工程代码已经有了良好的封装(企业内部都会有自己的开发框架,而不是只拿第三方开源框架来直接用),所以更多时候,我实在思考框架这样编码的思路和逻辑;开发过一两次任务后,发现用的真方便,而且逻辑严谨。上文列举的例子是:用protocol buffer来序列化数据,然后将二进制数据存放到redis;下面继续说一下如何操作java对象,将Object序列化和保存;

我们在使用MySQL数据库时,这是一个关系型数据库,常用的套路是代码分层,Service -- Dao -- Mapper/Sql ;从业务开始到操作层到数据库语言,这是一个好方法,redis 也同样是这样的操作套路,不过redis是key-value数据库,所以有一些不同:

1)redis的操作也有增删改查,但是方法的名字极其简单地,如set/get等等,当然深入使用还有更多发现;

2)redis的操作层本质上只有一个,那就是redis为java开发者提供的对数据库进行操作的API,service和dao就是在其基础上封装上去的;

 

Jedis的简单使用

我们常常使用jedis来连接使用redis;但是在公司项目里,却不是使用Jedis来操作的,这个问题我会继续跟进。

例子:

 

import redis.clients.jedis.Jedis;

public class test5 {
	public static void main (String [] args){
		Jedis jedis = new Jedis("10.0.1.195");
//		Jedis jedis = new Jedis("localhost");	//连接效果与详细IP一样
		System.out.println("connect successfully!");
		System.out.println("service running : "+ jedis.ping());
		
		System.out.println("========================");
		
		jedis.set("mmb", "8888");
		System.out.println("redis 存储的字符串为: "+ jedis.get("mmb"));
		
		System.out.println("========================");
	}
}


结果:

 

connect successfully!
service running : PONG
========================
redis 存储的字符串为: 8888
========================

 

 

代码实例:

 

 

下面还是以游戏公告为例子,

Dao层:

我对redis的Dao层进行了封装,实际调用的还是Jedis API:

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import **.redis.JRedis;
import **.proclamation.ProclamationInfoList;
import **.proclamation.RoleProclamationInfo;
import **.dao.redis.base.RedisKeyUtil;
import **.base.StorageKey;

@Repository
public class ProclamationRepo {
	@Autowired
	private JRedis jRedis;
	
	private static final String PROCLAMATION_SYSTEM_NOTICE_KEY_STR = "proclamation_system_notice_key_do_not_change_it";
	
	private byte[] getProclamationListNoticeKey(){
		return RedisKeyUtil.getByteArray(PROCLAMATION_SYSTEM_NOTICE_KEY_STR, StorageKey.global_world_channel_proclamationlist_notice);
	}

	public void saveProclamationInfoList(ProclamationInfoList info){	
		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = info.copyTo().toByteArray();
		jRedis.set(key, bytes);
	}
	
	public ProclamationInfoList getProclamationNotice(){
		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = jRedis.get(key);
		
		ProclamationInfoList proclamationInfoList = new ProclamationInfoList();
		if (bytes == null) {
			saveProclamationInfoList(proclamationInfoList);
		}else{
			proclamationInfoList.parseFrom(bytes);
		}
		
		return proclamationInfoList;
	}
	
	private byte[] getRoleProclamationInfoKey(long roleId) {				//该方法可以忽略
		return RedisKeyUtil.getByteArray(roleId, StorageKey.role_proclamation_info_key);
	}
	
	public void saveRoleProclamationInfo(RoleProclamationInfo roleProclamationInfo) {	//该方法可以忽略
		byte[] key = getRoleProclamationInfoKey(roleProclamationInfo.getRoleId());
		byte[] bytes = roleProclamationInfo.toByteArray();
		jRedis.set(key, bytes);
	}
	
	public RoleProclamationInfo getRoleProclamationInfo(long roleId){			//该方法可以忽略
		byte[] key = getRoleProclamationInfoKey(roleId);
		byte[] bytes = jRedis.get(key);
		if (bytes == null) {
			RoleProclamationInfo roleProclamationInfo = new RoleProclamationInfo(roleId);
			roleProclamationInfo.setRoleId(roleId);
			saveRoleProclamationInfo(roleProclamationInfo);
			return roleProclamationInfo;
		} else {
			RoleProclamationInfo roleProclamationInfo = new RoleProclamationInfo(bytes);
			roleProclamationInfo.setRoleId(roleId);
			return roleProclamationInfo;
		}
	}
	
}	
		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = info.copyTo().toByteArray();
		jRedis.set(key, bytes);
	}
	
	public ProclamationInfoList getProclamationNotice(){
		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = jRedis.get(key);
		
		ProclamationInfoList proclamationInfoList = new ProclamationInfoList();
		if (bytes == null) {
			saveProclamationInfoList(proclamationInfoList);
		}else{
			proclamationInfoList.parseFrom(bytes);
		}
		
		return proclamationInfoList;
	}
	
	private byte[] getRoleProclamationInfoKey(long roleId) {				//该方法可以忽略
		return RedisKeyUtil.getByteArray(roleId, StorageKey.role_proclamation_info_key);
	}
	
	public void saveRoleProclamationInfo(RoleProclamationInfo roleProclamationInfo) {	//该方法可以忽略
		byte[] key = getRoleProclamationInfoKey(roleProclamationInfo.getRoleId());
		byte[] bytes = roleProclamationInfo.toByteArray();
		jRedis.set(key, bytes);
	}
	
	public RoleProclamationInfo getRoleProclamationInfo(long roleId){			//该方法可以忽略
		byte[] key = getRoleProclamationInfoKey(roleId);
		byte[] bytes = jRedis.get(key);
		if (bytes == null) {
			RoleProclamationInfo roleProclamationInfo = new RoleProclamationInfo(roleId);
			roleProclamationInfo.setRoleId(roleId);
			saveRoleProclamationInfo(roleProclamationInfo);
			return roleProclamationInfo;
		} else {
			RoleProclamationInfo roleProclamationInfo = new RoleProclamationInfo(bytes);
			roleProclamationInfo.setRoleId(roleId);
			return roleProclamationInfo;
		}
	}
	
}

 

分析:

 

 

1)JRedis的引入,通过spring的IOC,@Autowired ,实例化导入;(JRedis应该是开源框架的一个数据结构,有待查实,但是功能定位确是和Jedis相似)

 

	@Autowired
	private JRedis jRedis;

 

----------------------------------

 

补充:JRedis 为redis的客户端框架,为开发者提供的;参考  https://redis.io/clients#java;

 

2)字符串 PROCLAMATION_SYSTEM_NOTICE_KEY_STR为 redis 存放 ProclamationListNotice 的一个标志,确保不会和其他保存在reids的键产生重名冲突;

 

3)getProclamationListNoticeKey方法,获取redis数据的key,调用了框架底层代码,传入标志和其他信息,通过拼装字符串,返回一个key;最底层使用的是redis的相关jar包,RedisStorageKey类的 getByteArray方法:

 

3.1

 

	public static byte[] getByteArray(String platFormId, StorageKey keyFlag){
		return RedisStorageKey.getByteArray(platFormId, (short)keyFlag.code);
	}
	


3.2

 

	public static byte[] getByteArray(String platformId, short keyFlag) {
		//***这里是一堆处理
		return bytes;
	}

 

 

4)saveProclamationInfoList方法,保存公告列表数据:

 

 

 

		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = info.copyTo().toByteArray();
		jRedis.set(key, bytes);

 

简单理解,获取key,将 ProclamationInfoList 序列化为二进制数组,作为value,最后set保存;

 

 

Q1:你会问:ProclamationInfoList 序列化为二进制数组这个过程如何实现呢?

A1:ok,前文《开发日常小结(2):如何使用Google提供的Protocol buffer协议实现序列化呢?--游戏公告序列化实例详解》可以解答:

 

	@Override
	public ProclamationInfoListProto copyTo() {
		ProclamationInfoListProto.Builder builder = ProclamationInfoListProto.newBuilder();
		for (ProclamationInfo info : proclamationInfoList) {
			builder.addProclamationInfoList(info.copyTo());
		}
		
		return builder.build();
	}
	@Override
	public byte[] toByteArray() {
		return copyTo().toByteArray();
	}


本质就是使用了protobuffer序列化结构(proto生成的java类),这个过程就是:;

 

5)getProclamationNotice方法,获取公告列表数据:

 

	public ProclamationInfoList getProclamationNotice(){
		byte[] key = getProclamationListNoticeKey();
		byte[] bytes = jRedis.get(key);
		
		ProclamationInfoList proclamationInfoList = new ProclamationInfoList();
		if (bytes == null) {
			saveProclamationInfoList(proclamationInfoList);
		}else{
			proclamationInfoList.parseFrom(bytes);
		}
		
		return proclamationInfoList;
	}
	@Override
	public void parseFrom(byte[] bytes) {
		try {
			ProclamationInfoListProto proto = ProclamationInfoListProto.parseFrom(bytes);
			copyFrom(proto);
		} catch (InvalidProtocolBufferException e) {
			e.printStackTrace();
		}
	}


再往下追,就是google的封装好的方法了,看不了了!!

 

    public static com.xs.fun.base.proto.service.WorldChannelProtoBuffer.ProclamationInfoListProto parseFrom(byte[] data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data);
    }

 

 

Service层:

 

 

 

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.**.proclamation.ProclamationInfoList;
import com.**.redis.ProclamationRepo;
import com.**.service.BaseProclamationService;

@Service
public class ProclamationService {
	
	@Autowired
	private ProclamationRepo proclamationRepo;
	
	//保存新公告
	public void saveProclamationInfoList(ProclamationInfoList info){
		proclamationRepo.saveProclamationInfoList(info);
	}
	//获取保存的新公告
	public ProclamationInfoList getProclamationNotice(){
		return proclamationRepo.getProclamationNotice();
	}

}

 

总结:

1)综上所述,reids的操作,和mysql一样可以代码分层,划分为service,dao和action(JRedis)三层;

2)序列化与数据保存流程,可以参考下图:

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值