SpringCloud Eureka&Nacos&Feign

概念

技术栈:

基础

微服务治理:Eureka注册中心、Nacos注册中心、OpenFeign、网关Gateway、配置中心Nacos
Docker
异步通信:MQ技术、SpringAMQP、消费者限流
分布式搜索结果

高级

微服务保护:流量控制、系统保护、熔断降级、服务授权
分布式事务:XA模式、TCC模式、AT模式、Saga模式
分布式缓存:数据持久化、Redis
多级缓存:多级缓存分级、Nginx缓存、Redis缓存、Canal数据同步
可靠消息服务:消息三方确认、惰性队列、延迟队列、镜像集群、仲裁队列

微服务概念

单体架构:所有业务功能集中在一个项目中开发,打包成一个包部署。优点:架构简单、部署成本低。缺点:耦合度高。
分布式架构:根据业务功能对系统进行拆分,每个业务模块作为单独模块开发,成为一个服务。优点:降低服务耦合,有利于服务升级拓展。缺点:要考虑(服务拆分粒度,服务集群地址如何维护、服务之间如何实现远程调用、服务健康状态如何感知)

微服务是一种经过良好架构设计的分布式架构方案,特征:
单一职责:微服务拆分粒度更小,每一个服务都对应唯一的业务能力,做到单一职责,避免重复业务开发
面向服务:微服务对外暴露业务接口
独立自治:团队独立、技术独立、数据独立、部署独立
强隔离性:服务调用做好隔离、容错、降级、避免出现级联问题

国内知名的微服务实现结构:SpringCloud、Dubbo、SpringCloudAlibaba
SpringCloud整合了:
注册中心:Eureka、Consul
服务远程调用:Feign(http协议)
配置中心:SpringCloudConfig
服务网关:SpringCloudGataway(主)、Zuul
服务监控和保护:Hystrix

SpringCloudAlibaba整合了:
注册中心:Eureka、Nacos
服务远程调用:Feign、Dubbo
配置中心:SpringCloudConfig、Nacos
服务网关:SpringCloudGataway(主)、Zuul
服务监控和保护:Sentinel

服务拆分

服务拆分注意事项
1、不同微服务,不要重复开发相同业务
2、微服务数据独立,不要访问其他微服务的数据库
3、将自己的服务暴露微接口,供其他微服务调用

服务调用

发起http请求调用其他微服务的接口
SpringBoot提供了@RestTemplate注解

具体步骤

1、在order-service的OrderApplication中注册RestTemplate

(写在配置类中)

@MapperScan("cn.itcast.order.mappper")
@RestTemplate
public class OrderApplication{
	public static void main(String[] args){
		SpringApplication.run(OrderApplication.class,args)
	}
	@Bean
	public RestTemplate restTemplate(){
		return new RestTemplate()
	}
}

2、然后在service类中发送请求

//首先自动注入RestTemplate
@Autowired
public RestTemplate restTemplate

//然后在具体业务代码中发送请求
String url="http://localhost:8080/user/"+order.getId();
User user=restTemplate.getForObject(url,User.class);//第二个参数是返回的类型,会将返回的json反编译成想要的类

Eureka注册中心

上述demo存在问题:服务提供者不一定是在一个IP上,若有多个IP应该如何写呢?服务消费者符合获取服务提供者的地址信息?

Eureka作用

服务提供者把自己的服务注册到Eureka-server(心跳机制),服务消费者找Eureka-server拉取服务,从几个服务提供者中挑一(负载均衡策略)发送http请求。

搭建Eureka注册中心

1、搭建Eureka服务:3步

新建eureka-server项目module
1、依赖


        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
            <version>2.2.9.RELEASEversion>
        dependency>

2、在启动类上加 @EnableEurakeServe 和@SpringBootApplication注解
3、配置文件yml中写(eureka本身也需要注册,方便以后eureka集群相互之间注册)

server:
	port:10086 #服务端口
spring:
	application: #eureka的服务名称
		name: eurekaserver
