欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

B083-SpringCloud-eureka ribbon feign hystrix

最编程 2024-07-17 16:13:10
...

目录

      • eureka
        • 基础项目准备
        • 注册中心的搭建
        • 生产者注册到eureka
        • 消费者注册到eureka并通过eureka调用生产者
        • eureka集群
      • 服务提供者集群
      • 集群以后消费者调用服务的问题
      • ribbon
        • 消费者使用ribbon负载均衡
        • 赋值负载均衡策略
        • 负载均衡优化
      • feign
      • Hystrix
        • Hystrix概述
        • Ribbon搭配Hystrix降级处理
        • Feign搭配Hystrix降级处理

eureka

基础项目准备

搭建未用到springcloud的微服务架构,同昨天
浏览器访问测试:http://localhost:5010/user/consumer/8

注册中心的搭建

同昨天(新建工程 导包 配置 启动类 注解)
浏览器访问测试:http://localhost:1010

生产者注册到eureka

同昨天(导包 配置 注解)
重启生产者,浏览器访问测试:http://localhost:4010/user/provider/3
tips:将eureka页面中显示的实例服务的计算机名称改为ip
在这里插入图片描述

消费者注册到eureka并通过eureka调用生产者

同昨天(导包 配置 注解)

UserConsumerController

/**
 * 服务的消费者
 */
@RestController
@RequestMapping("/user/consumer")
public class UserConsumerController {

//    private static String URL_PRIFIX = "http://localhost:4010";

    @Autowired
    private RestTemplate restTemplate;  // 用于发起远程调用的工具

    @Autowired
    private DiscoveryClient client;

    // 返回一个用户
    @RequestMapping("/{id}")
    public User getUser(@PathVariable("id") Long id){

        List<ServiceInstance> list = client.getInstances("user-provider");
        // 获取第一台机器
        ServiceInstance serviceInstance = list.get(0);
        // 获取第一台机器的ip
        String ip = serviceInstance.getHost();
        // 获取第一台机器的端口
        int port = serviceInstance.getPort();
        String url = "http://"+ip+":"+port+"/user/provider/" + id;

        // 根据id发起远程调用4010获取用户
//        return restTemplate.getForObject(URL_PRIFIX + "/user/provider/" + id,User.class);
        return restTemplate.getForObject(url,User.class);
    }
}

重启消费者,浏览器访问测试:http://localhost:5010/user/consumer/44

eureka集群

在这里插入图片描述
防止eureka单点故障
见文档6

application.yml备份一份单机版本,复制多份集群配置版本
如application-eureka1.yml

server:
  port: 1010
eureka:
  instance:
    hostname: eureka1
  client:
    registerWithEureka: false #是否要注册到eureka
    fetchRegistry: false #表示是否从Eureka Server获取注册信息
    serviceUrl:
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1020/eureka,http://eureka3:1030/eureka
spring:
  application:
    name: eureka1

外网无法访问http://eureka1:1010/eureka,需要本机配置虚拟域名
C:\Windows\System32\drivers\etc\hosts
在这里插入图片描述
关闭所有项目,修改application.yml

spring:
  profiles:
    active: eureka1

设置idea编辑该启动类配置支持多实例启动
在这里插入图片描述
active分别写eureka1、eureka2、eureka3启动后会找到以对应后缀的文件共同启动
浏览器访问测试:http://localhost:1010,http://localhost:1020,http://localhost:1030
在这里插入图片描述
设置把自己也注册进去
删掉或注释以下两行即可

#    registerWithEureka: false #是否要注册到eureka
#    fetchRegistry: false #表示是否从Eureka Server获取注册信息

active分别写eureka1、eureka2、eureka3启动后会找到以对应后缀的文件共同启动
tips:前两个启动报错正常,因为配置里的其他eureka还没启动,不用管,最后一个启动就不会报错了
浏览器访问测试:http://localhost:1010,http://localhost:1020,http://localhost:1030
在这里插入图片描述

服务提供者集群

在这里插入图片描述
生产环境把jar包部署多个服务器就ok了,但是现在是开发阶段同一台主机不同端口号来代替服务器 ,方案有两种。
方案1:拷贝一份代码为多份,修改端口不一样。分别启动
方案2:一份代码,多份配置。 每一个配置启动一份。
我们这里用第二种

application.yml备份一份单机版本,复制多份集群配置版本
如application-pro1.yml

server:
  port: 4010
spring:
  application:
    name: user-provider
eureka:
  client:
    service-url:
      defaultZone: http://eureka1:1010/eureka,http://eureka2:1020/eureka,http://eureka3:1030/eureka #告诉服务提供者要把服务注册到哪儿
  instance:
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
    hostname: user-provider:4010

