文章目录
前言
通过上一篇文章关于Nacos的搭建和启动,这篇文章将从如何将服务注册 到Nacos上并如何从Nacos上发现服务 两个方式讲解Nacos的实际使用,在介绍使用前,会通过一个简单介绍关于微服务版本的选用。那么,现在就开始我们的微服务实战之旅吧。
1. 微服务版本的选择
本专题主要是使用Alibaba微服务体系的组件来搭建微服务架构,从本篇开始到后面的文章都是使用同一系列的版本。关于微服务各组件的版本,阿里也出了一个比较详细的版本说明文档,这里就再具体说明了,感兴趣直接点击链表查看,下面就直接列出来本专题将使用的版本号:
- jdk Version:jdk17
- Spring Cloud Version : 2022.0.0
- Spring Boot Version: 3.0.2
- Spring Cloud Alibaba Version: 2022.0.0.0
- Nacos Version: 2.2.1
- Sentinel Version:1.8.6
- RocketMQ Version: 4.9.4
- Seata Version: 1.7.0
2. 服务示例工程
为了讲解服务的注册和发现,本文使用了两个微服务,一个是用户服务,另外一个是订单服务。两个服务都是可以单独提供api服务的springboot web应用,不同的是只能查询各自的服务,如果想通过用户服务里需要查询某个用户的订单,就需要再用户服务里调用订单服务的接口,这就是我们需要使用nacos完成的一个操作。
2.1 工程的架构
示例工程分三个模块:common(公共依赖),order(订单服务),user(用户服务):
最外层的pom主要是对微服务依赖的版本控制,需要引用主要的依赖:
<properties>
<java.version>17</java.version>
<spring-boot.version>3.0.2</spring-boot.version>
<spring-cloud.version>2022.0.0</spring-cloud.version>
<spring-cloud-alibaba.version>2022.0.0.0</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</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>
</dependencies>
</dependencyManagement>
2.2 集成nacos服务注册和发现步骤
2.2.1 添加pom依赖
以订单服务工程为例,只需在pom依赖中添加nacos的依赖
<!-- nacos服务注册与发现 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2.2.2 启动注解
在启动类添加@EnableDiscoveryClient
注解,非必须,因为该版本的依赖被引入后会自动开启该注解。
2.2.3 修改项目的配置
修改 application.yml 文件,添加项目名称和nacos的服务地址
spring:
application:
name: order
cloud:
nacos:
discovery:
server-addr: http://192.168.1.2:8848
2.2.4 启动和查看控制台
启动时,通过添加虚拟机的参数来控制服务需要启动的端口,在idea可以打开下面的配置面板进行设置
项目启动后,查看nacos控制台服务列表
3. 服务注册
按照前面的部署流程,完成一个服务部署两个实例的结构:
提示:使用idea可以通过复制配置的方式直接拷贝服务,拷贝后记得修改运行参数
注册完后可以在Nacos控制台查看服务列表
3.1 服务注册测试
除了从控制台可以查看服务的注册情况,还可以通过nacos提供的api来查询注册的结果,访问服务列表接口:
http://192.168.1.2:8848/nacos/v2/ns/instance/list?serviceName=order-service
返回结果:
至此我们的服务注册就已经集成完成,后面将介绍如何通过nacos进行服务之间的调用。
4. 服务发现/调用
4.1 需求说明
在order服务中有这个api,可以通过用户id查询所属的所有订单信息:
现在需要在user服务中请求该接口:http://192.168.1.2:8180/order/all-order/1
或者是
http://192.168.1.2:8181/order/all-order/1
4.2 传统方式调用
传统方式直接使用内置的RestTemplate
工具就可以访问,先注入Bean
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
controller中发起请求:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("all-order/{id}")
public Object getAllOrderByUserId(@PathVariable("id") Integer userId) {
String url = "http://192.168.1.2:8180/order/all-order/" + userId;
return restTemplate.getForEntity(url, Object.class);
}
浏览器输入:http://192.168.1.2:8280/user/all-order/1
虽然可以正常请求到order服务查询到数据,但是该方式存在下面的缺点:
- 请求具体服务的时候需要硬编码
- 可读性差,当参数比较复杂时难以维护
4.3 使用LoadBalancer结合RestTemplate请求
使用@LoadBalanced
注释修饰注册一个RestTemplate
bean
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
controller使用LoadBalanced提供的自动替换:
@Autowired
private RestTemplate restTemplate;
@RequestMapping("all-order/{id}")
public Object getAllOrderByUserId(@PathVariable("id") Integer userId) {
String url = "http://order/order/all-order/" + userId;
return restTemplate.getForEntity(url, Object.class);
}
如果服务调用时报下面的错误信息,则说明没有引用LoadBalanced相关的依赖
java.net.UnknownHostException: order-service
at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:567) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:633) ~[na:na]
at java.base/java.net.Socket.connect(Socket.java:583) ~[na:na]
at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:183) ~[na:na]
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:531) ~[na:na]
at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:636) ~[na:na]
at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:279) ~[na:na]
at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:384) ~[na:na]
在nacos版本,查看是否有LoadBalanced的依赖,像我下面的就是没有引用依赖的:
所以需要单独引用
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
相比较传统方式,这种方式也是同样有难以维护,特别是参数比较复杂的情况下,并且url也是属于硬编码,可读性比较差。
4.4 Spring Cloud OpenFeign 服务调用
Spring Cloud OpenFeign 是 Spring Cloud 在 Netflix OpenFeign 的基础上提供的一个声明式的 Web 服务客户端。它使得编写 Web 服务客户端变得更加简单,只需通过简单的接口与注解即可。本文只做简单的集成,更加详细的使用会有新的文章来阐述。
4.4.1 引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
4.4.2 开启注解
在springboot启动类添加注解
@SpringBootApplication
@EnableFeignClients
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
4.4.3 编写客户端(接口)
order-service服务有一个接口: /order/all-order/{id}
@RequestMapping("all-order/{id}")
public Object getAllOrderByUserId(@PathVariable("id") Integer userId) {
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId);
return orderMapper.selectList(queryWrapper);
}
所以在user-service服务中可以写成
package com.sch.user.service;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@FeignClient(value = "order-service", path = "/order")
public interface OrderRemoteService {
@RequestMapping("all-order/{id}")
public Object getAllOrderByUserId(@PathVariable("id") Integer userId);
}
@FeignClient
注解对应的就是openfeign客户端接口的申明,value对应在nacos注册服务的名称,path是可选的参数,表示该接口类有统一的路径。
4.4.4 调用测试
使用时就跟调用本地服务一样调用
@Autowired
private OrderRemoteService orderRemoteService;
@RequestMapping("all-order/{id}")
public Object getAllOrderByUserId(@PathVariable("id") Integer userId) {
return orderRemoteService.getAllOrderByUserId(userId);
}
总结
本文详细探讨了微服务注册和发现的实践方面。首先涉及微服务版本的选择,随后深入介绍了服务示例工程,并展示如何集成nacos进行服务注册和发现。文章还详述了服务的注册过程,并比较了不同的服务发现与调用方法,特别是利用Spring Cloud OpenFeign的调用技巧。希望能为读者提供了一个实用的微服务操作指南。