Eureka实现注册中心

CAP原则又称CAP定理,指的是在一个分布式系统中,Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可得兼。它是分布式系统中最核心最重要的理论。
分布式系统的CAP理论:理论首先把分布式系统中的三个特性进行了如下归纳:
l 一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
l 可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
l 分区容错性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
ZooKeeper和Eureka对比
ZooKeeper基于CP,不保证高可用,如果zookeeper正在选主,或者Zookeeper集群中半数以上机器不可用,那么将无法获得数据。Eureka基于AP,能保证高可用,即使所有机器都挂了,也能拿到本地缓存的数据。作为注册中心,其实配置是不经常变动的,只有发版(发布新的版本)和机器出故障时会变。对于不经常变动的配置来说,CP是不合适的,而AP在遇到问题时可以用牺牲一致性来保证可用性,既返回旧数据,缓存数据。
所以理论上Eureka是更适合作注册中心。而现实环境中大部分项目可能会使用ZooKeeper,那是因为集群不够大,并且基本不会遇到用做注册中心的机器一半以上都挂了的情况。所以实际上也没什么大问题。
Eureka部署服务端、提供端、消费端(消费者整合负载均衡Ribbon,提供方整合Mybaits)
服务端:
搭建SpringBoot项目

pom.xml
<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>eureka.server</groupId> <artifactId>eureka-server</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
application.yml
security:
basic:
enabled: true
user:
name: user
password: password123
server:
port: 8761
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http://user:password123@localhost:8761/eureka
logging:
level:
root: INFO
RunAppEureka.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class RunAppEureka {
public static void main(String[] args) {
SpringApplication.run(RunAppEureka.class, args);
}
}
提供方1(加入整合SpringBoot整合Mybatis)

