微服务是一种软件架构风格,它是以专注于单一职责的很多小型项目为基础,组合出复杂的大型应用。 (对应的是单体架构风格)
一、认识微服务
1、单体架构
单体架构:将业务的所有功能集中在一个项目中开发,打成一个包部署
优:架构简单、部署成本低
缺:团队协作成本高、系统发布效率低、系统可用性差
2、微服务
微服务架构:是服务化思想指导下的一套最佳实践架构方案。服务化,就是把单体架构中的功能模块拆分为多个独立项目。
3、SpringCloud
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
二、微服务拆分
1、服务拆分原则
什么时候拆分:
创业型项目:先采用单体架构,快速开发,快速试错。随着规模扩大,逐渐拆分。
确定的大型项目:资金充足,目标明确,可以直接选择微服务架构,避免后续拆分的麻烦。
怎么拆分:
高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
低耦合:每个微服务的功能要相对独立,尽量减少对其他微服务的依赖。
拆分方式:
纵向拆分:按照业务模块来拆分。
横向拆分:抽取公共服务,提高复用性。
2、拆分服务
工程结构有两种:独立Project(多个项目) 和 maven聚合(一个项目多个模块)
远程调用
Spring给我们提供了一个RestTemplate的API,可以方便的实现Http请求的发送。使用步骤如下:
①注入Rest Template到Spring容器 (项目中写到启动类了)
@Configuration
public class RemoteCallConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
②发起远程调用
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
"http://localhost:8081/items?ids={ids}",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
},
Map.of("ids", CollUtil.join(itemIds, ","))
);
三、服务注册和发现
1、注册中心原理
服务提供者通过心跳机制向注册中心报告自己的健康状况,当心跳异常时注册中心会将异常服务剔除,并通知订阅了该服务的消费者。
2、Nacos注册中心
基于Docker来部署Nacos的注册中心,首先我们要准备MySQL数据库表,用来存储Nacos的数据。由于是Docker部署,所以需要将资料中的SQL文件导入到Docker中的MySQL容器中。
然后,找到课前资料下的nacos文件夹其中的nacos/custom.env
文件中,有一个MYSQL_SERVICE_HOST也就是mysql地址,需要修改为你自己的虚拟机IP地址
然后,将课前资料中的nacos
目录上传至虚拟机的/root
目录。
进入root目录,然后执行下面的docker命令:
docker run -d \
--name nacos \
--env-file ./nacos/custom.env \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim
启动完成后,访问下面地址:http://192.168.100.128:8848/nacos/,注意将192.168.100.128
替换为你自己的虚拟机IP地址。
首次访问会跳转到登录页,账号密码都是nacos
3、服务注册
①在item-service
的pom.xml
中添加依赖:
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
②在item-service
的application.yml
中添加nacos地址配置:
spring:
application:
name: cart-service #微服务名称
cloud:
nacos:
server-addr: 192.168.100.128:8848 # nacos地址
4、服务发现
①我们在cart-service
中的pom.xml
中添加下面的依赖:
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
这里Nacos的依赖于服务注册时一致,这个依赖中同时包含了服务注册和发现的功能。因为任何一个微服务都可以调用别人,也可以被别人调用,即可以是调用者,也可以是提供者。
②在cart-service
的application.yml
中添加nacos地址配置:
spring:
cloud:
nacos:
server-addr: 192.168.150.101:8848
③服务发现
服务发现需要用到一个工具,DiscoveryClient,SpringCloud已经帮我们自动装配,我们可以直接注入使用:
private final DiscoveryClient discoveryClient;
// 2.查询商品
//2.1 根据服务名称获取服务的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("item-service");//获取实例,填写服务的名称
if(CollUtils.isEmpty(instances)){//判断是否为空
return;
}
//2.2 手写负载均衡,从实例列表中挑选实例
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));//随机使用实例
instance.getUri();
//2.3 利用restTemplate发起http请求,得到http的响应
ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
instance.getUri()+"/items?ids={ids}", //修改这里
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<ItemDTO>>() {
},
Map.of("ids", CollUtil.join(itemIds, ","))
);
四、OpenFeign
1、快速入门
OpenFeign是一个声明式的http客户端,是SpringCloud在Eureka公司开源的Feign基础上改造而来。其作用就是基于SpringMVC的常见注解,帮我们优雅的实现http请求的发送。
①引入依赖,包括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>
②通过@EnableFeignClients注解,启动OpenFeign功能
在启动类上添加@EnableFeignClients注解( basePackages = "com.hmall.api.client")
③在cart-service
中,定义一个新的接口,编写Feign客户端:(代替前面写的复杂的代码)
@FeignClient("item-service") //根据服务名称,去拉取实例列表
public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}
④使用FeignClient,实现远程调用
private final ItemClient itemClient;
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
2、连接池
Feign底层发起http请求,依赖于其它的框架。其底层支持的http客户端实现包括:
HttpURLConnection:默认实现,不支持连接池
Apache HttpClient :支持连接池
OKHttp:支持连接池
因此我们通常会使用带有连接池的客户端来代替默认的HttpURLConnection。比如,我们使用OK Http.
①在cart-service
的pom.xml
中引入依赖:
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
在cart-service
的application.yml
配置文件中开启Feign的连接池功能:
feign:
okhttp:
enabled: true # 开启OKHttp功能
3、最佳实践
思路1:抽取到微服务之外的公共module
思路2:每个微服务自己抽取一个module
方案1抽取更加简单,工程结构也比较清晰,但缺点是整个项目耦合度偏高。
方案2抽取相对麻烦,工程结构相对更复杂,但服务之间耦合度降低。
由于item-service已经创建好,无法继续拆分,因此这里我们采用方案1将itemClient接口抽取到hm-api模块中。
定义的FeignClient不存在SpringBootApplication的扫描包范围时,这些FeignClient无法使用。解决方案:
①在启动类的注解EnablefeignClients上指定FeignClient所在包
@EnableFeignClients(basePackages = "com.hmall.api.client")
②指定FeignClient字节码
@EnableFeignClients(clients = {ItemClient.class})
4、日志
OpenFeign只会在FeignClient所在包的日志级别为DEBUG时,才会输出日志。而且其日志级别有4级:
NONE:不记录任何日志信息,这是默认值。
BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。
Feign默认的日志级别就是NONE,所以默认我们看不到请求日志。
在hm-api模块下新建一个配置类,定义Feign的日志级别:
public class DefaultFeignConfig {
@Bean
public Logger.Level feignLogLevel(){
return Logger.Level.FULL;
}
}
接下来,要让日志级别生效,还需要配置这个类。有两种方式:
局部生效:在某个
FeignClient
中配置,只对当前FeignClient
生效。
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
全局生效:在
@EnableFeignClients
中配置,针对所有FeignClient
生效。
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)
一般去情况下不开启日志。