微服务项目入门(Nacos、OpenFeign)

单体架构

单体架构是一种将所有功能模块紧密集成在一起的架构模式。在这种架构中,应用程序的所有组件(如用户界面、业务逻辑、数据访问层等)都被打包到一个单一的可执行文件或部署单元中。

单体架构的优势

  • 开发效率高:对于小型或中型项目,开发团队可以快速实现功能,因为不需要考虑模块间的分布式通信问题。

  • 性能优化容易:由于所有模块运行在同一进程中,性能优化相对简单,可以通过代码优化、缓存等手段直接提升整体性能。

  • 技术门槛低:对于初学者或小型团队来说,单体架构更容易理解和上手,不需要掌握复杂的分布式技术。

  • 事务管理简单:在单体架构中,事务管理相对简单,因为所有模块都在同一个数据库事务中操作。

单体架构的劣势

  • 扩展性差:随着业务增长,单体应用的代码量和复杂度会不断增加,导致难以扩展。一旦某个模块出现问题,可能会影响整个应用程序的运行。

  • 技术债务累积:随着时间推移,代码库会变得庞大且难以维护,新功能的添加和旧功能的修改都会变得越来越困难。

  • 部署困难:每次更新都需要重新部署整个应用程序,即使只是修改了一个小模块。这可能导致部署时间长,且容易出错。

  • 技术栈受限:由于所有模块共享相同的代码库和技术栈,难以引入新的技术或框架,限制了技术的创新。

微服务架构

微服务架构(Microservices Architecture)是一种将应用程序拆分为多个小型、独立服务的架构风格。每个服务运行在自己的进程中,通过轻量级通信机制(如HTTP/REST)进行交互,并独立部署和扩展。

Spring Cloud官网

微服务拆分原则

从拆分目标来说,要做到:

  • * 高内聚:每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
  • * 低耦合:每个微服务的功能要相对独立,尽量减少对其它微服务的依赖。

从拆分方式来说,一般包含两种方式:

  • * 纵向拆分:按照业务模块来拆分
  • * 横向拆分:抽取公共服务,提高复用性

各个服务模块实现通信的方法——注册中心原理

服务治理中的三个角色

  • 服务提供者:暴露服务接口,供其它服务调用
  • 服务消费者:调用其它服务提供的接口
  • 注册中心:记录并监控微服务各实例状态,推送服务变更信息

消费者如何知道提供者的地址?

  • 服务提供者会在启动时注册自己信息到注册中心,消费者可以从注册中心订阅和拉取服务信息

消费者如何得知服务状态变更?

  • 服务提供者通过心跳机制向注册中心报告自己的健康状态,当心跳异常时注册中心会将异常服务剔除,并通知订阅了该服务的消费者

当提供者有多个实例时,消费者该选择哪一个?

  • 消费者可以通过负载均衡算法,从多个实例中选择一个

服务注册中心——Nacos

配置nacos的环境

docker直接使用环境变量启动Nacos:

docker run -d \
--name nacos \
-e MODE=standalone \
-e PREFER_HOST_MODE=hostname \
-p 8848:8848 \
-p 9848:9848 \
-p 9849:9849 \
--restart=always \
nacos/nacos-server:v2.1.0-slim

之后通过docker logs -f nacos查看日志

登录的账号密码:都是nacos

或者window本机上直接运行

Nacos Server 下载 | Nacos 官网

下载nacos的二进制文件,然后打开 Nacos 安装路径下的 bin/startup.cmd 文件进行编辑。

找到 set MODE="",将其值修改为 standalone,即:

set MODE="standalone"

直接双击 startup.cmd 文件即可启动 Nacos。 

单个微服务项目引入 Nacos Discovery 依赖:

<!-- nacos 服务注册发现,不需要指定版本,因为父组件里有 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>



<!-- 父组件 -->
<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-alibaba-dependencies</artifactId>
      <version>${spring-cloud-alibaba.version}</version>
      <type>pom</type>
      <scope>import</scope>
</dependency>

yml文件里配置 Nacos 地址:

spring:
  application:
    name: item-service  # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848  # nacos地址

在项目里配置好之后,在nacos的网页界面可以看到注册的服务

 调用Nacos服务,实现服务发现

在启动类里注册RestTemplate配置(用于发送http请求接收响应)

    @Bean // 放入配置类里
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

使用lombok的@RequiredArgsConstructor注解注入相关依赖(推荐)

@Service
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl<CartMapper, Cart> implements ICartService {
    private final DiscoveryClient discoveryClient; // 发现nacos里注册的微服务
    private final RestTemplate restTemplate;
......
}

方法里发送请求给其它微服务

private void handleCartItems(List<CartVO> vos) {
        // 提取购物车中所有商品的ID,并去重
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        // 找到名为"item-service"的微服务
        List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        // 如果没有可用的服务实例,则直接返回
        if(CollUtil.isEmpty(instances)){
            return ;
        }
        // 从服务实例列表中随机选择一个实例
        ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size())); // 最小随机

        // 构建请求URL,通过REST模板向选定的服务实例发送GET请求,获取响应信息
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                instance.getUri() + "/items?ids={ids}", // 注意请求的参数不要写错
                HttpMethod.GET, // 请求方法
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                },
                Map.of("ids", CollUtil.join(itemIds, ","))
        );

        // 解析响应状态码,如果请求失败(非2xx成功状态码),则直接返回
        if(!response.getStatusCode().is2xxSuccessful()){
            // 查询失败,直接结束
            return;
        }
        // 从响应中获取商品信息列表
        List<ItemDTO> items = response.getBody();
        // 如果没有获取到商品信息,则直接返回
        if (CollUtils.isEmpty(items)) {
            return;
        }
     ......
    }

