SpringCloud学习笔记(3)

Zookeeper

springCloud 整合 zookeeper

  • zookeeper是一个分布式协调工具,可以实现注册中心功能
  • 关闭Linux服务器防火墙后动zookeeper服务器
  • zookeeper服务器取代Eureka服务器,zk作为服务注册中心
提供者

新建模块provider-payment8004

pom:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.wzg.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>provider-payment8004</artifactId>

<dependencies>
<!--springcloud 整合 zookeeper 组件-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<!--zk发现-->
<artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</exclusion>
</exclusions>
</dependency>

<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>

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

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

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>

<!--mysql-connector-java-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>

<!--jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</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>

<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.wzg.springcloud</groupId>
<artifactId>api-commons</artifactId>
<version>${project.version}</version>
</dependency>

</dependencies>

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

</project>
yml:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 8004

spring:
application:
name: payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver #mysql驱动包
# driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/wzg_db01?useUnicode=true&characterEncoding-utr-8&useSSL=false
username: root
password: 123
druid:
test-while-idle: true
validation-query: SELECT 1
cloud:
zookeeper:
connect-string: 192.168.200.130:2181 # zk地址
主启动类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.wzg.springcloud;

/**
* @author whlie(true){learn}
*/
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient // 以后用这个就可以了,不用eureka了
public class PaymentMain8004 {
public static void main(String[] args){
SpringApplication.run(PaymentMain8004.class, args);
}
}
Controller :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.wzg.springcloud.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 javax.annotation.Resource;
import java.util.UUID;

/**
* @author whlie(true){learn}
*/
@RestController
@Slf4j
public class PaymentController {

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

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

问题1:因为引入了mybatis但没有指定mapper运行时idea发出警告

解决方法:8004模块中暂时还没用到sql我直接就把依赖移除了

问题2:设置了jdbc或mysql未指定数据源运行时idea发出警告

解决方法:在yml中写入指定数据源在主启动类上添加@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

问题3:idea运行时报错:KeeperErrorCode = ConnectionLoss

解决方法:我一开始用的zookeeper是3.7.1,Linux上用的也是这个但还是报错,我就换回了视频上的3.4.9

问题4:zookeeper版本冲突,和slf4j冲突

解决方法:在依赖中用exclusions排除其他版本的包

问题5:java.net.ConnectException: Connection timed out: no further information

解决方法:这个问题很常见,可能是Linux防火墙没关,我是关了防火墙但没开放2181端口。重新打开防火墙并开放2181端口就OK了

消费者

创建测试zookeeper作为服务注册中心的 消费者 模块 cloud-customerzk-order80

主启动类、pom文件、yml文件和提供者的类似

config
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.wzg.springcloud.config;

import org.springframework.boot.SpringBootConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
* @author whlie(true){learn}
*/
@SpringBootConfiguration
public class ApplicationContextConfig {
@Bean
@LoadBalanced // 继续加上这个
public RestTemplate getTemplate(){
return new RestTemplate();
}
}
controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.wzg.springcloud.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
* @author whlie(true){learn}
*/
@RestController
@Slf4j
public class OrderZKController {
public static final String INVOKE_URL="http://payment-service"; //和原来一样

@Resource
private RestTemplate restTemplate;

@RequestMapping("/consumer/payment/zk")
public String paymentInfo(){
String result = restTemplate.getForObject(INVOKE_URL + "/payment/zk",String.class);
return result;
}
}

测试:

localhost:8004/payment/zk

localhost/consumer/payment/zk

关于 zookeeper 的集群搭建,目前使用较少,而且在 yml 文件中的配置也是类似,以列表形式写入 zookeeper 的多个地址即可,而且zookeeper 集群,在 hadoop的笔记中也有记录。总而言之,只要配合zookeeper集群,以及yml文件的配置就能完成集群搭建

后面会用ribbon代替RestTemplate

Consul

consul也是服务注册中心的一个实现,是由go语言写的。官网地址: https://www.consul.io/intro 中文地址: https://www.springcloud.cc/spring-cloud-consul.html
Consul是一套开源的分布式服务发现和配置管理系统。
提供了微服务系统中的服务治理,配置中心,控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网络。

