Skip to content

Latest commit

 

History

History
421 lines (336 loc) · 11 KB

8.springcloud服务短路.md

File metadata and controls

421 lines (336 loc) · 11 KB

传统 Spring Web MVC

以 web 工程为例

创建 DemoRestController:

package com.springcloud.lesson8.web.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Random;
import java.util.concurrent.TimeoutException;

@RestController
public class DemoRestController {
    private final static Random random = new Random();

    /**
     * 当方法执行时间超过100ms时,触发异常
     * @return
     */
    @GetMapping("")
    public String index() throws Exception {
        long executeTIme = random.nextInt(200);
        if (executeTIme > 100) { // 执行超过100ms
            throw new TimeoutException("Execution is timeout");
        }
        return "Hello World";
    }
}

异常处理

通过@RestControllerAdvice 实现
package com.springcloud.lesson8.web.controller;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.concurrent.TimeoutException;

/**
 * @author <a href="mailto:[email protected]">Finen</a>
 * @description {@link DemoRestController} 类似于aop拦截
 * @see {@link DemoRestController} {@link DemoRestControllerAdvice}
 * @since 0.0.1
 */

@RestControllerAdvice(assignableTypes = DemoRestController.class)
public class DemoRestControllerAdvice {
    @ExceptionHandler(TimeoutException.class)
    public Object faultToleranceTimeout(Throwable throwable) {
        return throwable.getMessage();
    }
}

Spring Cloud Netflix Hystrix

增加Maven依赖

<dependencyManagement>
    <dependencies>
        <!-- Spring Boot 依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-dependencies</artifactId>
            <version>version>2.3.9.RELEASE</version>
        </dependency>

        <!-- Spring Cloud 依赖 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

使用@EnableHystrix 实现服务提供方短路

修改应用 user-service-provider 的引导类:

package com.springclouid.lesson8;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;


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

通过@HystrixCommand实现

增加 getUsers() 方法到 UserServiceProviderController

@RestController
public class UserServiceProviderController {
    @Autowired
    private UserService userService;

    private final static Random random = new Random();

    @PostMapping("/user/save")
    public boolean saveUser(@RequestBody User user) {
        return userService.saveUser(user);
    }

    /**
     * 增加超时处理
     * @return
     */
    @HystrixCommand(
            commandProperties = {
                    // 设置超时时间返回100ms
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMillisecond", value = "100")
            },
            fallbackMethod = "fallbackForGetUsers" // 设置fallback方法
    )
    @GetMapping("/user/list")
    public Collection<User> getUsers() throws InterruptedException {
        long executeTime = random.nextInt(200);
        // 休眠时间
        System.out.println("execute time" + executeTime);
        Thread.sleep(executeTime);
        return userService.findAll();
    }

    /**
     * {@link #getUsers()} 的fallback方法
     * @return
     */
    public Collection<User> fallbackForGetUsers() {
        return Collections.emptyList();
    }
}

getUsers() 添加 fallback 方法:

    /**
     * {@link #getUsers()} 的 fallback 方法
     *
     * @return 空集合
     */
    public Collection<User> fallbackForGetUsers() {
        return Collections.emptyList();
    }

使用@EnableCircuitBreaker 实现服务调用方短路

调整 user-ribbon-client ,为UserRibbonController 增加获取用户列表,实际调用user-service-provider "/user/list" REST 接口

增加具备负载均衡 RestTemplate

UserRibbonClientApplication 增加 RestTemplate 申明

    /**
     * 申明 具有负载均衡能力 {@link RestTemplate}
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

实现服务调用

    /**
     * 调用 user-service-provider "/user/list" REST 接口,并且直接返回内容
     * 增加 短路功能
     */
    @GetMapping("/user-service-provider/user/list")
    public Collection<User> getUsersList() {
        return restTemplate.getForObject("http://" + providerServiceName + "/user/list", Collection.class);
    }

激活 @EnableCircuitBreaker

package com.springclouid.lesson8;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.IRule;
import com.springclouid.lesson8.user.ribbon.client.ping.MyPing;
import com.springclouid.lesson8.user.ribbon.client.rule.MyRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@RibbonClient("user-service-provider") // 指定目标应用名称
@EnableCircuitBreaker
public class UserRibbonClientApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserRibbonClientApplication.class, args);
    }

    /**
     * 将 {@link MyRule} 暴露成 {@link Bean}
     * @return {@link MyRule}
     */
    @Bean
    public IRule myRule() {
        return new MyRule();
    }

    /**
     * 将 {@link MyPing} 暴露成 {@link Bean}
     * @return {@link MyPing}
     */
    @Bean
    public IPing myPing() {
        return new MyPing();
    }

    /**
     * 申明具有负载均衡能力的{@link RestTemplate}
     * @return
     */
    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

增加编程方式的短路实现

package com.springclouid.lesson8.user.ribbon.client.hystrix;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import org.springframework.web.client.RestTemplate;

import java.util.Collection;
import java.util.Collections;

/**
 * @author <a href="mailto:[email protected]">Finen</a>
 * @description
 * @see
 * @since
 */
public class UserRibbonClientHystrixCommand extends HystrixCommand<Object> {
    private final RestTemplate restTemplate;
    private final String providerServiceName;

    public UserRibbonClientHystrixCommand(RestTemplate restTemplate, String providerServiceName) {
        super(HystrixCommandGroupKey.Factory.asKey(
                "User-Ribbon-Client"),
                100);
        this.restTemplate = restTemplate;
        this.providerServiceName = providerServiceName;
    }

    @Override
    protected Object run() throws Exception {
        return restTemplate.getForObject("http://" + providerServiceName + "/user/list", Collection.class);
    }

    /**
     * Fallback 实现
     * @return
     */
    protected Object getFallback() {
        return Collections.emptyList();
    }
}

改造 UserRibbonController#getUsersList() 方法

package com.springclouid.lesson8.user.ribbon.client.web.controller;

import com.springclouid.lesson8.domain.User;
import com.springclouid.lesson8.user.ribbon.client.hystrix.UserRibbonClientHystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.Collection;

@RestController
public class UserRibbonController {

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 负载均衡器客户端
     */
    @Autowired
    private LoadBalancerClient loadBalancerClient;

    @Value("${provider.service.name}")
    private String providerServiceName;

    private UserRibbonClientHystrixCommand hystrixCommand;

    @GetMapping("")
    public String index() throws IOException {
        User user = new User();
        user.setId(1L);
        user.setName("xiaoming");

        //选择服务
       ServiceInstance serviceInstance = loadBalancerClient.choose(providerServiceName);
       return loadBalancerClient.execute(providerServiceName, serviceInstance, instance -> {
           // 服务器实例
           String host = instance.getHost();
           int port = instance.getPort();
           String url = "http://" + host + ":" + port + "/user/save";
           RestTemplate restTemplate = new RestTemplate();
           return restTemplate.postForObject(url, user, String.class);
       });
    }

    /**
     * 调用 user-service-provider "/user/list",并直接返回内容
     * 增加 短路功能
     */
    @GetMapping("/user-service-provider/user/list")
    public Collection<User> getUserList() {
        return new UserRibbonClientHystrixCommand(restTemplate, providerServiceName).execute();
    }
}

为生产为准备

Netflix Hystrix Dashboard

创建 hystrix-dashboard 工程

增加Maven 依赖

    <dependencies>

        <!-- 依赖 Hystrix Dashboard -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
        </dependency>

    </dependencies>

增加引导类

package com.spring.cloud.hystrix.dashboard;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author <a href="mailto:[email protected]">Finen</a>
 * @description
 * @see
 * @since
 */
@SpringBootApplication
public class HystrixDashboardApplication {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardApplication.class, args);
    }
}

增加 application.properties

## Hystrix Dashboard 应用
spring.application.name = hystrix-dashboard

## 服务端口
server.port = 10000