最近几年,微服务很火爆,随着公司规模的上升,对应各种服务也在不断的完善和增加,软件的架构从之前的单体架构逐渐朝着功能分解成不同模块继而拆分成一组特定服务。
著名的扩展立方体如上图所示,
X轴通过部署多个相同实例,继而在多个实例之间进行负载均衡;Z轴根据请求中特殊属性对请求进行路由;Y轴扩展根据功能将应用拆分为服务。
微服务将一个复杂的单体系统分解成若干小的业务,服务的边界明确,服务之间耦合降低,服务分布式集群化部署,可实现负载聚恒和熔断。
spring cloud中,
eureka
实现了服务注册和发现
ribbon
实现了服务的负载均衡
hystrix
实现了服务熔断机制
zuul
实现了服务路由网关
下面分别进行示例入门
首先搭建eureka-server,进行服务的注册,
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>spring-cloud-test-001</artifactId>
<groupId>com.leo.test</groupId>
<version>1.0.0-snapshot</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka-server-001</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>
querka-server配置application.yml如下:
---
server:
port: 8081
eureka:
instance:
prefer-ip-address: true
hostname: leo-node
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone:
http://${eureka.instance.hostname}:8082/eureka
spring:
profiles: test-001-1
---
server:
port: 8082
eureka:
instance:
prefer-ip-address: true
hostname: leo-node
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone:
http://${eureka.instance.hostname}:8081/eureka
spring:
profiles: test-001-2
编写启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class);
}
}
通过设置不同profile,同时启动多个eureka实例,相互注册,在idea中同时启动两个eureka-server如下:
工具栏菜单 run -> edit configurations
将EurekaServerApplication-8081配置复制过去,并更改名称为EurekaServerApplication-8082,并指定profile:
同时启动两个eureka server,访问 http://leo-node:8082/ 和 http://localhost:8081/如下:
下面进行eureka server客户端服务提供这 eureka-provider,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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-test-001</artifactId>
<groupId>com.leo.test</groupId>
<version>1.0.0-snapshot</version>
</parent>
<groupId>org.example</groupId>
<artifactId>service-provider-001</artifactId>
<version>1.0-SNAPSHOT</version>
<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>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
eureka-provider配置application.yml配置如下:
eureka:
client:
service-url:
defaultZone: http://leo-node:8081/eureka, http://leo-node:8082/eureka
spring:
application:
name: service-provider-001
---
spring:
profiles: test-001-1
server:
port: 8091
---
spring:
profiles: test-001-2
server:
port: 8092
编写启动类:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
public class ServiceProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceProviderApplication.class);
}
}
编写controller服务提供接口:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/provider")
public class HelloWorldController {
@Value("${server.port}")
private String port;
@RequestMapping("/sayHello")
public String sayHello(){
return "Hello world from "+port;
}
}
按照不同profile,启动两个eureka-provider服务提供端,
访问eureka server端,http://leo-node:8082和http://leo-node:8081/可以发现provider服务两个实例已经注册上来:
这样服务注册完成,下面编写服务消费端 eureka-consumer-ribbon,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">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-cloud-test-001</artifactId>
<groupId>com.leo.test</groupId>
<version>1.0.0-snapshot</version>
</parent>
<groupId>org.example</groupId>
<artifactId>service-consumer-001</artifactId>
<version>1.0-SNAPSHOT</version>
<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.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这里引入了hystrix,进行客户端的负载均衡和容错,
eureka-consumer-ribbon的配置application.xml如下:
eureka:
client:
service-url:
defaultZone:
http://leo-node:8081/eureka
server:
port: 9000
spring:
application:
name: service-consumer-ribbon-001
#暴露全部的监控信息
management:
endpoints:
web:
exposure:
include: "*"
编写启动类:
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard // 启用hystrix dashboard功能
public class ServiceConsumerRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceConsumerRibbonApplication.class);
}
/**
* 需要开启如下bean,才能正常访问 /hystrix 和 /hystrix.stream
* @return
*/
@Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}
ribbon的配置类如下:
@Configuration
public class RibbonConfig {
@LoadBalanced
@Bean
public RestTemplate newRestTemplate(){
SimpleClientHttpRequestFactory f = new SimpleClientHttpRequestFactory();
f.setConnectTimeout(1000);
f.setReadTimeout(1000);
return new RestTemplate(f);
}
}
对外controller如下:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
@RequestMapping("/consumer/ribbon")
public class ConsumerController {
@Value(("${server.port}"))
private String port;
@Autowired
private RestTemplate restTemplate;
@RequestMapping("/sayHello")
@HystrixCommand(fallbackMethod = "error")
public String test(){
String providerMsg = restTemplate.getForObject("http://service-provider-001/provider/sayHello",String.class);
return "from consumer and consumer port is "+port+" , provider msg is : "+providerMsg;
}
public String error(){
return "consumer ribbon error from hystrix";
}
}
启动eureka-consumer-ribbon,访问 http://leo-node:9000/consumer/ribbon/sayHello:
显示:
不停刷新,则变化显示如下:
=======================================================================
=======================================================================
当将其中一个provider停止后,不断刷新则显示:
可以看到请求的provider不存在或者发生错误时,hystric介入,返回了错误信息。