作者的其他平台:
| CSDN:blog.csdn.net/qq_41153943
| 掘金:juejin.cn/user/651387…
| 知乎:www.zhihu.com/people/1024…
| GitHub:github.com/JiangXia-10…
本文一共2662字,预计阅读13分钟
微服务是分布式架构的一种,分布式架构其实就是要把服务做一个拆分,而springcloud只是解决了拆分过程中的服务治理问题。
在单体架构中,我们把所有的服务都写在一起,随着业务的复杂代码的耦合度就会越来越高,不便于将来的升级维护。
所以往往需要拆分这些服务,微服务在拆分的时候,会根据业务功能模块把一个单体的应用拆分成许多个独立的项目,每个项目完成一部分的业务功能,然后独立开发和部署。这些独立的项目就成为一个微服务。进而构成一个服务集群。
比如最为常见的电商系统是由很多的服务组成的, 比如订单服务,用户功能,商品服务,支付服务等等,之前的单体架构时期这些模块使用单体架构来实现,那么耦合度会相当高,开发难度也会很大,尤其后期维护和拓展的时候难度大、也麻烦。但是如果使用微服务开发,这样就可以把每一个服务都当成一个单体应用来开发,那么订单服务作为一个微服务,用户服务也作为一个微服务开发。
然后由这些每个独立服务的微服务构成整个的电商系统。这样每个服务都可以根据具体的业务需要去进行集群部署,不仅降低了服务的耦合,也有利于服务的维护升级。
前面几篇文章都提到了微服务开发中的注册中心。我们知道一个业务往往就需要多个服务来共同完成,比如一个请求发送过来,它先是调用了服务A,然后服务A有可能再去调用服务B,服务B调用服务C,等等。
所以当业务逻辑越来越复杂的时候,这些服务之间的调用关系就会变得越加地错综复杂,这时候就需要一个注册中心,用来记录每一个服务的ip,端口,服务名称以及它能够完成的功能。这样就能够使得这些服务之间在需要互相调用的时候,不用去记录要调用服务的ip、端口,而只要到注册中心去找就可以了。
常见的注册中心有nacos(可以参考SpringCloud:搭建Nacos服务以及服务发现)、eureka(可以参考SpringCloud系列:服务注册与发现组件-Eureka(上)、SpringCloud系列:服务注册与发现组件-Eureka(下))、consul(可以参考SpringCloud系列:服务注册中心组件—consul)以及zookeeper(可以参考:zookeeper教程:入门篇)。
那么除了注册中心之外,微服务的服务调用最主要的还有各个服务之间的通信问题。今天就一起学习微服务之间的通信问题。
微服务之间的通信根据通信类型可以分为两种:
一种是同步通信:主要方式有HTTP REST、RPC的方式。HTTP REST方式指的是使用http协议进行数据传递,数据格式是使用json格式;RPC是一种远程过程调用,消息格式是采用二进制格式。
http rest 是属于osi七层模型中的应用层,而rpc则属于传输层,所以rpc的效率要比http rest的效率更加高效。
还有一种是异步通信:异步消息传递通常有两种模式,一种是无代理模式:指的是C端直接将消息发送给P端即可不需要得到恢复,当需要P端恢复时由于是异步通信,C端也不会阻塞,P端处理完后再根据接收到的信息向C端回复处理结果。这种方式比较小众主要适用于一些异步通知场景。另外一种就是:使用消息代理,这里需要借助一些消息中间件,比如RocketMQ、kafka等。这种方式的使用场景比较广泛,行业内有很多成熟方案。通过使用异步消息通信的方式能够很好的实现微服务的解耦。
今天主要说的是同步通信的方式,异步的主要借助的是一些消息中间件,后续学习中间件的时候再出篇文章介绍这种方式。springcloud中同步的通信方式主要使用的是http rest的方式。
Spring框架提供了一个httpclient对象RestTemplate,发起一个http的请求。
这里创建两个独立的子项目,分别为orders和users,并且使用consul注册中心,并向其注册。具体的创建过程和SpringCloud系列:服务注册中心组件—consul这篇文章创建的微服务项目一样可以参考,只需要修改一些配置,比如端口号,服务名即可,具体的源码放在文章最后,有需要可以下载参考。
项目结构如下:
分别启动两个项目,打开consul的管理页面,则两个微服务都已经分别注册进了consul注册中心了。
接下来就是开发服务之间的调用逻辑。服务调用其实就是接口调用,所以这里需要分别在user和order服务中新增controller接口。order中controller如下:
@RestController
@RequestMapping("order")
public class OrderController {
private static final Logger logger = LoggerFactory.getLogger(OrderController.class);
@GetMapping("order")
public String order(){
logger.info("order module is running....");
return "order module is running";
}
}
复制代码
这里需要使用的是在user服务中调用order中的接口,所以user中controller如下:
@RestController
@RequestMapping("user")
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping("user")
public String user(){
logger.info("user module is running....");
// 在user服务中调用order服务,并且接受返回值
//创建一个RestTemplate对象
RestTemplate restTemplate = new RestTemplate();
//调用order接口
String result = restTemplate.getForObject("http://localhost:8084/order/order",String.class);
logger.info("调用order服务成功....",result);
return "user服务中调用order服务,结果为:"+result;
}
}
复制代码
这里使用的是resttemplate对象进行接口调用并且接受返回对象。
到这里就开发完了,核心就是resttemplate对象的使用。然后分别启动user和order项目。首先地址栏输入:
http://localhost:8084/order/order
复制代码
回车,结果如下:
表示order服务的接口调用成功。但这里是直接调用。接下来测试user服务中调用order服务的接口,所以地址栏输入:
http://localhost:8083/user/user
复制代码
回车,结果如下:
控制台输出如下:
表示在user服务中调用order服务中的接口成功!
可以发现使用resttemplate对象进行服务调用其实很简单,就是使用resttemplate提供的getForXX的方法,然后接受返回的对象即可。所以可以总结下它的特点:
1、使用简单,直接支持请求/响应方式的通信,
2、并且可测试很高,可以通过浏览器或者curl命令来测试。
3、不需要中间代理,简化了系统架构
但是通过上述的代码,可以发现它的缺点也很明显:
1、客户端必须知道服务端的具体位置(URL),需要写在请求方法中;
2、调用服务请求的路径url是直接写在方法中的并且是写死的,所以无法实现服务集群时请求的负载均衡;
3、因为是写死的所以如果后续的迭代导致路径发生变化时不利于后续的维护和拓展工作。
4、可能会导致可用性降低。通信过程需要客户端和服务端双方同时在线,任何一段故障通信均将失败。
但是对于请求的负载均衡,springcloud提供了对应的负载均衡组件-ribbon。spring cloud ribbon是一个基于http和tcp的客户端负载均衡工具,是基于netflix ribbon实现的,通过spring cloud封装, 可以让我们将面向服务的resttemplate请求自动转换成客户端负载均衡的服务调用。
由于文章篇幅有限,关于ribbon的学习,后面继续。
本文源码在github:github.com/JiangXia-10…
Spring注解(三):@scope设置组件作用域
Spring常用注解大全,值得你的收藏!!!
Spring注解(七):使用@Value对Bean进行属性赋值
SpringBoot开发Restful风格的接口实现CRUD功能
Spring注解(六):Bean的生命周期中自定义初始化和销毁方法的四种方式
SpringCloud系列:服务注册中心组件—consul