SpringCloud Alibaba微服务实战三 - 服务调用

简介: SpringCloud Alibaba微服务实战三 - 服务调用

导读:通过前面两篇文章我们准备好了微服务的基础环境并让accout-service 和 product-service对外提供了增删改查的能力,本篇我们的内容是让order-service作为消费者远程调用accout-service和product-service的服务接口。


统一接口返回结构


在开始今天的正餐之前我们先把上篇文章中那个丑陋的接口返回给优化掉,让所有的接口都有统一的返回结构。

  • 建立公共模块cloud-common
  • 其他模块都引入cloud-common,修改pom文件,加入依赖
<dependency>
  <groupId>com.jianzh5.cloud</groupId>
  <artifactId>cloud-common</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
  • 建立接口返回的数据结构,这个数据结构大家可以根据自身项目情况统一即可
@Data
publicclass ResultData<T> {
    /** 结果状态 ,正常响应200,其他状态码都为失败*/
    privateint status;
    private String message;
    private T data;
    privateboolean success;
    privatelong timestamp ;
    ... 提供一些静态方法 ...
}
  • 改造accout-serviceproduct-service 模块中controller层的返回结构,改造完的代码如下:
@RestController
@Log4j2
publicclass AccountController {
    @Autowired
    private AccountService accountService;
    @PostMapping("/account/insert")
    public ResultData<String> insert(@RequestBody AccountDTO accountDTO){
        log.info("insert account:{}",accountDTO);
        accountService.insertAccount(accountDTO);
        return ResultData.success("insert account succeed");
    }
    @PostMapping("/account/delete")
    public ResultData<String> delete(@RequestParam String accountCode){
        log.info("delete account,accountCode is {}",accountCode);
        accountService.deleteAccount(accountCode);
        return ResultData.success("delete account succeed");
    }
    @PostMapping("/account/update")
    public  ResultData<String> update(@RequestBody AccountDTO accountDTO){
        log.info("update account:{}",accountDTO);
        accountService.updateAccount(accountDTO);
        return ResultData.success("update account succeed");
    }
    @GetMapping("/account/getByCode/{accountCode}")
    public ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode){
        log.info("get account detail,accountCode is :{}",accountCode);
        AccountDTO accountDTO = accountService.selectByCode(accountCode);
        return ResultData.success(accountDTO);
    }
}


服务调用


SpringCloud体系中,所有微服务间的通信都是通过Feign进行调用,Feign是一个http请求调用的轻量级框架,可以以Java接口注解的方式调用Http请求,而不用像使用HttpClientOKHttp3等组件通过封装HTTP请求报文的方式调用。Feign通过处理注解,将请求模板化,当实际调用的时候,传入参数,根据参数再应用到请求上,进而转化成真正的请求,这种请求相对而言比较直观。而且Feign默认集成了负载均衡器Ribbon,不需要自己实现负载均衡逻辑。

FeignSpringCloud的组件,在引入Feign之前我们先看看Spring BootSpring CloudSpring Cloud Alibaba 三者之间的关系,防止在业务中引入了错误的版本。

Spring Boot Spring Cloud Spring Cloud Alibaba
2.1.x Greenwich 0.9.x
2.0.x Finchley 0.2.x
1.5.x Edgware 0.1.x
1.5.x Dalston 0.1.x

很显然,我们引用的是SpringCloud Alibab 0.9.0,所以我们需要引入SpringCloud Greenwich

  • 引入SpringCloud版本依赖 在项目主pom <dependencyManagement>中引入SpringCloud依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-dependencies</artifactId>
  <version>Greenwich.SR2</version>
  <type>pom</type>
  <scope>import</scope>
</dependency>
  • 在所有需要用到Feign的模块中引入openfeign依赖
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • 抽取公共的接口层,建立Feign接口,接口的定义和返回值需要跟Controller层保持一致
@FeignClient(name = "account-service")
publicinterface AccountFeign {
    @PostMapping("/account/insert")
    ResultData<String> insert(@RequestBody AccountDTO accountDTO);
    @PostMapping("/account/delete")
    ResultData<String> delete(@RequestParam("accountCode") String accountCode);
    @PostMapping("/account/update")
    ResultData<String> update(@RequestBody AccountDTO accountDTO);
    @GetMapping("/account/getByCode/{accountCode}")
    ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode);
}

在接口上添加注解@FeignClient(name = "account-service"),表明这是一个Feign客户端,name属性的配置表示我这个接口最终会转发到accout-service上。