  • 服务发现:提供HTTP和DNS两种发现方式
  • 健康监测:支持多种方式,HTTP、TCP、Docker、Shell脚本定制化
  • KV存储:Key、Value的存储方式
  • 多数据中心:Consul支持多数据中心
  • 可视化Web界面

安装并运行

下载地址:https://www.consul.io/downloads.html

打开下载的压缩包,只有一个exe文件,实际上是不用安装的,在exe文件所在目录打开dos窗口使用即可。

使用开发模式启动:consul agent -dev

访问8500端口,即可访问首页

提供者

新建提供者模块:providerconsul-service8006

pom 文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.wzg.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>providerconsul-payment8006</artifactId>

<dependencies>
<!--springcloud consul server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

<!-- springboot整合Web组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

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

<!-- 日常通用jar包 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</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>

<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.wzg.springcloud</groupId>
<artifactId>api-commons</artifactId>
<version>${project.version}</version>
</dependency>

</dependencies>

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

</project>

yml 文件:

1
2
3
4
5
6
7
8
9
10
11
server:
port: 8006
spring:
application:
name: payment-service
cloud:
consul:
host: localhost
port: 8500
discovery: # 指定注册对外暴露的服务名称
service-name: ${spring.application.name}

主启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.wzg.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
* @author whlie(true){learn}
*/
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient // 提供者
public class ConsulProviderMain8006 {
public static void main(String[] args) {
SpringApplication.run(ConsulProviderMain8006.class,args);
}
}

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.wzg.springcloud.controller;

/**
* @author whlie(true){learn}
*/
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.RestController;

import java.util.UUID;

@RestController
@Slf4j
public class PaymentController {

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

@GetMapping(value = "/payment/consul")
public String paymentConsul(){
return "springcloud with consul: "+serverPort+"\t"+ UUID.randomUUID().toString();
}
}

消费者

新建 一个 在80端口的 消费者模块。pom和yml和提供者的类似,主启动类不用说,模块为consumerconsul-order80

bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.wzg.springcloud.bean;

/**
* @author whlie(true){learn}
*/
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 {

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

}

controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.wzg.springcloud.controller;

/**
* @author whlie(true){learn}
*/
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 INVOME_URL = "http://payment-service";

@Resource
private RestTemplate restTemplate;

@GetMapping("/consumer/payment/consul")
public String payment (){
String result = restTemplate.getForObject(INVOME_URL+"/payment/consul",String.class);
return result;
}
}

总结

组件名 语言 CAP 服务健康检查 对外暴露接口 SpringCloud集合
Eureka java AP 可配支持 HTTP 已集成
Consul Go CP 支持 HTTP/DNS 已集成
Zookeeper java CP 支持 客户端 已集成

CAP:

  • C:Consitency 强一致性
  • A:Available 可用性
  • P:Partition tolerance 分区容错性

CAP理论关注粒度是数据,而不是整体系统设计的

Ribbon

简介

SpringCloud Ribbon是基于NetfIixRibbon实现的一套客户端负载均衡的工具。

简单的说,Ribbon是Neix发布的开源项目,主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均衡算法。

LB负载均衡(LoadBalance)是什么?

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

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

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

  • Nginx是服务器负载均衡(集中式LB),客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。
  • Ribbon是本地负载均衡(进程内LB),在调用微服务接口时候,会在注册中心上获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。
  • 集中式LB:即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5,也可以是软件,如nginx),由该设施负责把访问请求通过某种策略转发至服务的提供方;
  • 进程内LB:将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器!Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。

Ribbon在工作时分成两步:

  • 第一步先选择Eureka Server,它优先选择在同一个区域内负载较少的server
  • 第二步再根据用户指定的策略,在从server取到的服务注册列表中选择一个地址。
    • 其中Ribbon提供了多种策略:比如轮询、随相和根据响应时间加权。

上面在eureka时,确实实现了负载均衡机制,那是因为 eureka-client包里面自带着ribbon

一句话,Ribbon 就是 负载均衡 + RestTemplate 调用。实际上不止eureka的jar包有,zookeeper的jar包,还有consul的jar包都包含了他,就是上面使用的服务调用。

1597385486515

如果自己添加,在 模块的 pom 文件中引入:

