微服务呀微服务

途人路上回望我,只因我的怪摸样
在这里插入图片描述

微服务是什么?

似乎"微"这个字很难有一个明确的定义,这个"微" 具体是怎样一种粒度才能叫"微";

其实在我们开发中我们对于整体服务的把控,在不断实践中去调整服务的粒度,这才是微服务架构想要达到的一个理想状态;

R C Martain曾这样论述:

把因相同原因而变化的东西聚合到一起,而把因不同原因而变化的东西分离开来

其实就是内聚性

我们在考虑微服务时,应该考虑的几个点:

  1. 微服务的粒度
  2. 微服务之间的通信使用哪种方式
  3. 哪些服务暴露在外,那些不能暴露

微服务粒度的一个检验标准:

是否修改一个服务而不对其他服务产生影响

细粒度的微服务架构可以帮助他们更快地交付软件 在微服务架构中,各个服务的部署是独立的,这样就可以更快地对特定部分的代码进行部署。如果真的出了问题,也只会影响一个服务,并且容易快速回滚

我们应该考虑不同的服务之间如何交互,或者说保证我们能够对整个系统的健康状态进行监控。至于多大程度地介入区域内部事务,在不同的情况下则有所不同

微服务的切分

水平切分:将同一个系统部署到多态机器上
垂直切分:按照业务进行切分

微服务之间的交互

在进行微服务的设计时还要考虑各个服务之间是如何交互的,现阶段主流的远程交互–

  1. RPC----Dubbo 一种使用简化的(http)交互方式
  2. SOAP----比较老的一套交互方式了,之前邮箱开发时用的这个,就是给一个xml文件,然后根据这个xml文件去调用相应的接口
  3. REST----比较新的一种交互的风格了 get/post/put/delete方式来实现交互

交互时候要考虑到:

  1. 节点故障
  2. 网络的可靠性~比如延时,乱序,丢包等
  3. 服务器的故障~断电,磁盘损坏等
  4. 数据的一致性问题(CAP和BASE)

为了提高系统的性能,可以选择数据的最终一致性,总之看实际情况合理选择

返回码的设计:

1)正常并且被正确处理的请求;
2)错误请求,并且服务识别出了它是错误的,但什么也没做;
3)被访问的服务宕机了,所以无法判断请求是否正常。

中间件的设计

尽量让中间件保持简单,而把业务逻辑放在自己的服务中间

说实话,spring虽然方便我们的代码开发,但是微服务这块 版本不兼容,这个版本移除了某个类,然后运行时就报错了,

今天准备整理一下 版本 这令人…的问题;
eureka-server

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.4</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.gavin.huawei</groupId>
	<artifactId>eureka_server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>eureka_server</name>
	<description>Demo project for Spring Boot</description>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
			<version>3.1.1</version>
			<exclusions>
				<exclusion>
					<artifactId>servlet-api</artifactId>
					<groupId>javax.servlet</groupId>
				</exclusion>
			</exclusions>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
			<version>2.7.4</version>
		</dependency>


	</dependencies>

</project>

yml文件

server:
  port: 7000 #注册中心的端口
eureka:
  instance:
    hostname: eureka_server #这里可以设置ip(host)有对应的就行
    prefer-ip-address: false
  client:
    service-url:
      defaultZone: http://127.0.0.1:7000/eureka
    register-with-eureka: false #服务自己就是治理中心,所以不向自己注册自己  取消当前微服务,寻找其他Eureka服务治理中心进行注册。
    fetch-registry: false #服务获取,用于注册中心之间服务的同步
spring:
  application:
    name: eureka_server #给微服务起个名


eureka_client

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.gavin.huawei</groupId>
    <artifactId>user</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>3.1.1</version>
            <exclusions>
                <exclusion>
                    <artifactId>servlet-api</artifactId>
                    <groupId>javax.servlet</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.4</version>
        </dependency>

    </dependencies>

</project>

yml文件

spring:
  application:
    name: user_client
server:
  port: 8000
eureka:
  instance:
    hostname: user_client
  client:
    service-url:
      defaultZone: http://127.0.0.1:7000/eureka

启动一下:
在这里插入图片描述

