此文是跟着某站张虎翼老师的SpringCloud视频写的笔记,真心推荐各位看一下微服务的这个视频链接: 张虎翼老师视频
1 微服务介绍
1.1 单体架构和分布式架构
-
单体架构:简单方便,高度耦合,扩展性差,适合小型项目。例如:学生管理系统
-
分布式架构:松耦合,扩展性好,但架构复杂,难度大。适合大型互联网项目,例如:京东、淘宝
-
微服务:一种良好的分布式架构方案
①优点:拆分粒度更小、服务更独立、耦合度更低
②缺点:架构非常复杂,运维、监控、部署难度提高
-
SpringCloud是微服务架构的一站式解决方案,集成了各种优秀微服务功能组件
1.2 微服务
微服务需要根据业务模块进行拆分,做到单一职责,不能重复开发相同功能
微服务可以将业务暴露为接口,供其他微服务使用
不同微服务应该有自己独立的数据库
1.3 微服务调用请求方式
微服务调用请求方式:
- 基于RestTemplate发起的http请求实现远程调用
- Http请求做远程调用是与语言无关的调用,只要知道对方的ip、端口、接口路径、请求参数即可
2 Eureka服务注册中心
2.1 问题的提出
在微服务中,每个服务调用其他的服务是怎么做到的呢?
动态调用其他服务的端口和ip地址怎么获取呢?
2.2 Eureka的实现
利用SpringCloud中的注册中心来解决,其中最广为人知的注册中心就是Eureka
Q1:当一个订单服务想要调用用户服务的时候:
- user-service服务实例启动后,将自己的信息注册到eureka-server(Eureka服务端)。这个叫服务注册
- eureka-server保存服务名称到服务实例地址列表的映射关系
- order-service根据服务名称,拉取实例地址列表。这个叫服务发现或服务拉取
Q2:订单服务怎么从多个用户服务中选择具体实例:
- order-service从实例列表中利用负载均衡算法选中一个实例地址
- 向该实例地址发起远程调用
Q3:用户服务如何得知某个用户服务实例是否依然健康,是不是已经宕机:
- user-service会每隔一段时间(默认30秒)向eureka-server发起请求,报告自己状态,称为心跳
- 当超过一定时间没有发送心跳时,eureka-server会认为微服务实例故障,将该实例从服务列表中剔除
- order-service拉取服务时,就能将故障实例排除了
一个微服务,既可以是服务提供者,又可以是服务消费者,因此eureka将服务注册、服务发现等功能统一封装到了eureka-client端
3 搭建Eureka
3.1 搭建EurekaServer
3.1.1创建一个maven项目
在IDEA中创建一个maven项目,用于搭建EurekaServer
3.1.2 导入依赖
版本号已经指定好了,可以去父项目中找到netflix依赖,里面有eureka的依赖版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
3.1.3 编写配置文件
在新的module中创建application.yml文件
server:
port: 10086
spring:
application:
name: eureka-server
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
3.1.4 编写Eureka启动类
@EnableEurekaServer 开始EurekaServer
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);
}
}
3.1.5 测试
这样我们的Eureka的服务器端创建好了,访问我们的地址http://127.0.0.1:10086可以看到Eureka注册中心的页面
3.2 服务注册EurekaClient
将订单和用户都注册到Eureka服务实例列表中去
3.2.1 导入依赖
在订单和用户的pom文件中引入依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
3.2.2 编写配置文件
在订单和用户的application.yml中添加上编写配置
订单的
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_order?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application:
name: orderservice
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
用户的
spring:
datasource:
url: jdbc:mysql://localhost:3306/cloud_user?useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
application:
name: userservice # eureka的服务名字
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
3.2.3 启动多个用户服务
- 右键Services中的项目,找到Copy Configuration
- 取个新名字
- 端口号改一下
-Dserver.port=xxxx -D表示参数server.port表示新的服务启动的端口号
3.2.4 测试
都启动起来,然后到Eureka的注册中心页面可以看到所有Eureka的客户端服务在线。
3.3 服务发现
将order-service的逻辑修改:向eureka-server拉取user-service的信息,实现服务发现
3.3.1 修改订单中url地址
修改访问的url路径,用服务名代替ip、端口
因为我们在user的application.yml中已经写好了服务名字:userserevice,所以我们在order.service中作以下修改:
package cn.itcast.order.service;
import cn.itcast.order.mapper.OrderMapper;
import cn.itcast.order.pojo.Order;
import cn.itcast.order.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private RestTemplate restTemplate;
public Order queryOrderById(Long orderId) {
// 1.查询订单
Order order = orderMapper.findById(orderId);
// 2.http请求
String url = "http://userservice/user/" + order.getUserId();
// 3.发送远程http请求
User user = restTemplate.getForObject(url, User.class);
// 4.封装请求返回的user信息
order.setUser(user);
// 5.返回
return order;
}
}
3.3.2 添加负载均衡注解
然后在orderApplication中的RestTemplate中
添加注解@LoadBalanced,实现负载均衡
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
3.3.3 测试
重新运行order测试,然后进行两次订单的请求
spring会自动帮助我们从eureka-server端,根据userservice这个服务名称,获取实例列表,而后完成负载均衡
可以看到在不同的user服务中进行,实现了负载均衡
4 Ribbon负载均衡
4.1 原理
SpringCloudRibbon的底层采用了一个拦截器,拦截了RestTemplate发出的请求,对地址做了修改。用一幅图来总结一下:
基本流程如下:
- 拦截我们的RestTemplate请求http://userservice/user/1
- RibbonLoadBalancerClient会从请求url中获取服务名称,也就是user-service
- DynamicServerListLoadBalancer根据user-service到eureka拉取服务列表
- eureka返回列表,localhost:8081、localhost:8082
- IRule利用内置负载均衡规则,从列表中选择一个,例如localhost:8081
- RibbonLoadBalancerClient修改请求地址,用localhost:8081替代userservice,得到http://localhost:8081/user/1,发起真实请求
4.2 常见负载均衡策略
不同规则的含义如下:
内置负载均衡规则类 | 规则描述 |
---|---|
RoundRobinRule | 简单轮询服务列表来选择服务器。它是Ribbon默认的负载均衡规则。 |
AvailabilityFilteringRule | 对以下两种服务器进行忽略: (1)在默认情况下,这台服务器如果3次连接失败,这台服务器就会被设置为“短路”状态。短路状态将持续30秒,如果再次连接失败,短路的持续时间就会几何级地增加。 (2)并发数过高的服务器。如果一个服务器的并发连接数过高,配置了AvailabilityFilteringRule规则的客户端也会将其忽略。并发连接数的上限,可以由客户端的..ActiveConnectionsLimit属性进行配置。 |
WeightedResponseTimeRule | 为每一个服务器赋予一个权重值。服务器响应时间越长,这个服务器的权重就越小。这个规则会随机选择服务器,这个权重值会影响服务器的选择。 |
ZoneAvoidanceRule | 以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器进行分类,这个Zone可以理解为一个机房、一个机架等。而后再对Zone内的多个服务做轮询。 |
BestAvailableRule | 忽略那些短路的服务器,并选择并发数较低的服务器。 |
RandomRule | 随机选择一个可用的服务器。 |
RetryRule | 重试机制的选择逻辑 |
默认的实现就是ZoneAvoidanceRule,是一种轮询方案
4.3 自定义负载均衡策略
通过定义IRule实现可以修改负载均衡规则,有两种方式:
- 代码方式:在order-service中的OrderApplication类中,定义一个新的IRule:
@Bean
public IRule randomRule(){
return new RandomRule();
}
- 配置文件方式:在order-service的application.yml文件中,添加新的配置也可以修改规则:
userservice: # 给某个微服务配置负载均衡规则,这里是userservice服务
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 负载均衡规则
注意,一般用默认的负载均衡规则,不做修改。
4.4.饥饿加载
Ribbon默认是采用懒加载,即第一次访问时才会去创建LoadBalanceClient,请求时间会很长。
而饥饿加载则会在项目启动时创建,降低第一次访问的耗时,通过下面配置开启饥饿加载:
ribbon:
eager-load:
enabled: true
clients: userservice
有一起学习微服务的小伙伴可以一起交流~