md feign 如果名字是一样的,config没法根据具体的client来配置了。这个坑 很深。
再比如 feign 上传文件 到微服务 得加feign-form jar,而且还不支持body传其它参数,只支持path 或者 param。
再比如 feign调用的时候,刚上线的微服务总是找不到,需要改eruaka发现配置。
一堆堆的啊
eclipse安装lombok
https://blog.csdn.net/ypf8312/article/details/80340604
https://blog.csdn.net/dorothy1224/article/details/79280591/
Spring Cloud分布式事务终极解决方案探讨
https://segmentfault.com/a/1190000012762869
Java Metrics系统性能监控工具
https://www.jianshu.com/p/e5bba03fd64f
Spring Boot 内嵌容器Undertow参数设置
https://www.cnblogs.com/duanxz/p/9337022.html
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@LocalServerPort private int port;
@Autowired private TestRestTemplate template;
@Before
@Test
assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
number: ${random.int}
uuid : ${random.uuid}
max: ${random.int(10)}
value: ${random.value}
server:
port: 8080
context-path: /springboot
girl:
name: B
age: 18
content: content:${name},age:${age}
@ConfigurationProperties(prefix=“girl”)
@Component 可加可不加,spring-boot-configuration-processor依赖可加可不加
public class GirlProperties
将配置属性注入java bean(属性+getter+setter),获取此处前一配置文件里girt: 的属性;
在应用类或者application类,加EnableConfigurationProperties注解
@RestController
@EnableConfigurationProperties({ConfigBean.class})
public class LucyController
自定义配置文件注入javabean,需要三个注解
@Configuration
@PropertySource(value = “classpath:test.properties”)
@ConfigurationProperties(prefix = “com.forezp”)
public class User
多环境配置文件
格式为application-{profile}.properties,其中{profile}对应你的环境标识
application-test.properties:测试环境
application-dev.properties:开发环境
application-prod.properties:生产环境
application.yml添加:
spring:
profiles:
active: dev
其中application-dev.yml:
server:
port: 8082
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
jdbcTemplate.update("insert into account(name, money) values(?, ?)",
account.getName(),account.getMoney());
List list = jdbcTemplate.query("select * from account where id = ?", new Object[]{id}, new BeanPropertyRowMapper(Account.class));
JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中
JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发人员可以编码来实现该API
JPA是需要Provider来实现其功能的,Hibernate就是JPA Provider中很强的一个,应该说无人能出其右。从功能上来说,JPA就是Hibernate功能的一个子集
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
username: root
password: 123456
jpa:
hibernate:
ddl-auto: update # 第一次简表create 后面用update
show-sql: true
如果通过jpa在数据库中建表,将jpa.hibernate,ddl-auto改为create,建完表之后,要改为update,要不然每次重启工程会删除表并新建
@Entity
public class Account {
@Id
@GeneratedValue
private int id ;
public interface AccountDao extends JpaRepository {
}
List accountDao.findAll()
Account accountDao.findOne(id)
String
Account account = new Account();
account.setMoney(money);
account.setName(name);
account.setId(id);
Account account1 = accountDao.saveAndFlush(account);
return account1.toString();
使用baseRepository.save()发现无论怎么同步锁都会出现数据误差,后来换成saveAndFlush方法 结果对了,以此推断save方法具有延迟性,在高并发下建议用saveAndFlush
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
public interface AccountMapper {
@Insert("insert into account(name, money) values(#{name}, #{money})")
int add(@Param("name") String name, @Param("money") double money);
@Update("update account set name = #{name}, money = #{money} where id = #{id}")
int update(@Param("name") String name, @Param("money") double money, @Param("id") int id);
@Delete("delete from account where id = #{id}")
int delete(int id);
@Select("select id, name as name, money as money from account where id = #{id}")
Account findAccount(@Param("id") int id);
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
mybatis.mapper-locations=classpath*:mybatis/*Mapper.xml
mybatis.type-aliases-package=com.forezp.entity
@Transactional
public void transfer() throws RuntimeException{
如果mongodb端口是默认端口,并且没有设置密码,可不配置,sprinboot会开启默认的。
spring.data.mongodb.uri=mongodb://localhost:27017/springboot-db
mongodb设置了密码,这样配置:
spring.data.mongodb.uri=mongodb://name:pass@localhost:27017/dbname
import org.springframework.data.annotation.Id;
public class Customer {
@Id
public String id;
public interface CustomerRepository extends MongoRepository {
public Customer findByFirstName(String firstName);
public List findByLastName(String lastName);
写一个接口,继承MongoRepository,这个接口有了几本的CURD的功能。如果你想自定义一些查询,比如根据firstName来查询,获取根据lastName来查询,只需要定义一个方法即可。注意firstName严格按照存入的mongodb的字段对应。在典型的java的应用程序,写这样一个接口的方法,需要自己实现,但是在springboot中,你只需要按照格式写一个接口名和对应的参数就可以了,因为springboot已经帮你实现了
spring.redis.host=localhost
spring.redis.port=6379
#spring.redis.password=
spring.redis.database=1
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=0
@Repository
public class RedisDao {
@Autowired
private StringRedisTemplate template;
public void setKey(String key,String value){
ValueOperations ops = template.opsForValue();
ops.set(key,value,1, TimeUnit.MINUTES);//1分钟过期
}
https://blog.csdn.net/forezp/article/details/71023510
public class WebLayerTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
.andExpect(content().string(containsString("Hello World")))
.andDo(document("home"));
}
}
org.asciidoctor
asciidoctor-maven-plugin
generate-docs
prepare-package
process-asciidoc
index.adoc
html
${project.build.directory}/snippets
https://blog.csdn.net/forezp/article/details/71023536
在线文档的查阅,在线文档的测试。另外swagger很容易构建restful风格的api,简单优雅帅气
io.springfox
springfox-swagger2
2.6.1
io.springfox
springfox-swagger-ui
2.6.1
@Configuration //@Configuration注解,表明它是一个配置类
@EnableSwagger2 //@EnableSwagger2开启swagger2
public class Swagger2 {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.apiInfo(apiInfo())
.select() //apis()指定扫描的包会生成文档
.apis(RequestHandlerSelectors.basePackage("com.forezp.controller"))
.paths(PathSelectors.any())
.build();
}
private ApiInfo apiInfo() { //apiINfo()配置一些基本的信息
return new ApiInfoBuilder()
.title("springboot利用swagger构建api文档")
.description("简单优雅的restfun风格,http://blog.csdn.net/forezp")
.termsOfServiceUrl("http://blog.csdn.net/forezp")
.version("1.0")
.build();
}
}
@Api:修饰整个类,描述Controller的作用
@ApiOperation:描述一个类的一个方法,或者说一个接口
@ApiParam:单个参数描述
@ApiModel:用对象来接收参数
@ApiProperty:用对象接收参数时,描述对象的一个字段
@ApiResponse:HTTP响应其中1个描述
@ApiResponses:HTTP响应整体描述
@ApiIgnore:使用该注解忽略这个API
@ApiError :发生错误返回的信息
@ApiParamImplicitL:一个请求参数
@ApiParamsImplicit 多个请求参数
如果请求参数在url上,@ApiImplicitParam 上加paramType = “path”
org.springframework.boot
spring-boot-starter-data-redis
一.CountDownLatch用法
http://www.importnew.com/21889.html
public class Receiver {
private static final Logger LOGGER = LoggerFactory.getLogger(Receiver.class);
private CountDownLatch latch;
@Autowired
public Receiver(CountDownLatch latch) {
this.latch = latch;
}
public void receiveMessage(String message) {
LOGGER.info("Received <" + message + ">");
latch.countDown();
}
}
@Bean
Receiver receiver(CountDownLatch latch) {
return new Receiver(latch);
}
@Bean
CountDownLatch latch() {
return new CountDownLatch(1);
}
@Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
在spring data redis中,利用redis发送一条消息和接受一条消息,需要三样东西:
一个连接工厂
一个消息监听容器
Redis template
@Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,MessageListenerAdapter listenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(listenerAdapter, new PatternTopic("chat"));
return container;
}
@Bean
MessageListenerAdapter listenerAdapter(Receiver receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
关于RedisTemplate和StringRedisTemplate
https://blog.csdn.net/notsaltedfish/article/details/75948281
public static void main(String[] args) throws Exception{
ApplicationContext ctx = SpringApplication.run(SpringbootRedisApplication.class, args);
StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
CountDownLatch latch = ctx.getBean(CountDownLatch.class);
LOGGER.info("Sending message...");
template.convertAndSend("chat", "Hello from Redis!");
latch.await();
System.exit(0);
}
org.springframework.boot
spring-boot-starter-amqp
@Component
public class Receiver {
private CountDownLatch latch = new CountDownLatch(1);
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
latch.countDown();
}
public CountDownLatch getLatch() {
return latch;
}
}
创建消息监听,并发送一条消息
RabbitTemplate提供了发送消息和接收消息的所有方法
需要一个消息监听容器
声明一个quene,一个exchange,并且绑定它们
一个组件去发送消息
https://blog.csdn.net/forezp/article/details/71023692
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
@Bean
public CommandLineRunner run(RestTemplate restTemplate) throws Exception {
return args -> {
String quote = restTemplate.getForObject(
"http://gturnquist-quoters.cfapps.io/api/random", String.class);
log.info(quote.toString());
};
}
https://blog.csdn.net/forezp/article/details/71023752
在方法上加@Scheduled注解,表明该方法是一个调度任务。
@Scheduled(fixedRate = 5000) :上一次开始执行时间点之后5秒再执行
@Scheduled(fixedDelay = 5000) :上一次执行完毕时间点之后5秒再执行
@Scheduled(initialDelay=1000, fixedRate=5000) :第一次延迟1秒后执行,之后按fixedRate的规则每5秒执行一次
@Scheduled(cron=” /5 “) :通过cron表达式定义规则,什么是cro表达式,自行搜索引擎。
https://blog.csdn.net/forezp/article/details/71023817
@ModelAttribute
https://blog.csdn.net/lovesomnus/article/details/78873089
https://blog.csdn.net/forezp/article/details/71024153
https://blog.csdn.net/forezp/article/details/71024169
@JsonIgnoreProperties(ignoreUnknown=true) 忽略json对实体中字段缺失的情况
@Async 异步java请求
@EnableAsync
通过dockerfile和maven-docker插件,构建docker镜像
https://blog.csdn.net/forezp/article/details/71024219
在微服务架构中,需要几个基础的服务治理组件,包括服务注册与发现、服务消费、负载均衡、断路器、智能路由、配置管理等
Eureka Server
server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false //1.
fetchRegistry: false //2. 通过这俩配置表明是server
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
Eureka Client
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8762
spring:
application:
name: service-hi //需要指明spring.application.name,这个很重要,
//这在以后的服务与服务之间相互调用一般都是根据这个name
IClientConfig ribbonClientConfig: DefaultClientConfigImpl
IRule ribbonRule: ZoneAvoidanceRule
IPing ribbonPing: NoOpPing
ServerList ribbonServerList: ConfigurationBasedServerList
ServerListFilter ribbonServerListFilter: ZonePreferenceServerListFilter
ILoadBalancer ribbonLoadBalancer: ZoneAwareLoadBalancer
@FeignClient(value = "service-hi")
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
@RestController
public class HiController {
@Autowired
SchedualServiceHi schedualServiceHi;
@RequestMapping(value = "/hi",method = RequestMethod.GET)
public String sayHi(@RequestParam String name){
return schedualServiceHi.sayHiFromClientOne(name);
//消费已定义的接口,调用远端服务
}
}
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class ServiceRibbonApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
@Bean //向IOC提交自己实例化的对象,注解在提供bean的方法上
@LoadBalanced
RestTemplate restTemplate() {
return new RestTemplate();
}
}
RestTemplate+Ribbon使用断路器hystrix
@Service
public class HelloService {
@Autowired //在使用的地方注入bean
RestTemplate restTemplate;
@HystrixCommand(fallbackMethod = "hiError") //fallbackMethod调用hiError()
public String hiService(String name) {
return restTemplate.getForObject("http://SERVICE-HI/hi?name="+name,String.class);
}
public String hiError(String name) { // hiError()
return "hi,"+name+",sorry,error!";
}
}
工程不可用的时候,service-ribbon调用API接口时,会执行快速失败,直接返回一组字符串,而不是等待响应超时,这很好的控制了容器的线程阻塞
Feign中使用断路器Hystrix
feign.hystrix.enabled=true
@FeignClient(value = "service-hi",fallback = SchedualServiceHiHystric.class)
//feign接口fallback=指定类
public interface SchedualServiceHi {
@RequestMapping(value = "/hi",method = RequestMethod.GET)
String sayHiFromClientOne(@RequestParam(value = "name") String name);
}
@Component
public class SchedualServiceHiHystric implements SchedualServiceHi {
@Override
public String sayHiFromClientOne(String name) {
return "sorry "+name;
}
}
Hystrix Dashboard (断路器:Hystrix 仪表盘)
新建zuul工程
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
server:
port: 8769
spring:
application:
name: service-zuul
zuul:
routes:
api-a:
path: /api-a/**
serviceId: service-ribbon
api-b:
path: /api-b/**
serviceId: service-feign
zuul不仅只是路由,并且还能过滤,做一些安全验证
@Component
public class MyFilter extends ZuulFilter{
private static Logger log = LoggerFactory.getLogger(MyFilter.class);
@Override
public String filterType() {
return "pre"; //返回一个字符串代表过滤器的类型,
//在zuul中定义了四种不同生命周期的过滤器类型
//pre:路由之前 routing:路由之时 post: 路由之后 error:发送错误调用
}
@Override
public int filterOrder() {
return 0; //filterOrder:过滤的顺序
}
@Override
public boolean shouldFilter() {
return true;
//shouldFilter:这里可以写逻辑判断,是否要过滤,本文true,永远过滤
}
@Override
public Object run() {
//run:过滤器的具体逻辑。可用很复杂,包括查sql,nosql去判断该请求到底有没有权限访问
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info(String.format("%s >>> %s", request.getMethod(), request.getRequestURL().toString()));
Object accessToken = request.getParameter("token");
if(accessToken == null) {
log.warn("token is empty");
ctx.setSendZuulResponse(false);
ctx.setResponseStatusCode(401);
try {
ctx.getResponse().getWriter().write("token is empty");
}catch (Exception e){}
return null;
}
log.info("ok");
return null;
}
}
支持配置服务放在配置服务的内存中(即本地),也支持放在远程Git仓库中。在spring cloud config 组件中,分两个角色,一是config server,二是config client
构建config server
spring.application.name=config-server
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/forezp/SpringcloudConfig/ //git仓库地址
spring.cloud.config.server.git.searchPaths=respo //仓库路径
spring.cloud.config.label=master //仓库分支
spring.cloud.config.server.git.username=your username //仓库用户名,公开仓库不需要
spring.cloud.config.server.git.password=your password //仓库密码,公开仓库不需要
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
构建config client
spring.application.name=config-client
spring.cloud.config.label=master //远程仓库分支
spring.cloud.config.profile=dev //dev开发,test测试,pro正式
spring.cloud.config.uri= http://localhost:8888/ //配置服务中心网址
server.port=8881
@Value("${foo}")
String foo;
@RequestMapping(value = "/hi")
public String hi(){
return foo;
}
创建eureka server工程,作为配置中心的注册中心
server:
port: 8889 //指定服务端口为8889
eureka: //加上作为服务注册中心的基本配置
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
改造config-server
spring.application.name=config-server
server.port=8888
spring.cloud.config.server.git.uri=https://github.com/forezp/SpringcloudConfig/
spring.cloud.config.server.git.searchPaths=respo
spring.cloud.config.label=master
spring.cloud.config.server.git.username= your username
spring.cloud.config.server.git.password= your password
eureka.client.serviceUrl.defaultZone=http://localhost:8889/eureka/
//指定服务地址。其他配置与单机版配置中心相同
改造config-client
spring.application.name=config-client
spring.cloud.config.label=master spring.cloud.config.profile=dev
//spring.cloud.config.uri= http://localhost:8888/eureka.client.serviceUrl.defaultZone=http://localhost:8889/eureka/ //服务注册地址
spring.cloud.config.discovery.enabled=true //从配置中心读取文件
spring.cloud.config.discovery.serviceId=config-server server.port=8881 //配置中心serverid,服务名
//在读取配置文件不再写ip地址,而是服务名,这时如果配置服务部署多份,通过负载均衡,从而高可用
改造config-client
spring.rabbitmq.host=localhost //地址
spring.rabbitmq.port=5672 //端口
// spring.rabbitmq.username= //用户名
// spring.rabbitmq.password= //密码
改造eureka-server
server:
port: 8761
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2:8769/eureka/
server:
port: 8769
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/
127.0.0.1 peer1
127.0.0.1 peer2
windows电脑,在c:/windows/systems/drivers/etc/hosts 修改
eureka:
client:
serviceUrl:
defaultZone: http://peer1:8761/eureka/
server:
port: 8762
spring:
application:
name: service-hi
启动eureka-server
java -jar eureka-server-0.0.1-SNAPSHOT.jar - -spring.profiles.active=peer1
java -jar eureka-server-0.0.1-SNAPSHOT.jar - -spring.profiles.active=peer2
https://blog.csdn.net/forezp/article/details/70198649
Docker通常用于如下场景:
web应用的自动化打包和发布;
自动化测试和持续集成、发布;
在服务型环境中部署和调整数据库或其他的后台应用;
从头编译或者扩展现有的OpenShift或Cloud Foundry平台来搭建自己的PaaS环境。
采用maven的方式去构建项目,并采用docker-maven-plugin去构建docker镜像
org.springframework.boot
spring-boot-maven-plugin
com.spotify
docker-maven-plugin
0.4.3
${docker.image.prefix}/${project.artifactId}
src/main/docker
/
${project.build.directory}
${project.build.finalName}.jar
Spotify 的 docker-maven-plugin 插件是用maven插件方式构建docker镜像的。
imageName指定了镜像的名字,本例为 forep/eureka-server
dockerDirectory指定 Dockerfile 的位置
resources是指那些需要和 Dockerfile 放在一起,在构建镜像时使用的文件,一般应用 jar 包需要纳入。
server:
port: 8761
eureka:
instance:
prefer-ip-address: true
client:
registerWithEureka: false
fetchRegistry: false
FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
ADD eureka-server-0.0.1-SNAPSHOT.jar app.jar
// RUN bash -c 'touch /app.jar'
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
EXPOSE 8761
在微服务架构中为例保证程序的可用性,防止程序出错导致网络阻塞,出现了断路器模型。断路器的状况反应了一个程序的可用性和健壮性,它是一个重要指标。Hystrix Dashboard是作为断路器状态的一个组件,提供了数据监控和友好的图形化界面
@SpringBootApplication
@EnableEurekaClient
@RestController
@EnableHystrix //必须的
@EnableHystrixDashboard //开启HystrixDashboard
public class ServiceHiApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceHiApplication.class, args);
}
@Value("${server.port}")
String port;
@RequestMapping("/hi")
@HystrixCommand(fallbackMethod = "hiError")
//声明断路点HystrixCommand
public String home(@RequestParam String name) {
return "hi "+name+",i am from port:" +port;
}
public String hiError(String name) {
return "hi,"+name+",sorry,error!";
}
}
@SpringBootApplication
@EnableTurbine
spring:
application.name: service-turbine
server:
port: 8769
security.basic.enabled: false
turbine:
aggregator:
clusterConfig: default # 指定聚合哪些集群,多个使用","分割,默认为default。可使用http://.../turbine.stream?cluster={clusterConfig之一}访问
appConfig: service-hi,service-lucy ### 配置Eureka中的serviceId列表,表明监控哪些服务
clusterNameExpression: new String("default")
# 1. clusterNameExpression指定集群名称,默认表达式appName;此时:turbine.aggregator.clusterConfig需要配置想要监控的应用名称
# 2. 当clusterNameExpression: default时,turbine.aggregator.clusterConfig可以不写,因为默认就是default
# 3. 当clusterNameExpression: metadata['cluster']时,假设想要监控的应用配置了eureka.instance.metadata-map.cluster: ABC,则需要配置,同时turbine.aggregator.clusterConfig: ABC
eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/