SpringCloud的学习02

1. 实现高可用

1.1 商品服务高可用

在这里插入图片描述

在这里插入图片描述
复制Sp01Item配置
在这里插入图片描述
测试效果:
在这里插入图片描述
在这里插入图片描述

1.2 eureka高可用实现

1.2.1 编辑配置文件

eureka:
  instance:
    #主机名,在集群中区分不同服务器
    hostname: eureka1
  client:
    register-with-eureka: true
    fetch-registry: true
    #连接eureka2
    service-url:
      defaultZone: http://eureka2:2002/eureka

在这里插入图片描述

eureka:
  instance:
    #主机名,在集群中区分不同服务器
    hostname: eureka2
  client:
    register-with-eureka: true
    fetch-registry: true
    #连接eureka1
    service-url:
      defaultZone: http://eureka1:2001/eureka

在这里插入图片描述

1.2.2 编辑启动配置

–spring.profiles.active=eureka1 --server.port-2001
在这里插入图片描述
在这里插入图片描述

1.2.3 测试效果

http://eureka1:2001/
在这里插入图片描述
http://eureka2:2002/

2. ribbon

在这里插入图片描述

2.1 RestTemplate

ribbon 提供了负载均衡和重试功能, 它底层是使用 RestTemplate 进行 Rest api 调用
RestTemplate 是SpringBoot提供的一个Rest远程调用工具,对Rest API调用做了高度地封装,提供了渐变的调用接口
它的常用方法:

  • getForObject(url,转换类型,提交的参数数据) - 执行get请求
  • postForObject(url,提交的协议体数据,转换类型) - 执行post请求

2.2 创建sp06-ribbon项目

2.2.1 添加依赖

<?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.3.8.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.tedu</groupId>
	<artifactId>sp06-ribbon</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>sp06-ribbon</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.SR9</spring-cloud.version>
		<vaadin.version>14.4.6</vaadin.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.vaadin</groupId>
			<artifactId>vaadin-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
		</dependency>
		<dependency>
			<groupId>com.netflix.eureka</groupId>
			<artifactId>eureka-client</artifactId>
			<version>1.10.11</version>
		</dependency>

		<dependency>
			<groupId>cn.tedu</groupId>
			<artifactId>sp01-commons</artifactId>
			<version>1.0-SNAPSHOT</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>com.vaadin</groupId>
				<artifactId>vaadin-bom</artifactId>
				<version>${vaadin.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

2.2.2 配置yml

spring:
  application:
    name: ribbon

server:
  port: 3001

eureka:
  client:
    service-url:
     defaultZone: http://eureka1:2001/eureka,http://eureka2:2002/eureka

2.2.3 启动类新建RestTemplate

package cn.tedu.sp06;

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

@SpringBootApplication
public class Sp06RibbonApplication {

	public static void main(String[] args) {
		SpringApplication.run(Sp06RibbonApplication.class, args);
	}
	@Bean
	public RestTemplate restTemplate(){
		return new RestTemplate(); 
	}
}

2.2.4 编辑RibbonController

package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RibbonController {
    @Autowired
    private RestTemplate rt;

    //调用获取商品列表
    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId){
        //调用远程的商品服务,获取商品列表  http://localhost:8001/{orderId}
        //{1} - RestTemplate 定义的占位符格式
        return rt.getForObject("http://localhost:8001/{1}",JsonResult.class,orderId);
    }

    //减少商品库存
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items){
        return rt.postForObject("http://localhost:8001/decreaseNumber", items, JsonResult.class);
    }

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId){
        return rt.getForObject("http://localhost:8101/{1}", JsonResult.class, userId);
    }
    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(@PathVariable Integer userId, Integer score){
        return rt.getForObject("http://localhost:8101/{1}/score?score={2}", JsonResult.class, userId, score);
    }

    @GetMapping("/order_service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId){
        return rt.getForObject("http://localhost:8201/{1}", JsonResult.class, orderId);
    }

    @GetMapping("/order_service")
    public JsonResult addOrder(){
        return rt.getForObject("http://localhost:8201/", JsonResult.class);
    }

}

