微服务调用特点
微服务调用和应用内调用不同点在于它是跨进程的,甚至是跨节点的,这意味着两点:
1、对外部有了依赖
2、如果是跨节点,就有了网络调用。我们知道网络都是不可靠的
如何做?
对外部有了依赖
微服务架构设计中有一条重要的原则叫严出宽进,严出意思就是说你提供给其他服务的东西要尽可能的进行严格的校验。宽进就是你调用别人的接口要宽容,兼容各种情况。比如说你需要考虑别人的节点down了/api超时/api不可用等等因素。
为了解决这个问题,我们必须要针对具体业务,分析依赖类型,是强依赖还是弱依赖,强依赖包装成自己的服务异常返回码/或者直接告诉前端调用不可用。弱依赖,catch所有异常,无论依赖方发生什么,不能影响我的接口返回。
此外,我依赖的服务某段时间内接口错误率很高,调用方还在不停的发送请求,那么就会一直得到错误的结果,这时候这些请求其实是无效的,所以这时候需要客户端熔断,不再去调用服务方,给服务方恢复的时间,等过段时间再去重试,发现服务方可用时,再去调用。
网络调用
网络调用是耗时的,所以我们需要利用池化技术,复用连接,比如在单体应用中我们需要与数据库连接,会利用到数据库连接池来提高数据操作效率。那么微服务调用也是这么个道理,我们与其他服务建议http/tcp连接,也需要建立连接池。另外一个服务可能会依赖多个微服务,不能因为和某个服务之间的连接出了问题,影响到与其他服务的连接,所以需要做连接池隔离。
服务间调用有可能失败,所以我们需要有重试机制,比如因为网络抖动引发的超时问题,我们可以通过重试提高API的可用性。 但是思考一下坏的情况,某段时间网络或者服务端真的有问题了。客户端超时时间设置的很大的话,客户端等待的时间就会很长,然后再加上重试机制,就会将客户端的连接占满,造成客户端相关API不可用。
简单代码实现
假设一个业务场景:
咱们现在开发一个电商网站,要实现支付订单的功能,流程如下:
1、创建一个订单之后,如果用户立刻支付了这个订单,我们需要将订单状态更新为“已支付”
2、扣减相应的商品库存
针对上述流程,我们需要有订单服务、库存服务。整个流程的大体思路如下:
用户针对一个订单完成支付之后,就会去找订单服务,更新订单状态
订单服务调用库存服务,完成相应功能
至此,整个支付订单的业务流程结束
下图这张图,清晰表明了各服务间的调用过程:
首先添加maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
配置文件
application.properties配置如下
server:
port: 8071
logging:
level:
root: info
spring:
datasource:
url: jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8&useSSL=false
username: root
password: lovo
type: com.alibaba.druid.pool.DruidDataSource
redis:
database: 0
host: localhost
port: 6379
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 8
min-idle: 0
timeout: 10000
cloud:
nacos:
server-addr: localhost:8848
sentinel:
transport:
port: 9999 #跟控制台交流的端口,随意指定一个未使用的端口即可
dashboard: localhost:8858 # 指定控制台服务的地址
client-ip: 192.168.65.1
application:
name: shop-commodity
mybatis-plus:
type-aliases-package: com.lovo.common.bean
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
启动类
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan("com.lovo")
public class CommodityApplication {
public static void main(String[] args) {
SpringApplication.run(CommodityApplication.class,args);
}
}
然后编写服务之间调用的FeignClient代码
feign调用实现
@FeignClient(value="shop-commodity",configuration = FeignConfig.class)
public interface ICommodityService {
/**
* 调用微服务的地址与请求类型
*/
@GetMapping("/commodities/{id}")
Result getCommodityById(@PathVariable("id") Long id);
@PostMapping("/commoditySnapshots")
Result addCommoditySnapshot(@RequestBody CommoditySnapshotBean commoditySnapshotBean);
}