微服務架構就是將一個完整的應用從數據存儲開始垂直拆分成多個不同的服務,每個服務都能獨立部署、獨立維護、獨立擴展,服務與服務間通過諸如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()方法結果