1. 程式人生 > >SpringBoot構建微服務實戰 之 整合Redis

SpringBoot構建微服務實戰 之 整合Redis

前言

本節我們將學習一下SpringBoot 如何整合單機版Redis

概述

首先我得陳述一下,本節學習的SpringBoot 整合R單機版Redis總共有如下步驟。

  • SpringBoot 初步整合 Spring-data-redis
    首先我們應該知道 Spring-data-redis 已近提供了對Redis很好應用的API,因而SpringBoot 整合 Redis實際上就是SpringBoot整合 Spring-data-redis
  • Spring-data-redis的序列化
    我們在使用Spring-data-redis 封裝好了redisTemplate時,可能會出現如下亂碼:

    127.0.0.1:6379> keys *
    1) "\xac\xed\x00\x05t\x00#ContentPlatform2:ES:UpSertESContent"
    2) "\xac\xed\x00\x05t\x00%ContentPlatform2:Lock_v16:CJH_ARTICLE"
    3) "\xac\xed\x00\x05t\x00!ContentPlatform2:Lock_v16:V_VIDEO"
    4) "\xac\xed\x00\x05t\x00\x1bContentPlatform2:ES:Content"
    5) "\xac\xed\x00\x05t\x00#ContentPlatform2:Lock_v16:CJH_VIDEO"
    6) "\xac
    \xed\x00\x05t\x00%ContentPlatform2:Lock_v16:CMS_ARTICLE"

    原因是:spring-data-redis的RedisTemplate《K,V》模板類在操作redis時預設使用JdkSerializationRedisSerializer來進行序列,另外在一些需要Redis 儲存java 物件時我們也希望Redis能支援JAVA物件的快速儲存。因而在初步整合Redis之後我們需要重新自定義Redis的 SerializationRedisSerializer。

  • Spring-data-redis的 redisTemplate二次定義
    首先我們應該知道Spring-data-redis 已經為我們封裝好了redisTemplate,使用預設的redistemplate 我們就能操作redis nosql 了,可是在實際的需求和場景中,我們應該重新自定義redisTemplate以滿足我們特定的需求。

整合Spring-data-redis

  • 引入依賴
    SpringBoot 總的編碼風格是:約定>配置>編碼。因而SpringBoot在整合Spring-data-redis時也做了很多預設自動配置。總的來說我們只需要在pom中引入如下引用變可以直接使用redisTemplate 來操作Redis nosql。

    <!-- SpringBoot-Redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
  • SpringBoot測試redis
    引入依賴之後便可以直接使用redisTemplate 來操作Redis Nosql了

    import javax.annotation.Resource;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.impact.ImpactApplication;
    import com.impact.framework.redis.vo.RedisVo;
    @SpringBootTest(classes = ImpactApplication.class)
    @RunWith(SpringJUnit4ClassRunner.class)
    @WebAppConfiguration
    public class TestRedis {
        @Resource
        //private StringRedisTemplate  strRedisTemplate;
        private RedisTemplate<String, Object>  redisTemplate;
    
        @Test
        public void testSet() {
            RedisVo vo = new RedisVo() ;
            vo.setMid("studyjava");
            vo.setAge(19);
            this.redisTemplate.opsForValue().set("study", vo);
            System.out.println((JSON) JSONObject.toJSON(this.redisTemplate.opsForValue().get("study")));
        }
    }

Spring-data-redis的序列化

關於 Spring-data-redis的序列化我們來學習一下 spring-data-redis序列化策略
spring-data-redis提供了多種serializer策略,這對使用jedis的開發者而言,實在是非常便捷。sdr提供了4種內建的serializer:

  • JdkSerializationRedisSerializer:使用JDK的序列化手段(serializable介面,ObjectInputStrean,ObjectOutputStream),資料以位元組流儲存,jdk序列化和反序列化資料
  • StringRedisSerializer:字串編碼,資料以string儲存
  • JacksonJsonRedisSerializer:json格式儲存
  • OxmSerializer:xml格式儲存

其中JdkSerializationRedisSerializer和StringRedisSerializer是最基礎的序列化策略,其中“JacksonJsonRedisSerializer”與“OxmSerializer”都是基於stirng儲存,因此它們是較為“高階”的序列化(最終還是使用string解析以及構建java物件)。

RedisTemplate中需要宣告4種serializer,預設為“JdkSerializationRedisSerializer”:

  • keySerializer :對於普通K-V操作時,key採取的序列化策略
  • valueSerializer:value採取的序列化策略
  • hashKeySerializer: 在hash資料結構中,hash-key的序列化策略
  • hashValueSerializer:hash-value的序列化策略