修改application.yml

spring:
  profiles:
    active: pro1

设置idea编辑该启动类配置支持多实例启动
active分别写pro1、pro2、pro3启动后会找到以对应后缀的文件共同启动
浏览器访问测试:http://localhost:1010
在这里插入图片描述
tips:spring application name一样就会单行一起展示,不一样就会多行分别展示
启动消费者服务,浏览器访问测试:http://localhost:5010/user/consumer/55

集群以后消费者调用服务的问题

在这里插入图片描述

ribbon

消费者使用ribbon负载均衡

见文档7.1,7.3,7.4.1,7.4.2

拷贝user-consumer-5010为user-consumer-ribbon-5110
user-consumer-ribbon-5110的pom里修改artifactId

<artifactId>user-consumer-ribbon-5110</artifactId>

父pom里增加

<module>user-consumer-ribbon-5110</module>

刷新Maven
application.yml调整端口

server:
  port: 5110
spring:
  application:
    name: user-consumer
eureka:
  client:
    service-url:
      defaultZone: http://localhost:1010/eureka #告诉服务提供者要把服务注册到哪儿
  instance:
    prefer-ip-address: true # 当调用getHostname获取实例的hostname时,返回ip而不是host名称
    ip-address: 127.0.0.1 # 指定自己的ip信息,不指定的话会自己寻找
    hostname: user-consumer:5110

删除target文件夹

导包

<!--客户端负载均衡实现 ribbon-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>

获取RestTemplate加注解

@Configuration
public class HttpUtils {

    @Bean
    @LoadBalanced //开启ribbon的负载均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

修改UserConsumerController

@RestController
@RequestMapping("/user/consumer")
public class UserConsumerController {

    private static String URL_PRIFIX = "http://USER-PROVIDER";

    @Autowired
    private RestTemplate restTemplate;  // 用于发起远程调用的工具

    @Autowired
    private DiscoveryClient client;

    // 返回一个用户
    @RequestMapping("/{id}")
    public User getUserById(@PathVariable("id") Long id){

        // 开启ribbon的负载均衡,使用服务名字调用
        User user = restTemplate.getForObject(URL_PRIFIX + "/user/provider/" + id, User.class);
        return user;
    }
}

服务提供者加代码以方便看是哪一个提供者提供的服务
UserProviderController

@RestController
@RequestMapping("/user/provider")
public class UserProviderController {

    @Autowired
    private ApplicationContext context;

    // 返回一个用户
    @RequestMapping("/{id}")
    public User getUser(@PathVariable("id") Long id) {
        // 为了方便看是哪一个提供者提供的服务
        String activeProfile = context.getEnvironment().getActiveProfiles()[0];
        return new User(id, "zs," + activeProfile);
    }
}

分别启动eureka集群,生产者集群和用了ribbon的消费者服务
浏览器访问测试:http://localhost:5110/user/consumer/5110,不断刷新会出现轮询效果

赋值负载均衡策略

见文档7.4.4

内置策略配置bean

@Configuration
public class HttpUtils {

    @Bean
    @LoadBalanced //开启ribbon的负载均衡
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }

    // 配置负载均衡策略,即返回一个IRule对象交给spring管理
    @Bean
    public IRule getRule(){
        // 随机选择一个可用的服务器的策略
        return new RandomRule();
    }
}

自定义策略重写IRule接口实现类重写对应方法

浏览器刷新访问测试:http://localhost:5110/user/consumer/5110

负载均衡优化

见文档7.4.5

小坑:okToRetryOnAllOperations: false,是否所有操作都重试
防止调用方使用ribbon请求超时但超时时间后被调用服务又处理成功,然后调用方认为未成功又重复请求的情况
对于添加数据请求数据库有做幂等性校验就没问题,即対某列添加唯一性约束,重复数据不会添加成功

feign

见文档7.5.1

拷贝user-consumer-ribbon-5110为user-consumer-feign-5210
修改artifactId
父pom添加module
修改application.yml
删除target

替换ribbon包为feign包,关闭ribbon注解,ribbon配置,ribbon策略代码和feign一样不用删
添加UserClient

@FeignClient("user-provider")
@RequestMapping("/user/provider/")
public interface UserClient {

    @RequestMapping("/{id}")	// 通过这些参数自动生成一个代理实现类,发起远程调用
    User getUser(@PathVariable("id") Long id);
}

启动类加注解

@SpringBootApplication
@EnableEurekaClient //表示是eureka的客户端
@EnableFeignClients(basePackages = "cn.ming.client") //如果是主内所在子包,可以不用加basePackages,但是最好加上
public class UserConsumerApp {