eureka:
	client:
		service-url: #eureka的地址信息
			defaultZone: http://127.0.0.1:10086/eureka

2、服务注册:2步

注册user-service带EurekaServer步骤:
1、在user-service项目引入spring-cloud-starter-netflix-eureka-client的依赖


        <dependency>
            <groupId>org.springframework.cloudgroupId>
            <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
            <version>2.2.9.RELEASEversion>
        dependency>

2、在user-service项目application.yml文件编写配置

spring:
	application:
		name: userservive
eureka:
	client:
		service-url:
			defaultZone: http://127.0.0.1:10086/eureka

注:同一服务多次启动:

1、(左下角)找到服务,右键选择“copy configuration”
2、在弹出窗口先改名字,然后点击“enviroment”,在VM potions中填写“-Dserver.Port=8082”//换一个端口才行,不然端口冲突

3、服务发现:2步

1、修改OrderService代码,修改访问的url路径,用服务名代替ip、端口
String url="http://userservice/user/"+order.getUserId()
2、在order-service项目的启动类OrderApplication中的RestTemplate添加 均衡负载注解

@Bean
@LoadBalanced//负载均衡注解
public RestTemplate restTemplate(){
	return new RestTemplate();
}

Ribbin负载均衡

上述的案例中,order-service发起请求“http://userservice/user/1”并不是真实的地址,会发送到Ribbon,再找Eureka-server返回服务列表到Ribbon,最后轮询一个服务提供者。
@LoadBalanced注解,说明这个请求要被Ribbon拦截处理。

修改负载均衡策略

原默认:ZoneAvoidanceRule,以区域可用的服务器为基础进行服务器的选择。使用Zone对服务器金勋个分类,这个Zone可以理解为一个机房、一个机架等。然后再对同一Zone内的服务做轮询。

有两种调整方式
1、代码方式:在order-service项目中启动类OrderApplication类中,定义一个新的方法:
(这样的方式是全局的,只要是order-service发起的访问任何微服务,都会把负载均衡策略改为这样的)

@Bean
public IRule randomRule(){
	return new RandomuRule();//改为随机负载
}

2、配置文件方式,在order-service项目的配置文件application.yml中添加新的配置:
(可以指定对某一个微服务的调用时均衡负载规则)

userservice: #指定服务名称
	ribbon:
		NFLoadBalancerRuleClassname: com.netflix.loadbalancer.RandomRule #负载均衡规则

饥饿加载

Ribbon默认采用懒加载,即第一次访问时才会创建LoadBalanceClient,请求时间会很长,而饥饿加载则会在项目启动时创建,降低第一次访问的耗时。配置方式:
在order-service项目的配置文件application.yml中添加新的配置:

ribbon:
	eager-load:
		enabled: true #启用饥饿加载
		clients:	#指定饥饿加载的微服务
			-userservice
			-xxxservice 

Nacos注册中心

Nacos相比Eureka功能更加丰富,国内也更受欢迎。

Nacos安装

github地址点左上角tags
下载后解压到非中文路径中,target里面是jar包,conf里面是配置文件(Nacos默认端口是8848),bin里是可执行文件。

在bin目录下的cmd窗口启动命令:startup.cmd -m standalone
登录默认账号密码都是nacos

Nacos使用

1、在项目父工程中添加spring-cloud-alibaba的管理依赖


<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-alibaba-dependenciesartifactId>
    <version>2.2.6.RELEASEversion>
    <type>pomtype>
    <scope>importscope>
dependency>

2、注释掉order-service(服务消费者)和user-service(服务提供者)原来的Eureka依赖
3、添加nacos的客户端依赖(消费者、提供者都需要)

<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
dependency>

4、修改user-service和order-service中的application.yml文件,注释Eureka的地址,添加Nacos地址

spring:
	cloud:
		nacos:
			server-addr: localhost:8848 #nacos服务端地址

5、启动并测试

Nacos服务分级存储模型

(原本是两级:一层服务,一层实例)
在Nacos分级存储模型中,最上层是服务,往下是集群,最下是提供服务的实例。集群可以理解成 北京机房、上海机房等等。在服务调用时,尽可能调用本地集群。