正如回字有多种写法,这里Feign也有多种使用方式。第一种就是我们这里介绍的,Feign和生产者的RequestMapping保持一致,大家可以看看上面改造后的AccountControllerAccountFeign一模一样有米有。

第二种方式就是让我们的Controller直接实现Feign接口,不再需要写RequestMapping,如:

@RestController
@Log4j2
publicclass ProductController implements ProductFeign {
    @Autowired
    private ProductService productService;
    @Override
    public ResultData<String> insert(@RequestBody ProductDTO productDTO){
        log.info("insert product:{}",productDTO);
        productService.insertProduct(productDTO);
        return ResultData.success("insert product succeed");
    }
}
  • 消费者模块引入Feign接口层的依赖
<dependency>
  <groupId>com.jianzh5.cloud</groupId>
  <artifactId>account-feign</artifactId>
  <version>1.0-SNAPSHOT</version>
</dependency>
  • 在消费者product-service启动类上添加@EnableFeignClients注解
@SpringBootApplication
@RestController
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.javadaily.feign.*")
publicclass OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}
  • 消费者端跟使用本地service一样使用Feign
@RestController
publicclass OrderController {
    @Autowired
    private AccountFeign accountFeign;
    @Autowired
    private ProductFeign productFeign;
    @PostMapping("/order/getAccount/{accountCode}")
    public ResultData<AccountDTO> getAccount(@PathVariable String accountCode){
        return accountFeign.getByCode(accountCode);
    }
    @PostMapping("/order/insertAccount")
    public ResultData<String> insertAccount(AccountDTO accountDTO){
        return accountFeign.insert(accountDTO);
    }
    @PostMapping("/order/updateAccount")
    public ResultData<String> updateAccount(AccountDTO accountDTO){
        return accountFeign.update(accountDTO);
    }
    @PostMapping("/order/deleteAccount/{accountCode}")
    public ResultData<String> deleteAccount(@PathVariable String accountCode){
        return accountFeign.delete(accountCode);
    }
}
  • 项目模块截图
  • 联调测试 我们请求OrderController中的接口,验证下接口请求结果:联调成功,搞定收工!

血与泪


使用feign过程中有以下几点需要注意,否则一不小心你就会掉进坑里。(我不会告诉你我当时在坑里踩了多久才爬上来)

  • Feign不支持直接使用对象作为参数请求接口中如果有多参数需要用实体接收,要么把参数一个一个摆开,要么在对象参数上加上@RequestBody注解,让其以json方式接收,如:
@PostMapping("/account/insert")ResultData<String> insert(@RequestBody AccountDTO accountDTO);
  • 消费者模块启动类上使用@EnableFeignClients注解后一定要指明Feign接口所在的包路径如:@EnableFeignClients(basePackages = "com.javadaily.feign.*")否则你的消费者启动时会报如下的错误:所以这里推荐你们在开发中所有feign模块最好能统一包名前缀com.javadaily.feign
  • @RequestParam的坑在Feign接口层使用@RequestParam注解要注意,一定要加上value属性,如:ResultData<String> delete(@RequestParam("accountCode") String accountCode);否则你会看到类似如下的错误:Caused by: java.lang.IllegalStateException: RequestParam.value() was empty on parameter 0这个异常
  • @PathVariable的坑在Feign接口层使用@PathVariable注解要注意,一定要跟上面一样加上value属性,如:ResultData<AccountDTO> getByCode(@PathVariable(value = "accountCode") String accountCode);否则你也会看到类似如下的错误@PathVariable(value = "accountCode") String accountCode
  • 在消费者配置文件中添加Feign超时时间配置在我们的order-service配置文件中增加feign超时时间配置
feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000

否则你会经常看到如下所示的错误:

java.net.SocketTimeoutException: Read timed out
  at java.net.SocketInputStream.socketRead0(Native Method) ~[?:1.8.0_112]

至此我们已经完成了项目公共返回接口的统一并且成功使用Feign调用远程生产者的服务,那么本期的“SpringCloud Alibaba微服务实战三 - 服务调用”篇也就该结束啦,咱们下期有缘再见!

