SpringCloud
Posted sp_wxf
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringCloud相关的知识,希望对你有一定的参考价值。
这里写目录标题
1.系统架构的演变
集中式架构 > 垂直拆分 > 分布式架构 > SOA > 微服务
1.1 集中式架构
将业务的所有功能集中在一个项目中开发,打成一个包部署。
- 优点
- 架构简单
- 部署成本低
- 缺点
- 耦合度高(维护困难、升级困难)
1.2 垂直拆分
当访问量逐渐增大,单一应用无法满足需求,此时为了应对更高的并发和业务需求,我们根据业务功能对系统进行拆分:
- 优点
- 系统拆分实现了流量分担,解决了并发问题
- 可以针对不同模块进行优化
- 方便水平扩展,负载均衡,容错率提高
- 缺点
- 系统间相互独立,会有很多重复开发工作,影响开发效率
1.3 分布式服务
当垂直应用越来越多,应用之间交互不可避免,将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,使前端应用能更快速的响应多变的市场需求。此时,用于提高业务复用及整合的分布式调用是关键。
- 优点
- 将基础服务进行了抽取,系统间相互调用,提高了代码复用和开发效率
- 缺点
- 系统间耦合度变高,调用关系错综复杂,难以维护
1.4 流动计算架构(SOA)
当服务越来越多,容量的评估,小服务资源的浪费等问题逐渐显现,此时需增加一个调度中心基于访问压力实时管理集群容量,提高集群利用率。此时,用于提高机器利用率的资源调度和治理中心(SOA)是关键
服务治理要做什么?
1.服务注册中心,实现服务自动注册和发现,无需人为记录服务地址
2.服务自动订阅,服务列表自动推送,服务调用透明化,无需关心依赖关系
3.动态监控服务状态监控报告,人为控制服务状态
- 缺点
- 服务间会有依赖关系,一旦某个环节出错会影响较大
- 服务关系复杂,运维、测试部署困难,不符合DevOps思想
1.5 微服务
微服务的架构特征:
- 单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
- 自治:自治是说服务间互相独立,互不干扰
- 团队独立:每个服务都是一个独立的开发团队,人数不能过多。
- 技术独立:因为是面向服务,提供Rest接口,使用什么技术没有别人干涉
- 前后端分离:采用前后端分离开发,提供统一Rest接口,后端不用再为PC、移动段开发不同接口
- 数据库分离:每个服务都使用自己的数据源
- 部署独立,服务间虽然有调用,但要做到服务重启不影响其它服务。有利于持续集成和持续交付。每个服务都是独立的组件,可复用,可替换,降低耦合,易维护
- 面向服务:面向服务是说每个服务都要对外暴露Rest风格服务接口API。并不关心服务的技术实现,做到与平台和语言无关,也不限定用什么技术实现,只要提供Rest的接口即可。
- 隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
微服务的上述特性其实是在给分布式架构制定一个标准,进一步降低服务之间的耦合度,提供服务的独立性和灵活性。做到高内聚,低耦合。
因此,可以认为微服务是一种经过良好架构设计的分布式架构方案 。
2.服务调用方式
2.1 RPC和HTTP
无论是微服务还是SOA,都面临着服务间的远程调用。那么服务间的远程调用方式有哪些呢?常见的远程调用方式有以下2种:
- RPC:Remote Produce Call远程过程调用,类似的还有RMI。自定义数据格式,基于原生TCP通信,速度快,效率高。早期的webservice,现在热门的dubbo,都是RPC的典型代表
- Http:http其实是一种网络传输协议,基于TCP,规定了数据传输的格式。现在客户端浏览器与服务端通信基本都是采用Http协议,也可以用来进行远程服务调用。缺点是消息封装臃肿,优势是对服务的提供和调用方没有任何技术限定,自由灵活,更符合微服务理念。
如果你们公司全部采用Java技术栈,那么使用Dubbo作为微服务架构是一个不错的选择。相反,如果公司的技术栈多样化,而且你更青睐Spring家族,那么SpringCloud搭建微服务是不二之选。在我们的项目中,我们会选择SpringCloud套件,因此我们会使用Http方式来实现服务间调用。
2.2 Http客户端工具
已经有很多的http客户端工具,能够帮助我们做这些事情,例如:
- HttpClient
- OKHttp
- URLConnection
接下来,不过这些不同的客户端,API各不相同
2.3 Spring的RestTemplate(封装Http客户端)
Spring提供了一个RestTemplate模板工具类,对基于Http的客户端进行了封装,并且实现了对象与json的序列化和反序列化,非常方便。RestTemplate并没有限定Http的客户端类型,而是进行了抽象,目前常用的3种都有支持:
- HttpClient
- OkHttp
- JDK原生的URLConnection(默认的)
使用步骤:
一、在Spring容器中注册RestTemplate
可以在启动类位置注册:
@SpringBootApplication
public class HttpDemoApplication
public static void main(String[] args)
SpringApplication.run(HttpDemoApplication.class, args);
@Bean
public RestTemplate restTemplate()
return new RestTemplate();
二、在测试类中注入,并调用其他服务的接口
@RunWith(SpringRunner.class)
@SpringBootTest(classes = HttpDemoApplication.class)
public class HttpDemoApplicationTests
@Autowired
private RestTemplate restTemplate;
@Test
public void httpGet()
// 调用springboot案例中的rest接口
User user = this.restTemplate.getForObject("http://localhost/user/1", User.class);
System.out.println(user);
通过RestTemplate的getForObject()方法,传递url地址及实体类的字节码,RestTemplate会自动发起请求,接收响应,并且帮我们对响应结果进行反序列化。
3.SpringCloud
SpringCloud集成了各种微服务功能组件,并基于SpringBoot实现了这些组件的自动装配,从而提供了良好的开箱即用体验。
3.1 微服务场景模拟
案例:从订单模块查询订单的同时,根据订单中包含的用户编号查询出用户信息
在进行模拟之前,我们先了解两个概念:服务拆分、远程调用
3.2服务拆分
在集中式架构中,我们会将订单服务和用户服务的功能写在同一个项目中,缺点也很明显:功能模块耦合度高,不利于开发和维护
但是在分布式架构中,我们会将服务进行拆分,微服务也同样如此
一、服务拆分原则:
- 不同微服务,不要重复开发相同业务
- 微服务数据独立,不要访问其它微服务的数据库
- 微服务可以将自己的业务暴露为接口,供其它微服务调用
二、服务拆分示例:
cloud-demo:父工程,管理依赖
- order-service:订单微服务,负责订单相关业务
- user-service:用户微服务,负责用户相关业务
3.3 提供者和消费者
在调用关系中,我们要明确服务提供者和服务消费者
- 服务提供者:
- 提供接口给其他微服务
- 服务消费者:
- 调用其他微服务的接口
3.4 微服务案例实现
一、服务提供者对外提供接口
服务提供者只需要对外提供接口即可,不需要其他操作
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController
@Autowired
private UserService userService;
/**
* 根据用户编号查询用户信息
*/
@GetMapping("/id")
public User queryById(@PathVariable("id") Long id)
return userService.queryById(id);
二、服务消费者注册RestTemplate
服务站消费者想要调用远程服务(Http方式)的前提是注册RestTemplate(此处是JDK原生的URLConnection), 我们可以在启动类中,注册RestTemplate实例
@SpringBootApplication
public class OrderApplication
public static void main(String[] args)
SpringApplication.run(OrderApplication.class, args);
@Bean
public RestTemplate restTemplate()
return new RestTemplate();
三、服务消费者远程调用服务
使用RestTemplate的getForObject()方法进行服务调用
@RestController
@RequestMapping("order")
public class OrderController
@Autowired
private OrderService orderService;
@Autowired
private RestTemplate restTemplate;
@GetMapping("orderId")
public Order queryOrderByUserId(@PathVariable("orderId") Long orderId)
Order order = orderService.queryOrderById(orderId);
// 远程调用(Http方式)
String url = "http://localhost:8081/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
order.setUser(user);
return order;
四、流程图
3.5 存在的问题
问题引入
- order-service在发起远程调用的时候,该如何得知user-service实例的ip地址和端口?
- 有多个user-service实例地址,order-service调用时该如何选择?
- order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
问题总结
- 在consumer中,我们把url地址硬编码到了代码中,不方便后期维护
- consumer需要记忆provider的地址,如果出现变更,可能得不到通知,地址将失效
- consumer不清楚provider的状态,服务宕机也不知道
- provider只有1台服务,不具备高可用性
- 即便provider形成集群,consumer还需自己实现负载均衡
其实上面说的问题,概括一下就是分布式服务必然要面临的问题
- 服务管理
- 如何自动注册和发现
- 如何实现状态监管
- 如何实现动态路由
- 服务如何实现负载均衡
- 服务如何解决容灾问题
- 服务如何实现统一配置
4.Eureka注册中心
问题分析
在刚才的案例中,user服务对外提供服务,需要对外暴露自己的地址。而order服务(调用者)需要记录服务提供者的地址。将来地址出现变更,还需要及时更新。这在服务较少的时候并不觉得有什么,但是在现在日益复杂的互联网环境,一个项目肯定会拆分出十几,甚至数十个微服务。此时如果还人为管理地址,不仅开发困难,将来测试、发布上线都会非常麻烦,这与DevOps的思想是背道而驰的。
4.1 Eureka的作用:
一、order-service怎么知道user-service的实例地址
获取地址信息流程:
- user-service实例启动后,将自己的信息注册到eureka-server(Eureka服务端),这个叫注册服务
- eureka-server保存服务名称到服务实例地址列表的映射关系
- 集群时, 一个服务名称可能对应多个服务实例地址
- provider根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
二、order-service怎么从多个user-service实例中选择具体的实例
- order-service从实例列表中利用负载均衡算法选中一个实例地址
- 向该实例地址发起远程调用
三、order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
- user-service服务会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
- 当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除
- order-service拉取服务列表时,就能将故障实例排除了
4.2 搭建eureka-server
一、引入eureka依赖
引入SpringCloud为eureka提供的starter依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
使用SpringCloud的组件,例如spring-cloud-starter-netflix-eureka-server
,需要引入spring-cloud-dependencies
- spring-cloud-dependencies:定义了SpringCloud组件的版本号
Springcloud的版本依赖问题(最全,包含springCloud所有的版本)
我们可以在父工程中定义
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR10</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
二、编写启动类
启动类一定要添加@EnableEurekaServer
注解,开启eureka的注册中心功能
package cn.itcast.eureka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication
public static void main(String[] args)
SpringApplication.run(EurekaApplication.class, args);
三、编写application.yml
server:
port: 10086 # 端口
spring:
application:
name: eureka-server # 应用名称, 会在Eureka中显示
eureka:
client:
service-url: # EurekaServer的地址,现在是自己的地址,如果是集群,需要加上其它Server的地址。
defaultZone: http://127.0.0.1:$server.port/eureka
四、启动服务
启动微服务,然后在浏览器访问:http://127.0.0.1:10086
4.3 provider进行服务注册
下面,我们将user-service注册到eureka-server中去。
一、引入依赖
引入下面的eureka-client依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
二、编写application.yml
spring:
application:
name: user-service # 应用名称,注册到eureka后的服务名称
eureka:
client:
service-url: # EurekaServer地址
defaultZone: http://127.0.0.1:10086/eureka
三、启动多个user-service实例
为了演示一个服务有多个实例的场景,我们添加一个SpringBoot的启动配置,再启动一个user-service。将第二个实例的端口改为8082
查看eureka-server管理页面:
4.4 consumer实现服务发现
一、引入依赖(依赖与服务注册一直)
之前说过,服务发现、服务注册统一都封装在eureka-client依赖,因此这一步与服务注册时一致。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
二、编写application.yml(配置与服务注册一致)
服务发现也需要知道eureka地址,因此第二步与服务注册一致,都是配置eureka信息:
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
启动项目后,查看eureka-server管理页面:
三、服务拉取和负载均衡
最后,我们要去eureka-server中拉取user-service服务的实例列表,并且实现负载均衡。不过这些动作不用我们去做,只需要添加一些注解即可。
在注册RestTemplate时,添加@LoadBalanced注解,实现负载均衡
四、修改远程接口路径
修改order-service服务中的cn.itcast.order.service包下的OrderService类中的queryOrderById方法。修改访问的url路径,用服务名代替ip、端口:
spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡。
以上是关于SpringCloud的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloudSpring Cloud Config 配置中心(二十)
SpringCloudSpring Cloud Config 客户端(二十一)
SpringCloudSpring Cloud Alibaba 及 Nacos介绍(二十六)
SpringCloudSpring Cloud Alibaba 之 Nacos配置中心(二十八)