配置Nacos集群

默认服务是在集群:Default
修改服务提供者user-service的application.yml,添加内容
启动后,如果再修改并启动新的实例,则只会改变新的实例的集群信息。

spring:
	cloud:
		nacos:
			server-addr: localhost:8848 #nacos 服务器地址
			discovery:
				cluster-name: HZ #配置集群名称,可以是机房位置

NacosRules负载均衡

想要实现 ”服务消费者调用服务提供者时,优先调用本地服务提供者“
1、需要给order-service设置集群配置,在其application.yml文件添加

spring:
	cloud:
		nacos:
			server-addr: localhost:8848 #nacos 服务器地址
			discovery:
				cluster-name: HZ #配置集群名称,可以是机房位置

2、重启order-service
(这时order-service发起访问并不会优先访问同集群下的user-service,仍然采用的轮询)
3、再order-service中设置负载均衡的 IRule 为 NacosRule,这个规则优先寻找与自己同集群的服务:

userservice:
	ribbon:
		NFLoadBalancerRuleClassName: com.alibaba.cloud.nacos.ribbon.NacosRule # 负载均衡规则

4、重启order-service

根据权重负载均衡

实际部署中可能出现:服务器设备性能有差异,部分实例所在机器性能好,另一些较差,我们希望性能好的机器承担更多用户请求。
Nacos提供了权重配置来控制访问频率,权重越大访问频率越高。

可以网页上的Nacos控制台,服务列表中,点击详情进行修改。

环境隔离-namespace

Nacos中服务存储和数据存储的最外层都是一个名为nameSpace的东西,用来做最外层隔离
结构如下:NameSpace–Group–Service/Data–集群–实例

1、网页Nacos控制台(左侧),新建命名空间。填写新的命名空间信息,会生成一个命名空间ID

2、在order-service的application.yml文件添加:
增加的是namespace

spring:  
	datasource:    
		url: jdbc:mysql://localhost:3306/heima?useSSL=false    
		username: root    
		password: 123    
		driver-class-name: com.mysql.jdbc.Driver   
cloud:   
	nacos:      
		server-addr: localhost:8848      
		discovery:        
			cluster-name: SH # 上海        
			namespace: 492a7d5d-237b-46a1-a99a-fa8e98e4b0f9 # 命名空间,填ID

**结果:**这时候配置完了,order-service就访问不了user-service了。因此,想要服务能被访问,必须放到相同目录下。

Nacos和Eurake区别

nacos会把服务提供者划分成临时实例和非临时实例。

服务检测

Nacos
临时实例(默认):心跳检测,检测不到就剔除
非临时实例:不做心跳检测,Nacos主动发请求询问,询问不到不会剔除,会标记为不健康
Eureka
心跳检测

Nacos配置非临时实例:

spring:
	cloud:
		nacos:
			discovery:
				ephemeral: flase #设置为非临时实例

服务获取

Eurake:服务消费者去Eureka注册中心拉取(30秒一次)
Nacos:Nacos注册中心做推送

Nacos配置管理

统一配置管理

目的:统一修改配置、实现配置更改热更新

实现操作:

Nacos统一配置管理

进入nacos控制台,点击左侧配置管理–配置列表,点击右侧➕,会弹出表单:

DataID是配置文件名称,不能叫application.yml,叫这个的话以后所有微服务都来找就分不清了。推荐:微服务名-环境,例如 userservise-dev.yaml
Group是分组,默认就好了。
配置内容: (不是放原来application.yml中的内容,因为并不是所有内容需要热更新)写开关类型的配置、模版类型,

点击发布

微服务配置拉取

原:项目启动–读取本地配置文件–创建spring容器–加载bean
新:项目启动–读取nacos配置文件–读取本地配置文件–创建spring容器–加载bean

步骤:
1、引入Nacos配置管理客户端依赖:


<dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
dependency>

