微服务项目中众多服务需要集成Feign和Redis的解决办法

本文介绍了一个微服务项目中如何有效地集成Feign和Redis,通过创建独立的redis-server模块,避免重复代码,提升性能。文章详细阐述了集成步骤,包括创建RedisUtils工具类、配置RedisController,以及在其他服务中通过Feign调用Redis实现缓存。同时,讨论了如何在数据操作后更新Redis缓存,确保数据一致性。
摘要由CSDN通过智能技术生成

初始想法

在一个完整的项目中,可能有许多微服务需要集成Feign和Redis来提高效率。
我们正常的想法就是,哪个服务需要就去那个服务集成,这样我们会发现那样不好,大量的重复代码,造成了性能下降,也不便于管理

有要重复使用的代码,抽取成公共方法;
有重复使用的类,抽取成为公共类;
微服务也一样
总结一个字--

我们将Redis和Feign抽取出来,成为一个redis-server模块。需要集成那么直接依赖redis-server服务即可

原理图

在这里插入图片描述Redis是需要用Tomcat跑起来的,所以需要添加端口;而feign则不需要

那么有一个问题!

Feign配置中需要自定义接口,那微服务怎么使用Feign呢?

真正的是微服务(如课程服务)集成feign,我们只是抽取接口成为公共模块
配置类上打feign客户端标签@EnableFeignClients(“填接口所在包”) 开启feign即可 (哪个服务需要谁就开启)

集成步骤

  • 注册到Eureka注册中心
  • 集成Config配置中心客户端
  • 集成Jedis操作Redis
  • zuul配置微服务的路由

步骤略过,这里有详细集成步骤
Eureka集成和集群:
https://blog.csdn.net/weixin_45561263/article/details/104347501
Ribbon负载均衡器/Feign/Hystrix断路器/Zuul网关/config 分布式配置:
https://blog.csdn.net/weixin_45561263/article/details/104349726

以我自己练习的项目来集成Redis

redis-server模块中准备RedisUtils工具类



import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.IOException;
import java.util.Properties;

/**
 * 获取连接池对象
 */
public enum RedisUtils {
    INSTANCE;
    static JedisPool jedisPool = null;

