Spring全家桶--SpringCloud(初级),35岁程序员的人生感悟

7.3.2 Eureka集群环境构建

创建cloud-eureka-server7002工程,过程参考7.3

  • 找到C:\Windows\System32\drivers\etc路径下的hosts文件,修改映射配置添加进hosts文件

127.0.0.1 eureka7001.com

127.0.0.1 eureka7002.com

  • 修改cloud-eureka-server7001配置文件

server:

port: 7001

eureka:

instance:

hostname: eureka7001.com #eureka服务端的实例名称

client:

register-with-eureka: false #false表示不向注册中心注册自己。

fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务

service-url:

#集群指向其它eureka

defaultZone: http://eureka7002.com:7002/eureka/

#单机就是7001自己

#defaultZone: http://eureka7001.com:7001/eureka/

  • 修改cloud-eureka-server7002配置文件

server:

port: 7002

eureka:

instance:

hostname: eureka7002.com #eureka服务端的实例名称

client:

register-with-eureka: false #false表示不向注册中心注册自己。

fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务

service-url:

#集群指向其它eureka

defaultZone: http://eureka7001.com:7001/eureka/

#单机就是7002自己

#defaultZone: http://eureka7002.com:7002/eureka/

测试

image-20220321113103629

7.3.3 订单支付微服务注册进Eureka集群

将支付服务8001微服务,订单服务80微服务发布到上面2台Eureka集群配置中

将它们的配置文件的eureka.client.service-url.defaultZone进行修改

eureka:

client:

#表示是否将自己注册进Eurekaserver默认为true。

register-with-eureka: true

#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡

fetchRegistry: true

service-url:

defaultZone: http://eureka7001.com:7001/eureka, http://eureka7002.com:7002/eureka

测试

image-20220321143910262

7.3.4 支付微服务集群配置

集群架构图如下:

image-20220321150615376

参考cloud-provicer-payment8001

基本和cloud-provicer-payment8001模块一致,需要修改的地方是yml文件中的端口

为了查看负载均衡的效果在8001/8002的Controller,添加serverPort

1.新建cloud-provider-payment8002

2.改POM

3.写YML - 端口8002

4.主启动

5.业务类

6.修改8001/8002的Controller,添加serverPort

package com.caq.cloud.controller;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import com.caq.cloud.service.PaymentService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

/**

*/

@RestController

@Slf4j

public class PaymentController {

@Resource

private PaymentService paymentService;

@Value(“${server.port}”)

private String serverPort;

@PostMapping(value = “/payment/create”)

public CommonResult create(@RequestBody Payment payment) {

int result = paymentService.create(payment);

log.info(“*****插入结果:” + result);

if (result > 0) {

return new CommonResult(200, “插入数据库成功,serverport:” + serverPort, result);

} else {

return new CommonResult(444, “插入数据库失败”, null);

}

}

@GetMapping(value = “/payment/get/{id}”)

public CommonResult getPaymentById(@PathVariable(“id”) Long id) {

Payment payment = paymentService.getPaymentById(id);

if (payment != null) {

return new CommonResult(200, “查询成功,serverport:” + serverPort, payment);

} else {

return new CommonResult(444, “没有对应记录”, null);

}

}

}

负载均衡

  • 为了让提供者实现负载均衡还需要在resetRestTemplate配置类中添加@LoadBalanced注解

  • cloud-consumer-order80订单服务访问地址不能写死

package com.caq.cloud.controller;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;

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

import org.springframework.web.bind.annotation.PathVariable;

import org.springframework.web.bind.annotation.RequestBody;

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

import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderController {

// public static final String PAYMENT_URL = “http://localhost:8001”;

public static final String PAYMENT_URL = “http://CLOUD-PAYMENT-SERVICE”;

@Resource

private RestTemplate resetTemplate;

@GetMapping(“/consumer/payment/create”)

public CommonResult create(Payment payment){

return resetTemplate.postForObject(PAYMENT_URL+“/payment/create”,payment,CommonResult.class);

}

@GetMapping(“/consumer/payment/get/{id}”)

public CommonResult getPayment(@PathVariable(“id”) Long id){

return resetTemplate.getForObject(PAYMENT_URL+“/payment/get/”+id,CommonResult.class);

}

}

package com.caq.cloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.client.RestTemplate;

@Configuration

public class ApplicationContextConfig {

//通过注解的方式注入,这样我们的容器里就有这个resttemplate对象

@Bean

@LoadBalanced

public RestTemplate getRestTemplate() {

return new RestTemplate();

}

}

测试

先要启动EurekaServer,7001/7002服务

再要启动服务提供者provider,8001/8002服务

image-20220321151713339

Eureka服务之间是:相互注册,相互守望

7.3.5 actuator微服务信息完善

在eureka服务端查看提供者服务器名称(将IP地址,换成可读性高的名字)

修改cloud-provider-payment8001,cloud-provider-payment8002

修改部分 :

  1. YML - eureka.instance.instance-id

  2. prefer-ip-address: true

instance:

instance-id: payment8001

prefer-ip-address: true

instance:

instance-id: payment8002

prefer-ip-address: true

修改之后

eureka主页将显示payment8001,payment8002代替原来显示的IP地址。

image-20220321165449588

7.3.6 服务发现Discovery

