依赖
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.0.1</version>
</dependency>
服务注册实现
注册类
public class RgisteToZk implements ApplicationRunner, ApplicationContextAware {
private static final Logger LOG = Logger.getLogger(RgisteToZk.class);
//ZooKeeper服务地址
private static final String SERVER = "127.0.0.1:2181";
//会话超时时间
private static final int SESSION_TIMEOUT = 30000;
//连接超时时间
private static final int CONNECTION_TIMEOUT = 5000;
//创建连接实例
private CuratorFramework client = null;
//服务注册的节点路径
private static final String BASE_SERVICES = "/services";
//需要注册的服务
private static final List<String> services = new LinkedList<>();
@Value("${server.port}")
private int port;
@PostConstruct
public void init() {
//获取zk连接实例
client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, new ExponentialBackoffRetry(1000, 3));
//启动
client.start();
}
@PreDestroy
public void destory() {
//关闭
client.delete();
client.close();
}
//注册服务
public void registe() {
try {
Stat pathStat = client.checkExists().forPath(BASE_SERVICES);
if (pathStat == null) {
//创建根路径
client.create().orSetData().withMode(CreateMode.PERSISTENT).forPath(BASE_SERVICES, "services".getBytes());
}
services.forEach(service -> {
try {
//创建临时有顺序的节点,因为存在节点挂掉和节点服务一致的情况,所以需要临时并且有顺序
client.create().orSetData().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath(BASE_SERVICES + "/", service.getBytes());
} catch (Exception e) {
e.printStackTrace();
LOG.error("注册失败!");
}
});
} catch (Exception e) {
e.printStackTrace();
LOG.error("节点创建失败!");
}
}
@Override
public void run(ApplicationArguments args) {
registe();
}
//获取服务
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
String address = "";
try {
address = InetAddress.getLocalHost().getHostAddress() + ":" + port;
} catch (UnknownHostException e) {
e.printStackTrace();
}
//获取容器中所有的服务
String[] allService = applicationContext.getBeanNamesForAnnotation(RestController.class);
for (int i = 0; i < allService.length; i++) {
String service = allService[i];
Class bean = applicationContext.getBean(service).getClass();
Method[] methods = bean.getMethods();
for (Method method : methods) {
GetMapping annotation = method.getAnnotation(GetMapping.class);
if (annotation != null) {
String[] path = annotation.value();
services.add(address + "/" + path[0]);
}
}
}
}
}
注册启动注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(RgisteToZk.class)
public @interface EnableServiceRgisteToZk {
}
实体类
public class User {
private String userName;
private int age;
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
public String getUserName() {
return userName;
}
public User setUserName(String userName) {
this.userName = userName;
return this;
}
public int getAge() {
return age;
}
public User setAge(int age) {
this.age = age;
return this;
}
}
服务类
@RestController
public class ProductController {
@GetMapping("product")
public String product() {
return "test";
}
}
启动类
@SpringBootApplication
@EnableServiceRgisteToZk
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
服务发现
服务发现类
//拉取服务
public class PullServices implements ApplicationRunner {
private static final Logger LOG = Logger.getLogger(PullServices.class);
//ZooKeeper服务地址
private static final String SERVER = "127.0.0.1:2181";
//会话超时时间
private static final int SESSION_TIMEOUT = 30000;
//连接超时时间
private static final int CONNECTION_TIMEOUT = 5000;
//创建连接实例
private CuratorFramework client = null;
//服务注册的节点路径
private static final String BASE_SERVICES = "/services";
public static final Map<String, String> services = new ConcurrentHashMap<>();
@PostConstruct
public void init() {
//获取zk连接实例
client = CuratorFrameworkFactory.newClient(SERVER, SESSION_TIMEOUT, CONNECTION_TIMEOUT, new ExponentialBackoffRetry(1000, 3));
//启动
client.start();
}
@PreDestroy
public void destory() {
//关闭
client.close();
}
private void pull() throws Exception {
List<String> paths = client.getChildren().forPath(BASE_SERVICES);
if (paths == null || paths.size() == 0) {
LOG.error("服务不存在!");
throw new NullPointerException();
}
paths.forEach(path -> {
try {
byte[] bytes = client.getData().forPath(BASE_SERVICES + "/" + path);
String pathData = new String(bytes, "UTF-8");
services.put(path, pathData);
//监听当前节点
TreeCache treeCache = new TreeCache(client, "/test");
//设置监听器和处理过程
treeCache.getListenable().addListener(new TreeCacheListener() {
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
ChildData data = event.getData();
if (data != null) {
String currentPath = data.getPath();
String pathData = new String(data.getData(), "UTF-8");
switch (event.getType()) {
case NODE_ADDED:
services.put(currentPath, pathData);
break;
case NODE_REMOVED:
services.remove(currentPath, pathData);
break;
case NODE_UPDATED:
services.put(currentPath, pathData);
break;
default:
break;
}
} else {
LOG.error("data is null : " + event.getType());
}
}
});
//开始监听
treeCache.start();
} catch (Exception e) {
e.printStackTrace();
}
});
}
@Override
public void run(ApplicationArguments args) throws Exception {
pull();
}
}
开启服务发现注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(PullServices.class)
public @interface EnablePullServices {
}
自定义负载工具类
public abstract class LoadBalance {
public final List<String> SERVICE_LIST;
protected LoadBalance(List<String> service_list) {
SERVICE_LIST = service_list;
}
public abstract String choseServiceHost();
}
public class RamdomLoadBalance extends LoadBalance {
public RamdomLoadBalance(List<String> service_list) {
super(service_list);
}
@Override
public String choseServiceHost() {
String result = "";
if (!CollectionUtils.isEmpty(SERVICE_LIST)) {
int index = new Random().nextInt(SERVICE_LIST.size());
result = SERVICE_LIST.get(index);
}
return result;
}
}
调用类
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
private static final ConcurrentHashMap concurrentHashMap = (ConcurrentHashMap) PullServices.services;
@GetMapping("test")
public String getUser() {
List<String> services = new LinkedList<>();
concurrentHashMap.forEach((key, value) -> {
services.add(((String) value));
});
LoadBalance balance = new RamdomLoadBalance(services);
String host = balance.choseServiceHost();
User user = restTemplate.getForObject("http://" + host, User.class);
return host + ".........." + user;
}
}
实体类
public class User implements Serializable {
private String userName;
private int age;
public User(){}
public User(String userName, int age) {
this.userName = userName;
this.age = age;
}
public String getUserName() {
return userName;
}
public User setUserName(String userName) {
this.userName = userName;
return this;
}
public int getAge() {
return age;
}
public User setAge(int age) {
this.age = age;
return this;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", age=" + age +
'}';
}
}
启动类
@SpringBootApplication
@EnablePullServices
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
测试结果
服务注册
服务发现