第三部分:微服务01

1.认识微服务

1.1 单体架构

复制代码
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署;
优点:架构简单、部署成本低;
缺点:团队协作成本高、系统发布效率低、系统可用性差;
单体架构适合开发功能相对简单,规模较小的项目;

1.2 微服务架构

复制代码
微服务架构:是服务化思想指导下的一套最佳实践架构方案,服务化,就是把单体架构中的功能模块拆分为多个独立项目。
拆分要求:粒度小(单一功能)、团队自治、服务自治;

1.3 SpringCloud

复制代码
SpringCloud是目前国内使用最广泛的微服务框架,官网;
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验;

2.微服务拆分

2.1 熟悉商城(基础电商项目)

复制代码
商城模块:用户模块、商品模块、购物车模块、订单模块、支付模块
1. 登录
2. 搜索商品
3. 购物车
4. 下单
5. 支付

2.2 服务拆分原则

复制代码
1. 什么时候拆
创业型项目:先采用单体架构,快速开发,快速试错。随着规模扩大,逐渐拆分;
确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦;

2. 怎么拆
从拆分目标来说,要做到:
- 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高;
- 低耦合:每个微服务的功能要相对独立,尽量减少对其他微服务的依赖;

从拆分的方式来说,一般包含两种方式:
- 纵向拆分(垂直拆分):按照业务模块来拆分;
- 横向拆分(水平拆分):抽取公共服务,提高复用性;

2.3 拆分服务(基于2.1的商城项目拆分)

复制代码
微服务项目在企业中有两种不同的结构:
1. 独立Project:将项目中拆分成N个微服务,而非模块,每个微服务都是独立Project,完全分开,完全独立,完全解耦,适用于大项目,每个微服务比较复杂,对商城建立一个文件夹,在该文件夹中存放N个微服务;
2. Maven聚合:将商城创建成Project,而每个微服务不再是project,而是Module,推适用于中小型项目;

拆分商品服务
将商城项目中与商品管理相关的功能拆分到一个微服务module中,命名为item-service
3. 创建module
4. 添加所需依赖
5. 启动类(修改扫描mapper)
6. 配置文件(.yml,修改其中的port不要冲突,微服务要起个名字,独立数据库连接)
7. 具体代码(实体类、mapper、service、controller)

拆分购物车
将商城项目中与购物车有关的功能拆分到一个微服务module中,命名为cart-service
同上

2.4 服务调用

复制代码
当需要完成微服务拆分后,某些数据在不同服务,无法直接调用本地方法查询数据,需要使用RestTemplate发送http请求,实现远程调用;

前端如何向后端发送网络请求?
答:前端发送http请求;

后端如何发送http请求?
Spring提供了一个RestTemplate工具,可以方便的实现Http请求的发送,使用步骤如下:
1. 注入RestTemplate到Spring容器(写入到启动类中);
@Bean
public RestTemplate restTemplate(){
	return new RestTemplate();
}
2. 发起远程调用
public <T> ResponseEntity<T> exchage(
	String url,   //请求路径
	HttpMethod method,  //请求方式
	@Nullable HttpEntity<?> requestEntity,  //请求实体,可以为空
	Class<T> responseType,  //返回值类型
	Map<String,?> uriVariables  //请求参数
)

服务调用具体实现:
3. 启动类中写Bean
@Bean
public RestTemplate restTemplate(){
	return new RestTemplate();
}

4. 在需要调用服务的service中注入restTemplate
(推荐使用构造函数 或者 使用@RequiredArgsConstructor注解,并在类中定义private final RestTemplate restTemplate);
@Autowired
private RestTemplate restTemplate;

5. 调用服务
简单常见的调用方法:
restTemplate.getXXX
restTemplate.postXXX
restTemplate.deleteXXX
restTemplate.putXXX