2.2.5 测试

http://localhost:3001/item-service/34
在这里插入图片描述

2.3 实现负载均衡

在这里插入图片描述

2.3.1 添加@LoadBalanced注解

在RestTemplate上添加注解@LoadBalanced
在这里插入图片描述

2.3.2 编辑RibbonController

设置返回url为动态地址

package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RibbonController {
    @Autowired
    private RestTemplate rt;

    /**
     * eureka注册表:
     *  item-service --- localhost:8001,localhost:8002
     * @param orderId
     * @return
     */
    //调用获取商品列表
    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId){
        //调用远程的商品服务,获取商品列表  http://localhost:8001/{orderId}
        //{1} - RestTemplate 定义的占位符格式
        return rt.getForObject("http://item-service/{1}",JsonResult.class,orderId);
    }

    //减少商品库存
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items){
        return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
    }

    @GetMapping("/user-service/{userId}")
    public JsonResult<User> getUser(@PathVariable Integer userId){
        return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
    }
    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(@PathVariable Integer userId, Integer score){
        return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
    }

    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId){
        return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
    }

    @GetMapping("/order-service")
    public JsonResult addOrder(){
        return rt.getForObject("http://order-service/", JsonResult.class);
    }

}

2.3.3 负载均衡测试

在这里插入图片描述
在这里插入图片描述

2.4 实现重试

在这里插入图片描述

2.4.1 添加依赖

<dependency>
	<groupId>org.springframework.retry</groupId>
	<artifactId>spring-retry</artifactId>
</dependency>

2.4.2 配置重试参数

  • MaxAutoRetries - 单台服务器重试次数
  • MaxAutoRetriesNextServer - 更换服务器次数
  • OkToRetryOnAllOperations - 是否对所有类型请求都重试,默认只对get重试
  • ConnectTimeout - 建立连接的超时时间
  • Readtimeout - 连接建立后请求已发送,等待响应超时时间
ribbon:
  MaxAutoRetriesNextServer: 2
  MaxAutoRetries: 1
  OkToRetryOnAllOperations: true

在这里插入图片描述

2.4.2 编辑启动类

@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
		f.setConnectTimeout(1000);
		f.setReadTimeout(1000);
		return new RestTemplate(f);
	}

在这里插入图片描述

2.4.3 编辑ItemController

在这里插入图片描述

//获取订单中的商品列表
    @GetMapping("/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId) throws InterruptedException {
        List<Item> items = itemService.getItems(orderId);
        log.info("获取商品列表, orderId"+orderId);

        //随机延迟
        if(Math.random()<0.9){
            //产生随机延迟时长
            int t = new Random().nextInt(5000);
            System.out.println("延迟"+t);
            Thread.sleep(t);
        }

        return JsonResult.ok().msg("server.port="+port).data(items);
    }

2.4.4 重试效果测试

http://localhost:3001/item-service/34
在这里插入图片描述
在这里插入图片描述

3. Hystrix 断路器

系统容错/系统限流工具

  • 降级
  • 熔断
    在这里插入图片描述

3.1 降级

调用远程服务失败,可以执行当前服务中的一段代码,来向客户端返回结果

3.1.1 添加 hystrix 起步依赖

在sp06-ribbon项目的pom.xml文件添加hystrix依赖

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

3.1.2 启动类添加@EnableCircuitBreaker注解

在sp06-ribbon项目的启动类上添加@EnableCircuitBreaker注解
在这里插入图片描述

3.1.3 添加降级代码

package cn.tedu.sp06.controller;

import cn.tedu.sp01.pojo.Item;
import cn.tedu.sp01.pojo.Order;
import cn.tedu.sp01.pojo.User;
import cn.tedu.web.util.JsonResult;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.List;

@RestController
@Slf4j
public class RibbonController {
    @Autowired
    private RestTemplate rt;