对于注册进eureka里面的微服务,可以通过服务发现来获得该服务的信息

  • 修改cloud-provider-payment8001的Controller

package com.caq.cloud.controller;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import com.caq.cloud.service.PaymentService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.cloud.client.ServiceInstance;

import org.springframework.cloud.client.discovery.DiscoveryClient;

import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;

import java.util.List;

/**

*/

@RestController

@Slf4j

public class PaymentController {

@Resource

private DiscoveryClient discoveryClient;

@GetMapping(value = “/payment/discovery”)

public Object discovery() {

List services = discoveryClient.getServices();

for (String service : services) {

log.info(“**********element:” + service);

}

List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);

for (ServiceInstance instance : instances) {

log.info(instance.getInstanceId()+“\t”+instance.getHost()+“\t”+instance.getPort()+“\t”+instance.getUri());

}

return this.discoveryClient;

}

}

  • 8001主启动类

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication

@EnableEurekaClient

@EnableDiscoveryClient

public class PaymentMain8001 {

public static void main(String[] args) {

SpringApplication.run(PaymentMain8001.class, args);

}

}

测试:

image-20220321174418262

image-20220321174510190

7.4 Eureka自我保护理论知识


保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。

如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式:

image-20220321174602844

导致原因

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

属于CAP里面的AP分支。

为什么会产生Eureka自我保护机制?

为了EurekaClient可以正常运行,防止与EurekaServer网络不通情况下,EurekaServer不会立刻将EurekaClient服务剔除

什么是自我保护模式?

默认情况下,如果EurekaServer在一定时间内没有接收到某个微服务实例的心跳,EurekaServer将会注销该实例(默认90秒)。但是当网络分区故障发生(延时、卡顿、拥挤)时,微服务与EurekaServer之间无法正常通信,以上行为可能变得非常危险了——因为微服务本身其实是健康的,此时本不应该注销这个微服务。Eureka通过“自我保护模式”来解决这个问题——当EurekaServer节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式

自我保护机制∶默认情况下EurekaClient定时向EurekaServer端发送心跳包

如果Eureka在server端在一定时间内(默认90秒)没有收到EurekaClient发送心跳包,便会直接从服务注册列表中剔除该服务,但是在短时间( 90秒中)内丢失了大量的服务实例心跳,这时候Eurekaserver会开启自我保护机制,不会剔除该服务(该现象可能出现在如果网络不通但是EurekaClient为出现宕机,此时如果换做别的注册中心如果一定时间内没有收到心跳会将剔除该服务,这样就出现了严重失误,因为客户端还能正常发送心跳,只是网络延迟问题,而保护机制是为了解决此问题而产生的)。

在自我保护模式中,Eureka Server会保护服务注册表中的信息,不再注销任何服务实例。

它的设计哲学就是宁可保留错误的服务注册信息,也不盲目注销任何可能健康的服务实例。一句话讲解:好死不如赖活着。

综上,自我保护模式是一种应对网络异常的安全保护措施。它的架构哲学是宁可同时保留所有微服务(健康的微服务和不健康的微服务都会保留)也不盲目注销任何健康的微服务。使用自我保护模式,可以让Eureka集群更加的健壮、稳定。

7.4.1 怎么禁止自我保护

  • 在eurekaServer端7001处设置关闭自我保护机制

出厂默认,自我保护机制是开启的

使用eureka.server.enable-self-preservation = false可以禁用自我保护模式

eureka:

server:

#关闭自我保护机制,保证不可用服务被及时踢除

enable-self-preservation: false

eviction-interval-timer-in-ms: 2000

关闭效果:

spring-eureka主页会显示出一句:

THE SELF PRESERVATION MODE IS TURNED OFF. THIS MAY NOT PROTECT INSTANCE EXPIRY IN CASE OF NETWORK/OTHER PROBLEMS.

  • 生产者客户端eureakeClient端8001

默认:

eureka.instance.lease-renewal-interval-in-seconds=30

eureka.instance.lease-expiration-duration-in-seconds=90

eureka:

instance:

instance-id: payment8001

prefer-ip-address: true

#心跳检测与续约时间

#开发时没置小些,保证服务关闭后注册中心能即使剔除服务

#Eureka客户端向服务端发送心跳的时间间隔,单位为秒(默认是30秒)

lease-renewal-interval-in-seconds: 1

#Eureka服务端在收到最后一次心跳后等待时间上限,单位为秒(默认是90秒),超时将剔除服务

lease-expiration-duration-in-seconds: 2

  • 测试

  • 7001和8001都配置完成

  • 先启动7001再启动8001

结果:先关闭8001,马上被删除了

八、Zookeeper

==========================================================================

zookeeper是一个分布式协调工具,可以实现注册中心功能.zookeeper服务器取代Eureka服务器,zk作为服务注册中心

8.1 安装


下载地址

http://archive.apache.org/dist/zookeeper/

解压安装即可~

[root@rabbitmq bin]# pwd

/myzookeeper/zookeeper-3.4.9/bin

进入/apache-zookeeper-3.5.8-bin/conf目录,将zoo_simple.cfg原地复制一份命名为zoo.cfg

启动服务

[root@rabbitmq bin]# ./zkServer.sh start

ZooKeeper JMX enabled by default

Using config: /myzookeeper/zookeeper-3.4.9/bin/…/conf/zoo.cfg

