刚学习Java时,我们编写的程序,运行时只有一个进程,这种程序就是我们常说的“单体架构”,最典型的单体架构图如下:
单体架构的软件,我们可以认为是没有架构的。
软件的架构,从单体架构,发展到垂直多程序架构、分布式架构、SOA架构,一路狂奔,到了现在的微服务架构。
虽然我们推崇微服务架构,但客观地说,单体架构依然盛行,包括很多比较大的软件,就是用单体架构来开发的,发布时也只有一个可执行程序。
单体架构之所以盛行,是因为采用其他架构时,我们除了实现业务功能,还要实现架构,例如模块之间的相互调用,这些都很复杂。
当我们使用SpringCloud开发微服务架构的软件时,会发现事情变得很轻松。我们可以方便地对软件的处理能力扩容,可以用Eureka和Ribbon,实现模块之间基于RESTful的相互调用。
说了这么多理论,估计读者都有点烦了,还是直接上代码吧。
在上一讲《Java第66讲——微服务之Eureka》文章中,详细介绍了如何建立Eureka服务器和Eureka客户端,大家可以参考,因此这里不再赘述。
我们建立的Eureka服务器的信息如下:
模块名称:register
监听端口:8800
注册url:http://localhost:8800/eureka/
Eureka客户端将会启动三个实例,其信息如下:
模块名称:producer
监听端口:三个实例监听的端口分别为9901,9902,9903
应用名称:service-producer
这些信息与《Java第66讲——微服务之Eureka》文章中实现的代码完全一致。
从名称来看,register是注册中心,producer是生产者,此次开发,将增加一个消费者consumer,获得producer提供的服务:
Ribbon是一个用来实现负载均衡的组件。
当我们有多个producer处于运行状态时,consumer可以根据负载情况,获得其中一个producer,然后进行调用。
使用Ribbon,我们可以根据系统当前的负载情况,决定增加或者减少producer数量。
通过Ribbon,我们轻松地实现了集群的能力。
在producer中增加一个CallController类,用于提供服务,该类的代码如下:
package com.flying.producer.controller;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CallController {
@Value("${server.port}")
private int thePort;
@RequestMapping("/call_me")
private String callMe(@RequestParam(value = "name") String caller){
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("Service of port ").append(thePort).append(" is called by ").append(caller);
return stringBuffer.toString();
}
}
新增consumer模块的过程如下:
<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-ribbon</artifactId>
<version>1.4.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
package com.flying.consumer;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
package com.flying.consumer.config;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class RestTemplateCreator {
@LoadBalanced
@Bean
RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
package com.flying.consumer.biz;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class CallerService {
@Autowired
RestTemplate restTemplate;
public String callProducer(){
return restTemplate.getForObject("http://SERVICE-PRODUCER/call_me#name=consumer", String.class);
}
}
package com.flying.consumer.controller;
import com.flying.consumer.biz.CallerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@Autowired
private CallerService callerService;
@GetMapping("/call_test")
public String testByExplorer(){
return callerService.callProducer();
}
}
server.port=10000
eureka.client.serviceUrl.defaultZone=http://localhost:8800/eureka/
spring.application.name=service-consumer
代码编写完成了,现在准备进行测试。
producer1:监听9901
producer2:监听9902
producer3:监听9903
这是第一次测试时显示的web页面:
刷新页面,显示的web页面为:
刷新两次页面后,显示了下面的web页面:
不敢想象,没有编写多少代码,借助于Spring Cloud,竟然轻松地实现了集群功能,实现了负载均衡功能!