無論如何,建議key/hashKey採用StringRedisSerializer

下面我們將著重學習一下 JacksonJsonRedisSerializer

  • 新建 RedisConfig.java

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    
    import com.fasterxml.jackson.annotation.JsonAutoDetect;
    import com.fasterxml.jackson.annotation.PropertyAccessor;
    import com.fasterxml.jackson.databind.ObjectMapper;
    
    @Configuration
    public class RedisConfig {
    
         /**
         * redisTemplate 序列化使用的jdkSerializeable, 儲存二進位制位元組碼, 所以自定義序列化類
         * @param redisConnectionFactory
         * @return
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
            RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
            redisTemplate.setConnectionFactory(redisConnectionFactory);
    
            // 使用Jackson2JsonRedisSerialize 替換預設序列化
            Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
    
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
    
            jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
    
            // 設定value的序列化規則和 key的序列化規則
            redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
            redisTemplate.setKeySerializer(new StringRedisSerializer());
            redisTemplate.afterPropertiesSet();
            return redisTemplate;
        }

    此時便重新定義了Redis的serializer 呼叫時只需在呼叫類中引用RedisTemplate Bean即可

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

RedisTemplate Re-definition

關於RedisTemplate的重新定義,首先我們要清楚自己的需求,源生的RedisTemplate是否能滿足我的需求,重新定義並不代表源生的就很LOW,在需求面前只有高效和更高效一說。

  • 定義重定義redisTemplate介面

    import java.util.List;
    
    /**
     * 二次封裝 RedisTemplate Interface
     * 以後根據需要可重新自定義
     * @author Dustyone
     *  * ImpactInit
     */
    public interface RedisService {
        /**
         * PUT K-V
         * @param key
         * @param value
         * @return
         */
        public boolean set(String key, String value);
    
        /**
         * Get Object By Key
         * @param key
         * @return
         */
        public Object get(String key);
    
        /**
         * Setting key expire Time
         * 以秒為單位
         * @param key
         * @param expire
         * @return
         */
        public boolean expire(String key, long expire);
    
        /**
         * PUT K-LIST
         * @param key
         * @param list
         * @return
         */
        public <T> boolean setList(String key, List<T> list);
    
        /**
         * GET K-LIST
         * @param key
         * @param clz
         * @return
         */
        public <T> List<T> getList(String key, Class<T> clz);
    
        /**
         * 將所有指定的值插入到存於 key 的列表的頭部
         * PUT K-O
         * @param key
         * @param obj
         * @return
         */
        public long setObject(String key, Object obj);
    
        /**
         * 重複新增指定Key中的指定元素
         * @param key
         * @param obj
         * @return
         */
        public long getObject(String key, Object obj);
    
    }

    更多的方法可以自己去定義,這裡就需要對Redis的一些基本命令要了解了。

  • 實現介面

    import java.util.List;
    import java.util.concurrent.TimeUnit;
    
    import javax.annotation.Resource;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.stereotype.Service;
    
    import com.impact.framework.redis.template.RedisService;
    
    /**
     * RedisTemplate的二次封裝實現方法
     * @author Dustyone
     *
     */
    @Service
    public class RedisServiceImpl implements RedisService {
    
        private static final Logger log = LoggerFactory.getLogger(RedisServiceImpl.class);
    
        //引入 Spring封裝好了的RedisTemplate
        @Resource
        private RedisTemplate<String, Object> redisTemplate;
    
        @Override
        public boolean set(String key, String value) {
            boolean flag = false;
            try {
                this.redisTemplate.opsForValue().set(key, value);
                flag = this.redisTemplate.opsForValue().get(key) == null
                        || "".equals(this.redisTemplate.opsForValue().get(key)) ? false : true;
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return flag;
        }
    
        @Override
        public Object get(String key) {
            Object retsult = null;
            try {
                retsult = this.redisTemplate.opsForValue().get(key);
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return retsult;
        }
    
        @Override
        public boolean expire(String key, long expire) {
            boolean flag = false;
            try {
                flag = redisTemplate.expire(key, expire, TimeUnit.SECONDS);
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return flag;
        }
    
        @Override
        public <T> boolean setList(String key, List<T> list) {
            return false;
        }
    
        @Override
        public <T> List<T> getList(String key, Class<T> clz) {
            // TODO Auto-generated method stub
            return null;
        }
    
        @Override
        public long setObject(String key, Object obj) {
            long ret = -1;
            try {
                this.redisTemplate.opsForValue().set(key, obj);
                ret = this.redisTemplate.opsForValue().get(key) == null
                        || "".equals(this.redisTemplate.opsForValue().get(key)) ? -1 : 1;
            } catch (Exception e) {
                log.error(e.getMessage());
                e.printStackTrace();
            }
            return ret;
        }
    
        @Override
        public long getObject(String key, Object obj) {
            // TODO Auto-generated method stub
            return 0;
        }
    
    }

    實現方法我們也可以靈活的定義。

  • Controller測試

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.druid.support.json.JSONUtils;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import com.impact.framework.redis.template.service.RedisServiceImpl;
    import com.impact.framework.redis.vo.RedisVo;
    
    @Controller
    @RequestMapping("/redis")
    public class RedisController {
    
        @Autowired
        private RedisServiceImpl redisServiceImpl;
    
        @GetMapping("addStr")
        @ResponseBody
        public String addRedisKV(){
            boolean flag = false;
            try {
                flag = redisServiceImpl.set("RedisKey", "RedisValue");
            } catch (Exception e) {
                e.printStackTrace();
            }
            return "儲存K-V狀況: "+flag;
        }
    
        @GetMapping("addObj")
        @ResponseBody
        public void addRedisObject(){
            JSONObject obj = new JSONObject();
            try {
                RedisVo vo = new RedisVo("Redis",185);
                redisServiceImpl.setObject("RedisVO1", vo);
    
                obj.put("vo", (JSON) JSONObject.toJSON(redisServiceImpl.get("RedisVO1")));
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            JSONUtils.toJSONString(obj);
        }
    
        @GetMapping("/getObj/{key}")
        @ResponseBody
        public Object getObjectBuKey(@PathVariable("key") String key){
            Object result = null;
            try {
                result  = redisServiceImpl.get(key);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return result;
        }
    }

    當然在測試之前我們需要把Redis的Service 拉起來。

相關推薦

SpringBoot構建微服務實 整合Redis

前言 本節我們將學習一下SpringBoot 如何整合單機版Redis 概述 首先我得陳述一下,本節學習的SpringBoot 整合R單機版Redis總共有如下步驟。 SpringBoot 初步整合 Spring-data-redis 首先

SpringBoot構建微服務實 熱部署(二)

在使用SpringBoot熱部署時可能會遇到一些異常比如熱部署設定完成生效之後,專案重啟會遇到快取重複存在的問題。 在部署時會遇見一下異常: The source of the existing CacheManager is: InputStreamConfigurati

SpringBoot構建微服務實 熱部署(-)

本小結主要學習下Spring boot實現熱部署的兩種方式,這兩種方法分別是使用 Spring Loaded和使用spring-boot-devtools進行熱部署。 什麼時熱部署 大家都知道在專案開發過程中,常常會改動頁面資料或者修改資料結構,為了顯示改動效果,往往需要重

微服務實高可用性

高可用性指你提供的服務要始終可用, 不管天災(停電, 斷網, 磁碟空間滿, 伺服器硬體損壞等), 人禍(軟體bug, 黑客破壞, 誤操作等), 甚至地震, 洪水抑或戰爭. 高可性性的指標就是可用時間與總時間之比 availability = uptime/(uptime + down

SpringCloud微服務實分散式服務跟蹤Sleuth

通常一個由客戶端發起的請求會在後端系統中經過多個不同的微服務呼叫來協同產生最後的請求結果,在複雜的微服務架構系統中,幾乎每一個前端請求都會形成一條複雜的分散式服務呼叫鏈路,每條鏈路服務痴線錯誤或者延遲都有可能引起請求的失敗。Spring Cloud Sle

SpringCloud微服務實配置中心Config

Spring Cloud Config是Spring Cloud團隊建立的一個全新專案,用來為分散式系統中基礎設施和微服務應用提供集中化的外部配置支援,它分為服務端和客戶端兩個部分。 一、服務端配置 1、建立SpringCloud-Config專案,

SpringCloud微服務實服務治理Eureka(單節點)

Euraka是NetFlix微服務套件中的一部分,它基於Netflix Eureka做了二次封裝,主要負責完成微服務架構中的服務治理和服務發現功能。 示例: 一、Eureka搭建服務註冊 1、建立maven專案SpringCloud-Eurek

.Net微服務實技術選型篇

王者榮耀    去年我有幸被老領導邀請以系統架構師的崗位帶技術團隊,並對公司專案以微服務進行了實施。無論是技術團隊還是技術架構都是由我親自的從0到1的選型與招聘成型的,此過程讓我受益良多,因此也希望在接下來的系列博文儘可能的與大家分享我的經驗。   古人有云:將軍難打無兵之仗。想要把微服務很好的實施也並非能一

.Net微服務實技術架構分層篇

一拍即合   上一篇《.Net微服務實戰之技術選型篇》,從技術選型角度講解了微服務實施的中介軟體的選擇與協作,工欲善其事,必先利其器,中介軟體的選擇是作為微服務的基礎與開始,也希望給一直想在.Net入門微服務的同行有一個很好的方向。在此篇重新整理了一下整個微服務專案的demo,希望對有需要的朋友起到一定的幫助

.Net微服務實負載均衡(上)

系列文章 .Net微服務實戰之技術選型篇 .Net微服務實戰之技術架構分層篇 .Net微服務實戰之DevOps篇  相關原始碼:https://github.com/SkyChenSky/Sikiro PS:最近開始在找工作,如果在廣州地區需要技術管理崗的(.Net架構師、技術經理)

.Net微服務實必須得面對的分散式問題

系列文章 .Net微服務實戰之技術選型篇 .Net微服務實戰之技術架構分層篇 .Net微服務實戰之DevOps篇 .Net微服務實戰之負載均衡(上) .Net微服務實戰之CI/CD .Net微服務實戰之Kubernetes的搭建與使用 .Net微服務實戰之負載均衡(下)  相關原始碼:https

SpringCloud微服務實——第二章Springboot

java with 當前 tom 參數 請求 bubuko zha 格式 Spring Boot項目工程      src/main/java:主程序入口HelloApplication,可以通過直接運行該類來啟動Spring Boot應用。   src/main/reso

《Spring Cloud微服務實》讀書筆記客戶端負載均衡:Spring Cloud Ribbon - 4

摘要 客戶端負載均衡元件部署在客戶端,由客戶端維護要訪問的服務端清單(清單來源於服務註冊中心)。在Spring Cloud 中預設使用Ribbon作為客戶端負載均衡。 關鍵詞:客戶端負載均衡,Ribbon 一、什麼是客戶端負載均衡 負載均衡是對系統高可用、緩解網路壓力、處理能力擴容的重要手段之一。通常

《Spring微服務實》讀書筆記——構建微服務

設計微服務架構 構建程式碼的腳手架 分解業務問題 描述業務問題,安裝名詞來描述問題 注意動詞 尋找資料內聚性 確定服務粒度 使用資料模型作為將單體應用分解到微服務的基礎。 可以通過下面的概念來確定正確的服務粒度: 廣泛的使用微

《Spring Cloud微服務實》讀書筆記基礎知識1

摘要 微服務是一種系統架構的設計風格,它主旨在於將一個原本獨立的系統,拆分成多個獨立執行的小型服務。不同服務之間通過Restful介面進行通訊協作。 關鍵詞:Spring Cloud,微服務 一、什麼是微服務 微服務是一種系統架構的設計風格,它主旨在於將一個原本

SpringBoot整合redis實現快取

主要程式碼: String key = "teacher_"+id; boolean hasKey = redisTemplate.hasKey(key); ValueOperations<String,Teacher> o

《Spring Cloud微服務實》讀書筆記服務治理:Spring Cloud Eureka

摘要 服務治理是微服務架構最為核心和基礎的模組,用於實現各個微服務例項的自動化註冊與發現。Spring Cloud Eureka 是對Netflix Eureka的二次封裝,負責服務的治理。 關鍵詞:服務治理 一、服務治理介紹 服務治理是微服務架構最為核心和基礎

《Spring Cloud微服務實》讀書筆記客戶端負載均衡:Spring Cloud Ribbon

摘要 客戶端負載均衡元件部署在客戶端,由客戶端維護要訪問的服務端清單(清單來源於服務註冊中心)。在Spring Cloud 中預設使用Ribbon作為客戶端負載均衡。 關鍵詞:客戶端負載均衡,Ribbon 一、什麼是客戶端負載均衡 負載均衡是對系統高可用、緩解網

SpringBoot整合Redis分析和實現-基於Spring Boot2.0.2版本

背景介紹 公司最近的新專案在進行技術框架升級,基於的Spring Boot的版本是2.0.2,整合Redis資料庫。網上基於2.X版本的整個Redis少之又少,中間踩了不少坑,特此把整合過程記錄,以供小夥伴們參考。 本文的基於在於會搭建Spring Boot專

Spring cloud微服務實(二)——Zuul整合Swagger2及許可權校驗

一、前言 ##二、 整合Swagger2 Swagger2大家肯定都用過,為什麼我在這裡還要提到呢,因為各個微服務都會提供自己的API文件,我們總不能一個地址一個地址去查吧,能不能有個統一的入口呢。這就是這節要講的。 2.1 Zuul整合Swagger2 其他