2、在userservice中的resource目录中加一个bootstrap.yml文件,这个文件是引导文件,优先级比application.yml高。
服务名称+开发环境+后缀名就是我们在nacos控制台配的文件

spring:
  application:
    name: userservice # 服务名称
  profiles:
    active: dev #开发环境,这里是dev 
  cloud:
    nacos:
      server-addr: localhost:8848 # Nacos地址
      config:
        file-extension: yaml # 文件后缀名

配置热更新

Nacos中的配置文件变更后,微服务无需重启就可以感知。通过一下两种配置实现:

1、在@Value注入的变量所在的类上添加注解@RefreshScope
(在配置文件中的东西,在具体代码中用Value注解来取用)

2、使用@Configuration注解
这个用在:新建一个类专门用作属性加载。然后在这个类上加@Configuration注解。

@Data
@Configuration(prefix="patttern")//前缀名和变量名拼接之后,在配置文件中匹配上
public class PatternProperties{
	private String format;
}

多环境配置共享

微服务启动时会从nacos读取多个配置文件:(都在nacos控制台)
[spring.application.name]-[spring.profiles.active].yaml,例如:userservice-dev.yaml
[spring.application.name].yaml,例如:userservice.yaml
[spring.application.name].yaml不包含环境,因此可以被多个环境共享。(环境发生改变时,第一个文件会改变,但是第二个不会)因此可以把共享的放在这个文件中。

在idea的运行日志可以看加载了那些yml配置文件

如果这些配置文件中有相同属性,会以 服务名-prifile.yaml>服务名称.yaml>本地 这个优先级。

Nacos集群

搭建基本步骤

1、搭建数据库,初始化表结构

2、下载nacos安装包

3、配置nacos

将压缩包解压到任意非中文目录下,进入nacos的conf目录,修改配置文件cluster.conf.example,重命名为cluster.conf:
然后添加内容:

127.0.0.1:8845
127.0.0.1.8846
127.0.0.1.8847

然后修改application.properties文件,添加数据库配置:

# 原文中以下这行去掉注释,说明数据库是哪种
spring.datasource.platform=mysql

# 原文中以下这行去掉注释,说明数据库数量
db.num=1

db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
db.user.0=root
db.password.0=123

4、启动nacos集群

将解压得到的nacos文件夹复制三份,分别命名为:nacos1、nacos2、nacos3
然后分别修改三个文件夹中的application.properties:
nacos1:

server.port=8845

nacos2:

server.port=8846

nacos3:

server.port=8847

分别启动三个nacos节点startup.cmd

5、nginx反向代理

解压到非中文目录下,修改conf/nginx.conf文件,配置如下:

upstream nacos-cluster {
    server 127.0.0.1:8845;
	server 127.0.0.1:8846;
	server 127.0.0.1:8847;
}

server {
    listen       80; #nacos端口
    server_name  localhost;

    location /nacos {
        proxy_pass http://nacos-cluster;
    }
}

浏览器访问:http://localhost/nacos即可。

6、源代码配置文件

spring:
  cloud:
    nacos:
      server-addr: localhost:80 # Nacos地址

Feign

RestTemplate方式存在的问题:

String url="http://userservice/user/"+order.getUserId();
User user=restTemplate.getForObject(url,User.class);

存在问题:
可读性差,编程体验不统一
复杂的URL难以维护(url的参数难写)

Feign介绍
Feign是一个声明式的http客户端,官方地址
其作用就是帮助我们优雅的实现http请求的发送,解决上面提到的问题。
Feign集成了ribbon,实现了负载均衡

Feign使用

① 引入依赖
② 添加@EnableFeignClients注解
③ 编写FeignClient接口
④ 使用FeignClient中定义的方法代替RestTemplate

1、引入依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-openfeignartifactId>
    <version>2.0.4.RELEASEversion>
dependency>

2、添加注解

在服务调用者order-service的启动类傻姑娘添加 @EnableFeignClients 注解

3、编写Feign客户端

在order-service中新建一个接口,内容如下:

package cn.itcast.order.client;