(1)利用RestTemplate发起http请求,得到http的响应
restTemplate.exchange(
	"http://localhost:8081/items?ids={ids}",
	HttpMethod.GET,
	null,
	new ParameterizedTypeReference<List<ItemDTO>>(){},
	Map.of("ids",Collutil.join(itemIds,","))
)
讲解:
Collutil.join(itemIds,","):将set类型的数据转换成字符串类型;
(2)解析响应
if(!response.getStatusCode().is2xxSuccessful()){   //验证查询结果是否成功
	//查询失败,直接结束
	return;
}else{  //查询成功
		List<ItemDTO> items = response.getBody();
}
(3)可以直接使用解析的响应结果

3.服务治理

3.1 注册中心原理

复制代码
【服务提供者】暴露服务接口,供其它服务调用                             
	 |									
	 | 1)注册服务信息 					
	 |  心跳续约
【注册中心】调用其它服务提供的接口
	 |
	 | 2)订阅item-service的信息
	 | 推送变更
【服务调用者】记录并监控微服务各实例状态,推送服务变更信息
3)负载均衡(多种算法)
4)服务调用者远程调用服务提供者提供的接口

消费者如何知道提供者的地址?
答:服务提供者会在启动时注册自己信息到注册中心,消费者可以从注册中心订阅和拉去服务信息;
消费者如何得知服务状态变更?
答:服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时注册中心将异常服务剔除,并通知订阅了该服务的消费者;
当提供者有多个实例时,消费者该选择哪一个?
答:消费者可以通过负载均衡算法,从多个实例中选择一个;

3.2 Nacos注册中心

复制代码
Nacos是目前国内企业中占比最多的注册中心组件,它是阿里巴巴的产品,目前已经加入了SpringCloudAlibaba中;
Nocas是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台;
安装nacos注册中心

3.3 服务注册

复制代码
服务注册步骤如下:
1)引入nacos discovery依赖:
<dependency>
	<groupId>com.alibaba.cloud</group>
	<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2)配置Nacos地址
spring:
	application:
		name: item-service    #服务名称
	cloud:
		nacos:
			server-addr: 192.168.150.101:8848  #nacos地址

3.4 服务发现

复制代码
消费者需要连接nacos以拉取和订阅服务,因此服务发现的前两步与服务注册是一样,后面再加上服务调用即可:
(1)引入nacos discovery依赖
(2)配置nacos地址
(3)服务发现
private final DiscoveryClient discoveryClient;
private void handleCartItems(List<CartVO> vos){
	//1.根据服务名称,拉取服务的实例列表
	List<ServiceInstance> instances=discoveryClient.getInstances("item-service");
	//2.负载均衡,挑选一个实例
	ServiceInstance instance=instance.get(RandomUtil.randomInt(instance.size()));
	//3.获取实例的IP和端口
	URI uri=instance.getUri();
	//...略
}

4.OpenFeign

4.1 快速入门

复制代码
OpenFeign是一个【声明式的http客户端】,是SpringCloud在Eureka公司开源的Feign基础上改造而来,官方地址:https://github.com/OpenFeign/feign
其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送;
java 复制代码
//2.1发现item-service服务的实例列表
List<ServiceInstance> instances=discoveryClient.getInstances("item-service");  //item-service服务名称,根据服务名才能拉取服务的实例列表
//2.2负载均衡,挑选一个实例
ServiceInstance instance=instances.get(RandomUtil.randomInt(instances.size));
//2.3发送请求,查询商品
ResponseEntity<List<ItemDTO>> response=restTemplate.exchange(   //请求方式和路径,根据请求方式和路径确定要请求的资源地址,访问到对应的接口
	instance.getUri()+"/items?ids={ids}",  //请求路径
	HttpMethod.GET,   //请求方式
	null,  //请求实体
	new ParamterizedTypeReference<List<ItemDTO>>(){},   //返回值类型,知道了返回值类型,才能将接收到的json结果处理为对应的Java实体
	Map.of("ids",CollUtil.join(itemIds,","))   //请求参数,请求参数,是调用接口必不可少的要求
);
java 复制代码
//OenFeign被SpringCloud自动装配,实现起来非常简单:
//(1)引入依赖,包括OpenFeign和负载均衡组件SpringCloudLoadBalancer
<!--OpenFeign-->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-openfeign</artifactId>
	</dependency>
