SpringCloud微服务架构,Spring Cloud 服务治理(Eureka,Consul,Nacos),Ribbon 客户端负载均衡,RestTemplate与OpenFeign实现远程调用
什么是SpringCloud微服务架构• "微服务”一词源于 Martin Fowler的名为 Microservices的博文,可以在他的官方博客上找到http://martinfowler.com/articles/microservices.html• 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过
什么是SpringCloud
微服务架构
• "微服务”一词源于 Martin Fowler的名为 Microservices的博文,可以在他的官方博客上找到
http://martinfowler.com/articles/microservices.html
• 微服务是系统架构上的一种设计风格,它的主旨是将一个原本独立的系统拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间一般通过 HTTP 的 RESTfuLAPI 进行通信协作。
• 被拆分成的每一个小型服务都围绕着系统中的某一项或些耦合度较高的业务功能进行构建,并且每个服务都维护着白身的数据存储、业务开发自动化测试案例以及独立部署机制。
• 由于有了轻量级的通信协作基础,所以这些微服务可以使用不同的语言来编写。
定义:是一些列组件的集合
组件:
- 注册中心
- 配置中心
- 熔断器
- 网关
- 负载均衡
- 消息总线
- 数据监控
SpringCloud与Dubbo的区别
- Dubbo是基于RPC协议实现远程调用的,同时要求所用语言必须是Java
- SpringCloud规定服务之间通过http协议进行通信
- Dubbo性能较好(因为底层基于socket),SpringCloud功能全
SpringCloud 服务治理(详细解释在代码注释中)
服务注册与发现
Eureka
• Eureka 是 Netflix 公司开源的一个服务注册与发现的组件 。
• Eureka 和其他 Netflix 公司的服务组件(例如负载均衡、熔断器、网关等) 一起,被 Spring Cloud 社区整合为
Spring-Cloud-Netflix 模块。
• Eureka 包含两个组件:Eureka Server (注册中心) 和 Eureka Client (服务提供者、服务消费者)。
Eureka – 搭建服务
搭建eureka服务,编写provider服务提供者,编写consumer服务消费者端,并将服务注册到eureka注册中心,使用Ribbon实现负载均衡
这些简单的实体类与返回的结果集就不粘贴出来了,主要看eureka搭建后的效果
父pom.xml
pom文件中包含一些nacos,OpenFeign后面会用
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fs</groupId>
<artifactId>study-springcloud</artifactId>
<version>1.0-SNAPSHOT</version>
<modules>
<module>fs-provider-8001</module>
<module>fs-consumer-80</module>
<module>fs-server-eureka-7001</module>
<module>fs-api-commons</module>
<module>fs-server-eureka-7002</module>
<module>fs-server-eureka-7003</module>
<module>fs-provider-nacos-8001</module>
<module>fs-consumer-nacos-80</module>
<module>fs-provider-8002</module>
<module>fs-consumer-openFeign-80</module>
</modules>
<!-- 作为父工程-->
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<!-- spring boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.2.RELEASE</version>
<type>pom</type>
<!-- import 导入父工程的配置-->
<scope>import</scope>
</dependency>
<!-- spring cloud -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR6</version>
<type>pom</type>
<!-- import 导入父工程的配置-->
<scope>import</scope>
</dependency>
<!-- spring-cloud-alibaba-dependencies 2.2.1.RELEASE -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>2.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- eureka-client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.2.4.RELEASE</version>
</dependency>
<!-- 整合MyBatis-->
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.20</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<!-- MyBatisPlus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
<!-- lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
</dependencies>
</project>
搭建 Provider 和 Consumer 服务。
Provider 创建 fs-provider-8001 项目
多个提供复制一下,然后更改端口号与application.yml中配置注册的注册中心即可
我只粘贴出application.yml,pom.xml,主启动,与服务提供的controller,因为dao,service就是使用了MyBatis-plus
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-provider-8001</artifactId>
<dependencies>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!--
第一种是:如果你的应用不会再需要返回xml的系列化格式,那么直接在pom.xml文件中将jackson-dataformat-xml这外包排除即可(如果其他包也进行了jackson-dataformat-xml的依赖引用也要视情况排除):
第二种是:不排除jackson-dataformat-xml包,而是直接在相应接口方法或Controller上明确指定将返回JSON格式的值:@GetMapping(value = "/user-instance", produces = MediaType.APPLICATION_PROBLEM_JSON_VALUE)
-->
<!-- 排除controller返回的格式为xml-->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- MyBatis-puls-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
搭建集群provider集群就把 server.prot改下,吧defaultZone:多个地址用逗号隔开
注意的是,域名记得在host文件中修改域名与端口的映射
server:
port: 8001
spring:
application:
name: fs-provider
datasource:
username: root
password: root
url: jdbc:mysql://192.168.93.132:3306/fs_springcloud
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #自定义数据源
# 配置eureka
eureka:
instance:
hostname: localhost # 主机名,写的是域名,本机在host文件中映射
prefer-ip-address: true # 将当前实例的ip注册到eureka server中.默认是false 注册主机名
ip-address: 127.0.0.1 # 修改instance-id显示
# # 修改instance-id显示,在eureka中的显示名称
# instance-id: ${eureka.instance.ip-address}:${spring.application.name}:${server.port}
# lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔
# lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务
client:
register-with-eureka: true # 将提供注册到注册eureka中心
fetch-registry: true # 从eureka上抓取已有的注册信息
service-url:
defaultZone: http://localhost:7001/eureka #,http://localhost2:7002/eureka,http://localhost3:7003/eureka # 注册中心地址
# 配置MyBatis-plus日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
FsProvider8001主启动
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
//开启Eureka客户端
@EnableEurekaClient
public class FsProvider8001 {
public static void main(String[] args) {
SpringApplication.run(FsProvider8001.class,args);
}
}
PaymentController
package com.fs.controller;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import com.fs.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/payment")
public class PaymentController {
//注入业务层
@Autowired
private PaymentService paymentService;
@RequestMapping("/findAll")
public Result<List<Payment>> findAll(){
List<Payment> all = paymentService.findAll();
return new Result<List<Payment>>(true,"查询成功8001",all);
}
@RequestMapping("/testTimeOut")
public Result test(){
try {
//让方法停止2秒,模拟方法执行时间过长,因为Ribbon的超时时间默认为1秒,超时就会报错
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Result<List<Payment>>(true,"查询成功");
}
}
Consumer 创建 fs-consumer-80 消费者 使用 RestTemplate 完成远程调用。
Resttemplate
• Spring提供的一种简单便捷的模板类,用于在 java 代码里访问 restful 服务。
• 其功能与 HttpClient 类似,但是 RestTemplate 实现更优雅,使用更方便。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-consumer-80</artifactId>
<dependencies>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka-Client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<!-- 排除controller返回的格式为xml-->
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 80
spring:
application:
name: fs-consumer-80
eureka:
client:
register-with-eureka: false # 消费者我目前的用途不需要将消费者注册到注册中心
fetch-registry: true # 从注册中心拉取服务
registry-fetch-interval-seconds: 30 # 默认30秒定时去注册中心拉取服务
service-url:
defaultZone: http://localhost:7001/eureka #,http://localhost2:7002/eureka,http://localhost3:7003/eureka # 注册中心地址
# 配置的方式设置Ribbon的负载均衡
#FS-PROVIDER: # 设置的我们服务提供方的应用名称
# ribbon: # 固定写法
# NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #负载均衡的规则,表示加权规则,yml配置优先级第一,Java代码第二,默认的最后
# 自定义属性来存储注册中心 提供者的名字
provider:
name: FS-PROVIDER
主启动 FsConsumer80
package com.fs;
import com.fs.config.MyRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient //激活发现客户端
//开启客户端负载均衡,不使用默认的轮询,
//name 设置我们这个负载均衡用于那个服务提供方 configuration 使用我们自定义的配置类中配置的负载均衡
@RibbonClient(name = "FS-PROVIDER",configuration = MyRule.class)
public class FsConsumer80 {
public static void main(String[] args) {
SpringApplication.run(FsConsumer80.class,args);
}
}
MyRule 定义负载均衡策略
== Ribbon负载均衡器==
-
定义:解决客户端的服务负载均衡算法处理器
-
功能:
- 负载均衡
- 简化远程调用过程
-
使用:
-
开启负载均衡功能(默认轮询策略)
@LoadBanlance
-
修改主机名为服务名
http://服务名称/login
-
-
怎么修改负载均衡策略
-
注解方式
### xxx 是一个JavaConfig类,其中㤇配置使用的策略Bean @RibbonClient(name="",configuration = XXX.Class)
-
配置方式
[服务名称1]: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule [服务名称2]: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.BestAvailableRule
-
MyRule
package com.fs.config;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
编码的方式设置
定义Ribbon负载均衡的配置类
注意:这个类不能在@Configuration 下的@@ComponentScan扫描的包下
因为@SpringBootApplication这个注解定义为扫描被这个注解注解的类的包及其子包
所以,我们这个Ribbon负载均衡的配置类就只能从新新建一个包,不在主配置类包下及其子包就可以了
这里配置为随机后记得在主启动类上加上一个注解@RibbonClient
*/
@Configuration
public class MyRule {
@Bean
public IRule myIRule(){
//import com.netflix.loadbalancer.RandomRule;这个类就就代表负载均衡为随机
//创建负载均衡的策略为随机
RandomRule randomRule = new RandomRule();
return randomRule;
}
}
RestTemplateConfig 注入RestTemplate
package com.fs.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/*
定义restTemplate
*/
@Configuration
public class RestTemplateConfig {
@Bean
//客户端负载均衡
@LoadBalanced //使用默认的负载均衡轮询
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
PaymentController 服务消费者
package com.fs.controller;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class PaymentController {
//注入RestTemplate
@Autowired
private RestTemplate restTemplate;
//注入DiscoveryClient,动态从eureka server 中获取 provider 的host与ip
@Autowired
private DiscoveryClient discoveryClient;
// String host;
// int port;
// private String PAYMENT_URL ="http://"+host+ ":"+port;
//从application.yml文件中读取
@Value("${provider.name}")
private String providerName;
@RequestMapping("/payment/findAll")
public Result<List<Payment>> findAll(){
//搭建集群后,使用服务名称做url,负载均衡的调用注册中心的服务
Result<List<Payment>> result = restTemplate.getForObject("http://"+providerName+"/payment/findAll", Result.class);
return result;
}
// @RequestMapping("/payment/findAll")
// public Result<List<Payment>> findAll(){
// //得到服务的实体,为什么是集合呢?因为未来的一个微服务名称是搭建集群的
// List<ServiceInstance> instances = discoveryClient.getInstances("FS-PROVIDER");
// String host = null;
// int port = 0;
//
// if (instances.size()>0){
// //获取第一个
// ServiceInstance serviceInstance = instances.get(0);
// //见名知意
// host = serviceInstance.getHost();
// port = serviceInstance.getPort();
// }else {
// return null;
// }
// Result<List<Payment>> result = restTemplate.getForObject("http://"+host+":"+port+"/payment/findAll", Result.class);
// result.setMessage(host+":"+port);
// return result;
// }
}
搭建 Eureka Server 服务。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-server-eureka-7001</artifactId>
<dependencies>
<!-- eureka-server -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 7001
spring:
application:
name: fs-server-eureka-ha
eureka:
instance:
hostname: localhost #主机名,域名,本机在host文件配置
client:
register-with-eureka: false # 是否将自己注册到eureka,搭建继续需要将eurekaserver注册到注册中心,相互注册
fetch-registry: false # 是否从EurekaServer抓取已有的注册信息
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
# eureka集群
# client:
# register-with-eureka: true # 是否将自己注册到eureka,搭建继续需要将eurekaserver注册到注册中心,相互注册
# fetch-registry: true # 是否从EurekaServer抓取已有的注册信息
# service-url:
# defaultZone: http://localhost2:7002/eureka,http://localhost3:7003/eureka
# server:
# enable-self-preservation: false # 默认true 开启自我保护机制,但是开发中可以关掉,方便开发,实际生产一定使用默认的true
# eviction-interval-timer-in-ms: 3000 #默认60秒
主启动 ServerEureka7001
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
//开启eureka的服务
@EnableEurekaServer
public class ServerEureka7001 {
public static void main(String[] args) {
SpringApplication.run(ServerEureka7001.class,args);
}
}
测试Eureka – 搭建服务
启动ServerEureka7001
启动FsProvider8001
启动FsConsumer80
浏览器分别打开:
http://localhost:7001/
http://localhost:8001/payment/findAll
http://localhost/consumer/payment/findAll
通过消费端使用RestTemplate远程调用我们的服务提供者
Eureka – 相关配置及特性
- instance
eureka:
instance:
hostname: localhost # 主机名
client:
service-url:
defaultZone: http://localhost:8761/eureka # eureka服务端地址,将来客户端使用该地址和eureka进行通信
register-with-eureka: true # 是否将自己的路径 注册到eureka上。eureka server 不需要的,eureka provider client 需要
fetch-registry: true # 是否需要从eureka中抓取路径。eureka server 不需要的,eureka consumer client 需要
- server
eureka:
server:
#是否开启自我保护机制,默认true
enable-self-preservation:
#清理间隔(单位毫秒,默认是60*1000)
eviction-interval-timer-in-ms:
instance:
lease-renewal-interval-in-seconds: 30 # 每一次eureka client 向 eureka server发送心跳的时间间隔
lease-expiration-duration-in-seconds: 90 # 如果90秒内eureka server没有收到eureka client的心跳包,则剔除该服务
- client
eureka:
client:
service-url:
# eureka服务端地址,将来客户端使用该地址和eureka进行通信
defaultZone:
register-with-eureka: # 是否将自己的路径 注册到eureka上。
fetch-registry: # 是否需要从eureka中抓取数据。
- dashboard
eureka:
dashboard:
enabled: true # 是否启用eureka web控制台
path: / # 设置eureka web控制台默认访问路径
Consul客户端配置(了解,不常用)
- 服务ip和地址
- 服务名称
换汤不换药,总的使用差不多
• Consul 是由 HashiCorp 基于 Go 语言开发的,支持多数据中心,分布式高可用的服务发布和注册服务软件。
• 用于实现分布式系统的服务发现与配置。
• 使用起来也较 为简单。具有天然可移植性(支持Linux、windows和Mac OS X);安装包仅包含一个可执行文件,
方便部署 。
• 官网地址: https://www.consul.io
spring:
cloud:
consul:
host: localhost # consul 服务端的 ip
port: 8500 # consul 服务端的端口 默认8500
discovery:
service-name: ${spring.application.name} # 当前应用注册到consul的名称
prefer-ip-address: true # 注册ip
application:
name: consul-provider # 应用名称
OpenFeign
什么是Feign
-
定义:是微服务之间通过http协议调用的简化使用的框架
-
事实:
- Feign自动集成Ribbon,且默认开启相关功能
-
使用:
-
导包
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency>
-
配置
这个配置信息可以在springboot的项目idea按两下shitf,输入feign**properties(****properties)这样的类中都可以发现这个类有哪些配置可以在yml配置文件中配置ribbon: ConnectTimeout: 1000 # 连接超时时间 默认1s ReadTimeout: 3000 # 逻辑处理的超时时间 默认1s
-
编码
-
开启Feign功能
@EnableFeignClients
-
编写Feign接口
// 指定当前类的方法都从Eureka中对应的服务去调用 @FeignClient("服务的名称") public interface ProviderService { // 通过Controller的地址映射注解告诉Feign该类findOne2方法从服务提供者的/findOne/{id}地址获取信息 // 通过Controller的参数获取注解告诉Feign的参数信息 // 通过返回值告诉Feign应该返回的参数应该封装的格式 @GetMapping("/goods/findOne/{id}") public Goods findOne(@PathVariable("id") int id); }
-
-
SpringBoot开启DEBUG日志
-
开启SpringBoot框架的DEBUG日志
-
开启全部
# 设置当前的日志级别 debug,feign只支持记录debug级别的日志 logging: level: root: debug
-
开启部分
logging: level: com.fs: debug
-
-
开启Feign的日志
-
必须开启对应Feign接口包的日志为DEBUG
-
编码
-
定义日志级别
@Configuration public class FeignLogConfig { /* NONE,不记录 BASIC,记录基本的请求行,响应状态码数据 HEADERS,记录基本的请求行,响应状态码数据,记录响应头信息 FULL;记录完成的请求 响应数据 */ @Bean public Logger.Level level(){ return Logger.Level.FULL; } }
-
配置生效
@FeignClient(value = "FEIGN-PROVIDER",configuration = FeignLogConfig.class)
-
-
Nacos客户端配置 基于OpenFeign远程调用(代码中详细注释)
• Nacos(Dynamic Naming and Configuration Service) 是阿里巴巴2018年7月开源的项目。
• 它专注于服务发现和配置管理领域 致力于帮助您发现、配置和管理微服务。Nacos 支持几乎所有主流类型的“服务”的发现、配置和管理。
• 一句话概括就是Nacos = Spring Cloud注册中心 + Spring Cloud配置中心。
• 官网:https://nacos.io/
• 下载地址: https://github.com/alibaba/nacos/releases
docker安装nacos
#1.3.2
# 创建 /home/dockerdata/nacos/logs目录用于挂载
# 拉取nacos镜像
docker pull nacos/nacos-server:1.3.2
# 运行容器 这里面的sql语句需要在github的nacos中去拷贝执行 执行成功后访问ip:端口/nacos
docker run -d \
-e PREFER_HOST_MODE=ip \
-e MODE=standalone \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_SERVICE_HOST=47.112.174.148 \
-e MYSQL_SERVICE_PORT=3306 \
-e MYSQL_SERVICE_USER=root \
-e MYSQL_SERVICE_PASSWORD=root \
-e MYSQL_SERVICE_DB_NAME=nacos \
-e TIME_ZONE='Asia/Shanghai' \
-v /docker/dockerdata/nacos/logs:/home/nacos/logs \
-p 8848:8848 \
--name nacos1.3.2 \
--restart=always \
nacos/nacos-server:1.3.2
# 意思
docker run -d \
-e PREFER_HOST_MODE=hostname \
-e MODE=standalone \
-e SPRING_DATASOURCE_PLATFORM=mysql \
-e MYSQL_MASTER_SERVICE_HOST=数据库ip \
-e MYSQL_MASTER_SERVICE_PORT=数据库端口 \
-e MYSQL_MASTER_SERVICE_USER=用户名 \
-e MYSQL_MASTER_SERVICE_PASSWORD=密码 \
-e MYSQL_MASTER_SERVICE_DB_NAME=对应的数据库名 \
-e MYSQL_SLAVE_SERVICE_HOST=从数据库ip \
-p 8848:8848 \
--name nacos-sa-mysql \
--restart=always \
nacos/nacos-server
使用上面的父项目搭建
fs-provider-nacos-8001 服务提供端
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-provider-nacos-8001</artifactId>
<dependencies>
<!-- springcloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--这两个spring-cloud-starter-alibaba-nacos-discovery都可以-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!-- <version>0.9.0.RELEASE</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!-- druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
</dependency>
<!-- MyBatis-puls-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 8001
spring:
application:
name: fs-provider-nacos
datasource:
username: root
password: root
url: jdbc:mysql://192.168.93.132:3306/fs_springcloud
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource #自定义数据源
# 将服务提供者注册到nacos注册中心
cloud:
nacos:
discovery:
server-addr: 192.168.93.132:8848 # 配置nacos 服务端地址
# 配置MyBatis-plus日志
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
主启动 FsNacosProvider8001
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FsNacosProvider8001 {
public static void main(String[] args) {
SpringApplication.run(FsNacosProvider8001.class,args);
}
}
PaymentController 服务提供controller
package com.fs.controller;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import com.fs.service.PaymentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/payment")
public class PaymentController {
//注入业务层
@Autowired
private PaymentService paymentService;
@RequestMapping("/findAll")
public Result<List<Payment>> findAll(){
List<Payment> all = paymentService.findAll();
return new Result<List<Payment>>(true,"查询成功nacos8001",all);
}
@RequestMapping("/testTimeOut")
public Result test(){
try {
//让方法停止2秒,模拟方法执行时间过长,因为Ribbon的超时时间默认为1秒,超时就会报错
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new Result<List<Payment>>(true,"查询成功");
}
}
fs-consumer-nacos-80 服务消费端
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>study-springcloud</artifactId>
<groupId>com.fs</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>fs-consumer-nacos-80</artifactId>
<dependencies>
<!-- springcloud alibaba nacos-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!--这两个spring-cloud-starter-alibaba-nacos-discovery都可以-->
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-alibaba-nacos-discovery -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>-->
<!-- <version>0.9.0.RELEASE</version>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/com.alibaba.nacos/nacos-client -->
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>
<version>1.3.2</version>
</dependency>
<!-- openFeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- spring-boot-starter-web spring-boot-starter-actuator绑定在一块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 自己的实体类-->
<dependency>
<groupId>com.fs</groupId>
<artifactId>fs-api-commons</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
application.yml
server:
port: 80
spring:
application:
name: fs-consumer-nacos
cloud:
nacos:
discovery:
server-addr: 192.168.93.132:8848 # 配置nacos 服务端地址
logging:
level:
# root: debug # 开启springboot的debug的日志信息,不配置springboot默认是info
# com.fs: debug # 开启部分springboot的debug的日志信息
# feign日志以什么级别监控那个feign组件功能使用的接口,使用debug级别(只能记录debug级别),然后调用服务方法,在控制台就能看到详细debug信息
com.fs.springcloud.server.PaymentOpenFeignService: debug
主启动 FsNacosConsumer80
package com.fs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
//开启OpenFeign
@EnableFeignClients
public class FsNacosConsumer80 {
public static void main(String[] args) {
SpringApplication.run(FsNacosConsumer80.class,args);
}
}
OpenFeignConfig
package com.fs.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/*
OpenFeign的日志配置类
*/
@Configuration
public class OpenFeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
//表示开启的是详细的feign日志,FULL表示最为详细的,点进去有4个
return Logger.Level.FULL;
}
//还需要在yml中配置feign日志已什么级别监控那个接口
}
PaymentOpenFeign 为OpenFeign的接口
package com.fs.feign;
import com.fs.config.OpenFeignConfig;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
/**
* feign的声明式接口,发起远程调用的,简化restTemplate
*
* 1.定义接口
* 2.接口上添加注解@FeignClient(value = "注册中心服务提供者名",configuration=定义的OpenFeign的日志类)
* 3.编写调用接口,接口的声明规则和提供方接口保持一致
* 4.去controller注入改接口对象,调用接口方法来完成远程调用
*/
@FeignClient(value = "fs-provider-nacos",configuration = OpenFeignConfig.class)
public interface PaymentOpenFeign {
//复制服务提供的controller方法,路径记得加上controller类上的路径
@RequestMapping("/payment/findAll")
Result<List<Payment>> findAll();
//测试time超时,由于我们在服务提供方的这个方法制作了sleep2秒,由于feign底层基于Ribbon,
//Ribbon默认超时时间为1秒,所以报错java.net.SocketTimeoutException: Read timed out
//解决办法在配置文件中配置Ribbon的超时时间
@RequestMapping("/payment/testTimeOut")
Result test();
}
PaymentController 使用OpenFeign远程调用服务提供者
package com.fs.controller;
import com.fs.feign.PaymentOpenFeign;
import com.fs.pojo.Payment;
import com.fs.result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
@RequestMapping("/consumer")
public class PaymentController {
//注入OpenFeign接口
@Autowired
private PaymentOpenFeign paymentOpenFeign;
@RequestMapping("/payment/findAll")
public Result<List<Payment>> findAll(){
//调用OpenFeign接口
Result<List<Payment>> all = paymentOpenFeign.findAll();
return all;
}
//测试time超时
@RequestMapping("/payment/testTimeOut")
Result test(){
Result test = paymentOpenFeign.test();
return test;
}
}
测试nacos
首先确保docker的nacos正常运行且能正常访问
运行:
FsNacosProvider8001
运行:
FsNacosConsumer80
浏览器访问nacos查看服务是否注册
直接访问服务提供接口,与使用消费者端远程调用服务提供接口
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)