import cn.itcast.order.pojo.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient("userservice")
public interface UserClient {
    @GetMapping("/user/{id}")
    User findById(@PathVariable("id") Long id);
}

这个客户端主要是基于SpringMVC的注解来声明远程调用的信息,比如:

  • 服务名称:userservice
  • 请求方式:GET
  • 请求路径:/user/{id}
  • 请求参数:Long id
  • 返回值类型:User

这样,Feign就可以帮助我们发送http请求,无需自己使用RestTemplate来发送了。

4、测试

修改order-service中的OrderService类中的queryOrderById方法,使用Feign客户端代替RestTemplate:

@Autowired
private UserClient userClient;
@Autowired
private OrderMapper orderMapper;

public Order queryOrderById(Long orderId){
	//查询订单
	Order order=orderMapper.findById(orderId);
	//利用Feign发起http请求
	User user=userClient.findById(order.getUserId());
	//封装user到order
	order.setUser(user);
	//返回
	return order;
}

Feign自定义配置

Feign可以支持很多的自定义配置,如下表所示:

类型 作用 说明
feign.Logger.Level 修改日志级别 包含四种不同的级别:NONE、BASIC、HEADERS、FULL
feign.codec.Decoder 响应结果的解析器 http远程调用的结果做解析,例如解析json字符串为java对象
feign.codec.Encoder 请求参数编码 将请求参数编码,便于通过http请求发送
feign. Contract 支持的注解格式 默认是SpringMVC的注解
feign. Retryer 失败重试机制 请求失败的重试机制,默认是没有,不过会使用Ribbon的重试

一般情况下,默认值就能满足我们使用,如果要自定义时,只需要创建自定义的@Bean覆盖默认Bean即可。

配置文件方式实现自定义配置

基于配置文件修改feign的日志级别可以针对单个服务:

feign:  
  client:
    config: 
      userservice: # 针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

也可以针对所有服务:

feign:  
  client:
    config: 
      default: # 这里用default就是全局配置,如果是写服务名称,则是针对某个微服务的配置
        loggerLevel: FULL #  日志级别 

而日志的级别分为四种:

  • NONE:不记录任何日志信息,这是默认值。
  • BASIC:仅记录请求的方法,URL以及响应状态码和执行时间
  • HEADERS:在BASIC的基础上,额外记录了请求和响应的头信息
  • FULL:记录所有请求和响应的明细,包括头信息、请求体、元数据。

Java代码方式实现自定义配置

先声明一个类,然后声明一个Logger.Level的对象:

public class DefaultFeignConfiguration  {
    @Bean
    public Logger.Level feignLogLevel(){
        return Logger.Level.BASIC; // 日志级别为BASIC
    }
}

如果要全局生效,将其放到启动类的@EnableFeignClients这个注解中:

@EnableFeignClients(defaultConfiguration = DefaultFeignConfiguration .class) 

如果是局部生效,则把它放到对应的@FeignClient这个注解中:

@FeignClient(value = "userservice", configuration = DefaultFeignConfiguration .class) 

Feign性能调优

Feign底层发起http请求,依赖于其它的框架。其底层客户端实现包括:
•URLConnection:默认实现,不支持连接池
•Apache HttpClient :支持连接池
•OKHttp:支持连接池

性能优化主要分两点:
1、使用连接池代替默认的URLConnection。
2、日志级别,最好用basic或者none。

以Apache的HttpClient为例

1、引入依赖

服务消费者order-service的pom文件中引入Apache的HttpClient依赖:


<dependency>
    <groupId>io.github.openfeigngroupId>
    <artifactId>feign-httpclientartifactId>
dependency>

2、配置连接池

在order-service的application.yml中添加配置:

feign:
  client:
    config:
      default: # default全局的配置
        loggerLevel: BASIC # 日志级别,BASIC就是基本的请求和响应信息
  httpclient:
    enabled: true # 开启feign对HttpClient的支持
    max-connections: 200 # 最大的连接数
    max-connections-per-route: 50 # 每个路径的最大连接数

你可能感兴趣的:(Java,spring,cloud,微服务,分布式)