一.新建父级工程
1.New—>Project
2.选择对应的JDK, default——>Next
3.输入项目组Group:com.xxx;组件名称Artifact:xxxx;Type:选择Maven Project;修改自动生成的Package——>Next
4.选择你需要的Spring Boot版本,其他的先不选——>Next
5.Project Name工程名称,和组件名称Artifact一样;Project location:设置项目文件存放目录——>Finish
6.父级工程创建完成后,删除src目录
7.修改pom文件
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--申明此项目为springboot项目-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>microservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>microservice</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
二.搭建注册中心模块microservice-center(eureka server)
服务注册、发现模块。
1.microservice-center
(1)File—>New—>Module…
(2)选择对应的JDK, default——>Next
(3)输入项目组Group:com.xxx;组件名称Artifact:xxxx;Type:选择Maven Project;修改自动生成的Package——>Next
(4)Dpendencies选择Spring Cloud Discovery—>Eureka Server,选择你需要的Spring Boot版本——>Next
(5)Project Name工程名称一般不做修改,和组件名称Artifact一样;Content root、Module file location 均按自动生成,不做修改——>Finish
(6)向MicroserviceCenterApplication添加注解@EnableEurekaServer表明是一个eureka服务
(7)将application.properties修改为application.yml(重命名快捷键Shift+F6),修改完成后加入以下配置
server:
port: 8101 #指定该Eureka实例的端口
eureka:
instance:
hostname: localhost #设置当前实例的主机名称
client:
registerWithEureka: false #禁止注册自身
fetchRegistry: false #因为该服务没有注册到其他注册中心,所以关闭从注册中心拉取服务列表。
serviceUrl: #服务注册中心地址
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
#eureka是一个高可用的组件,它没有后端缓存,每一个实例注册之后需要向注册中心发送心跳(因此可以在内存中完成)
#通过eureka.client.registerWithEureka:false和fetchRegistry:false来表明自己是一个eureka server.
#
(8)修改pom文件
使用父工程spring-cloud的spring boot依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>microservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>microservice-center</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>microservice-center</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
(9)启动microservice-center,运行,在浏览器访问:http://localhost:8101/,可以看到注册的服务。
2.创建microservice-project(eureka-client)
(1)创建过程同上一样,只是第四步选择Eureka Discovery Client——>Next
(2)MicroserviceProjectApplication中加入以下两个注解
@EnableEurekaClient
@RestController
@SpringBootApplication
@EnableEurekaClient
@RestController
public class MicroserviceProjectApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceProjectApplication.class, args);
}
}
(3)application.yml配置
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8101/eureka/
server:
port: 8102
spring:
application:
# 此处名称最好写项目名称,便于识别服务提供者
name: microservice-project
(4)在启动类上写个接口作为测试
@SpringBootApplication
@EnableEurekaClient
@RestController
public class MicroserviceProjectApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceProjectApplication.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/chen")
public String home(@RequestParam String name) {
return "chen "+name+",i am from port:" +port;
}
}
(5)启动microservice-center,在启动microservice-project,在浏览器上运行:http://localhost:8102/chen?name=forezp,返回接口访问内容
三.ribbon+restTemplate-服务消费者
a.在微服务架构中,业务都会被拆分成一个独立的服务,服务与服务的通讯是基于http restful的。 Spring cloud有两种服务调用方式,一种是ribbon+restTemplate,另一种是feign。
b.ribbon是一个负载均衡客户端,可以很好的控制htt和tcp的一些行为。Feign默认集成了ribbon。
1.基于以上项目复制microservice-project创建microservice-project02并修改yml中端口为8103并启动
spring.application.name不修改,仍为microservice-project: 由于服务之间是根据此名称进行相互调用,所以此时表示 8102,8103对外提供一个服务,服务名为microservice-project=====>等同于一个小的集群
2.创建一个服务消费者microservice-ribbon
1.创建步骤
- File—>New—>Module…
- 选择Spring Initializr,选择对应的JDK, default——>Next
- 输入项目组Group:com.xxx;组件名称Artifact:xxxx;Type:选择Maven Project;修改自动生成的Package——>Next
- Dpendencies选择Spring Cloud Discovery—>Eureka Server,选择你需要的Spring Boot版本——>Next
- Project Name工程名称一般不做修改,和组件名称Artifact一样;Content root、Module file location 均按自动生成,不做修改——>Finish
2.启动类中添加注解和方法
- @EnableDiscoveryClient
- 通过LoadBalanced注解表明这个restRemplate开启负载均衡的功能
@SpringBootApplication
@EnableDiscoveryClient
public class MicroserviceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceRibbonApplication.class, args);
}
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
- 编写测试类controller和service,主要调用是因为service中的RestTemplate
microservice-ribbon结构如下:
HelloRibbonService
package com.example.microserviceribbon.service;
public interface HelloRibbonService {
public String chenService(String name);
}
HelloRibbonServiceImpl
package com.example.microserviceribbon.service.impl;
import com.example.microserviceribbon.service.HelloRibbonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloRibbonServiceImpl implements HelloRibbonService {
@Autowired
RestTemplate restTemplate;
@Override
public String chenService(String name) {
//使用注册到Eureka服务中心的客户端,由客户端分配具体调用哪个服务
return restTemplate.getForObject("http://microservice-project/chen?name="+name,String.class);
}
}
HelloRibbonController
package com.example.microserviceribbon.controller;
import com.example.microserviceribbon.service.impl.HelloRibbonServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloRibbonController {
@Autowired
HelloRibbonServiceImpl helloRibbonService;
@RequestMapping(value = "/chen")
public String hi(@RequestParam String name){
return helloRibbonService.chenService(name);
}
}
- 一次运行center、project、project02、ribbon,在浏览器上多次访问http://localhost:8104/陈?name=Chen Houbo,浏览器交替显示:“chen Chen Houbo,i am from port:8102”,“chen Chen Houbo,i am from port:8103”;这说明我们通过调用restTemplate.getForObject(“http://microservice-project/chen?name=”+name,String.class)方法时,已经做了负载均衡,访问了不同端口的服务实例
场景总结:
1.一个服务注册中心,microservice-center端口为8101
2.向服务注册中心microservice-center注册两个客户端服务:名称为microservice-project包含服务:8102/8103
3.向服务之策中心microservice-center注册消费者服务:microservice-ribbon端口:8104
4.当microservice-ribbon通过restTemplate调用microservice-project的chen接口时,因为用了ribbon负载均衡,会轮流的调用microservice-project:8102和8103两个端口的chen接口;
四.Feign-服务消费者
a.Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。
使用Feign,只需要创建一个接口并注解。它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。
Feign支持可插拔的编码器和解码器。Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果
b.简单理解:
Feign 采用的是基于接口的注解
Feign 整合了ribbon
1.创建一个服务消费者microservice-feign
(1)指定端口
(2)pom文件中加入feign依赖入
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>microservice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.example</groupId>
<artifactId>microservice-feign</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>microservice-feign</name>
<description>Demo project for Spring Boot</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<!--高版本feign依赖============================-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>
(3)启动类中加入@EnableFeignClients注解开启Feign的功能(@EnableDiscoveryClient和@EnableFeignClients)
package com.example.microservicefeign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
@EnableDiscoveryClient
public class MicroserviceFeignApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceFeignApplication.class, args);
}
}
(4)编写测试类service和controller,feign基于接口——创建serviceTest
microservice-feign解构如下:
HelloFeignService
package com.example.microservicefeign.service;
import com.example.microservicefeign.service.impl.HelloFeignServiceImpl;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient(value = "eureka-client-01", fallback = HelloFeignServiceImpl.class)
public interface HelloFeignService {
@GetMapping(value = "/chen")
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
HelloFeignServiceImpl
package com.example.microservicefeign.service.impl;
import com.example.microservicefeign.service.HelloFeignService;
public class HelloFeignServiceImpl implements HelloFeignService {
@Override
public String sayHiFromClientOne(String name) {
return "hello"+name;
}
}
HelloFeignController
package com.example.microservicefeign.controller;
import com.example.microservicefeign.service.HelloFeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloFeignController {
@Autowired
private HelloFeignService helloFeignService;
@GetMapping(value = "chen")
public String sayHi(@RequestParam String name){
return helloFeignService.sayHiFromClientOne(name);
}
}
(5)浏览器上多次访问http://localhost:8105/chen?name=Feign,浏览器交替显示:“chen Feign,i am from port:8102”,“chen Feign,i am from port:8103”;
五.断路器(Hystrix)
1.微服务架构中,根据业务分析来拆分一个个的服务,服务与服务之间可以相互调用(RPC)在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。为了解决这个问题,业界提出了断路器模型。
2.Netflix开源了Hystrix组件,实现了断路模式,SpringCloud对这一组件进行了整合。
在微服务架构中,一个请求需要调用多个服务是非常常见的,较底层的服务如果出现故障,会导致连锁故障。当对特定的服务调用的不可用达到一个阈值(Hystrix是5秒20次断路器将会被打开。断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值)。
3.Ribbon使用断路器
(1)添加断路器依赖:spring-cloud-starter-netflix-hystrix如果springboot是2.0及以上的新版本还需要加入一个依赖
(2)启动类加注解@EnableHystrix开启Hystrix(@EnableDiscoveryClient和@EnableHystrix)
(3)改造HelloRibbonServiceImpl类,在chenService方法上加上@HystrixCommand注解
注意: 该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法, 熔断方法直接返回了一个字符串,字符串为“chen,”+name+“,sorry,error!”,
package com.example.microserviceribbon.service.impl;
import com.example.microserviceribbon.service.HelloRibbonService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class HelloRibbonServiceImpl implements HelloRibbonService {
@Autowired
RestTemplate restTemplate;
@Override
@HystrixCommand(fallbackMethod="chenError")
public String chenService(String name) {
//使用注册到Eureka服务中心的客户端,由客户端分配具体调用哪个服务
return restTemplate.getForObject("http://microservice-project/chen?name="+name,String.class);
}
public String chenError(String name){
return "chen," + name +"sorry,error!";
}
}
(4)断路测试
- 启动ribben访问
- 关闭microservice-project,再访问,会得到短路由返回值
- microservice-project工程不可用的时候,microservice-ribbon调用 microservice-chen的API接口时,会执行快速失败,直接返回一组字符串,而不是等待响应超时,这很好的控制了容器的线程阻塞
4.Feign中使用断路器
(1)Feign是自带断路器的,在D版本的Spring Cloud中,它没有默认打开。需要在配置文件中配置打开:feign.hystrix.enabled=true
(2)在@FeignClient接口的注解中加上fallback的指定类HelloFeignServiceImpl
(3)HelloFeignServiceImpl需要实现HelloFeignService接口,并注入到Ioc容器中(组件注解)
(4)断路测试-同上
5.Hystrix Dashboard (断路器:Hystrix 仪表盘)
–仪表盘添加+ribbon和feign相同
–以ribbon为例:
(1)添加依赖
<!--添加断路器-仪表盘依赖==========-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
(2)主程序启动类中
- 加入@EnableHystrixDashboard注解,开启hystrixDashboard
- 在spring版本2.0以上需要注入一个servlet(启动类中)
//仪表盘注解@EnableHystrixDashboard
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/actuator/hystrix.stream");//访问该页面就是监控页面
return registrationBean;
}
(3)访问http://localhost:8104/hystrix