string cloud 配置中心、注册中心、服务的注册与发现,客户端负载均衡(实例)

spring cloud

Spring Cloud是一系列框架的有序集合。它利用Spring Boot的开发便利性巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监控等,都可以用Spring Boot的开发风格做到一键启动和部署。Spring将目前各家公司开发的比较成熟、经得起实际考验的服务框架组合起来,通过Spring Boot风格进行再封装屏蔽掉了复杂的配置和实现原理,最终给开发者留出了一套简单易懂、易部署和易维护的分布式系统开发工具包



配置中心

在分布式系统中,由于服务数量巨多,而每个服务实例都会有一个或几个配置文件(yml,properties,json…)。而这些文件,分布在系统的各个角落,管理起来特别麻烦,为了方便服务配置文件统一管理,实时更新,所以需要分布式配置中心组件。在Spring Cloud中,有分布式配置中心组件springcloud config,它支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config组件中,分两个角色,一是configserver,二是configclient。


在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>cn.et</groupId>
	<artifactId>ConfigurationCenter</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath />
	</parent>
	<dependencies>
	    <!-- 添加web功能 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		<!-- 配置中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-server</artifactId>
		</dependency> 
	</dependencies>
	
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Edgware.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	
</project>


这里配置文件我们放在GitHub的远程仓库里,远程仓库文件ms-product.properties内容如下

spring.datasource.url=jdbc:mysql://localhost/et_shop
spring.datasource.username=root
spring.datasource.password=LCH
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.application.name=userservice

我们还需要配置端口号和远程仓库地址,创建文件application.properties

server.port=8090
spring.cloud.config.server.git.uri=https://github.com/DreamLCH/conf.git


到此,一个简单的配置中心我们已经配置好了,编写运行类进行测试,新建项目启动类ConfigServer,通过@EnableConfigServer开启配置中心服务。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServerMain {
	public static void main(String[] args) {
		SpringApplication.run(ConfigServerMain.class, args);
	}
}

通过访问http://localhost:8090/ms-product.properties可以访问到配置文件的内容

http请求地址和资源文件映射如下(其中application为服务名): 
- /{application}/{profile}[/{label}] 
- /{application}-{profile}.yml 
- /{label}/{application}-{profile}.yml 
- /{application}-{profile}.properties 
- /{label}/{application}-{profile}.properties


新建一个SpringBoot的客户端项目Application去配置中心获取配置文件的属性值

添加依赖:

<!-- 客户端 微服务 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency> 


添加服务端的配置,在项目的resources目录下,新建bootstrap.yml,在其中添加如下内容:

server: 
    port: 8888
spring: 
    application: 
        name: ms
    profiles: 
        active: product
    cloud: 
        config: 
            uri: http://localhost:8090


配置启动类,并在Controller中获取Git文件中的属性

新建启动类ApplicationMain,并使用@RestController注册为Controller接口。使用@Value获取属性信息:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class ApplicationMain {

	@Value("${spring.datasource.url}")
	private String url;


	@RequestMapping("/url")
	public String getUrl() {
		return url;
	}

	public static void main(String[] args) {
		SpringApplication.run(ApplicationMain.class, args);
	}
}

访问地址:http://localhost:8888/url

显示结果如下:


注册中心

Spring Cloud 封装了 Netflix 公司开发的 Eureka 模块来实现服务注册和发现。Eureka 采用了 C-S 的设计架构。Eureka Server 作为服务注册功能的服务器,它是服务注册中心。而系统中的其他微服务,使用 Eureka 的客户端连接到 Eureka Server,并维持心跳连接。这样系统的维护人员就可以通过 Eureka Server 来监控系统中各个微服务是否正常运行。Spring Cloud 的一些其他模块(比如Zuul)就可以通过 Eureka Server 来发现系统中的其他微服务,并执行相关的逻辑。

Eureka由两个组件组成:Eureka服务器和Eureka客户端。Eureka服务器用作服务注册服务器。Eureka客户端是一个java客户端,用来简化与服务器的交互、作为轮询负载均衡器,并提供服务的故障切换支持。Netflix在其生产环境中使用的是另外的客户端,它提供基于流量、资源利用率以及出错状态的加权负载均衡。