微服务之间的调用:

举个例子:
两个controller:

package com.gavin.controller;

import com.gavin.huawei.ResultMessage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import sun.misc.Request;

import javax.servlet.http.HttpServletRequest;

/**
 * @author Gavin
 */
@RestController
@RequestMapping("/fund")
public class AccountController {
    @CrossOrigin
    @PostMapping("/account/balance/{userId}/{amount}")
    public ResultMessage  deductingBalance(
            @PathVariable("userId") Long userId,
            @PathVariable("amount") Double amount,
            HttpServletRequest request
    ){
        String message="端口:"+ request.getServerPort()+"扣减成功";
        return new ResultMessage(true,message);
    }


}

package com.gavin.controller;

import com.gavin.huawei.ResultMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.Map;

/**
 * @author Gavin
 */
@RestController
@RequestMapping("/product")
public class ProductController {
    @Autowired
    private RestTemplate restTemplate;

    @CrossOrigin
    @GetMapping("/purchase/{userId}/{productId}/{amount}")
    public ResultMessage purchaseProduct(@PathVariable("userId") Long userId,
                                         @PathVariable("productId") Long productId,
                                         @PathVariable("amount") Double amount
    ) {
        System.out.println("扣减产品金额");
        //这里的FUNDATION就是微服务fund中的eureka.instance.appname---对应application列,是一个微服务的统称, 而spring.application.name就是status的名字对用注册中心的名字,
        String url = "http://FUNDATION/fund/account/balance/{userId}/{amount}";
        Map<String, Object> params = new HashMap<>();
        params.put("userId", userId);
        params.put("amount", amount);
        ResultMessage resultMessage = restTemplate.postForObject(url, null, ResultMessage.class, params);
        System.out.println(resultMessage.getMsg());
        System.out.println("记录交易信息");
        return new ResultMessage(true, "交易成功");
    }

}

可以看一下调用的方式:
首先是跨域的问题:
另一个是url

这里的url通过名称进行调用;

这是因为配置文件中:
有两个关于名称的配置项

server:
  port: 9000
spring:
  application:
    name: fundation
eureka:
  instance:
    hostname: 127.0.0.1
    appname: fundation
  client:
    service-url:
      defaultZone: http://127.0.0.1:7000/eureka,http://127.0.0.1:7001/eureka,http://127.0.0.1:7002/eureka
    fetch-registry: true
    register-with-eureka: true #用于向注册中心注册

在这里插入图片描述

spring.application.name 对用eureka注册中心中 status 那一列
eureka.instance.appname 对应 APPLICATION那一列

最好是名称一致

所以我们在调用微服务时,通过向注册中心寻找名字的方式来寻找我们需要的服务;

如果找不到就会报404 path找不到的错误;

在这里插入图片描述
另一个在数据传输的时候会涉及序列话,所以对于数据实体类要有一个空参的构造函数才能保证调用的顺利;

这里的服务提供者和消费者并不是对立的,一个微服务可以同时是服务消费者和服务提供者

配置文件
在这里插入图片描述
在这里插入图片描述

微服务的命名规则:

微服务的实例默认的名称规则是:
如果我们配置了spring.application.appname ,则名称为

设备名称:spring.application.name:server.port

在这里插入图片描述

负载均衡:

负载均衡是分布式环境中必须要实现的功能:
其主要优点是:

  1. 降低对单个服务器的压力
  2. 高可用性和高性能
    如果某个节点出现问题,可以通过负载均衡去选择屏蔽掉他,或者某个节点的处理能力弱,那么可以减少访问该服务器;
  3. 可伸缩
    可以通过增加节点的方式来提高服务器的能力,或者减少节点
  4. 请求过滤
    可以屏蔽掉一些不合法的请求来减轻服务器的压力

负载均衡是怎样运作的?

负载均衡要生效首先要知道

  • 1,有哪些微服务,这些微服务哪一些能用,
  • 2,如何选择要调用的微服务;

所以Ribbon要有一个实例清单,并定期更新这个清单中的微服务列表
在源码中有:

  • 1, 获取服务实例清单 接口
  • 2,获取可用实例的清单(由线程定时更新这个清单)

