文章目录
SpringCloud介绍
百度百科介绍:Spring Cloud
是一系列框架的有序集合。它利用Spring Boot
的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot
的开发风格做到一键启动和部署。Spring Cloud
并没有重复制造轮子,它只是将各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot
风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包。
简单来说:Spring Cloud
是目前比较完整的微服务解决方案,它基于SpringBoot
框架、它有非常多的模块,如注册/配置中心、断路器、负载均衡等,它是集成其他优秀框架组成的一个微服务框架。
Spring Cloud
是微服务框架,那么我要了解微服务架构的几个问题。
什么是集群?
多台服务器部署相同的一个项目(Application
)构成的一个集群,主要作用是:通过负载均衡分担单台服务器压力。
什么是分布式?
不同的模块(Module
)部署在不同的服务器,主要作用是分布式解决网站带来的高并发问题。
什么是RPC?
RPC
的全称是 Remote Procedure Call
远程过程调用:一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
简单来说:RPC
就是从一台机器(Client
)上通过参数传递的方式调用另一台机器(Server
)上的一个函数或方法(可以统称为Service
)并得到返回结果。
了解更多:RPC
隐藏了底层的通讯细节,并不需要你直接处理Socket
或HTTP
通讯,RPC
的响应模型:客户端(Client
)发起请求(Request
),服务端(Server
)返回响应(Response
),类似于HTTP
的工作模式。RPC
的使用模式像调用本地方法(函数)一样去调用远程的方法(函数)。
常见的RPC
框架:RMI
(Java远程方法协议)、GRPC
(谷歌)、Thrift
(Apache)、Spring Cloud
等等
什么是RestFul?
RestFul
是一种网络应用程序的设计风格和开发方式,基于HTTP,可以使用XML格式定义或JSON
格式定义。RestFul
适用于移动互联网厂商作为业务使能接口的场景,实现第三方OTT调用移动网络资源的功能,动作类型为新增、变更、删除所调用资源。
简单来说:RestFul
是一种架构风格,提供了设计原则和约束条件,而不是架构。而满足这些约束条件和原则的应用程序或设计就是 RestFul
架构或服务。
什么是SOAP?
SOAP
(Simple Object Access Protocol
)简单对象访问协议是交换数据的一种协议规范,是一种轻量的、简单的、基于XML
(标准通用标记语言下的一个子集)的协议,它被设计成在WEB
上交换结构化的和固化的信息。
简单来说,SOAP
是一种数据交换协议规范,是轻量级且简单并基于XML的协议规范。(SOAP
协议和HTTP
协议相似,都是底层的通信协议,只是请求包的格式不同,SOAP
包是XML
格式的,HTTP
包是(请求头、消息头、内容这些…))
SOAP
的消息是基于xml
并封装成了符合http
协议,因此它符合任何路由器、 防火墙或代理服务器的要求(相当于Java
的跨平台)。
SOAP
可以使用任何语言来完成,只要发送正确的SOAP
请求即可,基于SOAP
的服务可以在任何平台无需修改即可正常使用。
WebService
(soa):http
+ xml
重量级的
RestFul
(微服务):http
+ result
+ json
轻量级的
什么是SOA?(面向服务的架构)
面向服务的架构(SOA
)是一个组件模型,它将应用程序的不同功能单元(称为服务)进行拆分,并通过这些服务之间定义良好的接口和协议联系起来。接口是采用中立的方式进行定义的,它应该独立于实现服务的硬件平台、操作系统和编程语言。这使得构件在各种各样的系统中的服务可以以一种统一和通用的方式进行交互。
简单来说,SOA
就是将项目基于服务进行拆分,通过接口和定义好的协议规范通讯,让每个服务成为独立的组件的一种设计架构。
SOA
架构最大的好处和作用就是使得维护变得更简单、降低了整体的风险、更加的灵活,能够通过服务的组合实现各种业务流程。
SOA
把工程拆分成服务层、表现层两个工程。服务层中包含业务逻辑,只需要对外提供服务即可。表现层只需要处理和页面的交互,业务逻辑都是调用服务层的服务来实现。
什么是微服务?
微服务是一种架构设计概念,目的是使各服务间隔离(分布式也是隔离),自治(分布式依赖整体组合)其它特性 (单一职责、边界、异步通信、独立部署) 是分布式概念的严格执行。
应用的架构演变过程如下图所示:
省略SOA项目架构例图
图画的不太好( QAQ )
微服务架构优点: 解耦、服务划分粒度细,可维护性高、产品迭代周期短、项目互不影响等等…
微服务架构缺点: 开发效率低、微服务治理复杂、研发技术复杂,团队人数有一定的要求、运营成本高等等…
什么是微服务架构?
简单来说:微服务架构就是将一个项目(工程)进行按模块拆分,各模块的通讯使用RPC
(远程调用)技术。
注册中心(Eureka)
Eureka
分为:Eureka
客户端(Client
)、Eureka
服务器端(Server
)
Eureka Server (注册中心)
需要引入Eureka
客户端依赖然后在application.yml
配置Eureka
客户端的信息,再创建启动类添加相应的注解,最后启动,访问http://localhost:8761
即可看到注册进来的Client
端的信息有那些。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
Eureka Client (会员服务)
同样需要先引入依赖、配置application.yml
文件,创建启动类添加注解。
eureka:
client:
serviceUrl:
# 将自己注册到Eureka Server端,下面是Server端的注册地址(即Server端yml的地址)
defaultZone: http://ip:port/eureka/
server:
port: 8762
# 本工程的应用名称
spring:
application:
name: user-service
@SpringBootApplication
@EnableEurekaClient
@RestController
public class UserServiceApplication {
public static void main(String[] args) {
SpringApplication.run(UserServiceApplication.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/hello")
public String hello(@RequestParam String name){
return "Hello , port:" + port + "welcome " + name+ ", come here ~";
}
}
按照Client
部分的指示再创建一个程序作为积分系统启动,就和图上的关系一样了。
先启动好Server
,然后启动Client
,最后浏览器访问Server
端的Eureka
信息页就能看到我们的Client
已经注册到Eureka Server
啦。(如果你觉得访问的时候没有密码不安全,可以设置密码的哦 →《SpringCloud中文文档》)
但是在实际项目中,Eureka
一般都是集群,避免宕机带来的影响。
声明式调用(Feign)
在使用SpringCloud
框架时,两个服务之间是如何调用的呢?
在以前传统的项目,大多数都是使用HttpClient
进行调用,而在SpringCloud
中,我们可以使用的方式有两种:Restemplate
方式、Feign
声明式调用。
以下图为例,如果其中一个程序想要调用另一个程序的某个接口。
Restemplate 方式
Restemplate
是Eureka
自带的一个远程调用工具。需要在消费服务的工程的启动类装配Restemplate
对象。
// 积分系统启动类
@SpringBootApplication
@EnableEurekaClient
public class ScoreServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ScoreServiceApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
@Service
public class ScoreServiceImpl implements ScoreService {
@Autowired
private RestTemplate restTemplate;
@Overrride
public String demoMethod(String name) {
// user-service 指的是被调用的服务(会员系统)的application.yml中的程序名
// hello 指的是被调用的服务(会员系统)Controller中的“/hello”的接口
// 这句代码的意思是调用会员服务的hello接口,其返回值是String
return restTemplate.getForObject("http://user-service/hello?name=" + name, String.class);
}
}
@RestController
public class DemoControler {
@Autowired
private ScoreServiceImpl scoreServiceImpl;
@RequestMapping(value = "/testRestTemplate")
public String test(@RequestParam String name){
return scoreServiceImpl.demoMethod(name);
}
}
之后我们打开浏览器访问积分系统(http://localhost:port/hello?name=darian``)的"/testRestTemplate
"请求,其实就是去调用会员系统的“/hello
”请求获取到结果啦。结果:“Hello , port:8762 ,welcome darian come here ~”。
如果说会员系统做了集群(Ribbon
负载均衡,下面有讲),有多个会员系统实例,我们就需要在刚才的积分系统引入依赖并在启动类的RestTemplate
加上注解@LoadBalanced
。
// 积分系统启动类
@SpringBootApplication
@EnableEurekaClient
public class ScoreServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ScoreServiceApplication.class, args);
}
@Bean
@LoadBalanced // 表示支持Ribbon负载均衡,如果你没有集成就无需该注解
RestTemplate restTemplate() {
return new RestTemplate();
}
}
这样,即使你会员系统做了(集群)Ribbon
负载均衡,这样积分系统调用会员系统的"/hello
"接口就会以轮询的方式更换实例了。
Feign声明式调用
Feign
是一个声明式的伪Http
客户端,它使得写Http
客户端变得更简单。使用Feign
,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign
注解。Feign
默认集成了Ribbon
,并和Eureka
结合,默认实现了负载均衡(Ribbon
)的效果。
简而言之:Feign
采用的是基于接口的注解、Feign
整合了Ribbon
基于上面的实例,我们新增一个订单系统,如下图
1.订单系统使用Feign,需要引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2.定义Feign的核心(接口)
Feign是声明式调用,是基于接口的,所以核心是基于接口调用。
@FeignClient(value = "user-service") // value=被调用的服务的application name
public interface FeignDemo {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String demoFeignMethod(@RequestParam(value = "name") String name);
}
@RestController
public class OrderController {
@Autowired
private FeignDemo feignDemo;
@RequestMapping(value = "/testFeign")
public String testFeign(@RequestParam String name){
return feignDemo.demoFeignMethod(name);
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
启动订单系统,浏览器访问“/testFeign
”(http://localhost:port/testFeign?name=fdarian
),Feign底层就会帮我们去调用会员系统的接口了!如果会员系统做了集群(Ribbon负载均衡),也没有关系,因为Feign
默认集成了Ribbon
。
负载均衡器(Ribbon)
上面示例讲到会员系统进行了集群(负载均衡Ribbon
),我们来了解下
Ribbon
是一个负载均衡客户端,可以很好的控制http
和tcp
的一些行为。Feign
默认集成了Ribbon
。
需要集群(负载均衡Ribbon
)的工程需引入相应依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
断路器(Hystrix)
基于上面的实例我们继续了解Hystrix
,如果在服务调用过程中,(服务提供者)被调用的服务宕机了,那么我们的(服务消费者)服务调用者就会迟迟得不到回应。
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图举例:
如图所示,如果订单系统一直没有回应,那么我们的服务消费者就一直会等待,解决这样的问题我们就需要使用到断路器Hystrix。
基于上面使用Feign的订单系统来集成Hystrix断路器做示例:
首先引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
远程调用有两种方式(Restemplate(Ribbon)
和Feign
),Hystrix
也有两种方式,一种是针对Restemplate
使用断路器、一种是针对Feign
方式使用断路器。
Restemplate(+Ribbon)方式使用断路器:
(基于上面的积分系统的Service)在对应的Service上添加本地方法,并在远程调用方法上加注解@HystrixCommand(fallbackMethod = "本地方法")
@Service
public class ScoreServiceImpl implements ScoreService {
@Autowired
private RestTemplate restTemplate;
@Overrride
@HystrixCommand(fallbackMethod = "restTemplateRibbonhystrix")
public String demoMethod(String name) {
// user-service 指的是被调用的服务(会员系统)的application.yml中的程序名
// hello 指的是被调用的服务(会员系统)Controller中的“/hello”的接口
// 这句代码的意思是调用会员服务的hello接口,其返回值是String
return restTemplate.getForObject("http://user-service/hello?name=" + name, String.class);
}
public String restTemplateRibbonhystrix() {
return "hystrix提示:调用user-service接口失败";
}
}
Feign方式使用熔断器:
Feign方式使用熔断器非常的方便,只需要实现Feign接口即可,并对Feign
接口指定fallback
属性。启动类添加相应注解即可。
基于上面的Feign(订单系统示例):
@FeignClient(value = "user-service",fallback=FeignHystrixImpl .class) // value=被调用的服务的application name
public interface FeignDemo {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String demoFeignMethod(@RequestParam(value = "name") String name);
}
@Component
public class FeignHystrixImpl implements FeignDemo {
public String demoFeignMethod(String name) {
return "Feign Hystix提示:远程调用接口失败,已被Hystrix熔断!";
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@EnableHystrix
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
}
Hystrix Dashboard (断路器:Hystrix 仪表盘)也可以去了解一下。
网关(Zuul)
使用Zuul搭建API网关
什么是API 网关?
在Spring Cloud微服务系统中,一种常见的负载均衡方式,客户端的请求首先经过负载均衡(Ngnix),再到达服务网关(zuul集群),然后再到具体的服务。
什么是Zuul网关?
路由器和过滤器:Zuul
路由在微服务体系结构的一个组成部分。例如,/可以映射到您的Web应用程序,/api/users映射到用户服务,并将/api/shop映射到商店服务。Zuul是Netflix的基于JVM的路由器和服务器端负载均衡器。
创建工程,搭建Zuul,引入核心依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
application.yml配置
#省略eureka配置
server:
port: 8769
spring:
application:
name: darian-zuul
zuul:
routes:
user-zuul:
path: /user-zuul/**
service-id: user-service
order-zuul:
path: /order-zuul:/**
service-id: order-service
# 省略其他
以/user-zuul/
开头的请求都转发给user-service
服务(会员系统)、以/order-zuul/
开头的请求都转发给order-service
服务(订单系统);
运行我们的会员系统和订单系统,访问http://localhost:8769/order-zuul/hello?name=darian
就会路由到我们的订单服务的("/hello
")接口。
Zuul不仅能作为网关分发客户端请求,还能做过滤(认证)验证Token等等操作
下面是用于过滤Token的实例:
@Component
public class MyFilter extends ZuulFilter{
private static Logger log = LoggerFactory.getLogger(MyFilter.class);
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 0;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext context= RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
log.info("{} ==> {}", request.getMethod(), request.getRequestURL().toString());
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
context.setSendZuulResponse(false);
context.setResponseStatusCode(401);
try {
context.getResponse().getWriter().write("unfind token!");
}catch (Exception e){}
return null;
}
log.info("No problem!");
return null;
}
}
filterType:返回一个字符串代表过滤器的类型,在zuul中定义了四种不同生命周期的过滤器类型
pre
:路由之前
routing
:路由之时
post
: 路由之后
error
:发送错误调用
filterOrder
:过滤的顺序
shouldFilter
:这里可以写逻辑判断,是否要过滤,本文true,永远过滤。
run
:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问。
分布式配置中心(Config)
分布式配置中心(Spring Cloud Config
),在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件spring cloud config ,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。
在spring cloud config 组件中,分两个角色,一是config server
(服务器端),二是config client
(客户端)。
Config Server
引入依赖然后在启动类加上@EnableConfigServer
,配置yml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
@SpringBootApplication
@EnableConfigServer
public class ConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigServerApplication.class, args);
}
}
spring:
application:
name: darian-config
cloud:
config:
server:
git:
#配置git仓库地址
uri: https://gitee.com/xxx/xxx.git
#配置仓库路径
search-paths: xxx_config
#用户名和密码;仓库没设密码,要设的话,仓库设密码后记得加上密码配置
username: xxx
password: xxx
#配置仓库的分支
label: maste
搭建好Server端,你就可以使用浏览器访问获取配置信息(根据你的git仓库路径去访问你具体的配置文件)。
Config Client
我们可以把上面已有的订单系统和积分系统、会员系统都作为我们的ConfigClient
。
首先引入依赖,再修改yml文件
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
将配置文件application.yml更改为bootstrap.yml并添加配置
server:
port: 8888
spring:
application:
name: darian-client
cloud:
#读取文件的版本环境 即文件对应文件是什么环境的 astrology-client-dev.yml 结尾-dev 表示是dev环境
config:
profile: prod
label: master
discovery:
#此处可以使用uri属性替换该属性,uri是死的。推荐使用config Server端的程序名
service-id: darian-config
#开启读取权限
enabled: true
username:
password:
@SpringBootApplication
@RestController
public class ConfigClientApplication {
public static void main(String[] args) {
SpringApplication.run(ConfigClientApplication.class, args);
}
@Value("${name}")
String name;
@RequestMapping(value = "/testConfig")
public String testConfig() {
return name;
}
}
打开浏览器访问http://localhost:8888/testConfig
测试下是否从Server端获取到配置
实际中,如果使用了分布式配置中心也应该是高可用的,如下图。
Spring Cloud总结
Spring Cloud就是一个RPC微服务框架,提供注册中心(注册服务与发现服务)、断路器(熔断器(Hystrix
))、网关(Zuul
)、负载均衡(Ribbon
)Feign(声明式远程调用技术:底层http协议)、配置中心、消息总线等组件,是一个搭建分布式系统比较全面的微服务框架。
tips:部分引用了百度百科,图片引用于网络搜索的图片,以及《SpringCloud中文文档》