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

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

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

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

先从简单入手,以使用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模块完成了服务调用的优化,保证了代码的易维护性与稳定性。

相关文章
|
9天前
|
JSON Java API
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
112 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
4月前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与最佳实践
在微服务架构中,数据库访问的效率直接影响到系统的性能和可扩展性。本文探讨了优化微服务架构中数据库访问的策略与最佳实践,包括数据分片、缓存策略、异步处理和服务间通信优化。通过具体的技术方案和实例分析,提供了一系列实用的建议,以帮助开发团队提升微服务系统的响应速度和稳定性。
|
2月前
|
弹性计算 运维 开发者
后端架构优化:微服务与容器化的协同进化
在现代软件开发中,后端架构的优化是提高系统性能和可维护性的关键。本文探讨了微服务架构与容器化技术如何相辅相成,共同推动后端系统的高效运行。通过分析两者的优势和挑战,我们提出了一系列最佳实践策略,旨在帮助开发者构建更加灵活、可扩展的后端服务。
|
2月前
|
消息中间件 运维 Cloud Native
云原生架构下的微服务优化策略####
本文深入探讨了云原生环境下微服务架构的优化路径,针对服务拆分、通信效率、资源管理及自动化运维等核心环节提出了具体的优化策略。通过案例分析与最佳实践分享,旨在为开发者提供一套系统性的解决方案,以应对日益复杂的业务需求和快速变化的技术挑战,助力企业在云端实现更高效、更稳定的服务部署与运营。 ####
|
2月前
|
存储 NoSQL 分布式数据库
微服务架构下的数据库设计与优化策略####
本文深入探讨了在微服务架构下,如何进行高效的数据库设计与优化,以确保系统的可扩展性、低延迟与高并发处理能力。不同于传统单一数据库模式,微服务架构要求更细粒度的服务划分,这对数据库设计提出了新的挑战。本文将从数据库分片、复制、事务管理及性能调优等方面阐述最佳实践,旨在为开发者提供一套系统性的解决方案框架。 ####
|
2月前
|
Kubernetes API Docker
构建高效后端服务:微服务架构的深度实践与优化####
本文深入探讨了微服务架构在现代后端开发中的应用,通过剖析其核心概念、设计原则及实施策略,结合具体案例分析,展示了如何有效提升系统的可扩展性、可靠性和维护性。文章还详细阐述了微服务拆分的方法论、服务间通信的最佳实践、以及容器化与编排工具(如Docker和Kubernetes)的应用技巧,为读者提供了一份全面的微服务架构落地指南。 ####
|
3月前
|
监控 API 开发者
后端开发中的微服务架构实践与优化
【10月更文挑战第17天】 本文深入探讨了微服务架构在后端开发中的应用及其优化策略。通过分析微服务的核心理念、设计原则及实际案例,揭示了如何构建高效、可扩展的微服务系统。文章强调了微服务架构对于提升系统灵活性、降低耦合度的重要性,并提供了实用的优化建议,帮助开发者更好地应对复杂业务场景下的挑战。
31 7
|
3月前
|
Cloud Native API 持续交付
利用云原生技术优化微服务架构
【10月更文挑战第13天】云原生技术通过容器化、动态编排、服务网格和声明式API,优化了微服务架构的可伸缩性、可靠性和灵活性。本文介绍了云原生技术的核心概念、优势及实施步骤,探讨了其在自动扩展、CI/CD、服务发现和弹性设计等方面的应用,并提供了实战技巧。
|
3月前
|
开发者 Docker 微服务
利用Docker Compose优化微服务架构
在微服务架构中,Docker Compose提供了一种简便有效的方法来定义和运行多容器Docker应用程序,通过YAML文件配置服务、网络和卷,实现一键创建和启动。这不仅确保了开发、测试和生产环境的一致性,还简化了团队协作和维护工作,大幅提升了开发效率。本文将详细介绍Doker Compose的核心优势、基本使用方法及高级功能,帮助你更好地管理和优化微服务架构。
|
3月前
|
存储 Kubernetes 监控
深度解析Kubernetes在微服务架构中的应用与优化
【10月更文挑战第18天】深度解析Kubernetes在微服务架构中的应用与优化
140 0