微服务是一种将各个模块拆分开独立运行以提高系统整体效率的技术,其主要特征为:
国内的知名微服务框架有SpringCloud和Dubbo(阿里巴巴)
用户访问服务网关,服务网关请求路由负载均衡(服务集群),服务集群在注册中心中拉取注册和服务信息,在配置中心中拉取配置信息。
Dubbo | SpringCloud | SpringCloudAlibaba | |
---|---|---|---|
注册中心 | zookeeper、Redis | Eureka、Consul | Nacos、Eureka |
服务远程调用 | Dubbo协议 | Feign(http协议) | Dubbo、Feign |
配置中心 | 无 | SpringCloudConfig | SpringCloudConfig、Nacos |
服务网关 | 无 | SpringCloudGateway、zuul | SpringCloudGateWay、zuul |
服务监控和保护 | dubbo-admin、功能弱 | Hystrix | Sentinel |
服务注册发现:
Eureka、Nacos、Consul
远程服务调用:
OpenFeign、Dubbo
服务链路监控:
Zipkin、Sleuth
统一配置管理:
SpringCloudConfig、Nacos
统一网关路由:
SpringCloudGateWay、Zuul
流控、降级、保护:
Hystix、Sentinel
注意:SpringCloud与SpringBoot有较为严格的匹配关系、一定要注意兼容性
由于业务有可能涉及到多个模块,但同时由于微服务的限制我们不能重复开发功能,所以有了服务远程调用的概念,在业务实现时,一个模块调用另外一个模块就成为服务远程调用
使用到RestTemplate,RestTemplate是Spring的一个功能,用来发送Http请求
创建RestTemplate并注入Spring容器,在SpringBoot中,这件事只能在配置类中完成,这里在启动类中做这件事:
/**
* 创建RestTemplate对象用于发送Http请求
* 并注入到Spring容器中
* @return
*/
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
在业务层(Service)注入restTemplate
@Autowired
private RestTemplate restTemplate;
调用restTemplate的方法,注意什么样的请求调用什么样的方法:
//设置请求地址
String url = "http://localhost:8081/user/" + order.getUserId();
//通过RestTemplate发送请求给url的这个地址,默认返回类型为json,也可以生命返回类型为一个对象,
//只需要在第二个参数里添加对象的反射类
//这里是get请求所以使用get方法
User user = restTemplate.getForObject(url, User.class);
调用其他模块的服务叫做消费者,被调用的模块叫做提供者
注意在上面的原生服务远程调用中,只能将请求的链接写死,这十分不利于功能的拓展,另外,其也无法处理分布式的问题。
而Eureka可以解决这些问题。
服务提供者会将自己的端口号信息提供给eureka-server注册中心,而服务消费者会从eureka-server注册中心中拉取对应需要的模块的请求地址,如果有很多地址的话,会通过负载均衡技术选用一个地址并进行远程调用。另外,Eureka会与服务提供者保持一个心跳请求,Eureka每隔30秒回向服务提供者确认服务列表信息,心跳不正常的信息会被剔除。
Eureka负责保存服务提供者提供的信息,
实现流程:
搭建EurekaServer —> 将模块注册到eureka —> 在对应的模块中进行服务拉取,然后通过负载均衡技术进行服务选取与远程调用。
引入依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
在启动类中添加Eureka的启动注解:@EnableEurekaServer
@EnableEurekaServer
@SpringBootApplication
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
创建配置文件并添加如下配置:
server:
port: 10086 #服务端口
spring:
application:
name: eurekaserver # eureka的服务名称
eureka:
client:
service-url: # eureka的地址信息
defaultZone: http://127.0.0.1:10086/eureka
在客户端中加载Eureka客户端依赖
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
添加配置:
# 添加服务名称,使erueka-server能够找到你的服务
spring:
application:
name: xxxservice
# eureka配置,使其能够找到eureka-server注册中心
eureka:
client:
service-url: # eureka的地址信息
defaulZone: http://127.0.0.1:10086/eureka
在左下角可以将服务进行复制从而得到多个注册信息,要注意修改端口号
在restTemplate调用方法的url处将链接的地址和端口号部分更改为服务名:
//设置请求地址
String url = "http://userservice/user/" + order.getUserId();
使用之后在配置类中为这个服务添加负载均衡注解@LoadBalanced
/**
* 创建RestTemplate对象用于发送Http请求
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
这样注册之后,在模块之间进行服务远程调用时就会根据负载均衡自动调用对应的端口进行服务调用
SpringCloud中的负载均衡是由Ribbon实现的,restTemplate发送http://userservice/user/
这样的请求之后,Ribbon对这个请求进行拦截并解析userservice
,其在Eureka-Server中查找对应服务名的端口号,在找到之后返回其真实的请求地址,例如:http://localhost:8080/user/
若Eureka中存在多个地址的话,会对其进行负载均衡的判断来选择一个合适的地址
常见的负载均衡算法有:
负载均衡原理:
RoundRobinRule:轮询负载均衡
AvailablityFilteringRule:
WeightedResponseTimeRule:
为每个服务器赋一个权重值,服务器相应时间越长,这个权重就越小,这个规则会随机的选择服务器,而赋予的权重会影响随机的 结果
ZoneAvoidanceRule:(默认)
以区域可用性进行服务器选择,并在配置的区域中进行轮询选择,若不进行区域配置,则可认为其就是普通的轮询负载均衡技术, 就像是离哪里近,就选择哪里的服务器。
BestAvliableRule:
忽略短路服务器并选择并发数较低的服务器
RandomRule:
随机负载均衡
RetryRule:
重试机制的选择逻辑
在要服务消费者中进行定义,在主类(配置类)中进行如下定义:
注意这样做是全局配置,无论最终选择哪个服务进行调用都会遵循下面的规则,若想要根据不同的服务进行调用则需要使用yml配置文件进行:
@Bean
public IRule randomRule() {
return new RandomRule();
}
在配置文件中进行负载均衡算法的选取:
userservice:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #定义针对于userservice的负载均衡规则
Ribbon的加载策略默认为懒加载,即只有在服务拉取时才创建RibbonLoadBalancerClient对象与DynamicServerListLoadBalancer对象,这样做会使第一次进行服务获取的时间速度变得很慢。
我们可以对加载策略进行修改,修改加载策略为饥饿加载:
在yml中这样定义:
ribbon:
eager-load:
enable: true # 开启饥饿加载
clients:
- userservice