    /**
     * eureka注册表:
     *  item-service --- localhost:8001,localhost:8002
     * @param orderId
     * @return
     */
    //调用获取商品列表
    @HystrixCommand(fallbackMethod = "getItemsFB")//如果远程调用失败,调用降级方法
    @GetMapping("/item-service/{orderId}")
    public JsonResult<List<Item>> getItems(@PathVariable String orderId){
        //调用远程的商品服务,获取商品列表  http://localhost:8001/{orderId}
        //{1} - RestTemplate 定义的占位符格式
        return rt.getForObject("http://item-service/{1}",JsonResult.class,orderId);
    }

    //减少商品库存
    @HystrixCommand(fallbackMethod = "decreaseNumberFB")
    @PostMapping("/item-service/decreaseNumber")
    public JsonResult decreaseNumber(@RequestBody List<Item> items){
        return rt.postForObject("http://item-service/decreaseNumber", items, JsonResult.class);
    }

    @GetMapping("/user-service/{userId}")
    @HystrixCommand(fallbackMethod = "getUserFB")
    public JsonResult<User> getUser(@PathVariable Integer userId){
        return rt.getForObject("http://user-service/{1}", JsonResult.class, userId);
    }


    @HystrixCommand(fallbackMethod = "addScorerFB")
    @GetMapping("/user-service/{userId}/score")
    public JsonResult addScore(@PathVariable Integer userId, Integer score){
        return rt.getForObject("http://user-service/{1}/score?score={2}", JsonResult.class, userId, score);
    }

    @HystrixCommand(fallbackMethod = "getOrderFB")
    @GetMapping("/order-service/{orderId}")
    public JsonResult<Order> getOrder(@PathVariable String orderId){
        return rt.getForObject("http://order-service/{1}", JsonResult.class, orderId);
    }

    @HystrixCommand(fallbackMethod = "addOrderFB")
    @GetMapping("/order-service")
    public JsonResult addOrder(){
        return rt.getForObject("http://order-service/", JsonResult.class);
    }

    //降级方法的参数和返回值,需要和原始方法一致,方法名任意
    public JsonResult<List<Item>> getItemsFB(String orderId){
        return JsonResult.err("获取订单商品列表失败");
    }

    public JsonResult decreaseNumberFB(List<Item> items) {
        return JsonResult.err("更新商品库存失败");
    }

    public JsonResult<User> getUserFB(Integer userId) {
        return JsonResult.err("获取用户信息失败");
    }

    public JsonResult addScoreFB(Integer userId, Integer score) {
        return JsonResult.err("增加用户积分失败");
    }

    public JsonResult<Order> getOrderFB(String orderId) {
        return JsonResult.err("获取订单失败");
    }

    public JsonResult addOrderFB() {
        return JsonResult.err("添加订单失败");
    }

}

3.1.4 降级测试效果

在这里插入图片描述

3.1.5 配置hystrix 超时设置

Hystrix超时和Ribbon超时需要配合设置,否则ribbon重试可能无效
说明: hystrix等待超时后, 会执行降级代码, 快速向客户端返回降级结果, 默认超时时间是1000毫秒

  • Hystrix超时时间>=Ribbon总的超时时间
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 1000

3.2 熔断

说明: 访问压力增大,出现大量失败时,可以断开链路,对后台服务不再发生请求
熔断自动触发发生条件:

  • 10秒发生20次请求(必须首先成立)
  • 50%请求都失败,执行降级代码

半开状态:

  • 断路器打开后几秒,会进入半开状态,在半开状态下会尝试向后台服务发送一次调用,如果成功则关闭断路器,恢复正常;如果仍然调用失败,则继续保持打开状态几秒钟,再进入半开状态,尝试向后台服务发送一次调用,循环往复.

3.2.1 熔断测试

访问网页: http://localhost:3001/item-service/4
F5多次刷新,发现无需等待直接降级返回,等一会测试发现又需要等待,则熔断测试成功

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

_Solider

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值