1
2
3
4
5
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
1234
RestTemplate

对于RestTemplate 的一些说明:

有两种请求方式:post和get ,还有两种返回类型:object 和 Entity

  • getForObject()/getForEntity()
    • Object:返回对象响应体中数据转化成的对象,基本上可以理解成json
    • Entity:返回对象是ResponseEntity对象,包含了响应中的一些重要信息,比如响应头、响应状态码、响应体等
    • 返回的entity.getBody()即得到了Object
  • postForObject()/postForEntity()

负载均衡

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

Ribbon 负载均衡规则类型:

  • com.netflix.loadbalancer.RoundRobinRule:轮询
  • com.netflix.loadbalancer.RandomRule:随机
  • com.netfIix.IoadbaIancer.RetryRuIe:先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试,获取可用的服务
  • WeightedResponseTimeRule:对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择
  • BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务
  • AvailabilityFilteringRule:先过滤掉故障实例,再选择并发较小的实例
  • ZoneAvoidanceRule:默认规则,复合判断server所在区域的性能和server的可用性选择服务器

img

配置负载均衡规则:

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

这个自定义配置类不能放在@ComponentScan 所扫描的当前包下以及子包下,否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的了。

注意上面说的,而Springboot主启动类上的 @SpringBootApplication 注解,相当于加了@ComponentScan注解,会自动扫描当前包及子包,所以注意不要放在SpringBoot主启动类的包内。

创建包:

  • java
    • com.hh
      • myrule
        • MySelfRule.java
      • springcloud
        • 主启动类

在这个包下新建 MySelfRule类:

1
2
3
4
5
6
7
8
9
10
11
12
package com.dkf.myrule;

import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;

@Configuration
public class MySelfRule {
@Bean
public IRule myrule(){
return new RandomRule(); //负载均衡规则定义为随机
}
}

然后在主启动类上添加如下注解 @RibbonClient:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.dkf.springcloud;

import com.dkf.myrule.MySelfRule;

@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@RibbonClient(name="CLOUD-PROVIDER-SERVICE", configuration = MySelfRule.class)//指定该负载均衡规则对哪个提供者服务使用 , 加载自定义规则的配置类
public class OrderMain80 {

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

轮询算法原理

负载均衡轮询算法

rest接口第几次请求次数 % 服务器集群总数量 = 实际调用服务器位置下标

每次服务器重启后,rest接口计数从1开始。

1597387609476

ribbon源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private AtomicInteger nextServerCyclicCounter;

public Server choose(ILoadBalancer lb, Object key) {

Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();

int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);

if (server == null) {
/* Transient. */
Thread.yield();
continue;
}

if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}

// Next.
server = null;
}

if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: " + lb);
}
return server;
}
private int incrementAndGetModulo(int modulo) {
for (;;) {
int current = nextServerCyclicCounter.get();//获取原子的值
int next = (current + 1) % modulo;
if (nextServerCyclicCounter.compareAndSet(current, next)) //CAS
return next;
}
}

手写负载算法:cas+自旋

首先8001、8002服务controller层加上

1
2
3
4
@GetMapping("/payment/lb")
public String getPaymentLB() {
return SERVER_PORT;
}

LoadBalancer接口:

1
2
3
4
5
6
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;

public interface LoadBalancer {
ServiceInstance instances(List<ServiceInstance> serviceInstances);
}

实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.springframework.cloud.client.ServiceInstance;
import java.sql.SQLOutput;

@Component
public class MyLB implements LoadBalancer {

private AtomicInteger atomicInteger = new AtomicInteger(0);

private final int getAndIncrement() {
int current;
int next;

do {
current = this.atomicInteger.get();
next = current >= Integer.MAX_VALUE ? 0 : current + 1;
} while (!atomicInteger.compareAndSet(current, next));
System.out.println("第几次访问,次数next:" + next);
return next;
}

@Override
public ServiceInstance instances(List<ServiceInstance> serviceInstances) {
int index = getAndIncrement() % serviceInstances.size();
return serviceInstances.get(index);
}
}

controller类中添加:

