spring cloud之eureka
eureka是spring cloud集成的注册中心,注册中心有很多,比较常用的有spring cloud的 eureka,Dubbo的Zookeeper,这两个都是注册中心。
Eureka 注册中心三种角色
1.Eureka Server:通过 Register、Get、Renew 等接口提供服务的注册和发现。
2Application Service (Service Provider):服务提供方,把自身的服务实例注册到 Eureka Server 中
3Application Client (Service Consumer):服务调用方,通过 Eureka Server 获取服务列表,消费服务。
eureka工作原理:
1.Register(服务注册):把自己的 IP 和端口注册给 Eureka。
2.Renew(服务续约):发送心跳包,每 30 秒发送一次。告诉 Eureka 自己还活着。
3.Cancel(服务下线):当 provider 关闭时会向 Eureka 发送消息,把自己从服务列表中删除。防
止 consumer 调用到不存在的服务。
4.Get Registry(获取服务注册列表):获取其他服务列表。
5.Replicate(集群中数据同步):eureka 集群中的数据复制与同步。
6.Make Remote Call(远程调用):完成服务的远程调用。
CAP 原则
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency (一致性)、
Availability (可用性)、Partition tolerance (分区容错性),三者不可 兼得。
CAP 由 由 Eric Brewer 在 在 2000 年 年 PODC 会议上提出。 。 该猜想在提出两年后被证明成
的 立,成为我们熟知的 CAP 定理
Zookeeper 与 与 Eureka
1,自我保护的条件
一般情况下,微服务在 Eureka 上注册后,会每 30 秒发送心跳包,Eureka 通过心跳来
判断服务时候健康,同时会定期删除超过 90 秒没有发送心跳服务。
2,有两种情况会导致 Eureka Server 收不到微服务的心跳
a.是微服务自身的原因
b.是微服务与 Eureka 之间的网络故障
通常(微服务的自身的故障关闭)只会导致个别服务出现故障,一般不会出现大面积故
障,而(网络故障)通常会导致 Eureka Server 在短时间内无法收到大批心跳。
考虑到这个区别,Eureka 设置了一个阀值,当判断挂掉的服务的数量超过阀值时,
Eureka Server 认为很大程度上出现了网络故障,将不再删除心跳过期的服务。
3,那么这个阀值是多少呢?
15 分钟之内是否低于 85%;
Eureka Server 在运行期间,会统计心跳失败的比例在 15 分钟内是否低于 85%
这种算法叫做 Eureka Server 的自我保护模式
下面演示代码:在provider中有优雅停服,consumer没有
admin注册中心
1.pom文件
<?xml version="1.0" encoding="UTF-8"?> <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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.21.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.mr.li</groupId> <artifactId>spring-cloud-eureka-admin</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-cloud-eureka-admin</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Edgware.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.application.yml
#给此服务起个名字
spring:
application:
name: spring-eureka-admin
#设置端口号
server:
port: 8761
#是否将自己注册到Eureka-Server中,默认的为true,在注册中心多节点时可开启互相注册发现,但单节点是不可以。
eureka:
client:
registerWithEureka: false
fetchRegistry: false #是否从Eureka-Server中获取服务注册信息,默认为true
# server:
# enableSelfPreservation: false #自我保护:ture 开启, false 关闭
# eviction:
# interval-timer-in-ms: 6000 #当收不到心跳包时清理服务间隔:默认1000 * 60(1分钟)
#开启http basic的安全认证
security:
basic:
enabled: true
user:
name: user
password: 123456
3.启动类
package cn.mr.li.springcloudeurekaadmin; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer; /** * 添加@EnableEurekaServer表示这是一个注册中心服务端 * @author Administrator * */ @EnableEurekaServer @SpringBootApplication public class SpringCloudEurekaAdminApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudEurekaAdminApplication.class, args); System.out.println("eureka admin start success"); } }
provider生产者
1.pom文件
<?xml version="1.0" encoding="UTF-8"?> <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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.21.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.mr.li</groupId> <artifactId>spring-cloud-eureka-client-provider</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-cloud-eureka-client-provider</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Edgware.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 本来是client的,但是要以客户端的方式优雅停服,所以要改成server启动器,因为 此启动器中包含actuator.jar,此jar包是可以以客户端的方式停服的,并且将服务从注册中心移除 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.application.yml
#修改端口
server:
port: 8080
#别名
spring:
application:
name: eureka-provider
#设置注册中心地址
eureka:
client:
serviceUrl:
#user是spring security的用户名123456是密码@是分割的
defaultZone: http://user:123456@192.168.31.20:8761/eureka/
endpoints:
shutdown:
enabled: true #启用shutdown
sensitive: false #禁用密码验证
3.启动类
package cn.mr.li.springcloudeurekaclient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * 此注解表示是一个Eureka客户端,也就是被注册的项目 * @author Administrator * */ @EnableEurekaClient @SpringBootApplication public class ProviderApplication { public static void main(String[] args) { SpringApplication.run(ProviderApplication.class, args); System.out.println("eureka provider start success"); } }
4.controller
package cn.mr.li.springcloudeurekaclient.controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @RequestMapping("/user") public String show() { return "hello world"; } }
5.httpClilentUtil在测试包下,主要用于停止服务,doPost的方式
package cn.mr.li.springcloudeurekaclient; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.utils.URIBuilder; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; import org.apache.http.util.EntityUtils; public class HttpClientUtil { public static String doGet(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpclient = HttpClients.createDefault(); String resultString = ""; CloseableHttpResponse response = null; try { // 创建uri URIBuilder builder = new URIBuilder(url); if (param != null) { for (String key : param.keySet()) { builder.addParameter(key, param.get(key)); } } URI uri = builder.build(); // 创建http GET请求 HttpGet httpGet = new HttpGet(uri); // 执行请求 response = httpclient.execute(httpGet); // 判断返回状态是否为200 if (response.getStatusLine().getStatusCode() == 200) { resultString = EntityUtils.toString(response.getEntity(), "UTF-8"); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (response != null) { response.close(); } httpclient.close(); } catch (IOException e) { e.printStackTrace(); } } return resultString; } public static String doGet(String url) { return doGet(url, null); } public static String doPost(String url, Map<String, String> param) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建参数列表 if (param != null) { List<NameValuePair> paramList = new ArrayList<>(); for (String key : param.keySet()) { paramList.add(new BasicNameValuePair(key, param.get(key))); } // 模拟表单 UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList,"utf-8"); httpPost.setEntity(entity); } // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } public static String doPost(String url) { return doPost(url, null); } public static String doPostJson(String url, String json) { // 创建Httpclient对象 CloseableHttpClient httpClient = HttpClients.createDefault(); CloseableHttpResponse response = null; String resultString = ""; try { // 创建Http Post请求 HttpPost httpPost = new HttpPost(url); // 创建请求内容 StringEntity entity = new StringEntity(json, ContentType.APPLICATION_JSON); httpPost.setEntity(entity); // 执行http请求 response = httpClient.execute(httpPost); resultString = EntityUtils.toString(response.getEntity(), "utf-8"); } catch (Exception e) { e.printStackTrace(); } finally { try { response.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return resultString; } //优雅停服:在该项目运行期间可调用此方法将服务器停止并从注册中心移除。 public static void main(String[] args) { String url ="http://192.168.31.20:8080/shutdown"; //该url必须要使用dopost方式来发送 HttpClientUtil.doPost(url); } }
consumer消费者
1.pom文件
<?xml version="1.0" encoding="UTF-8"?> <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> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.21.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.mr.li</groupId> <artifactId>spring-cloud-eureka-client-consumer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-cloud-eureka-client-consumer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Edgware.SR6</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.application.yml
#修改端口
server:
port: 9090
#别名
spring:
application:
name: eureka-consumer
#设置注册中心地址
eureka:
client:
serviceUrl:
defaultZone: http://user:123456@192.168.31.20:8761/eureka/
3.启动类
package cn.mr.li.springcloudeurekaclient; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; /** * 此注解表示是一个Eureka客户端,也就是被注册的项目 * @author Administrator * */ @EnableEurekaClient @SpringBootApplication public class ConsumerApplication { public static void main(String[] args) { SpringApplication.run(ConsumerApplication.class, args); System.out.println("eureka consumer start success"); } }
4.controller
package cn.mr.li.springcloudeurekaclient.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import cn.mr.li.springcloudeurekaclient.service.UserService; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/tttt") public String show() { return "consumer: "+userService.show(); } }
5.service
package cn.mr.li.springcloudeurekaclient.service; public interface UserService { /** * 演示跨接口调用 * @return */ String show(); }
6.serviceImpl
package cn.mr.li.springcloudeurekaclient.service.impl; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import cn.mr.li.springcloudeurekaclient.service.UserService; @Service public class UserServiceImpl implements UserService{ @Autowired private LoadBalancerClient loadBalancerClient;//ribbon负载均衡器 @Override public String show() { //选择调用的服务的名称 //ServiceInstance 封装了服务的基本信息,如 IP,端口 ServiceInstance si = this.loadBalancerClient.choose("eureka-provider"); //拼接访问服务的URL StringBuffer url = new StringBuffer(); //http://localhost:9090/user url.append("http://").append(si.getHost()).append(":").append(si.getPort()).append("/user"); //springMVC RestTemplate RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<String> type = new ParameterizedTypeReference<String>() {}; //ResponseEntity:封装了返回值信息 ResponseEntity<String> response = rt.exchange(url.toString(),HttpMethod.GET, null, type); String str =response.getBody(); return str; } }