微服务架构:
它是一种架构模式,它提倡将单一应用程序划分为一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值。每个服务运行在其独立的进程中,服务于服务间采用轻量级的通信机制互相协作(通常是基于HTTP协议的RESTful API)。每个服务都围绕着具体业务进行构建,并且能够被独立的部署到生产环境、类生产环境等。另外,应当尽量避免统一的、集中式的服务管理机制,对具体的一个服务而言,应根据业务上下文,选择合适的语言、工具对其进行构建。
一个微服务架构里面包含哪些东西?
服务注册与发现 、服务调用、服务熔断、负载均衡、服务降级、服务消息队列、配置中心管理、服务网关、服务监控、全链路追踪、自动化构建部署、服务定时任务调度操作。
SpringCloud:
是分布式微服务架构的一站式解决方案,是多种微服务架构落地技术的集合体,俗称微服务全家桶。
SpringBoot是一种服务开发技术
服务注册与发现:EUREKA
服务负载与调用:NETFLIX OSS RIBBON、NETTFLIX
服务熔断降级:HYSTRIX
服务网关:Zuul
服务分布式配置:SpringCloud Config
服务开发: SPRING BOOT
关于cloud对应boot的使用版本选型问题,springboot官网有这样的规定:
H版本必须使用2.2版本;
如果是G版本,必须对应2.1版本;
跟随视频学习的环境配置要求:
但是我们现在去springcloud官网去看最适配的版本的话,可以看到
Hoxton SR4 目前最适配2.2.5版本的boot。
添加packing为pom形式,表明是个父工程
并添加下面的依赖
<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.0modelVersion>
<groupId>org.xzq.springcloudgroupId>
<artifactId>cloud2020artifactId>
<version>1.0-SNAPSHOTversion>
<packaging>pompackaging>
<modules>
<module>cloud-provider-payment8001module>
<module>cloud-consumer-order80module>
<module>cloud-api-commonsmodule>
<module>cloud-eureka-server7001module>
<module>cloud-eureka-server7002module>
<module>cloud-provider-payment8002module>
<module>cloud-provider-payment8004module>
<module>cloud-comsumerzk-order80module>
<module>cloud-providerconsul-payment8006module>
<module>cloud-consumerconsul-order80module>
<module>cloud-consumer-feign-order80module>
modules>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.8maven.compiler.source>
<maven.compiler.target>1.8maven.compiler.target>
<junit.version>4.12junit.version>
<log4j.version>1.2.17log4j.version>
<lombok.version>1.16.18lombok.version>
<mysql.version>8.0.18mysql.version>
<druid.verison>1.1.16druid.verison>
<mybatis.spring.boot.verison>1.3.0mybatis.spring.boot.verison>
properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>2.2.2.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-dependenciesartifactId>
<version>Hoxton.SR1version>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-dependenciesartifactId>
<version>2.2.0.RELEASEversion>
<type>pomtype>
<scope>importscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>${mysql.version}version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>${druid.verison}version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>${mybatis.spring.boot.verison}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>${lombok.version}version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>${junit.version}version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>${log4j.version}version>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
<addResources>trueaddResources>
configuration>
plugin>
plugins>
build>
project>
其中的dependencyManagement作用:子模块继承之后,提供作用:锁定版本+子module不用写groupId和version
点击图示按钮,会发现test变灰
以上都完成后,可以选择install,点击run,看看maven与idea是否整合成功。
在创建好该module后,我们来到父工程的pom文件查看发现多了这么一串东西,说明子模块确实搭建完毕了:
而子项目里,groupId和version确实没写,继承的是父项目的:
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zipkinartifactId>
dependency>
<dependency>
<groupId>com.eiletxie.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 8001 #服务端口
spring:
application:
name: cloud-payment-service #服务名
datasource:
type: com.alibaba.druid.pool.DruidDataSource #当前数据源操作类型
driver-class-name: com.mysql.cj.jdbc.Driver #数据库驱动包
url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: 1234
devtools:
restart:
enabled: true #是否支持热部署
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.xzq.springcloud.entities #所有entity别名所在包
@SpringBootApplication
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Payment implements Serializable {
private Long id;
private String Serial;
}
Lombok项目是一个Java库,它会自动插入编辑器和构建工具中,Lombok提供了一组有用的注释,用来消除Java类中的大量样板代码。仅五个字符(@Data)就可以替换数百行代码从而产生干净,简洁且易于维护的Java类。
Lombok也存在一定风险,在一些开发工具商店中没有Project Lombok支持选择。 IDE和JDK升级存在破裂的风险,并且围绕项目的目标和实施存在争议。
常用注解:
@Setter :注解在类或字段,注解在类时为所有字段生成setter方法,注解在字段上时只为该字段生成setter方法。
@Getter :使用方法同上,区别在于生成的是getter方法。
@ToString :注解在类,添加toString方法。
@EqualsAndHashCode: 注解在类,生成hashCode和equals方法。
@NoArgsConstructor: 注解在类,生成无参的构造方法。
@RequiredArgsConstructor: 注解在类,为类中需要特殊处理的字段生成构造方法,比如final和被@NonNull注解的字段。
@AllArgsConstructor: 注解在类,生成包含类中所有字段的构造方法。
@Data: 注解在类,生成setter/getter、equals、canEqual、hashCode、toString方法,如为final属性,则不会为该属性生成setter方法。
@Slf4j: 注解在类,生成log变量,严格意义来说是常量。
@Mapper
public interface PaymentDao {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
<mapper namespace="com.atguigu.springcloud.dao.PaymentDao">
<insert id="create" parameterType="com.atguigu.springcloud.entities.Payment" useGeneratedKeys="true" keyProperty="id">
insert into payment(serial) values (#{serial})
insert>
<resultMap id="BaseResultMap" type="com.atguigu.springcloud.entities.Payment">
<id column="id" property="id" jdbcType="BIGINT" />
<id column="serial" property="serial" jdbcType="VARCHAR" />
resultMap>
<select id="getPaymentById" resultMap="BaseResultMap" parameterType="Long">
select * from payment where id = #{id}
select>
mapper>
public interface PaymentService {
public int create(Payment payment);
public Payment getPaymentById(@Param("id") Long id);
}
@Service
public class PaymentServiceImpl implements PaymentService {
@Resource
private PaymentDao paymentDao;
@Override
public int create(Payment payment) {
return paymentDao.create(payment);
}
@Override
public Payment getPaymentById(Long id) {
return paymentDao.getPaymentById(id);
}
}
@RestController
public class PaymentController {
@Resource
private PaymentService paymentService;
@PostMapping("/payment/create")
public CommonResult create(Payment payment){
int result = paymentService.create(payment);
if(result>0){
return new CommonResult(200,"插入数据库成功",result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping("/payment/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
if(payment != null){
return new CommonResult(200,"查询成功",payment);
}else{
return new CommonResult(444,"没有对应记录,查询id:"+id,null);
}
}
}
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<configuration>
<fork>truefork>
<addResources>trueaddResources>
configuration>
plugin>
plugins>
build>
同上创建module
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-consumer-order80artifactId>
<dependencies>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 80
# 注册Eureka服务
eureka:
client:
# Eureka服务注册中心会将自己作为客户端来尝试注册它自己,必須禁止
register-with-eureka: false
fetch-registry: false
不加上面的下班部分会一直报错:
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server,要阻止其注册
@Configuration
public class ApplicationContextConfig {
@Bean
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
/**
* @author pzy
* @ClassName OrderController
* @Date 2020/5/11 19:28
*/
@RestController
@Slf4j
public class OrderController {
public static final String PAYMENT_URL="http://localhost:8001";
@Autowired
RestTemplate restTemplate;
@GetMapping("/consumer/payment/create")
public CommonResult create(Payment payment){
return restTemplate.postForObject(PAYMENT_URL+"/payment/create",payment,CommonResult.class);
}
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPayment(@PathVariable("id")Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
}
当我们测试插入功能时,虽然浏览器显示成功,但会发现serial没插入进入数据库,如图:
那是因为payment里面需要加上@RequestBody注解。
所以上方的paymentController需要加上@RequestBody。
@PostMapping("/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
if(result>0){
return new CommonResult(200,"插入数据库成功",result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
将冗余的代码(比如上面两个项目都有一样的实体类)挑选出来,做成一个公共工程。
<dependencies>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.1.0version>
dependency>
dependencies>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
1.什么是服务治理?
springcloud封装了Netflix公司开发的Eureka模块来实现服务治理。
在传统的RPC远程调用框架中,管理每个服务与服务之间依赖关系比较复杂,管理比较复杂,所以需要使用服务治理,管理服务与服务之间依赖关系,可以实现服务调用、负载均衡、容错等,实现服务发现与注册。
2.什么是服务注册?
Eureka采用了CS的设计架构,Eureka作为注册服务功能的服务器,它是服务注册中心。而系统中的其他微服务,使用Eureka的客户端连接到Eureka Server并维持心跳连接。这样系统的维护人员就可以通过Eureka Server来监控系统中各个微服务是否正常运行。
在服务注册与发现中,有一个注册中心。当服务器启动的时候,会把当前自己服务器的信息,比如服务地址通讯地址等以别名方式注册到注册总欣赏。另一方(消费者|服务站提供者),以该别名的方式去注册中心你上获取到实际的服务通讯地址,然后再实现本地RPC调用。ROC远程调用框架核心涉及思想:在于注册中心,因为使用注册中心管理每个服务与服务之间的依赖关系(服务治理概念)。在任何rpc远程框架中,都会有一个注册中心(存放服务地址相关信息(接口地址))。
3.eureka包含两个组件
Eureka Server提供服务注册服务:
各个微服务节点通过配置启动后,会在EurekaServer中进行注册,这样EurekaServer中的服务注册表中将会存储所有可用服务节点的信息,服务街店的信息可以在界面中直观看到。
EurekaClient通过注册中心进行访问
是一个java客户端,用户简化Eureka Server的交互,客户端同时也具备一个内置的、使用轮询(round-robin)负载算法的负载均衡器。在应用启动后,将会向Eureka Server发送心跳(默认周期为30秒)。如果Eureka Server在多个心跳周期内没有接收到某个节点的心跳,EurekaServer将会从服务注册表中把这个服务街店移除(默认90秒)
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-eureka-server7001artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-serverartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 7001
eureka:
instance:
hostname: localhsot #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
#设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
@EnableEurekaServer这边作为eureka的服务端,作为服务中心
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class,args);
}
}
加入spring-cloud-starter-netflix-eureka-client依赖,标志其为客户端
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-provider-payment8001artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
添加eureka的配置
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: 123456
devtools:
restart:
enabled: true
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities
添加@EnableEurekaClient注解表示是一个客户端
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
如同支付微服务入驻一样
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-consumer-order80artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
defaultZone: http://localhost:7001/eureka
@SpringBootApplication
@EnableEurekaClient
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
eureka server做的事:
eureka server整个工作流程:
问题:微服务RPC远程服务调用最核心的是什么?
高可用,试想你的注册中心只有一个only one,他出故障就呵呵了,会导致整个服务环境不可用;
解决办法:搭建eureka注册中心集群,实现负载均很+故障容错
eureka集群注册原理:
互相注册,互相守望
C:\Windows\System32\drivers\etc的host文件最后加入以下配置:
# springcloud2020
127.0.0.1 eureka7001.com
127.0.0.1 eureka7002.com
127.0.0.1 eureka7003.com
7001更改:
hostname、defaultZone更改
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
#设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7002.com:7002/eureka/
7002:
server:
port: 7002
eureka:
instance:
hostname: eureka7002.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
#设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
defaultZone: http://eureka7001.com:7001/eureka/
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7002 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7002.class,args);
}
}
修改8001、80的application.yml文件的属性
server:
port: 80
spring:
application:
name: cloud-order-service
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-provider-payment8002artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druid-spring-boot-starterartifactId>
<version>1.1.10version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 8002
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: 123456
devtools:
restart:
enabled: true
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities
直接从8001复制粘贴,改主启动类名字
因为8001和8002的application.yml里面对于应用的名称都是一样的,我们需要在controller里面定义端口serverPort,才能区别他们。
package com.atguigu.springcloud.controller;
import com.atguigu.springcloud.entities.CommonResult;
import com.atguigu.springcloud.entities.Payment;
import com.atguigu.springcloud.service.PaymentService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
/**
* @author pzy
* @ClassName PaymentController
* @Date 2020/5/11 18:36
*/
@RestController
@Slf4j
public class PaymentController {
@Resource
private PaymentService paymentService;
@Value("${server.port}")
private String serverPort;
@PostMapping("/payment/create")
public CommonResult create(@RequestBody Payment payment){
int result = paymentService.create(payment);
if(result>0){
return new CommonResult(200,"插入数据库成功,端口号:"+serverPort,result);
}else{
return new CommonResult(444,"插入数据库失败",null);
}
}
@GetMapping("/payment/get/{id}")
public CommonResult getPaymentById(@PathVariable("id") Long id){
Payment payment = paymentService.getPaymentById(id);
System.out.println("插入成功"+payment);
if(payment != null){
return new CommonResult(200,"查询成功,端口号:"+serverPort,payment);
}else{
return new CommonResult(444,"没有对应记录,查询id:"+id,null);
}
}
}
效果图:
但是!这个时候我们用80访问的话,会发现无论访问多少次,用的都是8001的端口:
将原先写的单机版的改成eureka里面的微服务名称
//public static final String PAYMENT_URL="http://localhost:8001";
public static final String PAYMENT_URL="http://CLOUD-PAYMENT-SERVICE";
重启order80。然后我们访问,会发现还是有问题!
原因是,我们配置了以服务名的方式访问,但不能确定是哪一个服务。
赋予了RestTemplate负载均衡的能力,默认是轮循
@Configuration
public class ApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
加上instance:
instance-id: payment8001,
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/
instance:
instance-id: payment8001
加上prefer-ip-address属性
instance:
instance-id: payment8002
prefer-ip-address: true #访问路径可以显示up地址
对于注册进eureka里面的服务,可以通过服务发现来获得该服务的消息。
引入一个新的内容 DiscoveryClient类
@Resource
private DiscoveryClient discoveryClient;
测试类代码
@GetMapping("/payment/discovery")
public Object discovery(){
List<String> strings = discoveryClient.getServices();
for (String str: strings){
System.out.println("*******elements"+str);
}
List<ServiceInstance> list = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");
for (ServiceInstance info : list){
System.out.println(info.getServiceId()+"\t"+info.getHost()+"\t"+info.getPort()+"\t"+info.getUri());
}
return discoveryClient;
}
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class,args);
}
}
保护迷失主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。
如果在Eureka Server的首页看到以下这段提示,说明Eureka进入了保护模式:
简单的来讲:
某事某刻一个微服务不可用了,Eureka不会李克清理,依旧会对该微服务的信息进行保存。
属于CAP里面的AP分支。
为什么会产生Eureka自我保护机制?
为了防止EurekaClient可以正常运行,但是与EurekaServer网络不通的情况下, EurekaServer不会立刻将EurekaClient服务剔除。
什么是自我保护模式?
这个时候我们只启动7001和8001
server:
port: 7001
eureka:
instance:
hostname: eureka7001.com #eureka服务端实例名称
client:
register-with-eureka: false #表示不向注册中心注册自己
fetch-registry: false #表示自己就是注册中心,我的职责就是维护服务实例,并不区检索服务
service-url:
#设置与Eureka Server交互的地址,查询服务和注册服务都需要依赖这个地址
#defaultZone: http://eureka7002.com:7002/eureka/
#单机就是自己
defaultZone: http://eureka7001.com:7001/eureka/
server:
#关闭自我保护机制,保证不可用服务被及时剔除
enable-self-preservation: false
eviction-interval-timer-in-ms: 2000 #2秒
server:
port: 8001
spring:
application:
name: cloud-payment-service
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/cloud?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: 123456
devtools:
restart:
enabled: true
eureka:
client:
#表示是否将自己注册进EurekaServer 默认为true
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetch-registry: true
service-url:
#defaultZone: http://localhost:7001/eureka
defaultZone: http://eureka7001.com:7001/eureka/, http://eureka7002.com:7002/eureka/
instance:
instance-id: payment8001
prefer-ip-address: true #访问路径可以显示up地址
#Eureka客户端向服务端发送心跳的时间间隔,默认是30秒
lease-renewal-interval-in-seconds: 1
#Eureka服务端在收到最后一次心跳后等待的时间上限,默认为90秒,超时将剔除服务
lease-expiration-duration-in-seconds: 2
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.atguigu.springcloud.entities
zookeeper是一个分布式协调工具,可以实现注册中心功能。
<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>cloud2020-version01artifactId>
<groupId>com.atguigu.springcloudgroupId>
<version>1.0-SNAPSHOTversion>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud-provider-payment8004artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
project>
server:
port: 8004
spring:
application:
name: cloud-provider-payment
cloud:
zookeeper:
connect-string: 172.20.10.3:2181
@EnableDiscoveryClient该注解用于向使用consul或者zookeeper作为注册中心时注册服务
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8004 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8004.class,args);
}
}
@RestController
@Slf4j
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/zk")
public String getZookeeper(){
return "springcloud with zookeeper:"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
因为我这边是在虚拟机上安装了docker,并在docker里面安装了zookeeper,所以需要以下命令才能进入zookeeper,这边做个记录:
1.docker exec -it dk01(zookeeper的别名) bash
2.使用命令 ./bin/zkServer.sh status 来查看节点的状态
cd bin
ls
zkServer.sh status
3.使用zkCli.sh开启客户端
zkCli.sh
4.ls /services 查看现在入驻的服务
5.ls /services/cloud-provider-payment查看流水号
6.ls /services/cloud-provider-payment/流水号
7.get /services/cloud-provider-payment/流水号
当我们停掉payment8004微服务后,我们在虚拟机上的zookeeper会剔除掉这个微服务么?答案是肯定的,只不过会等待一段时间才会,所以这个服务节点在zookeeper里面是个临时节点。
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-zookeeper-discoveryartifactId>
<exclusions>
<exclusion>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.apache.zookeepergroupId>
<artifactId>zookeeperartifactId>
<version>3.4.14version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 80
spring:
application:
name: cloud-consumer-order
cloud:
zookeeper:
connect-string: 172.20.10.3:2181
@SpringBootApplication
@EnableDiscoveryClient
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
@Configuration
public class MyApplicationContextConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@RestController
public class OrderZkController {
public static final String INVOKE_URL="http://cloud-provider-payment";
@Resource
RestTemplate restTemplate;
@GetMapping("/consumer/payment/zk")
public String getPaymentInfo(){
String result =restTemplate.getForObject(INVOKE_URL+"/payment/zk",String.class);
return result;
}
}
简介:
consul是一套开源的分布式服务发现和配置管理系统,由HashiCorp公司用GO语言开发。
提供了微服务系统中的服务治理、配置中心、控制总线等功能。这些功能中的每一个都可以根据需要单独使用,也可以一起使用以构建全方位的服务网络,总之Consul提供了扬中完整的服务网格解决方案。
能干嘛?
进入到安装包的盘符,cmd命令:
先配置系统环境变量,在path里面添加包的路径;
consul --version 查看版本
consul agent -dev 使用开发模式启动
然后可以访问 localhost:8500访问可视化界面
<dependencies>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 8006
spring:
application:
name: cloud-provider-payment
###consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname:127.0.0.1
service-name: ${spring.application.name}
@SpringBootApplication
@EnableDiscoveryClient
public class PaymentMain8006 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8006.class,args);
}
}
@RestController
public class PaymentController {
@Value("${server.port}")
private String serverPort;
@RequestMapping("/payment/consul")
public String getConsul(){
return "springcloud with consul:"+serverPort+"\t"+ UUID.randomUUID().toString();
}
}
<dependencies>
<dependency>
<groupId>com.atguigu.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-consul-discoveryartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
server:
port: 80
spring:
application:
name: cloud-consumer-payment
###consul注册中心地址
cloud:
consul:
host: localhost
port: 8500
discovery:
#hostname:127.0.0.1
service-name: ${spring.application.name}
@SpringBootApplication
@EnableDiscoveryClient
public class OrderConsulMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderConsulMain80.class,args);
}
}
@Configuration
public class MyApplicationConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
@RestController
public class OrderConsulController {
public static final String INVOKE_URL="http://cloud-provider-payment";
@Resource
RestTemplate restTemplate;
@GetMapping("/consumer/payment/consul")
public String getPaymentInfo(){
String result =restTemplate.getForObject(INVOKE_URL+"/payment/consul",String.class);
return result;
}
}
三个注册中心的异同
所谓的CAP:
C:Consistency(强一致性)
A:Availability(可用性)
P:Partition tolerance(分区错容性)
CAP理论关注粒度是数据,而不是整体系统设计的策略
我们的系统因为要保证分区错容性,所以要么是CP、要么是AP;
AP:高可用
CP:数据一致
CAP理论的核心是:一个分布式系统不可能同同时很好的满足一致性、可用性和分区错容性这三个需求,因此,根据CAP原理将NoSQL数据库分成了满足CA原则、满足CP原则和满足AP原则三大类:
Ribbon概念
Spring Cloud Ribbon是基于Netflix洗Ribbon实现的一套客户端。 负载均衡的工具
主要功能是 提供客户端的软件负载均衡算法和服务调用。 Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单地说,就是在配置文件中列出LoadBalancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们很容易使用Ribbon实现自定义的负载均很算法。
LB概念
LB负载均衡(Load Balance)是什么?
简单地说就是将用户的请求平摊的分配到多个服务商,从而达到系统的HA(高可用)。常见的负载均很软件有Ngix,LVS,硬件F5等。
Ribbon本地负载均衡客户端VS Nginx服务端负载均衡区别?
Nginx是服务器负载均衡,客户端所有请求都会交给nginx,然后由nginx实现转发请求。即负载均衡是由服务端实现的。属于集中式LB
Ribbon本地负载均衡,在调用微服务接口时候,会在注册中心获取注册信息服务列表之后缓存到JVM本地,从而在本地实现RPC远程服务调用技术。属于进程内LB
ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和Eureka结合只是其中的一个实例。
Ribbon在工作时分成两步
我们再回到8001、8002、7001、7002、80,项目,我们发现我们的pom文件里面没有搜到ribbon相关的依赖,这是为什么呢?
因为spring-cloud-starter-netflix-eureka-client这个依赖里面已经包含了ribbon
二讲RestTemplate
getForObject和getForEntity
order添加对getForEntity()测试方法
@GetMapping("/consumer/payment/getForEntity/{id}")
public CommonResult<Payment> getPaymentEntity(@PathVariable("id")Long id){
ResponseEntity<CommonResult> resultResponseEntity = restTemplate.getForEntity(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
if(resultResponseEntity.getStatusCode().is2xxSuccessful()){
return resultResponseEntity.getBody();
}else {
return new CommonResult<>("444","操作失败");
}
}
返回结果跟下面的getForObject一样
@GetMapping("/consumer/payment/get/{id}")
public CommonResult getPayment(@PathVariable("id")Long id){
return restTemplate.getForObject(PAYMENT_URL+"/payment/get/"+id,CommonResult.class);
}
Ribbon核心组件IRule:根据特定算法从服务列表中选取一个要访问的服务
==这个自定义配置类不能放在@ComponentScan所扫描的当前包下以及子包下!==否则我们自定义的这个配置类就会被所有的Ribbon客户端所共享,达不到特殊化定制的目的。
@Configuration
public class MySelfRule {
@Bean
public IRule myrule(){
return new RandomRule(); //定义为随机
}
}
3)主启动类加上注解@RibbonClient
告诉启动类指定针对哪个服务 进行负载均衡,然后使用哪个自定义的规则
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "cloud-payment-service",configuration = MySelfRule.class)
public class OrderMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderMain80.class,args);
}
}
OpenFeign是什么?
Feign是一个声明式的WebService客户端。使用Feign能让编写Web Service客户端更加简单。
它的使用方法是定义一个服务接口然后在上面添加注解。Feign也支持可拔插式的编码器和解码器。Spring Cloud对Feign进行了封装,使其支持了Spring MVC标准注解和HTTPMessageConverters。Feign可以与Eureka和Ribbon组合使用以支持负载均衡。
能干什么?
Feign旨在使编写java Http客户端变得更容易。
前面在使用Ribbon+RestTemplate时,利用RestTemplate对http请求的封装处理,形成了一套模板化的调用方法。但是在实际开发中,由于对服务以来的调用可能不止一处,==往往一个接口会被多出调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。==所以,Feign在此基础上做了进一步封装,由他来帮助我们定义和实现依赖服务接口的定义。在Feign的是现在,我们只需创建一个借口并使用注解的方式来配置它(以前是Dao接口上面标注Mapper注解,现在是一个微服务接口上面标注一个Feign注解即可),即可完成对服务提供方的接口绑定,简化了使用Spring cloud Ribbon时,自动封装服务调用客户端的开发量。
Feign继承了Ribbon
利用Ribbon维护了Payment的服务列表信息,并且通过轮询实现了客户端的负载均衡。而与Ribbon不同是的是,==通过Feign只需要定义服务绑定接口且以声明式的方法,==优雅而简单的实现了服务调用。
feign用于消费侧
在这之前我们用的是Ribbon+RestTemplate结合,现在是OpenFeign,如图:
<dependencies>
<dependency>
<groupId>com.eiletxie.springcloudgroupId>
<artifactId>cloud-api-commonsartifactId>
<version>${project.version}version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-openfeignartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-actuatorartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
dependencies>
我们可以看到OpenFeign里面就整合了Ribbon,所以就有了Ribbon的负载均衡功能。
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
#服务注册中心集群
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
新的注解@EnableFeignClients:使用feign并开启
@SpringBootApplication
@EnableFeignClients
public class FeignOrderMain80 {
public static void main(String[] args) {
SpringApplication.run(FeignOrderMain80.class,args);
}
}
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE")
public interface PaymentFeignService {
@GetMapping("/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id);
}
@FeignClient标注去eureka上使用什么微服务接口,映射地址实际上为8001的接口对外暴露的接口地址
这边的接口需要用@Component标注,可用@Service替代;
@Component, @Repository, @Service的区别:
在Spring2.0之前的版本中,@Repository注解可以标记在任何的类上,用来表明该类是用来执行与数据库相关的操作(即dao对象),并支持自动处理数据库操作产生的异常
在Spring2.5版本中,引入了更多的Spring类注解:@Component,@Service,@Controller。@Component是一个通用的Spring容器管理的单例bean组件。而@Repository, @Service, @Controller就是针对不同的使用场景所采取的特定功能化的注解组件。
因此,当你的一个类被@Component所注解,那么就意味着同样可以用@Repository, @Service, @Controller来替代它,同时这些注解会具备有更多的功能,而且功能各异。
最后,如果你不知道要在项目的业务层采用@Service还是@Component注解。那么,@Service是一个更好的选择。
@RestController
public class OrderFeignController {
@Resource
PaymentFeignService paymentFeignService;
@GetMapping("/consumer/payment/get/{id}")
public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id){
return paymentFeignService.getPaymentById(id);
}
}
如果我们在微服务提供者中故意写一个等待3秒的接口程序,然后消费者来调用,这个时候OpenFeign就会报错:
8001提供者接口代码:
@GetMapping("/payment/feign/timeout")
public String paymentFeignTimeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(3);
return serverPort;
}
报错信息 读取超时:
默认Feign客户端只等待一秒钟,但是服务端处理需要超过1瞄准,导致Feign客户端不想等待了,直接报错。为了避免这样的情况,有时候我们需要设置Feign客户端的超时控制。
这个时候我们只需要增加application.yml里面的配置即可:
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
#服务注册中心集群
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
#设置feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
Feign提供了日志打印功能,我们可以通过配置来调整日志级别,从而了解Feign中Http请求的细节。
说白了就是对Feign接口的调用情况进行监控和输出
日志级别
1)添加一个配置类:
package com.atguigu.springcloud.config;
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author pzy
* @ClassName FeignConfig
* @Date 2020/5/14 16:02
*/
@Configuration
public class FeignConfig {
@Bean
Logger.Level feignLoggerLevel(){
return Logger.Level.FULL;
}
}
2)yml文件配置 添加对日志级别的设置
```markup
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
#服务注册中心集群
defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
#设置feign客户端超时时间(OpenFeign默认支持Ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络正常的情况下,两端连接所用的时间
ReadTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ConnectTimeout: 5000
logging:
level:
#feign日志以什么级别监控哪个接口
com.atguigu.springcloud.service.PaymentFeignService: debug