前言-微服务架构设计风格
微服务设于基于RESTful架构,使用RESTful可以将愈发复杂单体应用通过HTTP请求、JSON传输数据拆分为不同的业务模块,达到服务独立部署、快速启动、模块协同开发、低耦合、代码复用、职责单一的目的,使团队间相对隔离的敏捷式开发。微服务的盛行首要解决的便是不同服务间调用的问题。
现有解决方案
- Ribbon+RestTemplate
- Feign
- OpenFeign
Ribbon+RestTemplate是基于HTTP+TCP实现服务间通信,其原理是通过RestTemplate构造HTTP请求来完成,相当于单体应用通过HTTP调用第三方接口,是一种远程调用的实现方式。
Feign基于Ribbon+RestTemplate,其内部是通过JDK动态代理的方式,将对外调用的接口抽象成接口,使用远程API的Method方法实例,进行MethodHandler方法处理器分发,根据参数构造Request实例,一般是Controller实现自定义的接口,供外部调用。Feign内部实现了负载均衡,根据负载量、请求量分发至不同的服务,但目前Feign已不再维护。
OpenFeign是springcloud在Feign的基础上支持了SpringMVC的注解,如@RequestMapping等等。OpenFeign的@FeignClient可以解析SpringMVC的@RequestMapping注解下的接口,并通过动态代理的方式产生实现类,实现类中做负载均衡并调用其他服务。
OpenFeign是目前使用最为广泛的远程服务调用方式。
下文使用的demo开源地址:
https://github.com/FirstMrRight/org_eureka_demo.git
OpenFeign使用方式
0.0 创建一个Maven项目,在新创建的Maven项目中创建SpringBoot项目,共需要eureka中心、provider、openfeign、commons四个SpringBoot项目,这四个项目的父pom是最初创建的Maven项目。
父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>
<groupId>org.example</groupId>
<artifactId>org_eureka_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>
1.1 Eureka项目
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>org.javaboy</groupId>
<artifactId>eureka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>eureka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<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>
1.2 在Eureka启动类中增加@EnableEurekaServer注解,声明为服务的注册中心
在配置文件中必须要配置服务名称、端口以及是否为客户端
spring.application.name=eureka
server.port=1111
eureka.client.fetch-registry=false
eureka.client.register-with-eureka=false
2.1配置生产者
在生产者中定义可对外调用的接口
public interface IUserService {
@GetMapping("/hello")
//这里的方法名无所谓,随意取
String hello();
}
Controller实现该接口
@RestController
public class HelloController implements IUserService {
@Value("${server.port}")
Integer port;
@Override
public String hello() {
String s = "hello javaboy:" + port;
System.out.println(new Date());
return s;
}
}
在配置文件中将客户端注册到eureka上,配置服务名称,作为外部调用的唯一标识
spring.application.name=provider
server.port=1113
eureka.client.service-url.defaultZone=http://localhost:1111/eureka
3.1配置消费者
消费者即服务调用者,我们使用openFeign来实现外部服务的调用。
pom中需要引入以下依赖:
<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-openfeign</artifactId>
</dependency>
在消费者的启动类中使用@EnableFeignClients注解声明服务为一个客户端。
创建一个接口,继承自生产者接口,使用@FeignClient(value = “provider”)来确定调用哪个服务,value值是服务名称。
@FeignClient(value = "provider")
public interface HelloService extends IUserService {
}
该接口的实现便是生产者Controller中对应的方法。
注入该生产者的Service
User实体类在commons包
@RestController
public class HelloController {
@Autowired
HelloService helloService;
@GetMapping("/hello")
public String hello() throws UnsupportedEncodingException {
String s = helloService.hello();
System.out.println(s);
User user = new User();
user.setId(1);
user.setUsername("javaboy");
user.setPassword("123");
return helloService.hello();
}
}
启动顺序:
注册中心>生产者>消费者
4.1接口调用测试