5.Spring-Cloud中服務的提供者和服務的消費者


注意:請使用官方建議的springboot和springcloud對應的版本

在之間的博客中已經寫到如何建立高可用的注冊中心,地址見高可用注冊中心

現在我們來創建服務治理體系下的一個服務(即服務的提供者)

1.pom.xml

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>2.spring-cloud-service-provide</groupId>
  <artifactId>service-provide</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring-cloud Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <!--springboot采用1.5.x  對應springcloud版本為 Dalston-->
  <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>
  </parent>

<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/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>2.spring-cloud-service-provide</groupId>
  <artifactId>service-provide</artifactId>
  <packaging>jar</packaging>
  <version>0.0.1-SNAPSHOT</version>
  <name>spring-cloud Maven Webapp</name>
  <url>http://maven.apache.org</url>
  <!--springboot采用1.5.x  對應springcloud版本為 Dalston-->
  <parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath/>

  </parent>

<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>
<!-- 這樣變成可執行的jar -->
    <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

 

2.application.properties

#指定微服務的名稱后續在調用的時候只需要使用該名稱就可以進行服務的訪問
spring.application.name=service-provide
#默認為8080
#server.port=8081
#注冊中心地址
eureka.client.serviceUrl.defaultZone=http://testhost:8000/eureka/,http://testhost2:8001/eureka/

3.springboot啟動類

package com.niugang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.ComponentScan;
/**
 * 服務提供者
 * @author niugang
 *
 */
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

 

 

4.演示controller

 

package com.niugang.controller;
import java.util.List;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ComputeController {
private final Logger logger = Logger.getLogger(getClass());
@Autowired
private DiscoveryClient client;
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String add() {

              //方法已過時,不建議使用
// ServiceInstance instance = client.getLocalServiceInstance();
List<String> services = client.getServices();
if (!services.isEmpty() && services.size() > 0) {
String serviceId = services.get(0);
List<ServiceInstance> instances = client.getInstances(serviceId);
if (!instances.isEmpty() && instances.size() > 0) {
ServiceInstance serviceInstance = instances.get(0);
logger.info(
"/hello, host:" + serviceInstance.getHost() + ", service_id:" + serviceInstance.getServiceId());
}
}


return "hello spring cloud";
}
}

 


java -jar service-provide-0.0.1-SNAPSHOT.jar --server.port=8081打成jar文件,服務提供者,通過不同端口啟動,做到負載均衡,(啟動之前要啟動,注冊中心)

java -jar service-provide-0.0.1-SNAPSHOT.jar --server.port=8082

 

注冊中心分別啟動,為了高可用

java -jar spring-cloud-0.0.1-SNAPSHOT.jar   --spring.profiles.active=testhost

java -jar spring-cloud-0.0.1-SNAPSHOT.jar   --spring.profiles.active=testhost2

上面都啟動完成之后,通過http://testhost:8000訪問

,通過http://testhost2:8001訪問訪問的結果和上面截圖一樣。

現在注冊中心和服務提供者都已經有了,接下來需要來構建服務的消費者,它主要來完成發現服務和消費服務。服務的發現由Eureka客戶端完成,而服務的消費者任務由Ribbon完成。Ribbon是一個基於HTTP和TCP的客戶端負載均衡,它可以在通過客戶端配置ribbonServerList的服務列表去輪訓訪問以達到負載均衡的作用。

第一種方式:構建服務的發現和服務的消費

1.pom.xl

<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>4.spring-cloud-ribbon-consumer</groupId>
<artifactId>ribbon-consumer</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud Maven Webapp</name>
<url>http://maven.apache.org</url>
<!--springboot采用1.5.x 對應springcloud版本為 Dalston -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.2.RELEASE</version>
<relativePath />
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<spring-cloud.version>Dalston.RELEASE</spring-cloud.version>
</properties>

    <dependencies>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--引入ribbon 負載均衡 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<!-- 搞了好久踩的坑,就是在復制上面代碼的時候 順手把scope test復制了,其意思為緊參與junit類似的測試工作,所以一直報找不到服務或者或者拒絕連接 -->