1
2
3
4
5
6
7
8
9
10
11
12
@GetMapping("/consumer/payment/lb")
public String getPaymentLB() {
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");//获得总的提供者数
if (instances == null || instances.size() <= 0) {
return null;
}

ServiceInstance serviceInstance = loadBalancer.instances(instances);//传入总的实例数
URI uri = serviceInstance.getUri();

return restTemplate.getForObject(uri + "/payment/lb", String.class);
}

OpenFeign

概述

这里和之前学的dubbo很像,例如消费者的controller 可以调用提供者的 service层方法,但是不一样,它貌似只能调用提供者的 controller,即写一个提供者项目的controller的接口,消费者来调用这个接口方法,就还是相当于是调用提供者的 controller ,和RestTemplate 没有本质区别

Feign能干什么:

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

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

Feign集成了Ribbon

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

Feign OpenFeign
Feign是SpringCloud组件中的一个轻量级RESTful的HTTP服务客户端。Feign内置了Ribbon,用来做客户端负载均衡,去调用服务注册中心的服务。Feign的使用方式是:使用Feign的注解定义接口,调用这个接口,就可以调用服务注册中心的服务 OpenFeign是SpringCloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的下的接囗,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
org.springframework.cloud spring-cloud-starter-feign org.springframework.cloud spring-cloud-starter-openfeign

消费端

新建consumer-feign-order80模块

feign用在消费端,feign自带负载均衡配置,所以不用手动配置

pom :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>cloud2022</artifactId>
<groupId>com.wzg.springcloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>consumer-feign-order80</artifactId>

<dependencies>
<!-- Open Feign,他里面也有ribbon,所以有负载均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.wzg.springcloud</groupId>
<artifactId>api-commons</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

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

</project>

yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
server:
port: 80

eureka:
client:
register-with-eureka: false
service-url: # 配置服务中心,openFeign去里面找服务
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/


#设置feign客户端超时时间(OpenFeign默认支持ribbon)
feign:
client:
config:
default:
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ReadTimeout: 5000

logging:
level:
# feign日志以什么级别监控哪个接口
com.atguigu.springcloud.service.PaymentFeignService: debug

主启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wzg.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

/**
* @author whlie(true){learn}
*/
@SpringBootApplication
@EnableFeignClients //关键注解
public class OrderFeignMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderFeignMain80.class, args);
}
}

service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.wzg.springcloud.service;

import com.wzg.springcloud.entities.CommonResult;
import com.wzg.springcloud.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;

/**
* @author whlie(true){learn}
*/
@Component //别忘了添加这个
@FeignClient(value = "PAYMENT-SERVICE") //服务名称,要和eureka上面的一致才行
public interface PaymentService {
//这个就是provider 的controller层的方法定义。
@GetMapping(value = "/payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);

@GetMapping(value = "/payment/feign/timeout")
public String paymentFeignTimeout();
}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.wzg.springcloud.controller;

import com.wzg.springcloud.entities.CommonResult;
import com.wzg.springcloud.entities.Payment;
import com.wzg.springcloud.service.PaymentService;
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;

/**
* @author whlie(true){learn}
*/
//使用起来就相当于是普通的service。
@RestController
public class OrderFeignController {
@Resource
private PaymentService paymentService;//动态代理

@GetMapping("/feign/payment/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentService.getPaymentById(id);
}

//消费方80
@GetMapping(value = "/consumer/payment/feign/timeout")
public String paymentFeignTimeout(){
// OpenFeign客户端一般默认等待1秒钟
return paymentService.paymentFeignTimeout();
}
}

超时控制

Openfeign默认超时等待为一秒,在消费者里面配置超时时间

开启日志打印

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

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

日志级别:

  • NONE.默认的,不显示任何日志;
  • BASIC,仅记录请求方法、URL、响应状态码及执行时间;
  • HEADERS:除了BASIC中定义的信息之外,还有请求和响应的头信息
  • FULL:除了HEADERS中定义的信息之外,还有请求和响应的正文及元数据。

首先写一个config配置类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.wzg.springcloud.config;

/**
* @author whlie(true){learn}
*/
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文件中开启日志打印配置


SpringCloud学习笔记(3)
https://yztldxdz.top/2022/09/18/SpringCloud学习笔记(3)/
发布于
2022年9月18日
许可协议