服务注册与发现的概念
服务之间相互访问: 例如:用户中心与内容中心之间相互调用。
问题:
服务调用需要知道对方的服务地址,地址写在哪里?
如果服务是多个实例部署,该调用哪一个?
如果服务是多个实例部署,每个实例负载如何分配?
解决不用修改代码始终能找到服务的方法: 独立的配置文件,让所有的服务读取这个配置。
依然存在的问题:
随着服务越来越多,每个服务部署的实例越来越多,哪个上线,哪个下线不能靠手工维护。
外部配置文件的问题,无法感知服务是不是存活,当只有调用时,才会发现服务存活与否。
服务注册中心。
原理:可以把服务注册与发现类比成一张表。
服务注册与发现的产品
注册中心:
使用Java开发的有:Nacos、Eureka、Zookeeper。
使用Go语言开发的:Consul、CoreDNS。
选择注册与发现的产品:
首先我们学习的Java,更好的兼容,优先选择Java开发的。
其次,Nacos作为后起之秀,当然要借助前辈的优势,所以Nacos能支持的,都支持了。
在Spring Cloud体系上,Alibaba的产品,被称之为第二代微服务的典范,是应用的趋势,所以我们采用Nacos。
服务注册中心:
解决了服务始终能被找到,并且能及时发现服务是否存活的问题。
一般集群都会部署很多个微服务节点。这些节点一般都具备两种角色,称为:“服务的提供者” 和 “服务的消费者”。
“服务提供者”将自己的服务地址等信息登记到“服务注册中心”中,调用者(“服务消费者”)需要的时候,每次都先去“服务注册中心”查询即可。既解决了人工维护微服务节点状态的问题,也能解决多节点间负载均衡的问题。
服务注册与发现-Nacos
下载Nacos:
Redirecting to: https://nacos.io/
登陆Nacos。 https://localhost:8848/nacos
重要的功能: 服务管理。 配置管理。
使用Nacos
新建Spring Boot的web项目。
导入Spring Cloud Alibaba依赖。
各版本之间的对应。
导入Spring Cloud Alibaba依赖。
在dependencyManagement中加入依赖。
<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>
在dependencies中加入Nacos依赖。
编写配置文件 在application.yml中,进行如下配置:
server:
servlet:
context-path:/user
port:8080
spring:
application:
name: user-center
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
启动项目,注册服务,在Nacos控制台中,存在配置的应用名称,说明服务注册成功。
引入nacos后的架构
服务调用
常见的服务间调用方式有二种:
RPC(Remote Produce Call)远程过程调用。 自定义数据格式,基于原生TCP通信,速度快、效率高,但较为复杂。 比如WebService框架CXF,阿里的Dubbo。
HTTP,基于HTTP协议的服务调用。 跨平台、跨语言,但HTTP协议的信息较为臃肿,速度较慢。 比如HttpClient等。
基于HTTP的服务调用
JDK提供的HttpUrlConnection。
Apache的HttpClient。
Spring封装的RestTemplate。
Feign: NetFlix提供的,在RestTemplate和Ribbon基础上封装。
OpenFeign: Spring提供的,声明式服务调用。
RestTemplate介绍
RestTemplate:
他简化了与HTTP服务的通信方式,统一了RESTful的标准,封装了http链接。
相较于HttpClient等框架,他是一种更优雅的调用方式。
常用方法: getForEntity()、getForObject()。 postForEntity()、postForObject()、postForLocation()。 PUT()、DELETE()等等。
RestTemplate问题
RestTemplate,调用的问题:
地址是写死的,如果地址发生变化怎么办?。
如果部署了多个实例(为了负载均衡)怎么办?
借助DiscoveryClient可以通过服务ID来访问,也可以按特定算法来访问服务的某个实例。
通过服务名让服务始终能找到:
//从注册中心一直只可以找到user-center
list<ServiceInstance>instances =discoveryclient.getInstances(serviceld:"user-center");
//使用是jdk1.8的特性
String targetuRL =instances.stream().map(instance ->instance.geturi().tostring()+"/"+
instance.getServiceId()+"/users/{id}")
.findFirst()//如果有多个实例,只取第一个实例
.orElseThrow(()->new IllegalArgumentException("当前没有实例"));
log.info(targetURL);
//通过id查询share
Share share =shareMapper.selectByPrimaryKey(id);
//通过share的userid查询userDTO
Integer userId =share.getUserId();
UserDTO userDTO =restTemplate.getForObject(
targetURL,
UserDTO.class,
userId);
使用随机算法,随机访问一个实例:
//从注册中心一直只可以找到user-center
List<ServiceInstance>instances =discoveryclient.getInstances(serviceld:"user-center");
//使用是jdk1.8的特性
List<String>targetuRLS =instances.stream()
.map(instance ->instance.getUri().tostring()+"/"+
instance.getServiceId()+"/users/{id}").collect(Collectors.tolist());//随机一个实例出来
int i =ThreadLocalRandom.current().nextInt(targetURLS.size());String targetuRL =targetURLS.get(i);
log.info(targetURL);
负载均衡简析
负载均衡是对系统的高可用、网络压力的缓解和处理能力扩容的重要手段之一。 以上例子中DiscoveryClient实现了简单的负载均衡
但仍然存在问题:
访问服务的代码,实现了负载,但很繁琐。
在业务逻辑中,出现了选择服务实例的代码,耦合高。
Ribbon简介
Ribbon是Netflix开源的客户端侧的负载均衡器。他简化服务调用的开发,并为我们提供一系列的负载均衡的算法。
Ribbon的使用
添加Nacos依赖,因为Nacos依赖了Ribbon,所以不需要额外引入,默认情况下,也不需要加配置。 给RestTemplate开启Ribbon:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
修改服务调用代码:
//通过id查询share
Share share =shareMapper.selectByPrimaryKey(id);//通过share的userid查询userDTO
Integer userId =share.getUserId();
//url直接使用服务名,ribbon会给我们自动选择一个实例执行
UserDTo userDTO =restTemplate.getForObject(
url:"http://user-center/user-center/users/{id}",
JserDTO.class,
userId);
Ribbon的负载均衡规则
Ribbon的负载均衡配置
#细粒度配置,针对某一个服务
user-center:
ribbon:
NFLoadBalancerRuleClassName:com.netflix.loadbalancer.RandomRule
为所有微服务配置负载均衡: 在ComponentScan外新建全局Ribbon配置类:
@Configuration
public class DefaultRibbonconfiguration {
@Bean
public IRule ribbonRule(){return new RandomRule();}
所有微服务配置负载均衡: 在项目中新增Ribbon配置类
@Configuration
@RibbonClients(defaultConfiguration =DefaultRibbonConfiguration.class)public class RibbonConfiguration {
}
Ribbon的配置项
Ribbon配置属性的方式
<clientName>.ribbon.xxx 如下属性:
NFLoadBalancerClassName: ILoadBalancer实现类。
NFLoadBalancerRuleClassName: Irule实现类。
NFLoadBalancerPingClassName: Iping实现类。
NIWSServerListClassName: ServerList实现类。
NIWSServerListFilterClassName: ServerListFilter实现类
Ribbon懒加载配置
服务重启后,第一次访问会比较慢,原因是Ribbon默认为所有的微服务开启的是懒加载模式,当服务调用到的时候,才会去创建对应的请求连接,如http://user-center/users/1,解决这个问题,需要在application.yml中配置如下:
ribbon:
eager-load;
#开启饥饿加载
enabled: true
#细粒度配置,指定服务,如果是多个,使用逗号分隔
clients: user-center
Ribbon扩展
在Ribbon的规则中,不支持Nacos的权重,而Nacos权重在实例的应用中,作用很大,可根据机器的配置动态调整所承受的请求量,所以需要扩展Ribbon的负载均衡策略。
编写代码扩展Ribbon支持Nacos权重,继承AbstractLoadBalancerRule,编写代码,开启配置。
@override
public Server choose(Object o){
try {
BaseLoadBalancer loadBalancer =(BaseLoadBalancer)this.getLoadBalancer();
//想要请求的微服务名称
String name =loadBalancer.getName();
NamingService namingService =nacosDiscoveryProperties.namingServiceInstance();
Instance instance =namingService.selectoneHealthyInstance(name);
log.info("选择的实例是:port={},instance ={}",instance.getPort(),instance);
return new NacosServer(instance);
}catch(NacosException e){
e.printstackTrace();
return null;
Feign
什么是Feign: Feign是Netflix开源的声明式HTTP客户端,致力于让编写http请求更简单。
RestTemplate与Feign的区别:
OpenFeign
什么是OpenFeign: OpenFeign是一个显示声明式的WebService客户端。 Spring Cloud对Feign进行了封装,使其支持MVC注解和HttpMessageConverts等,可以做到使用HTTP请求访问远程服务,就像调用本地方法一样的,开发者完全感知不到这是在调用远程方法,更感知不到在访问HTTP请求。
使用OpenFeign
在启动类上加注解:
编写服务调用接口:
在Service中注入并调用相关的方法
OpenFeign的组成
OpenFeign的配置
全局配置日志,所有的Feign接口生效:
属性配置方式支持的配置项
OpenFeign的性能优化
性能优化的方法:
把默认的请求方式修改为httpclient或okhttp。
在配置文件中,配置连接池(性能提升15%左右)。
配置feign的日志级别,建议设置成BASIC。
引依赖:
写配置: