微服务中,如何优化接口调用

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 微服务中,如何优化接口调用

在微服务的设计中,我们通常考虑到的是通过加密、熔断、限流等操作保证接口的安全性、健壮性等问题,但是在代码的编写中,你考虑过优化接口的调用方式吗?下面我们就来看一看,如何更优雅的调用接口。

假设在我们的系统中现在有两个微服务,订单服务和库存服务,业务流程是当使用订单服务创建订单时,先调用库存服务查询是否有库存,如果有库存才能完成订单的下单操作。

先从简单入手,以使用RestTemplate进行服务间调用为例。定义库存服务及其提供的对外调用接口:

@Service
public class StockService {
    public Integer query(String id){
        return 10;
    }
}
@RestController
public class StockController {
    @Autowired
    StockService stockService;
    @PostMapping("/query")
    public Integer queryStock(String id){
        return stockService.query(id);
    }
}

再定义订单服务及其提供的对外调用接口,在订单服务中,使用RestTemplate调用库存服务:

@Service
public class OrderService {
    @Autowired
    private RestTemplate restTemplate;
    public String createOrder(String id){
        Integer stock = restTemplate.postForObject("http://stockService/query", id, Integer.class);
        System.out.println(stock);
        return "create success";
    }
}
@RestController
public class OrderController {
    @Autowired
    OrderService orderService;
    @GetMapping("/create")
    public String createOrder(@RequestParam String id){
        return orderService.createOrder(id);
    }
}

这样写可以正常进行调用并返回结果,但存在一些的问题:

在使用RestTemplate 进行服务间调用使用的是字符串,如果调用的路径填写错误,编译器在编译的时候不会进行提示,只有在真正调用服务时才会发现错误

如果在订单服务中使用了多次库存服务,那么这个库存服务的接口地址就会出现多次,如果后期维护中接口的路径发生变化,那么需要修改所有出现调用的地方

针对这两个问题,如果将调用路径单独封装成常量,那么在调用的时候直接引用这个常量,可以避免字符串出现错误,并且在后续的修改中,只修改一个地方就可以了。在订单服务中创建一个类来维护接口字符串:

public class StockURL{
    public static final String PREFIX="http://stockService";
    public static final String STOCK_QUERY="/query";
}

并将调用改为:

public String createOrder(String id){
    Integer stock = restTemplate.postForObject(StockURL.PREFIX+StockURL.STOCK_QUERY, id, Integer.class);
    System.out.println(stock);
    return "create success";
}

这样做的确可以一定程度规避接口名称错误带来的风险,但是回头一看,这个接口名在库存服务中同样也可以被直接用到,也就是说如果直接由服务的提供方来维护接口名的话,是不是更好一些呢?

为了让这个常量在两个微服务中同时被调用,可以单独创建一个Module来维护它,将这个Module命名为stock-api,并将之前创建的StockURL类直接复制过来。在订单服务和库存服务的pom文件中引入我们创建的模块依赖:

<dependency>
  <groupId>com.cn.hydra</groupId>
  <artifactId>stock-api</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>

订单服务维持原样不动,库存服务可以修改接口:

@PostMapping(StockURL.STOCK_QUERY)
public Integer queryStock(String id){
    return stockService.query(id);
}

这样,由库存服务的提供者来维护stock-api模块,只需要修改常量就可以做到只需要修改一次,其他地方不再需要修改。

那么,是否还存在其他问题呢?仔细看一下发起请求调用:

restTemplate.postForObject(StockURL.PREFIX+ StockURL.STOCK_QUERY, id, Integer.class);

RestTemplate 发送请求时的参数和返回类型由发起调用方指明,那么这样仍然存在风险。虽然在微服务调用间一般都会提供比较详细的接口文档说明,但是如果接口发生变更但文档没有及时更新,那么仍然可能发生调用时的错误。同样,这样的错误是编译器不会提醒,只有在调用时才会被发现的。

那么,如果在调用远程方法时,希望能够像调用本地方法一样,给出参数和返回值的提示,不符合要求时能够及时报错,那么就要继续改造,由服务提供方进行接口的维护。

我们把OrderService类拿到stock-api模块中加以改造,库存服务提供者来维护这个接口:

@Service
@ConditionalOnBean(RestTemplate.class)
public class StockServiceApi {
    @Autowired
    private RestTemplate restTemplate;
    public Integer query(String id){
        Integer stock = restTemplate.postForObject(StockURL.PREFIX+ StockURL.STOCK_QUERY, id, Integer.class);
        return stock;
    }
}

需要注意,这样提供的Service在订单服务中是无法通过@Autowired被直接注入的,因为Springboot的自动扫描是扫描不到这个Bean的,如果我们不希望再通过@Bean的方式手动注入的话,那么我们可以模仿starter的方式,来将这个Bean注入到容器中,在stock-api模块的最外层定义一个Configuration类进行扫描:

@Configuration
@ComponentScan
public class Config {
}

创建META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.cn.Config

在订单服务中重写方法调用,现在就可以直接调用接口,省去了还要翻阅接口文档看RestTemplate 调用需要判断参数和返回值的麻烦了:

@RestController
public class OrderController2 {
    @Autowired
    StockServiceApi stockServiceApi;
    @GetMapping("/create2")
    public String createOrder(@RequestParam String id){
        Integer stock = stockServiceApi.query(id);
        System.out.println(stock);
        return "create success";
    }
}

