SpringCloud微服务框架搭建
以上是我的springcloud项目,eureka是注册中心,ribbon和feign都是cloud的rpc远程调用。
首先先创建空项目Empty Project
一、注册中心Eureka
1.创建基于web的Maven项目(springcloud)
2.创建服务注册中心。在springcloud项目中创建SpringBoot项目(springboot):
选中项目右击–>New–>Module
勾选Cloud Discovery–>Eureka server。以方便导包。
3.编写springboot项目
3.1 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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cy</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <name>eureka-server</name> <description>Eureka Server project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</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> </project>
网上很多用spring cloud starter eureka server 但是官方弃用了,这里使用 spring-cloud-starter-netflix-eureka-server
3.2修改application.yml文件
server: port: 8761 eureka: instance: hostname: localhost client: registerWithEureka: false fetchRegistry: false serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka
registerWithEureka 和 fetchRegistry 设置为false,表明自己属于服务中心主体
为什么要用yml文件?而不是xml或properties文件?
说明:xml或properties文件都是以标签来配置数据,这样就造成了资源的浪费,不利于我们开发大型项目,而yml(yaml)文件则取消了这样的配置,文件中都是以数据为中心,层级结构以空格作为层级分隔符。使数据得到突出,简化了开发,同时占用的资源也更少。
3.3编写启动类SpringbootApplication.java
@SpringBootApplication @EnableEurekaServer public class EurekaServerApplication { public static void main(String[] args) { SpringApplication.run(EurekaServerApplication.class, args); } }
添加@EnableEurekaServer,该注解表明标注类是一个Eureka Server。
4.启动项目
在浏览器中输入http://localhost:8761/
二、 服务提供者
创建一个服务提供者 (eureka client),当client向server注册时,它会提供一些元数据,例如主机和端口,URL,主页等。Eureka server 从每个client实例接收心跳消息。 如果心跳超时,则通常将该实例从注册server中删除。1 创建项目service-support
略,和上文相似,pom文件和yml配置见下文
2、配置
2.1 引入maven依赖
<?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>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cy</groupId> <artifactId>service-support</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-support</name> <description>Service Support project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</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> </project>
2.2 application.yml配置
server: port: 8762 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ spring: application: name: service-support
2.3 加注解@EnableEurekaClient
表明自己是一个eurekaclient.
@SpringBootApplication @EnableEurekaClient public class ServiceSupportApplication { public static void main(String[] args) { try{ SpringApplication.run(ServiceSupportApplication.class, args); }catch (Exception e){ e.printStackTrace(); } } }
2.4 创建controller
@RestController public class TestController { @Value("${server.port}") String port; @RequestMapping(value = "/support",method = RequestMethod.GET) public String home(@RequestParam String name) { return "hi " + name + ",i am from port:" + port; } }
2.5演示效果
需要指明spring.application.name,这个很重要,这在以后的服务与服务之间相互调用一般都是根据这个name 。 启动工程,打开http://localhost:8761 ,即eureka server 的网址
这时打开 http://localhost:8762/support?name=forezp ,你会在浏览器上看到 :
hi forezp,i am from port:8762
三、 服务消费者(Feign)
什么是Feign
Feign是一个声明式的伪Http客户端,它使得写Http客户端变得更简单。使用Feign,只需要创建一个接口并注解。
它具有可插拔的注解特性,可使用Feign 注解和JAX-RS注解。Feign支持可插拔的编码器和解码器。
Feign默认集成了Ribbon,并和Eureka结合,默认实现了负载均衡的效果。
简而言之:
Feign 采用的是基于接口的注解
Feign 整合了ribbon
1准备工作
启动eureka-server,端口为8761; 启动service-support 两次,端口分别为8762 、8773.
2新建一个spring-boot工程,
取名为serice-feign,代码如下:
2.1 编辑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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cy</groupId> <artifactId>service-feign</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-feign</name> <description>Service-feign project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <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> </project>
2.2 application.yml配置
在工程的配置文件application.yml文件,指定程序名为service-feign,端口号为8765,服务注册地址为http://localhost:8761/eureka/ ,代码如下:
server: port: 8765 eureka: client: serviceUrl: defaultZone: http://localhost:8761/eureka/ spring: application: name: service-feign
2.3定义一个feign接口
/**绑定service-support服务,调用该服务的support方法*/ @FeignClient(value = "service-support") public interface SchedualServiceHi { @RequestMapping(value = "/support", method = RequestMethod.GET) String sayHiFromClientOne(@RequestParam(value = "name") String name); }
2.4一个”/hi”的API接口
@RestController public class HiController { @Autowired SchedualServiceHi schedualServiceHi; @RequestMapping(value = "/hi",method = RequestMethod.GET) public String sayHi(@RequestParam String name){ return schedualServiceHi.sayHiFromClientOne(name); } }
2.5启动方式
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class ServiceFeignApplication { public static void main(String[] args) { SpringApplication.run(ServiceFeignApplication.class, args); } }
2.6演示效果
启动程序,多次访问http://localhost:8765/hi?name=forezp(http://localhost:8765/hi?name=forezp),浏览器交替显示:
hi forezp,i am from port:8762 hi forezp,i am from port:8763
四、Ribbon
1、创建springboot项目,命名service-ribbon
2、编辑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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.4</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.cy</groupId> <artifactId>service-ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <name>service-ribbon</name> <description>Service-ribbon project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>2021.0.1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <!-- <dependency>--> <!-- <groupId>org.springframework.cloud</groupId>--> <!-- <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>--> <!-- <version>2.2.6.RELEASE</version>--> <!-- </dependency>--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.1.0.RELEASE</version> </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> </project>
3、yml文件
server: port: 8764 spring: application: name: service-ribbon eureka: client: registerWithEureka: true fetchRegistry: true serviceUrl: defaultZone: http://localhost:8761/eureka/
4、启动类
@SpringBootApplication @EnableDiscoveryClient public class ServiceRibbonApplication { public static void main(String[] args) { SpringApplication.run(ServiceRibbonApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
5、controller和service
@RestController public class HelloControler { @Autowired HelloService helloService; @RequestMapping(value = "/hi",method = RequestMethod.GET) public String hi(@RequestParam String name) { return helloService.hiService(name); } } @Service public class HelloService { @Autowired RestTemplate restTemplate; public String hiService(String name) { return restTemplate.getForObject("http://service-support/"+"support?name=" + name, String.class); } }
6、访问
踩坑记录:配置负载均衡ribbon
,在加入@LoadBalanced
启动后就报错No instances available for service-support
spring-cloud-starter-netflix-eureka-client 3.0版本的已经内置ribbon了,所以我们导入的spring-cloud-starter-netflix-ribbon会与之冲突,这里去掉spring-cloud-starter-netflix-ribbon就解决了
参考博客:https://www.cnblogs.com/wangpaipilot/articles/14372787.html
五、Hystrix断路器
在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
为了解决这个问题,业界提出了断路器模型。
7.1 什么是Hystrix
Netflix开源了Hystrix组件,实现了断路器模式,SpringCloud对这一组件进行了整合。 在微服务架构中,一个请求需要调用多个服务是非常常见的,如下图:
较底层的服务如果出现故障,会导致连锁故障。当对特定的服务的调用的不可用达到一个阀值(Hystric 是5秒20次) 断路器将会被打开。
断路打开后,可用避免连锁故障,fallback方法可以直接返回一个固定值。
准备工作
这篇文章基于上一篇文章的工程,首先启动上一篇文章的工程,启动eureka-server 工程;启动service-hi工程,它的端口为8762。
在ribbon使用断路器
改造serice-ribbon 工程的代码,首先在pox.xml文件中加入spring-cloud-starter-hystrix的起步依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.1.0.RELEASE</version> </dependency>
改造service
改造HelloService类,在hiService方法上加上@HystrixCommand注解。该注解对该方法创建了熔断器的功能,并指定了fallbackMethod熔断方法,熔断方法直接返回了一个字符串,字符串为”hi,”+name+”,sorry,error!”,代码如下:
@Service
public class HelloService {
@Autowired
RestTemplate restTemplate;
//断路器,远程调用失败的回调函数
@HystrixCommand(fallbackMethod = "hiError")
public String hiService(String name)
{
return restTemplate.getForObject("http://service-support/"+"support?name=" + name, String.class);
}
public String hiError(String name) {
return "hi," + name + ",sorry,error!";
}
}
在启动类上加入
@SpringBootApplication @EnableDiscoveryClient @EnableHystrix //断路器 public class ServiceRibbonApplication { public static void main(String[] args) { SpringApplication.run(ServiceRibbonApplication.class, args); } @Bean @LoadBalanced RestTemplate restTemplate() { return new RestTemplate(); } }
把service-support服务停掉,然后测试