<!-- <scope>test</scope> -->
</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>
<!-- 這樣變成可執行的jar -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>


</project>

2.application.properties

#指定微服務的名稱后續在調用的時候只需要使用該名稱就可以進行服務的訪問
spring.application.name=ribbon-consumer
server.port=9001
#負載平衡RestTemplate可以配置為重試失敗的請求。默認情況下,該邏輯被禁用
spring.cloud.loadbalancer.retry.enabled=true
#注冊中心地址
eureka.client.serviceUrl.defaultZone=http://testhost:8000/eureka/

3.啟動類

package com.niugang;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.web.client.RestTemplate;
/**
 * 負責服務的發現與消費
 * 
 * @author niugang
 *
 */
@SpringBootApplication
/*
 * 默認情況下,DiscoveryClient的實現將使用遠程發現服務器自動注冊本地Spring
 * Boot服務器。可以通過在@EnableDiscoveryClient中設置autoRegister=false來禁用此功能。
 */
/*
 * 默認情況下,ServiceRegistry實現將自動注冊正在運行的服務。要禁用該行為,有兩種方法。您可以設置@EnableDiscoveryClient(
 * autoRegister=false)永久禁用自動注冊。您還可以設置spring.cloud.service-registry.auto-
 * registration.enabled=false以通過配置禁用該行為。
 */
@EnableDiscoveryClient
@ComponentScan

public class Application {
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
 
        

4.controller演示

package com.niugang.controller;

import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class ComputeController {

private final Logger logger = org.slf4j.LoggerFactory.getLogger(ComputeController.class);

@Autowired
LoadBalancerClient loadBalancerClient;

@Autowired
RestTemplate restTemplate;


@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)
public String ribbon() {
logger.info("start invoke service");
//從負載平衡器中為指定的服務選擇ServiceInstance
ServiceInstance serviceInstance = loadBalancerClient.choose("service-provide");
String url = "http://" + serviceInstance.getHost() + ":" + serviceInstance.getPort() + "/hello";
logger.info("access service url:{}",url);
return restTemplate.getForEntity(url, String.class).getBody();
}
}

 

 


啟動服務消費者,訪問注冊中心:

通過瀏覽器訪問,消費者,http://localhost:9001/ribbon-consumer

看控制台打印的日志:已經做負載均衡了

第二種方式:

pom.xml文件不變和application.properties修改訪問端口為9002

啟動類:

package com.niugang;
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.ComponentScan;
import org.springframework.web.client.RestTemplate;

/**
 * 負責服務的發現與消費
 * 
 * @author niugang
 *
 */
@SpringBootApplication

@EnableDiscoveryClient
@ComponentScan
public class Application {

/*
* 要創建負載平衡RestTemplate,創建RestTemplate @Bean並使用@LoadBalanced限定符。
* 
* 警告: 通過自動配置不再創建RestTemplate bean。它必須由單個應用程序創建。
* 
*/
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

 

演示controller

package com.niugang.controller;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;


@RestController
public class ComputeController {

private final Logger logger = org.slf4j.LoggerFactory.getLogger(ComputeController.class);
@Autowired
       private RestTemplate restTemplate;
@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)
public String ribbon() {
logger.info("start invoke service");
//URI需要使用虛擬主機名(即服務名稱,而不是主機名)
return restTemplate.getForEntity("http://service-provide/hello", String.class).getBody();
}
}

 

 

配置多個RestTemplate(以下來自官方文檔)通過查看service-provide控制台信息,可以看到ribbon通過輪訓的方式訪問了,不同端口的服務。

 

如果你想要一個沒有負載平衡的RestTemplate,創建一個RestTemplate bean並注入它。要創建@Bean時,使用@LoadBalanced限定符來訪問負載平衡RestTemplate

重要

請注意下面示例中的普通RestTemplate聲明的@Primary注釋,以消除不合格的@Autowired注入。
@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("http://example.com", String.class);
    }
}

 

提示

如果您看到錯誤java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89,請嘗試注入RestOperations或設置spring.aop.proxyTargetClass=true

 

 微信公眾號

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM