springcloud介绍之微服务架构常见组件使用

放本地文件夹都快吃土了,准备清理文件夹,关于SpringCloud的!

1、eureka

eureka

单机版:

  • spring-cloud-eureka-700
  • <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-client -->
    <!--客户端依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        <version>2.2.10.RELEASE</version>
    </dependency>
    
    <!--服务端端依赖-->
    <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-eureka-server -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        <version>2.2.7.RELEASE</version>
    </dependency>
    

服务类配置

  • server:
      port: 7001
    
    eureka:
      instance:
        hostname: localhost7000
      client:
        #    表示不向注册中心注册自己
        register-with-eureka: false
        #    自己就是注册中心,并不需要检索服务
        fetch-registry: false
        #    注册服务依赖的地址
        service-url:
          defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
    

在服务的主程序类添加注解@EnableEurekaServer

客户端配置

# eureka
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka

在客户端的主程序类添加注解@EnableEurekaClient

测试结果

Eureka示意图

Eureak示意图

集群搭建

为什么要集群? 因为微服务调用过程中,如果Eureka宕机了,那么整个微服务均不可使用,显然是不符合微服务架构

搭建微服务至少需要3个实例,一般默认为奇数个最合适(为什么是奇数个,具体参考投票算法!)

下面是一个简单集群搭建示意图

Eureka 集群核心 互相注册,互相守望

7001的配置

7002的配置

server:
  port: 7002

eureka:
  client:
    #    表示不向注册中心注册自己
    fetch-registry: false
#    自己就是注册中心,并不需要检索服务
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7001/eureka

server:
  port: 7001

eureka:
  client:
 	#    表示不向注册中心注册自己
    fetch-registry: false
#    自己就是注册中心,并不需要检索服务
    register-with-eureka: false
    service-url:
      defaultZone: http://localhost:7002/eureka

支付8001配置,其他配置同理

# eureka
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      # 这里修改为注册到两个服务中
      defaultZone: http://localhost:7001/eureka,http://localhost:7002/eureka

Eureka自我保护

自我保护: 某一刻某个微服务不可用了,Eureka不会立即清理微服务,依旧对该微服务的信息进行保存。

自我保护默认开启了 ,有效时间90s。

现在测试恢复为单机版

 eureka:
  instance:
    hostname: localhost7001
    instance-id: 1
    lease-renewal-interval-in-seconds: 0 # 设置保护服务有效访问时间

2、Zookeeper

官网地址

中文地址

详情参考

服务提供

  • 创建项目 cloud-provide-zookeeper-7006
  • 引入依赖
  • 修改配置文件

依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- SpringBoot整合zookeeper客户端 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
            <!--先排除自带的zookeeper3.5.3 防止与3.5.6起冲突-->
            <exclusions>
                <exclusion>
                    <groupId>org.apache.zookeeper</groupId>
                    <artifactId>zookeeper</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--添加zookeeper3.5.6版本-->
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配置文件


server:
  port: 7006

#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-provider-payment
  cloud:
    zookeeper:
      connect-string: 192.168.56.40:2181 # zookeeper服务IP加端口号

主程序

@SpringBootApplication
@EnableDiscoveryClient
public class StartApplication7006 {

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

controller

@RestController
@Slf4j
public class PaymentController {

    @Value("${server.port}")
    private String serverPort;

    @RequestMapping(value = "/payment/zk")
    public String paymentzk(){
        return "springcloud with zookeeper: "+serverPort+"\t"+ UUID.randomUUID().toString();
    }
}

访问地址 http://localhost:7006/payment/zk

zookeeper 显示内容

[zk: 127.0.0.1:2181(CONNECTED) 1] ls -R /
/
/services
/watcher
/zk_testNode1
/zk_testNode2
/zookeeper
/services/cloud-provider-payment
/services/cloud-provider-payment/d9a75d7b-b183-4644-afa1-f5e45bf6b331
/zookeeper/config
/zookeeper/quota
[zk: 127.0.0.1:2181(CONNECTED) 2] 

这两个节点已经进入了说明创建成功!

/services/cloud-provider-payment
/services/cloud-provider-payment/d9a75d7b-b183-4644-afa1-f5e45bf6b331

服务消费

