🌟 后端 | SpringCloud -> 微服务调用



微服务

  • 一个微服务负责一部分业务功能,并且其核心数据不依赖于其它模块。
  • 每个微服务都有自己独立的开发、测试、发布、运维。
  • 每个微服务都独立打包部署,访问自己独立的数据库。
  • 每个微服务的职责要尽量单一,包含的业务相互关联度高、完整度高。
  • 每个微服务的功能要相对独立,尽量减少对其它微服务的依赖,或者依赖接口的稳定性要强。

服务注册与发现

有时微服务需要提供接口供其它微服务访问,比如前端调用后端一般直接访问网关的微服务,再由网关转发到注册中心指定的微服务。

通常会在服务的 application.yml 中配置 eureka 地址、端口、密码,这样服务启动时就会注册自己的服务信息(服务名、IP、端口)到注册中心

  • 服务提供者会定期向注册中心发送请求,报告自己的健康状态
  • 当服务有新实例启动时,会发送注册服务请求,其信息会被记录在注册中心的服务实例列表

引入 eureka 依赖

1
2
3
4
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>

服务调用试例

使用 RestTemplate 可以进行微服务间调用。
在用 eureka 实现服务治理后,之前的写死 IP 和端口可以改成利用 DiscoveryClient 服务发现 + RestTemplate 服务调用

如下 mps-service 服务中调用 cup-service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Service
@RequiredArgsConstructor
public class MsgServiceImpl extends ServiceImpl<MsgMapper, Msg> implements IMsgService {
// 服务调用
private final RestTemplate restTemplate;
// 服务发现
private final DiscoveryClient discoveryClient;

private void handleMessages(List<MsgVO> msg) {
// 发现 cup 服务实例
List<ServiceInstance> instances = discoveryClient.getInstances("cup-service");
// 负载均衡
ServiceInstance instance = instances.get(RandomUtil.randomInt(instances.size()));

// 服务调用
ResponseEntity<List<MsgDTO>> response = restTemplate.exchange(
// 用 DiscoveryClient 取代
// "http://localhost:8081/Msgs?ids={ids}",
instance.getUri() + "Msgs?ids={ids}"
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MsgDTO>>() {
},
Map.of("ids", CollUtil.join(itemIds, ","))
);
}
}

openfeign

OpenFeign 基于动态代理帮我们生成远程调用的代码,而无需我们手动再编写以上调用手续

哪个微服务需要调用其他微服务,就需要在这个微服务引入 openFeign 依赖和配置,比如 mps-service 调用 cup-service,就需要在 mps-service 中增加配置

mps 引入依赖

1
2
3
4
5
6
7
8
9
10
<!--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 功能

1
2
3
4
5
6
7
@EnableFeignClients
@SpringBootApplication
public class MpsApplication {
public static void main(String[] args) {
SpringApplication.run(MpsApplication.class, args);
}
}

直接定义调用接口,使用注解 @OpenFeign 指向微服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.mps.msg.client;

import com.mps.msg.domain.dto.MsgDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

// 指向目标微服务
@FeignClient("cup-service")
public interface MsgClient {

@GetMapping("/msgs")
List<MsgDTO> queryMsgByIds(@RequestParam("ids") Collection<Long> ids);
}
  • @FeignClient(“cup-service”) :声明服务名称
  • @GetMapping :声明请求方式
  • @GetMapping(“/msgs”) :声明请求路径
  • @RequestParam(“ids”) Collection ids :声明请求参数

mps-service 简化调用 cup

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import com.mps.msg.client.MsgClient

@Service
@RequiredArgsConstructor
public class MsgServiceImpl extends ServiceImpl<MsgMapper, Msg> implements IMsgService {

private final MsgClient msgClient;

private void handleMessages(List<MsgVO> msg) {
// 服务调用
Set<Long> msgId = msg.stream().map(MsgVO::getMsgId).collect(Collectors.toSet());
<List<MsgDTO> msgs = MsgClient.queryMsgByIds(msgId);
}
}

对应 cup-service 也应开启配置支持 openfeign

引入依赖

1
2
3
4
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-okhttp</artifactId>
</dependency>

application.yml 中

1
2
3
feign:
okhttp:
enabled: true # 开启OKHttp功能

将 openfeign 抽取为公共 API,每个微服务都可以使用

opgs-common
opgs-api
gateway-service
mps-service
cup-service

将上方配置都转移到 opgs-api,任何微服务要调用 cup-service 中的接口,只需要引入opgs-api 模块依赖即可,无需自己编写 Feign 客户端了

MsgClient 现在定义到了 com.opgs.api.client 包下, 需在调用方服务的启动类上添加声明即可。

1
2
3
4
5
6
7
8
9
10
11
package com.opgs.mps;

import com.opgs.api.client.MsgClient;

@EnableFeignClients(clients = {MsgClient.class})
@SpringBootApplication
public class MpsApplication {
public static void main(String[] args) {
SpringApplication.run(MpsApplication.class, args);
}
}

定义日志

1
2
3
4
5
6
7
8
9
10
11
package com.mps.api.config;

import feign.Logger;
import org.springframework.context.annotation.Bean;

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

局部生效:

1
@FeignClient(value = "item-service", configuration = DefaultFeignConfig.class)

全局生效:

1
@EnableFeignClients(defaultConfiguration = DefaultFeignConfig.class)

输出日志:

1
2
3
4
5
6
7
8
9
10
11
17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient          : [ItemClient#queryItemByIds] ---> GET http://item-service/items?ids=100000006163 HTTP/1.1
17:35:32:148 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] ---> END HTTP (0-byte body)
17:35:32:278 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] <--- HTTP/1.1 200 (127ms)
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] connection: keep-alive
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] content-type: application/json
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] date: Fri, 26 May 2023 09:35:32 GMT
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] keep-alive: timeout=60
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] transfer-encoding: chunked
17:35:32:279 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds]
17:35:32:280 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] [{"id":100000006163,"name":"巴布豆(BOBDOG)柔薄悦动婴儿拉拉裤XXL码80片(15kg以上)","price":67100,"stock":10000,"image":"https://m.360buyimg.com/mobilecms/s720x720_jfs/t23998/350/2363990466/222391/a6e9581d/5b7cba5bN0c18fb4f.jpg!q70.jpg.webp","category":"拉拉裤","brand":"巴布豆","spec":"{}","sold":11,"commentCount":33343434,"isAD":false,"status":2}]
17:35:32:281 DEBUG 18620 --- [nio-8082-exec-1] com.mps.api.client.ItemClient : [ItemClient#queryItemByIds] <--- END HTTP (369-byte body)