1. 流程
在这我们创建两个支付微服务 一个订单服务 和 一个单机版的eureka服务注册中心
并且将 支付服务注册进eureka
在订单服务中使用openfeign调用 支付服务 并且实现负载均衡
其实openfeign也是在集成了ribbon
2.euruka服务注册中心的搭建
2.1.pom
(版本控制都在父项目中,springboot版本 2.2.2.RELEASE springcloud版本 Hoxton.SR1 其他的自行选择)
<dependencies>
<!--eureka-server-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
<!--boot web actuator-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
2.2yml
server:
port: 7001
eureka:
instance:
hostname:localhost #eureka服务端的实例名称
client:
register-with-eureka: false #false表示不向注册中心注册自己。
fetch-registry: false #false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务
service-url:
#设置与Eureka server 交互的地址查询服务和注册服务都需要依赖这个地址。
defaultZone: http://localhost.com:7001/eureka/ #单机版 指向自己
2.3主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
/**
* @author Xkuna
* @date 2020/7/22 15:32.
*/
@SpringBootApplication
@EnableEurekaServer
public class EurekaMain7001 {
public static void main(String[] args) {
SpringApplication.run(EurekaMain7001.class, args) ;
}
}
3.创建支付服务8001
3.1pom
<dependencies>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
3.2yml
server:
port: 8001
spring:
application:
name: cloud-payment-service #微服务名称
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
defaultZone: http://localhost:7001/eureka
instance:
instance-id: payment8001
prefer-ip-address: true
3.3主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Xkuna
* @date 2020/7/21 23:05.
*/
@SpringBootApplication
@EnableEurekaClient
public class PaymentMain8001 {
public static void main(String[] args) {
SpringApplication.run(PaymentMain8001.class, args) ;
}
}
3.4创建controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* @author Xkuna
* @date 2020/7/22 11:10.
*/
@RestController
@Slf4j
public class PaymentControler {
@Value("${server.port}")
private String serverPort ;
@GetMapping("/port")
public String getPort(){
return this.serverPort ;
}
@GetMapping("/timeout")
public String timeout() {
try {
TimeUnit.SECONDS.sleep(3) ;
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.serverPort ;
}
}
4.创建支付服务8002
将支付服务8001 copy一份 把端口改为 8002 即可
5.创建订单服务80
5.1pom
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!--一般基础通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</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>
5.2 yml
server:
port: 80
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://localhost:7001/eureka/
5.3主启动类
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @author Xkuna
* @date 2020/8/7 15:51.
*/
@SpringBootApplication
@EnableFeignClients//开启openfeign
public class OrderOpenfeginMain80 {
public static void main(String[] args) {
SpringApplication.run(OrderOpenfeginMain80.class, args) ;
}
}
5.4创建service接口
创建一个openfeign调用支付服务的接口
方法 直接copy支付服务的接口
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author Xkuna
* @date 2020/8/7 16:00.
*/
@Component
@FeignClient(value = "CLOUD-PAYMENT-SERVICE") //调用的服务名称
public interface PaymentFeignService {
@GetMapping("/port")
public String getPort() ;
@GetMapping("/timeout")
public String timeout() ;
}
5.5创建controller
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import top.xkuna.springcloud.service.PaymentFeignService;
import javax.annotation.Resource;
/**
* @author Xkuna
* @date 2020/8/7 16:03.
*/
@RestController
@Slf4j
public class OrderController {
@Resource
private PaymentFeignService paymentFeignService ;
@GetMapping("/port")
public String getPort(){
return paymentFeignService.getPort() ;
}
@GetMapping("/timeout")
public String timeout() {
return paymentFeignService.timeout() ;
}
}
6.启动测试
先启动eureka7001 然后启动 支付服务8001 8002 最后启动 订单服务80
访问: http://localhost/port
访问成功 并且发现实现了负载均衡
但是当我们访问timeout接口时 出现了报错
原因是openfeign 负载均衡的底层还是调用了ribbon
在ribbon中 调用服务时 默认的调用 时限为 1000ms
我们在支付服务的timeout接口 暂停了 3 s 所以超时调用报错
修改ribbon默认调用时限
在订单服务的yml配置中添加
#设置feign客户端超时时间(OpenFeign默认支持ribbon)
ribbon:
#指的是建立连接所用的时间,适用于网络状况正常的情况下,两端连接所用的时间
ConnectTimeout: 5000
#指的是建立连接后从服务器读取到可用资源所用的时间
ReadTimeout: 5000
此时我们再次调用 支付服务
成功调用 支付服务 并且实现了负载均衡
7.openfeign日志打印
openfeign自带了日志打印功能
7.1创建日志配置类
import feign.Logger;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author Xkuna
* @date 2020/8/7 16:29.
*/
@Configuration
public class FeginConfig {
@Bean
Logger.Level feignLoggerLevel() {
return Logger.Level.FULL;
}
}
7.2修改yml
在yml中添加 日志配置
logging:
level:
top.xkuna.springcloud.service: debug //前面的是要输出日志的openfeign接口包 或者类
访问订单服务port接口
查看控制台
8.结束语
openfeign 跟ribbon比 使用起来是不是更舒服一些呢~ 😄 😄
可以根据自己需求选择 ribbon 或者 feign 再或者 openfeign 来做服务调用
踩坑
报错The bean 'xx.FeignClientSpecification' could not be registered. A bean with that name has already been defined and overriding is disabled.
这句话的意思是 ioc容器中已经存在FeignClientSpecification这个bean
出现这种情况是 多个feign客户端调用了 同一个服务
比如:
这两个feign客户端调用了 同一个服务 导致bean注册失败
出现这种情况,一般来说 是 服务功能划分还是不够“微”,
解决方法是 在yml中添加一个配置
spring:
main:
allow-bean-definition-overriding: true #表示后发现的bean会覆盖之前相同名称的bean。
不过 建议还是不要这样做,否则后者覆盖前者,多人分工合作的时候,难以避免某些bean被覆盖,会出现很多诡异的问题 ,甚至会带来线上真实的业务损失
所以 建议 要么将该服务下的业务 写在同一个 feign客户端接口中, 要么就在项目初期 规划好项目服务的划分,真正达到微服务中的“微”