为什么使用nacos?
Nacos(Dynamic Naming and Configuration Service) 是阿里开源的 服务发现 与 配置管理 平台,专为微服务架构设计。以下是其核心价值的总结:
如果不使用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台实例,服务调用者如何知道新实例的地址? 为了解决上述问题,就必须引入注册中心的概念了
下图是注册中心、服务提供者、服务消费者三者间关系:
1.Nacos部署
我是将nacos部署到了linux服务器上了,我的服务器是centos7。 其中需要两个文件一个是nacos数据库文件,另一个就是配置nacos的环境变量,我放到百度网盘里面了。 链接: pan.baidu.com/s/1nc5GH5Zx… 提取码: uxs6
按需修改环境变量文件即可
然后,将资料中的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端口
我上面模拟一个服务多个实例的情况
可以发现服务注册成功
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
-
缺点:
- 连接复用逻辑简单,容易产生冗余连接。
- 缺乏智能控制(如空闲超时、最大并发限制)。
- 不同版本的JDK实现差异大(如Android与Java SE)。
-
-
OKHttp连接池
-
优势:
- 默认启用高效连接池,自动复用底层TCP连接。
- 支持每个地址的最大空闲连接数配置(默认5个)。
- 自动清理闲置连接(默认5分钟无活动后释放)。
- 对HTTP/2多路复用的深度优化(同一连接并发请求)。
-
所以此处选用OKHttp
<!--OK http 的依赖 -->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>
feign:
okhttp:
enabled: true # 开启OKHttp功能
重启服务,连接池就生效了