Nacos部署详细步骤(整合OpenFeign)

287 阅读4分钟

为什么使用nacos?

Nacos(Dynamic Naming and Configuration Service) 是阿里开源的 服务发现配置管理 平台,专为微服务架构设计。以下是其核心价值的总结:

image.png

如果不使用nacos的话,当一个服务想要调用另一个服务时,可以看下面的代码

private void handleCartItems(List<CartVO> vos) {
    // TODO 1.获取商品id
    Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
    // 2.查询商品
    // List<ItemDTO> items = itemService.queryItemByIds(itemIds);
    // 2.1.利用RestTemplate发起http请求,得到http的响应
    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, ","))
    );
    // 2.2.解析响应
    。
    。
    。

}

不过这种手动发送Http请求的方式存在一些问题。试想一下,假如商品微服务被调用较多,为了应对更高的并发,我们进行了多实例部署,

此时,每个item-service的实例其IP或端口不同,问题来了:

  • item-service这么多实例,服务调用者如何知道每一个实例的地址?
  • http请求要写url地址,服务调用者到底该调用哪个实例呢?
  • 如果在运行过程中,某一个item-service实例宕机,服务调用者依然在调用该怎么办?
  • 如果并发太高,item-service临时多部署了N台实例,服务调用者如何知道新实例的地址? 为了解决上述问题,就必须引入注册中心的概念了

下图是注册中心、服务提供者、服务消费者三者间关系: image.png

1.Nacos部署

我是将nacos部署到了linux服务器上了,我的服务器是centos7。 其中需要两个文件一个是nacos数据库文件,另一个就是配置nacos的环境变量,我放到百度网盘里面了。 链接: pan.baidu.com/s/1nc5GH5Zx… 提取码: uxs6

image.png 按需修改环境变量文件即可

然后,将资料中的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

然后记得开放nacos虚拟机的防火墙端口号

sudo firewall-cmd --zone=public --add-port=8848/tcp --permanent

sudo firewall-cmd --reload

访问下面地址:http://192.168.150.101:8848/nacos/,注意将192.168.150.101替换为你自己的虚拟机IP地址。

首次访问会跳转到登录页,账号密码都是nacos

2.服务注册

现在将服务加入Nacos

引入以下pom文件(这里没写版本是因为我 父工程使用了SpringCloud统一管理版本了)

<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

配置nacos

spring:
  application:
    name: item-service # 服务名称
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848 # nacos地址,ip写你自己的ip端口

image.png

我上面模拟一个服务多个实例的情况

image.png

可以发现服务注册成功

3.服务发现

在服务调用者中加入下述nacos依赖

<!--nacos 服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

服务调用者配置Nacos地址

spring:
  cloud:
    nacos:
      server-addr: 192.168.150.101:8848  # nacos地址,ip写你自己的ip端口

再次使用代码实现服务调用

  private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        ```
        // 2.1获取nacos注册中心的实例
        List<ServiceInstance> instances = discoveryClient.getInstances("item-service");
        // 2.2负载均衡
        ServiceInstance serviceInstance = instances.get(RandomUtil.randomInt(instances.size()));
        // 2.3查询商品
        ResponseEntity<List<ItemDTO>> response = restTemplate.exchange(
                serviceInstance.getUri() + "/items?ids={ids}", //请求路径
                HttpMethod.GET,
                null,
                new ParameterizedTypeReference<List<ItemDTO>>() {
                },
                Map.of("ids", CollUtil.join(itemIds, ","))
        );

        //解析响应
        if (!response.getStatusCode().is2xxSuccessful()) {
            return;
        }

        List<ItemDTO> items = response.getBody();
        。
        。
        。

    }

可以发现请求路径是从nacos中动态获得的,是nacos帮我们维护的

5.使用OpenFeign简化开发(一行代码实现)

我们利用Nacos实现了服务的治理,利用RestTemplate实现了服务的远程调用。但是远程调用的代码太复杂了 在服务调用者引入OpenFeign的依赖和loadBalancer依赖:

  <!--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>

启用OpenFeign: 在启动类上加上@EnableFeignClients

编写OpenFeign客户端

@FeignClient("item-service") //此处需要和要调用的服务一致
public interface ItemClient {
    //此接口不需要实现
    @GetMapping("items")
    List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
}

上述items要和下述代码中的请求路径保持一致

@Api(tags = "商品管理相关接口")
@RestController
@RequestMapping("/items")
@RequiredArgsConstructor
public class ItemController {
    @ApiOperation("根据id批量查询商品")
    @GetMapping
    public List<ItemDTO> queryItemByIds(@RequestParam("ids") List<Long> ids) {
        return itemService.queryItemByIds(ids);
    }
}

OpenFeign替我们完成了服务拉取、负载均衡、发送http请求的所有工作最后直接使用下述代码即可完成服务调用,(记得注入itemClient):

private void handleCartItems(List<CartVO> vos) {
        // 1.获取商品id
        Set<Long> itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
        
        List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
        。
        。
        。
    }

6.OpenFeign配置连接池

OpenFeign底层默认使用Client连接池,但是

连接池实现对比

  • 通用Client

    • 缺点

      1. 连接复用逻辑简单,容易产生冗余连接。
      2. 缺乏智能控制(如空闲超时、最大并发限制)。
      3. 不同版本的JDK实现差异大(如Android与Java SE)。
  • OKHttp连接池

    • 优势

      1. 默认启用高效连接池,自动复用底层TCP连接。
      2. 支持每个地址的最大空闲连接数配置(默认5个)。
      3. 自动清理闲置连接(默认5分钟无活动后释放)。
      4. 对HTTP/2多路复用的深度优化(同一连接并发请求)。

所以此处选用OKHttp

<!--OK http 的依赖 -->
<dependency>
  <groupId>io.github.openfeign</groupId>
  <artifactId>feign-okhttp</artifactId>
</dependency>
feign:
  okhttp:
    enabled: true # 开启OKHttp功能

重启服务,连接池就生效了