1、Eureka Server

  • 提供服务注册和发现

2、Service Provider

  • 服务提供方

  • 将自身服务注册到Eureka,从而使服务消费方能够找到

3、Service Consumer

  • 服务消费方

  • 从Eureka获取注册服务列表,从而能够消费服务


Eureka Server

spring cloud已经帮我们实现了服务注册中心,我们只需要很简单的几个步骤就可以完成。

添加依赖:

<!-- 注册中心 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-eureka-server</artifactId>
		</dependency>


添加配置文件application.yml

#eureka注册中心对外暴露的端口 提供web界面 查看所有注册的微服务
server:
  port: 8761
#设置注册中心当前主机的主机名  必须指定appname
eureka:
  instance:
    hostname: myhost
    appname: myhost
  client: 
    #表示是否将自己注册到Eureka Server,默认为true
    registerWithEureka: false
    #表示是否从Eureka Server获取注册信息,默认为true。
    fetchRegistry: false
    #该地址是将来 所有微服务用于注册到注册中心的地址 【重要】
    serviceUrl:
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

主机的主机名可以去C:\Windows\System32\drivers\etc\hosts文件中设置

127.0.0.1 myhost
127.0.0.1 userservice


添加启动类,加上@EnableEurekaServer注解

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class RegServer {
  public static void main(String[] args) {
    SpringApplication.run(RegServer.class, args);
  }
}


启动工程后,访问:http://localhost:8761,可以看到下面的页面,其中还没有发现任何服务




Service Provider


添加依赖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>cn.et</groupId>
	<artifactId>MicroService</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.10.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<dependencies>
	
	    <!-- 添加web功能 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		
		
		<!-- 客户端  -->
		<dependency> 
            <groupId>org.springframework.cloud</groupId>  
            <artifactId>spring-cloud-starter-eureka</artifactId>  
        </dependency>
        
        
        <!-- 获取配置中心配置客户端 -->
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency> 
        
		<!-- mysql -->
		<dependency>
		    <groupId>mysql</groupId>
		    <artifactId>mysql-connector-java</artifactId>
		    <version>5.1.38</version>
		</dependency>
		
		<!-- 集成 mysql -->
		<dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.2</version>
		</dependency>
		
		
		<!-- 阿里巴巴druid连接池 -->
		<dependency>
		    <groupId>com.alibaba</groupId>
		    <artifactId>druid</artifactId>
		    <version>1.1.8</version>
		</dependency>
		
		<!-- 自动刷新工具 -->
		<dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-devtools</artifactId>
	        <optional>true</optional>
	    </dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Edgware.SR2</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
</project>

这里可以看到引入了很多其他的架包,集成了mybatis用于查询数据库数据,使用了druid连接池,数据源datasource我们从之前写好的配置中心去获取。


在resources目录下新建bootstrap.yml配置文件,在其中添加如下内容:

#对外提供接口调用的端口
server:
  port: 8888
#设置注册中心当前主机的主机名  
eureka:
  instance:
    hostname: userservice
    appname: userservice
  #注册到注册中心
  client: 
    #表示是否将自己注册到Eureka Server,默认为true
    registerWithEureka: true
    #是否将 注册中心上所有的微服务抓取到本地 存储
    fetchRegistry: false
    #该地址是将来 所有微服务用于注册到注册中心的地址 【重要】
    serviceUrl:
      defaultZone: http://myhost:8761/eureka/
#去配置中心获取配置
spring: 
    application: 
        name: ms
    profiles: 
        active: product
    cloud: 
        config: 
            uri: http://localhost:8090


创建datasource的bean实例,使用@Value从配置中心获取值

import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;

/**
 * <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" >
 * </bean>
 * @author Administrator
 *
 */
@Configuration
@SpringBootApplication
public class MyConfiguration {
	@Value("${spring.datasource.url}")
	private String url;
	@Value("${spring.datasource.username}")
	private String userName;
	@Value("${spring.datasource.password}")
	private String password;
	@Value("${spring.datasource.driver-class-name}")
	private String driverClassName;