Starting zookeeper … STARTED

更多安装问题请看Bug记录章节

8.2 订单服务注册进zookeeper


1.新建名为cloud-provider-payment8004的Maven工程。

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-provider-payment8004

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

org.springframework.boot

spring-boot-starter-web

com.caq.cloud

cloud-api-commons

${project.version}

org.springframework.cloud

spring-cloud-starter-zookeeper-discovery

org.apache.zookeeper

zookeeper

org.apache.zookeeper

zookeeper

3.4.9

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 8004

spring:

application:

name: cloud-provider-payment

#服务别名----注册zookeeper到注册中心名称

cloud:

zookeeper:

connect-string: 10.216.8.247:2181

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication

@EnableDiscoveryClient//该注解用于向使用consul或者zookeeper作为注册中心时注册服务

public class PaymentMain8004 {

public static void main(String[] args) {

SpringApplication.run(PaymentMain8004.class,args);

}

}

5.业务类

package com.caq.cloud.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.RequestMapping;

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

import java.util.UUID;

@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:8004/payment/zk

验证测试2 :接着用zookeeper客户端操作

image-20220321202204808

image-20220321202334505

8.3 临时还是持久节点


ZooKeeper的服务节点是临时节点,没有Eureka那含情脉脉。

8.4 订单服务注册进zookeeper


1.新建cloud-consumerzk-order80

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-consumerzk-order80

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

org.springframework.boot

spring-boot-starter-web

org.springframework.cloud

spring-cloud-starter-zookeeper-discovery

org.apache.zookeeper

zookeeper

org.apache.zookeeper

zookeeper

3.4.9

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 80

spring:

application:

name: cloud-consumer-order

#服务别名----注册zookeeper到注册中心名称

cloud:

zookeeper:

connect-string: 10.216.8.247:2181

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication

@EnableDiscoveryClient

public class OrderZKMain80 {

public static void main(String[] args) {

SpringApplication.run(OrderZKMain80.class, args);

}

}

5.业务类

config

package com.caq.cloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.client.RestTemplate;

@Configuration

public class ApplicationContextConfig {

@Bean

@LoadBalanced

public RestTemplate getRestTemplate(){

return new RestTemplate();

}

}

controller

package com.caq.cloud.controller;

import com.sun.org.glassfish.external.statistics.annotations.Reset;

import lombok.extern.slf4j.Slf4j;

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

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

import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderZKController {

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

@Resource

private RestTemplate restTemplate;

@GetMapping(value = “/consumer/payment/zk”)

public String paymentInfo() {

String result = restTemplate.getForObject(INVOKE_URL + “/payment/zk”, String.class);

return result;

}

}

测试

运行ZooKeeper服务端,cloud-consumerzk-order80,cloud-provider-payment8004。

打开ZooKeeper客户端:

[zk: localhost:2181(CONNECTED) 12] ls /services

[cloud-provider-payment, cloud-consumer-order]

image-20220322160409781

九、Consul

=======================================================================

9.1 Consul简介


Consul官网

Consul下载地址

What is Consul?

Consul is a service mesh solution providing a full featured control plane with service discovery, configuration, and segmentation functionality. Each of these features can be used individually as needed, or they can be used together to build a full service mesh. Consul requires a data plane and supports both a proxy and native integration model. Consul ships with a simple built-in proxy so that everything works out of the box, but also supports 3rd party proxy integrations such as Envoy. link

Consul是一个服务网格解决方案,它提供了一个功能齐全的控制平面,具有服务发现、配置和分段功能。这些特性中的每一个都可以根据需要单独使用,也可以一起用于构建全服务网格。Consul需要一个数据平面,并支持代理和本机集成模型。Consul船与一个简单的内置代理,使一切工作的开箱即用,但也支持第三方代理集成,如Envoy。

consul

英 [ˈkɒnsl] 美 [ˈkɑːnsl]

n. 领事

Consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp 公司用Go语言开发。

提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网格,总之Consul提供了一种完整的服务网格解决方案。

它具有很多优点。包括:基于raft协议,比较简洁;支持健康检查,同时支持HTTP和DNS协议支持跨数据中心的WAN集群提供图形界面跨平台,支持Linux、Mac、Windows。

能干嘛?

  • 服务发现 - 提供HTTP和DNS两种发现方式。

  • 健康监测 - 支持多种方式,HTTP、TCP、Docker、Shell脚本定制化

  • KV存储 - Key、Value的存储方式

  • 多数据中心 - Consul支持多数据中心

  • 可视化Web界面

怎么玩

9.2 安装并运行Consul


我们在windows上安装,有一点要注意,这个终端要一直处于打开的状态。关掉的话就相当于是把Consul服务给关掉了!

官网安装说明

windows版解压缩后,得consul.exe,打开cmd

image-20220322171438065

  • 开发模式启动consul agent -dev

  • 访问测试

image-20220322165310074

9.3 服务提供者注册进Consul


1.新建Module支付服务provider8006

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-providerconsul-payment

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

com.caq.cloud

cloud-api-commons

${project.version}

org.springframework.cloud

spring-cloud-starter-consul-discovery

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

cn.hutool

hutool-all

RELEASE

test

cn.hutool

hutool-all

RELEASE

test

3.YML

服务发现Discovery