负载均衡策略:

BestAvailableRule—>>当前服务器被分配最少请求的那个

会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务。逐个找服务,如果断路器打开,则忽略。

AvailabilityFilteringRule–>>过滤掉那些被标记为tripped/无法连接的服务器以及超过最大请求阈值的服务实例

会先过滤掉多次访问故障而处于断路器跳闸状态的服务和过滤并发的连接数量超过阀值得服务,然后对剩余的服务列表安装轮询策略进行访问。

ResponseTimeWeightedRule–>>响应时间权重策略

WeightdResponseTimeRule–>>对于响应时间短的有更大的概率分配到请求

据平均响应时间计算所有的服务的权重,响应时间越快服务权重越大,容易被选中的概率就越高。刚启动时,如果统计信息不中,则使用RoundRobinRule(轮询)策略,等统计的信息足够了会自动的切换到WeightedResponseTimeRule。响应时间长,权重低,被选择的概率低。反之,同样道理。此策略综合了各种因素(网络,磁盘,IO等),这些因素直接影响响应时间。

RetryRule–>>重试服务策略

RoundRobinRule–>> 轮询选择服务(通过下标选择)

RandomRule–>>随机选择服务(通过随机数来选择)
ZoneAvoidanceRule–>判断实例所在区域的性能和故障选择合适的实例 默认的策略

区域权衡策略)**默认策略
复合判断Server所在区域的性能和Server的可用性,轮询选择服务器。
简单来讲就是:找到那些性能较差的zone,然后将其排除在外,随机选择性能较好的Zone;

负载均衡的使用:

首先明确负载均衡是通过eureka治理中心来发出的,所注意在eureka的配置文件中要定义好负载均衡的策略:
明确对那个服务开启负载均衡:
然后配置负载均衡的策略:

ribbon:
  eureka:
    enabled: true # 开启负载均衡 默认使用 ZoneAwareLoadBalancer 负载均衡器,使用ZoneAvoidanceRule策略
# 设置调用某个微服务时的负载均衡的模式
fundation:  # 在远程调用模块里配置~  服务提供者的负载均衡 模式
  ribbon: #负载均衡由ribbon实现

    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #配置规则 随机
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #配置规则 轮询
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RetryRule #配置规则 重试
    #    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule #配置规则 响应时间权重
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule #配置规则 最空闲连接策略
    ConnectTimeout: 500 #请求连接超时时间
    ReadTimeout: 1000 #请求处理的超时时间
    OkToRetryOnAllOperations: true #对所有请求都进行重试
    MaxAutoRetriesNextServer: 2 #切换实例的重试次数
    MaxAutoRetries: 1 #对当前实例的重试次数

一个配置文件模板
在这里插入图片描述
运行时的截图:
在这里插入图片描述

负载均衡的源码

之前的文章解析过,这里只放上链接 负载均衡源码分析

自定义负载均衡策略

一般情况下使用自带的就好了,但是有时候我们需要自定义一下负载均衡策略,所以如果我们要自定义,就需要知道ribbon的工作原理:

1, 服务实例的监测

在ribbon中要维护服务实例的清单,所以必须要有一个线程来负责获取实例的状态,以便及时上架/下架可用的服务实例列表

@Configuration
@ConditionalOnBean({LoadBalancerZoneConfig.class, EurekaLoadBalancerProperties.class})
public class EurekaLoadBalancerClientConfiguration {
    private static final Log LOG = LogFactory.getLog(EurekaLoadBalancerClientConfiguration.class);
    private final EurekaClientConfig clientConfig;
    private final EurekaInstanceConfig eurekaConfig;
    private final LoadBalancerZoneConfig zoneConfig;
    private final EurekaLoadBalancerProperties eurekaLoadBalancerProperties;

    public EurekaLoadBalancerClientConfiguration(@Autowired(required = false) EurekaClientConfig clientConfig, @Autowired(required = false) EurekaInstanceConfig eurekaInstanceConfig, LoadBalancerZoneConfig zoneConfig, EurekaLoadBalancerProperties eurekaLoadBalancerProperties) {
        this.clientConfig = clientConfig;
        this.eurekaConfig = eurekaInstanceConfig;
        this.zoneConfig = zoneConfig;
        this.eurekaLoadBalancerProperties = eurekaLoadBalancerProperties;
    }

