所有好走的路都是下坡路,等你意识到错了,已经回不了头。
Spring Cloud
SpringCloud是一套微服务开发一站式解决方案,它提供了微服务开发所需的很多功能组件,比如服务统一管理,配置统一管理,路由网关,断路器,事件总线,集群状态配置等等,而且springcloud与springboot无缝衔接,
配合springboot能够更轻松的搭配出一套架构平台。
微服务
微服务其实是一种架构的设计风格,并没有明确的技术绑定和架构概念。简单来说,微服务架构风格就是将原来的单一架构开发为一组小型服务(微服务)的方法,每个服务都运行在自己独立的进程中(服务间的隔离),服务间采用轻量级的通信机制(http,rpc-netty,webservice),这些服务按照业务拆分,并且独立部署(自动化部署),服务会有一个统一的管理中心管理(zookeeper,eureka, nacos),服务可以采用不同的语言开发,并且使用不同的存储技术(数据库垂直拆分)。
1、eureka注册中心的搭建
- 创建父工程
继承springboot,springcloud
- 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>
<packaging>pom</packaging>
<modules>
<module>springclud_eureka</module>
<module>springcloud_student</module>
<module>springcloud_class</module>
<module>springcloud_teacher</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.yuxi</groupId>
<artifactId>springcloud_boot</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springcloud_boot</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR5</spring-cloud.version>
</properties>
<!-- <dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</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>
- 创建eureka微服务模块
创建一个maven工程,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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud_boot</artifactId>
<groupId>com.yuxi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springclud_eureka</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 编写启动类,注意多了一个注解
package com.yuxi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Administrator
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
- 配置文件application.yml
server:
port: 20000
spring:
application:
# 配置为服务的名称,以后的监控中心可以看到
name: EurekaService
eureka:
client:
service-url:
defaultZone: http://localhost:20000/eureka
#当前配置的是监控中心, 没有必要抓取服务
fetch-registry: false
# 是否自我注册
register-with-eureka: false
2、eureka客户端student服务模块的搭建
- 创建一个Maven工程,继承springcloud_demo工程。
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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud_boot</artifactId>
<groupId>com.yuxi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud_student</artifactId>
<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>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
</dependencies>
<!--打包插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- 配置启动类
package com.yuxi;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author Administrator
*
* @EnableEurekaClient 在使用spring cloud2.x之后的版本,该注解可以省略
*/
@SpringBootApplication
@EnableEurekaClient
public class StudentApplication {
public static void main(String[] args) {
SpringApplication.run(StudentApplication.class,args);
}
/**
* 创建一个RestTemplate实例交给spring容器去管理,实现的是远程调用
* @return 返回一个实例
*/
@Bean
@LoadBalanced
public RestTemplate getRest(){
return new RestTemplate();
}
/**
* 配置的是负载均衡的策略,被调用方集群的时候可以用该算法来选择服务对象
* @return
*/
@Bean
public IRule getRule(){
return new RandomRule();
}
}
- 配置application.yml文件
server:
port: 8080
spring:
application:
# 不可以用下划线
name: micro-Student
eureka:
client:
service-url:
defaultZone: http://localhost:20000/eureka
- 编写微服务的功能接口(根据微服务本身的业务不同,编写不同的接口)
package com.yuxi.contorller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
@RestController
@RequestMapping("/stu")
public class StudentController {
@Resource
private RestTemplate restTemplate;
/**
* @ResponseBody ,将方法的返回值,放入response相应体中
* @param id
* @return
*/
@RequestMapping("/name")
public String getNameById(int id){
String s = restTemplate.getForObject("http://micro-class/cla/clazz?id="+id,String.class);
switch (id){
case 1:
return "zhangsan" + s;
case 2:
return "lisi" + s;
default:
return "hello";
}
}
}
3、服务的调用-Ribbon + RestTemplate(独立的组件)
- Ribbon
Ribbon是一个客户端的负载均衡器,用来进行springcloud之间微服务负载均衡调用
- 负载均衡的方式
1)客户端负载均衡
2)服务端负载均衡
2.1)网络4层负载
2.2)网络7层负载 - nginx
4、Ribbon+RestTemplate的使用(class微服务模块)
- 在创建一个班级的微服务,用于后续的调用
- 在主动的调用方(学生服务 -> 班级服务,学生服务就是主动调用方)添加依赖:
<!-- 添加ribbon依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
- 在调用方声明RestTemplate组件
@Bean
@LoadBalanced//负载均衡
public RestTemplate getRest(){
return new RestTemplate();
}
- 在调用方编写服务调用代码
String s = restTemplate.getForObject("http://micro-class/cla/clazz?id="+id,String.class);
- 被调用方class微服务的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud_boot</artifactId>
<groupId>com.yuxi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud_class</artifactId>
<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.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- class服务的启动类(
@EnableFeignClients(basePackages = "com.yuxi.feign")
注解用于后续的技术,目前不用)
package com.yuxi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Administrator
*/
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients(basePackages = "com.yuxi.feign")
public class ClassApplication {
public static void main(String[] args) {
SpringApplication.run(ClassApplication.class,args);
}
}
- class模块的application.yml文件
server:
port: 8081
spring:
application:
name: micro-class
eureka:
client:
service-url:
defaultZone: http://localhost:20000/eureka
# 配置feign的超时和重连
# Feign自带超时和重试的功能,但是默认是关闭的。所以我们看到的超时和重试的效果,是底层Ribbon提供的。
# 如果开启了Feign的超时重试就会覆盖掉Ribbon的设置
ribbon:
# 连接超时
ConnectTimeout: 1000
#读取超时
ReadTimeout: 1000
#配置ribbon的重连次数,默认是0
MaxAutoRetries: 0
#配置ribbon的下个服务的重试次数,默认是1
MaxAutoRetriesNextServer: 0
#配置Feign的超时,一旦配置了Feign的超时,ribbon的重试和超时会全部失效
feign:
client:
config:
MIRCO-TEACHER: # (可变选项,可以定位)可以针对不同的微服务配置不同的超时时间
connectTimeout: 1000
readTimeout: 6000
MIRCO-STUDENT:
connectTimeout: 1000
readTimeout: 2000
- 编写class模块的数据接口(TeaFeign是以后用的,目前忽略)
package com.yuxi.controller;
import com.yuxi.feign.TeaFeign;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
/**
* @author Administrator
*/
@RestController
@RequestMapping("/cla")
public class ClassController {
@Resource
private TeaFeign teaFeign;
@RequestMapping("/clazz")
public String getClass(int id){
// 借助feign接口远程调用教师服务
String feignTea = teaFeign.getTea(id);
switch (id){
case 1:
return "一班" + feignTea;
case 2:
return "二班" + feignTea;
case 3:
return "三班" + feignTea;
default:
return "没有";
}
}
}
5、 服务的调用-Feign(底层其实就是Ribbon)
- 为什么有了Ribbon还有用Feign?
Feign底层也调用了Ribbon,Feign其实本质上是Ribbon + Hystrix(豪猪)的集合体,因为Ribbon本身的写法不够面向对象,很多Java程序员对这种写法会很不习惯。
6、Feign的使用
在创建一个teacher微服务模块,以供后续的调用
- 调用方添加feign的依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
- 在调用方(class模块)中编写一个feign接口:
package com.yuxi.feign;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
/**
*使用feign来调用服务
* @author Administrator
*/
@FeignClient("micro-teacher")
public interface TeaFeign {
/**
* 调用教师模块的功能
* @param id
* @return
*/
@RequestMapping(value = "/tea/gettea")
String getTea(@RequestParam("id") int id);
}
- 在需要调用微服务的时候(class模块),注入改接口
@Resource
private TeaFeign teaFeign;
@RequestMapping("/clazz")
public String getClass(int id){
// 借助feign接口远程调用教师服务
String feignTea = teaFeign.getTea(id);
.
.
.
- 在调用方配置启动类注解(以上代码中有了)
@SpringBootApplication(scanBasePackages = "com.qf")
@EnableEurekaClient
//表示启动feign,而且这个注解需要找到Feign接口所在的包,默认去启动类所在包下找
//所有如果Feign接口没有在启动类的包下,就需要手动配置扫描
@EnableFeignClients(basePackages = "com.qf.feign")
public class ClassesApplication {
public static void main(String[] args) {
SpringApplication.run(ClassesApplication.class, args);
}
}
- 编写被调用方启动类(teacher)
package com.yuxi;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TeacherApplication {
public static void main(String[] args) {
SpringApplication.run(TeacherApplication.class,args);
}
}
- 编写teacher模块的配置文件
server.port=8082
spring.application.name=micro-teacher
eureka.client.service-url.defaultZone=http://localhost:20000/eureka
- 编写teacher模块的数据接口
package com.yuxi.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Administrator
*/
@RestController
@RequestMapping("/tea")
public class TeacherController {
@RequestMapping("/gettea")
public String getTea(@RequestParam(value = "id") int id) {
/**
* 测试feign的重连和超时
*/
System.out.println("正在连接***********************************");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 正常执行功能代码
switch (id) {
case 1:
return "王老师";
case 2:
return "李老师";
case 3:
return "赵老师";
default:
return "没有";
}
}
}
- teacher模块的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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>springcloud_boot</artifactId>
<groupId>com.yuxi</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud_teacher</artifactId>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
总结
本次小demo创建了四个微服务,分别是作为注册中心的eureka,作为客户端的student,class,teacher四个。其中,student在调用class服务的时候用的是Ribbon + RestTemplate方式实现调用;class在调用teacher服务的时候用的是Feign,三个服务之间使用了两种不同的调用方式,在平时使用较多的是第二种调用方式。