1 服务说明
准备两个maven项目,eureka-sales、eureka-user,通过访问eureka-sales服务调用eureka-user服务,三种访问方式下 eureka-user 代码不变,只是为了提供被访问接口。
1-1 eureka
pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka-server</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8761
spring:
application:
name: eureka
eureka:
client:
register-with-eureka: false
fetch-registry: false
server:
waitTimeInMsWhenSyncEmpty: 0
serviceUrl:
defaultZone: http://localhost:${server.port}/eureka/
启动类,加上 @EnableEurekaServer 注解:
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
1-2 eureka-sales
pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
定义接口:
@RequestMapping("/sales")
public interface SalesRest {
@RequestMapping(value = "/queryGoodsList/{type}", method = RequestMethod.GET)
Object queryGoodsList(Integer type);
}
接口实现类,根据传入的type,决定以何种方式访问 eureka-user 服务,1 表示 ribbon 方式,2 表示feign 方式,其他数字表示 http 方式:
@RestController
public class SalesRestImpl implements SalesRest {
@Autowired
private SalesService salesService;
@Override
public String queryGoodsList(@PathVariable Integer type) {
System.out.println("start to queryGoodsList --->");
if(type.intValue() == 1) {
return salesService.queryGoodsListByRibbon();
}else if(type.intValue() == 2) {
return salesService.queryGoodsListByFeign();
}else {
return salesService.queryGoodsListByHttp();
}
}
}
方法实现:
@Service
public class SalesService {
@Autowired
RestTemplate restTemplate;
@Autowired
UserFeignClient userFeignClient;
private static final String RIBBON_URL = "http://user:8082/user/getUserInfo";
private static final String HTTP_URL = "http://127.0.0.1:8082/user/getUserInfo";
private static final String IP = IpUtil.getIp();
public String queryGoodsListByRibbon() {
String sales_result = "queryGoodsListByRibbon success : [sales_ip:" + IP + "] ";
String result = restTemplate.getForObject(RIBBON_URL, String.class);
return sales_result + result;
}
public String queryGoodsListByFeign() {
String sales_result = "queryGoodsListByFeign success : [sales_ip:" + IP + "] ";
String result = (String) userFeignClient.getUserInfo();
return sales_result + result;
}
public String queryGoodsListByHttp() {
String sales_result = "queryGoodsListByHttp success : [sales_ip:" + IP + "] ";
String result = HttpClientUtil.doGet(HTTP_URL);
return sales_result + result;
}
}
client
@FeignClient(name = "USER")
public interface UserFeignClient {
@RequestMapping(value = "/user/getUserInfo", method = RequestMethod.GET)
String getUserInfo();
}
1-3 eureka-user
pom依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
接口:
@RequestMapping("/user")
public interface UserRest {
@RequestMapping(value = "/getUserInfo", method = RequestMethod.GET)
String getUserInfo();
}
接口实现类:
@RestController
public class UserRestImpl implements UserRest{
@Autowired
private UserService userService;
@Override
public String getUserInfo() {
System.out.println("start to getUserInfo --->");
return userService.getUserInfo();
}
}
方法实现,为了验证 ribbon 的负载均衡,这里返回服务的端口号:
@Service("userService")
public class UserService {
@Value("${server.port}")
private Integer port;
public String getUserInfo() {
// 当前项目所在IP
String ip = IpUtil.getIp();
return "getUserInfo success : [user_ip:" + ip + "user_port:" + port + "] ";
}
}
2 ribbon方式
Ribbon 是 Netflix 发布的负载均衡器,与 Eureka 配合使用时,Ribbon 可以自动从 Eureka Server 获取服务提供者地址列表,并基于负载均衡算法,请求其中某个服务提供者实例。Ribbon的依赖是:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
但是 spring-cloud-starter-eureka 中已经包含了 ribbon 依赖,所以直接添加 eureka 即可。
2-1 eureka-sales
pom依赖,这里直接有 eureka 就行,eureka 已经包含了 ribbon 依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8081
spring:
application:
name: sales
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
启动类,这里把 RestTemplate 注册一下,并且加上 @LoadBalanced 开启负载均衡:
@SpringBootApplication
@EnableDiscoveryClient
public class SalesApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SalesApplication.class,args);
}
}
调用 eureka-user 服务,其中 RIBBON_URL 中的 user 是eureka-user注册到eureka中心的名称,也就是 application.name :
@Service
public class SalesService {
@Autowired
RestTemplate restTemplate;
private static final String RIBBON_URL = "http://user:8082/user/getUserInfo";
private static final String IP = IpUtil.getIp();
public String queryGoodsListByRibbon() {
String sales_result = "queryGoodsListByRibbon success : [sales_ip:" + IP + "] ";
String result = restTemplate.getForObject(RIBBON_URL, String.class);
return sales_result + result;
}
}
2-2 测试
1. 启动 eureka 2. 启动 eureka-sales 3.启动 eureka-user 4.修改 eureka-user 的 application.yml 文件,将端口改为8083,启动第二个 eureka-user 服务。
访问接口:http://127.0.0.1:8081/sales/queryGoodsList/1
此时以 ribbon 的方式访问10次,结果说明有进行负载均衡
3 feign方式
3-1 eureka-sales
pom依赖,添加 feign 依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
</dependencies>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
application.yml
server:
port: 8081
spring:
application:
name: sales
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
instance:
prefer-ip-address: true
启动类,添加 @EnableFeignClients 注解:
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class SalesApplication {
@Bean
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(SalesApplication.class,args);
}
}
创建 feign client 接口,这里的 USER 是 eureka-user 在 eureka 中心的注册名称,里面的接口定义成跟 eureka-user(被调用服务)中的接口一样:
@FeignClient(name = "USER")
public interface UserFeignClient {
@RequestMapping(value = "/user/getUserInfo", method = RequestMethod.GET)
String getUserInfo();
}
调用 eureka-user 服务:
@Service
public class SalesService {
@Autowired
UserFeignClient userFeignClient;
private static final String IP = IpUtil.getIp();
public String queryGoodsListByFeign() {
String sales_result = "queryGoodsListByFeign success : [sales_ip:" + IP + "] ";
String result = (String) userFeignClient.getUserInfo();
return sales_result + result;
}
}
3-2 测试
1. 启动 eureka 2. 启动 eureka-sales 3.启动 eureka-user 4.修改 eureka-user 的 application.yml 文件,将端口改为8083,启动第二个 eureka-user 服务。
访问接口:http://127.0.0.1:8081/sales/queryGoodsList/2
此时以 ribbon 的方式访问10次,结果说明有进行负载均衡
4 http方式
就是自己封装一个 httpclient 的 get 方法,然后访问:
http://127.0.0.1:8081/sales/queryGoodsList/3,然后观察结果,发现并没有自动进行负载均衡:
5 附录
eureka 项目:https://code.aliyun.com/995586041/eureka.git
eureka-sales 项目:https://code.aliyun.com/995586041/ribbon_feign_http_sales.git
eureka-user 项目:https://code.aliyun.com/995586041/ribbon_feign_http_user.git
core-simple 项目:https://code.aliyun.com/995586041/core-simple.git
上文中相关工具方法在 core-simple 中