    @PostConstruct
    public void postprocess() {
        if (StringUtils.isEmpty(this.zoneConfig.getZone())) {
            String zone = this.getZoneFromEureka();
            if (!StringUtils.isEmpty(zone)) {
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Setting the value of 'spring.cloud.loadbalancer.zone' to " + zone);
                }

                this.zoneConfig.setZone(zone);
            }

        }
    }

    private String getZoneFromEureka() {
        boolean approximateZoneFromHostname = this.eurekaLoadBalancerProperties.isApproximateZoneFromHostname();
        if (approximateZoneFromHostname && this.eurekaConfig != null) {
            return ZoneUtils.extractApproximateZone(this.eurekaConfig.getHostName(false));
        } else {
            String zone = this.eurekaConfig == null ? null : (String)this.eurekaConfig.getMetadataMap().get("zone");
            if (StringUtils.isEmpty(zone) && this.clientConfig != null) {
                String[] zones = this.clientConfig.getAvailabilityZones(this.clientConfig.getRegion());
                zone = zones != null && zones.length > 0 ? zones[0] : null;
            }

            return zone;
        }
    }
}

ribbon的饥渴加载机制

我们知道服务在向eureka注册时会有延时,当第一次调用的时候可能会发生延时的情况,没关系,ribbon提供了饥渴加载的方式:

ribbon:
  eureka:
    enabled: true # 开启负载均衡 默认使用 ZoneAwareLoadBalancer 负载均衡器,使用ZoneAvoidanceRule策略
  eager-load:
    enable: true
  clients: user, product ,fund #加载的微服务名

这样那三个微服务就可以得到及时加载,

另一个我们了解一下 --ribbon可以不通过 eureka 来使用,就是获取服务的列表需要自己来配置(没必要呀!!)

SpringBoot监控

首先引入依赖:

<!--springboot监控-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-hateoas</artifactId>
        <version>2.7.0</version>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
        <version>2.7.0</version>
    </dependency>

启动项目之后访问:http://localhost:7001/actuator/beans

之后我们可以看到暴露的一些beans
在这里插入图片描述但是这么暴露就很危险,我们需要结合spring的安全机制—spring security来进行安全方面的校验;

region与zone

为了提高业务的访问速度,企业提高会将服务部署在不同的地区,甚至可能会跨国:
eureka也提供了应对的方式:


eureka:
  client:
    region: Asia
    availability-zones: Shanghai

region :一个大型的区域,可以是一个国家甚至一个洲

availability-zones:服务可以覆盖到的范围,通常是一个地区/省

关于注册与续约的一些配置:

server:
  port: 6768 #指定tomcat端口与eureka端口一致
spring:
  application:
#    微服务名称
    name: eurekaservice1
eureka:
  instance:
  #服务治理中心 可以是ip也可以是实例名(实例名要配置host)
    hostname: eurekaservice  #实例名  如果实例名一致,那么eureka会认为其是一个微服务
    prefer-ip-address: true #通过IP获得实例
#    续约时间间隔
    lease-renewal-interval-in-seconds: 30
  client:
#    关于注册和续约的配置
#    服务注册地址
    service-url:
      defaultZone: http://127.0.0.1:6768/eureka  #eureka 地址
#      是否注册---服务
    register-with-eureka: false
#    服务获取
    fetch-registry: true
#    当服务获取超时 后,最大尝试次数,默认10s
    cache-refresh-executor-exponential-back-off-bound: 10
#    服务获取刷新时间间隔 默认30s
    registry-fetch-interval-seconds: 30
#    续约超时,最大尝试次数
    heartbeat-executor-exponential-back-off-bound: 10
#    注册任务线程时间间隔
    instance-info-replication-interval-seconds: 30
#    是否启用守护线程监听Eureka客户端状态 默认true
    on-demand-update-status-change: true
#    首次服务注册延迟时间 默认 40
    initial-instance-info-replication-interval-seconds: 40
    