###consul服务端口号

server:

port: 8006

spring:

application:

name: consul-provider-payment

####consul注册中心地址

cloud:

consul:

host: localhost

port: 8500

discovery:

#hostname: 127.0.0.1

#设置consul服务发现时的名字

service-name: ${spring.application.name}

4.主启动类

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication

@EnableDiscoveryClient

public class PaymentMain8006 {

public static void main(String[] args) {

SpringApplication.run(PaymentMain8006.class,args);

}

}

5.业务类Controller

package com.caq.cloud.controller;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.web.bind.annotation.RequestMapping;

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

import java.util.UUID;

/**

  • 发现和前面几个注册中心都是一样,我们主要学习的是什么?

  • 主要学的是什么?把微服务注册到我们Consul里面

*/

@RestController

@Slf4j

public class PaymentController

{

@Value(“${server.port}”)

private String serverPort;

@RequestMapping(value = “/payment/consul”)

public String paymentConsul()

{

return “springcloud with zookeeper: “+serverPort+”\t”+ UUID.randomUUID().toString();

}

}

测试

  • http://localhost:8006/payment/consul

  • http://localhost:8500 - 会显示provider8006

image-20220322171824938

9.4 服务

9.4 服务消费者注册进Consul


1.新建Module消费服务order80 - cloud-consumerconsul-order80

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-customerconsul-order80

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

org.springframework.cloud

spring-cloud-starter-consul-discovery

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

###consul服务端口号

server:

port: 80

spring:

application:

name: cloud-consumer-order

####consul注册中心地址

cloud:

consul:

host: localhost

port: 8500

discovery:

#hostname: 127.0.0.1

service-name: ${spring.application.name}

4.主启动类

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication

@EnableDiscoveryClient

public class OrderConsulMain80 {

public static void main(String[] args) {

SpringApplication.run(OrderConsulMain80.class, args);

}

}

5.配置Bean

package com.caq.cloud.config;

import org.springframework.cloud.client.loadbalancer.LoadBalanced;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.client.RestTemplate;

@Configuration

public class ApplicationContextConfig {

@Bean

@LoadBalanced

public RestTemplate getRestTemplate(){

return new RestTemplate();

}

}

6.Controller

package com.caq.cloud.controller;

import lombok.extern.slf4j.Slf4j;

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

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

import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderConsulController {

public static final String INVOKE_URL = “http://consul-provider-payment”;

@Resource

private RestTemplate restTemplate;

@GetMapping(value = “/consumer/payment/consul”)

public String paymentInfo() {

String result = restTemplate.getForObject(INVOKE_URL + “/payment/consul”, String.class);

return result;

}

}

测试

运行consul,cloud-providerconsul-payment8006,cloud-consumerconsul-order80

http://localhost:8500/ 主页会显示出consul,cloud-providerconsul-payment8006,cloud-consumerconsul-order80三服务。

image-20220322220520524

十、三个注册中心异同点

==========================================================================

| 组件名 | 语言CAP | 服务健康检查 | 对外暴露接口 | Spring Cloud集成 |

| — | — | — | — | — |

| Eureka | Java | AP | 可配支持 | HTTP |

| Consul | Go | CP | 支持 | HTTP/DNS |

| Zookeeper | Java | CP | 支持客户端 | 已集成 |

CAP:

  • C:Consistency (强一致性)

  • A:Availability (可用性)

  • P:Partition tolerance (分区容错性)

image-20220322225718060

最多只能同时较好的满足两个。

CAP理论的核心是:一个分布式系统不可能同时很好的满足一致性,可用性和分区容错性这三个需求。

因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:

CA - 单点集群,满足—致性,可用性的系统,通常在可扩展性上不太强大。

CP - 满足一致性,分区容忍必的系统,通常性能不是特别高。

AP - 满足可用性,分区容忍性的系统,通常可能对一致性要求低一些。

AP架构(Eureka)

当网络分区出现后,为了保证可用性,系统B可以返回旧值,保证系统的可用性。

结论:违背了一致性C的要求,只满足可用性和分区容错,即AP

img

CP架构(ZooKeeper/Consul)

当网络分区出现后,为了保证一致性,就必须拒接请求,否则无法保证一致性。

结论:违背了可用性A的要求,只满足一致性和分区容错,即CP。

img

CP 与 AP 对立同一的矛盾关系。

十一、Ribbon入门介绍

============================================================================

ribbon

英 [ˈrɪbən] 美 [ˈrɪbən]

n. (用于捆绑或装饰的)带子;丝带;带状物;狭长的东西;绶带;勋带

Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端负载均衡的工具。

简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。

简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

Github - Ribbon

Ribbon目前也进入维护模式。

Ribbon未来可能被Spring Cloud LoadBalacer替代。

LB负载均衡(Load Balance)是什么?

简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA (高可用)。

常见的负载均衡有软件Nginx,LVS,硬件F5等。

Ribbon本地负载均衡客户端VS Nginx服务端负载均衡区别

Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。

Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。

集中式LB

即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;

进程内LB

将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。

Ribbon就属于进程内LB, 它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

一句话说明白Ribbon

负载均衡 + RestTemplate调用

11.1 Ribbon的负载均衡和Rest调用


架构说明

总结:Ribbon其实就是一个软负载均衡的客户端组件,它可以和其他所需请求的客户端结合使用,和Eureka结合只是其中的一个实例。