<!--负载均衡-->
	<dependency>
		<groupId>org.springframework.cloud</groupId>
		<artifactId>spring-cloud-starter-loadbalancer</artifactId>
	</dependency>
	
//(2)启动类中,通过@EnableFeignClient注解,启用OpenFeign功能
	@EnableFeignClients
	@SpringBootApplication
	public class CartApplication{	//...略}
	
//(3)编写FeignClient
@FeignClient(value="item-service")
public interface ItemClient{
	@GetMapping("/items")
	List<ItemDTO> queryItemByIds(@RequestParam("ids") Collentioon<Long> ids);
}

//(4)使用FeignClient,实现远程调用
private final ItemClient itemClient;
List<ItemDTO> items=itemClient.queryItemByIds(List.of(1,2,3));

4.2 连接池

复制代码
OpenFeign对Http请求做了优雅的伪装,不过其底层发起http请求,依赖于其它的框架,这些框架可以自己选择,包括一下三种:
1. HttpURLConnection:默认实现,不支持连接池
2. Apache HttpClient:支持连接池
3. OKHttp:支持连接池
具体源码可以参考FeignBlockingLoadBalancerClient类中的delegate成员变量;

OpenFeign整合OKHttp的步骤如下:
(1)引入依赖
<dependency>
	<groupId>io.github.openfeign</groupId>
	<artifactId>feign-okhttp</artifactId>
</dependency>
(2)开启连接池功能
feign:
	okhttp:
		enabled: true   #开启OKHttp连接池支持

4.3 最佳实践

复制代码
将都可能用到的实体类和接口都放到一个公共的模块,在想要使用这些公共内容的模块的pom中引入公共模块;
当定义的FeignClient不在SpringBootApplication的扫描包范围时,这些FeignClient无法使用,有两种发视解决:
方式一:在启动类中指定FeignClient所在包
	@EnableFeignClient(basePackages="com.hmall.api.clients")
方式二:指定FeignClient字节码
	@EnableFeignClients(clients = {UserClient.class})

4.4 日志输出

复制代码
根据日志信息查询执行出错的详细问题所在。
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志,而且其日志级别有4级:
NONE:不记录任何日志信息,这是默认值
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间;
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息;
FULL:记录所有请求和响应的明细,包括头信息,请求体,源数据;
由于Feign默认的日志级别就是NONE,所以默认我们看不到请求日志;

要自定义日志级别需要声明一个类型为Logger.Level的Bean,在其中定义日志级别:
public class DefaultFeignConfig{
	@Bean
	public Logger.Level feignLogLevel(){
		return Logger.level.FULL;
	}
}
但此时这个Bean并未生效,要想配置某个FeignClient的日志,可以在@FeignClient注解中声明:
@FeignClient(value="item-service",configuration=DefaultFeignConfig.class)
如果想要全局配置,让所有FeignClient都按照这个日志配置,则需要在启动类上加上@EnableFeignClients注解中声明:
@EnableFeignClients(defaultConfiguration=DefaultFeignConfig.class)
相关推荐
水银嘻嘻2 小时前
web 自动化之 KDT 关键字驱动详解
运维·自动化
fanly112 小时前
凯亚物联网增加MQTT设备功能测试
微服务·surging microservice
熊大如如2 小时前
Java 反射
java·开发语言
Vone_662 小时前
node.js 邮箱验证服务器搭建
运维·服务器·node.js
猿来入此小猿3 小时前
基于SSM实现的健身房系统功能实现十六
java·毕业设计·ssm·毕业源码·免费学习·猿来入此·健身平台
丢丢丢丢丢丢~3 小时前
apache2的默认html修改
linux·运维·服务器
wusam3 小时前
Linux系统管理与编程20:Apache
linux·运维·服务器·apache·shell编程
goTsHgo3 小时前
Spring Boot 自动装配原理详解
java·spring boot
ChironW3 小时前
Ubuntu 24.04 LTS系统上配置国内时间同步
linux·运维·服务器·ubuntu
卑微的Coder3 小时前
JMeter同步定时器 模拟多用户并发访问场景
java·jmeter·压力测试