目录
相关文章
|
6月前
|
NoSQL MongoDB 微服务
微服务——MongoDB实战演练——文章评论的基本增删改查
本节介绍了文章评论的基本增删改查功能实现。首先,在`cn.itcast.article.dao`包下创建数据访问接口`CommentRepository`,继承`MongoRepository`以支持MongoDB操作。接着,在`cn.itcast.article.service`包下创建业务逻辑类`CommentService`,通过注入`CommentRepository`实现保存、更新、删除及查询评论的功能。最后,新建Junit测试类`CommentServiceTest`,对保存和查询功能进行测试,并展示测试结果截图,验证功能的正确性。
99 2
|
6月前
|
NoSQL Java MongoDB
微服务——MongoDB实战演练——文章评论实体类的编写
本节主要介绍文章评论实体类的编写,创建了包`cn.itcast.article.po`用于存放实体类。具体实现中,`Comment`类通过`@Document`注解映射到MongoDB的`comment`集合,包含主键、内容、发布时间、用户ID、昵称等属性,并通过`@Indexed`和`@CompoundIndex`注解添加单字段及复合索引,以提升查询效率。同时提供了Mongo命令示例,便于理解和操作。
97 2
|
6月前
|
NoSQL 测试技术 MongoDB
微服务——MongoDB实战演练——MongoTemplate实现评论点赞
本节介绍如何使用MongoTemplate实现评论点赞功能。传统方法通过查询整个文档并更新所有字段,效率较低。为优化性能,采用MongoTemplate对特定字段直接操作。代码中展示了如何利用`Query`和`Update`对象构建更新逻辑,通过`update.inc(&quot;likenum&quot;)`实现点赞数递增。测试用例验证了功能的正确性,确保点赞数成功加1。
109 0
|
6月前
|
NoSQL 测试技术 MongoDB
微服务——MongoDB实战演练——根据上级ID查询文章评论的分页列表
本节介绍如何根据上级ID查询文章评论的分页列表,主要包括以下内容:(1)在CommentRepository中新增`findByParentid`方法,用于按父ID查询子评论分页列表;(2)在CommentService中新增`findCommentListPageByParentid`方法,封装分页逻辑;(3)提供JUnit测试用例,验证功能正确性;(4)使用Compass插入测试数据并执行测试,展示查询结果。通过这些步骤,实现对评论的高效分页查询。
86 0
|
3月前
|
NoSQL Java 微服务
2025 年最新 Java 面试从基础到微服务实战指南全解析
《Java面试实战指南:高并发与微服务架构解析》 本文针对Java开发者提供2025版面试技术要点,涵盖高并发电商系统设计、微服务架构实现及性能优化方案。核心内容包括:1)基于Spring Cloud和云原生技术的系统架构设计;2)JWT认证、Seata分布式事务等核心模块代码实现;3)数据库查询优化与高并发处理方案,响应时间从500ms优化至80ms;4)微服务调用可靠性保障方案。文章通过实战案例展现Java最新技术栈(Java 17/Spring Boot 3.2)的应用.
185 9
|
3月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
|
3月前
|
负载均衡 Java API
基于 Spring Cloud 的微服务架构分析
Spring Cloud 是一个基于 Spring Boot 的微服务框架,提供全套分布式系统解决方案。它整合了 Netflix、Zookeeper 等成熟技术,通过简化配置和开发流程,支持服务发现(Eureka)、负载均衡(Ribbon)、断路器(Hystrix)、API网关(Zuul)、配置管理(Config)等功能。此外,Spring Cloud 还兼容 Nacos、Consul、Etcd 等注册中心,满足不同场景需求。其核心组件如 Feign 和 Stream,进一步增强了服务调用与消息处理能力,为开发者提供了一站式微服务开发工具包。
459 0
|
12月前
|
SpringCloudAlibaba API 开发者
新版-SpringCloud+SpringCloud Alibaba
新版-SpringCloud+SpringCloud Alibaba
|
6月前
|
负载均衡 Dubbo Java
Spring Cloud Alibaba与Spring Cloud区别和联系?
Spring Cloud Alibaba与Spring Cloud区别和联系?
|
7月前
|
人工智能 SpringCloudAlibaba 自然语言处理
SpringCloud Alibaba AI整合DeepSeek落地AI项目实战
在现代软件开发领域,微服务架构因其灵活性、可扩展性和模块化特性而受到广泛欢迎。微服务架构通过将大型应用程序拆分为多个小型、独立的服务,每个服务运行在其独立的进程中,服务与服务间通过轻量级通信机制(通常是HTTP API)进行通信。这种架构模式有助于提升系统的可维护性、可扩展性和开发效率。
2103 2