  • 创建项目 cloud-consumer-zookeeper-8006
  • 引入依赖
  • 修改配置文件
  • 添加配置
  • 添加controller

依赖同上面


server:
  port: 8006

#服务别名----注册zookeeper到注册中心名称
spring:
  application:
    name: cloud-consumer-order
  cloud:
    zookeeper:
      connect-string: 192.168.56.40:2181 # zookeeper服务IP加端口号

主程序

@SpringBootApplication
@EnableDiscoveryClient
public class ZookeeperApplication {

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

配置

@Configuration(proxyBeanMethods = false)
public class MyConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

controller

@RestController
@RequestMapping("order")
public class OrderController {

    public static final String INVOKE_URL = "http://cloud-provider-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consume/consul")
    public String paymentInfo(){
        return restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
    }
}

启动程序,访问路径 http://localhost:8006/order/consume/consul

接受内容

[C:\~]$ curl http://localhost:8006/order/consume/consul
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    69  100    69    0     0   7915      0 --:--:-- --:--:-- --:--:--  9857
springcloud with zookeeper: 7006	42139137-5434-4142-ac54-bff5dade5f22

3、Consul

官网地址: https://www.consul.io/docs

Welcome to the Consul documentation! Consul is a multi-networking tool that offers a fully-featured service mesh solution that solves the networking and security challenges of operating microservices and cloud infrastructure (multi-cloud and hybrid cloud). This documentation covers the main concepts of Consul, what problems it can solve, and contains a quick start for using Consul.

欢迎关注Consul 文档 !Consul是一个多网络工具,它提供了一个全功能的服务网格解决方案,解决操作微服务和云基础设施(多云和混合云)的网络和安全挑战。本文档涵盖了Consul的主要概念、它可以解决的问题,并包含使用Consul的快速入门。

下载地址

下载的consul

D:\software\server\consul>.\consul.exe

检查

D:\software\server\consul>consul version
Consul v1.13.1
Revision c6d0f9ec
Build Date 2022-08-11T19:07:00Z
Protocol 2 spoken by default, understands 2 to 3 (agent will automatically use protocol >2 when speaking to compatible agents)

启动

D:\software\server\consul>consul agent -dev
==> Starting Consul agent...
           Version: '1.13.1'
        Build Date: '2022-08-11 19:07:00 +0000 UTC'
           Node ID: 'bd4c58a6-a94e-0a7c-b522-4225f4278c95'
         Node name: 'DESKTOP-RU4KR1S'
        Datacenter: 'dc1' (Segment: '<all>')
            Server: true (Bootstrap: false)
       Client Addr: [127.0.0.1] (HTTP: 8500, HTTPS: -1, gRPC: 8502, DNS: 8600)
      Cluster Addr: 127.0.0.1 (LAN: 8301, WAN: 8302)
           Encrypt: Gossip: false, TLS-Outgoing: false, TLS-Incoming: false, Auto-Encrypt-TLS: false

==> Log data will now stream in as it occurs:

服务提供者

提供consoul服务 cloud-provide-consul-7005

 <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-consul-discovery -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
            <version>2.2.7.RELEASE</version>
        </dependency>

application.yml

server:
  port: 7005
spring:
  application:
    name: consul-provide-payment
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}
@SpringBootApplication
@EnableDiscoveryClient
@RestController
public class StartConsulApplication7005 {

    @Value("{server.port}")
    private String port;

    @RequestMapping(value = "/payment/consul")
    public String home() {
        return "Hello world " + port;
    }

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

}

服务消费者

###consul服务端口号
server:
  port: 8003

spring:
  application:
    name: cloud-consumer-order
  ####consul注册中心地址
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        #hostname: 127.0.0.1
        service-name: ${spring.application.name}