    static {
        //1 创建连接池配置对象
        JedisPoolConfig config = new JedisPoolConfig();
        //2 进行配置-四个配置
        config.setMaxIdle(1);//最小连接数
        config.setMaxTotal(11);//最大连接数
        config.setMaxWaitMillis(10 * 1000L);//最长等待时间
        config.setTestOnBorrow(true);//测试连接时是否畅通
        //3 通过配置对象创建连接池对象
        Properties properties = null;
        try {
            properties = new Properties();
            properties.load(RedisUtils.class.getClassLoader().getResourceAsStream("redis.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String host = properties.getProperty("redis.host");
        String port = properties.getProperty("redis.port");
        String password = properties.getProperty("redis.password");
        String timeout = properties.getProperty("redis.timeout");

        jedisPool = new JedisPool(config, host, Integer.valueOf(port),Integer.valueOf(timeout), password);
    }

    //获取连接
    public Jedis getSource() {
        return jedisPool.getResource();
    }

    //关闭资源
    public void closeSource(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }

    }

    /**
     * 设置字符值
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        Jedis jedis = getSource();
        jedis.set(key, value);
        closeSource(jedis);
    }

    /**
     * 设置
     * @param key
     * @param value
     */
    public void set(byte[] key, byte[] value) {
        Jedis jedis = getSource();
        jedis.set(key, value);
        closeSource(jedis);
    }

    /**
     *
     * @param key
     * @return
     */
    public byte[]  get(byte[] key) {
        Jedis jedis = getSource();
        try {
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeSource(jedis);
        }
        return null;

    }

    /**
     * 设置字符值
     *
     * @param key
     */
    public String get(String key) {
        Jedis jedis = getSource();
        try {
            return jedis.get(key);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            closeSource(jedis);
        }

        return null;

    }
}

在resources文件夹中创建redis.properties文件

redis.host=127.0.0.1
redis.port=6379
redis.password=123456
redis.timeout=10000

编写RedisController:实现set,和get方法



import cn.itsource.hrm.util.AjaxResult;
import cn.itsource.redis.utils.RedisUtils;
import org.springframework.web.bind.annotation.*;

@RequestMapping("/redis")
@RestController
public class RedisController {

   
    @PostMapping("/set")//  从请求中获取值,将值设置给对应参数key
    public AjaxResult set(@RequestParam("key") String key,@RequestParam("value") String value){
        RedisUtils.INSTANCE.set(key,value );
        return AjaxResult.me();
    }

    
	@GetMapping("/get/{key}")//PathVariable:将参数key值设置给uri中的key
    public AjaxResult get(@PathVariable("key") String key){
        String result = RedisUtils.INSTANCE.get(key);
        //通过setResultObj方法返回到前台对象
        return AjaxResult.me().setResultObj(result);
    }

}

课程集成Feign实现课程缓存

  • 依赖Feign的模块
<dependency>
                <groupId>cn.itsource</groupId>
                <artifactId>hrm-redis-feign</artifactId>
                <version>1.0-SNAPSHOT</version>
            </dependency>
  • 配置类开启Feign
@EnableFeignClients("cn.xxx.redis.feignclients")//开启服务集成feign

通过Redis实现课程缓存(具体思路和步骤)

  1. 查询课程分类的时候先查询Redis
  2. 如果Redis有就组装数据,直接返回(使用Fastjson操作json,即string和json对象之间的转换)
  3. 如果Reids没有就从Mysql中查
  4. Mysql查到之后同步一份到Redis
  5. 返回结果

缓存更新
思路:
在添加,修改,删除的时候重置缓存
即操作完数据之后同步一份到Redis

具体实现代码

/**
 * <p>
 * 课程目录 服务实现类
 * </p>
 *
 * @author rjm
 * @since 2020-02-17
 */
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {

   @Autowired
   private RedisFeignClient redisFeignClient;

   //操作也使用Redis
    //controller用什么方法就覆写什么方法
    //覆写删除、修改、添加方法
   @Override
   public boolean insert(CourseType entity) {
       //先对数据库执行操作
       boolean result  = super.insert(entity);
       //重置Redis
       resetRedis();
       return result;
   }

    @Override
    public boolean deleteById(Serializable id) {
        boolean result= super.deleteById(id);
        //重置Redis
        resetRedis();
        return result;
    }


    @Override
    public boolean updateById(CourseType entity) {
        boolean result = super.updateById(entity);
        //重置Redis
        resetRedis();
        return result;
    }

    //自定义重置Redis的方法
    public List<CourseType> resetRedis(){
       
        //Redis没数据,从数据库中查询
        List<CourseType> courseTypes = baseMapper.selectList(null);
        //将courseTypes数据转换成string类型,同步一份到Redis
        redisFeignClient.set("course_type", JSON.toJSONString(courseTypes));
        
        return courseTypes;
    }


    @Override
    public List<CourseType> treeData() {

        //查询使用Redis


        //集成feign使用Redis
        List<CourseType> courseTypes = null;
        //先查询Redis,
        AjaxResult result = redisFeignClient.get("course_type");
        if (result.isSuccess() && result.getResultObj() != null) {//存在数据且不为空
            //获取json的数据字符串
            String coursetypes = result.getResultObj().toString();
            //返回CourseType类型的数据
            courseTypes = JSON.parseArray(coursetypes, CourseType.class);
        } else {
            courseTypes = resetRedis();
        }

        //先查所有课程分类   wrapper:条件
        //在通用impl里有basemapper,上面泛型放什么就是谁的basemapper,直接调用里面的方法
//        List<CourseType> courseTypes = baseMapper.selectList(null);
/*---------------下面是获取课程多级分类的代码-------------------*/
        //准备集合装所有 顶级父亲
        List<CourseType> topLevelParentCourseTypes = new ArrayList<>();
        //遍历所有课程分类
        for (CourseType courseType : courseTypes) {
            //判断pid,为0即为顶级父亲
            //Java里面定义的id是Long包装类型,转成long来和0比较是最准确的
            if(courseType.getPid().longValue() == 0){
                //将顶级课程分类放入集合中的CourseType中
                topLevelParentCourseTypes.add(courseType);
            } else {//非顶级课程分类,即pid不为0
                //遍历非顶级课程分类
                //核心思路:各自找各自的父,找到后装进各自父课程类型中的children中
                for (CourseType currentCourseTypes : courseTypes) {
                    //当前分类的id和某个分类的pid相等那么这一对就是父子
                    if(currentCourseTypes.getId().longValue() == courseType.getPid().longValue()){
                        //父  currentCourseTypes       子  courseType
                        //将子装进父中children里
                        currentCourseTypes.getChildren().add(courseType);
                        //成功找到一对父子并装好结构,结束该循环。
                        break;
                    }
                }
            }
            //break后,开始下一次循环找其他的父子并组装
        }
        //返回已经组装好的结构
        return topLevelParentCourseTypes;
    }


}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值