微服务架构就是将一个完整的应用从数据存储开始垂直拆分成多个不同的服务,每个服务都能独立部署、独立维护、独立扩展,服务与服务间通过诸如RESTful API的方式互相调用。Spring Cloud是一个相对比较新的微服务框架。
springcloud主要提供的模块包括:服务发现(Eureka),断路器(Hystrix),智能路由(Zuul),客户端负载均衡(Ribbon)等。
一、Eureka
eureka是分布式服务注册中心,分为单点的和高可用。
单点的注册中心,用于服务的依赖注册。
配置如下:
其中spring.application.name是指定注册到Eureka Server上的应用名称
eureka.instance.prefer-ip-address表示将自己的IP注册到Eureka Server
server.port=8761
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=localhost
eureka.client.registerWithEureka=false
eureka.client.fetchRegistry=false
eureka.client.serviceUrl.defaultZone= http://${eureka.instance.hostname}:${server.port}/eureka/
单点的注册存在缺陷,实际生产环境一般采用高可用的。
详见上篇博文:http://blog.csdn.net/han_xiaoxue/article/details/79443227
二、Ribbon实现客户端负载均衡
Ribbon是负载均衡器,有助于控制HTTP和TCP客户端的行为,为Ribbon配置服务提供者地址列表后,Ribbon可以基于负载均衡算法(例如轮询、随机)自动的帮助消费者去请求。
在SpringCloud中,当Ribbon与Eureka配合使用时,Ribbon可自动从Eureka Server获取服务提供者地址列表,并基于负载均衡算法,请求其中一个服务提供者实例。
pom.xml
<parent>
<groupId>com.ekeyfund.springcloud</groupId>
<artifactId>springcloud-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<start-class>com.ekeyfund.springcloud.SpringcloudH5RibbonHystrixApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</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-eureka</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</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>
<configuration>
<mainClass>${start-class}</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
MVCConfiguration.java
主要是在RestTemplate中添加了@LoadBalanced注解即可整合Ribbon实现客户端的负载均衡。
@Configuration
@EnableWebMvc
public class MVCConfiguration extends WebMvcConfigurerAdapter{
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter(){
return new MappingJackson2HttpMessageConverter();
}
@Bean
public RequestMappingHandlerAdapter requestMappingHandlerAdapter(MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter){
RequestMappingHandlerAdapter requestMappingHandlerAdapter=new RequestMappingHandlerAdapter();
List<HttpMessageConverter<?>> messageConverters=new ArrayList<>();
messageConverters.add(mappingJackson2HttpMessageConverter);
requestMappingHandlerAdapter.setMessageConverters(messageConverters);
return requestMappingHandlerAdapter;
}
@Bean
@LoadBalanced//开启客户端负载均衡
public RestTemplate restTemplate(){
return new RestTemplate();
}
}
UserController.java
@Autowired
private LoadBalancerClient loadBalancerClient;
@Autowired
private RestTemplate restTemplate;
@GetMapping("/login")
public User login(@RequestParam String name, @RequestParam String password){
LOGGER.info("call user service login method");
ResponseEntity<User> responseEntity =this.restTemplate.getForEntity("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/login?name={1},password={2}",User.class,name,password);
return responseEntity.getBody();
}
/**
* ribbon负载均衡测试方法
*/
@GetMapping("/log-user-service-instance")
public void logUserServiceInstance(){
ServiceInstance serviceInstance=this.loadBalancerClient.choose("springcloud-provider-user-service");
LOGGER.info("serviceInstance info ---> serviceId is "+serviceInstance.getServiceId()+" host is "+serviceInstance.getHost()+"port is "+serviceInstance.getPort() );
}
从UserController中的login,get和list方法可以看出,我们将请求地址变更为http://SPRINGCLOUD-PROVIDER-USER-SERVICE,而SPRINGCLOUD-PROVIDER-USER-SERVICE是用户微服务的虚拟主机名,当Ribbon和Eureka配合使用时,会自动将虚拟主机名映射城微服务的网络地址。
在新增的logUserServiceInstance()方法中可以通过LoadBalancerClient的API更加直观的获取当前选择的用户微服务节点。
那么通过上面的调用,可以看出,拼接字符串的方式构造URL,而在其他业务场景中可能还会有更多的参数,如果还以这种方式构造URL,那么就会变得更低效,难以维护。
因此我们可以采用Feign
三、Feign实现声明式REST调用
Spring Cloud在原有基础上使Feign支持SpringMVC注解,并且整合了Ribbon和Eureka。Feign实现声明式的RESTful API 调用
pom.xml
<parent>
<groupId>com.ekeyfund.springcloud</groupId>
<artifactId>springcloud-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<start-class>com.ekeyfund.springcloud.SpringcloudFeignH5Application</start-class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>${start-class}</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
3.1、首先创建接口,并添加@FeignClient注解
@FeignClient(value = "springcloud-provider-user-service") //
public interface UserFeignClient {
@RequestMapping(value = "/list",method = RequestMethod.GET)
List<User> list();
@RequestMapping(value = "/login",method = RequestMethod.GET)
User login(@RequestParam("name") String name, @RequestParam("password") String password);
}
@FeignClient注解中的springcloud-provider-user-service是一个任意的客户端名称,用于创建Ribbon负载均衡器。
3.2.修改Controller,让其调用Feign接口
@RestController
public class UserFeignController {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(UserFeignController.class);
@Autowired
UserFeignClient userFeignClient;
@Autowired
private LoadBalancerClient loadBalancerClient;
@GetMapping(value = "/list")
public List<User> list(){
return userFeignClient.list();
}
@GetMapping("/login")
public User login(@RequestParam String name,@RequestParam String password){
return userFeignClient.login(name,password);
}
/**
* ribbon负载均衡测试方法
* springcloud 将feign和ribbon以及eureka进行了集成
*/
@GetMapping("/log-user-service-instance")
public void loguserserviceinstance(){
ServiceInstance serviceInstance=this.loadBalancerClient.choose("springcloud-provider-user-service");
LOGGER.info("serviceInstance info ---> serviceId is "+serviceInstance.getServiceId()+" host is "+serviceInstance.getHost()+"port is "+serviceInstance.getPort() );
}
}
3.3、启动类用@EnableFeignClients注解,开启SpringCloud Feign的支持功能
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class SpringcloudFeignH5Application {
public static void main(String[] args) {
SpringApplication.run(SpringcloudFeignH5Application.class, args);
}
}
到此不仅实现了声明式的Restful API调用,还实现了客户端的负载均衡
四、断路器(Hystrix)
hystrix是一种能够在远程服务不可用时自动熔断(打开开关),并在远程服务恢复时自动恢复(闭合开关)的设施。
pom.xml
<parent>
<groupId>com.ekeyfund.springcloud</groupId>
<artifactId>springcloud-parent</artifactId>
<version>2.0.0-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<properties>
<start-class>com.ekeyfund.springcloud.SpringcloudH5RibbonHystrixApplication</start-class>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</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-eureka</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.8</version>
</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>
<configuration>
<mainClass>${start-class}</mainClass>
<layout>ZIP</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
4.1在启动类添加@EnableCircuitBreaker为项目启动断路器支持
@EnableCircuitBreaker //启动断路器支持
@EnableDiscoveryClient
@SpringBootApplication
public class SpringcloudH5RibbonHystrixApplication {
public static void main(String[] args) {
SpringApplication.run(SpringcloudH5RibbonHystrixApplication.class, args);
}
}
4.2修改UserController,让其中的list方法具备容错能力
为list方法添加一个回退方法listFallback,该方法与list方法具有相同的参数和返回值类型。
在list方法上,使用注解@HystrixCommand的fallBackMethod属性,指定回退方法是listFallback。
@HystrixCommand(fallbackMethod = "listFallback",commandProperties = {
@HystrixProperty(name="execution.isolation.thread.timeoutInMilliseconds",value = "50000"),
@HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds",value = "10000")
},
threadPoolProperties = {
@HystrixProperty(name = "coreSize",value = "1"),
@HystrixProperty(name="maxQueueSize",value = "20")
}
)
@GetMapping("/list")
public List<User> list(){
User[] users=this.restTemplate.getForObject("http://SPRINGCLOUD-PROVIDER-USER-SERVICE/list",User[].class);
List<User> userList = Arrays.asList(users);
return userList;
}
/**
* 当list方法所在的服务不可用时,会调用此方法
* @return
*/
public List<User> listFallback(){
User user =new User();
user.setName("admin");
List<User> userList=new ArrayList<>();
userList.add(user);
return userList;
}
当服务状态可用时的返回list()方法结果 ,关掉两个springcloud-provider-user-service进程后,再次访问返回listFallback()方法结果