SpringCloud Netflix笔记

微服务

微服务:解决接口越来越多,单体应用运行缓慢问题。

踩坑记录

找不到Mapper

***************************
APPLICATION FAILED TO START
***************************

Description:

Field deviceMapper in com.esagent.es.EsDataInit required a bean of type 'com.example.mapper.YourMapper' that could not be found.

The injection point has the following annotations:
- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.example.mapper.YourMapper' in your configuration.

原因是mybatis版本有问题!

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
<!-- 版本需要和其他依赖对上 -->
</dependency>
</dependencies>
</dependencyManagement>

服务间调用

一个服务只调用一张表。当一个服务需要调用其他表时,使用HTTP调用其他微服务。

Commons

新建一个commons模块,用于存放全局使用的Entity

<dependency>
<groupId>com.example</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

RestTemplate

public UserBorrowDetail getUserBorrowDetailByUid(int uid) {
List<Borrow> borrow = mapper.getBorrowsByUid(uid);
//RestTemplate支持多种方式的远程调用
RestTemplate template = new RestTemplate();
//这里通过调用getForObject来请求其他服务,并将结果自动进行封装
//获取User信息
User user = template.getForObject("http://localhost:8082/user/"+uid, User.class);
//获取每一本书的详细信息
List<Book> bookList = borrow
.stream()
.map(b -> template.getForObject("http://localhost:8080/book/"+b.getBid(), Book.class))
.collect(Collectors.toList());
return new UserBorrowDetail(user, bookList);
}

Eureka 注册中心

像上面这样直接调用微服务URL的方法是非常紧耦合的代码。Eureka可以帮我们解决这个问题。

Eureka能够自动注册并发现微服务,然后对服务的状态、信息进行集中管理。当我们需要获取其他服务的信息时,只需要向Eureka进行查询。

添加依赖

  • 父工程
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2024.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
  • Eureka模块
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>

Hystrix 服务熔断(已弃用)

direction: right
服务1 -> 服务2: 请求
服务2 -> 服务3: 请求
服务3 -> 服务4: 请求
服务4 -> 服务5: 故障 {
style.stroke: red
}
服务5.style.stroke-dash: 4
服务5.style.stroke: red

服务降级

当某个微服务宕机时,可以通过Hystrix返回备选方案。

服务熔断

当某个微服务过长时间没有响应,开启熔断器,直接不调用微服务的方法,只调用降级的服务。重新等待一段时间后,才继续尝试调用微服务方法,并根据响应情况关闭熔断器。

OpenFeign 服务降级

OpenFeign 更方便的HTTP客户端请求工具

实现一个FallBackClient类,并继承Client

@Component   // 注意,需要将其注册为Bean,Feign才能自动注入
public class UserFallbackClient implements UserClient{
@Override
public User getUserById(int uid) { // 这里我们自行对其进行实现,并返回我们的替代方案
User user = new User();
user.setName("我是替代方案");
return user;
}
}

Client中指定fallback参数

// fallback参数指定为我们刚刚编写的实现类
@FeignClient(value = "userservice", fallback = UserFallbackClient.class)
public interface UserClient {

@RequestMapping("/user/{uid}")
User getUserById(@PathVariable("uid") int uid);
}

最后,在配置文件中开启熔断支持

feign:
circuitbreaker:
enabled: true

Gateway 网关

并不是所有的微服务都需要直接暴露给外部调用。使用网关隔离内外网、转发微服务请求,并实现负载均衡。

Gateway <-> Internet
Eureka -> Gateway: 查询服务列表
Internet.shape: cloud


Gateway -> 微服务1
多个实例: {
微服务2-1 <-> 微服务2-2
}
Gateway -> 多个实例: 负载均衡

微服务1 -> Eureka: 注册
多个实例 -> Eureka: 注册

gateway-server模块

依赖项

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>

路由配置

server:
port: gateway-port
eureka:
client:
service-url:
defaultZone: http://localhost:eureka-port-1/eureka, http://localhost:eureka-port-2/eureka
spring:
application:
name: gateway
cloud:
gateway:
# 配置路由,注意这里是个列表,每一项都包含了很多信息
routes:
- id: borrow-service # 路由名称
uri: lb://borrowservice # 路由的地址,lb表示使用负载均衡到微服务,也可以使用http正常转发
predicates: # 路由规则,断言什么请求会被路由
- Path=/borrow/** # 只要是访问的这个路径,一律都被路由到上面指定的服务
- id: book-service
uri: lb://bookservice
predicates:
- Path=/book/**
filters: # 添加过滤器
- AddRequestHeader=Header, HeaderContent
# 添加请求头信息

Config 配置中心

Spring Cloud Config可以在云端集中地管理所有环境中应用程序的外部配置。

服务端配置

<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
server:
port: 8700
spring:
application:
name: configserver
cloud:
config:
server:
git:
# 远程仓库地址
uri: http://git.../config-repo
# 默认分支设定为远程分支的名称
default-label: main
eureka:
client:
service-url:
defaultZone: http://localhost:eureka-port-1/eureka, http://localhost:eureka-port-1/eureka
@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigApplication.class, args);
}
}

配置文件命名规则

{服务名称}-{环境}.yml

根据网址即可访问配置

http://your-host:your-port/{服务名称}/{环境}/{分支名称}

客户端配置

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

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
# boostrap.yml
spring:
cloud:
config:
# 名称,其实就是文件名称
name: bookservice
# 配置服务器的地址
uri: http://localhost:8700
# 环境
profile: prod
# 分支
label: main

微服务CAP原则

Consistency、Availability、Partition-Tolerance 三者不可同时保证,最多只能保证两个。

  • Consistency 一致性:在分布式系统中,所有数据备份在同一时刻都是同样的最新值。
  • Availability 可用性:系统非故障节点收到每个请求都必须回应。熔断和降级就是维持可用性。
  • Parition-Tolerance 分区容错性:分布式系统中,节点之间的网络可能因为故障导致不连通。需要容忍这些意外情况。

高容忍性,将数据存放在多个节点,复制次数增加,一致性难以保证;
高一致性,更新所有节点数据所需的时间变长,可用性会降低。

AC 可用性 + 一致性

高可用性和高一致性,意味着某个节点数据更新后,需要尽可能快地同步数据到其他节点,对网络要求非常高。在实际情况下,网络不可靠,容易丢包。最好的办法就是违反分布式系统的概念,将数据集中存放。

CP 一致性 + 分区容错性

高一致性,意味着某个节点数据更新后,需要完全同步给其他节点;高分区容错性,意味着我们将容忍网络的不可靠问题,网络出现卡顿也继续传输。因此服务会在一段时间内完全失效,可用性无法保证。

AP 可用性 + 分区容错性

高可用性和高分区容错性,意味着保证服务可用,而放弃节点数据的高度统一,使数据在不一致的情况下进行响应。
虽然这种办法拿不到最新的数据,但是只要数据同步在后台继续运行,在某个时刻一定能够成功同步数据,实现最终的一致性。

AP是实际上最能被接受的方案。

例如Eureka集群就使用了AP方案,在一台服务器宕机的情况下立刻切换另外一台服务器,保证可用性,即使这台服务器的数据可能不是最新数据。