ApplicationContextConfig

@Configuration
public class ApplicationContextConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

OrderController

@RestController
@RequestMapping("/order")
public class OrderController {

    public static final String INVOKE_URL = "http://consul-provide-payment";

    @Resource
    private RestTemplate restTemplate;

    @GetMapping(value = "/consume/consul")
    public String paymentInfo(){
        return restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
    }
}

注册成功

provide

consumer

4、openfen

官网 openfen doc

入门

导入依赖

 <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
            <version>3.1.3</version>
        </dependency>

创建项目 colud-server-openfen-8002

application.yml

server:
  port: 8002
  servlet:
    context-path: /
spring:
  application:
    name: colud-server-openfen-8002
# eureka
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
        prefer-ip-address: true #显示ip地址

主程序类

@EnableEurekaClient //启用服务注册客户端
@SpringBootApplication
@EnableFeignClients // 导入openfen 注解支持
public class ColudServerOpenfen8002Application {

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

}

接口 TestService

@Component //注册为spring组件,交予IOC容器管理
@FeignClient(value = "cloud-payment-service") //添加FeignClient注解,绑定服务提供者。
public interface TestService {
    @GetMapping("/payment/list")
    Result list();
}

controller

@RestController
@RequestMapping("/openfen/payment/test")
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/list")
    public Result list() {
        return testService.list();
    }
}

启动

注意:启动之前先启动 eureka-server 和 payment-server

openfen使用过程

openfen使用

其他操作 参考官方文档

  • 超时
  • 请求错误
  • ……

5、Hystrix

Github

基本介绍

来自官网的一段话

In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency.

在分布式环境中,不可避免地,许多服务依赖性中的一些会失败。 HyStrix是一个库,可帮助您通过添加延迟公差和容错逻辑来控制这些分布式服务之间的交互。 HyStrix通过隔离服务之间的访问点,停止级联失败并提供后备选项来实现这一目标,所有这些都可以提高系统的整体弹性。

注意

官网停止更新

Hystrix is no longer in active development, and is currently in maintenance mode.

Hystrix不再处于主动开发中,目前处于维护模式。

但是 Hystrix 的思想仍然是高级的!仍然有参考价值

构建

依赖

<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-hystrix -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
    <version>2.2.10.RELEASE</version>
</dependency>

配置

server:
  port: 8004
  servlet:
    context-path: /
spring:
  application:
    name: cloud-hystrix-server-2


# eureka
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      defaultZone: http://localhost:7001/eureka

服务降级

单个异常测试

