• SpringCloud+Eureka+Feign+Ribbon的简化搭建流程,加入熔断,网关和Redis缓存[2]


    作者:故事我忘了
    个人微信公众号:程序猿的月光宝盒

    [toc] ## 前提:本篇是基于

    SpringCloud+Eureka+Feign+Ribbon的简化搭建流程和CRUD练习[1]

    的修改与拓展


    1.修改consumerCenterFeign.java,把返回值全部设置为String

    /**
     * 是consumer调用provider(需要指定provider的名字)
     * 请求的清单列表:规定调用地址、参数、返回值
     * 在正常走通的时候不走CenterFeignFallBack,当provider down的时候走熔断器,相当于是类的try-catch
     */
    @FeignClient(name = "provider",fallback = CenterFeignFallBack.class)
    public interface CenterFeign {
        @GetMapping("/optionData.do")
        public String optionData();
    
        @PostMapping("/showEmpData.do")
        //Feign:不支持对象传参,所以要用@RequestBody
        public String showEmpData(@RequestBody Emp emp);
    
        @PostMapping("/add.do")
        public String add(@RequestBody Emp emp);
    
        @PostMapping("/edit.do")
        public String edit(@RequestBody  Emp emp);
    
        @GetMapping("/del.do")
        public String del(@RequestParam("empno") Integer empno);
    }
    

    2.在CenterFeign.java的同包下创建实现了CenterFeign接口的CenterFeignFallBack.java作为熔断器

    @Component
    public class CenterFeignFallBack implements CenterFeign {
        @Override
        public String optionData() {
            return "provider-optionData-error";
        }
    
        @Override
        public String showEmpData(Emp emp) {
            return "provider-showEmpData-error";
        }
    
        @Override
        public String add(Emp emp) {
            return "provider-add-error";
        }
    
        @Override
        public String edit(Emp emp) {
            return "provider-edit-error";
        }
    
        @Override
        public String del(Integer empno) {
            return "provider-del-error";
        }
    }
    

    3.修改consumerCenterController.java,返回值全改为String

    @RestController
    public class CenterController{
        @Resource
        private CenterFeign centerFeign;
    
        @GetMapping("/optionData-consumer.do")
        public String optionData() {
            return centerFeign.optionData();
        }
    
        @PostMapping("/showEmpData-consumer.do")
        public String showEmpData(@RequestBody Emp emp) {
            return centerFeign.showEmpData(emp);
        }
    
        @PostMapping("/add-consumer.do")
        public String add(@RequestBody Emp emp) {
            return centerFeign.add(emp);
        }
    
        @PostMapping("/edit-consumer.do")
        public String edit(@RequestBody Emp emp) {
            return centerFeign.edit(emp);
        }
    
        @GetMapping("/del-consumer.do")
        public String del(@RequestParam("empno") Integer empno) {
            return centerFeign.del(empno);
        }
    }
    

    4.确认consumer中的配置文件application.properties中有没有开启熔断feign.hystrix.enabled=true,没有的话加上

    #端口号
    server.port=8764
    #应用名
    spring.application.name=consumer
    #eureka客户端服务url默认区域
    eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
    #开启熔断器
    feign.hystrix.enabled=true
    #下线名称.ribbon.NF加载平衡规则类名,这里先注释
    provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule
    

    5.开启服务测试

    访问http://localhost:8761/

    发现所有服务已经注册

    图片

    正常走provider没有问题

    图片


    图片

    图片

    走consumer也没有问题,只是返回的格式是字符串类型,不是json类型,但是没关系,格式还是json的格式,前台该怎么转还能怎么转,不影响

    图片

    那怎么走容错?

    现在手动停止一个provider

    图片

    因为这里我开了负载均衡策略(在consumer中的配置文件中provider.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RandomRule),所以有一定几率触发熔断器,

    图片

    这就相当于类之间的try-catch,没有熔断器的话这里百分百是报错误代码.那这里我先把负载均衡关掉,在测试有没有走容错,(猜一下,会走吗?)

    图片

    图片

    这里我测了这么多次,都没有走熔断,所以显然不走,为何?

    因为没有手动设置负载均衡策略的话,默认走的是轮询.机制,啥是Ribbon轮询机制?

    简单的说就是有abc三台服务器,正常的情况下走a->b->c,在有一台down了的时候,那台就自动跳过,比如b down了,那么就是a->c

    以上,个人理解,若有误请指正,よろしくお願いします~


    现在,既然一台服务器工作是没有问题 那我两台provider全部停止呢?

    图片

    那答案是肯定的,绝壁走容错~~

    图片

    6.测试完没有问题,现在添加网关功能模块

    新建模块(Spring initializr)zuul-client

    图片

    图片

    7.编辑配置文件application.properties

    #网关端口
    server.port=8765
    #应用名
    spring.application.name=zuul
    #网关路径提供者,后面的**表示:xxx.do
    zuul.routes.provider=/pro/**/
    #网关路径消费者,后面的**表示:xxx.do
    zuul.routes.consumer=/con/**/
    #网关走熔断机制
    #ribbon超时设置
    ribbon.ReadTimeout=30000
    ribbon.ConnectTimeout=30000
    

    8.在启动类上添加注解

    //开启网关代理
    @EnableZuulProxy
    //开启eureka客户端
    @EnableEurekaClient
    @SpringBootApplication
    public class ZuulClientApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(ZuulClientApplication.class, args);
        }
    
    }
    

    9.开启服务测试

    图片


    测试provider

    图片

    测试consumer

    图片

    10.网关作用

    统一了所有客户端的ip地址和端口号,我们只要给不同层级的应用起别名就ok了(就是这里的图片)

    11.加入Redis缓存

    11.1在provider-one模块的pom文件中加入redis依赖

     <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    11.2修改provider-one模块的DeptServiceImpl文件,加入 @Autowired private RedisTemplate redisTemplate;

    @Service
    public class DeptServiceImpl implements DeptService {
        @Autowired
        private DeptMapper deptMapper;
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        public Map<String, Object> optionData() {
            Map<String, Object> map=new HashMap<>();
            List<Map<String, Object>> list = deptMapper.selAllDeptData();
            // 定义Redis存储集合的对象
            ListOperations<String,Map<String,Object>> redisList = redisTemplate.opsForList();
            //拼接Redis中存储数据对应的key
            String key = "depts";
            //判断Redis中是否有key,没有就说明是第一次访问,将数据放入Redis
            if(!redisTemplate.hasKey(key)){
                //直接将数据库查询出来的值放入Redis
                System.out.println("provider-one-optionData的值已经放入Redis");
                redisList.leftPushAll(key,list);
            }
            map.put("data",list);
            return map;
        }
    }
    

    11.3对应的 修改DeptService,返回值变成map

    public interface DeptService {
        Map<String,Object> optionData();
    }
    

    11.4修改CenterController,把返回值类型改为Map

        public Map<String, Object> optionData() {
            return deptService.optionData();
        }
    
    

    11.5同样的在provider-two的pom.xml中加入redis依赖

    <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    

    11.6修改provider-two模块的DeptServiceImpl文件,加入 @Autowired private RedisTemplate redisTemplate;

    @Service
    public class DeptServiceImpl implements DeptService {
        @Autowired
        private DeptMapper deptMapper;
        @Autowired
        private RedisTemplate redisTemplate;
    
        @Override
        public Map<String, Object> optionData() {
            Map<String, Object> map=new HashMap<>();
            List<Map<String, Object>> list = deptMapper.selAllDeptData();
            // 定义Redis存储集合的对象
            ListOperations<String,Map<String,Object>> redisList = redisTemplate.opsForList();
            //拼接Redis中存储数据对应的key
            String key = "depts";
            //判断Redis中是否有key,没有就说明是第一次访问,将数据放入Redis
            if(!redisTemplate.hasKey(key)){
                //直接将数据库查询出来的值放入Redis
                System.out.println("provider-two-optionData的值已经放入Redis");
                redisList.leftPushAll(key,list);
            }
            map.put("data",list);
            return map;
        }
    }
    

    11.7对应的 修改DeptService,返回值变成map

    public interface DeptService {
        Map<String,Object> optionData();
    }
    

    11.8修改CenterController,把返回值类型改为Map

        public Map<String, Object> optionData() {
            return deptService.optionData();
        }
    

    11.9更新provider-one模块的配置文件application.properties,加入Redis配置

    #服务端口号
    server.port=8762
    #应用名
    spring.application.name=provider
    #eureka客户端服务url默认区域
    eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
    
    #数据源驱动类名
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    #数据源url
    spring.datasource.url=jdbc:mysql:///kh75
    #数据源用户名
    spring.datasource.username=root
    #数据源密码
    spring.datasource.password=admin
    
    
    
    #后期会写mapper.xml,这里先注释
    #mybatis.mapper-locations=classpath:mapper/*.xml
    
    #给实体类起别名,同样这里先注释
    #mybatis.type-aliases-package=cn.kgc.vo
    
    #配置Redis
    spring.redis.port=6379
    #redis所在的主机地址
    spring.redis.host=xxx.xxx.xxx
    spring.redis.database=0
    

    11.10更新provider-two模块的配置文件application.properties,加入Redis配置

    #服务端口号
    server.port=8763
    #应用名
    spring.application.name=provider
    #eureka客户端服务url默认区域
    eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
    
    #数据源驱动类名
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    #数据源url
    spring.datasource.url=jdbc:mysql:///kh75
    #数据源用户名
    spring.datasource.username=root
    #数据源密码
    spring.datasource.password=admin
    
    
    
    #后期会写mapper.xml,这里先注释
    #mybatis.mapper-locations=classpath:mapper/*.xml
    
    #给实体类起别名,同样这里先注释
    #mybatis.type-aliases-package=cn.kgc.vo
    #配置Redis
    spring.redis.port=6379
    #redis所在的主机地址
    spring.redis.host=xxx.xxx.xx
    spring.redis.database=0
    

    11.11更新consumer的CenterFeign,所有返回值都是map

    @FeignClient(name = "provider",fallback = CenterFeignFallBack.class)
    public interface CenterFeign {
        @GetMapping("/optionData.do")
         Map<String,Object> optionData();
    
        @PostMapping("/showEmpData.do")
        //Feign:不支持对象传参,所以要用@RequestBody
        Map<String,Object> showEmpData(@RequestBody Emp emp);
    
        @PostMapping("/add.do")
        Map<String,Object> add(@RequestBody Emp emp);
    
        @PostMapping("/edit.do")
        Map<String,Object> edit(@RequestBody  Emp emp);
    
        @GetMapping("/del.do")
        Map<String,Object> del(@RequestParam("empno") Integer empno);
    }
    

    11.12更新CenterFeignFallBack

    @Component
    public class CenterFeignFallBack implements CenterFeign {
        private Map<String, Object> map = new HashMap<> ();
        @Autowired
        private RedisTemplate redisTemplate;
        @Override
        public Map<String,Object> optionData() {
             ListOperations<String,Map<String,Object>> redisList = redisTemplate.opsForList();
            map.put("msg","provider-optionData-error");
            map.put("feign-data",redisList.range("depts",0,-1));
            return map;
        }
    
        @Override
        public Map<String,Object> showEmpData(Emp emp) {
             ListOperations<String,Map<String,Object>> redisList = redisTemplate.opsForList();
            map.put("msg","provider-showEmpData-error");
            //这里对应的serviceImp里面还没修改成Redis,先放着
            map.put("feign-data",redisList.range("emps",0,-1));
            return map;
        }
    
        @Override
        public Map<String,Object> add(Emp emp) {
            map.put("msg","provider-add-error");
            return map;
        }
    
        @Override
        public Map<String,Object> edit(Emp emp) {
            map.put("msg","provider-edit-error");
            return map;
        }
    
        @Override
        public Map<String,Object> del(Integer empno) {
            map.put("msg","provider-del-error");
            return map;
        }
    }
    

    11.13再修改consumer中的CenterController,返回值也全改成Map

    @RestController
    public class CenterController{
        @Resource
        private CenterFeign centerFeign;
    
        @GetMapping("/optionData-consumer.do")
        public Map<String,Object> optionData() {
            return centerFeign.optionData();
        }
    
        @PostMapping("/showEmpData-consumer.do")
        public Map<String,Object> showEmpData(@RequestBody Emp emp) {
            return centerFeign.showEmpData(emp);
        }
    
        @PostMapping("/add-consumer.do")
        public Map<String,Object> add(@RequestBody Emp emp) {
            return centerFeign.add(emp);
        }
    
        @PostMapping("/edit-consumer.do")
        public Map<String,Object> edit(@RequestBody Emp emp) {
            return centerFeign.edit(emp);
        }
    
        @GetMapping("/del-consumer.do")
        public Map<String,Object> del(@RequestParam("empno") Integer empno) {
            return centerFeign.del(empno);
        }
    }
    

    11.14检查consumer的pom文件里是否有加入Redis依赖,没有加上...

    11.15 打开所有模块进行测试

    图片

    图片

    图片

    图片

    图片

    图片

    把提供者断掉,理论上走熔断和redis数据

    图片

    图片

    以上....

  • 相关阅读:
    kettle-学习参考
    spring retry 重试机制完整例子
    一个四五年的Java开发程序员,该准备哪些去面试?
    Java之io nio aio 的区别
    Java对象的存活判断
    Java+微信支付(下预购单+回调+退款+查询账单)
    Java +支付宝 +接入
    关于MQ 消息队列的通俗理解和 rabbitMQ 使用
    java 对接芝麻信用 -用芝麻私钥解密错误
    mysql 乐观判断 校验
  • 原文地址:https://www.cnblogs.com/jsccc520/p/12117304.html
Copyright © 2020-2023  润新知