以上代码有几个要根据具体业务而不同实现的:

微服务集群环境下负载均衡策略的选择
  • 简单场景:随机或轮询。

  • 高性能场景:最少连接或响应时间加权。

  • 高可用场景:故障回避或区域优先。

  • 复杂场景:自定义策略或一致性哈希。

构造请求

请求到方式、请求体、请求url里的参数

以上全部白雪😂😂😂 


OpenFeign

OpenFeign 是一个声明式的 Web 服务客户端,主要用于简化微服务架构中 HTTP 请求的处理。它通过注解和接口的形式定义远程服务的调用,帮助开发者省去编写大量 HTTP 请求的样板代码😂

  1. OpenFeign 已经被 SpringCloud 自动装配,实现起来非常简单:

    <!--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. 在启动类里通过 @EnableFeignClients 注解,启用 OpenFeign 功能。

    @EnableFeignClients
    @SpringBootApplication
    public class CartApplication { // ... 略 }
  3. 在项目里新建Client文件夹,里面放xxxClient的接口文件,接口里定义springmvc风格的代码用于请求其它微服务

    @FeignClient("item-service") // 指定请求的微服务
    public interface ItemClient {
        @GetMapping("/items") // 可以直接把那个对应的微服务里的方法拷过来
        List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids);
    }
  4. 要给其它微服务发送请求的方法所在的类里注入接口

    @RequiredArgsConstructor
    private final ItemClient itemClient; // 加上面的注解
    List<ItemDTO> items = itemClient.queryItemByIds(new ArrayList<>(itemIds));     // 直接调用接口里的方法,获取请求的结果

OKhttp连接池

OKhttp连接池的特性

  1. 高效性OkHttp 支持连接池化和复用,减少了请求延迟,提高了效率。它还支持HTTP/2和SPDY协议,可以更高效地使用网络资源。

  2. 可靠性:它支持自动重定向和自动恢复,例如在网络连接失败时自动重试请求。

  3. 安全性:OkHttp 支持HTTPS和TLS,提供了多种安全配置选项,包括证书固定(Certificate Pinning)和主机名验证。

  4. 可定制性:开发者可以自定义请求头、响应拦截器、缓存策略等,以满足不同的需求。

  5. 易用性:OkHttp 提供了简单直观的API,使得发送HTTP请求变得非常容易。

  6. 响应缓存:OkHttp 支持响应缓存,可以减少不必要的网络请求,加快应用的响应速度。

  7. GZIP压缩:它支持请求和响应的GZIP压缩,可以减少数据传输量,加快传输速度。

  8. 同步和异步请求:OkHttp 支持同步和异步请求,开发者可以根据需要选择适合的方式。

  9. WebSocket支持:OkHttp 还支持WebSocket,可以方便地实现双向通信。

引入依赖:

<!--ok-http-->
<dependency>
    <groupId>io.github.openfeign</groupId>
    <artifactId>feign-okhttp</artifactId>
</dependency>

打开开关 

feign:
  okhttp:
    enabled: true  # 开启OkHttp连接池支持,默认是false

OpenFeign最佳实现方案

以上方案的实现思路方案

新建一个XXX-api的模块,同时导入公共的dto和和每个模块需要暴露出去的api(通过OpenFeign实现)。(config是配置openfeign日志级别)

之后在XXX-api模块里导入依赖

        <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>
        <!--ok-http-->
        <dependency>
            <groupId>io.github.openfeign</groupId>
            <artifactId>feign-okhttp</artifactId>
        </dependency>

 配置完XXX-api通用接口模块之后,在每一个需要使用到XXX-api模块里接口的模块导入XXX-api依赖。

        <dependency>
            <groupId>com.heima</groupId>
            <artifactId>hm-api</artifactId>
            <version>1.0.0</version>
        </dependency>

此时注意:

只要哪个模块里导入了XXX-api通用模块,这个模块就不需要写额外实现的代码以及额外导入的依赖,因为这些都在 XXX-api通用模块里

最后找到在要使用到XXX-api模块里接口的模块,在它的启动类里加上注解如下,确定扫描范围。

@EnableFeignClients(basePackages = "com.hmall.api.client")

OpenFeign日志

OpenFeign 的日志级别设置:

  • NONE:不记录任何日志信息,这是默认值。

  • BASIC:仅记录请求的方法、URL以及响应状态码和执行时间。

  • HEADERS:在 BASIC 的基础上,额外记录了请求和响应的头信息。

  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

此外,OpenFeign 只会在 FeignClient 所在包的日志级别为 DEBUG 时,才会输出日志。

自定义日志级别 Bean

  • 创建一个配置类,并在其中定义日志级别。

    public class DefaultFeignConfig {
        @Bean
        public Logger.Level feignLogLevel() {
            return Logger.Level.FULL;
        }
    }

配置特定 FeignClient 的日志:可以在 @FeignClient 注解(启动类里)中声明配置。 

// 配置局部
// @FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)
// 配置全局
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)// 配置全局
public class CartApplication {
    public static void main(String[] args) {
        SpringApplication.run(CartApplication.class, args);
    }
}

此时就可以在日志里看到openfeign发送请求的详细信息了:

注意:如果是真正运行的过程中,不需要开启openfeign的日志 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值