微服务
微服务:解决接口越来越多,单体应用运行缓慢问题。
踩坑记录
找不到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 template = new RestTemplate(); 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>
|
<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 public class UserFallbackClient implements UserClient{ @Override public User getUserById(int uid) { User user = new User(); user.setName("我是替代方案"); return user; } }
|
在Client
中指定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 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); } }
|
配置文件命名规则
根据网址即可访问配置
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>
|
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方案,在一台服务器宕机的情况下立刻切换另外一台服务器,保证可用性,即使这台服务器的数据可能不是最新数据。