通过上述配置我们可以了解的几个点:

  • 1,启动个eureka客户端,她并不会立马注册到Eureka服务器;
    再默认情况下要等待40s才会发送注册请求,如果注册不成功,那么会每30s尝试注册一次;
  • 2,对于服务发现,客户端存在自己的缓存清单,这个清单并不是实时更新的,如果遇到某个服务挂了,清单中也并不会立即更新,只有当刷新缓存清单后才能发现新注册的服务,同样的如果清单中对应的某个挂了,也并不会及时被发现而是要等更新后才会发现;
  • 3,同时我们压要考虑网络波动对于时间的一个影响;

关于断路器Hystrix

熔断器微服务:

@SpringBootApplication
@EnableHystrixDashboard
public class hystrixdashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(hystrixdashboardApplication.class,args);
    }
}

在Fund服务模块上开启熔断机制:

@EnableEurekaClient
@SpringBootApplication(scanBasePackages = "com.gavin")
//@EnableCircuitBreaker
@EnableCaching
//开启熔断
@EnableHystrix
@EnableFeignClients(basePackages = "com.gavin")//将openfeign的客户端接口注入到ioc容器中
public class FundApplication {
    public static void main(String[] args) {
        SpringApplication.run(FundApplication.class,args);
    }
}

在user微服务模块里 搭建一个熔断器控制层:

/**
 * @author Gavin
 */
@RestController
@RequestMapping("/user")
public class HystrixController {

    private  static Long MAX_SLEEP_TIME=5000L;

    /**
     * 超时熔断
     *
     * @return
     */
    @GetMapping("/timeout")
    public ResultMessage timeOut(){
        long sleepTime=(long)(MAX_SLEEP_TIME*Math.random());
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            System.out.println("异常出现");
        }
        return new ResultMessage(true,"执行时间"+sleepTime);
    }

    /**
     * 异常熔断
     *
     * @param msg
     * @return
     */
    @GetMapping("/exc/{msg}")
    public ResultMessage exception(@PathVariable("msg") String msg){
        if("spring".equals(msg)){
            Random random=new Random();
         int num=   random.nextInt(9999);
            return new ResultMessage(true,msg+num);
        }else{
            throw new RuntimeException("参数异常,请检查参数msg");
        }
    }


    /**
     * 异常熔断
     *
     * @param msg
     * @return
     */
    @PostMapping("/exc2")
    public ResultMessage exception2(@RequestBody CommonResp msg){
        if("spring_boot".equals(msg.getMsg())){
            String msgMsg = msg.getMsg();
            return new ResultMessage(true,msgMsg);
        }else{
            throw new RuntimeException("参数异常,请检查参数"+msg.getMsg());
        }
    }


    @Autowired
    private UserMapper userMapper;

    @GetMapping("/user/info/{id}")
    public User getUserInfo(@PathVariable("id")String id){
        return  userMapper.selectByPrimaryKey(id);
    }
}

在product微服务模块中试验一下

@Service
public class ProductServiceImpl implements ProductService {

    @Autowired
    private RestTemplate restTemplate;

    //HystrixCommand??? ????÷??????Hystrix??????,?????????????????õ????
    @HystrixCommand(fallbackMethod = "fallback1")
    @Override
    public ResultMessage timeOut() {
        //注解的形式
        String url = "http://USER/user/timeout";
        return restTemplate.getForObject(url, ResultMessage.class);
    }

    @Override
    @CacheResult(cacheKeyMethod = "getMsg")//
    @HystrixCommand(commandKey = "key1", fallbackMethod = "fallback2")
    public ResultMessage exception(String msg) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("msg", msg);

        String url = "http://USER/user/exc/{msg}";

