一、簡介
在上一篇中,我們介紹注冊中心Eureka,但是沒有服務注冊和服務調用,服務注冊和服務調用本來應該在上一章就應該給出例子的,但是我覺得還是和Feign一起講比較好,因為在實際項目中,都是使用聲明式調用服務。而不會在客服端和服務端存儲2份相同的model和api定義。Feign在RestTemplate的基礎上對其封裝,由它來幫助我們定義和實現依賴服務接口的定義。Spring Cloud Feign 基於Netflix Feign 實現的,整理Spring Cloud Ribbon 與 Spring Cloud Hystrix,並且實現了聲明式的Web服務客戶端定義方式。
二、實踐
在上一節中,我繼續添加工程模塊,最后的模塊如下:

首先我們需要建一個工程,名為hello-service-api 在工程主要定義對外提供的model 和api。服務的提供方和服務的消費方都要依賴該工程jar,這樣我們就可以只維護一份model 和api定義。在該例子中主要如下結構

非常簡單,只有1個HelloServieRemoteApi 接口定義和User對象。
@RequestMapping("/hello-service-remote")
public interface HelloServiceRemoteApi {
@RequestMapping(value = "/hello1", method = RequestMethod.GET)
String hello(@RequestParam("name") String name);
@RequestMapping(value = "/hello2", method = RequestMethod.GET)
User hello(@RequestHeader("name") String name,@RequestHeader("age") Integer age);
@RequestMapping(value = "/hello3", method = RequestMethod.POST)
String hello(@RequestBody User user);
}
在上面的接口定義中,我們非常的清晰,在接口上面我們主映射為/hello-service-remote,個人感覺已接口的名字通過“-”這樣可以非常的區分不同的接口路徑,防止多接口時發生重復。接下來具體方法的上面可以定義於方法名一樣的路徑映射,我這里已 /hello1,/hello2,/hello3為主。
public class User implements Serializable {
private static final long serialVersionUID = -7233238826463139634L;
private Long id;
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
上面就是簡單的一個User對象。
從上面的接口中發現,該api工程需要引入spring-web包。所以它的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>
<artifactId>hello-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
<groupId>com.qee.hello</groupId>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.2.9.RELEASE</version>
</dependency>
</dependencies>
</project>
從上面的pom.xml定義中,我們知道hello-service-api並不集成父工程micro-service-integration。一般作為api提供的工程jar,依賴越少越好。
接下來我們需要創建一個提供者工程,我們把它命名為hello-service-compose。該工程也是標准的Spring Boot工程。具體的目錄如下:

在工程中我們有一個剛才hello-service-api接口定義的實現。代碼如下:
@RestController
public class HelloServiceRemoteApiImpl implements HelloServiceRemoteApi {
@Override
public String hello(@RequestParam("name") String name) {
return "hello " + name;
}
@Override
public User hello(@RequestHeader("name") String name, @RequestHeader("age") Integer age) {
try {
name= URLDecoder.decode(name,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return new User(name, age);
}
@Override
public String hello(@RequestBody User user) {
if (user == null) {
return "未知";
}
return user.toString();
}
}
因為是測試工程,所以它沒有復雜的業務邏輯。接下來就是HelloProviderApplication的啟動main.
package com.qee.remote;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@EnableDiscoveryClient
@SpringBootApplication
public class HelloProviderApplication {
public static void main(String[] args) {
SpringApplication.run(HelloProviderApplication.class, args);
}
}
在上面有2個注解,第一個 SpringBootApplication 就是Spring Boot 啟動注解,EnableDiscoveryClient 該注解會把RestController修飾的類注冊到注冊中心去。
接下來我們來看下application.properties
server.port=8885
spring.application.name=hello-service-compose
eureka.instance.hostname=register.center.com
eureka.instance.server.port=8881
#默認的注冊域
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${eureka.instance.server.port}/eureka/
#控制台彩色輸出
spring.output.ansi.enabled=ALWAYS
從上面信息我們知道,改工程啟動端口為8885,注冊中心地址為register.center.com:8881。
接下來我們查看一下該工程的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">
<parent>
<artifactId>micro-service-integration</artifactId>
<groupId>spring.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hello-service-compose</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.qee.hello</groupId>
<artifactId>hello-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
從pom.xml文件中知道該工程依賴了web,euraka,ribbon,actuator,hello-service-api 包。其中euraka為服務注冊和發現包,ribbon為服務調用負載均衡包,actuator為工程元信息檢測包。還有我們自己定義的hello-service-api包。
在上面的簡單配置和編寫后,我們就可以啟動工程把該HelloServiceRemoteApi注冊到注冊中心去了。
現在有了服務接口定義包和服務提供工程,現在我們編寫一下服務調用工程。命名為hello-service-web。該工程的目錄結構如下:

首先我們來看下HelloBackgroundService 這個接口。
@FeignClient(value = "hello-service-compose")
public interface HelloBackgroundService extends HelloServiceRemoteApi{
}
非常的簡單,主要繼承我們之前編輯的HelloServiceRemoteApi,並且在上面打上FeignClient注解,該注解指定服務名來綁定服務。該注解同時會使服務調用具有負載均衡的能力。
接下來我們來看下HelloController類
@RestController
public class HelloController {
@Autowired
private HelloBackgroundService helloBackgroundService;
@RequestMapping("/hello")
public Map<String,Object> hello(){
Map<String,Object> ret = new HashMap<String, Object>();
StringBuffer sb = new StringBuffer();
String s1 = helloBackgroundService.hello("張三");
sb.append(s1).append("\n");
User user = null;
try {
user = helloBackgroundService.hello(URLEncoder.encode("李四", "UTF-8"), 30);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
sb.append(user.toString()).append("\n");
String s3 = helloBackgroundService.hello(new User("王五", 19));
sb.append(s3).append("\n");
ret.put("show",sb.toString());
return ret;
}
}
從上面得知我們,我們就可以調用之前的我們編寫的HelloBackgroundService了。接下來我們查看一下啟動類HelloConsumerApp
package com.qee;
import feign.Logger;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class HelloConsumerApp {
@Bean
Logger.Level feginLoggerLevel(){
return Logger.Level.FULL;
}
public static void main(String[] args) {
SpringApplication.run(HelloConsumerApp.class, args);
}
}
在該啟動了中又多了一個注解EnableFeignClients ,該注解開啟Spring Cloud Feign的支持。接着我們來查看一下application.properties
server.port=8887
spring.application.name=hello-service-web
eureka.instance.hostname=register.center.com
eureka.instance.server.port=8881
#默認的注冊域
eureka.client.serviceUrl.defaultZone=http://${eureka.instance.hostname}:${eureka.instance.server.port}/eureka/
#開啟請求壓縮功能
feign.compression.request.enabled=true
#開啟響應壓縮功能
feign.compression.response.enabled=true
#指定壓縮請求數據類型
feign.compression.request.mime-types=text/xml;application/xml;application/json
#如果傳輸超過該字節,就對其進行壓縮
feign.compression.request.min-request-size=2048
#控制台彩色輸出
spring.output.ansi.enabled=ALWAYS
#日志配置,該接口的日志級別
logging.level.com.qee.service.HelloBackgroundService=DEBUG
從上面的注釋中,我們已經可以知道具體的配置參數的作用,這里就不詳細介紹了。從上面的配置和編寫我們可以知道,該工程需要如下的依賴包,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">
<parent>
<artifactId>micro-service-integration</artifactId>
<groupId>spring.cloud</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hello-service-web</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.qee.hello</groupId>
<artifactId>hello-service-api</artifactId>
<version>1.0-SNAPSHOT</version>
</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>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
</project>
該服務消費端,比服務提供方多了一個jar依賴,就是feign。該jar的作用就是提供聲明式的服務調用。到這里我們本章的內容大致結束,最后我們來運行這幾個工程。查看如下結果:

從上面我們可以看到2個工程hello-service-compose 和hello-service-web都已經注冊到注冊中心eureka上了。接下來看一下調用結果:

到這里服務注冊中心啟動,服務注冊,服務消費大致都已完成,之后會向大家一起學習服務調用的負載均衡Ribbon和服務容錯保護Hystrix.