改造到这个程度,可以看到服务调用者的工作被大幅度简化了,但是服务提供者同时要对外提供ControllerServiceApi两者,增添了一定的工作量。并且,ControllerService的数量应该是一一对应的。那么为了更稳定的维护这个对应关系,其实可以创建一个接口来实现绑定:

public interface IStockService {
    Integer query(String id);
}

再分别让ControllerService实现这个接口:

public class StockServiceApi implements IStockService{...
public class StockController implements IStockService {...

这样当修改接口时,就会提示我们去修改所有的实现类。

到这,对接口调用的优化进行一下总结:

通过定义字符串常量避免接口地址调用出错

服务提供方同时提供接口及接口的Api调用,消灭服务调用者调用时参数及返回值的潜在错误

服务提供方通过接口的方式绑定ControllerApi

讨论完使用RestTemplate的调用方式,接下来看一下使用Feign调用时,应该如何优化接口调用。

先看一下正常方式下,订单服务使用Feign调用库存服务:

@FeignClient("stockService")
public interface IFeignOrderService {
    @PostMapping("/query")
    Integer query(String id);
}

我们知道,Feign中调用的接口路径和服务提供方的接口路径是一致的,那么也可以通过提供公共接口的方式进行优化。在stock-api模块中添加一个接口:

public interface IFeignStockService {
    @PostMapping("/query")
    Integer query(String id);
}

修改服务提供方的Controller类实现该接口,方法上不再需要加@PostMapping注解,会自动继承:

@RestController
public class FeignStockController implements IFeignStockService {
    @Autowired
    StockService stockService;
    @Override
    public Integer query(String id) {
        return stockService.query(id);
    }
}

修改服务调用方接口,同样继承stock-api模块中的接口,写一个空接口即可:

@FeignClient("stockService")
public interface IFeignOrderService extends IFeignStockService {
}

在使用Feign的情况下,本身就通过声明式的服务调用简化了接口的使用过程,通过上述的这一种方式,能够进而通过api模块完成了服务调用的优化,保证了代码的易维护性与稳定性。

相关文章
|
6月前
|
运维 云计算 Docker
深入理解与实践:基于Docker的微服务架构优化策略
本文旨在为软件开发和运维人员提供一个全面的指南,探讨如何通过Docker容器技术优化微服务架构。我们不仅深入分析了Docker在微服务环境中的关键作用,还提出了一系列实践策略,以提高部署效率、增强系统稳定性,并确保服务的可伸缩性和安全性。通过具体案例分析和比较传统部署方式的局限性,本文展示了Docker如何成为微服务架构优化不可或缺的工具,旨在帮助读者构建一个更加灵活、高效和可靠的服务环境。
209 1
|
消息中间件 架构师 Java
除了「加机器」,其实你的微服务还能这样优化
除了「加机器」,其实你的微服务还能这样优化
78 0
|
2月前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与最佳实践
在微服务架构中,数据库访问的效率直接影响到系统的性能和可扩展性。本文探讨了优化微服务架构中数据库访问的策略与最佳实践,包括数据分片、缓存策略、异步处理和服务间通信优化。通过具体的技术方案和实例分析,提供了一系列实用的建议,以帮助开发团队提升微服务系统的响应速度和稳定性。
|
14天前
|
监控 API 开发者
后端开发中的微服务架构实践与优化
【10月更文挑战第17天】 本文深入探讨了微服务架构在后端开发中的应用及其优化策略。通过分析微服务的核心理念、设计原则及实际案例,揭示了如何构建高效、可扩展的微服务系统。文章强调了微服务架构对于提升系统灵活性、降低耦合度的重要性,并提供了实用的优化建议,帮助开发者更好地应对复杂业务场景下的挑战。
17 7
|
17天前
|
Cloud Native API 持续交付
利用云原生技术优化微服务架构
【10月更文挑战第13天】云原生技术通过容器化、动态编排、服务网格和声明式API,优化了微服务架构的可伸缩性、可靠性和灵活性。本文介绍了云原生技术的核心概念、优势及实施步骤,探讨了其在自动扩展、CI/CD、服务发现和弹性设计等方面的应用,并提供了实战技巧。
|
1月前
|
开发者 Docker 微服务
利用Docker Compose优化微服务架构
在微服务架构中,Docker Compose提供了一种简便有效的方法来定义和运行多容器Docker应用程序,通过YAML文件配置服务、网络和卷,实现一键创建和启动。这不仅确保了开发、测试和生产环境的一致性,还简化了团队协作和维护工作,大幅提升了开发效率。本文将详细介绍Doker Compose的核心优势、基本使用方法及高级功能,帮助你更好地管理和优化微服务架构。
|
13天前
|
存储 Kubernetes 监控
深度解析Kubernetes在微服务架构中的应用与优化
【10月更文挑战第18天】深度解析Kubernetes在微服务架构中的应用与优化
56 0
|
2月前
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
56 5
|
2月前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与实践
随着微服务架构的普及,如何高效管理和优化数据库访问成为了关键挑战。本文探讨了在微服务环境中优化数据库访问的策略,包括数据库分片、缓存机制、异步处理等技术手段。通过深入分析实际案例和最佳实践,本文旨在为开发者提供实际可行的解决方案,以提升系统性能和可扩展性。
|
3月前
|
缓存 NoSQL Redis
go-zero微服务实战系列(七、请求量这么高该如何优化)
go-zero微服务实战系列(七、请求量这么高该如何优化)