今天是2020-10-6日
打开spring cloud的主页(https://spring.io/projects/spring-cloud#overview)
首先就看到最新的版本号 : Spring Cloud Hoxton.SR8
大版本号:Hoxton (从ABCDEFGH往上排),这就是H版
小版本号: SNAPSHOT:快照版本;随时可能修改 M:MileStone,M1表示第1个里程碑版本,一般同时标注PRE,表示预览版版。 SR:Service Release,SR1表示第1个正式版本,一般同时标注GA:(GenerallyAvailable),表示稳定版本。
Release Train | Boot Version |
---|---|
2.2.x, 2.3.x (Starting with SR5) | |
2.1.x | |
2.0.x | |
1.5.x | |
1.5.x |
打开第二个tab页 LEARN ,看到这个版本列表
Hoxton.SR8 CURRENT GA | API Doc. | |
2020.0.0-SNAPSHOT SNAPSHOT | Reference Doc. | API Doc. |
2020.0.0-M3 PRE | Reference Doc. | API Doc. |
Hoxton.BUILD-SNAPSHOT SNAPSHOT | Reference Doc. | API Doc. |
Greenwich.SR6 GA | Reference Doc. | API Doc. |
Greenwich.BUILD-SNAPSHOT SNAPSHOT | Reference Doc. | API Doc. |
打开 Reference Doc. 超级链接,可以看到和Spring Boot的版本对应关系
Release Train Version: Hoxton.SR8
Supported Boot Version: 2.3.3.RELEASE
第一章 创建服务注册中心 eureka-server
微服务架构,最基本的,就是要有一个服务注册和服务发现中心。之前用eureka(官方不再维护了),还可以使用zookeeper,etcd,consul,nacos。
建立父工程,即多module maven工程,注意pom的packaging 为 pom, 不是jar
父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">
<modelVersion>4.0.0</modelVersion>
<groupId>gaofeng</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<name>springcloud</name>
<description>springcloud</description>
<modules>
<module>eurekaserver</module>
<module>app1</module>
<module>app2</module>
<module>zuul</module>
</modules>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.boot.version>2.3.3.RELEASE</spring.boot.version>
<spring.cloud.version>Hoxton.SR8</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.3.RELEASE</spring.cloud.alibaba.version>
<env>dev</env>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
子pom.xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>gaofeng</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>eurekaserver</artifactId>
<dependencies>
<!-- 引入 Spring Cloud Netflix Eureka Server 相关依赖,将 Eureka 作为注册中心的服务器,并实现对其的自动配置 -->
<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>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec-${env}</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
eurekaserver/src/main/resources/application.yml
server:
port: 8761 # 设置 Eureka-Server 的端口
spring:
application:
name: eureka-server
eureka:
client:
register-with-eureka: false # 不注册到 Eureka-Server,默认为 true
fetch-registry: false # 不从 Eureka-Server 获取注册表,默认为 true
package org.eurekaserver;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
// EurekaServer管理页面 http://127.0.0.1:8761
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class,args);
}
}
mvn clean install
java -jar target/eurekaserver-0.0.1-SNAPSHOT-exec-dev.jar
第二章 创建第一个微服务 app1
pom.xml
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>gaofeng</groupId>
<artifactId>springcloud</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>app1</artifactId>
<dependencies>
<!-- 引入 Spring 相关依赖,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 引入 Spring Cloud Netflix Eureka Client 相关依赖,将 Eureka 作为注册中心的客户端,并实现对其的自动配置 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
<configuration>
<classifier>exec-${env}</classifier>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
app1/src/main/resources/application.yml
server:
port: 10002 # 设置 app1 的端口
spring:
application:
name: app1
eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址
package org.app1;
import java.util.Random;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@SpringBootApplication
@EnableDiscoveryClient
public class App1 {
public static void main(String[] args) {
SpringApplication.run(App1.class, args);
}
}
@RestController
class TestController2 {
private int id = new Random().nextInt();
@GetMapping("/echo")
public String echo(String name) {
return "provider:"+id+":" + name;
}
}
$ java -jar target/app1-0.0.1-SNAPSHOT-exec-dev.jar
$ curl -is http://127.0.0.1:10002/echo?name=gg
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 22
Date: Tue, 06 Oct 2020 15:57:07 GMT
provider:-537651807:gg
第三章 微服务之间如何调用rest接口
再启动一个app1的实例,端口号改为10004
$ java -jar target/app1-0.0.1-SNAPSHOT-exec-dev.jar --server.port=10004
Instances currently registered with Eureka
Application | AMIs | Availability Zones | Status |
---|---|---|---|
APP1 | n/a (2) | (2) | UP (2) - DESKTOP-EIPDTAD:app1:10002 , DESKTOP-EIPDTAD:app1:10004 |
APP3 | n/a (1) | (1) | UP (1) - DESKTOP-EIPDTAD:app3:10005 |
server:
port: 10005 # 设置 app3 的端口
spring:
application:
name: app3
eureka:
client:
register-with-eureka: true # 注册到 Eureka-Server,默认为 true
fetch-registry: true # 从 Eureka-Server 获取注册表,默认为 true
service-url:
defaultZone: http://127.0.0.1:8761/eureka/ # Eureka-Server 地址
package org.app2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@EnableDiscoveryClient
@SpringBootApplication
public class App3 {
public static void main(String[] args) {
SpringApplication.run(App3.class, args);
}
@Configuration
public class RestTemplateConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
static class TestController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/hello")
public String hello(String name) {
String response = restTemplate.getForObject("http://app1/echo?name="+name, String.class);
// 返回结果
return "consumer:" + response;
}
}
}
curl -is http://127.0.0.1:10005/hello?name=gg
他的本质过程其实是:
package org.app2;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class App2 {
public static void main(String[] args) {
SpringApplication.run(App2.class, args);
}
@Configuration
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
static class TestController {
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private RestTemplate restTemplate;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping("/hello")
public String hello(String name) {
// <1> 获得服务 `demo-provider` 的一个实例
ServiceInstance instance;
if (System.currentTimeMillis()%2==0) {
// 获取服务 `demo-provider` 对应的实例列表
List<ServiceInstance> instances = discoveryClient.getInstances("app1");
System.out.println(instances);
// 选择第一个
instance = instances.size() > 0 ? instances.get(0) : null;
} else {
instance = loadBalancerClient.choose("app1");
}
// <2> 发起调用
if (instance == null) {
throw new IllegalStateException("获取不到实例");
}
String targetUrl = instance.getUri() + "/echo?name=" + name;
System.out.println(targetUrl);
String response = restTemplate.getForObject(targetUrl, String.class);
// 返回结果
return "consumer:" + response;
}
}
}
第四章 使用另外一种(声明式)http客户端openFegin(老的Fegin已经不更新了)
关键点1:@FeignClient("app1")
关键点2:public String echo(@RequestParam("name") String name); 必须加@RequestParam("name")
验证方法:curl -s 127.0.0.1:10005/feign?name=gf
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class App4 {
public static void main(String[] args) {
SpringApplication.run(App4.class,args);
}
}
@RestController
class RemoteServer {
@Autowired
RemoteApi remoteApi;
@RequestMapping(value = "/feign",method = RequestMethod.GET)
public String remoteServer(@RequestParam String name){
return remoteApi.echo(name);
}
}
@FeignClient("app1")
interface RemoteApi{
@GetMapping(value = "/echo")
public String echo(@RequestParam("name") String name);
}
设置Fegin的超时时间,在yml中增加
ribbon:
ReadTimeout: 1000
ConnectTimeout: 1000
打印Fegin的日志,在yml中增加
logging:
level:
ROOT: INFO
#针对指定的类 设置输出级别
org.app2.*: debug
feign:
client:
config:
default:
loggerLevel: FULL
第五章 限流、降级、熔断 hystrix
服务降级有很多种降级方式!如开关降级、限流降级、熔断降级!
在yml中开启熔断机制
feign:
hystrix:
enabled: true
代码修改
@FeignClient(value="app1",fallback = FallbackImpl.class)
interface RemoteApi{
@GetMapping(value = "/echo2") //故意把url写错,当Feign调用这个url出错时,会返回FallbackImpl
public String echo(@RequestParam("name") String name);
}
@Service
class FallbackImpl implements RemoteApi{ //编写熔断的处理类
public String echo(String name) {
return "error error error error error";
}
}
第六章 consul
G:\gaofeng\consul_1.8.4_windows_amd64
1、启动 consul agent -dev
2、打开界面 http://localhost:8500/
3、注册服务
curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{
"ID": "userServiceId2",
"Name": "userService",
"Tags": [ "primary", "v1" ],
"Address": "127.0.0.1",
"Port": 8000,
"EnableTagOverride": false,
"Check": {
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com",
"Interval": "10s"
}
}'
curl http://127.0.0.1:8500/v1/agent/service/register -X PUT -i -H "Content-Type:application/json" -d '{
"ID": "userServiceId1",
"Name": "userService",
"Tags": [ "primary", "v1" ],
"Address": "127.0.0.1",
"Port": 8000,
"EnableTagOverride": false,
"Check": {
"DeregisterCriticalServiceAfter": "90m",
"HTTP": "http://www.baidu.com",
"Interval": "10s"
}
}'
4、查询服务
curl http://127.0.0.1:8500/v1/catalog/service/userService
5、服务更新:id是唯一的,更新时保持id不变即可
app1consul的pom
<dependencies>
<dependency><!-- 健康检查依赖于此包 -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
yaml文件
server:
port: 8501 # 设置 微服务 的端口
spring:
application:
name: App1Consul
cloud:
consul:
host: localhost
port: 8500
discovery:
serviceName: App1Consul #注册到consul的服务名称
@SpringBootApplication
@EnableDiscoveryClient
public class App1Consul {
static final int id = new Random().nextInt(100);
public static void main( String[] args ){
SpringApplication.run(App1Consul.class, args);
}
}
@RestController
class Desk{
@GetMapping("hello")
public String hello(String name) {
return "\n"+App1Consul.id + " : " + name;
}
}
@SpringBootApplication
@EnableDiscoveryClient
public class App2Consul {
public static void main( String[] args ){
SpringApplication.run(App2Consul.class, args);
}
}
@Configuration
class RestTemplateConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
@RestController
class Controller1{
@Autowired RestTemplate restTemplate;
@GetMapping("hello")
public String hello(@RequestParam("name") String name) {
String response = restTemplate.getForObject("http://App1Consul/hello?name="+name, String.class);
return response;
}
}
app2consul的端口为8621
效果测试: curl -s 127.0.0.1:8601/hello?name=gg