        return restTemplate.getForObject(url, ResultMessage.class, paramMap);
    }

    public String getMsg(String msg) {
        return msg;
    }

    /**
     * @CatchResult/@CatchKey优先级高于cacheKeyMethod
     * @param msg
     */
    @Override
    @CacheRemove(commandKey = "key1", cacheKeyMethod = "getMsg")//将请求结果删除
    @HystrixCommand
    public void flushCacheKey(String msg) {

        System.out.println("flush success");

    }

    /**
     *
     * CacheResult 缓存结果  CacheKey 缓存参数 msg
     * 对于熔断器,当抛出HystrixBadRequestException时不会出发getfallback,而是会抛出异常
     * ignoreExceptions  --忽略某些异常
     *              @CacheKey用于参数上,可标记缓存的键,若没有标注,则使用所有参数
     * @param msg
     * @return
     */

    @Override
    @CacheResult(cacheKeyMethod = "") //缓存关键字,对于本次请求来讲 ,cacheKeyMethod指定缓存key的生成方法;
    @HystrixCommand(commandKey = "key2",fallbackMethod = "fallback3",ignoreExceptions = HystrixBadRequestException.class) //
    public ResultMessage exception2(@CacheKey("msg") String msg) {

        String url = "http://USER/user/exc2";
        Map<String, Object> paramMap = new HashMap();
        paramMap.put("msg", msg);
        System.out.println(msg);
        return restTemplate.postForObject(url, paramMap, ResultMessage.class);
    }

    //代码的形式实现熔断--实现HystrixCommand<ResultMessage>

    /**
     *
     * @return
     */
    @Override
    public ResultMessage timeout2() {
        HystrixCommandGroupKey userGroup = HystrixCommandGroupKey.Factory.asKey("userGroup");
        com.netflix.hystrix.HystrixCommand.Setter setter= com.netflix.hystrix.HystrixCommand.Setter.withGroupKey(userGroup);
        UserTimeoutCommand userTimeoutCommand = new UserTimeoutCommand(setter, restTemplate);
        return userTimeoutCommand.execute();
//        异步执行
//        Future<ResultMessage> queue = userTimeoutCommand.queue();
//        try{
//            return queue.get();
//        }catch (Exception ex){
//            return userTimeoutCommand.getFallback()_;
//        }



    }



    public ResultMessage fallback1() {
        return new ResultMessage(false, "failed");

    }

    public ResultMessage fallback2(String msg) {
        return new ResultMessage(false, "failed" + msg);
    }

    public ResultMessage fallback3(String msg, Throwable ex) {
        ex.printStackTrace();
        return new ResultMessage(false, "post failed" + msg);
    }
}

小结:
1,引入依赖

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
            <version>2.2.8.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
            <version>2.2.2.RELEASE</version>
        </dependency>

2,配置一个熔断器微服务,用于可视化某些微服务方法熔断的一些信息:

在这里插入图片描述
3,使用注解 @HystrixCommand(fallbackMethod = “fallback1”) 指定熔断后要执行的方法;

在这里插入图片描述
接下来找一个调用timeout的方法请求试一下:

在这里插入图片描述
前端展示,
在这里插入图片描述
后端已经发生了异常:
在这里插入图片描述

Hystrix的缓存注解

缓存注解的目的针对如果发生了熔断,再次发送请求时能够快速响应;

    /**
     *
     * CacheResult 缓存结果  CacheKey 缓存参数 msg
     * 对于熔断器,当抛出HystrixBadRequestException时不会出发getfallback,而是会抛出异常
     * ignoreExceptions  --忽略某些异常
     *              @CacheKey用于参数上,可标记缓存的键,若没有标注,则使用所有参数
     * @param msg
     * @return p136
     */

    @Override
    @CacheResult(cacheKeyMethod = "") //缓存关键字,对于本次请求来讲 ,cacheKeyMethod指定缓存key的生成方法;
    @HystrixCommand(commandKey = "key2",fallbackMethod = "fallback3",ignoreExceptions = HystrixBadRequestException.class) //
    public ResultMessage exception2(@CacheKey("msg") String msg) {

        String url = "http://USER/user/exc2";
        Map<String, Object> paramMap = new HashMap();
        paramMap.put("msg", msg);
        System.out.println(msg);
        return restTemplate.postForObject(url, paramMap, ResultMessage.class);
    }



   @Override
    @CacheRemove(commandKey = "key1", cacheKeyMethod = "getMsg")//将缓存的结果删除
    @HystrixCommand
    public void flushCacheKey(String msg) {

        System.out.println("flush success");

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeMartain

祝:生活蒸蒸日上!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值