案例中有三个角色:服务注册中心、服务提供者、服务消费者,其中服务注册中心就是我们上一篇的eureka单机版启动既可,流程是首先启动注册中心,服务提供者生产服务并注册到服务中心中,消费者从服务中心中获取服务并执行。
注册中心
上一篇文章中已经有注册中心的工程搭建。
服务提供者
添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
启动类添加注册发现注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient //注册发现注解
public class ProviderApplication {
public static void main(String[] args) {
SpringApplication.run(ProviderApplication.class, args);
}
}
配置文件
#服务端口
server.port=8010
#服务名
spring.application.name=cloud-provider
#指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
service
import com.uv.provider.entity.User;
import java.util.ArrayList;
import java.util.List;
import org.springframework.stereotype.Service;
/*
* @author uv
* @date 2018/10/8 14:46
*
*/
@Service
public class UserService {
private static List<User> userList = null;
//模拟数据库数据
static {
userList = new ArrayList<>();
User user1 = new User();
User user2 = new User();
User user3 = new User();
user1.setId(1).setName("Tom").setSex("男").setAge(18);
user2.setId(2).setName("Sam").setSex("男").setAge(19);
user3.setId(3).setName("Ling").setSex("女").setAge(18);
userList.add(user1);
userList.add(user2);
userList.add(user3);
}
public List<User> getUserList() {
return userList;
}
public User getUser(int id) {
for (User user : userList) {
if(user.getId() == id) {
return user;
}
}
return null;
}
}
User
/*
* @author uv
* @date 2018/10/8 14:47
*
*/
public class User {
private Integer id;
private String name;
private String sex;
private Integer age;
public Integer getId() {
return id;
}
public User setId(Integer id) {
this.id = id;
return this;
}
public String getName() {
return name;
}
public User setName(String name) {
this.name = name;
return this;
}
public String getSex() {
return sex;
}
public User setSex(String sex) {
this.sex = sex;
return this;
}
public Integer getAge() {
return age;
}
public User setAge(Integer age) {
this.age = age;
return this;
}
}
controller
import com.uv.provider.entity.User;
import com.uv.provider.service.UserService;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/*
* @author uv
* @date 2018/10/8 15:26
*
*/
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("user/list")
public List<User> getUserList() {
return userService.getUserList();
}
@GetMapping("user/{id}")
public User getUserByPath(@PathVariable("id") int id) {
return userService.getUser(id);
}
@GetMapping("user")
public User getUserById(int id) {
return userService.getUser(id);
}
}
服务消费者
添加依赖
<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>
启动类添加注册发现注解和远程调用注解
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient //启用服务注册与发现
@EnableFeignClients //启用feign进行远程调用
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
配置文件
Ribbon进行客户端负载均衡的Client并不是在服务启动的时候就初始化好的,而是在调用的时候才会去创建相应的Client,所以第一次调用的耗时不仅仅包含发送HTTP请求的时间,还包含了创建RibbonClient的时间,这样一来如果创建时间速度较慢,同时设置的超时时间又比较短的话,很容易就会出现成功启动的时候第一次访问会有报错的情况发生,但是之后又恢复正常访问的问题。使用饥饿加载模式,在服务启动时就会加载服务。
#服务端口
server.port=8020
#服务名
spring.application.name=cloud-consumer
#指定服务注册中心的位置
eureka.client.serviceUrl.defaultZone=http://localhost:8000/eureka/
#开启Ribbon的饥饿加载模式
ribbon.eager-load.enabled=true
#指定需要饥饿加载的服务名
ribbon.eager-load.clients=cloud-provider
#Fegin 连接超时,默认的ReadTimeout时间为5s,ConnectTimeout时间为2s
ribbon.ReadTimeout=60000
ribbon.ConnectTimeout=60000
Client:远程调用 (远程调用返回的类必须有无参构造方法,否则报错:com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of com.uv.consumer.entity.User: no suitable constructor found, can not deserialize from Object value)
import com.uv.consumer.entity.User;
import java.util.List;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/*
* @author uv
* @date 2018/10/8 18:48
* 注意:远程调用返回的类(User)必须有无参构造方法,否则报错
*/
//name 为调用服务的spring.application.name
@FeignClient(name = "cloud-provider")
public interface UserClient {
//不能使用组合注解,如GetMapping
@RequestMapping(value = "user/list", method = RequestMethod.GET)
List<User> getUserList();
@RequestMapping(value = "user/{id}", method = RequestMethod.GET)
User getUserByPath(@PathVariable("id") int id);
//使用参数时,必须用@RequestParam注解
@RequestMapping(value = "user", method = RequestMethod.GET)
User getUserById(@RequestParam("id") int id);
}
测试controller
import com.uv.consumer.entity.User;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/*
* @author uv
* @date 2018/10/8 19:00
*
*/
@RestController
public class controller {
@Autowired
private UserClient userClient;
@RequestMapping("test")
public void test() {
List<User> userList = userClient.getUserList();
System.out.println(userList.size());
User userById = userClient.getUserById(2);
System.out.println(userById.getName());
User userByPath = userClient.getUserByPath(3);
System.out.println(userByPath.getName());
}
}
依次启动eureka、provider、consumer服务,通过http://localhost:8000/ 可以看到,provider和consumer已经注册到Eureka中。
使用 http://localhost:8020/test 进行测试,结果如下,远程调用成功。
github实例代码:https://github.com/UVliuwei/springcloud-demo