服务不能用了,给出一个基本服务。如果服务请求出错,给定一个基本服务

    @HystrixCommand(fallbackMethod = "paymentTimeoutHandler", 
    commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000"))
    @GetMapping(value = "/timeout/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result paymentTimeout(@PathVariable("id") Integer id) {
        log.info("发送请求2:{}", id);
        return paymentService.paymentTimeout().put("参数信息", id);
    }

// 指定出错给的方法
    public Result paymentTimeoutHandler(Integer id) {
        return Result.error("请求超时了哦·····").put("处理线程池", Thread.currentThread().getName()).put("id", id);
    }

请求

http://localhost:8004/payment/timeout/200

请求内容

{
	"code": 200,
	"id": 200,
	"message": "请求超时了哦·····",
	"处理线程池": "HystrixTimer-1"
}

修改超时时间 ,将timeout改为5000ms

    @HystrixCommand(fallbackMethod = "paymentTimeoutHandler", 
    commandProperties = @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000"))
    @GetMapping(value = "/timeout/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Result paymentTimeout(@PathVariable("id") Integer id) {
        log.info("发送请求2:{}", id);
        return paymentService.paymentTimeout().put("参数信息", id);
    }

发送以上请求得到内容

{
	"code": 200,
	"data": "请求超时",
	"参数信息": 200,
	"message": "请求成功",
	"timeout": 3000,
	"线程池": "hystrix-PaymentController-1"
}

全局异常配置

每个方法配置一个方法,都写,明显代码耦合度过高!现在配置一下全局异常

@Component
@DefaultProperties(groupKey="paymentService",
//命令执行超时时间,默认1000ms
        commandProperties={@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value="5000")},
        threadPoolProperties={@HystrixProperty(name="coreSize",value="10")
                ,@HystrixProperty(name="maxQueueSize",value="1000")},
        threadPoolKey="paymentService"
)
public class FallbackHandler {

    public Result defaultFallbackMethod(){
        return Result.error("全局默认异常处理!");
    }
}

使用eureka、openfen异常,解耦之后

@Component //注册为spring组件,交予IOC容器管理
@FeignClient(value = "cloud-payment-service",fallback =TestServiceException.class) //添加FeignClient注解,绑定服务提供者。
public interface TestService {
    @GetMapping("/payment/list")
    Result list();
}

TestServiceException

public class TestServiceException implements TestService{

    @Override
    public Result list() {
        return Result.error().put("error","兜底信息");
    }
}

服务熔断

认识:

类似于保险丝,当服务访问量达到最大值,直接跳闸!(也可以认为是一种服务降级)

恢复服务熔断:

服务限流

秒杀高并发请求,限制请求数量,比如一秒钟最大请求5个请求……

服务监控

cloud-consumer-hystrix-dashboard9001

 <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
 </dependency>
@SpringBootApplication
@EnableHystrixDashboard
public class HystrixDashboardMain9001 {
    public static void main(String[] args) {
        SpringApplication.run(HystrixDashboardMain9001.class,args);
    }
}

总结

所有配置

@HystrixCommand(fallbackMethod = "fallbackMethod", 
                groupKey = "strGroupCommand", 
                commandKey = "strCommand", 
                threadPoolKey = "strThreadPool",
              
                commandProperties = {
                    // 设置隔离策略,THREAD 表示线程池 SEMAPHORE:信号池隔离
                    @HystrixProperty(name = "execution.isolation.strategy", value = "THREAD"),
                    // 当隔离策略选择信号池隔离的时候,用来设置信号池的大小(最大并发数)
                    @HystrixProperty(name = "execution.isolation.semaphore.maxConcurrentRequests", value = "10"),
                    // 配置命令执行的超时时间
                    @HystrixProperty(name = "execution.isolation.thread.timeoutinMilliseconds", value = "10"),
                    // 是否启用超时时间
                    @HystrixProperty(name = "execution.timeout.enabled", value = "true"),
                    // 执行超时的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnTimeout", value = "true"),
                  
                    // 执行被取消的时候是否中断
                    @HystrixProperty(name = "execution.isolation.thread.interruptOnCancel", value = "true"),
                    // 允许回调方法执行的最大并发数
                    @HystrixProperty(name = "fallback.isolation.semaphore.maxConcurrentRequests", value = "10"),
                    // 服务降级是否启用,是否执行回调函数
                    @HystrixProperty(name = "fallback.enabled", value = "true"),
                    // 是否启用断路器
                    @HystrixProperty(name = "circuitBreaker.enabled", value = "true"),
                    // 该属性用来设置在滚动时间窗中,断路器熔断的最小请求数。例如,默认该值为 20 的时候,如果滚动时间窗(默认10秒)内仅收到了19个请求, 即使这19个请求都失败了,断路器也不会打开。
                    @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "20"),
                  
                    // 该属性用来设置在滚动时间窗中,表示在滚动时间窗中,在请求数量超过 circuitBreaker.requestVolumeThreshold 的情况下,如果错误请求数的百分比超过50, 就把断路器设置为 "打开" 状态,否则就设置为 "关闭" 状态。
                    @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
                    // 该属性用来设置当断路器打开之后的休眠时间窗。 休眠时间窗结束之后,会将断路器置为 "半开" 状态,尝试熔断的请求命令,如果依然失败就将断路器继续设置为 "打开" 状态,如果成功就设置为 "关闭" 状态。
                    @HystrixProperty(name = "circuitBreaker.sleepWindowinMilliseconds", value = "5000"),
                    // 断路器强制打开
                    @HystrixProperty(name = "circuitBreaker.forceOpen", value = "false"),
                    // 断路器强制关闭
                    @HystrixProperty(name = "circuitBreaker.forceClosed", value = "false"),
                    // 滚动时间窗设置,该时间用于断路器判断健康度时需要收集信息的持续时间
                    @HystrixProperty(name = "metrics.rollingStats.timeinMilliseconds", value = "10000"),
                  
                    // 该属性用来设置滚动时间窗统计指标信息时划分"桶"的数量,断路器在收集指标信息的时候会根据设置的时间窗长度拆分成多个 "桶" 来累计各度量值,每个"桶"记录了一段时间内的采集指标。
                    // 比如 10 秒内拆分成 10 个"桶"收集这样,所以 timeinMilliseconds 必须能被 numBuckets 整除。否则会抛异常
                    @HystrixProperty(name = "metrics.rollingStats.numBuckets", value = "10"),
                    // 该属性用来设置对命令执行的延迟是否使用百分位数来跟踪和计算。如果设置为 false, 那么所有的概要统计都将返回 -1。
                    @HystrixProperty(name = "metrics.rollingPercentile.enabled", value = "false"),
                    // 该属性用来设置百分位统计的滚动窗口的持续时间,单位为毫秒。
                    @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "60000"),
                    // 该属性用来设置百分位统计滚动窗口中使用 “ 桶 ”的数量。
                    @HystrixProperty(name = "metrics.rollingPercentile.numBuckets", value = "60000"),
                    // 该属性用来设置在执行过程中每个 “桶” 中保留的最大执行次数。如果在滚动时间窗内发生超过该设定值的执行次数,
                    // 就从最初的位置开始重写。例如,将该值设置为100, 滚动窗口为10秒,若在10秒内一个 “桶 ”中发生了500次执行,
                    // 那么该 “桶” 中只保留 最后的100次执行的统计。另外,增加该值的大小将会增加内存量的消耗,并增加排序百分位数所需的计算时间。
                    @HystrixProperty(name = "metrics.rollingPercentile.bucketSize", value = "100"),
                  
                    // 该属性用来设置采集影响断路器状态的健康快照(请求的成功、 错误百分比)的间隔等待时间。
                    @HystrixProperty(name = "metrics.healthSnapshot.intervalinMilliseconds", value = "500"),
                    // 是否开启请求缓存
                    @HystrixProperty(name = "requestCache.enabled", value = "true"),
                    // HystrixCommand的执行和事件是否打印日志到 HystrixRequestLog 中
                    @HystrixProperty(name = "requestLog.enabled", value = "true"),

                },
                threadPoolProperties = {
                    // 该参数用来设置执行命令线程池的核心线程数,该值也就是命令执行的最大并发量
                    @HystrixProperty(name = "coreSize", value = "10"),
                    // 该参数用来设置线程池的最大队列大小。当设置为 -1 时,线程池将使用 SynchronousQueue 实现的队列,否则将使用 LinkedBlockingQueue 实现的队列。
                    @HystrixProperty(name = "maxQueueSize", value = "-1"),
                    // 该参数用来为队列设置拒绝阈值。 通过该参数, 即使队列没有达到最大值也能拒绝请求。
                    // 该参数主要是对 LinkedBlockingQueue 队列的补充,因为 LinkedBlockingQueue 队列不能动态修改它的对象大小,而通过该属性就可以调整拒绝请求的队列大小了。
                    @HystrixProperty(name = "queueSizeRejectionThreshold", value = "5"),
                }
               )
public String doSomething() {
	...
}

posted @ 2023-04-25 07:33  wuxin001  阅读(35)  评论(0编辑  收藏  举报