单词
discoveryClient-发现客户端、loadBalance-负载均衡、Rule-规则、current-当前的、fallback-回退(降级)、break-中断(熔断)、flowlimit-限流、circuit-电路、dashboard-仪表盘、refresh-刷新、actuator-执行器
SpringCloud是一系列框架的有序集合,是一揽子框架的解决方案
(约定 > 配置 > 编码)
微服务架构
微服务架构是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务与服务间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建
分布式架构
或者说是对分布式架构的理解
- 服务注册与发现
- 服务调用
- 服务熔断
- 负载均衡
- 服务降级
- 服务消息队列
- 配置中心管理
- 服务网关
- 服务监控
- 全链路追踪
- 自动化构建部署
- 服务定时任务调度操作
- …
一些架构需要学习掌握的技术
开始搭建项目
- dependencyManagement里只是声明依赖,并不实现引入,因此子项目需要显示的声明需要用的依赖。
<!--dependencyManagement: 子模块继承之后,提供作用:锁定版本+子module不用写groupId和version -->
<dependencyManagement>
<dependencies>
<!--spring boot 2.2.2-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.2.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
......
这样做的好处就是:如果有多个子项目都引用同一样依赖,则可以避免在每个使用的子项目里都声明一个版本号,这样当想升级或切换到另一个版本时,只需要在顶层父容器里更新,而不需要一个一个子项目的修改﹔另外如果某个子项目需要另外的一个版本,只需要声明version就可。
跳过maven的test单元测试,项目可以跑的更快
一个微服务模块搭建的基本流程
1、建module
2、改POM
3、写YML
4、主启动
5、业务类
RestTemplate
RestTemplate提供了多种便捷访问远程Http服务的方法,
是一种简单便捷的访问restful服务模板类,是Spring提供的用于访问Rest服务的客户端模板工具集
1、创建一个父类总工程
pom.xml
<dependencyManagement>: 子模块继承之后,提供作用:锁定版本+子module不用写groupId和version
</dependencyManagement>
2、编写服务的生产者
1、建module
cloud_provider_payment8001
2、改POM
需要的依赖
<artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-actuator</artifactId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<artifactId>druid-spring-boot-starter</artifactId>
<artifactId>mysql-connector-java</artifactId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<artifactId>spring-boot-devtools</artifactId>
<artifactId>lombok</artifactId>
<artifactId>spring-boot-starter-test</artifactId>
3、写YML
改端口号8001
,mysql jdbc 驱动 数据源 ,mybatis扫描
4、主启动
编写主启动类
5、业务类
5.1、编写Payment.java 的bean
5.2、编写CommontResult.java 的bean 面相前端开放的一个bean,前端只需要调用这个bean的结果即可
5.3、Dao、DaoImpl、mapper.xml
5.4、service、serviceImpl
5.5、controller
消费者调用该写入数据库的业务时,会给生产者传回一个json字符串,造成写入数据库错误,写入一个null值,所以需要在形参中加上==@RequestBody== 注解 将传回来的字符串转换成对象
@Autowired
private PaymentService paymentService;
@PostMapping(value="/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
log.info("=====插入结果====="+payment);
if(result > 0){
return new CommonResult(200,"插入成功",payment);
}
return new CommonResult(444,"插入失败",payment);
}
3、编写服务的消费者
1、建module
cloud_consumer_order80
2、改POM
<artifactId>spring-boot-starter-web</artifactId>
<artifactId>spring-boot-starter-actuator</artifactId>
<artifactId>spring-boot-devtools</artifactId>
<artifactId>lombok</artifactId>
<artifactId>spring-boot-starter-test</artifactId>
3、写YML
改端口号80
默认不用写就是80端口,所以消费者只用写ip地址即可访问服务
4、主启动
主启动类
5、业务类
5.1、编写Payment.java 的bean
5.2、 编写CommonResult.java 的 bean
5.3 编写config配置类 ApplicationContextConfig.java
// 配置类
@Configuration
public class ApplicationContextConfig {
// 可以复用的restTemplate方法
@Bean
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
5.4、编写controller层
使用到了RestTemplate , 写操作使用postForObject() , 读操作使用getForObject()
@RestController
@Slf4j
public class OrderController {
@Resource
private RestTemplate restTemplate;
public static final String PAYMENT_URL = "http://localhost:8001";
@GetMapping("/consumer/payment/create")
public CommonResult<Payment> create(Payment payment) {
// 用于访问Rest服务的客户端模板工具集 ( URL访问地址,传参类型,返回类型 )
return restTemplate.postForObject(PAYMENT_URL + "/payment/create", payment, CommonResult.class);
}
@GetMapping("/consumer/payment/getPayment/{id}")
public CommonResult<Payment> getPayment(@PathVariable("id") long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/getPayment/"+id,CommonResult.class);
}
}
创建一个抽取公用API的模块
1、pom.xml
新加入了一个hutool工具包
<artifactId>hutool-all</artifactId>
2、将entities实体类包抽取出来
3、使用maven的生命周期的方法
3.1、clean清空
3.2、install安装打包一下
4、在需要使用到该实体类的模块中引入依赖
<dependency><!-- 引入自己定义的api通用包,可以使用Payment支付Entity -->
<groupId>com.study</groupId>
<artifactId>cloud_api_commons</artifactId>
<version>${project.version}</version>
</dependency>
Eureka
( 已停止更新 )
Eureka包含两个组件 : Eureka Server 和 Eureka Client
Eureka Server提供服务注册服务
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有 可用服务节点的信息,服务节点的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个lava客户端,用于简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round- robin)负载算 法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如 果Eureka Server在多个心 跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这 个服务节点移除(默认90秒)
服务注册中心
需要先启动服务注册中心,再服务的提供者。
创建服务注册中心模块
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
服务注册中心的yaml
server:
port: 7001
eureka:
instance:
hostname: localhost # eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false 表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
defaultZone: http://${
eureka.instance.hostname}:${
server.port}/eureka/
服务注册中心的主启动类
@SpringBootApplication
@EnableEurekaServer // 表示自己是Eureka服务注册中心
public class EurekaMain7001 {
...}
服务提供者
依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
服务提供者的yaml
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
# 向哪个注册中心注册服务
defaultZone: http://localhost:7001/eureka
服务提供者的主启动类
@SpringBootApplication
@EnableEurekaClient // 服务提供者,让注册中心发现、并扫描到该服务
public class PaymentMainAPP {
...}
集群版Eureka
1、新建一个服务的注册中心 eureka7002
2、在windows的hosts文件中加上本机的ip地址,服务注册中心的服务名( 相当于域名 )
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
3、修改服务注册中心的yaml文件
3.1、修改eureka服务名 eureka7001.com
在浏览器地址栏中输入,可以映射到127.0.0.1
(127.0.01
localhost
192.168.177.1
(127.0.0.1 = 本机ip地址 ,localhost = 本机的域名,192.168.177.1 = 本机网卡对外的ip))
3.2、修改 defaultZone :ip地址:port/eureka/
,之前是单机版本,只需要把自己的地址注册上即可,现在是集群,所以要写上其他服务注册中心的地址
多台服务注册中心需要 相互注册,互相关联,相互守望 , 如果有多台可以用 ==,==逗号隔开
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com # eureka服务端的实例名称
client:
#false表示不向注册中心注册自己
register-with-eureka: false
#false 表示自己段就是注册中心,我的职责就是我维护服务实例,并不需要去检索服务
fetch-registry: false
service-url:
#设置与Eureka Server交互的地址查询服务和注册服务都需要依赖这个地址
# defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#使用集群的方式
defaultZone: http://eureka7002.com:7002/eureka/
Client向集群注册中心注册服务
修改yaml
文件
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
# 向哪个注册中心注册服务
# defaultZone: http://localhost:7001/eureka
#向集群注册中心注册服务
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
Client端集群(提供者)
不仅要做服务注册中心的集群,还要做服务提供者的集群,这样可用性更高
1、再创建一个服务提供者的模块 cloud_provider_payment8002
1.1、改 POM
1.2、写YAML
yaml的端口号改成8002
,(多个服务提供者的服务名需要保持一致,让消费者可以通过一个服务名找到多个提供者)
1.3 、主启动类
1.4、业务类
可选: 可以在controller层加上 以便以端口号识别的参数
@Value("${server.port}") // 从配置文件中读取端口号
private String serverPort ;
....
return new CommonResult(200,"查询成功 + " + serverPort,paymentById);
2、在消费者 (80
端口)修改controller层的URL访问地址
// public static final String PAYMENT_URL = "http://localhost:8001";
// URL访问地址从服务提供者的ip改成服务提供者集群对外暴露的服务名(需要在RestTemplate的配置上加上@LoadBalanced 在可以生效)
public static final String PAYMENT_URL = "http://CLOUD-PAYMENT";
2.1、给RestTemplate加上注解@LoadBalance
赋予它负载均衡的能力 (默认轮询的方式来负载均衡)
// 配置类
@Configuration
public class ApplicationContextConfig {
// 可以复用的restTemplate方法
@Bean
@LoadBalanced // 赋予RestTemplate 负载均衡的能力
public RestTemplate getRestTemplate(){
return new RestTemplate();}}
完善微服务的配置信息(可选)
前台监控页面查看时可以显示更详细的信息
instance:
instance-id: payment8001 #显示服务的主机名
prefer-ip-address: true #访问路径可以显示服务的ip地址
服务发现Discover
使用参数DisCoverClient
@Resource//DiscoverClient服务发现
private DiscoveryClient discoveryClient;
写方法获取实例信息
@GetMapping(value = "/payment/discover")
public Object discover(){
// Eureka注册的微服务有哪些,服务列表清单
List<String> services = discoveryClient.getServices();
for (String element:services) {
log.info("services--->"+element);
}
------------------结果-----------------------------
services--->cloud-order-com.study.cloud.service
services--->cloud-payment-service
------------------------------------------------
//根据微服务的名称,获取微服务具体的信息
List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance instance : instances) {
// 可以使用根据服务名实例获取到的内容来get该服务名的详细的信息
log.info("instance--->"+instance.getInstanceId()+"\t"+instance.getHost()+"\t"+instance.getPort()+"\t"+instance.getUri());
}
return this.discoveryClient;
}
----