image-20220322231716434

Ribbon在工作时分成两步:

  • 第一步先选择EurekaServer ,它优先选择在同一个区域内负载较少的server。

  • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。

其中Ribbon提供了多种策略:比如轮询、随机和根据响应时间加权。

POM

n. 实体,独立存在体

先前工程项目没有引入spring-cloud-starter-ribbon也可以使用ribbon。

这是因为spring-cloud-starter-netflix-eureka-client自带了spring-cloud-starter-ribbon引用。

RestTemplate Java Doc

getForObject() / getForEntity() - GET请求方法

getForObject():返回对象为响应体中数据转化成的对象,基本上可以理解为Json。

getForEntity():返回对象为ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等。

@GetMapping(“/consumer/payment/getForEntity/{id}”)

public CommonResult getPayment2(@PathVariable(“id”) Long id)

{

ResponseEntity entity = restTemplate.getForEntity(PAYMENT_URL+“/payment/get/”+id,CommonResult.class);

if(entity.getStatusCode().is2xxSuccessful()){

return entity.getBody();//getForObject()

}else{

return new CommonResult<>(444,“操作失败”);

}

}

11.2 Ribbon默认自带的负载规则


lRule:根据特定算法中从服务列表中选取一个要访问的服务

image-20220322233923708

  • RoundRobinRule 轮询

  • RandomRule 随机

  • RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重

  • WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择

  • BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

  • AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例

  • ZoneAvoidanceRule 默认规则,复合判断server所在区域的性能和server的可用性选择服务器

11.3 Ribbon负载规则替换


1.修改cloud-consumer-order80

2.注意配置细节

官方文档明确给出了警告:

这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下,

否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

(也就是说不要将Ribbon配置类与主启动类同包)

3.新建package - com.caq.myrule

4.在com.caq.myrule下新建MySelfRule规则类

import com.netflix.loadbalancer.IRule;

import com.netflix.loadbalancer.RandomRule;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class MySelfRule {

@Bean

public IRule myRule(){

return new RandomRule();

}

}

5.主启动类添加@RibbonClient

import com.lun.myrule.MySelfRule;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication

@EnableEurekaClient

//添加到此处

@RibbonClient(name = “CLOUD-PAYMENT-SERVICE”, configuration = MySelfRule.class)

public class OrderMain80

{

public static void main( String[] args ){

SpringApplication.run(OrderMain80.class, args);

}

}

11.4 测试


开启cloud-eureka-server7001,cloud-consumer-order80,cloud-provider-payment8001,cloud-provider-payment8002

浏览器-输入http://localhost/consumer/payment/get/1

返回结果中的serverPort在8001与8002两种间反复横跳。

11.5 Ribbon默认负载轮询算法原理


默认负载轮训算法: rest接口第几次请求数 % 服务器集群总数量 = 实际调用服务器位置下标,每次服务重启动后rest接口计数从1开始。

List instances = discoveryClient.getInstances(“CLOUD-PAYMENT-SERVICE”);

如:

List [0] instances = 127.0.0.1:8002

List [1] instances = 127.0.0.1:8001

8001+ 8002组合成为集群,它们共计2台机器,集群总数为2,按照轮询算法原理:

当总请求数为1时:1%2=1对应下标位置为1,则获得服务地址为127.0.0.1:8001

当总请求数位2时:2%2=О对应下标位置为0,则获得服务地址为127.0.0.1:8002

当总请求数位3时:3%2=1对应下标位置为1,则获得服务地址为127.0.0.1:8001

当总请求数位4时:4%2=О对应下标位置为0,则获得服务地址为127.0.0.1:8002

如此类推…

十二、OpenFeign是什么

==============================================================================

Feign is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same HttpMessageConverters used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign. link

Feign是一个声明式WebService客户端。使用Feign能让编写Web Service客户端更加简单。它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HttpMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。

feign

英 [feɪn] 美 [feɪn]

v. 假装,装作,佯装(有某种感觉或生病、疲倦等)

官方文档

Github地址

Feign能干什么

Feign旨在使编写Java Http客户端变得更容易。

前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模版化的调用方法。但是在实际开发中,由于对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的实现下,我们只需创建一个接口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。

Feign集成了Ribbon

利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同的是,通过feign只需要定义服务绑定接口且以声明式的方法,优雅而简单的实现了服务调用。

Feign和OpenFeign两者区别

Feign是Spring Cloud组件中的一个轻量级RESTful的HTTP服务客户端Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务。

引入依赖

org.springframework.cloud

spring-cloud-starter-feign

OpenFeign是Spring Cloud在Feign的基础上支持了SpringMVC的注解,如@RequesMapping等等。OpenFeign的@Feignclient可以解析SpringMVc的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。

org.springframework.cloud

spring-cloud-starter-openfeign

12.1 OpenFeign服务调用


image-20220323091255508

接口+注解:微服务调用接口 + @FeignClient

1.新建cloud-consumer-feign-order80

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-consumer-feign-order80

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

org.springframework.cloud

spring-cloud-starter-openfeign

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

com.caq.cloud

cloud-api-commons

${project.version}

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 80

eureka:

client:

register-with-eureka: false

service-url:

defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication

@EnableFeignClients