	@Bean
	public DataSource dataSource() {
		DruidDataSource dds = new DruidDataSource();
		dds.setDriverClassName(driverClassName);
		dds.setUrl(url);
		dds.setUsername(userName);
		dds.setPassword(password);
		return dds;
	}

	@Bean
	public ServletRegistrationBean DruidStatView() {
		ServletRegistrationBean sr = new ServletRegistrationBean();
		sr.setServlet(new StatViewServlet());
		List<String> list = new ArrayList<String>();
		list.add("/druid/*");
		sr.setUrlMappings(list);
		return sr;
	}

}

编写dao层接口

import java.util.Map;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import com.et.psm.model.User;

@Mapper
public interface UserDao {

	@Select("select id name,pwd password from userlogin where id = #{name}")
	public User queryUserByName(@Param("name") String name);

	@Insert("insert into userlogin values(#{userName},#{password})")
	public void addUser(Map map);

	@Update("update userlogin set pwd=#{m.password} where id=#{userName}")
	public void updateUser(@Param("userName") String userName, @Param("m") Map map);

	@Delete("delete from userlogin where id=#{name}")
	public void deleteUser(@Param("name") String name);
}

实体类user

public class User {
	private String name;
	private String password;

	public User() {
		super();
		// TODO Auto-generated constructor stub
	}

	public User(String name, String password) {
		super();
		this.name = name;
		this.password = password;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@Override
	public String toString() {
		return "User [name=" + name + ", password=" + password + "]";
	}

}

service接口实现类,service接口这里就不列出来了

import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.et.psm.dao.UserDao;
import com.et.psm.model.User;
import com.et.psm.service.UserService;

@Service
public class UserServiceImpl implements UserService {

	@Autowired
	UserDao userdao;

	@Override
	public Map queryUserByName(String name, String password) {
		User user = userdao.queryUserByName(name);
		Map map = new HashMap();

		if (user == null) {
			map.put("statuscode", 0);
			map.put("message", "你输入的用户名不存在。");
		} else if (user.getPassword().equals(password)) {
			map.put("statuscode", 1);
			map.put("message", "账号密码正确。");
		} else {
			map.put("statuscode", -1);
			map.put("message", "你输入的密码错误。");
		}
		return map;
	}

	@Override
	public Map addUser(Map map) {
		Map returnMap = new HashMap();
		try {
			userdao.addUser(map);
			returnMap.put("statuscode", 0);
			returnMap.put("message", "添加成功。");
		} catch (Exception e) {
			returnMap.put("statuscode", 1);
			returnMap.put("message", "添加失败。");
		} finally {
			return returnMap;
		}
	}

	@Override
	public Map updateUser(String userName, Map map) {
		Map returnMap = new HashMap();

		try {
			userdao.updateUser(userName, map);
			returnMap.put("statuscode", 0);
			returnMap.put("message", "修改成功。");
		} catch (Exception e) {
			returnMap.put("statuscode", 1);
			returnMap.put("message", "修改失败。");
		} finally {
			return returnMap;
		}
	}

	@Override
	public Map deleteUser(String name) {
		Map returnMap = new HashMap();

		try {
			userdao.deleteUser(name);
			returnMap.put("statuscode", 0);
			returnMap.put("message", "删除成功。");
		} catch (Exception e) {
			returnMap.put("statuscode", 1);
			returnMap.put("message", "删除失败。");
		} finally {
			return returnMap;
		}
	}

}

控制层

import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.et.psm.service.UserService;

@RestController
public class UserController {

	@Autowired
	UserService userService;

	@GetMapping("/user")
	public Map validUser(String userName, String password) {
		return userService.queryUserByName(userName, password);
	}

	@PostMapping("/user")
	public Map addUser(@RequestBody Map map) {
		return userService.addUser(map);
	}

	@PutMapping("/user/{userName}")
	public Map UpdateUser(@PathVariable String userName, @RequestBody Map map) {
		return userService.updateUser(userName, map);
	}

