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

本文涉及的产品
注册配置 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模块完成了服务调用的优化,保证了代码的易维护性与稳定性。

相关文章
|
4月前
|
运维 云计算 Docker
深入理解与实践:基于Docker的微服务架构优化策略
本文旨在为软件开发和运维人员提供一个全面的指南,探讨如何通过Docker容器技术优化微服务架构。我们不仅深入分析了Docker在微服务环境中的关键作用,还提出了一系列实践策略,以提高部署效率、增强系统稳定性,并确保服务的可伸缩性和安全性。通过具体案例分析和比较传统部署方式的局限性,本文展示了Docker如何成为微服务架构优化不可或缺的工具,旨在帮助读者构建一个更加灵活、高效和可靠的服务环境。
200 1
|
11月前
|
消息中间件 架构师 Java
除了「加机器」,其实你的微服务还能这样优化
除了「加机器」,其实你的微服务还能这样优化
65 0
|
3天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与最佳实践
在微服务架构中,数据库访问的效率直接影响到系统的性能和可扩展性。本文探讨了优化微服务架构中数据库访问的策略与最佳实践,包括数据分片、缓存策略、异步处理和服务间通信优化。通过具体的技术方案和实例分析,提供了一系列实用的建议,以帮助开发团队提升微服务系统的响应速度和稳定性。
|
Java 微服务 Spring
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)(优化篇)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)(优化篇)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题
从0到1 手把手搭建spring cloud alibaba 微服务大型应用框架(六)(优化篇)开发篇-如何解决微服务开发环境请求实例转发到别人机器问题
|
3天前
|
消息中间件 缓存 监控
优化微服务架构中的数据库访问:策略与实践
随着微服务架构的普及,如何高效管理和优化数据库访问成为了关键挑战。本文探讨了在微服务环境中优化数据库访问的策略,包括数据库分片、缓存机制、异步处理等技术手段。通过深入分析实际案例和最佳实践,本文旨在为开发者提供实际可行的解决方案,以提升系统性能和可扩展性。
|
25天前
|
缓存 NoSQL Redis
go-zero微服务实战系列(七、请求量这么高该如何优化)
go-zero微服务实战系列(七、请求量这么高该如何优化)
|
24天前
|
消息中间件 NoSQL Kafka
go-zero微服务实战系列(九、极致优化秒杀性能)
go-zero微服务实战系列(九、极致优化秒杀性能)
|
3月前
|
机器学习/深度学习 设计模式 人工智能
人工智能和机器学习技术来优化微服务架构
人工智能和机器学习技术来优化微服务架构
45 1
|
3月前
|
数据库 微服务 NoSQL
探索微服务架构下的数据库选型与优化策略
在现代软件开发中,微服务架构已成为一种常见的设计范式。而数据库在微服务架构中的选型与优化策略对整个系统的性能和稳定性至关重要。本文将探讨在微服务环境下,如何选择适合的数据库类型以及优化数据库性能的策略。
|
3月前
|
NoSQL Java 数据库
优化基于阿里云的微服务架构下的数据库访问性能
在应对大型电商项目中数据库访问性能瓶颈问题时,团队通过阿里云工具分析发现高QPS、慢查询和不合理数据交互是关键。优化措施包括:1) 索引优化,针对慢查询添加或调整索引;2) 开启读写分离,使用RDS读写分离功能和DRDS进行水平拆分;3) 引入Redis缓存热点数据,减少直接数据库访问;4) 服务化数据访问,降低跨服务数据库调用;5) 使用Sentinel进行限流和熔断,保护数据库资源。这些改进显著提升了系统响应速度和用户体验。