public class OrderFeignMain80 {

public static void main(String[] args) {

SpringApplication.run(OrderFeignMain80.class, args);

}

}

5.业务类

业务逻辑接口+@FeignClient配置调用provider服务

新建PaymentFeignService接口并新增注解@FeignClient

package com.caq.cloud.service;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.stereotype.Component;

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

import org.springframework.web.bind.annotation.PathVariable;

@Component

@FeignClient(value = “CLOUD-PAYMENT-SERVICE”)

public interface PaymentFeignService {

@GetMapping(value = “/payment/get/{id}”)

public CommonResult getPaymentById(@PathVariable(“id”) Long id);

@GetMapping(value = “/payment/feign/timeout”)

public String paymentFeignTimeout();

}

控制层Controller

package com.caq.cloud.controller;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import com.caq.cloud.service.PaymentFeignService;

import lombok.extern.slf4j.Slf4j;

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

import org.springframework.web.bind.annotation.PathVariable;

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

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderFeignController {

@Resource

private PaymentFeignService paymentFeignService;

@GetMapping(value = “/consumer/payment/get/{id}”)

public CommonResult getPaymentById(@PathVariable(“id”) Long id) {

return paymentFeignService.getPaymentById(id);

}

@GetMapping(value = “/consumer/payment/feign/timeout”)

public String paymentFeignTimeout() {

// OpenFeign客户端一般默认等待1秒钟

return paymentFeignService.paymentFeignTimeout();

}

}

测试

先启动2个eureka集群7001/7002

再启动2个微服务8001/8002

启动OpenFeign启动

http://localhost/consumer/payment/get/1

Feign自带负载均衡配置项

image-20220323091823733

12.2 OpenFeign超时控制


超时设置,故意设置超时演示出错情况

1.服务提供方8001/8002故意写暂停程序

image-20220323100205970

服务消费方80添加超时方法PaymentFeignService

package com.caq.cloud.service;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.stereotype.Component;

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

import org.springframework.web.bind.annotation.PathVariable;

@Component

@FeignClient(value = “CLOUD-PAYMENT-SERVICE”)

public interface PaymentFeignService {

@GetMapping(value = “/payment/get/{id}”)

public CommonResult getPaymentById(@PathVariable(“id”) Long id);

@GetMapping(value = “/payment/feign/timeout”)

public String paymentFeignTimeout();

}

package com.caq.cloud.controller;

import com.caq.cloud.entities.CommonResult;

import com.caq.cloud.entities.Payment;

import com.caq.cloud.service.PaymentFeignService;

import lombok.extern.slf4j.Slf4j;

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

import org.springframework.web.bind.annotation.PathVariable;

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

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderFeignController {

@Resource

private PaymentFeignService paymentFeignService;

@GetMapping(value = “/consumer/payment/get/{id}”)

public CommonResult getPaymentById(@PathVariable(“id”) Long id) {

return paymentFeignService.getPaymentById(id);

}

@GetMapping(value = “/consumer/payment/feign/timeout”)

public String paymentFeignTimeout() {

// OpenFeign客户端一般默认等待1秒钟

return paymentFeignService.paymentFeignTimeout();

}

}

测试

多次刷新http://localhost/consumer/payment/feign/timeout

将会跳出错误Spring Boot默认错误页面,主要异常:feign.RetryableException:Read timed out executing GET http://CLOUD-PAYMENT-SERVCE/payment/feign/timeout。

OpenFeign默认等待1秒钟,超过后报错

YML文件里需要开启OpenFeign客户端超时控制

#设置feign客户端超时时间(OpenFeign默认支持ribbon)(单位:毫秒)

ribbon:

#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间

ReadTimeout: 5000

#指的是建立连接后从服务器读取到可用资源所用的时间

ConnectTimeout: 5000

12.3 OpenFeign日志增强


日志打印功能

Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign 中 Http请求的细节。

说白了就是对Feign接口的调用情况进行监控和输出

日志级别

NONE:默认的,不显示任何日志;

BASIC:仅记录请求方法、URL、响应状态码及执行时间;

HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息;

FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

配置日志bean

import feign.Logger;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

@Configuration

public class FeignConfig

{

@Bean

Logger.Level feignLoggerLevel()

{

return Logger.Level.FULL;

}

}

YML文件里需要开启日志的Feign客户端

logging:

level:

feign日志以什么级别监控哪个接口

com.caq.cloud.service.PaymentFeignService: debug

后台日志查看

image-20220323101150244

十三、Hystrix

=========================================================================

13.1 初始Hystrix


hystrix

n. 豪猪属;猬草属;豪猪;豪猪亚属

分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。

服务雪崩

多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.

对于高流量的应用来说,单一的后避依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。

所以,通常当你发现一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩。

Hystrix是什么

Hystrix是一个用于处理分布式系统的延迟容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

"断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

熔断器–远程微服务的异常处理兜底方法

13.1.1 Hystrix停更

能干嘛

  • 服务降级

  • 服务熔断

  • 接近实对的监控

Hystrix官宣,停更进维

link

  • 被动修bugs

  • 不再接受合并请求

  • 不再发布新版本

13.1.2 Hystrix的关键概念

服务降级

服务器忙,请稍后再试,不让客户端等待并立刻返回一个友好提示,fallback

