官网地址: https://spring.io/projects/spring-cloud
1.认识微服务
单体架构:将业务的所有功能集中在一个项目开发,打包成一个包部署。
· 优点:架构简单、部署成本低
· 缺点:耦合度高
![](https://i-blog.csdnimg.cn/blog_migrate/8bc542d0e1487eaeb1e96c1dae9840c6.png)
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为独立项目开发,成为一个服务。
优点:降低服务耦合、有利于服务升级拓展
缺点:服务调用关系错综复杂
![](https://i-blog.csdnimg.cn/blog_migrate/405c5880fb5e5c10eab5cc76aa6993a8.png)
微服务:微服务是一种经过良好架构设计的分布式架构方案,微服务的架构特征如下:
单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责
自治:团队独立、技术独立、数据独立,独立部署和交付
面向服务:服务提供统一标准的接口,与语言和技术无关
隔离性强:服务调用做好隔离、容错、降级,避免出现级联问题
![](https://i-blog.csdnimg.cn/blog_migrate/ccde482851c3c92c7049b38c65c953aa.png)
微服务结构:
注册中心:维护微服务中每个节点信息和状态
配置中心:统一管理整个微服务群的配置
服务网关:用户通过服务网关请求路由,访问微服务
![](https://i-blog.csdnimg.cn/blog_migrate/b4af69d921d7d5e7c07497d4ccbb3954.png)
微服务技术对比
![](https://i-blog.csdnimg.cn/blog_migrate/510429a299f96346cbc2b7d178ae9524.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1eedd5b815253cde93a5eda89778124e.png)
上图上面两种基于springcloud、下面两种基于Dubbo
总结:
单体架构:简单方便,高度耦合,扩展性差,适合小型项目。例如:学生管理系统
分布式架构:松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝
微服务:一种良好的分布式架构方案
①优点:拆分粒度更小、服务更独立、耦合度更低
②缺点:架构非常复杂,运维、监控、部署难度提高
SpringCloud是微服务架构的一站式解决方案,集成了各种优秀微服务功能组件
2.服务拆分及远程调用
微服务拆分原则:
不同微服务,不要重复开发相同业务
微服务数据独立,不要访问其它微服务的数据库
微服务可以将自己的业务暴露为接口,供其它微服务调用
案例1.在java代码中发起ajax请求
问题描述:为了实现订单模块对用户模块的调用
![](https://i-blog.csdnimg.cn/blog_migrate/1b7dd3c4d1f8b85d9189690b267330a1.png)
step1.在order启动类中自动注入RestTemplate的Bean
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
/**
* 创建RestTemplete并注入spring容器
*/
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
step2.在order的业务层发起对user的请求。
核心代码: User user = restTemplate.getForObject(url, User.class);
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplete发起http请求,查询用户
String url = "http://localhost:8081/user/" + order.getId();
User user = restTemplate.getForObject(url, User.class);//User.class为返回值
//3.封装user
order.setUser(user);
// 4.返回
return order;
}
}
3.Eureka(有读法:有瑞卡)注册中心
概念1.服务提供者和消费者
服务调用关系:
• 服务提供者:暴露接口给其它微服务调用
• 服务消费者:调用其它微服务提供的接口
• 提供者与消费者角色其实是相对的
• 一个服务可以同时是服务提供者和服务消费者
概念2.Eureka
![](https://i-blog.csdnimg.cn/blog_migrate/84541d830cae769441450974b9bc532b.png)
问题1:order-service如何得知user-service实例地址?
获取地址信息的流程如下:
user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务端)。这个叫服务注册
eureka-server保存服务名称到服务实例地址列表的映射关系
order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
问题2:order-service如何从多个user-service实例中选择具体的实例?
order-service从实例列表中利用负载均衡算法选中一个实例地址
向该实例地址发起远程调用
问题3:order-service如何得知某个user-service实例是否依然健康,是不是已经宕机?
user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除
order-service拉取服务时,就能将故障实例排除了
总结:
![](https://i-blog.csdnimg.cn/blog_migrate/b3168afec6702d1e1de03a70f2ccdfe5.png)
案例1.搭建Eureka注册中心
step1.创建eureka-service模块(Meven模块)
![](https://i-blog.csdnimg.cn/blog_migrate/952709123e41f4ae32076ab07bee26ba.png)
step2.引入依赖
<dependency>
<!-- eureka服务端-->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
step3.编写启动类
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
step4.配置信息
eureka也要把自己注册进去,因此有eureka-client-......
server:
port: 10086 #服务端口
spring:
application:
name: eurekaserver
eureka:
client:
service-url: #eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka //目前单机,所以只有eureka自己
step5.启动时出现失败,可能是Meven版本问题,将meven中的版本配置删除即可。
![](https://i-blog.csdnimg.cn/blog_migrate/33c9d52fc00ba0ff4c8655a4ff8d84cf.png)
案例2.服务注册,将user-service注册到Eureka中
step1.引入客户端依赖(注意和Eureka引入的依赖不一样)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
step2.配置文件
添加服务名称和注册到eureka
spring:
application:
name: userservice
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
服务如图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/7f1d354550c659269d94c92f35dba8ac.png)
案例3 从Eureka中心拉取服务
在此为oreder-service拉取user-service(基于2.案例1)
step1.修改业务层url
将原来的localhost:8080改为user-service服务名称(这个名称和user-service yml文件中配置的一致)
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.利用RestTemplete发起http请求,查询用户
// String url = "http://localhost:8081/user/" + order.getUserId();
String url = "http://user-service/user/" + order.getUserId();
User user = restTemplate.getForObject(url, User.class);
//3.封装user
order.setUser(user);
// 4.返回
return order;
}
step2.添加负载均衡@LoadBalanced
@MapperScan("cn.itcast.order.mapper")
@SpringBootApplication
public class OrderApplication {
public static void main(String[] args) {
SpringApplication.run(OrderApplication.class, args);
}
/**
* 创建RestTemplete并注入spring容器
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
4.Ribbon负载均衡(读法:瑞本)(loadBalanced)
概念1.负载均衡的原理
![](https://i-blog.csdnimg.cn/blog_migrate/d11632c018de0239df6a2a0901f08609.png)
说白了,负载均衡就是将请求userservice转换成localhost:8081的过程。
![](https://i-blog.csdnimg.cn/blog_migrate/ab2a6bb1a91af01b5839cf8d39e82b26.png)
ClientHttpRequestInterceptor接口就是客户端http请求拦截器,该实现类也实现了intercept方法。
![](https://i-blog.csdnimg.cn/blog_migrate/5a3c6dada2e34a9ffa97abc6933be4c7.png)
调试
![](https://i-blog.csdnimg.cn/blog_migrate/a0ab2a6089f350eab53e894e042e9c06.png)
在loadBalancer中实现负载均衡,在深层代码就不展示了。
![](https://i-blog.csdnimg.cn/blog_migrate/ca91486819a7ecba46f4785f161ed7b7.png)
概念2.负载均衡策略
就比如user-service有多个服务器,Irule接口负责选择其中一个服务器。
![](https://i-blog.csdnimg.cn/blog_migrate/b3df768c386cf72ede41a778f691096f.png)
Ribbon默认为轮询的方式,那么,如何修改IRule实现负载均衡
![](https://i-blog.csdnimg.cn/blog_migrate/50fe5408b0f50d04636e836958277d33.png)
@Bean
public IRule randomRule(){
return new RandomRule();
}
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
两种方法的差距:当orderservice调用其他服务时,上面的适用于全部服务,而下面只适用于userservice服务。
概念3.饥饿加载
![](https://i-blog.csdnimg.cn/blog_migrate/1b0b707ff8286d0d3448092465a60731.png)
可以配置对多个服务进行饥饿加载
ribbon:
eager-load:
enabled: true #开启饥饿加载
clients: #指定饥饿加载的服务名称
- userservice
- eurekaservice
5.Nacos(奈考斯)注册中心
由于springcloud没有集成Nacos,因此需要自行下载并启动,首先如何打开Nacos,在bin目录下输入startup.cmd -m standalone命令即可。
![](https://i-blog.csdnimg.cn/blog_migrate/09235461d9da0d0d4441b52233b84049.png)
进入该网址,默认账号密码为nacos即可进入控制台
![](https://i-blog.csdnimg.cn/blog_migrate/7f3ee569b5a6ccbba65e04c5d6bf5af9.png)
![](https://i-blog.csdnimg.cn/blog_migrate/583288a6eef1cdc4306b3733c9773606.png)
案例1.Nacos配置
Nacos是SpringCloudAlibaba的组件,而SpringCloudAlibaba也遵循SpringCloud中定义的服务注册、服务发现规范。因此使用Nacos和使用Eureka对于微服务来说,并没有太大区别。
主要差异在于:
依赖不同
服务地址不同
1)引入依赖
step 1.在cloud-demo父工程的pom文件中的<dependencyManagement>中引入SpringCloudAlibaba的依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.6.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
step2.然后在user-service和order-service中的pom文件中引入nacos-discovery依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
注意:不要忘了注释掉eureka的依赖。
2)配置nacos地址
在user-service和order-service的application.yml中添加nacos地址:
spring:
cloud:
nacos:
server-addr: localhost:8848
注意:不要忘了注释掉eureka的地址
3)重启
重启微服务后,登录nacos管理页面,可以看到微服务信息:
![](https://i-blog.csdnimg.cn/blog_migrate/6f95f2a578bedcd28573bb3802a412fb.png)
查看详情
![](https://i-blog.csdnimg.cn/blog_migrate/b08c11e06ede0832b170f233de429d2d.png)
案例2.服务分级存储模型
一个服务例如userservice服务可以有若干个实例(即不同的服务器),对于不同的服务器进行划分
![](https://i-blog.csdnimg.cn/blog_migrate/b4fe527a47122761c7bc0a482662083a.png)
如何修改集群呢
spring.cloud.nacos.discovery.cluster-name
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: 1234
driver-class-name: com.mysql.jdbc.Driver
application:
name: userservice
cloud:
nacos:
server-addr: localhost:8848 #nacos服务地址
discovery:
cluster-name: HZ # 集群名称为HZ(杭州)
mybatis:
type-aliases-package: cn.itcast.user.pojo
configuration:
map-underscore-to-camel-case: true
logging:
level:
cn.itcast: debug
pattern:
dateformat: MM-dd HH:mm:ss:SSS
#eureka:
# client:
# service-url: #eureka的地址信息
# defaultZone: http://127.0.0.1:10086/eureka
可以看见两个user-service的集群不同。
![](https://i-blog.csdnimg.cn/blog_migrate/b763238b6116e81eda385b14fc5bf352.png)
总结:
![](https://i-blog.csdnimg.cn/blog_migrate/07774c025af756965de3f827d51e5f7a.png)
案例3.Nacos-NacosRule负载均衡
服务(order-service)在选择集群时不会优先选择同集群(都设置成HZ)的服务(user-service)。
当修改策略之后即刻优先使用同一集群了
userservice:
ribbon:
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则
如何修改服务器的访问权重?可以根据权重大小来分配对服务器的请求。
![](https://i-blog.csdnimg.cn/blog_migrate/3a46c9f917171ef234747c678041494f.png)
当修改权重为0时,就不会访问该服务器了,可以偷偷的给这个服务器升级更新,米哈游我劝你也这么干,不要一次更新好几个小时,太慢了。
案例 4. 环境隔离namespace
![](https://i-blog.csdnimg.cn/blog_migrate/4d5fddf8d9877017ac31563867513742.png)
修改服务器(orderservice)的命名空间。这样orderservice只能访问相同命名空间的userservice
spring:
cloud:
nacos:
server-addr: localhost:8848
discovery:
cluster-name: HZ
namespace: 5bd756a3-b9ec-496d-b2b3-d4755bf41217 #必须填写namespace的id
案例5.Eureka 和 Nacos对比
Nacos的服务实例分为两种l类型:
临时实例:如果实例宕机超过一定时间,会从服务列表剔除,默认的类型。
非临时实例:如果实例宕机,不会从服务列表剔除,也可以叫永久实例。
配置一个服务实例为永久实例:
spring:
cloud:
nacos:
discovery:
ephemeral: false # 设置为非临时实例
Nacos和Eureka整体结构类似,服务注册、服务拉取、心跳等待,但是也存在一些差异:
![](https://i-blog.csdnimg.cn/blog_migrate/bb53456d1f5742ebaa443435f58067bb.png)
Nacos与eureka的共同点
都支持服务注册和服务拉取
都支持服务提供者心跳方式做健康检测
Nacos与Eureka的区别
Nacos支持服务端主动检测提供者状态:临时实例采用心跳模式,非临时实例采用主动检测模式
临时实例心跳不正常会被剔除,非临时实例则不会被剔除
Nacos支持服务列表变更的消息推送模式,服务列表更新更及时
Nacos集群默认采用AP方式,当集群中存在非临时实例时,采用CP模式;Eureka采用AP方式