	@DeleteMapping("/user/{userName}")
	public Map deleteUser(@PathVariable String userName) {
		return userService.deleteUser(userName);
	}

}


运行类,添加@EnableDiscoveryClient

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceMain {

  public static void main(String[] args) {
    SpringApplication.run(UserServiceMain.class, args);
  }
}


运行项目,再访问http://localhost:8761 会看到已经有服务注册到注册中心了




Service Consumer

引入依赖

<!-- 客户端 -->
	<dependency> 
            <groupId>org.springframework.cloud</groupId>  
            <artifactId>spring-cloud-starter-eureka</artifactId>  
        </dependency> 
        
        <!-- ribbon 服务调用和负载均衡 -->
        <dependency> 
            <groupId>org.springframework.cloud</groupId>  
            <artifactId>spring-cloud-starter-ribbon</artifactId>  
        </dependency>

ribbon:使用RestTemplate实现http调用 提供了客户端负载均衡的能力


运行类:@RibbonClient(value="USERSERVICE")指定客户端主机名

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.ribbon.RibbonClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@RibbonClient(value="USERSERVICE")
public class UserViewMain {
	@LoadBalanced // 启动负载均衡
	@Bean
	RestTemplate restTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		SpringApplication.run(UserViewMain.class, args);
	}
}

控制层:

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

@RestController
public class UserController {

	@Autowired
	RestTemplate template;

	@RequestMapping("/validUser")
	public String validUser(String userName, String password) {
		Map returnCode = template
				.getForObject("http://USERSERVICE/user?userName=" + userName + "&password=" + password, Map.class);

		return (String) returnCode.get("message");
	}

	@PostMapping("/user")
	public String addUser(@RequestParam Map map) {
		// 模拟请求头
		HttpHeaders requestHeaders = new HttpHeaders();
		// 传递的json
		requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
		// 返回可接受json类型
		requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
		// 将请求头和请求体(表单数据)打包
		HttpEntity<Map> request = new HttpEntity<Map>(map, requestHeaders);

		Map returnCode = template.postForObject("http://USERSERVICE/user", request, Map.class);

		return (String) returnCode.get("message");
	}


	@PutMapping("/user/{userName}")
	public String updateUser(@PathVariable String userName,@RequestParam Map map) {
		// 模拟请求头
		HttpHeaders requestHeaders = new HttpHeaders();
		// 传递的json
		requestHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
		// 返回可接受json类型
		requestHeaders.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON_UTF8));
		// 将请求头和请求体(表单数据)打包
		HttpEntity<Map> request = new HttpEntity<Map>(map, requestHeaders);
		Map variables = new HashMap();
		variables.put("userName", userName);
		try {
			template.put("http://USERSERVICE/user/{userName}", request, variables);
			return "修改成功。";
		} catch (Exception e) {
			return "修改失败。";
		}
	}

	@DeleteMapping("/user/{userName}")
	public String deleteUser(@PathVariable String userName) {
		Map variables = new HashMap();
		variables.put("userName", userName);
		try {
			template.delete("http://USERSERVICE/user/{userName}", variables);
			return "删除成功。";
		} catch (Exception e) {
			return "删除失败。";
		}
	}
}

注意:这里使用的是restful风格设计,所以在编写jsp文件的修改删除的时候需要加一个隐藏表单域,提交时要先提交到js进行处理,不然无法访问到控制层的修改或删除方法,如下

<%@ page language="java" 
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Insert title here3</title>
<script type="text/javascript">
   function sub(){
	   var userName=document.getElementsByName("userName")[0].value;
	   document.forms[0].action="user/"+userName;
	   document.forms[0].submit();
   }

</script>
</head>
<body>
  <form action="/user" method="post">
  <input type="hidden" name="_method" value="delete">
     用户名 :<input name="userName"/>
     <input type="button" οnclick="sub()" value="删除"/>
  </form>
</body>
</html>

现在整个实例已经完成了,在Service Consumer 里面编写jsp文件访问控制层里的crdu方法进行操作,当注册多个客户端时,调用服务将会轮询调用客户端的服务。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值