哪些情况会出发降级

  1. 程序运行导常

  2. 超时

  3. 服务熔断触发服务降级

  4. 线程池/信号量打满也会导致服务降级

服务熔断

类比保险丝达到最大服务访问后,直接拒绝访问,拉闸限电,然后调用服务降级的方法并返回友好提示。

服务的降级 -> 进而熔断 -> 恢复调用链路

服务限流

秒杀高并发等操作,严禁一窝蜂的过来拥挤,大家排队,一秒钟N个,有序进行。

13.2 Hystrix支付微服务构建


将cloud-eureka-server7001改配置成单机版

1.新建cloud-provider-hygtrix-payment8001

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-provider-hygtrix-payment8001

org.springframework.cloud

spring-cloud-starter-netflix-hystrix

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

com.caq.cloud

cloud-api-commons

${project.version}

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 8001

spring:

application:

name: cloud-provider-hystrix-payment

eureka:

client:

register-with-eureka: true

fetch-registry: true

service-url:

#defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka

defaultZone: http://eureka7001.com:7001/eureka

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication

@EnableEurekaClient

public class PaymentHystrixMain8001 {

public static void main(String[] args) {

SpringApplication.run(PaymentHystrixMain8001.class, args);

}

}

5.业务类

service

package com.caq.cloud.service;

import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service

public class PaymentService {

/**

*/

public String paymentInfo_OK(Integer id)

{

return “线程池: “+Thread.currentThread().getName()+” paymentInfo_OK,id: “+id+”\t”+“O(∩_∩)O哈哈~”;

}

public String paymentInfo_TimeOut(Integer id)

{

try { TimeUnit.MILLISECONDS.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }

return “线程池: “+Thread.currentThread().getName()+” id: “+id+”\t”+“O(∩_∩)O哈哈~”+" 耗时(秒): 3";

}

}

controller

package com.caq.cloud.controller;

import com.caq.cloud.service.PaymentService;

import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Value;

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

import org.springframework.web.bind.annotation.PathVariable;

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

import javax.annotation.Resource;

@RestController

@Slf4j

public class PaymentController

{

@Resource

private PaymentService paymentService;

@Value(“${server.port}”)

private String serverPort;

@GetMapping(“/payment/hystrix/ok/{id}”)

public String paymentInfo_OK(@PathVariable(“id”) Integer id)

{

String result = paymentService.paymentInfo_OK(id);

log.info("*****result: "+result);

return result;

}

@GetMapping(“/payment/hystrix/timeout/{id}”)

public String paymentInfo_TimeOut(@PathVariable(“id”) Integer id)

{

String result = paymentService.paymentInfo_TimeOut(id);

log.info("*****result: "+result);

return result;

}

}

测试

启动eureka7001

启动cloud-provider-hystrix-payment8001

访问

image-20220323162720214

13.2.1 JMeter高并发压测后卡顿

Apache JMeter™应用程序是开源软件,是一个100%纯Java应用程序,设计用于加载测试功能行为和测量性能。它最初是为测试Web应用程序而设计的,但后来扩展到了其他测试功能。

JMeter一个可以模拟多个请求的软件

开启Jmeter,来20000个并发压死8001,20000个请求都去访问paymentInfo_TimeOut服务

Jmeter压测结论

上面还是服务提供者8001自己测试,假如此时外部的消费者80也来访问,那消费者只能干等,最终导致消费端80不满意,服务端8001直接被拖慢。

13.2.2 订单微服务调用支付服务出现卡顿

1.新建 - cloud-consumer-feign-hystrix-order80

2.POM

<?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”>

cloud

com.caq.cloud

1.0-SNAPSHOT

4.0.0

cloud-consumer-feign-hystrix-order80

<maven.compiler.source>8</maven.compiler.source>

<maven.compiler.target>8</maven.compiler.target>

org.springframework.cloud

spring-cloud-starter-openfeign

org.springframework.cloud

spring-cloud-starter-netflix-hystrix

org.springframework.cloud

spring-cloud-starter-netflix-eureka-client

com.caq.cloud

cloud-api-commons

${project.version}

org.springframework.boot

spring-boot-starter-web

org.springframework.boot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 80

eureka:

client:

register-with-eureka: false

service-url:

defaultZone: http://eureka7001.com:7001/eureka/

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication

@EnableFeignClients

//@EnableHystrix

public class OrderHystrixMain80

{

public static void main(String[] args)

{

SpringApplication.run(OrderHystrixMain80.class,args);

}

}

5.业务类

package com.caq.cloud.service;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.stereotype.Component;

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

import org.springframework.web.bind.annotation.PathVariable;

@Component

@FeignClient(value = “CLOUD-PROVIDER-HYSTRIX-PAYMENT” /,fallback = PaymentFallbackService.class/)

public interface PaymentHystrixService

{

@GetMapping(“/payment/hystrix/ok/{id}”)

public String paymentInfo_OK(@PathVariable(“id”) Integer id);

@GetMapping(“/payment/hystrix/timeout/{id}”)

public String paymentInfo_TimeOut(@PathVariable(“id”) Integer id);

}

package com.caq.cloud.controller;

import com.caq.cloud.service.PaymentHystrixService;

import lombok.extern.slf4j.Slf4j;

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