    public static void main(String[] args) {
        SpringApplication.run(UserConsumerApp.class,args);
    }
}

修改UserConsumerController

@RestController
@RequestMapping("/user/consumer")
public class UserConsumerController {

    @Autowired
    private UserClient client;

    // 返回一个用户
    @RequestMapping("/{id}")
    public User getUserById(@PathVariable("id") Long id){
        System.out.println(client.getClass());//
        User user = client.getUser(id);
        return user;
    }
}

启动eureka集群,生产者集群和此项目
浏览器刷新访问测试:http://localhost:5210/user/consumer/5210

Hystrix

Hystrix概述

见文档2.1,2.2
在这里插入图片描述
如下单前检查用户是否登录或调取用户信息,其中一台挂掉会影响请求到这台机器上的所有请求,影响越来越大,造成雪崩现象

eureka只是定时更新可调用服务列表,处理此问题不专业

Hystrix可対问题机器进行隔离,熔断,限流,降级,缓存,
隔离:单独分开
熔断:例如:每当20个请求中,有50%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到5s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。
限流:减少给此服务分发的请求数量
降级:回调返回设定的错误,避免直接没任何响应
缓存:把请求排队或转给其他服务处理

Ribbon搭配Hystrix降级处理

Hystrix是在远程调用是应用的,所以要搭配ribbon或feign使用
在服务提供者方向写

项目准备
拷贝user-provider-4010为user-provider-hystrix-4110
修改父pom和子pom
修改application.yml
删除target

导包

		<!--断路器-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

启动类加注解

@SpringBootApplication
@EnableEurekaClient //表示是eureka的客户端
@EnableHystrix  //表示添加 hystrix 服务熔断降级
public class UserProviderApp {

    public static void main(String[] args) {
        SpringApplication.run(UserProviderApp.class,args);
    }
}

修改UserProviderController

@RestController
@RequestMapping("/user/provider")
public class UserProviderController {

    @Autowired
    private ApplicationContext context;

    // 返回一个用户
    @RequestMapping("/{id}")
    @HystrixCommand(fallbackMethod = "fallbackMethod")	//服务降级的处理回调的方法
    public User getUser(@PathVariable("id") Long id) {
        // 为了方便看是哪一个提供者提供的服务
        String activeProfile = context.getEnvironment().getActiveProfiles()[0];

        if (id == 2){	//模拟服务器出错
            throw new RuntimeException("服务器异常1");
        }
        return new User(id, "zs," + activeProfile);
    }

    public User fallbackMethod(Long id){	//降级处理的方法
        return new User(id,"服务器异常2");
    }
}

启动eureka集群,带hystrix的生产者,带ribbon的消费者
浏览器访问测试:http://localhost:5110/user/consumer/2

Feign搭配Hystrix降级处理

写在消费者一边

项目准备
拷贝user-consumer-feign-5210到user-consumer-feign-hystrix-5310
修改父pom和子pom
修改application.yml
删除target文件夹

不用新加依赖,有openfeign就行
application.yml

feign:
   hystrix:
       enabled: true #开启熔断支持
   client:
    config:
      remote-service:           #服务名,填写default为所有服务
        connectTimeout: 3000
        readTimeout: 3000
hystrix:
  command:
      default:
        execution:
          isolation:
            thread:
              timeoutInMilliseconds: 3000

新增类UserFallBack

@Component //降级处理
public class UserFallBack implements FallbackFactory<UserClient> {
    @Override
    public UserClient create(Throwable throwable) {

        return new UserClient() {
            @Override
            public User getUser(Long id) {
                return new User(id,"系统繁忙,请联系管理员");
            }
        };
    }
}

修改UserClient

@FeignClient(value = "user-provider",fallbackFactory = UserFallBack.class)
@RequestMapping("/user/provider/")
public interface UserClient {

    @RequestMapping("/{id}")    // 通过这些参数自动生成一个代理实现类,发起远程调用
    User getUser(@PathVariable("id") Long id);
}

修改user-provider-4010的UserProviderController

    @RequestMapping("/{id}")
    public User getUser(@PathVariable("id") Long id) {
        // 为了方便看是哪一个提供者提供的服务
        String activeProfile = context.getEnvironment().getActiveProfiles()[0];
        if (id == 2){   //模拟服务器出错,让hystrix搭配feign降级处理
            throw new RuntimeException("服务器异常");
        }
        return new User(id, "zs," + activeProfile);
    }

启动eureka集群,user-provider-4010和user-consumer-feign-hystrix-5310
浏览器访问测试:http://localhost:5310/user/consumer/2