<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>eureka.provider</groupId> <artifactId>eureka-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <impala.jdbc.version>2.5.30</impala.jdbc.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.12</version> </dependency> <dependency> <groupId>com.oracle</groupId> <artifactId>ojdbc6</artifactId> <version>11.2.0.3</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
application.yml
server:
port: 7900
spring:
application:
name: provider-user
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.OracleDriver
url: jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST=172.24.7.177)(PORT=1521)))(CONNECT_DATA=(SERVICE_NAME=iovdb)))
username: foton
password: foton[zk]
mybatis:
typeAliasesPackage: cn.hz.pojo
mapperLocations: classpath:mappers/*.xml
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
logging:
level:
root: INFO
ProviderRunApp.java
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@MapperScan("cn.hz.mapper")
public class ProviderRunApp {
public static void main(String[] args) {
SpringApplication.run(ProviderRunApp.class, args);
}
}
User.java
import java.io.Serializable;
import java.util.Date;
import org.springframework.format.annotation.DateTimeFormat;
import com.fasterxml.jackson.annotation.JsonFormat;
public class User implements Serializable {
/**
*
*/
private static final long serialVersionUID = 8693407369491314806L;
/*
* id NUMBER(19) not null, created TIMESTAMP(6), modified TIMESTAMP(6),
* description VARCHAR2(255 CHAR), name VARCHAR2(255 CHAR),
* cbm_mag_company_id NUMBER(19), category_id NUMBER(19), cbm_mag_user_id
* NUMBER(19), layout CLOB
*/
private Long id;
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss"
)
private Date created;
@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
@JsonFormat(
pattern = "yyyy-MM-dd HH:mm:ss"
)
private Date modified;
private String description;
private String name;
private int sort;
private Long companyId;
private Long userId;
private Long parentId;
getter setter。。。
@Override
public String toString() {
return "User [id=" + id + ", created=" + created + ", modified=" + modified + ", description=" + description
+ ", name=" + name + ", sort=" + sort + ", companyid=" + companyId + ", userId=" + userId
+ ", parentId=" + parentId + "]";
}
UserMapper.java
import java.util.List;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import cn.hz.pojo.User;
//注解和xml方式混合
public interface UserMapper {
//调用xml方式
public List<User> find();
//调用注解方式
@Select("select id, created, modified, description, name, sort, cbm_mag_company_id as companyId, parent_id as parentId, cbm_mag_user_id as userId from IOV_DASH_CATEGORY where id=#{id}")
public User get(@Param("id") Long id);
}
UserService.java
import java.util.List;
import cn.hz.pojo.User;
public interface UserService {
public List<User> find();
public User get(Long id);
}
UserServiceImpl.java
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import cn.hz.mapper.UserMapper;
import cn.hz.pojo.User;
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
public List<User> find() {
return userMapper.find();
}
public User get(Long id) {
return userMapper.get(id);
}
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- namespace命名空间,唯一特性 --> <mapper namespace="cn.hz.mapper.UserMapper"> <resultMap type="cn.hz.pojo.User" id="user"> <id column="id" property="id" jdbcType="BIGINT"/> <result column="created" property="created" javaType="java.sql.Date"/> <result column="modified" property="modified" javaType="java.sql.Date"/> <result column="description" property="description" jdbcType="VARCHAR"/> <result column="name" property="name" jdbcType="VARCHAR"/> <result column="sort" property="sort" jdbcType="INTEGER"/> <result column="companyId" property="cbm_mag_company_id" jdbcType="BIGINT"/> <result column="userId" property="cbm_mag_user_id" jdbcType="BIGINT"/> <result column="parentId" property="parent_id" jdbcType="BIGINT"/> </resultMap> <select id="find" resultType="user"> select id, created, modified, description, name, sort, cbm_mag_company_id as companyId, parent_id as parentId, cbm_mag_user_id as userId from IOV_DASH_CATEGORY </select> </mapper>
UserController.java (为整合MyBatis测试使用)
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import cn.hz.pojo.User;
import cn.hz.service.UserService;
@RestController
@RequestMapping(value = "/user")
public class UserController {
@Autowired
private UserService userSerivce;
@GetMapping("/{name}")
public String getName(@PathVariable String name) {
return "hello:" + name;
}
@GetMapping("/list")
public String list() {
return userSerivce.find().toString();
}
@GetMapping("/get/{id}")
public String get(@PathVariable Long id) {
User user = userSerivce.get(id);
if (null == user)
return null;
else
return userSerivce.get(id).toString();
}
}
提供方2和提供方1创建基本相同,此处忽略
消费者:(整合Ribbon调用不同的提供方)
1.1.1 Ribbon
Feign是netflix开发的声明式、模板化的http客户端,在使用时就像调用本地(服务消费者自己)的方法一般,帮助我们更加优雅的调用服务提供者的API。Feign自身支持springMVC,还整合了Eureka、Ribbon,极大的简化了Feign的使用。就整合Euraka而言,只需和普通的服务配置Eureka server的信息即可。整合Ribbon,就意味着不再需要通过标注@LoadBalanced的实例化后的RestTemplate去调用服务提供者方法了。Feign只需通过简单的定义一个接口即可实现负载均衡。
和nginx不同,它是客户端侧负载均衡。


pom.xml
<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>eureka.client</groupId> <artifactId>eureka-client</artifactId> <version>0.0.1-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.SR1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> </project>
application.yml
server:
port: 8010
spring:
application:
name: consumer-client
eureka:
client:
serviceUrl:
defaultZone: http://user:password123@localhost:8761/eureka
logging:
level:
root: INFO
ClientRunApp.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name="provider-user", configuration=RibbonRuleConfig.class)
public class ClientRunApp {
@Bean
@LoadBalanced //Ribbon负载均衡
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ClientRunApp.class, args);
}
}
RibbonRuleConfig.java
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RandomRule;
/**
*
* 自定义Ribbon配置
* 规定:这个类不能再@ComponentScan和@SpringBootApplication本包和子包下,否则引起@RibbonClients扫描冲突
* 注意:随机第一次打断点进入,之后多次刷新就不进入,可能由于本地缓存原因
*/
@Configuration
public class RibbonRuleConfig {
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
}
UserController.java
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.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/user/{id}")
@ResponseBody
public String getName(@PathVariable String id) {
//String url = "http://localhost:7900/user/"+ name;
//provider-user就是Eureka中提供服务
String url = "http://provider-user/user/get/"+id;
return "client:" + this.restTemplate.getForObject(url, String.class);
//return url;
}
}
浙公网安备 33010602011771号