import org.springframework.web.bind.annotation.PathVariable;

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

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderHystirxController {

@Resource

private PaymentHystrixService paymentHystrixService;

@GetMapping(“/consumer/payment/hystrix/ok/{id}”)

public String paymentInfo_OK(@PathVariable(“id”) Integer id)

{

String result = paymentHystrixService.paymentInfo_OK(id);

return result;

}

@GetMapping(“/consumer/payment/hystrix/timeout/{id}”)

public String paymentInfo_TimeOut(@PathVariable(“id”) Integer id) {

String result = paymentHystrixService.paymentInfo_TimeOut(id);

return result;

}

}

测试

2W个线程压8001

消费端80微服务再去访问正常的Ok微服务8001地址

http://localhost/consumer/payment/hystrix/ok/32

消费者80被拖慢

原因:8001同一层次的其它接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕。

正因为有上述故障或不佳表现才有我们的降级/容错/限流等技术诞生。

13.2.3 降级容错解决的维度要求

超时导致服务器变慢(转圈) - 超时不再等待

出错(宕机或程序运行出错) - 出错要有兜底

解决:

  • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级。

  • 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级。

  • 对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级。

13.3 Hystrix之服务降级


13.3.1 Hystrix之服务降级订单侧fallback

fallback

n. 应急计划,应变计划;减少,削减;储备物,备用物;退路;可依靠之物

adj. 退守的;应变的

降级配置 - @HystrixCommand

8001先从自身找问题

设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处埋,作服务降级fallback。

8001fallback

业务类启用 - @HystrixCommand报异常后如何处理

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
img

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

MySQL50道高频面试题整理:

oot

spring-boot-starter-actuator

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

3.YML

server:

port: 80

eureka:

client:

register-with-eureka: false

service-url:

defaultZone: http://eureka7001.com:7001/eureka/

4.主启动

package com.caq.cloud;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringBootApplication

@EnableFeignClients

//@EnableHystrix

public class OrderHystrixMain80

{

public static void main(String[] args)

{

SpringApplication.run(OrderHystrixMain80.class,args);

}

}

5.业务类

package com.caq.cloud.service;

import org.springframework.cloud.openfeign.FeignClient;

import org.springframework.stereotype.Component;

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

import org.springframework.web.bind.annotation.PathVariable;

@Component

@FeignClient(value = “CLOUD-PROVIDER-HYSTRIX-PAYMENT” /,fallback = PaymentFallbackService.class/)

public interface PaymentHystrixService

{

@GetMapping(“/payment/hystrix/ok/{id}”)

public String paymentInfo_OK(@PathVariable(“id”) Integer id);

@GetMapping(“/payment/hystrix/timeout/{id}”)

public String paymentInfo_TimeOut(@PathVariable(“id”) Integer id);

}

package com.caq.cloud.controller;

import com.caq.cloud.service.PaymentHystrixService;

import lombok.extern.slf4j.Slf4j;

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

import org.springframework.web.bind.annotation.PathVariable;

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

import javax.annotation.Resource;

@RestController

@Slf4j

public class OrderHystirxController {

@Resource

private PaymentHystrixService paymentHystrixService;

@GetMapping(“/consumer/payment/hystrix/ok/{id}”)

public String paymentInfo_OK(@PathVariable(“id”) Integer id)

{

String result = paymentHystrixService.paymentInfo_OK(id);

return result;

}

@GetMapping(“/consumer/payment/hystrix/timeout/{id}”)

public String paymentInfo_TimeOut(@PathVariable(“id”) Integer id) {

String result = paymentHystrixService.paymentInfo_TimeOut(id);

return result;

}

}

测试

2W个线程压8001

消费端80微服务再去访问正常的Ok微服务8001地址

http://localhost/consumer/payment/hystrix/ok/32

消费者80被拖慢

原因:8001同一层次的其它接口服务被困死,因为tomcat线程池里面的工作线程已经被挤占完毕。

正因为有上述故障或不佳表现才有我们的降级/容错/限流等技术诞生。

13.2.3 降级容错解决的维度要求

超时导致服务器变慢(转圈) - 超时不再等待

出错(宕机或程序运行出错) - 出错要有兜底

解决:

  • 对方服务(8001)超时了,调用者(80)不能一直卡死等待,必须有服务降级。

  • 对方服务(8001)down机了,调用者(80)不能一直卡死等待,必须有服务降级。

  • 对方服务(8001)OK,调用者(80)自己出故障或有自我要求(自己的等待时间小于服务提供者),自己处理降级。

13.3 Hystrix之服务降级


13.3.1 Hystrix之服务降级订单侧fallback

fallback

n. 应急计划,应变计划;减少,削减;储备物,备用物;退路;可依靠之物

adj. 退守的;应变的

降级配置 - @HystrixCommand

8001先从自身找问题

设置自身调用超时时间的峰值,峰值内可以正常运行,超过了需要有兜底的方法处埋,作服务降级fallback。

8001fallback

业务类启用 - @HystrixCommand报异常后如何处理

小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-4qx7DpRF-1711085423786)]
[外链图片转存中…(img-UO6W4n3r-1711085423787)]
[外链图片转存中…(img-z0qCUsra-1711085423787)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频

如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-iLyxCCtW-1711085423788)]

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

MySQL50道高频面试题整理:

[外链图片转存中…(img-2Cgk6QDH-1711085423788)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值